Pergunta

Eu estou tentando criar um Expression que irá invocar um método sobrecarregado genérico específico (Enumerable.Average no meu primeiro caso de teste). As ligações de tipo específico não são conhecidos até a execução no entanto assim que eu preciso para uso Reflection para encontrar e criar o método genérico correto (o Expression está sendo criado a partir do texto analisado).

Então, se eu sei que em tempo de execução que eu quero encontrar essa sobrecarga específica:

public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)

Como posso resolver isso MethodInfo particular usando reflexão?

Até agora eu tenho a seguinte declaração seleção:

MethodInfo GetMethod(Type argType, Type returnType)
{
    var methods = from method in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
      where method.Name == "Average" &&
      method.ContainsGenericParameters &&                              
      method.GetParameters().Length == 2 &&
      // and some condition where method.GetParameters()[1] is a Func that returns type argType
      method.ReturnType == returnType
      select method;

      Debug.Assert(methods.Count() == 1);
      return methods.FirstOrDefault();
}

Os estreitos acima-lo a três sobrecargas mas quero refletir e encontrar a sobrecarga específica que leva um Func<TSource, int> onde argType == typeof(int).

Estou perplexo e qualquer ajuda é apreciada.

Foi útil?

Solução

Você precisa usar MethodInfo.MakeGenericMethod

EDIT: OK, eu tinha entendido mal o problema ... Este método deve fazer o que quiser:

MethodInfo GetMethod(Type argType, Type returnType)
{
    var enumerableType = typeof(IEnumerable<>).MakeGenericType(new Type[] { argType });
    Console.WriteLine(enumerableType);
    var methods = from method in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
      let parameters = method.GetParameters()
      let genParams = method.GetGenericArguments()
      where method.Name == "Average" &&
      method.ContainsGenericParameters &&                              
      parameters.Length == 2 &&
      parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>) &&
      parameters[1].ParameterType.GetGenericArguments()[1] == argType &&
      method.ReturnType == returnType
      select method;

      return methods.FirstOrDefault();
}

Outras dicas

Uma vez que você está construindo uma expressão em vez de executar diretamente, você pode pular a etapa MethodInfo e ir direto para o MethodCallExpression usando a sobrecarga Expression.Call que leva o nome do método, em vez de um MethodInfo.

var call = Expression.Call(typeof(Enumerable),
             "Average",
            new Type[] { typeof(MyTSource) },
            enumerableParameter, lambdaParameter
                );

Aqui está como fazê-lo:

static MethodInfo GetMethod(Type argType, Type returnType)
{
    var methods = from m in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                  where m.ContainsGenericParameters
                  && m.Name == "Average"
                  && m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)
                  && m.GetParameters()[1].ParameterType.GetGenericArguments()[1] == returnType
                  select m;
    return methods.First();
}

@Joren Obrigado pela dica ligada. Esse exemplo diferencia com base na contagem de argumento para, mas isso me levou na direção certa.

A seleção que funciona é

var methods = from method in typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static)
where method.Name == Name &&
method.ContainsGenericParameters &&                                                    
method.ReturnType == returnType &&
method.GetParameters().Length == 2 &&
method.GetParameters()[1].ParameterType.GetGenericArguments().Count() == 2 &&
method.GetParameters()[1].ParameterType.GetGenericArguments()[1] == argType
select method;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top