반영 : 매개 변수를 사용하여 메소드를 호출하는 방법


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"}};
Nick Kovalsky

답변:


236

null 매개 변수 배열을 사용한 호출과 마찬가지로 "methodInfo"를 "classInstance"로 변경하십시오.

  result = methodInfo.Invoke(classInstance, parametersArray);

이것은 원격 어셈블리의 인스턴스로 작업 할 때를 제외하고 작동합니다. 문제는 그것이 도움이되지 않는 동일한 오류를 쏟았다는 것입니다. 나는 그것을 고치려고 몇 시간을 보냈고 여기에 제공된 사례와 사례에 새로운 일반 솔루션을 게시했습니다. 누군가가 필요할 수 있습니다 :)
Martin Kool

4
매개 변수가 여러 유형 인 경우 배열은 어떻게됩니까? 객체 배열 ??
Radu Vlad

예, 인수에 여러 유형이있는 경우 객체 여야합니다.
Martin Johansson

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

    }
}

그런 다음 호출하는 동안 아래와 같이 객체 배열 내부에 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"});

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이 답변을 게시합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.