2

如何找到泛型重载方法?例如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()进行比较来将每个预期方法映射到特定类型,从而推断出该方法的通用参数,所以我可以做到。.GetParameters ().Select (p=>p.ParameterType)types.MakeGenericMethod

对于这项任务来说,这似乎有点太复杂了,所以也许我想太多了。

有什么帮助吗?

4

4 回答 4

4

是的,您必须基本上查看所有方法。仅通过指定类型参数的数量等,无法直接使用泛型方法。

但是,您可以使用的一件事是,除了泛型之外,具有相同签名的重载只会被泛型类型参数的数量所重载。您不能使用约束或类型参数名称来拥有:

 void Foo<T1>(String x)
 void Foo<T2>(String y)

等等

是的,这一切都很复杂。如果我是你,我会非常努力地避免需要这样做。

于 2009-12-11T12:42:37.330 回答
1

真正使用反射调用泛型方法可能很丑陋。为了解决正确的问题,MethodInfo我通常会尝试选择最简单的独特因素并使用它 - 这可能意味着泛型参数/方法参数等的数量。我尽量避免不得不比较未绑定的泛型类型本身。

dynamic4.0 中,我相信(尽管我需要检查)这可以使许多这些调用更简单(因为它本质上是在运行时执行方法/通用解析),但它不适用于扩展方法;-(

另一种选择是Expression,它在某些方面可能更漂亮一些,但你需要Compile()它等等,而且Expression本身很复杂。(实际上,忘记这一点 - 这仍然很难)

于 2009-12-11T12:43:37.900 回答
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" } );

这似乎支持非泛型方法,但不支持显式泛型参数(显然),并且可能需要一些工作,但这是它的核心。

于 2009-12-13T07:42:22.527 回答
0

当您在 Type 实例上调用 GetMethod 或 InvokeMember 时,您可以传递Binder类的自定义子类。自定义活页夹可以改变选择成员的方式,因为他们会收到可供选择的候选成员列表。

于 2009-12-11T12:47:37.930 回答