Как вызвать метод расширения с помощью отражения? - PullRequest
29 голосов
/ 21 сентября 2009

Я ценю, что подобные вопросы уже задавались ранее, но я изо всех сил пытаюсь вызвать метод Linq Где в следующем коде. Я хочу использовать рефлексию для динамического вызова этого метода, а также для динамического построения делегата (или лямбды), используемого в предложении Где Это пример короткого кода, который после работы поможет сформировать часть интерпретируемого 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;
    }

Ответы [ 5 ]

46 голосов
/ 21 сентября 2009

Как уже говорили другие, методы расширений - это магия компилятора, вы всегда можете использовать 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();

        }
    }
}
10 голосов
/ 21 сентября 2009

Методы расширения на самом деле просто статические методы под водой. Вызов метода расширения, например, foo.Frob ( arguments ) - это на самом деле просто SomeClass.Frob (foo, arguments ). В случае метода Where вы ищете System.Linq.Enumerable.Where. Так что получите typeof Enumerable и вызовите Where на этом.

2 голосов
/ 28 декабря 2012

Я немного опоздал, но это могло бы помочь вам, если вам нужно вызвать расширения Linq типа IEnumerable, который неизвестен.

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

тогда, возможно, проверьте obj, если не ноль, и

int count = test.Count()

для меня это сработало очень хорошо.

1 голос
/ 21 сентября 2009

Ваш пример кода немного сбивает с толку ... если MyObject не является перечисляемым.

Используя рефлексию, вам придется вызывать Where в System.Linq.Enumerable, передавая перечисляемое значение, которое вы хотите преформировать Where on.

0 голосов
/ 21 сентября 2009

Методы расширения - это трюк компилятора c #, и они не существуют в соответствующем типе. Они (именно эти) существуют в статических классах в пространствах имен System.Linq. Я бы предложил отразить это в отражателе, а затем вызвать отражение этих типов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...