如何使用反射调用扩展方法?

时间:2009-09-20 23:04:06

标签: c# linq reflection delegates lambda

我很感激之前已经提出了类似的问题,但是我很难在以下代码中调用Linq Where 方法。我希望使用反射来动态调用此方法,并动态构建 Where 子句中使用的委托(或lambda)。这是一个简短的代码示例,一旦工作,将有助于形成我正在构建的解释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;
    }

6 个答案:

答案 0 :(得分:45)

正如其他人所说,扩展方法是编译魔术,你可以一直使用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();

        }
    }
}

答案 1 :(得分:10)

扩展方法实际上只是水下的静态方法。像foo.Frob( arguments )这样的扩展方法调用实际上只是SomeClass.Frob(foo, arguments )。在Where方法的情况下,您正在寻找System.Linq.Enumerable.Where。因此,获取typeof Enumerable并调用Where on on。

答案 2 :(得分:2)

我有点休息但是如果你需要调用一个IEnumerable类型的Linq扩展,这可能对你有所帮助。

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

然后可能测试obj,如果不是null和

int count = test.Count()

对我来说非常好。

答案 3 :(得分:1)

您的代码示例有点令人困惑......除非MyObject是可枚举的。

使用反射你必须调用System.Linq.Enumerable上的Where,传入你要在其中预先形成的枚举。

答案 4 :(得分:0)

扩展方法是一种c#编译器技巧,它们在相关类型中不存在。它们(这些特定的)存在于System.Linq名称空间内的静态类中。我建议在反射器中反映这一点,然后调用这些类型的反射。

答案 5 :(得分:0)

这是方法名称唯一的一般情况的答案(因此,与原来发布的方法不同,因为Enumerable.Where重载了)。

假设您有一个扩展类型的目标对象targetObject,其中扩展方法在类TargetClassExtensions中定义,并且扩展方法的名称为ExtensionMethod,该对象采用一个整数参数,是您要为其调用类TargetGenericClass的通用参数。

然后,通过反射调用此扩展方法,请执行以下操作:

int inputInteger = 9; // Example input for the generic method.

object? result = typeof(TargetClassExtensions)
    .GetMethod(nameof(TargetClassExtensions.ExtensionMethod))
    .MakeGenericMethod(typeof(TargetGenericClass))
    .Invoke(null, new object[] { targetObject, inputInteger });