web-dev-qa-db-fra.com

Réflexion: Comment invoquer une méthode avec des paramètres

J'essaie d'invoquer une méthode via une réflexion avec des paramètres et j'obtiens:

l'objet ne correspond pas au type de cible

Si j'appelle une méthode sans paramètre, cela fonctionne bien. Basé sur le code suivant si j'appelle la méthode Test("TestNoParameters"), cela fonctionne bien. Cependant, si j'appelle Test("Run"), j'obtiens une exception. Quelque chose ne va pas avec mon code?

Mon objectif initial était de transmettre un tableau d'objets, par exemple public void Run(object[] options) mais cela n'a pas fonctionné et j'ai essayé quelque chose de plus simple, par exemple. string sans succès.

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

Remplacez "methodInfo" par "classInstance", comme dans l'appel avec le tableau de paramètres null.

  result = methodInfo.Invoke(classInstance, parametersArray);
219
womp

Vous avez un bug juste là

result = methodInfo.Invoke(methodInfo, parametersArray);

cA devrait etre

result = methodInfo.Invoke(classInstance, parametersArray);
28
Oleg I.

Une erreur fondamentale est ici:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Vous appelez la méthode sur une instance de MethodInfo. Vous devez transmettre une instance du type d'objet sur lequel vous souhaitez appeler.

result = methodInfo.Invoke(classInstance, parametersArray);
23
jason

La solution fournie ne fonctionne pas pour les instances de types chargés à partir d'un assembly distant. Pour ce faire, voici une solution qui fonctionne dans toutes les situations et qui implique un remappage explicite de type du type renvoyé via l'appel CreateInstance.

Voici comment créer mon classInstance, car il se trouvait dans un assembly distant.

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

Cependant, même avec la réponse fournie ci-dessus, vous obtiendrez toujours la même erreur. Voici comment s'y prendre:

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

Ensuite, faites comme les autres utilisateurs mentionnés ici.

10
Martin Kool

Je voudrais l'utiliser comme ça, sa façon de faire est plus courte et cela ne posera aucun problème

        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);
        }
4
Nick N.
 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);
                    }
                }

            }
        }
2
M Fatih Koca

J'ai essayé de travailler avec toutes les réponses suggérées ci-dessus mais rien ne semble fonctionner pour moi. J'essaie donc d'expliquer ce qui a fonctionné pour moi ici.

Je crois que si vous appelez une méthode comme la Main ci-dessous ou même avec un paramètre unique, comme dans votre question, il vous suffit de changer le type de paramètre de string à object pour cela. travailler. J'ai une classe comme ci-dessous

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

    }
}

Ensuite, vous devez passer le parameterArray dans un tableau d'objets comme ci-dessous tout en l'invoquant. La méthode suivante est ce que vous devez travailler

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

Cette méthode facilite l’invocation de la méthode, elle peut être appelée comme suit

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

J'appelle la moyenne pondérée par réflexion. Et avait utilisé la méthode avec plus d'un paramètre.

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
1
Sachin Pete