C # usa System.Type come parametro generico


89

Ho un elenco di tipi (System.Type) che devono essere interrogati sul database.

Per ciascuno di questi tipi, devo chiamare il seguente metodo di estensione (che fa parte di LinqToNhibernate):

Session.Linq<MyType>()

Tuttavia non ho MyType, ma preferisco usare un Type.

Quello che ho è:

System.Type typeOne;

Ma non posso eseguire quanto segue:

Session.Linq<typeOne>()

Come posso utilizzare un tipo come parametro generico?

Risposte:


95

Non puoi, direttamente. Lo scopo dei generics è fornire l'indipendenza dai tipi in fase di compilazione , in cui si conosce il tipo a cui si è interessati in fase di compilazione e si può lavorare con istanze di quel tipo. Nel tuo caso, conosci solo il Typequindi non puoi ottenere alcun controllo in fase di compilazione che tutti gli oggetti che hai sono istanze di quel tipo.

Avrai bisogno di chiamare il metodo tramite la riflessione - qualcosa del genere:

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

Se hai bisogno di usare molto questo tipo, potresti trovare più conveniente scrivere il tuo metodo generico che chiama tutti gli altri metodi generici di cui ha bisogno, e quindi chiamare il tuo metodo con reflection.


1
Ho letto di una soluzione che utilizza la riflessione per chiamare il metodo. Ma speravo ci fosse un'altra soluzione.
Jan

il metodo invoke restituisce un "Object". Non sono in grado di interrogare questo oggetto fino a quando non lo ho lanciato nel tipo corretto. (Che probabilmente sarebbe IQueryable <T>). Come posso lanciare l'oggetto sul tipo che ho?
Jan

3
@ Jan: Non puoi, ma non saresti nemmeno in grado di usare quel tipo, perché non conosci il tipo in fase di compilazione ... qui è dove potrebbe valere la pena scrivere un metodo generico che fa tutto quello che volete in un modo fortemente tipizzato, e chiamando che con la riflessione. In alternativa, il non generico IQueryablefa quello che ti serve?
Jon Skeet

2
@ Jon: Grazie, proverò a scrivere il mio metodo generico. Sfortunatamente l'Iqueryable non generico non risolverà il problema.
Jan

1
@ Jon: usando il mio metodo generico per chiamare un altro metodo generico ha risolto il problema
gen

30

Per fare ciò è necessario utilizzare la riflessione:

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

(supponendo che Linq<T>()sia un metodo statico sul tipo Session)

Se Sessionè effettivamente un oggetto , dovrai sapere dove il Linqmetodo è effettivamente dichiarato e passare Sessioncome argomento:

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

1

Ho un metodo generale che chiama 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;
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.