Como eu encontro sobrecarga genérico específico usando reflexão?
-
13-09-2019 - |
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.
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;