C # Verwenden Sie System.Type als generischen Parameter

84

Ich habe eine Liste von Typen (System.Type), die in der Datenbank abgefragt werden müssen.

Für jeden dieser Typen muss ich die folgende Erweiterungsmethode aufrufen (die Teil von LinqToNhibernate ist):

Session.Linq<MyType>()

Ich habe jedoch keinen MyType, sondern möchte stattdessen einen Typ verwenden.

Was ich habe ist:

System.Type typeOne;

Aber ich kann Folgendes nicht ausführen:

Session.Linq<typeOne>()

Wie kann ich einen Typ als generischen Parameter verwenden?

Jan.
quelle

Antworten:

90

Sie können nicht direkt. Der Sinn von Generika besteht darin, Sicherheit für Kompilierungszeiten zu bieten , bei der Sie den Typ kennen, an dem Sie zur Kompilierungszeit interessiert sind, und mit Instanzen dieses Typs arbeiten können. In Ihrem Fall kennen Sie nur das, Typesodass Sie keine Überprüfungen zur Kompilierungszeit erhalten können, ob Objekte, die Sie haben, Instanzen dieses Typs sind.

Sie müssen die Methode über Reflection aufrufen - ungefähr so:

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

Wenn Sie diesen Typ häufig verwenden müssen, ist es möglicherweise bequemer, eine eigene generische Methode zu schreiben, die alle anderen generischen Methoden aufruft, und dann Ihre Methode mit Reflexion aufzurufen .

Jon Skeet
quelle
1
Ich habe über eine Lösung gelesen, die Reflexion verwendet, um die Methode aufzurufen. Aber ich hoffte, dass es eine andere Lösung gab.
Jan
Die Aufrufmethode gibt ein "Objekt" zurück. Ich kann dieses Objekt erst abfragen, wenn ich es in den richtigen Typ umgewandelt habe. (Was wahrscheinlich IQueryable <T> wäre). Wie kann ich das Objekt in den Typ umwandeln, den ich habe?
Jan
3
@Jan: Das kannst du nicht - aber dann könntest du diesen Typ auch nicht verwenden, weil du den Typ zur Kompilierungszeit nicht kennst ... hier kann es sich lohnen, eine generische Methode zu schreiben, die macht alles, was Sie wollen, auf eine stark typisierte Art und Weise und nennt das mit Reflexion. Macht das Nicht-Generische alternativ das, IQueryablewas Sie brauchen?
Jon Skeet
2
@ Jon: Danke, ich werde versuchen, meine eigene generische Methode zu schreiben. Leider lässt sich das nicht generische Iqueryable nicht lösen.
Jan
1
@ Jon: meine eigene generische Methode gelöst andere generische Methode zu nennen , das Problem
Jan
30

Dazu müssen Sie Reflection verwenden:

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

(vorausgesetzt, dies Linq<T>()ist eine statische Methode für den Typ Session)

Wenn Sessiones sich tatsächlich um ein Objekt handelt , müssen Sie wissen, wo die LinqMethode tatsächlich deklariert ist, und Sessionals Argument übergeben:

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

Ich habe eine allgemeine Methode, die Call Generic Method Through Reflection aufruft

/// <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
quelle