Réflexion: comment appeler la méthode avec des paramètres

197

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

l'objet ne correspond pas au type cible

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

Mon objectif initial était de passer 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 une chaîne 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);
                }
            }
        }
    }
}
Ioannis
la source
4
la ligne correcte serait object [] parametersArray = new object [] {new object [] {"Hello"}};
Nick Kovalsky

Réponses:

236

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

  result = methodInfo.Invoke(classInstance, parametersArray);
womp
la source
Cela fonctionne, sauf lorsque vous travaillez avec une instance d'un assembly distant. Le problème était qu'il renverse la même erreur qui n'est pas très utile. J'ai passé plusieurs heures à essayer de le réparer et j'ai publié une nouvelle solution générale à la fois pour mon cas et pour celle fournie ici. Au cas où quelqu'un en aurait besoin :)
Martin Kool
4
si les paramètres sont de plusieurs types, à quoi devrait ressembler le tableau? un tableau d'objets ??
Radu Vlad
Oui, ce devrait être un objet [] s'il y a plusieurs types d'arguments
Martin Johansson
29

Vous avez un bug ici

result = methodInfo.Invoke(methodInfo, parametersArray);

ça devrait être

result = methodInfo.Invoke(classInstance, parametersArray);
Oleg I.
la source
24

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 que vous souhaitez invoquer.

result = methodInfo.Invoke(classInstance, parametersArray);
Jason
la source
11

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

C'est ainsi que je dois créer mon classInstance, car il était situé 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 obtiendriez 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.

Martin Kool
la source
5

Je l'utiliserais comme ça, son chemin est plus court 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);
        }
Nick N.
la source
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);
                    }
                }

            }
        }
M Fatih Koca
la source
3

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 Mainci - dessous ou même avec un seul paramètre comme dans votre question, il vous suffit de changer le type de paramètre de stringà objectpour que cela fonctionne. 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 paramètreArray à l'intérieur d'un tableau d'objets comme ci-dessous tout en l'invoquant. La méthode suivante est ce dont vous avez besoin pour 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 permet d'appeler facilement la méthode, elle peut être appelée comme suit

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

J'invoque 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
Sachin Pete
la source
0
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );

s'il n'est pas externe .dll (au lieu de this.GetType(), vous pouvez utilisertypeof(YourClass) ).

ps poster cette réponse car de nombreux visiteurs entrent ici pour cette réponse.

T.Todua
la source