Как мне найти конкретную общую перегрузку с помощью отражения?
-
13-09-2019 - |
Вопрос
Я пытаюсь создать Expression
это вызовет определенный общий перегруженный метод (Enumerable.Average
в моем первом тестовом примере).Однако привязки конкретного типа неизвестны до времени выполнения, поэтому мне нужно использовать Reflection
чтобы найти и создать правильный универсальный метод (the 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)
.
Я в тупике, и я ценю любую помощь.
Решение
Вам нужно использовать MethodInfo.Создать общий метод
Редактировать:Хорошо, я неправильно понял проблему...Этот метод должен делать то, что вы хотите :
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();
}
Другие советы
Поскольку вы создаете выражение, а не выполняете напрямую, вы можете пропустить шаг MethodInfo и перейти сразу к MethodCallExpression, используя выражение.Перегрузка вызова, которая принимает имя метода, а не MethodInfo .
var call = Expression.Call(typeof(Enumerable),
"Average",
new Type[] { typeof(MyTSource) },
enumerableParameter, lambdaParameter
);
Вот как это сделать:
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 за подсказку по ссылке.Этот пример отличается в зависимости от количества аргументов, но он направил меня в правильном направлении.
Выбор, который работает, - это
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;