反射 - 使用属性作为类迭代类

时间:2014-10-16 22:26:19

标签: vb.net

我需要遍历一个类的整个结构。我正在询问发送到我的WCF方法的对象,我现在在每个类中都有一个ToString()的重载,它列出了所有属性及其值。这有效;但它是硬编码的,每次我们向类添加属性时都需要更新。

目前的解决方案是VB,但下一个版本将是C# - 因此两个标签。

该类可以仅由原始类型组成,或者该类可以由其他对象组成。迭代一个简单的类不是问题。

我无法确定某个属性何时是另一个类。因此,在下面的示例中,我可以遍历约会和患者并转储其每个属性的值。

我很难尝试迭代PatientAppointment。我已经搜索过MSDN和SO,尝试了类型中的无数属性等等无济于事。

Public Class PatientAppointment
    Public Property Pat As Patient
    Public Property Appt As Appointment
End Class

Public Class Appointment
    Public Property ApptProp1 As String
    Public Property ApptProp2 As Integer
End Class

Public Class Patient
    Public Property PatProp1 As String
    Public Property PatProp2 As Integer
End Class

2 个答案:

答案 0 :(得分:0)

听起来好像你正在寻找一种方法来确定你的类型有一定程度的确定性。一种方法可能是使用自定义属性,该属性易于查找并使用Reflection进行过滤。

Public Class RealMcCoy
    Inherits Attribute

    Public Property IsReal As Boolean 

    Public Sub New(b as Boolean)
      IsReal = b
    End Sub
End Class

<RealMcCoy(True)>      
Public Class Patient
    ...
End Class

<RealMcCoy(True)>   
Public Class Appointment
   ...
End Class

现在,当迭代属性时,检查它是否是RealMcCoy以查看它是否是您想要/需要钻取的属性。由于属性将在类型上,因此有一个额外的步骤来获取每个属性的类型并轮询

    Dim props As PropertyInfo() = myFoo.GetType.GetProperties
    Dim pt As Type

    For Each pi As PropertyInfo In props
        pt = pi.PropertyType         ' get the property type

        Dim attr() As RealMcCoy =
                DirectCast(pt.GetCustomAttributes(GetType(RealMcCoy), True), RealMcCoy())
        If attr.Length > 0 Then
            ' bingo, baby...optional extra test:
            If attr(0).IsReal Then
                Console.Beep()
            End If
        Else
            ' skip this prop - its not a real mccoy
        End If
    Next
End Sub

如果添加一个Type并且不添加Attribute,它就会中断,但它比不必更新每个Types的构成属性更容易破解。假接口更容易查询,但也有同样的缺点。

我不确定我是否理解“游戏”事物的问题 - 您是否害怕其他类型会被拾取?属性很难“游戏”,因为它们被编译到程序集中,上面仍然可以用来返回GUID(可能是一个附加到程序集?)而不是bool来提供一些保证。

绝对肯定很难得到。


RealMcCoy属性可能不会应用于您的顶级类型(PatientAppointment),只会将类型(类)应用于其他类型的属性。该对象是一种轻松识别这些的方法。

根据其使用方式,TypeName-PropertyName对已经标识为RealMcCoys的字典或散列表可能很有用,因此整个Reflection过程可以被短路。您可以在shown in this answer之前预先构建完整列表,而不是动态添加 - 请参阅RangeManager.BuildPropMap过程。

我不太确定继承方法,因为你可能想在某处实际使用继承。接口可能会更好地工作:最初,它的存在可能是一开始的触发器,但也可用于提供服务。


简单的测试用例:

' B and C classes are not tagged
Friend Class FooBar
    Public Property Prop1 As PropA
    Public Property Prop2 As PropB
    Public Property Prop3 As PropC
    Public Property Prop4 As PropD
    Public Property Prop5 As PropE
End Class

在for循环中添加一行:

Dim f As New FooBar
' use instance:
Dim props As PropertyInfo() = f.GetType.GetProperties
Dim pt As Type

For Each pi As PropertyInfo In props
    pt = pi.PropertyType

    Dim attr() As RealMcCoy =
            DirectCast(pt.GetCustomAttributes(GetType(RealMcCoy), True), RealMcCoy())

    Console.WriteLine("Prop Name: {0},  prop type: {1}, IsRealMcCoy? {2}",
            pi.Name, pt.Name, If(attr.Length > 0, "YES!", "no"))
Next

输出:

Prop Name: Prop1  prop type: PropA IsRealMcCoy? YES!  
Prop Name: Prop2  prop type: PropB IsRealMcCoy? no  
Prop Name: Prop3  prop type: PropC IsRealMcCoy? no 
Prop Name: Prop4  prop type: PropD IsRealMcCoy? YES! 
Prop Name: Prop5  prop type: PropE IsRealMcCoy? YES!

答案 1 :(得分:0)

非常感谢Plutonix! 这是我的最终功能。如果我的回答取代了Plutonix的答案,那么我需要对此进行调整,因为他完全值得我接受这一点。

我已将其测试为8级嵌套类型。由于这只是原型,基本的异常处理。

    Function IterateClass(ByVal o As Object, ByVal topLevelFlag As Boolean, ByVal indentLevel As Integer) As String

    Dim retVal As New StringBuilder

    Try

        '' Iterate top level of supplied type ''
        '' Query each property for custom attribute ADMICompositeClassAttribute ''
        '' If exists and set to true then we're dealing with a base class that we need to break down recursively ''
        '' If not then immediately dump this type's properties ''


        '' Build header of output ''
        If topLevelFlag Then

            '' <<EXCERPTED>> ''

        End If


        '' We start walking through the hierarchy here, no matter how much we recurse we still need to do this each time ''
        Dim properties_info As PropertyInfo()
        properties_info = o.GetType().GetProperties()

        For Each p As PropertyInfo In properties_info
            Dim propName As String = p.Name
            Dim propTypeList As List(Of String) = p.PropertyType.ToString().Split(".").ToList()
            Dim propType As String = propTypeList(propTypeList.Count - 1).Replace("[", "").Replace("]", "")
            Dim pValue As Object = Nothing

            '' We check this type for custom attribute ADMICompositeClassAttribute ''
            '' Is this type a composite? ''
            Dim oAttr() As ADMICompositeClassAttribute = p.PropertyType.GetCustomAttributes(GetType(ADMICompositeClassAttribute), False)
            Dim indentString As String = String.Concat(Enumerable.Repeat(" ", indentLevel))
            If oAttr.Length > 0 AndAlso oAttr(0).IsComposite Then

                '' This is a nested type, recurse it ''
                Dim dynType As Type = p.PropertyType()
                Dim dynObj As Object = Activator.CreateInstance(dynType)

                '' <<EXCERPTED ''

            Else

                pValue = p.GetValue(o, Nothing)

            End If

        Next


    Catch ex As Exception
        retVal.AppendLine(ex.ToString())

    End Try
    Return retVal.ToString()


End Function