C # menggunakan System.Type sebagai parameter Generik

89

Saya memiliki daftar tipe (System.Type) yang perlu ditanyakan pada database.

Untuk masing-masing jenis ini, saya perlu memanggil metode ekstensi berikut (yang merupakan bagian dari LinqToNhibernate):

Session.Linq<MyType>()

Namun saya tidak memiliki MyType, tetapi saya ingin menggunakan Type.

Yang saya miliki adalah:

System.Type typeOne;

Tetapi saya tidak dapat melakukan hal berikut:

Session.Linq<typeOne>()

Bagaimana saya bisa menggunakan Type sebagai parameter Generik?

Jan
sumber

Jawaban:

95

Anda tidak bisa, secara langsung. Inti dari obat generik adalah untuk menyediakan keamanan jenis waktu kompilasi , di mana Anda mengetahui jenis yang Anda minati pada waktu kompilasi, dan dapat bekerja dengan instance jenis itu. Dalam kasus Anda, Anda hanya tahu Typesehingga Anda tidak bisa mendapatkan pemeriksaan waktu kompilasi apa pun bahwa objek apa pun yang Anda miliki adalah instance dari tipe itu.

Anda harus memanggil metode melalui refleksi - sesuatu seperti ini:

// Get the generic type definition
MethodInfo method = typeof(Session).GetMethod("Linq", 
                                BindingFlags.Public | BindingFlags.Static);

// Build a method with the specific type argument you're interested in
method = method.MakeGenericMethod(typeOne);
// The "null" is because it's a static method
method.Invoke(null, arguments);

Jika Anda perlu sering menggunakan tipe ini, Anda mungkin merasa lebih nyaman untuk menulis metode generik Anda sendiri yang memanggil metode generik lain apa pun yang diperlukan, lalu memanggil metode Anda dengan refleksi.

Jon Skeet
sumber
1
Saya membaca tentang solusi yang menggunakan refleksi untuk memanggil metode tersebut. Tapi saya berharap ada solusi lain.
Jan
metode pemanggilan mengembalikan sebuah "Objek". Saya tidak dapat menanyakan objek ini sampai saya memasukkannya ke Jenis yang benar. (Yang mungkin akan IQuerizable <T>). Bagaimana cara mentransmisikan objek ke tipe yang saya miliki?
Jan
3
@ Jan: Anda tidak bisa - tetapi Anda juga tidak akan dapat menggunakan jenis itu, karena Anda tidak tahu jenisnya pada waktu kompilasi ... di sinilah mungkin ada gunanya Anda menulis metode umum yang melakukan segala sesuatu yang Anda inginkan dalam cara kuat-mengetik, dan memanggil yang dengan refleksi. Atau, apakah non-generik IQueryablemelakukan apa yang Anda butuhkan?
Jon Skeet
2
@ Jon: Terima kasih, saya akan mencoba menulis metode umum saya sendiri. Sayangnya Iquerizable non-generik tidak akan menyelesaikan masalah.
Jan
1
@ Jon: menggunakan metode generik saya sendiri untuk memanggil metode generik lain memecahkan masalah
Jan
30

Untuk melakukan ini, Anda perlu menggunakan refleksi:

typeof(Session).GetMethod("Linq").MakeGenericMethod(typeOne).Invoke(null, null);

(dengan asumsi itu Linq<T>()adalah metode statis pada tipe Session)

Jika Sessionsebenarnya adalah sebuah objek , Anda harus mengetahui di mana Linqmetode tersebut sebenarnya dideklarasikan, dan masuk Sessionsebagai argumen:

typeof(DeclaringType).GetMethod("Linq").MakeGenericMethod(typeOne)
     .Invoke(null, new object[] {Session});
Marc Gravell
sumber
1

Saya punya satu metode umum yang disebut Call Generic Method Through Reflection

/// <summary>
    /// This method call your method through Reflection 
    /// so i wil call the method like CallGenericMethodThroughReflection<Session>(assemblyQualifiedName,Linq,false,new[] { file }) 
    /// </summary>
    /// <typeparam name="T">Call method from which file</typeparam>
    /// <param name="assemblyQualifiedName">Your can get assemblyQualifiedName like typeof(Payroll.Domain.Attendance.AttendanceApplicationMaster).AssemblyQualifiedName</param>
    /// <param name="methodName"></param>
    /// <param name="isStaticMethod"></param>
    /// <param name="paramaterList"></param>
    /// <param name="parameterType">pass parameter type list in case of the given method have overload  </param>
    /// <returns>return object of calling method</returns>
    public static object CallGenericMethodThroughReflection<T>(string assemblyQualifiedName, string methodName,bool isStaticMethod ,object[] paramaterList,Type[] parameterType = null)
    {
        try
        {
            object instance = null;
            var bindingAttr = BindingFlags.Static | BindingFlags.Public;
            if (!isStaticMethod)
            {
                instance = Activator.CreateInstance<T>();
                bindingAttr = BindingFlags.Instance | BindingFlags.Public;
            }
            MethodInfo MI = null;
            var type = Type.GetType(assemblyQualifiedName);
            if(parameterType == null)
                MI = typeof(T).GetMethod(methodName, bindingAttr);
            else
                MI = typeof(T).GetMethod(methodName, bindingAttr,null, parameterType, null);//this will work in most case some case not work
            if (type == null || MI == null) // if the condition is true it means given method or AssemblyQualifiedName entity not found
                return null;
            var genericMethod = MI.MakeGenericMethod(new[] { type });
            return genericMethod.Invoke(instance, paramaterList);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
Kalpesh Dabhi
sumber