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

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);
WOMP
источник
Это работает, кроме случаев работы с экземпляром удаленной сборки. Проблема заключалась в том, что она выдает ту же ошибку, которая не очень помогает. Я потратил несколько часов, пытаясь это исправить, и опубликовал новое общее решение для моего случая и представленное здесь. На случай, если кому-то это понадобится :)
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 публикует этот ответ, потому что многие посетители заходят сюда для этого ответа.

T.Todua
источник