Invoke member through reflection passing derived class as argument

大城市里の小女人 提交于 2019-12-25 06:00:38

问题


I'm trying to call the method 'MyMethod' of class 'CMyClass'. This method has a parameter of type "CBaseClass", and I'm passing an object of type "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

The argument exception says "object of type 'MyNamespace.CDerivedClass' cannot be converted to type 'MyNamespace.CBaseClass'.

I changed "ByRef" to "ByVal" in MyMethod signature, but nothing changed. I tried to change type of 'par' object with:

Dim par As CBaseClass = New CDerivedClass()

without success. How I can invoke correctly the method "MyMethod" with an instance of derived class? Thank you very much.


回答1:


I just tried your code in a stand alone project and it worked fine. I only adjusted the assembly line to get the current assembly instead of from a file, and I changed the name in GetType to reflect my namespace. My guess is that perhaps your DLL you are loading is out of date, or you have duplicate definitions of the classes in your calling assembly, and thats why it cannot do the type conversion.

        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



回答2:


Finally I solved using serialization...

So 'par' is the string containing the serialized object of type CDerivedClass in the calling project.

MyMethod is changed to:

MyMethod(xml_CBaseClass As String)

In dll project the string parameter xml_CBaseClass is deserialized creating an object of CBaseClass.

Note: since I have derived type, Deserialization of derived class give another problem. The solution is https://stackoverflow.com/a/590711/1315873 (I just made a little change, using StringWriter for serialization, StringReader for deserialization, instead of using MemoryBuffer).

CBaseClass has fixed derived types so I wrote them hard-coded, but to be flexible you can do something like:

    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 and all its derivated classes must have constructor New() without parameters.

I load assemblies using LoadFrom() since I don't know their names (I use Dir() to get all them from a known fixed folder).



来源:https://stackoverflow.com/questions/30380302/invoke-member-through-reflection-passing-derived-class-as-argument

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!