Come trovo specifica sovraccarico generico utilizzando la riflessione?
-
13-09-2019 - |
Domanda
Sto tentando di creare un Expression
che invocare una specifica generica metodo di overload (Enumerable.Average
nel mio primo banco di prova). Gli attacchi di tipo specifico non sono noti fino al runtime tuttavia quindi ho bisogno di usare Reflection
per trovare e creare il metodo generico corretta (la Expression
viene creato dal testo analizzato).
Quindi, se io so in fase di esecuzione che voglio trovare questo sovraccarico specifica:
public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
Come posso risolvere quel particolare MethodInfo
utilizzando la riflessione?
Finora ho la seguente dichiarazione selezione:
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();
}
È possibile che questo restringe il campo a tre sovraccarichi ma voglio riflettere e trovare il sovraccarico di specifico che prende un Func<TSource, int>
dove argType == typeof(int)
.
Sono perplesso e ogni aiuto è apprezzato.
Soluzione
È necessario utilizzare MethodInfo.MakeGenericMethod
EDIT: OK, avevo frainteso il problema ... Questo metodo dovrebbe fare quello che vuoi:
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();
}
Altri suggerimenti
Dal momento che si sta costruendo un'espressione piuttosto che eseguire direttamente, è possibile ignorare il passaggio MethodInfo e andare direttamente al MethodCallExpression utilizzando il sovraccarico Expression.Call che prende nome di un metodo piuttosto che un MethodInfo.
var call = Expression.Call(typeof(Enumerable),
"Average",
new Type[] { typeof(MyTSource) },
enumerableParameter, lambdaParameter
);
Ecco come si fa:
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();
}
Grazie per il suggerimento @Joren collegato. Questo esempio differenzia basa sul conteggio argomento per, ma mi ha fatto nella giusta direzione.
La scelta che funziona è
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;