C # usa System.Type como parâmetro genérico

87

Eu tenho uma lista de tipos (System.Type) que precisam ser consultados no banco de dados.

Para cada um desses tipos, preciso chamar o seguinte método de extensão (que faz parte de LinqToNhibernate):

Session.Linq<MyType>()

No entanto, não tenho MyType, mas quero usar um Type.

O que eu tenho é:

System.Type typeOne;

Mas não posso fazer o seguinte:

Session.Linq<typeOne>()

Como posso usar um tipo como parâmetro genérico?

Jan
fonte

Respostas:

95

Você não pode, diretamente. O objetivo dos genéricos é fornecer segurança de tipo em tempo de compilação , onde você sabe o tipo de interesse em tempo de compilação e pode trabalhar com instâncias desse tipo. No seu caso, você só sabe o, Typeportanto, não pode obter nenhuma verificação em tempo de compilação de que quaisquer objetos que você tenha são instâncias desse tipo.

Você precisará chamar o método por meio de reflexão - algo assim:

// 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 você precisa usar muito esse tipo, pode achar mais conveniente escrever seu próprio método genérico que chama quaisquer outros métodos genéricos de que precisa e, em seguida, chamar seu método com reflexão.

Jon Skeet
fonte
1
Eu li sobre uma solução que usa reflexão para chamar o método. Mas eu esperava que houvesse outra solução.
janeiro
o método invoke retorna um "Objeto". Não sou capaz de consultar este objeto até que o lance para o tipo correto. (O que provavelmente seria IQueryable <T>). Como posso converter o objeto para o tipo que tenho?
janeiro
3
@Jan: Você não pode - mas também não seria capaz de usar esse tipo, porque você não conhece o tipo em tempo de compilação ... é aqui que pode valer a pena escrever um método genérico que faz tudo que você quer de uma forma fortemente tipada, e chamando isso de reflexão. Como alternativa, o não genérico IQueryablefaz o que você precisa?
Jon Skeet de
2
@ Jon: Obrigado, tentarei escrever meu próprio método genérico. Infelizmente, o Iqueryable não genérico não resolverá o problema.
janeiro
1
@ Jon: usando o meu próprio método genérico para chamar outro método genérico resolveu o problema
Jan
30

Para fazer isso, você precisa usar reflexão:

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

(assumindo que Linq<T>()é um método estático no tipo Session)

Se Sessionfor realmente um objeto , você precisará saber onde o Linqmétodo está realmente declarado e passar Sessioncomo um argumento:

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

Eu tenho um método geral que chama Método genérico de chamada por reflexão

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