Reflexión: cómo invocar el método con parámetros


197

Estoy tratando de invocar un método mediante reflexión con parámetros y obtengo:

el objeto no coincide con el tipo de destino

Si invoco un método sin parámetros, funciona bien. Basado en el siguiente código si llamo al método Test("TestNoParameters"), funciona bien. Sin embargo, si llamo Test("Run"), recibo una excepción. ¿Hay algún problema con mi código?

Mi propósito inicial era pasar una serie de objetos, por ejemplo, public void Run(object[] options)pero esto no funcionó y probé algo más simple, por ejemplo, una cadena sin éxito.

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

44
la línea correcta sería object [] parameterArray = new object [] {new object [] {"Hello"}};
Nick Kovalsky

Respuestas:


236

Cambie "methodInfo" a "classInstance", al igual que en la llamada con la matriz de parámetros nulos.

  result = methodInfo.Invoke(classInstance, parametersArray);

Esto funciona, excepto cuando se trabaja con una instancia de un ensamblaje remoto. El problema fue que derrama el mismo error que no es muy útil. Pasé varias horas tratando de solucionarlo, y publiqué una nueva solución general tanto para mi caso como para el que se proporciona aquí. En caso de que alguien pueda necesitarlo :)
Martin Kool

44
Si los parámetros son de varios tipos, ¿cómo debería ser la matriz? una serie de objetos ??
Radu Vlad

Sí, debería ser un objeto [] si hay varios tipos de argumentos
Martin Johansson

29

Tienes un error ahí mismo

result = methodInfo.Invoke(methodInfo, parametersArray);

debería ser

result = methodInfo.Invoke(classInstance, parametersArray);

24

Un error fundamental está aquí:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Estás invocando el método en una instancia de MethodInfo. Debe pasar una instancia del tipo de objeto sobre el que desea invocar.

result = methodInfo.Invoke(classInstance, parametersArray);

11

La solución proporcionada no funciona para instancias de tipos cargados desde un ensamblaje remoto. Para hacer eso, aquí hay una solución que funciona en todas las situaciones, que implica una reasignación de tipo explícito del tipo devuelto a través de la llamada CreateInstance.

Así es como necesito crear mi classInstance, ya que se encuentra en un ensamblado remoto.

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

Sin embargo, incluso con la respuesta proporcionada anteriormente, aún obtendría el mismo error. Aquí es cómo hacerlo:

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

Luego haz lo que los otros usuarios mencionados aquí.


5

Lo usaría así, es mucho más corto y no dará ningún problema

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

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

            }
        }

3

Traté de trabajar con todas las respuestas sugeridas anteriormente, pero nada parece funcionar para mí. Así que estoy tratando de explicar lo que funcionó para mí aquí.

Creo que si está llamando a algún método como el Mainsiguiente o incluso con un solo parámetro como en su pregunta, solo tiene que cambiar el tipo de parámetro de stringa objectpara que esto funcione. Tengo una clase como abajo

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

    }
}

Luego debe pasar el parámetroArray dentro de una matriz de objetos como a continuación mientras lo invoca. El siguiente método es lo que necesita para trabajar.

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

Este método facilita la invocación del método, se puede llamar de la siguiente manera

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

1

Invoco el promedio ponderado a través de la reflexión. Y había utilizado el método con más de un parámetro.

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

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

si no es .dll externo (en lugar de this.GetType(), puede usartypeof(YourClass) ).

ps publicando esta respuesta porque muchos visitantes ingresan aquí para esta respuesta.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.