Ioannis
Ioannis

Reputation: 3035

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. However if I call Test("Run"), I get an exception. Is something wrong with my code?

My initial purpose was to pass an array of objects e.g. public void Run(object[] options) but this did not work and I tried something simpler e.g. string without success.

// 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);
                }
            }
        }
    }
}

Upvotes: 247

Views: 506497

Answers (10)

Vinod Srivastav
Vinod Srivastav

Reputation: 4255

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 in the class 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.

I have created an example class like below

Example Assembly.dll

//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 static string StaticString()
        { 
                return "static string example";
        }     
        
        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. The following method is what you need to call with MethodName & parameterList to invoke the method from Assembly.dll above.

Invoker ExecuteMethod

private object ExecuteMethod(string methodName,object parameterObject = null)
{
    Assembly assembly = Assembly.LoadFile("Assembly.dll");
    Type typeInstance = assembly.GetType("TestAssembly.Main");
    MethodInfo methodInfo = typeInstance.GetMethod(methodName);
    ParameterInfo[] parameterInfo = methodInfo.GetParameters();

    object result = null;
    
    if (typeInstance != null) //non static
    {       
        if(methodInfo.IsStatic == false)
        {
            //instance is needed to invoke the method
            object classInstance = Activator.CreateInstance(typeInstance, null);

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

This method makes it easy to invoke the method, it can be called as following

Usage Example

ExecuteMethod("Hello");
ExecuteMethod("Run","Vinod");
ExecuteMethod("TestNoParameters");
ExecuteMethod("Execute",new object[]{"Vinod","Srivastav"});
ExecuteMethod("StaticString");

Upvotes: 9

T.Todua
T.Todua

Reputation: 56341

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

Upvotes: 8

Miguel Tom&#225;s
Miguel Tom&#225;s

Reputation: 1911

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

        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

Upvotes: 0

Sachin Pete
Sachin Pete

Reputation: 21

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

Upvotes: 1

M Fatih Koca
M Fatih Koca

Reputation: 601

 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);
                    }
                }

            }
        }

Upvotes: 4

Nick N.
Nick N.

Reputation: 13559

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);
        }

Upvotes: 6

Martin Kool
Martin Kool

Reputation: 4245

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.

This is how I need to create my classInstance, as it was located in a remote assembly.

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

Upvotes: 12

Oleg I.
Oleg I.

Reputation: 821

You have a bug right there

result = methodInfo.Invoke(methodInfo, parametersArray);

it should be

result = methodInfo.Invoke(classInstance, parametersArray);

Upvotes: 34

jason
jason

Reputation: 241583

A fundamental mistake is here:

result = methodInfo.Invoke(methodInfo, parametersArray); 

You are invoking the method on an instance of MethodInfo. You need to pass in an instance of the type of object that you want to invoke on.

result = methodInfo.Invoke(classInstance, parametersArray);

Upvotes: 27

womp
womp

Reputation: 116977

Change "methodInfo" to "classInstance", just like in the call with the null parameter array.

  result = methodInfo.Invoke(classInstance, parametersArray);

Upvotes: 273

Related Questions