web-dev-qa-db-ja.com

リフレクションを使用して拡張メソッドを呼び出すにはどうすればよいですか?

以前に同様の質問があったことを感謝しますが、次のコードでLinq Whereメソッドを呼び出すのに苦労しています。リフレクションを使用してこのメ​​ソッドを動的に呼び出し、Where句で使用されるデリゲート(またはラムダ)を動的に構築しようとしています。これは短いコードサンプルであり、機能すると、私が構築しているインタープリター型DSLの一部を形成するのに役立ちます。乾杯。

    public static void CallWhereMethod()
    {
        List<MyObject> myObjects = new List<MyObject>(){new MyObject{Name="Jon Simpson"}};
        System.Delegate NameEquals = BuildEqFuncFor<MyObject>("Name", "Jon Simpson");
        object[] atts = new object[1] ;
        atts[0] = NameEquals;

        var ret = typeof(List<MyObject>).InvokeMember("Where", BindingFlags.InvokeMethod, null, InstanceList,atts);
    }

    public static Func<T, bool> BuildEqFuncFor<T>(string prop, object val)
    {
        return t => t.GetType().InvokeMember(prop,BindingFlags.GetProperty,
                                             null,t,null) == val;
    }
34
Jon Simpson

他の人が言ったように、拡張メソッドはコンパイラの魔法です。いつでもVSを右クリックして、定義に移動し、静的メソッドを実装する実際の型を見つけることができます。

そこから、かなり毛深いになります。 Whereはオーバーロードされているため、必要な署名に一致する実際の定義を見つける必要があります。 GetMethodにはジェネリック型にいくつかの制限があるため、検索を使用して実際の型を見つける必要があります。

メソッドを見つけたら、MethodInfo呼び出しを使用してMakeGenericMethod固有にする必要があります。

これが完全に機能するサンプルです:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication9 {
    class Program {

        class MyObject {
            public string Name { get; set; }
        } 

        public static void CallWhereMethod() {
            List<MyObject> myObjects = new List<MyObject>() { 
                new MyObject { Name = "Jon Simpson" },
                new MyObject { Name = "Jeff Atwood" }
            };


            Func<MyObject, bool> NameEquals = BuildEqFuncFor<MyObject>("Name", "Jon Simpson");


            // The Where method lives on the Enumerable type in System.Linq
            var whereMethods = typeof(System.Linq.Enumerable)
                .GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Where(mi => mi.Name == "Where"); 

            Console.WriteLine(whereMethods.Count());
            // 2 (There are 2 methods that are called Where)

            MethodInfo whereMethod = null;
            foreach (var methodInfo in whereMethods) {
                var paramType = methodInfo.GetParameters()[1].ParameterType;
                if (paramType.GetGenericArguments().Count() == 2) {
                    // we are looking for  Func<TSource, bool>, the other has 3
                    whereMethod = methodInfo;
                }
            }

            // we need to specialize it 
            whereMethod = whereMethod.MakeGenericMethod(typeof(MyObject));

            var ret = whereMethod.Invoke(myObjects, new object[] { myObjects, NameEquals }) as IEnumerable<MyObject>;

            foreach (var item in ret) {
                Console.WriteLine(item.Name);
            }
            // outputs "Jon Simpson"

        }

        public static Func<T, bool> BuildEqFuncFor<T>(string prop, object val) {
            return t => t.GetType().InvokeMember(prop, BindingFlags.GetProperty,
                                                 null, t, null) == val;
        }

        static void Main(string[] args) {
            CallWhereMethod();
            Console.ReadKey();

        }
    }
}
49
Sam Saffron

拡張メソッドは、実際には水中での静的メソッドにすぎません。 foo.Frob(arguments)のような拡張メソッド呼び出しは、実際にはSomeClass.Frob(foo、arguments)です。 Whereメソッドの場合、System.Linq.Enumerable.Whereを探しています。したがって、typeof Enumerableを取得し、その上でWhereを呼び出します。

10
Joren

私は少し遅れて遅れていますが、タイプが不明なIEnumerableのLinq拡張機能を呼び出す必要がある場合に役立ちます。

_IEnumerable<dynamic> test = obj as IEnumerable<dynamic>;_

次に、nullでない場合はobjをテストし、

int count = test.Count()

私にとっては非常にうまくいきました。

2
Jens Marchewka

MyObjectが列挙可能でない限り、コードサンプルは少し混乱します。

リフレクションを使用して、Where on System.Linq.Enumerableを呼び出し、実行する列挙型をWhereonに渡す必要があります。

1
user1228

拡張メソッドはc#コンパイラのトリックであり、関係する型には存在しません。それら(これらの特定のもの)は、System.Linq名前空間内の静的クラスに存在します。これをリフレクターに反映してから、これらのタイプにリフレクションを呼び出すことをお勧めします。

0
Preet Sangha