简体   繁体   English

反思:如何使用参数调用方法

[英]Reflection: How to Invoke Method with parameters

I am trying to invoke a method via reflection with parameters and I get:我试图通过带参数的反射调用一个方法,我得到:

object does not match target type对象与目标类型不匹配

If I invoke a method without parameters, it works fine.如果我调用一个没有参数的方法,它工作正常。 Based on the following code if I call the method Test("TestNoParameters") , it works fine.根据以下代码,如果我调用方法Test("TestNoParameters") ,它工作正常。 However if I call Test("Run") , I get an exception.但是,如果我调用Test("Run") ,则会出现异常。 Is something wrong with my code?我的代码有问题吗?

My initial purpose was to pass an array of objects eg public void Run(object[] options) but this did not work and I tried something simpler eg string without success.我最初的目的是传递一个对象数组,例如public void Run(object[] options)但这不起作用,我尝试了一些更简单的东西,例如 string 没有成功。

// Assembly1.dll
namespace TestAssembly
{
    public class Main
    {
        public void Run(string parameters)
        { 
            // Do something... 
        }
        public void TestNoParameters()
        {
            // Do something... 
        }
    }
}

// Executing Assembly.exe
public class TestReflection
{
    public void Test(string methodName)
    {
        Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
        Type type = assembly.GetType("TestAssembly.Main");

        if (type != null)
        {
            MethodInfo methodInfo = type.GetMethod(methodName);

            if (methodInfo != null)
            {
                object result = null;
                ParameterInfo[] parameters = methodInfo.GetParameters();
                object classInstance = Activator.CreateInstance(type, null);

                if (parameters.Length == 0)
                {
                    // This works fine
                    result = methodInfo.Invoke(classInstance, null);
                }
                else
                {
                    object[] parametersArray = new object[] { "Hello" };

                    // The invoke does NOT work;
                    // it throws "Object does not match target type"             
                    result = methodInfo.Invoke(methodInfo, parametersArray);
                }
            }
        }
    }
}

将“methodInfo”更改为“classInstance”,就像在使用空参数数组的调用中一样。

  result = methodInfo.Invoke(classInstance, parametersArray);

You have a bug right there你有一个错误

result = methodInfo.Invoke(methodInfo, parametersArray);

it should be它应该是

result = methodInfo.Invoke(classInstance, parametersArray);

A fundamental mistake is here:一个根本性的错误在这里:

result = methodInfo.Invoke(methodInfo, parametersArray); 

You are invoking the method on an instance of MethodInfo .您正在调用MethodInfo实例上的方法。 You need to pass in an instance of the type of object that you want to invoke on.您需要传入要调用的对象类型的实例。

result = methodInfo.Invoke(classInstance, parametersArray);

The provided solution does not work for instances of types loaded from a remote assembly.提供的解决方案不适用于从远程程序集加载的类型实例。 To do that, here is a solution that works in all situations, which involves an explicit type re-mapping of the type returned through the CreateInstance call.为此,这里有一个适用于所有情况的解决方案,它涉及通过 CreateInstance 调用返回的类型的显式类型重新映射。

This is how I need to create my classInstance, as it was located in a remote assembly.这是我需要如何创建我的 classInstance,因为它位于远程程序集中。

// sample of my CreateInstance call with an explicit assembly reference
object classInstance = Activator.CreateInstance(assemblyName, type.FullName); 

However, even with the answer provided above, you'd still get the same error.但是,即使有了上面提供的答案,您仍然会遇到相同的错误。 Here is how to go about:以下是如何去做:

// first, create a handle instead of the actual object
ObjectHandle classInstanceHandle = Activator.CreateInstance(assemblyName, type.FullName);
// unwrap the real slim-shady
object classInstance = classInstanceHandle.Unwrap(); 
// re-map the type to that of the object we retrieved
type = classInstace.GetType(); 

Then do as the other users mentioned here.然后按照此处提到的其他用户进行操作。

I would use it like this, its way shorter and it won't give any problems我会像这样使用它,它的方式更短,不会出现任何问题

        dynamic result = null;
        if (methodInfo != null)
        {
            ParameterInfo[] parameters = methodInfo.GetParameters();
            object classInstance = Activator.CreateInstance(type, null);
            result = methodInfo.Invoke(classInstance, parameters.Length == 0 ? null : parametersArray);
        }

I tried to work with all the suggested answers above but nothing seems to work for me.我尝试使用上述所有建议的答案,但似乎对我没有任何作用。 So i am trying to explain what worked for me here.所以我试图解释什么对我有用。

I believe if you are calling some method like the Main below or even with a single parameter as in your question, you just have to change the type of parameter from string to object for this to work.我相信,如果您正在调用像下面的Main这样的方法,或者甚至在您的问题中使用单个参数,您只需将参数类型从string更改为object即可。 I have a class like below我有一个像下面这样的课程

//Assembly.dll
namespace TestAssembly{
    public class Main{

        public void Hello()
        { 
            var name = Console.ReadLine();
            Console.WriteLine("Hello() called");
            Console.WriteLine("Hello" + name + " at " + DateTime.Now);
        }

