从VB类实例获取字段对象,而不是FieldInfo

时间:2015-01-21 04:04:55

标签: vb.net reflection

我试图遍历类中的对象(字段)并在每个对象上调用一个方法。每个对象都是不同的类型。这是父类:

Public Class MySettings
    Public IdentifyByFacType As RadioButtonSetting

    Public WtrFacTypes As ListSetting
    Public OilFacTypes As ListSetting

    Public GroupByRef As CheckboxSetting
    Public GroupRefAttr As TxtboxSetting
End Class

以下是其中一个子对象类的一部分:

<Serializable>
Public Class TxtboxSetting
    <XmlIgnore()>
    Public MyControl As Windows.Forms.TextBox
    <XmlIgnore()>
    Public DefaultSetting As String

    Private _SavedSetting As String

    Public Property SavedSetting As String
        Get
            Return _SavedSetting
        End Get
        Set(value As String)
            _SavedSetting = value
            CurrentValue = value
        End Set
    End Property

    Public Sub New()
    End Sub

    Public Sub New(DefaultSetting As String, MyControl As Windows.Forms.TextBox)
        Me.DefaultSetting = DefaultSetting
        Me.MyControl = MyControl
    End Sub

    Public Sub RestoreDefault()
        CurrentValue = DefaultSetting
    End Sub
End Class

MySettings类的所有子对象(例如GroupRefAttr)具有相同的方法和属性,但内部代码不同。

所以我将有几个类,如MySettings类,每个类都有不同的子对象。给定这样一个类的实例,我想自动遍历字段并在每个字段上调用方法RestoreDefault。我不想知道MySettings课程中存在哪些对象。相反,知道他们都有RestoreDefault方法,我只想在每个对象上调用方法。

尽管经常进行搜索,我还没有办法做到这一点。通过反思,我只能做到这一点:

Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
    Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
    RestoreDefault.Invoke(Opts, Nothing)
Next

但是,在RestoreDefault.Invoke(Opts, Nothing)行中,我无法传递Opts,因为我正在处理Opts中的字段,而不是Opts本身。像这样的语句可以起作用:RestoreDefault.Invoke(Opts.GroupRefAttr, Nothing),但是这需要我提前知道MySettings类中的对象,这会破坏目的。有没有办法在运行时抓取字段实例对象并将其关闭?

2 个答案:

答案 0 :(得分:0)

当你调用RestoreDefault方法时,你需要在设置(即字段的值)上调用它,而不是包含设置的类。将代码更改为此可以解决您的问题:

Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
    Dim setting As Object = var.GetValue(Opts)
    Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
    RestoreDefault.Invoke(setting, Nothing)
Next

但是,如果您引入基类或接口,您应该能够摆脱部分或全部反射。容器设置类可以具有一组设置,每个设置都具有共享基类或具有RestoreDefault方法的接口。然后,容器设置类将通过基类或接口调用此方法,而不必使用反射。

基类:

Public MustInherit Class BaseSetting
    Public MustOverride Sub RestoreDefault
End Class

特定设置类:

Public Class TxtboxSetting
    Inherits BaseSetting

    Public Overrides Sub RestoreDefault()
        ' Specific implementation
    End Sub
End Class

在源自BaseSetting的任何类中,您现在可以调用RestoreDefault方法而无需使用反射。

但是,考虑到您的设计,您可能仍希望使用反射来获取MySettings类中的设置。你可以这样做:

Dim settings = From fieldInfo in Opts.GetType.GetFields
               Where GetType(BaseSetting).IsAssignableFrom(fieldInfo.FieldType)
               Select DirectCast(fieldInfo.GetValue(Opts), BaseSetting)
For Each setting In settings
    setting.RestoreDefault()
Next

反射用于查找从BaseSetting派生的所有字段,然后在每个字段上调用RestoreDefault。这种方法不会受到&#34;魔术字符串&#34;代码取决于字符串中表示的RestoreDefault方法的名称的问题。

(另外,调用MySettings有点误导,因为没有从MySettings继承。而是这个类包含其他设置。)

答案 1 :(得分:0)

  

MySettings类的所有子对象(例如GroupRefAttr)具有相同的方法和属性,但内部代码不同。

在这种情况下,应定义子对象类型,使它们实现一个需要存在相同方法和属性的公共接口。现在,我将命名该接口IControlSetting。然后,您的For循环看起来像这样:

Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
    Dim setting As IControlSetting = TryCast(var.GetValue(Opts), IControlSetting)
    If setting Is Nothing Then Continue
    setting.RestoreDefault()
Next

此外,我还要更改您的MySettings类型以封装字典或IControlSetting对象。然后你可以迭代字典来检查每个对象,而不是需要反射。这可能是这样的:

Public Class MySettings
    Private allSettings As Dictionary(Of String, IControlSetting)

    Public Sub New()
       allSettings = new Dictionary(Of String, IControlSetting)()

       allSettings.Add("IdentifyByFacType", New RadioButtonSetting())
       allSettings.Add("WtrFacTypes", New ListSetting())
       allSettings.Add("OilFacTypes", New ListSetting())
       '... 
    End Sub

    Public Property IdentifyByFacType As RadioButtonSetting
        Get
            Return DirectCast(allSettings("IdentifyByFacType"), RadioButtonSetting)
        End Get
        'The setters may be optional, depending on how you expect to use these
        Set(ByVal value As RadioButtonSetting)
            allSettings("IdentifyByFacType") = value
        End Set
    End Property

    Public Property WtrFacTypes As ListSetting
        Get
            Return DirectCast(allSettings("WtrFacTypes"), RadioButtonSetting)
        End Get
        Set(ByVal value As ListSetting)
            allSettings("WtrFacTypes") = value
        End Set
    End Property

    Public Property OilFacTypes As ListSetting
        Get
            Return DirectCast(allSettings("OilFacTypes"), RadioButtonSetting)
        End Get
        Set(ByVal value As ListSetting)
            allSettings("OilFacTypes") = value
        End Set
    End Property

    '...


    Public Sub RestoreAllDefaults()
        For Each setting As KeyValuePair(Of String, IControlSetting) In allSettings
            setting.Value.RestoreDefault()
        Next setting
    End Sub     

End Class