2

オーバーロードされた一般的なメソッドを見つけるにはどうすればよいですか? たとえばQueryable

public static IQueryable<TResult> Select<TSource , TResult> ( this IQueryable<TSource> source , Expression<Func<TSource , int , TResult>> selector );

私は既存のソリューションを探しましたが、それらは十分に汎用的ではない (メソッドのパラメーター数に基づいているなど)、私が持っているよりも多くのパラメーターが必要な (ジェネリック型の定義または引数が必要)、または単純に間違っている (don 'ネストされたジェネリックなどを考慮しないでください。)

定義クラスの型 — Type type、メソッド名 —string nameおよびパラメーター型の配列 (ジェネリック定義ではない) — がありType[] typesます。

.GetGenericArguments()これまでのところ、メソッドの(ジェネリック型ツリー?)を配列.GetParameters ().Select (p=>p.ParameterType)内の対応する項目と比較して、メソッドのtypesジェネリック引数を推測することにより、各メソッドを特定の型にマップする必要がある.MakeGenericMethodようです。

これはタスクには少し複雑すぎるように思えるので、全体を考えすぎているのかもしれません。

何か助けはありますか?

4

4 に答える 4

4

はい、基本的にすべての方法に目を通す必要があります。型パラメータの数などを指定するだけでは、ジェネリック メソッドに直接到達する方法はありません。

ただし、使用できることの 1 つは、ジェネリック性以外の同じシグネチャを持つオーバーロードは、ジェネリック型パラメーターの数によってのみオーバーロードされるという事実です。制約または型パラメーター名を使用して、次のことを行うことはできません。

 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 に答える