        public void Run(string parameters)
        { 
            Console.WriteLine("Run() called");
            Console.Write("You typed:"  + parameters);
        }

        public string TestNoParameters()
        {
            Console.WriteLine("TestNoParameters() called");
            return ("TestNoParameters() called");
        }

        public void Execute(object[] parameters)
        { 
            Console.WriteLine("Execute() called");
           Console.WriteLine("Number of parameters received: "  + parameters.Length);

           for(int i=0;i<parameters.Length;i++){
               Console.WriteLine(parameters[i]);
           }
        }

    }
}

Then you have to pass the parameterArray inside an object array like below while invoking it.然后,您必须在调用它时将 parameterArray 传递到如下所示的对象数组中。 The following method is what you need to work以下方法是您需要工作的

private void ExecuteWithReflection(string methodName,object parameterObject = null)
{
    Assembly assembly = Assembly.LoadFile("Assembly.dll");
    Type typeInstance = assembly.GetType("TestAssembly.Main");

    if (typeInstance != null)
    {
        MethodInfo methodInfo = typeInstance.GetMethod(methodName);
        ParameterInfo[] parameterInfo = methodInfo.GetParameters();
        object classInstance = Activator.CreateInstance(typeInstance, null);

        if (parameterInfo.Length == 0)
        {
            // there is no parameter we can call with 'null'
            var result = methodInfo.Invoke(classInstance, null);
        }
        else
        {
            var result = methodInfo.Invoke(classInstance,new object[] { parameterObject } );
        }
    }
}

This method makes it easy to invoke the method, it can be called as following这个方法可以很容易地调用方法,它可以被调用如下

ExecuteWithReflection("Hello");
ExecuteWithReflection("Run","Vinod");
ExecuteWithReflection("TestNoParameters");
ExecuteWithReflection("Execute",new object[]{"Vinod","Srivastav"});
 Assembly assembly = Assembly.LoadFile(@"....bin\Debug\TestCases.dll");
       //get all types
        var testTypes = from t in assembly.GetTypes()
                        let attributes = t.GetCustomAttributes(typeof(NUnit.Framework.TestFixtureAttribute), true)
                        where attributes != null && attributes.Length > 0
                        orderby t.Name
                        select t;

        foreach (var type in testTypes)
        {
            //get test method in types.
            var testMethods = from m in type.GetMethods()
                              let attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true)
                              where attributes != null && attributes.Length > 0
                              orderby m.Name
                              select m;

            foreach (var method in testMethods)
            {
                MethodInfo methodInfo = type.GetMethod(method.Name);

                if (methodInfo != null)
                {
                    object result = null;
                    ParameterInfo[] parameters = methodInfo.GetParameters();
                    object classInstance = Activator.CreateInstance(type, null);

                    if (parameters.Length == 0)
                    {
                        // This works fine
                        result = methodInfo.Invoke(classInstance, null);
                    }
                    else
                    {
                        object[] parametersArray = new object[] { "Hello" };

                        // The invoke does NOT work;
                        // it throws "Object does not match target type"             
                        result = methodInfo.Invoke(classInstance, parametersArray);
                    }
                }

            }
        }

I'am posting this answer because many visitors enter here from google for this problem.我发布这个答案是因为许多访问者从谷歌进入这里解决这个问题。


string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );

when external .dll -instead of this.GetType() , you might use typeof(YourClass) .当外部 .dll -而this.GetType() ,您可以使用typeof(YourClass)

I m invoking the weighted average through reflection.我正在通过反射调用加权平均值。 And had used method with more than one parameter.并且使用了不止一个参数的方法。

Class cls = Class.forName(propFile.getProperty(formulaTyp));// reading class name from file

Object weightedobj = cls.newInstance(); // invoke empty constructor

Class<?>[] paramTypes = { String.class, BigDecimal[].class, BigDecimal[].class }; // 3 parameter having first is method name and other two are values and their weight
Method printDogMethod = weightedobj.getClass().getMethod("applyFormula", paramTypes); // created the object 
return BigDecimal.valueOf((Double) printDogMethod.invoke(weightedobj, formulaTyp, decimalnumber, weight)); calling the method

On .Net 4.7.2 to invoke a method inside a class loaded from an external assembly you can use the following code in VB.net在 .Net 4.7.2 上调用从外部程序集加载的类中的方法,您可以在 VB.net 中使用以下代码

        Dim assembly As Reflection.Assembly = Nothing
        Try
            assembly = Reflection.Assembly.LoadFile(basePath & AssemblyFileName)
            Dim typeIni = assembly.[GetType](AssemblyNameSpace & "." & "nameOfClass")
            Dim iniClass = Activator.CreateInstance(typeIni, True)
            Dim methodInfo = typeIni.GetMethod("nameOfMethod")

            'replace nothing by a parameter array if you need to pass var. paramenters
            Dim parametersArray As Object() = New Object() {...}
            'without parameters is like this
            Dim result = methodInfo.Invoke(iniClass, Nothing)
        Catch ex As Exception
            MsgBox("Error initializing main layout:" & ex.Message)
            Application.Exit()
            Exit Sub
        End Try

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

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