这是一个测试代码:
Public Class Class2
Public GoodName as String
Public Function someFunction ()
End Function
End Class
Public Class Class1
Public Enum ENUM_VAL1_
abc = 1
def = 2
End Enum
Public Enum ERROR_VAL1_
yhn = 1
ujm = 2
End Enum
Public Function fun1(byval _enumArg1 as ENUM_VAL1_,
byval _stringArg as String,
byref _classArg Class2) as ERROR_VAL1_
... SOMETHING HERE
return something
End Function
End Class
和测试
Dim asm As Assembly = Assembly.LoadFrom(<the source>)
If asm Is Nothing Then Return Nothing
Dim typ As Type = asm.GetType(<namespace>.<class1>)
If typ Is Nothing Then Return Nothing
Dim obj As Object = Activator.CreateInstance(typ)
If obj Is Nothing Then Return Nothing
Dim arg As Object() = New Object() {ENUM_VAL1_.abc, "qazxsw", New Class2}
Dim res As Object = typ.InvokeMember( _
"fun1", _
BindingFlags.[Default] Or BindingFlags.InvokeMethod, _
Nothing, _
obj, _
arg)
[Dim res As Object = typ.InvokeMember( _
"fun1", _
BindingFlags.[Default] Or BindingFlags.InvokeMethod, _
Nothing, _
obj, _
arg)
]
返回错误:function '[fullname]' not found.
然后我意识到fun1签名与我的调用不同(即使枚举值是整数)。我做了一些研究,当目标程序集上的属性或函数具有与标准类型不同的参数时,我发现了一些关于如何实现反射的“一些”示例。但我成功地将这些样本“概念”转化为我的需要。
所以,我在这里放了一些带有虚拟枚举类型的虚拟类,只是为了指出问题的骨架。
原始代码是关于windows防火墙/端口(win7 / xp / vista),其中有许多枚举值,包括来自firewallApi.dll和Hnetcfg.dll的类型。
我遇到的问题是我不能使用以下语句“InvokeMember”(也称为“SetProperties”和“SetProperties”):
Dim args As Object() = New Object() {"SQL", 6, "1433", 1}
其中我的汇编函数有这些参数类型。
Public Function PortExists( _
ByVal _ruleName As String, _
ByVal _protocol As NET_FW_IP_PROTOCOL_, _
ByVal _remotePorts As String, _
ByVal _direction As NET_FW_RULE_DIRECTION_ _
) As FW_ERROR_CODE
好的,为了简化我制作了自己的枚举类型(不需要安装额外的库)
Public Function PortExists( _
ByVal _ruleName As String, _
ByVal _protocol As FW_IP_PROTOCOL, _
ByVal _remotePorts As String, _
ByVal _direction As FW_RULE_DIRECTION _
) As FW_ERROR_CODE
不知何论,参数'6'和'1'应分别转换为FW_IP_PROTOCOL
和FW_RULE_DIRECTION
类型......
我不知道怎么做!
答案 0 :(得分:0)
通常一个好的设计会使Reflection变得多余。我的建议是让不同的对象实现一些定义要调用的函数的通用接口。
通用定义:
Public Enum MyValueEnum
value1 = 1
value2 = 2
End Enum
Public Enum MyErrorEnum
error1 = 1
error2 = 2
End Enum
Public Class SomeClass
Public GoodName As String
Public Function SomeFunction() As Object
Return Nothing
End Function
End Class
界面
Public Interface IMyInterface
Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
ByRef someObject As SomeClass) As MyErrorEnum
End Interface
实现接口的两个不同的类
Public Class SomeImplementation
Implements IMyInterface
Public Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
ByRef someObject As SomeClass) As MyErrorEnum _
Implements IMyInterface.TheFunction
'...
Return MyErrorEnum.error1
End Function
End Class
Public Class SomeOtherImplementation
Implements IMyInterface
Public Function TheFunction(ByVal val As MyValueEnum, ByVal name As String, _
ByRef someObject As SomeClass) As MyErrorEnum _
Implements IMyInterface.TheFunction
'...
Return MyErrorEnum.error2
End Function
End Class
测试
Dim obj As IMyInterface
Dim result As MyErrorEnum
obj = New SomeImplementation()
result = obj.TheFunction(MyValueEnum.value1, "test", New SomeClass())
obj = New SomeOtherImplementation()
result = obj.TheFunction(MyValueEnum.value2, "another test", New SomeClass())
答案 1 :(得分:0)
好的,这是解决方案。
当我们从反射中使用Invoke方法时,我们必须将参数作为对象(object())传递,并且类型完全匹配。 即使枚举类型的值是一个整数(这是我原来的问题),我必须将此值转换为引用程序集类型的确切枚举类型。所以以下函数&lt; _FEnumToObject&gt;会做这个工作。我添加了_F来推送列表底部的所有私有函数并将它们分组(IDE属性列表); _S代表sub和_P代表属性......
Private Function _FEnumToObject( _
ByVal _typeName As String, _
ByVal _typeValue As String _
) As Object
Dim enumType As Type = m_Assembly.[GetType](_typeName)
Dim enumInfo As FieldInfo = enumType.GetField(_typeValue)
Dim valueINT As Integer = CInt(enumInfo.GetValue(enumType))
Dim valueOBJ As Object = [Enum].ToObject(enumType, valueINT)
Return valueOBJ
End Function
下一个函数&lt; _FProtocolToObject&gt;用于将协议值(enumurated)转换为对象。
Public Function _FProtocolToObject(ByVal _protocol As FW_IP_PROTOCOL) As Object
'where <m_AssemblyRef> is like "wf7."
Return _FEnumToObject(m_AssemblyRef + "FW_IP_PROTOCOL", _protocol.ToString)
End Function
这是最后的决定:
Public Function PortExists( _
ByVal _ruleName As String, _
ByVal _protocol As FW_IP_PROTOCOL, _
ByVal _portNumber As Integer _
) As Boolean
If m_AssemblyNotFound Then Return False
Static mi As MethodInfo = m_RemoteType.GetMethod("PortExists")
Dim m As FW_ERROR_CODE = FW_ERROR_CODE.UNKNOWN
Try
Dim a As Object = New Object() _
{ _
_ruleName, _
_FProtocolToObject(_protocol), _
_portNumber _
}
m = mi.Invoke(m_RemoteObject, a)
Catch ex As Exception
'some error handler here...
End Try
Return (m = FW_ERROR_CODE.SUCCESS)
End Function
我还发布了一些额外的代码来在我的示例代码中提供一些无法解释/未设置的变量赋值。
Private ReadOnly Property _PAssemblyPath() As String
Get
Dim ad As String = IO.Path.Combine(My.Application.Info.DirectoryPath, "bin")
Dim af As String = IO.Path.Combine(ad, _PAssemblyName + ".dll")
Return af
End Get
End Property
Public Sub New()
m_AssemblyPath = _PAssemblyPath
m_AssemblyRef = _PAssemblyName + "."
m_ClassPath = m_AssemblyRef + _PClassName
If IO.File.Exists(m_AssemblyPath) Then
Dim name As New AssemblyName()
name.CodeBase = m_AssemblyPath
m_Assembly = AppDomain.CurrentDomain.Load(name)
m_RemoteObject = m_Assembly.CreateInstance(m_ClassPath)
m_RemoteType = m_Assembly.[GetType](m_ClassPath)
m_AssemblyNotFound = False
Else
'some error handler here
End If
End Sub
假设'SCSysInfo'是用于检查OS系统等的实用程序类......
Private ReadOnly Property _PClassName() As String
Get
Dim s As String = "CFwRule"
If SCSysInfo.IsWin7 Then
s += "7"
ElseIf SCSysInfo.IsWinVista Then
s += "Vista"
ElseIf SCSysInfo.IsWinXP Then
s += "XP"
End If
Return s
End Get
End Property
Private ReadOnly Property _PAssemblyName() As String
Get
Dim s As String = String.Empty
If SCSysInfo.IsWin7 Then
s = "fw7"
ElseIf SCSysInfo.IsWinVista Then
s = "fwv"
ElseIf SCSysInfo.IsWinXP Then
s = "fwxp"
End If
Return s
End Get
End Property
我的班级(上面部分列出)中的'PortExists'功能还有两个针对vista和win7的签名。有一点我必须提到反射不能识别方法/属性多重签名。使用反射时,您必须只有一个方法或属性的签名。谈到我的情况:win7,vista和xp为端口编程完全不同的属性。在每个程序集(我在各自的环境中编译)中,存在具有不同参数数量的“相同”方法和属性。从我的“适配器”类(其中一部分在上面发布)我从具有3个不同签名的所有3个程序集中调用“相同”方法(基于OS我选择适当的签名)。所以,我的所有3个“PortExists”方法的签名都在我的“适配器”类中。我希望我的解释有意义......谢谢,