从 VB Class 实例获取 Field 对象,而不是 FieldInfo
Get a Field Object, not FieldInfo, from a VB Class Instance
我正在尝试遍历 class 中的对象(字段)并在每个对象上调用一个方法。每个对象都是不同的类型。这是父 class:
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
这是子对象之一的一部分 classes:
<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
class的所有子对象,比如GroupRefAttr
,都有相同的方法和属性,只是内部代码不同
所以我会有几个 class 像 MySettings
class,每个都有不同的子对象。给定这样一个 class 的实例,我想自动遍历字段并在每个字段上调用一个方法 RestoreDefault
。我不想知道 MySettings
class 中存在哪些对象。相反,知道它们都有 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
class 中的对象,这违背了目的。有没有办法在运行时获取字段实例对象并将其关闭?
当您调用 RestoreDefault
方法时,您需要在设置(即字段的值)上调用它,而不是包含该设置的 class。将您的代码更改为此应该可以解决您的问题:
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
但是,如果您引入基 class 或接口,您应该能够摆脱部分或全部反射。容器设置 class 可以有一组设置,每个设置都有一个共享基础 class 或与 RestoreDefault
方法的接口。容器设置 class 将通过基础 class 或接口调用此方法,而无需使用反射。
基地class:
Public MustInherit Class BaseSetting
Public MustOverride Sub RestoreDefault
End Class
具体设置class:
Public Class TxtboxSetting
Inherits BaseSetting
Public Overrides Sub RestoreDefault()
' Specific implementation
End Sub
End Class
在从 BaseSetting
派生的任何 class 上,您现在可以调用 RestoreDefault
方法而无需使用反射。
但是,考虑到您的设计,您可能仍希望使用反射来获取 MySettings
class 中包含的设置。你可以这样做:
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
。此方法不会遇到 "magic string" 问题,即您的代码取决于字符串中表示的 RestoreDefault
方法的名称。
(此外,将 MySettings
class 称为 父级 有点误导,因为没有任何内容继承自 MySettings
。取而代之的是class 包含其他设置。)
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
在这种情况下,应该定义子对象类型,以便它们实现一个通用接口,该接口要求存在这些相同的方法和属性。现在,我将该接口命名为 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
我正在尝试遍历 class 中的对象(字段)并在每个对象上调用一个方法。每个对象都是不同的类型。这是父 class:
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
这是子对象之一的一部分 classes:
<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
class的所有子对象,比如GroupRefAttr
,都有相同的方法和属性,只是内部代码不同
所以我会有几个 class 像 MySettings
class,每个都有不同的子对象。给定这样一个 class 的实例,我想自动遍历字段并在每个字段上调用一个方法 RestoreDefault
。我不想知道 MySettings
class 中存在哪些对象。相反,知道它们都有 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
class 中的对象,这违背了目的。有没有办法在运行时获取字段实例对象并将其关闭?
当您调用 RestoreDefault
方法时,您需要在设置(即字段的值)上调用它,而不是包含该设置的 class。将您的代码更改为此应该可以解决您的问题:
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
但是,如果您引入基 class 或接口,您应该能够摆脱部分或全部反射。容器设置 class 可以有一组设置,每个设置都有一个共享基础 class 或与 RestoreDefault
方法的接口。容器设置 class 将通过基础 class 或接口调用此方法,而无需使用反射。
基地class:
Public MustInherit Class BaseSetting
Public MustOverride Sub RestoreDefault
End Class
具体设置class:
Public Class TxtboxSetting
Inherits BaseSetting
Public Overrides Sub RestoreDefault()
' Specific implementation
End Sub
End Class
在从 BaseSetting
派生的任何 class 上,您现在可以调用 RestoreDefault
方法而无需使用反射。
但是,考虑到您的设计,您可能仍希望使用反射来获取 MySettings
class 中包含的设置。你可以这样做:
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
。此方法不会遇到 "magic string" 问题,即您的代码取决于字符串中表示的 RestoreDefault
方法的名称。
(此外,将 MySettings
class 称为 父级 有点误导,因为没有任何内容继承自 MySettings
。取而代之的是class 包含其他设置。)
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
在这种情况下,应该定义子对象类型,以便它们实现一个通用接口,该接口要求存在这些相同的方法和属性。现在,我将该接口命名为 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