C # ใช้ System.Type เป็นพารามิเตอร์ทั่วไป


89

ฉันมีรายการประเภท (System.Type) ที่ต้องสอบถามในฐานข้อมูล

สำหรับแต่ละประเภทนี้ฉันต้องเรียกส่วนขยายต่อไปนี้ (ซึ่งเป็นส่วนหนึ่งของ LinqToNhibernate):

Session.Linq<MyType>()

อย่างไรก็ตามฉันไม่มี MyType แต่ฉันต้องการใช้ Type แทน

สิ่งที่ฉันมีคือ:

System.Type typeOne;

แต่ฉันไม่สามารถทำสิ่งต่อไปนี้ได้:

Session.Linq<typeOne>()

ฉันจะใช้ Type เป็นพารามิเตอร์ Generic ได้อย่างไร

คำตอบ:


95

คุณไม่สามารถโดยตรง ประเด็นสำคัญคือการให้ความปลอดภัยของประเภทเวลาคอมไพล์ซึ่งคุณจะทราบประเภทที่คุณสนใจในเวลาคอมไพล์และสามารถทำงานกับอินสแตนซ์ประเภทนั้นได้ ในกรณีของคุณคุณรู้เพียงอย่างเดียวTypeดังนั้นคุณจึงไม่สามารถตรวจสอบเวลาคอมไพล์ได้ว่าออบเจ็กต์ใด ๆ ที่คุณมีเป็นอินสแตนซ์ประเภทนั้น

คุณจะต้องเรียกใช้เมธอดผ่านการสะท้อน - อะไรทำนองนี้:

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

หากคุณจำเป็นต้องใช้ประเภทนี้เป็นจำนวนมากคุณอาจพบว่าสะดวกกว่าในการเขียนวิธีการทั่วไปของคุณเองซึ่งเรียกวิธีการทั่วไปอื่น ๆ ที่ต้องการแล้วเรียกวิธีการของคุณด้วยการไตร่ตรอง


1
ฉันอ่านเกี่ยวกับโซลูชันที่ใช้การสะท้อนเพื่อเรียกใช้เมธอด แต่ฉันหวังว่าจะมีทางออกอื่น
ม.ค.

วิธีการเรียกใช้จะส่งคืน "วัตถุ" ฉันไม่สามารถค้นหาวัตถุนี้ได้จนกว่าฉันจะส่งไปยังประเภทที่ถูกต้อง (ซึ่งน่าจะเป็น <T> ที่น่าสนใจ) ฉันจะโยนวัตถุให้เป็นแบบที่ฉันมีได้อย่างไร?
ม.ค.

3
@ Jan: คุณทำไม่ได้ - แต่คุณจะไม่สามารถใช้ประเภทนั้นได้เช่นกันเพราะคุณไม่รู้จักประเภทในเวลาคอมไพล์ ... นี่คือที่ที่คุณควรเขียนวิธีการทั่วไปซึ่งอาจคุ้มค่า ทำทุกอย่างที่คุณต้องการในรูปแบบที่พิมพ์มากและเรียกสิ่งนั้นด้วยการสะท้อน หรืออีกวิธีหนึ่งคือ non-generic IQueryableทำในสิ่งที่คุณต้องการหรือไม่?
Jon Skeet

2
@ จอน: ขอบคุณฉันจะลองเขียนวิธีการทั่วไปของตัวเอง น่าเสียดายที่ Iqueryable ที่ไม่ใช่ทั่วไปไม่สามารถแก้ปัญหาได้
ม.ค.

1
@ จอน: ใช้วิธีการทั่วไปของตัวเองที่จะเรียกวิธีการทั่วไปอื่นแก้ปัญหา
ม.ค.

30

ในการทำสิ่งนี้คุณต้องใช้การสะท้อน:

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

(สมมติว่าLinq<T>()เป็นวิธีการคงที่ในประเภทSession)

ถ้าSessionเป็นออบเจ็กต์จริงคุณจะต้องรู้ว่ามีLinqการประกาศเมธอดอย่างไรและส่งต่อSessionเป็นอาร์กิวเมนต์:

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

1

ฉันมีวิธีการทั่วไปวิธีหนึ่งที่เรียกวิธีการเรียกทั่วไปผ่านการสะท้อนกลับ

/// <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;
        }
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.