简体   繁体   English

反射:如何调用将类对象作为输入的方法

[英]Reflection : How to invoke methods which takes a class object as input

I am using reflection to invoke a method from another dll.That method takes a class object as input. 我正在使用反射从另一个dll调用一个方法。该方法将一个类对象作为输入。 How can i call that method. 我怎么称呼这种方法。 I tried copying the same class to both dlls and i created an object of that class and passed it. 我尝试将同一个类复制到两个dll,然后创建了该类的对象并传递它。 It throws invliad conversion error in complie time itself. 它会在complie time本身中引发invliad转换错误。 Then i tried to let the function take an object as argument and then tried to cast it to the required class. 然后我试着让函数将一个对象作为参数,然后尝试将它转换为所需的类。 It is throwing invalid cast exception in runtime. 它在运行时抛出无效的强制转换异常。 this is what i tried 这是我试过的

            Test objTest = new Test("name","type");             
            Assembly assembly = Assembly.Load("MyProject.Components");             
            Type dllType = assembly.GetType("MynameSpace.MyClass");
            if (dllType != null)
            {
                MethodInfo m = dllType.GetMethod("MyFunction");
                object objdll;
                objdll = Activator.CreateInstance(dllType);
                object[] args = { objTest };
                if ((m != null))
                {                        
                    strReturnValue += (string)m.Invoke(objdll, args);                        
                }
            }

In the second dll the method is like 在第二个dll中,方法就像

public string MyFunction(Test obj)

My problem is that Class Test is in the other assembly(the classes are in two different assemblies) 我的问题是Class Test在另一个程序集中(这些类在两个不同的程序集中)

You have a bit of a design problem. 你有一点设计问题。 You have an assembly (let's call it assembly A) containing the sample reflection code that you posted. 您有一个程序集(让我们称之为程序集A),其中包含您发布的示例反射代码。 You also have a second assembly (let's call it assembly B) that contains MyClass and Test. 你还有一个包含MyClass和Test的第二个程序集(我们称之为程序集B)。

The issue is that in your reflection code you are attempting to create an instance of the Test class so that you can pass it as a parameter to MyClass.MyFunction. 问题是您在反射代码中尝试创建Test类的实例,以便将其作为参数传递给MyClass.MyFunction。

You mentioned that you copied the source code for the Test class into assembly A; 您提到您将Test类的源代码复制到程序集A中; that will not work. 那不管用。 What you've done there is essentially create two different classes with the same name and same structure. 你在那里做的基本上是创建两个具有相同名称和相同结构的不同类。 Since the two classes are not the same as far as the CLR is concerned, you will get an invalid cast exception if you try to cast one to the other. 由于这两个类就CLR而言并不相同,如果你试图将一个类转换为另一个,你将得到一个无效的强制转换异常。

Given what you've posted so far, it seems to me that the most straightforward solution for your approach is to have a third assembly (let's call it assembly C) that contains components that are known to both assemblies A and B. Create a class library project in your solution, move the Test class into that project, get rid of any other occurrences of the Test class in the first two projects, and add references in both of the first two projects referencing the new project. 鉴于您到目前为止发布的内容,在我看来,对您的方法最直接的解决方案是使用第三个程序集(让我们称之为程序集C),其中包含程序集A和B都知道的组件。创建一个类在您的解决方案中的库项目,将Test类移动到该项目中,在前两个项目中删除Test类的任何其他事件,并在引用新项目的前两个项目中添加引用。 Once you've done that, both assembly A and assembly B will be referencing the same class definition for the Test class and the sample code that you've posted will work. 完成后,程序集A和程序集B将引用Test类的相同类定义,并且您发布的示例代码将起作用。

Let me point out something, though. 但是,让我指出一些事情。 If the code in assembly A doesn't know enough about the code in assembly B in order to instantiate MyClass and call MyFunction directly (rather than through reflection), then how does it know enough about that code to know what parameters to pass? 如果程序集A中的代码对程序集B中的代码知之甚少,以便实例化MyClass并直接调用MyFunction(而不是通过反射),那么它如何充分了解该代码以了解要传递的参数? Does MyFunction have a common method signature that assembly A understands? MyFunction是否具有程序集A理解的通用方法签名? If that's the case, then MyClass should probably implement an interface that assembly A knows about so that assembly A can invoke MyFunction directly, as shown below: 如果是这种情况,那么MyClass应该实现程序集A知道的接口,以便程序集A可以直接调用MyFunction,如下所示:

        Assembly assembly = Assembly.Load("MyProject.Components");
        Type dllType = assembly.GetType("MynameSpace.MyClass");
        if (dllType != null)
        {
            IMyInterface instance = Activator.CreateInstance(dllType) as IMyInterface;
            if (instance != null) // check if this object actually implements the IMyInterface interface
            {
                instance.MyFunction(objTest);
            }
        }

If that doesn't seem like an approach that you want, then there are other options. 如果这看起来不像您想要的方法,那么还有其他选择。 Since it seems like you don't want for assembly A to have a direct reference to assembly B, if you keep the Test class inside of assembly B, then there isn't any way for assembly A to have any knowledge of the Test class in order to construct one. 由于您似乎不希望程序集A直接引用程序集B,如果将Test类保留在程序集B中,那么程序集A没有任何方法可以了解Test类的任何知识为了构建一个。 In that case, you could use a factory pattern approach, basically so that assembly A knows of some kind of factory object that is capable of instantiating a Test object. 在这种情况下,您可以使用工厂模式方法,基本上使组件A知道某种能够实例化Test对象的工厂对象。 Below is an example of an implementation: 以下是一个实现示例:

