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

C# 反射:如何调用以类对象作为输入的方法,c#,reflection,C#,Reflection,我正在使用反射从另一个dll调用一个方法。该方法将类对象作为输入。我怎样才能调用该方法。 我尝试将同一个类复制到两个DLL,并创建了该类的对象并将其传递。它在complie time本身中抛出invliad转换错误。 然后我尝试让函数接受一个对象作为参数,然后尝试将它转换为所需的类。它正在运行时引发无效的强制转换异常。 这就是我试过的 Test objTest = new Test("name","type"); Asse

我正在使用反射从另一个dll调用一个方法。该方法将类对象作为输入。我怎样才能调用该方法。 我尝试将同一个类复制到两个DLL,并创建了该类的对象并将其传递。它在complie time本身中抛出invliad转换错误。 然后我尝试让函数接受一个对象作为参数,然后尝试将它转换为所需的类。它正在运行时引发无效的强制转换异常。 这就是我试过的

            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);                        
                }
            }
在第二个dll中,方法如下

public string MyFunction(Test obj)

我的问题是班级考试在进行中 另一个程序集(类位于 两个不同的组件)


如果你所说的class object是一个
类型的对象,那么你可以把对象的类型作为参数传递…例如

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

MethodInfo.Invoke()
声明如下:

public Object Invoke(
    Object obj,
    Object[] parameters
)
        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);
            }
        }
第一个参数指定要处理的对象,第二个参数指定函数的参数

我在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 "";
    }
}
您必须以与加载MyClass实例相同的方式加载测试对象,并且由于测试需要构造函数中的参数,您必须使用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" });

现在您可以在代码中使用
objTest

您有一点设计问题。您有一个程序集(我们称之为程序集A),其中包含您发布的示例反射代码。您还有第二个程序集(我们称之为程序集B),其中包含MyClass和Test

问题在于,在反射代码中,您试图创建测试类的实例,以便将其作为参数传递给MyClass.MyFunction

您提到您将测试类的源代码复制到程序集A中;那是行不通的。实际上,您所做的是创建两个具有相同名称和相同结构的不同类。由于就CLR而言,这两个类并不相同,因此如果您尝试将一个类强制转换为另一个类,则会得到无效的强制转换异常

根据您目前发布的内容,在我看来,您的方法最直接的解决方案是使用第三个程序集(我们称之为程序集C),其中包含程序集a和B都已知的组件。在解决方案中创建类库项目,将测试类移到该项目中,去掉前两个项目中出现的任何其他测试类,并在引用新项目的前两个项目中添加引用。完成此操作后,程序集A和程序集B都将引用测试类的相同类定义,并且您发布的示例代码将起作用

不过,让我指出一点。如果程序集A中的代码对程序集B中的代码了解不够,无法实例化MyClass并直接(而不是通过反射)调用MyFunction,那么它如何对该代码了解足够,从而知道要传递哪些参数?MyFunction是否有程序集a理解的公共方法签名?如果是这种情况,那么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);
            }
        }
如果这似乎不是您想要的方法,那么还有其他选择。由于您似乎不希望程序集A直接引用程序集B,因此如果您将测试类保留在程序集B中,那么程序集A就无法了解测试类以构造测试类。在这种情况下,您可以使用工厂模式方法,基本上使程序集a知道某种能够实例化测试对象的工厂对象。下面是一个实现示例:

我在上面提到了创建第三个项目。我仍然建议这样做。在我的示例中,我将我的命名为“MyProject.Common”。它包含以下代码:

// 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;
    }
}
//定义一个简单的工厂接口
公共接口工厂
{
对象CreateInstance();
}
//还有一个通用的(嘿,为什么不呢?)
公共接口IFactory:IFactory
{
新的T CreateInstance();
}
//定义工厂属性,该属性将用于标识工厂的具体实现
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Parameter,AllowMultiple=false,Inherited=true)]
公共类FactoryAttribute:属性
{
公共类型FactoryType{get;set;}
公共FactoryAttribute(类型factoryType)
{
this.FactoryType=FactoryType;
}
}
IFactory接口和Factory属性将被我的解决方案中的其他项目所了解和理解,因为它们都引用MyProject.Common项目

下面是我的“MyProject.Components”项目中包含的代码:

公共类测试
{
公共字符串名称{get;set;}
公共字符串类型{get;set;}
公共测试(字符串名称、字符串类型)
{
this.Name=Name;
this.Type=Type;
}
}
公共类测试工厂:IFactory
{
#区域工业委员会成员
公共测试实例()
{
返回新测试(“名称”、“类型”);
}
#端区
#区域工业委员会成员
对象IFactory.CreateInstance()
{
返回这个.CreateInstance();
}
#端区
}
公共类MyClass
{
//第一个参数上的Factory属性表示类TestFactory
//应用作工厂对象来构造此方法的参数
公共字符串MyFunction([Factory(typeof(TestFactory))]testobj)
{
if(obj==null)
返回null;
其他的
返回obj.ToString();
}
}
最后,我将您发布的原始反射代码替换为以下代码:

public Object Invoke(
    Object obj,
    Object[] parameters
)
        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);
            }
        }
Assembly=Assembly。