Reflexion: Aufrufen der Methode mit Parametern

197

Ich versuche, eine Methode durch Reflektion mit Parametern aufzurufen und erhalte:

Objekt stimmt nicht mit Zieltyp überein

Wenn ich eine Methode ohne Parameter aufrufe, funktioniert sie einwandfrei. Basierend auf dem folgenden Code Test("TestNoParameters")funktioniert es einwandfrei, wenn ich die Methode aufrufe. Wenn ich jedoch anrufe Test("Run"), erhalte ich eine Ausnahme. Stimmt etwas mit meinem Code nicht?

Mein ursprünglicher Zweck war es, eine Reihe von Objekten zu übergeben, z. B. public void Run(object[] options)aber dies funktionierte nicht und ich versuchte etwas Einfacheres, z. B. eine Zeichenfolge, ohne Erfolg.

// 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
quelle
4
die richtige Zeile wäre object [] parametersArray = new object [] {new object [] {"Hello"}};
Nick Kovalsky

Antworten:

236

Ändern Sie "methodInfo" in "classInstance", genau wie beim Aufruf mit dem Null-Parameter-Array.

  result = methodInfo.Invoke(classInstance, parametersArray);
womp
quelle
Dies funktioniert, außer wenn Sie mit einer Instanz einer Remote-Assembly arbeiten. Das Problem war, dass derselbe Fehler auftritt, der nicht sehr hilfreich ist. Ich habe mehrere Stunden damit verbracht, das Problem zu beheben, und eine neue allgemeine Lösung für meinen und den hier bereitgestellten Fall veröffentlicht. Für den Fall, dass jemand es braucht :)
Martin Kool
4
Wenn die Parameter von mehreren Typen sind, wie sollte das Array aussehen? eine Reihe von Objekten?
Radu Vlad
Ja, es sollte ein Objekt [] sein, wenn es mehrere Arten von Argumenten gibt
Martin Johansson
29

Sie haben genau dort einen Fehler

result = methodInfo.Invoke(methodInfo, parametersArray);

es sollte sein

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

Ein grundlegender Fehler ist hier:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Sie rufen die Methode für eine Instanz von auf MethodInfo. Sie müssen eine Instanz des Objekttyps übergeben, für den Sie aufrufen möchten.

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

Die bereitgestellte Lösung funktioniert nicht für Instanzen von Typen, die von einer Remote-Assembly geladen wurden. Zu diesem Zweck finden Sie hier eine Lösung, die in allen Situationen funktioniert. Dazu gehört eine explizite Neuzuordnung des Typs des Typs, der über den Aufruf von CreateInstance zurückgegeben wird.

Auf diese Weise muss ich meine classInstance erstellen, da sie sich in einer Remote-Assembly befand.

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

Selbst mit der oben angegebenen Antwort wird jedoch immer noch der gleiche Fehler angezeigt. So geht's:

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

Dann machen Sie es wie die anderen hier genannten Benutzer.

Martin Kool
quelle
5

Ich würde es so benutzen, es ist viel kürzer und es wird keine Probleme geben

        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.
quelle
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
quelle
3

Ich habe versucht, mit allen oben vorgeschlagenen Antworten zu arbeiten, aber nichts scheint für mich zu funktionieren. Also versuche ich zu erklären, was hier für mich funktioniert hat.

Ich glaube, wenn Sie eine Methode wie die Mainfolgende oder sogar einen einzelnen Parameter wie in Ihrer Frage aufrufen , müssen Sie nur den Parametertyp von stringauf ändern , objectdamit dies funktioniert. Ich habe eine Klasse wie unten

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

    }
}

Dann müssen Sie den ParameterArray in einem Objektarray wie unten übergeben, während Sie es aufrufen. Die folgende Methode ist das, was Sie zum Arbeiten benötigen

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

Diese Methode erleichtert das Aufrufen der Methode und kann wie folgt aufgerufen werden

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

Ich rufe den gewichteten Durchschnitt durch Reflexion auf. Und hatte Methode mit mehr als einem Parameter verwendet.

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

wenn es keine externe DLL ist (statt this.GetType(), könnten Sie verwenden typeof(YourClass)).

ps poste diese Antwort, weil viele Besucher hier für diese Antwort eingeben.

T.Todua
quelle