查找并调用通用重载方法

时间:2009-12-11 12:29:32

标签: c# generics reflection invoke overloading

如何找到通用重载方法?例如,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它。

这对于任务来说似乎有点过于复杂,所以也许我会过度思考整个事情。

任何帮助?

4 个答案:

答案 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类的自定义子类。自定义活页夹可以改变选择成员的方式,因为它们会收到要从中挑选的候选成员列表。