【问题标题】:Invoke member through reflection passing derived class as argument通过将派生类作为参数传递的反射调用成员
【发布时间】:2015-05-21 17:15:03
【问题描述】:

我正在尝试调用类“CMyClass”的方法“MyMethod”。这个方法有一个“CBaseClass”类型的参数,我正在传递一个“CDerivedClass”类型的对象。

Class CBaseClass
    Public m_AMember As String
    Sub MethodOne()
        // DoSomething
    End Sub
End Class

Class CDerivedClass
    Inherits CBaseClass
    Public m_MyMember As Integer
    Sub MethodTwo()
        // DoSomething
    End Sub
End Class

Class CMyClass
    Sub MyMethod(ByVal obj As CBaseClass)
        // DoSomething
    End Sub
End Class

Sub Main()
    'load assembly
    Dim objAssembly As Assembly = Assembly.LoadFrom("myfile.dll")

    'create class instance and MethodInfo object
    Dim t As Type = objAssembly.GetType("MyNamespace.CMyClass")
    Dim obj As Object = Activator.CreateInstance(t)
    Debug.Assert(obj IsNot Nothing)
    Dim m As MethodInfo = t.GetMethod("MyMethod")
    Debug.Assert(m IsNot Nothing)

    'Init arguments (only one)
    Dim par As New CDerivedClass()
    Dim parameters As Object() = New Object(0) {par}

    'invoke method
    m.Invoke(obj, parameters) '<<<< ArgumentException here!
End Sub

参数异常说“'MyNamespace.CDerivedClass' 类型的对象无法转换为'MyNamespace.CBaseClass' 类型。

我在 MyMethod 签名中将“ByRef”更改为“ByVal”,但没有任何改变。 我尝试使用以下方法更改“par”对象的类型:

Dim par As CBaseClass = New CDerivedClass()

没有成功。 如何使用派生类的实例正确调用方法“MyMethod”? 非常感谢。

【问题讨论】:

  • 您可能正在与加载上下文作斗争,请尝试使用 Load() 而不是 LoadFrom()。
  • 也许你是对的?字节数组没有任何变化,但是使用 Load("ConsoleApplication1") 它运行(来自@Kratz 答案的来源)...但是在我的原始项目中,dll 位于相对于 exe 的另一条路径中,它没有找到它...我在互联网上搜索过,但我不知道如何使用 Load(AssemblyName)。我在某个路径中有很多 dll(例如“c:\my_dlls”),而我的调用项目在另一个路径中(例如“c:\myProjects\MyCallingProject\bin\debug\MyCallingProject.exe)。
  • Hmya,故意调用 DLL Hell 是一个非常严重的错误。使用 AppDomain.AssemblyResolve 事件帮助 CLR 查找存储在奇怪位置的 DLL。
  • 在我的项目中,路径中有 N 个库。我不知道他们的名字,也不知道他们的 AssemblyNames 的信息。在加载程序集的指令之前,我有一个 for 循环读取在指定的库路径中找到的所有文件。

标签: vb.net reflection invoke


【解决方案1】:

我刚刚在一个独立项目中尝试了您的代码,它运行良好。我只调整了装配线以获取当前程序集而不是从文件中获取,并且我更改了 GetType 中的名称以反映我的命名空间。我的猜测是,您正在加载的 DLL 可能已过期,或者您的调用程序集中的类定义重复,这就是它无法进行类型转换的原因。

        Imports System.Reflection

    Module Module1

        Sub Main()
            'load assembly
            Dim objAssembly As Assembly = Assembly.GetExecutingAssembly()

            'create class instance and MethodInfo object
            Dim t As Type = objAssembly.GetType("ConsoleApplication1.CMyClass")
            Dim obj As Object = Activator.CreateInstance(t)
            Debug.Assert(obj IsNot Nothing)
            Dim m As MethodInfo = t.GetMethod("MyMethod")
            Debug.Assert(m IsNot Nothing)

            'Init arguments (only one)
            Dim par As New CDerivedClass()
            Dim parameters As Object() = New Object(0) {par}

            'invoke method
            m.Invoke(obj, parameters) '<<<< No ArgumentException here!
        End Sub

    End Module

    Class CBaseClass
        Public m_AMember As String
        Sub MethodOne()

        End Sub
    End Class

    Class CDerivedClass
        Inherits CBaseClass
        Public m_MyMember As Integer
        Sub MethodTwo()

        End Sub
    End Class

    Class CMyClass
        Sub MyMethod(ByVal obj As CBaseClass)

        End Sub
    End Class

【讨论】:

  • 感谢您试用我的代码。我试过你的,没关系,因为它使用相同的程序集。从那个项目开始,我在类定义中添加了“public”,从“myfile.exe”更改了程序集初始化加载。我编译了项目并将 ConsoleApplication1.exe 的副本重命名为“myfile.exe”。启动应用程序我有相同的 ArgumentException :(
  • 出于好奇,如果您将参数更改为从 Dim par As Object = Activator.CreateInstance(objAssembly.GetType("MyNamespace.CDerivedClass")) 之类的同一个程序集中加载会怎样?
  • 它有效:'(所以它混淆了调用项目的CDerivedClass和调用DLL的CDerivedClass(即使它是两个项目之间共享的同一个文件并且命名空间相同)?
【解决方案2】:

最后我用序列化解决了...

所以'par'是调用项目中包含CDerivedClass类型的序列化对象的字符串。

MyMethod 改为:

MyMethod(xml_CBaseClass As String)

在 dll 项目中,字符串参数 xml_CBaseClass 被反序列化创建一个 CBaseClass 对象。

注意:由于我有派生类型,派生类的反序列化会带来另一个问题。解决方案是https://stackoverflow.com/a/590711/1315873 (我只是做了一点改动,使用StringWriter进行序列化,使用StringReader进行反序列化,而不是使用MemoryBuffer)。

CBaseClass 有固定的派生类型,所以我将它们写成硬编码,但为了灵活,您可以执行以下操作:

    Dim subTypes as New List(Of Type) '<- all classes derived from 'myType'
    For Each t In myType.Assembly.GetTypes()
        If t.IsSubclassOf(myType) Then
            subTypes.Add(t)
        End If
    Next

CBaseClass 及其所有派生类必须具有不带参数的构造函数 New()。

我使用 LoadFrom() 加载程序集,因为我不知道它们的名称(我使用 Dir() 从已知的固定文件夹中获取所有它们)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多