Comment je trouve une surcharge générique spécifique en utilisant la réflexion?
-
13-09-2019 - |
Question
Je cherche à créer un Expression
qui invoquer une méthode surchargée générique spécifique (Enumerable.Average
dans mon premier test). Les liaisons de type spécifique ne sont pas connus jusqu'à ce que l'exécution mais si je dois utiliser Reflection
pour trouver et créer la méthode générique correcte (la Expression
est créée à partir du texte analysé).
Donc, si je sais à l'exécution que je veux trouver cette surcharge spécifique:
public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
Comment puis-je résoudre ce MethodInfo
particulier en utilisant la réflexion?
Jusqu'à présent, je compte de la sélection suivante:
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();
}
Le rétrécissant au-dessus jusqu'à trois, mais je veux les surcharges à réfléchir et à trouver la surcharge spécifique qui prend un Func<TSource, int>
où argType == typeof(int)
.
Je suis perplexe et toute aide est appréciée.
La solution
Vous devez utiliser MethodInfo.MakeGenericMethod
EDIT: OK, j'avais mal compris le problème ... Cette méthode devrait faire ce que vous voulez:
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();
}
Autres conseils
Puisque vous construisez une expression plutôt que d'exécuter directement, vous pouvez sauter l'étape MethodInfo et aller directement à la MethodCallExpression en utilisant la surcharge de Expression.Call qui prend un nom de méthode plutôt qu'une MethodInfo.
var call = Expression.Call(typeof(Enumerable),
"Average",
new Type[] { typeof(MyTSource) },
enumerableParameter, lambdaParameter
);
Voici comment faire:
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();
}
Merci pour l'astuce @Joren liée. Cet exemple différencie en fonction du nombre d'arguments pour, mais il m'a dans la bonne direction.
La sélection qui fonctionne est
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;