Как найти конкретную общую перегрузку с помощью отражения?

Я пытаюсь создать Expression, который будет вызывать определенный общий перегруженный метод (Enumerable.Average в моем первом тестовом примере). Однако привязки конкретных типов неизвестны до времени выполнения, поэтому мне нужно использовать Reflection, чтобы найти и создать правильный общий метод (Expression создается из проанализированного текста).

Итак, если я знаю во время выполнения, что хочу найти эту конкретную перегрузку:

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

Как разрешить этот конкретный MethodInfo с помощью отражения?

Пока у меня есть следующее утверждение выбора:

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();
}

Вышеизложенное сужает его до трех перегрузок, но я хочу подумать и найти конкретную перегрузку, которая принимает Func<TSource, int>, где argType == typeof(int).

Я в тупике, и любая помощь приветствуется.


person dkackman    schedule 23.09.2009    source источник
comment
Здесь есть соответствующий ответ Сэма Саффрона: stackoverflow.com/questions/1452261/. Вопрос касается методов расширения, но ответ по-прежнему относится к вашему вопросу, поскольку рассматриваемый метод расширения (System.Enumerable.Where) является перегруженным универсальным методом, очень похожим на ваш.   -  person Joren    schedule 23.09.2009


Ответы (4)


Вам необходимо использовать MethodInfo.MakeGenericMethod

РЕДАКТИРОВАТЬ: ОК, я неправильно понял проблему... Этот метод должен делать то, что вы хотите:

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();
}
person Thomas Levesque    schedule 23.09.2009
comment
Правильно, но сначала мне нужно найти правильный MethodInfo для создания реализации. Enumerable.Average имеет перегрузку, которая принимает Func‹T, int›, Func‹T, double›, Func‹T, Decimal› и т. д. - person dkackman; 23.09.2009

Поскольку вы строите выражение, а не выполняете его напрямую, вы можете пропустить шаг MethodInfo и сразу перейти к MethodCallExpression, используя перегрузку Expression.Call, которая принимает имя метода, а не MethodInfo.

var call = Expression.Call(typeof(Enumerable),
             "Average",
            new Type[] { typeof(MyTSource) },
            enumerableParameter, lambdaParameter
                );
person tnyfst    schedule 23.09.2009
comment
Хммм. Я пропустил эту перегрузку для Колл. Я должен взглянуть. - person dkackman; 23.09.2009

Вот как это сделать:

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();
}
person Pop Catalin    schedule 23.09.2009

Спасибо @Joren за подсказку. Этот пример различается на основе количества аргументов, но он привел меня в правильном направлении.

Выбор, который работает,

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;
person dkackman    schedule 23.09.2009