Refleksi: Cara Meminta Metode dengan parameter

197

Saya mencoba untuk memanggil metode melalui refleksi dengan parameter dan saya mendapatkan:

objek tidak cocok dengan tipe target

Jika saya memanggil metode tanpa parameter, itu berfungsi dengan baik. Berdasarkan kode berikut jika saya memanggil metode Test("TestNoParameters"), itu berfungsi dengan baik. Namun jika saya menelepon Test("Run"), saya mendapat pengecualian. Apakah ada yang salah dengan kode saya?

Tujuan awal saya adalah untuk melewatkan array objek misalnya public void Run(object[] options)tetapi ini tidak berhasil dan saya mencoba sesuatu yang lebih sederhana mis. String tanpa keberhasilan.

// 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);
                }
            }
        }
    }
}
Ioannis
sumber
4
baris yang benar adalah objek [] parameterArray = objek baru [] {objek baru [] {"Hello"}};
Nick Kovalsky

Jawaban:

236

Ubah "methodInfo" menjadi "classInstance", sama seperti dalam panggilan dengan array parameter nol.

  result = methodInfo.Invoke(classInstance, parametersArray);
womp
sumber
Ini berfungsi, kecuali saat bekerja dengan instance dari perakitan jarak jauh. Masalahnya adalah ia mengeluarkan kesalahan yang sama yang tidak terlalu membantu. Saya menghabiskan beberapa jam mencoba untuk memperbaikinya, dan memposting solusi umum baru untuk kasus saya dan yang disediakan di sini. Jika ada yang mungkin membutuhkannya :)
Martin Kool
4
jika parameternya terdiri dari beberapa tipe, seperti apa array itu? berbagai benda ??
Radu Vlad
Ya, itu harus menjadi objek [] jika ada beberapa jenis argumen
Martin Johansson
29

Anda memiliki bug di sana

result = methodInfo.Invoke(methodInfo, parametersArray);

harus

result = methodInfo.Invoke(classInstance, parametersArray);
Oleg I.
sumber
24

Kesalahan mendasar ada di sini:

result = methodInfo.Invoke(methodInfo, parametersArray); 

Anda memohon metode ini pada contoh dari MethodInfo. Anda harus memberikan contoh jenis objek yang ingin Anda gunakan.

result = methodInfo.Invoke(classInstance, parametersArray);
jason
sumber
11

Solusi yang disediakan tidak berfungsi untuk contoh jenis yang dimuat dari rakitan jarak jauh. Untuk melakukan itu, berikut adalah solusi yang berfungsi di semua situasi, yang melibatkan pemetaan ulang tipe eksplisit dari jenis yang dikembalikan melalui panggilan CreateInstance.

Ini adalah bagaimana saya perlu membuat classInstance saya, karena terletak di perakitan jarak jauh.

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

Namun, bahkan dengan jawaban yang diberikan di atas, Anda masih akan mendapatkan kesalahan yang sama. Berikut ini cara melakukannya:

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

Kemudian lakukan seperti pengguna lain yang disebutkan di sini.

Martin Kool
sumber
5

Saya akan menggunakannya seperti ini, caranya lebih pendek dan tidak akan menimbulkan masalah

        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);
        }
Nick N.
sumber
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);
                    }
                }

            }
        }
M Fatih Koca
sumber
3

Saya mencoba untuk bekerja dengan semua jawaban yang disarankan di atas tetapi sepertinya tidak ada yang berhasil untuk saya. Jadi saya mencoba menjelaskan apa yang berhasil untuk saya di sini.

Saya percaya jika Anda memanggil beberapa metode seperti di Mainbawah ini atau bahkan dengan parameter tunggal seperti dalam pertanyaan Anda, Anda hanya perlu mengubah jenis parameter dari stringmenjadi objectagar ini berfungsi. Saya memiliki kelas seperti di bawah ini

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

    }
}

Maka Anda harus melewati parameterArray di dalam array objek seperti di bawah ini saat memintanya. Metode berikut adalah apa yang Anda butuhkan untuk bekerja

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

Metode ini membuatnya mudah untuk memanggil metode, dapat disebut sebagai berikut

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

Saya meminta rata-rata tertimbang melalui refleksi. Dan telah menggunakan metode dengan lebih dari satu parameter.

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
Sachin Pete
sumber
0
string result = this.GetType().GetMethod("Print").Invoke(this, new object[]{"firstParam", 157, "third_Parammmm" } );

jika bukan eksternal .dll (alih-alih this.GetType(), Anda dapat menggunakantypeof(YourClass) ).

ps memposting jawaban ini karena banyak pengunjung masuk ke sini untuk jawaban ini.

T.Todua
sumber