I mentioned above about creating a third project. 我上面提到过创建第三个项目。 I would still recommend doing so. 我仍然会建议这样做。 In my example, I've named mine "MyProject.Common". 在我的例子中,我将我的名字命名为“MyProject.Common”。 It contains the following code: 它包含以下代码:

// define a simple factory interface
public interface IFactory
{
    object CreateInstance();
}

// and a generic one (hey, why not?)
public interface IFactory<T> : IFactory
{
    new T CreateInstance();
}

// define a Factory attribute that will be used to identify the concrete implementation of a factory
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class FactoryAttribute : Attribute
{
    public Type FactoryType { get; set; }

    public FactoryAttribute(Type factoryType)
    {
        this.FactoryType = factoryType;
    }
}

The IFactory interfaces and the Factory attribute will be known and understood by the other projects in my solution, since they both reference the MyProject.Common project. 我的解决方案中的其他项目将知道并理解IFactory接口和Factory属性,因为它们都引用了MyProject.Common项目。

Below is the code contained within my "MyProject.Components" project: 下面是我的“MyProject.Components”项目中包含的代码:

public class Test
{
    public string Name { get; set; }

    public string Type { get; set; }

    public Test(string name, string type)
    {
        this.Name = name;
        this.Type = type;
    }
}

public class TestFactory : IFactory<Test>
{
    #region IFactory<Test> Members

    public Test CreateInstance()
    {
        return new Test("name", "type");
    }

    #endregion

    #region IFactory Members

    object IFactory.CreateInstance()
    {
        return this.CreateInstance();
    }

    #endregion
}


public class MyClass
{
    // the Factory attribute on the first parameter indicates that the class TestFactory
    // should be used as a factory object to construct the argument for this method
    public string MyFunction([Factory(typeof(TestFactory))]Test obj)
    {
        if (obj == null)
            return null;
        else
            return obj.ToString();
    }
}

Finally, I've replaced the original reflection code that you posted with the following: 最后,我用以下内容替换了您发布的原始反射代码:

        Assembly assembly = Assembly.Load("MyProject.Components");
        Type dllType = assembly.GetType("MynameSpace.MyClass");
        if (dllType != null)
        {
            MethodInfo m = dllType.GetMethod("MyFunction");
            object objdll;
            objdll = Activator.CreateInstance(dllType);

            // use the parameter information to construct the arguments
            ParameterInfo[] parameters = m.GetParameters();
            object[] args;
            if (parameters != null && parameters.Length > 0)
            {
                args = new object[parameters.Length];
                for (int i = 0; i < parameters.Length; i++)
                {
                    // check for factory attributes on the actual parameter
                    FactoryAttribute[] attributes = parameters[i].GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[];

                    // if no attributes were found, check the parameter type for factory attributes
                    if (attributes == null || attributes.Length == 0)
                        attributes = parameters[i].ParameterType.GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[];

                    // if no attributes were found still, then give up
                    if (attributes == null || attributes.Length == 0)
                    {
                        // this parameter has no factory specified,
                        // so how would this code know how to create the argument for that parameter ???
                        args[i] = null;
                        continue; // move on to the next parameter
                    }

                    // there should only be one factory attribute, so use the first one
                    // assumption made here is that all factory classes will have a parameterless constructor
                    IFactory factory = Activator.CreateInstance(attributes[0].FactoryType) as IFactory;

                    args[i] = factory.CreateInstance();
                }
            }
            else
                // there are no parameters
                args = null;


            if ((m != null))
            {
                strReturnValue += (string)m.Invoke(objdll, args);
            }
        }

If you mean by class object an object that is a Type object then you can just pass the type of the object as the parameter...eg 如果你的对象是一个Type对象的对象,那么你可以只传递对象的类型作为参数...例如

object[] args = {typeof(typeneeded)};

or 要么

object[] args = { assembly.GetType(typeneeded) };

MethodInfo.Invoke() is declared as the following: MethodInfo.Invoke()声明如下:

public Object Invoke(
    Object obj,
    Object[] parameters
)

The first parameter specifies the object to work on, the second one specifies the parameters for the function. 第一个参数指定要处理的对象,第二个参数指定函数的参数。

I replicated your code in LINQPad, and this works fine: 我在LINQPad中复制了你的代码,这很好用:

void Main()
{
    string strReturnValue = "";
    Test objTest = new Test("name","type");             
    Type dllType = typeof(MyClass);
    if (dllType != null)
    {
        MethodInfo m = dllType.GetMethod("MyFunction");
        object objdll;
        objdll = Activator.CreateInstance(dllType);
        object[] args = { objTest };
        if ((m != null))
        {                        
            strReturnValue += (string)m.Invoke(objdll, args);                        
        }
    }
}

public class Test
{
    public Test(string s1, string s2)
    {
    }
}

public class MyClass
{
    public string MyFunction(Test t)
    {
        return "";
    }
}

You will have to load the Test object the same way you load the MyClass instance, and as Test requires parameters in the constructor, you'll have to use a ConstructorInfo: 您必须以加载MyClass实例的方式加载Test对象,并且由于Test需要构造函数中的参数,您必须使用ConstructorInfo:

Assembly assembly = Assembly.Load(); //assembly of "Test"
Type testType = assembly.GetType("Test");
ConstructorInfo ci = testType.GetConstructor(
                              BindingFlags.Public | BindingFlags.Instance, 
                              null, 
                              new Type[]{typeof(string), typeof(string)}, 
                              null);
Test objTest = ci.Invoke(new object[] { "name", "type" });

now you can use objTest in the code. 现在你可以在代码中使用objTest

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM