¿Cómo puedo encontrar la sobrecarga genérica específica utilizando la reflexión?
-
13-09-2019 - |
Pregunta
Estoy intentando crear un Expression
que invocará una específica genérica método sobrecargado (Enumerable.Average
en mi primer caso de prueba). Los enlaces de tipo específico no se conocen hasta el tiempo de ejecución sin embargo así que necesito utilizar Reflection
de encontrar y crear el método genérico correcto (el Expression
se crea a partir del texto analizado).
Así que si sé en tiempo de ejecución que quiero encontrar esta sobrecarga específica:
public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
¿Cómo resuelvo que MethodInfo
particular a través de la reflexión?
Hasta ahora tengo la siguiente declaración de selección:
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();
}
Lo anterior lo reduce a tres sobrecargas pero quiero reflexionar y encontrar la sobrecarga específica que toma un Func<TSource, int>
donde argType == typeof(int)
.
Estoy perplejo y cualquier ayuda se agradece.
Solución
Es necesario utilizar MethodInfo.MakeGenericMethod
EDIT: OK, yo había entendido mal el problema ... Este método debe hacer lo que quiera:
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();
}
Otros consejos
Dado que usted está construyendo una expresión en lugar de ejecutar directamente, puede omitir el paso MethodInfo e ir directamente a la MethodCallExpression utilizando la sobrecarga de Expression.Call que tiene un nombre de método en lugar de un MethodInfo.
var call = Expression.Call(typeof(Enumerable),
"Average",
new Type[] { typeof(MyTSource) },
enumerableParameter, lambdaParameter
);
He aquí cómo hacerlo:
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();
}
Gracias por la pista @Joren vinculado. Ese ejemplo que diferencia basa en el recuento de argumento para pero me consiguió en la dirección correcta.
La selección que funciona es
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;