C # użyj System.Type jako parametru ogólnego

89

Mam listę typów (System.Type), które wymagają zapytania w bazie danych.

Dla każdego z tych typów muszę wywołać następującą metodę rozszerzenia (która jest częścią LinqToNhibernate):

Session.Linq<MyType>()

Jednak nie mam MyType, ale chcę zamiast tego użyć Type.

Co mam to:

System.Type typeOne;

Ale nie mogę wykonać następujących czynności:

Session.Linq<typeOne>()

Jak mogę użyć typu jako parametru ogólnego?

Jan
źródło

Odpowiedzi:

95

Nie możesz, bezpośrednio. Celem ogólnych jest zapewnienie bezpieczeństwa typu w czasie kompilacji , gdzie znasz typ, który Cię interesuje w czasie kompilacji, i możesz pracować z instancjami tego typu. W twoim przypadku wiesz tylko, że Typenie możesz uzyskać żadnych kontroli w czasie kompilacji, że jakiekolwiek posiadane obiekty są instancjami tego typu.

Będziesz musiał wywołać metodę przez odbicie - coś takiego:

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

Jeśli trzeba użyć tego rodzaju dużo, może się okazać, że wygodniej jest napisać swój własny sposób ogólny który wzywa wszelkie inne metody generyczne potrzebuje, a następnie wywołać swoją metodę z refleksji.

Jon Skeet
źródło
1
Czytałem o rozwiązaniu, które używa odbicia do wywołania metody. Ale miałem nadzieję, że jest inne rozwiązanie.
Jan
metoda invoke zwraca „Object”. Nie mogę zapytać o ten obiekt, dopóki nie przerzucę go na właściwy typ. (Który prawdopodobnie byłby IQueryable <T>). Jak mogę rzutować obiekt na typ, który mam?
Jan
3
@Jan: Nie możesz - ale wtedy też nie byłbyś w stanie użyć tego typu, ponieważ nie znasz typu w czasie kompilacji ... w tym miejscu może warto napisać ogólną metodę, która robi wszystko, co chcesz, w mocno wpisany sposób i nazywając to z refleksją. Ewentualnie, czy produkty inne niż ogólne IQueryablerobią to, czego potrzebujesz?
Jon Skeet,
2
@Jon: Dzięki, spróbuję napisać własną ogólną metodę. Niestety nieogólny Iqueryable nie rozwiąże problemu.
Jan
1
@Jon: użycie mojej własnej ogólnej metody do wywołania innej ogólnej metody rozwiązało problem
Jan
30

Aby to zrobić, musisz użyć odbicia:

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

(zakładając, że Linq<T>()jest to metoda statyczna na typie Session)

Jeśli Sessionfaktycznie jest obiektem , musisz wiedzieć, gdzie Linqmetoda jest faktycznie zadeklarowana i przekazać ją Sessionjako argument:

typeof(DeclaringType).GetMethod("Linq").MakeGenericMethod(typeOne)
     .Invoke(null, new object[] {Session});
Marc Gravell
źródło
1

Mam jedną ogólną metodę, która wywołuje metodę ogólną Call przez odbicie

/// <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
źródło