Отражение: как вызвать метод с параметрами


197

Я пытаюсь вызвать метод через отражение с параметрами, и я получаю:

объект не соответствует типу цели

Если я вызываю метод без параметров, он работает нормально. Исходя из следующего кода, если я вызываю метод Test("TestNoParameters"), он работает нормально. Однако если я позвонюTest("Run") , я получу исключение. Что-то не так с моим кодом?

Моей первоначальной целью было передать массив объектов, например, public void Run(object[] options)но это не сработало, и я попытался что-то более простое, например, строка без успеха.

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

4
правильная строка будет object [] parametersArray = new object [] {new object [] {"Hello"}};
Ник Ковальский

Ответы:


236

Измените "methodInfo" на "classInstance", как в вызове с массивом нулевых параметров.

  result = methodInfo.Invoke(classInstance, parametersArray);

Это работает, кроме случаев работы с экземпляром удаленной сборки. Проблема заключалась в том, что она выдает ту же ошибку, которая не очень помогает. Я потратил несколько часов, пытаясь это исправить, и опубликовал новое общее решение для моего случая и представленное здесь. На случай, если кому-то это понадобится :)
Martin Kool

4
если параметры имеют несколько типов, каким должен быть массив? массив объектов ??
Раду Влад

Да, это должен быть объект [], если в аргументах несколько типов
Мартин Йоханссон

29

У вас есть ошибка прямо там

result = methodInfo.Invoke(methodInfo, parametersArray);

так должно быть

result = methodInfo.Invoke(classInstance, parametersArray);

24

Основная ошибка здесь:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Вы вызываете метод для экземпляра MethodInfo. Вам нужно передать экземпляр типа объекта, который вы хотите вызвать.

result = methodInfo.Invoke(classInstance, parametersArray);

11

Предоставленное решение не работает для экземпляров типов, загруженных из удаленной сборки. Чтобы сделать это, вот решение, которое работает во всех ситуациях, которое включает в себя явное переопределение типа, возвращаемого через вызов CreateInstance.

Вот как мне нужно создать свой classInstance, так как он был расположен в удаленной сборке.

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

Однако даже с ответом, приведенным выше, вы все равно получите ту же ошибку. Вот как это сделать:

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

Затем сделайте так, как другие пользователи, упомянутые здесь.


5

Я бы использовал его так, он короче, и это не доставит никаких проблем.

        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

Я пытался работать со всеми предложенными ответами выше, но мне ничего не помогло. Поэтому я пытаюсь объяснить, что сработало для меня здесь.

Я считаю, что если вы вызываете какой-либо метод, подобный приведенному Mainниже, или даже с одним параметром, как в вашем вопросе, вам просто нужно изменить тип параметра с stringна objectна, чтобы это работало. У меня есть класс, как показано ниже

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

    }
}

Затем вы должны передать параметр Array в массив объектов, как показано ниже, при его вызове. Следующий метод - это то, что вам нужно для работы

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

Этот метод облегчает вызов метода, его можно вызвать следующим образом

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

1

Я вызываю средневзвешенное значение через отражение. И использовал метод с более чем одним параметром.

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

если это не внешний .dll (вместо this.GetType(), вы можете использоватьtypeof(YourClass) ).

ps публикует этот ответ, потому что многие посетители заходят сюда для этого ответа.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.