Invoke member through reflection passing derived c

2019-09-17 08:54发布

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.

2条回答
孤傲高冷的网名
2楼-- · 2019-09-17 09:00

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
查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-09-17 09:20

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).

查看更多
登录 后发表回答