Wie ich bestimmte allgemeine Überlastung mit Reflexion finden?
-
13-09-2019 - |
Frage
Ich versuche, eine Expression
zu erstellen, die eine bestimmte generisch überladene Methode (Enumerable.Average
in meinem ersten Testfall) aufruft. Die spezifische Typ Bindungen werden erst zur Laufzeit jedoch bekannt, so dass ich Reflection
verwenden muß, finden und erstellen die richtige generische Methode (die Expression
aus analysiert Text erstellt werden).
Also, wenn ich weiß, zur Laufzeit, dass ich diese spezifische Überlastung finden will:
public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
Wie löse ich diese bestimmte MethodInfo
Reflexion mit?
Bisher habe ich die folgende Auswahlanweisung:
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();
}
Die es oben verengt zu drei Überlastungen nach unten, aber ich möchte reflektieren und die spezifische Überlastung zu finden, die eine Func<TSource, int>
nimmt wo argType == typeof(int)
.
Ich bin ratlos und jede Hilfe ist willkommen.
Lösung
Sie müssen verwenden MethodInfo.MakeGenericMethod
EDIT: OK, ich habe das Problem falsch verstanden hatte ... Diese Methode sollte das tun, was Sie wollen:
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();
}
Andere Tipps
Da Sie einen Ausdruck Gebäude sind nicht direkt ausgeführt wird, können Sie den Schritt Method überspringen und direkt auf die Method die Expression.Call Überlastung verwenden, die einen Methodennamen nimmt eher als ein Method.
var call = Expression.Call(typeof(Enumerable),
"Average",
new Type[] { typeof(MyTSource) },
enumerableParameter, lambdaParameter
);
Hier ist, wie es zu tun:
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();
}
Danke @Joren für den verknüpften Hinweis. Das Beispiel differenziert auf Argument Zählung basiert, aber es hat mich in der richtigen Richtung.
Die Auswahl, die funktioniert, ist
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;