C # utilise System.Type comme paramètre générique

87

J'ai une liste de types (System.Type) qui doivent être interrogés sur la base de données.

Pour chacun de ces types, je dois appeler la méthode d'extension suivante (qui fait partie de LinqToNhibernate):

Session.Linq<MyType>()

Cependant, je n'ai pas MyType, mais je souhaite utiliser un Type à la place.

Ce que j'ai c'est:

System.Type typeOne;

Mais je ne peux pas effectuer les opérations suivantes:

Session.Linq<typeOne>()

Comment puis-je utiliser un type comme paramètre générique?

Jan
la source

Réponses:

95

Vous ne pouvez pas, directement. Le but des génériques est de fournir une sécurité de type au moment de la compilation , où vous connaissez le type qui vous intéresse au moment de la compilation, et pouvez travailler avec des instances de ce type. Dans votre cas, vous ne connaissez que le, Typedonc vous ne pouvez pas obtenir de vérification à la compilation que les objets que vous avez sont des instances de ce type.

Vous devrez appeler la méthode par réflexion - quelque chose comme ceci:

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

Si vous avez besoin d'utiliser beaucoup ce type, vous trouverez peut-être plus pratique d'écrire votre propre méthode générique qui appelle toutes les autres méthodes génériques dont elle a besoin, puis d'appeler votre méthode avec réflexion.

Jon Skeet
la source
1
J'ai lu une solution qui utilise la réflexion pour appeler la méthode. Mais j'espérais qu'il y avait une autre solution.
Jan
la méthode invoke renvoie un "Object". Je ne suis pas en mesure d'interroger cet objet tant que je ne l'ai pas converti dans le type approprié. (Ce qui serait probablement IQueryable <T>). Comment puis-je convertir l'objet dans le type que j'ai?
Jan
3
@Jan: Vous ne pouvez pas - mais vous ne pourrez pas non plus utiliser ce type, car vous ne connaissez pas le type au moment de la compilation ... c'est là que cela vaut peut-être la peine d'écrire une méthode générique qui fait tout ce que vous voulez d'une manière fortement typée, et appelez cela avec réflexion. Sinon, le non générique IQueryablefait-il ce dont vous avez besoin?
Jon Skeet
2
@Jon: Merci, je vais essayer d'écrire ma propre méthode générique. Malheureusement, l'Iqueryable non générique ne résoudra pas le problème.
Jan
1
@ Jon: en utilisant ma propre méthode générique pour appeler une autre méthode générique a résolu le problème
Jan
30

Pour ce faire, vous devez utiliser la réflexion:

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

(en supposant qu'il Linq<T>()s'agit d'une méthode statique sur le type Session)

S'il Sessions'agit en fait d'un objet , vous aurez besoin de savoir où la Linqméthode est réellement déclarée et de la passer en Sessionargument:

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

J'ai une méthode générale qui appelle 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
la source