Bagaimana cara menggunakan refleksi untuk memanggil metode generik?

1071

Apa cara terbaik untuk memanggil metode generik ketika parameter tipe tidak diketahui pada waktu kompilasi, tetapi sebaliknya diperoleh secara dinamis saat runtime?

Pertimbangkan kode contoh berikut - di dalam Example()metode, apa cara paling ringkas untuk memohon GenericMethod<T>()menggunakan yang Typedisimpan dalam myTypevariabel?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}
Bevan
sumber
7
Saya mencoba solusi Jon dan tidak bisa membuatnya bekerja sampai saya mempublikasikan metode generik di kelas saya. Saya tahu bahwa Jon yang lain menjawab mengatakan bahwa Anda perlu menentukan tag binding tetapi ini tidak membantu.
naskew
12
Anda juga perlu BindingFlags.Instance, bukan hanya BindingFlags.NonPublic, untuk mendapatkan metode pribadi / internal.
Lars Kemmann
2
Versi modern dari pertanyaan ini: stackoverflow.com/q/2433436/103167
Ben Voigt
@Peter Mortensen - fyi saya menggunakan spasi sebelum '?' untuk memisahkan bagian-bagian bahasa Inggris dari bagian-bagian non-Inggris (C #); IMHO menghapus ruang membuatnya terlihat seperti itu? adalah bagian dari kode. Jika tidak ada kode, saya pasti setuju dengan menghapus spasi, tetapi dalam kasus ini ...
Bevan

Jawaban:

1139

Anda perlu menggunakan refleksi untuk memulai metode, kemudian "buat" dengan memberikan argumen tipe dengan MakeGenericMethod :

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Untuk metode statis, berikan nullargumen pertama ke Invoke. Itu tidak ada hubungannya dengan metode generik - itu hanya refleksi normal.

Seperti yang disebutkan, banyak dari ini lebih sederhana pada penggunaan C # 4 dynamic- jika Anda dapat menggunakan inferensi tipe, tentu saja. Itu tidak membantu dalam kasus di mana inferensi tipe tidak tersedia, seperti contoh yang tepat dalam pertanyaan.

Jon Skeet
sumber
92
+1; perhatikan bahwa GetMethod()hanya menganggap metode instance publik secara default, jadi Anda mungkin perlu BindingFlags.Staticdan / atau BindingFlags.NonPublic.
20
Kombinasi flag yang benar adalah BindingFlags.NonPublic | BindingFlags.Instance(dan opsional BindingFlags.Static).
Lars Kemmann
4
Sebuah pertanyaan yang ditandai sebagai penipuan ini bertanya-tanya bagaimana melakukan ini dengan metode statis - dan secara teknis demikian pula pertanyaannya di sini. parameter pertama generic.Invoke () harus null saat memanggil metode statis. Parameter pertama hanya diperlukan saat memanggil metode instance.
Chris Moschini
2
@ChrisMoschini: Menambahkan itu ke jawabannya.
Jon Skeet
2
@ gzou: Saya telah menambahkan sesuatu ke jawabannya - tetapi perhatikan bahwa untuk memanggil metode umum dalam pertanyaan , dynamictidak membantu karena tipe inferensi tidak tersedia. (Tidak ada argumen yang dapat digunakan kompiler untuk menentukan argumen tipe.)
Jon Skeet
170

Hanya tambahan untuk jawaban aslinya. Sementara ini akan berhasil:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Ini juga sedikit berbahaya karena Anda kehilangan waktu kompilasi GenericMethod. Jika nanti Anda melakukan refactoring dan mengganti nama GenericMethod, kode ini tidak akan melihat dan akan gagal pada saat dijalankan. Juga, jika ada post-processing dari rakitan (misalnya mengaburkan atau menghapus metode / kelas yang tidak digunakan) kode ini mungkin rusak juga.

Jadi, jika Anda tahu metode yang Anda tautkan pada waktu kompilasi, dan ini tidak disebut jutaan kali jadi overhead tidak masalah, saya akan mengubah kode ini menjadi:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Meskipun tidak terlalu cantik, Anda memiliki referensi waktu kompilasi ke GenericMethodsini, dan jika Anda refactor, menghapus atau melakukan apa pun dengan GenericMethod, kode ini akan tetap berfungsi, atau setidaknya istirahat pada waktu kompilasi (jika misalnya Anda menghapus GenericMethod).

Cara lain untuk melakukan hal yang sama adalah dengan membuat kelas pembungkus baru, dan membuatnya melalui Activator. Saya tidak tahu apakah ada cara yang lebih baik.

Adrian Gallero
sumber
5
Dalam kasus di mana refleksi digunakan untuk memanggil metode, biasanya nama metode itu sendiri ditemukan oleh metode lain. Mengetahui nama metode terlebih dahulu tidak umum.
Bevan
13
Ya, saya setuju untuk menggunakan refleksi secara umum. Tetapi pertanyaan awal adalah bagaimana memanggil "GenericMethod <myType> ()" Jika sintaks itu diizinkan, kita tidak memerlukan GetMethod () sama sekali. Tetapi untuk pertanyaan "bagaimana saya menulis" GenericMethod <myType> "? Saya pikir jawabannya harus mencakup cara untuk menghindari kehilangan tautan waktu kompilasi dengan GenericMethod. Sekarang jika pertanyaan ini umum atau tidak, saya tidak tahu, tetapi Saya tahu saya memiliki masalah yang persis sama kemarin, dan itulah mengapa saya menjawab pertanyaan ini
Adrian Gallero
20
Anda bisa melakukan GenMethod.Method.GetGenericMethodDefinition()bukan this.GetType().GetMethod(GenMethod.Method.Name). Ini sedikit lebih bersih dan mungkin lebih aman.
Daniel Cassidy
Apa maksud "myType" dalam sampel Anda?
Pengembang
37
Sekarang Anda dapat menggunakannameof(GenericMethod)
dmigo
140

Memanggil metode generik dengan parameter tipe yang hanya diketahui saat runtime dapat sangat disederhanakan dengan menggunakan dynamictipe alih-alih API refleksi.

Untuk menggunakan teknik ini jenis harus diketahui dari objek yang sebenarnya (bukan hanya turunan dari Typekelas). Jika tidak, Anda harus membuat objek jenis itu atau menggunakan solusi API refleksi standar . Anda dapat membuat objek dengan menggunakan metode Activator.CreateInstance .

Jika Anda ingin memanggil metode generik, bahwa dalam penggunaan "normal" akan memiliki tipe disimpulkan, maka itu hanya datang untuk casting objek tipe yang tidak dikenal dynamic. Ini sebuah contoh:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

Dan inilah output dari program ini:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Processadalah metode instance generik yang menulis tipe sebenarnya dari argumen yang diteruskan (dengan menggunakan GetType()metode) dan tipe parameter generik (dengan menggunakan typeofoperator).

Dengan melemparkan argumen objek untuk dynamicmengetik, kami menunda menyediakan parameter tipe hingga runtime. Ketika Processmetode dipanggil dengan dynamicargumen maka kompiler tidak peduli tentang jenis argumen ini. Kompiler menghasilkan kode yang pada saat runtime memeriksa jenis argumen yang dilewati (dengan menggunakan refleksi) dan memilih metode terbaik untuk memanggil. Di sini hanya ada satu metode generik ini, jadi ini dipanggil dengan parameter tipe yang tepat.

Dalam contoh ini, hasilnya sama seperti jika Anda menulis:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

Versi dengan tipe dinamis jelas lebih pendek dan lebih mudah untuk ditulis. Anda juga tidak perlu khawatir tentang kinerja memanggil fungsi ini beberapa kali. Panggilan berikutnya dengan argumen dari tipe yang sama harus lebih cepat berkat mekanisme caching di DLR. Tentu saja, Anda dapat menulis kode yang memanggil cache delegasi, tetapi dengan menggunakan dynamictipe ini Anda mendapatkan perilaku ini secara gratis.

Jika metode generik yang ingin Anda panggil tidak memiliki argumen tipe parametrized (jadi parameter tipenya tidak dapat disimpulkan) maka Anda dapat membungkus permohonan metode generik dalam metode pembantu seperti dalam contoh berikut:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

Keamanan jenis meningkat

Apa yang benar-benar hebat tentang menggunakan dynamicobjek sebagai pengganti untuk menggunakan API refleksi adalah bahwa Anda hanya kehilangan waktu kompilasi memeriksa jenis khusus ini yang Anda tidak tahu sampai runtime. Argumen lain dan nama metode dianalisis secara statis oleh kompiler seperti biasa. Jika Anda menghapus atau menambahkan lebih banyak argumen, mengubah jenisnya atau mengganti nama metode, maka Anda akan mendapatkan kesalahan waktu kompilasi. Ini tidak akan terjadi jika Anda memberikan nama metode sebagai string Type.GetMethoddan argumen sebagai array objek MethodInfo.Invoke.

Di bawah ini adalah contoh sederhana yang menggambarkan bagaimana beberapa kesalahan dapat ditangkap pada waktu kompilasi (kode komentar) dan lainnya saat runtime. Ini juga menunjukkan bagaimana DLR mencoba menyelesaikan metode mana yang harus dihubungi.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

Di sini kita kembali menjalankan beberapa metode dengan melemparkan argumen ke dynamictipe. Hanya verifikasi tipe argumen pertama yang ditunda ke runtime. Anda akan mendapatkan kesalahan kompilator jika nama metode yang Anda panggil tidak ada atau jika argumen lain tidak valid (jumlah argumen yang salah atau jenis yang salah).

Ketika Anda meneruskan dynamicargumen ke suatu metode maka panggilan ini akhir - akhir ini terikat . Metode resolusi kelebihan terjadi pada saat runtime dan mencoba untuk memilih kelebihan yang terbaik. Jadi jika Anda memanggil ProcessItemmetode dengan objek BarItembertipe maka Anda akan benar-benar memanggil metode non-generik, karena metode ini lebih cocok untuk jenis ini. Namun, Anda akan mendapatkan kesalahan runtime ketika Anda melewati argumen Alphatipe karena tidak ada metode yang dapat menangani objek ini (metode generik memiliki kendala where T : IItemdan Alphakelas tidak mengimplementasikan antarmuka ini). Tapi itulah intinya. Kompiler tidak memiliki informasi bahwa panggilan ini valid. Anda sebagai seorang programmer mengetahui hal ini, dan Anda harus memastikan bahwa kode ini berjalan tanpa kesalahan.

Jenis pengembalian gotcha

Ketika Anda memanggil metode non-kekosongan dengan parameter tipe dinamis, jenis kembalinya mungkin akan menjadi dynamicterlalu . Jadi, jika Anda ingin mengubah contoh sebelumnya ke kode ini:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

maka jenis objek hasil akan dynamic. Ini karena kompiler tidak selalu tahu metode mana yang akan dipanggil. Jika Anda mengetahui jenis pengembalian panggilan fungsi maka Anda harus secara implisit mengonversinya ke jenis yang diperlukan sehingga sisa kode diketik secara statis:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

Anda akan mendapatkan kesalahan runtime jika jenisnya tidak cocok.

Sebenarnya, jika Anda mencoba untuk mendapatkan nilai hasil pada contoh sebelumnya maka Anda akan mendapatkan kesalahan runtime di iterasi loop kedua. Ini karena Anda mencoba menyimpan nilai kembali dari fungsi batal.

Mariusz Pawelski
sumber
Mariusz, bingung oleh "Namun, Anda akan mendapatkan kesalahan runtime ketika Anda melewati argumen tipe Alpha karena tidak ada metode yang dapat menangani objek ini." Jika saya memanggil var a = new Alpha () ProcessItem (a, "test" + i , i) Mengapa metode ProcessItem generik tidak menangani ini secara efektif, mengeluarkan "Item Proses Umum"?
Alex Edelstein
@AlexEdelstein Saya mengedit jawaban saya untuk mengklarifikasi sedikit. Itu karena ProcessItemmetode generik memiliki batasan generik dan hanya menerima objek yang mengimplementasikan IItemantarmuka. Ketika Anda akan menelepon ProcessItem(new Aplha(), "test" , 1);atau ProcessItem((object)(new Aplha()), "test" , 1);Anda akan mendapatkan kesalahan kompiler tetapi ketika casting untuk dynamicAnda tunda centang itu untuk runtime.
Mariusz Pawelski
Jawaban dan penjelasan yang bagus, sangat cocok untuk saya. Jauh lebih baik daripada jawaban yang diterima, lebih pendek untuk menulis, lebih berkinerja, dan lebih aman.
ygoe
17

Dengan C # 4.0, refleksi tidak diperlukan karena DLR dapat menyebutnya menggunakan tipe runtime. Karena menggunakan pustaka DLR agak merepotkan secara dinamis (alih-alih kode penghasil kompiler C # untuk Anda), kerangka kerja sumber terbuka Dynamitey (.net standar 1.5) memberi Anda akses run-time cache yang mudah di-cache ke panggilan yang sama yang akan dihasilkan oleh kompiler untukmu.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));
jbtule
sumber
13

Menambahkan ke jawaban Adrian Gallero :

Memanggil metode generik dari tipe info melibatkan tiga langkah.

TLDR: Memanggil metode generik yang dikenal dengan objek tipe dapat dilakukan dengan:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

di mana GenericMethod<object>nama metode untuk memanggil dan jenis apa pun yang memenuhi batasan generik.

(Aksi) cocok dengan tanda tangan dari metode yang akan dipanggil yaitu ( Func<string,string,int>atau Action<bool>)

Langkah 1 adalah mendapatkan MethodInfo untuk definisi metode generik

Metode 1: Gunakan GetMethod () atau GetMethods () dengan jenis yang sesuai atau flag yang mengikat.

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

Metode 2: Buat delegasi, dapatkan objek MethodInfo dan kemudian panggil GetGenericMethodDefinition

Dari dalam kelas yang berisi metode:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Dari luar kelas yang berisi metode:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Dalam C #, nama metode, yaitu "ToString" atau "GenericMethod" sebenarnya merujuk pada sekelompok metode yang mungkin berisi satu atau lebih metode. Sampai Anda memberikan jenis parameter metode, tidak diketahui metode mana yang Anda maksud.

((Action)GenericMethod<object>)merujuk pada delegasi untuk metode tertentu. ((Func<string, int>)GenericMethod<object>) merujuk pada kelebihan yang berbeda dari GenericMethod

Metode 3: Buat ekspresi lambda yang berisi ekspresi pemanggilan metode, dapatkan objek MethodInfo dan kemudian GetGenericMethodDefinition

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

Ini rusak menjadi

Buat ekspresi lambda di mana tubuh adalah panggilan ke metode yang Anda inginkan.

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

Ekstrak tubuh dan dilemparkan ke MethodCallExpression

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

Dapatkan definisi metode generik dari metode tersebut

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

Langkah 2 memanggil MakeGenericMethod untuk membuat metode generik dengan jenis yang sesuai.

MethodInfo generic = method.MakeGenericMethod(myType);

Langkah 3 adalah memanggil metode dengan argumen yang sesuai.

generic.Invoke(this, null);
Grax32
sumber
8

Tidak ada yang memberikan solusi " Refleksi klasik ", jadi di sini adalah contoh kode lengkap:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

Kelas di atas DynamicDictionaryFactorymemiliki metode

CreateDynamicGenericInstance(Type keyType, Type valueType)

dan itu menciptakan dan mengembalikan instance IDictionary, jenis kunci dan nilainya yang persis ditentukan pada panggilan keyTypedan valueType.

Berikut ini adalah contoh lengkap cara memanggil metode ini untuk membuat instance dan menggunakan Dictionary<String, int>:

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

Ketika aplikasi konsol di atas dijalankan, kami mendapatkan hasil yang benar dan diharapkan:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3
Dimitre Novatchev
sumber
2

Ini adalah 2 sen saya berdasarkan jawaban Grax , tetapi dengan dua parameter yang diperlukan untuk metode generik.

Asumsikan metode Anda didefinisikan sebagai berikut dalam kelas Pembantu:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

Dalam kasus saya, tipe U selalu merupakan koleksi yang dapat diamati yang menyimpan objek tipe T.

Karena tipe saya sudah ditentukan sebelumnya, saya pertama-tama membuat objek "dummy" yang mewakili koleksi yang dapat diamati (U) dan objek yang disimpan di dalamnya (T) dan yang akan digunakan di bawah ini untuk mendapatkan tipenya ketika memanggil Make

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

Kemudian panggil GetMethod untuk menemukan fungsi Generik Anda:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

Sejauh ini, panggilan di atas cukup identik dengan apa yang telah dijelaskan di atas tetapi dengan perbedaan kecil ketika Anda harus melewati beberapa parameter untuk itu.

Anda perlu meneruskan larik Type [] ke fungsi MakeGenericMethod yang berisi tipe objek "dummy" yang dibuat di atas:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

Setelah selesai, Anda perlu memanggil metode Invoke seperti yang disebutkan di atas.

generic.Invoke(null, new object[] { csvData });

Dan kamu sudah selesai. Mempesona!

MEMPERBARUI:

Sebagai @Bevan disorot, saya tidak perlu membuat array saat memanggil fungsi MakeGenericMethod karena dibutuhkan dalam params dan saya tidak perlu membuat objek untuk mendapatkan jenis karena saya bisa meneruskan jenis langsung ke fungsi ini. Dalam kasus saya, karena saya memiliki tipe yang telah ditentukan di kelas lain, saya hanya mengubah kode saya menjadi:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo berisi 2 properti tipe Typeyang saya atur di run time berdasarkan nilai enum yang diteruskan ke konstruktor dan akan memberi saya jenis yang relevan yang kemudian saya gunakan dalam MakeGenericMethod.

Terima kasih lagi karena telah menyoroti @Bevan ini.

Thierry
sumber
Argumen untuk MakeGenericMethod()memiliki kata kunci params sehingga Anda tidak perlu membuat array; Anda juga tidak perlu membuat instance untuk mendapatkan tipe - methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))akan cukup.
Bevan
0

Terinspirasi oleh jawaban Enigmativity - anggaplah Anda memiliki dua (atau lebih) kelas, seperti

public class Bar { }
public class Square { }

dan Anda ingin memanggil metode Foo<T>dengan Bardan Square, yang dinyatakan sebagai

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

Kemudian Anda dapat menerapkan metode Ekstensi seperti:

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

Dengan ini, Anda bisa memohon Fooseperti:

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

yang bekerja untuk setiap kelas. Dalam hal ini, akan ditampilkan:

Square
Bar

Mat
sumber