Reflection: Cách gọi Phương thức với các tham số


197

Tôi đang cố gắng gọi một phương thức thông qua sự phản chiếu với các tham số và tôi nhận được:

đối tượng không phù hợp với loại mục tiêu

Nếu tôi gọi một phương thức không có tham số, nó hoạt động tốt. Dựa trên đoạn mã sau nếu tôi gọi phương thức Test("TestNoParameters"), nó hoạt động tốt. Tuy nhiên nếu tôi gọi Test("Run"), tôi nhận được một ngoại lệ. Có gì đó không đúng với mã của tôi?

Mục đích ban đầu của tôi là để vượt qua một loạt các đối tượng, ví dụ như public void Run(object[] options)điều này không hoạt động và tôi đã thử một cái gì đó đơn giản hơn, ví dụ như chuỗi không thành công.

// 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
dòng chính xác sẽ là object [] tham sốArray = new object [] {new object [] {"Hello"}};
Nick Kovalsky

Câu trả lời:


236

Thay đổi "methodInfo" thành "classInstance", giống như trong cuộc gọi với mảng tham số null.

  result = methodInfo.Invoke(classInstance, parametersArray);

Điều này hoạt động, ngoại trừ khi làm việc với một phiên bản của một hội đồng từ xa. Vấn đề là nó đã phát sinh cùng một lỗi không hữu ích lắm. Tôi đã dành vài giờ để cố gắng sửa nó, và đăng một giải pháp chung mới cho cả trường hợp của tôi và giải pháp được cung cấp ở đây. Trong trường hợp bất cứ ai cũng có thể cần nó :)
Martin Kool

4
Nếu các tham số có nhiều loại, mảng sẽ như thế nào? một mảng các đối tượng ??
Radu Vlad

Có, nó phải là một đối tượng [] nếu có nhiều loại đối số
Martin Johansson

29

Bạn có một lỗi ngay đó

result = methodInfo.Invoke(methodInfo, parametersArray);

nó nên

result = methodInfo.Invoke(classInstance, parametersArray);

24

Một lỗi cơ bản là ở đây:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Bạn đang gọi phương thức trên một thể hiện của MethodInfo. Bạn cần phải vượt qua trong một thể hiện của loại đối tượng mà bạn muốn gọi.

result = methodInfo.Invoke(classInstance, parametersArray);

11

Giải pháp được cung cấp không hoạt động đối với các thể loại được tải từ một cụm từ xa. Để làm điều đó, đây là một giải pháp hoạt động trong mọi tình huống, bao gồm một ánh xạ lại kiểu rõ ràng của kiểu được trả về thông qua lệnh gọi CreatInstance.

Đây là cách tôi cần để tạo classInstance của mình, vì nó được đặt trong một cụm từ xa.

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

Tuy nhiên, ngay cả với câu trả lời được cung cấp ở trên, bạn vẫn sẽ gặp lỗi tương tự. Đây là cách đi về:

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

Sau đó làm như những người dùng khác được đề cập ở đây.


5

Tôi sẽ sử dụng nó như thế này, nó ngắn hơn và nó sẽ không gây ra vấn đề gì

        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

Tôi đã cố gắng làm việc với tất cả các câu trả lời được đề xuất ở trên nhưng dường như không có gì phù hợp với tôi. Vì vậy, tôi đang cố gắng giải thích những gì làm việc cho tôi ở đây.

Tôi tin rằng nếu bạn đang gọi một số phương thức như Maindưới đây hoặc thậm chí với một tham số duy nhất như trong câu hỏi của bạn, bạn chỉ cần thay đổi loại tham số từ stringđể objectđiều này hoạt động. Tôi có một lớp học như dưới đây

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

    }
}

Sau đó, bạn phải truyền tham sốArray bên trong một mảng đối tượng như bên dưới trong khi gọi nó. Phương pháp sau đây là những gì bạn cần để làm việc

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

Phương thức này giúp dễ dàng gọi phương thức, nó có thể được gọi như sau

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

1

Tôi đang gọi trung bình có trọng số thông qua sự phản ánh. Và đã sử dụng phương thức có nhiều hơn một tham số.

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

nếu nó không phải là bên ngoài (thay vì this.GetType(), bạn có thể sử dụng typeof(YourClass)).

ps đăng câu trả lời này vì nhiều khách truy cập vào đây cho câu trả lời này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.