如何找到通用重载方法?例如,Queryable
的
public static IQueryable<TResult> Select<TSource , TResult> ( this IQueryable<TSource> source , Expression<Func<TSource , int , TResult>> selector );
我已经找到了现有的解决方案,它们要么不够通用(基于方法的参数计数等),需要比我更多的参数(需要通用类型定义或参数),或者只是普通的错误(不考虑嵌套泛型等)
我有定义的类类型 - Type type
,方法名称 - string name
和参数类型的数组(不是通用定义) - Type[] types
。
到目前为止,似乎我必须通过比较方法.GetGenericArguments()
的(通用类型树?)和{{1}中的相应项来将每个预期方法的.GetParameters ().Select (p=>p.ParameterType)
映射到特定类型数组,因此推导出方法的泛型参数,所以我可以types
它。
这对于任务来说似乎有点过于复杂,所以也许我会过度思考整个事情。
任何帮助?
答案 0 :(得分:3)
是的,您必须基本查看所有方法。只需指定类型参数的数量等,就无法直接使用泛型方法。
但是,你可以使用的一件事是,除了泛型之外,具有相同签名的重载只会被泛型类型参数的 number 重载。您不能使用约束或类型参数名称来:
void Foo<T1>(String x)
void Foo<T2>(String y)
等
是的,这一切都非常复杂。如果我是你,我会努力避免需要这样做。
答案 1 :(得分:1)
真正使用反射调用泛型方法可能会很难看。要解决正确的MethodInfo
我通常会尝试选择最简单的唯一因子并使用它 - 这可能意味着泛型参数/方法参数的数量等。我尽量避免必须比较未绑定的泛型类型自己。
在4.0中使用dynamic
,我相信(虽然我需要检查)这可以使这些调用中的许多更简单(因为它基本上是在方法/通用解析运行时),但它不适用于扩展方法; - (
另一个选项是(实际上,忘记了 - 它仍然很难)Expression
,在某些方面可能会更漂亮,但你需要Compile()
等,而Expression
本身复杂。
答案 2 :(得分:1)
好的,所以我只编写了这个,这本质上是一个手动类型推断,我认为应该做我需要的。
public static class TypeExtensions {
public static Type GetTypeDefinition ( this Type type ) {
return type.IsGenericType ? type.GetGenericTypeDefinition () : type;
}
public static IEnumerable<Type> GetImproperComposingTypes ( this Type type ) {
yield return type.GetTypeDefinition ();
if ( type.IsGenericType ) {
foreach ( var argumentType in type.GetGenericArguments () ) {
foreach ( var t in argumentType.GetImproperComposingTypes () ) yield return t;
}
}
}
private static Dictionary<Type , Type> GetInferenceMap ( ParameterInfo[] parameters , Type[] types ) {
var genericArgumentsMap = new Dictionary<Type , Type> ();
var match = parameters.All ( parameter => parameter.ParameterType.GetImproperComposingTypes ().Zip ( types[parameter.Position].GetImproperComposingTypes () ).All ( a => {
if ( !a.Item1.IsGenericParameter ) return a.Item1 == a.Item2;
if ( genericArgumentsMap.ContainsKey ( a.Item1 ) ) return genericArgumentsMap[a.Item1] == a.Item2;
genericArgumentsMap[a.Item1] = a.Item2;
return true;
} ) );
return match ? genericArgumentsMap : null;
}
public static MethodInfo MakeGenericMethod ( this Type type , string name , Type[] types ) {
var methods = from method in type.GetMethods ()
where method.Name == name
let parameters = method.GetParameters ()
where parameters.Length == types.Length
let genericArgumentsMap = GetInferenceMap ( parameters , types )
where genericArgumentsMap != null
where method.GetGenericArguments ().Length == genericArgumentsMap.Keys.Count ()
select new {
method ,
genericArgumentsMap
};
return methods.Select ( m => m.method.IsGenericMethodDefinition ? m.method.MakeGenericMethod ( m.method.GetGenericArguments ().Map ( m.genericArgumentsMap ).ToArray () ) : m.method ).SingleOrDefault ();
}
}
所以给出
public class Foos {
public void Foo<T1 , T2 , T3> ( int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , string>> d ) {
}
public void Foo<T1 , T2 , T3> ( int a , T1 b , IEnumerable<T2> c , Expression<Func<T1 , T3 , int>> d ) {
}
public void Foo () {
}
public void Foo ( string s ) {
}
}
我可以选择我想要的方法:
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , new[] { typeof ( int ) , typeof ( DateTime ) , typeof ( IEnumerable<string> ) , typeof ( Expression<Func<DateTime , double , int>> ) } );
method.Invoke ( new Foos () , new object[] { 1 , DateTime.Now , null , null } );
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , Type.EmptyTypes );
method.Invoke ( new Foos () , new object[] { } );
var method = typeof ( Foos ).MakeGenericMethod ( "Foo" , new[] { typeof ( string ) } );
method.Invoke ( new Foos () , new object[] { "zozo" } );
这似乎支持非泛型方法,但不支持显式泛型参数(显然),也许需要一些工作,但这是它的核心。
答案 3 :(得分:0)
在Type实例上调用GetMethod或InvokeMember时,您可以传递Binder类的自定义子类。自定义活页夹可以改变选择成员的方式,因为它们会收到要从中挑选的候选成员列表。