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


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

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

если я вызываю метод без параметров, он отлично работает. На основе следующего кода, если я вызываю метод 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);
                }
            }
        }
    }
}
7   167   2010-02-04 22:02:49

7 ответов:

измените " methodInfo "на" classInstance", как и в вызове с массивом параметров null.

  result = methodInfo.Invoke(classInstance, parametersArray);

у вас ошибка тут

result = methodInfo.Invoke(methodInfo, parametersArray);

он должен быть!--3-->

result = methodInfo.Invoke(classInstance, parametersArray);

фундаментальная ошибка здесь:

result = methodInfo.Invoke(methodInfo, parametersArray); 

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

result = methodInfo.Invoke(classInstance, parametersArray);

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

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

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

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

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

Я верю, что если вы вызываете какой-то метод, как 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]);
           }
        }

    }
}

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

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

            }
        }