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.

¿Fue útil?

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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top