简体   繁体   English

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

[英]How do I invoke an extension method using reflection?

I appreciate that similar questions have been asked before, but I am struggling to invoke the Linq Where method in the following code.我很感激之前有人问过类似的问题,但我很难在以下代码中调用 Linq Where方法。 I am looking to use reflection to dynamically call this method and also dynamically build the delegate (or lambda) used in the Where clause.我希望使用反射来动态调用此方法,并动态构建Where子句中使用的委托(或 lambda)。 This is a short code sample that, once working, will help to form part of an interpreted DSL that I am building.这是一个简短的代码示例,一旦工作,将有助于形成我正在构建的解释型 DSL 的一部分。 Cheers.干杯。

    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;
    }

As others said, extensions methods are compiler magic, you can alway use VS right click, go to definition to find the real type that implements the static method.正如其他人所说,扩展方法是编译器魔术,您可以随时使用VS右键单击,转到定义找到实现静态方法的真实类型。

From there, it gets fairly hairy .从那里,它变得相当多毛 Where is overloaded, so you need to find the actual definition that matches the signature you want. Where是重载的,因此您需要找到与您想要的签名匹配的实际定义。 GetMethod has some limitations with generic types so you have to find the actual one using a search. GetMethod对泛型类型有一些限制,因此您必须使用搜索找到实际的类型。

Once you find the method, you must make the MethodInfo specific using the MakeGenericMethod call.找到该方法后,您必须使用MakeGenericMethod调用使MethodInfo特定。

Here is a full working sample:这是一个完整的工作示例:

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();

        }
    }
}

Extension methods are really just static methods underwater.扩展方法实际上只是水下的静态方法。 An extension method call like foo.Frob( arguments ) is really just SomeClass.Frob(foo, arguments ).像 foo.Frob( arguments ) 这样的扩展方法调用实际上只是 SomeClass.Frob(foo, arguments )。 In the case of the Where method, you're looking for System.Linq.Enumerable.Where.在 Where 方法的情况下,您正在寻找 System.Linq.Enumerable.Where。 So get the typeof Enumerable and invoke Where on that.因此,获取 Enumerable 的 typeof 并调用 Where 。

I'm a bit off and late but this could help you if you need to call Linq extensions of a IEnumerable wich type is unkown.我有点晚了,但是如果您需要调用 IEnumerable 类型未知的 Linq 扩展,这可以帮助您。

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

then maybe test obj if not null and然后也许测试 obj 如果不是 null 和

int count = test.Count()

for me that worked very well.对我来说效果很好。

Your code sample is a little confusing... unless MyObject is an enumerable.您的代码示例有点令人困惑……除非 MyObject 是可枚举的。

Using reflection you'll have to invoke Where on System.Linq.Enumerable, passing in the enumerable you want to preform Where on.使用反射,您必须调用 System.Linq.Enumerable 上的 Where,传入您想要执行 Where on 的枚举。

Here's an answer for a general case where the method name is unique (so not in the same case the original posted asked, because Enumerable.Where is overloaded).这是方法名称唯一的一般情况的答案(因此与原始发布的情况不同,因为 Enumerable.Where 已重载)。

Say you have a target object targetObject of the type which is extended, where the extension method is defined in a class TargetClassExtensions and whose extension method's name is ExtensionMethod which takes in an integer parameter and is generic for which you want to call for the class TargetGenericClass .假设您有一个扩展类型的目标对象targetObject ,其中扩展方法在类TargetClassExtensions定义,其扩展方法的名称是ExtensionMethod ,它接受一个整数参数并且是您想要为类TargetGenericClass调用的TargetGenericClass .

Then, to call this extension method through reflection, do the following:然后,要通过反射调用此扩展方法,请执行以下操作:

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 });

Extention methods are ac# compiler trick, and they don't exist in the type concerned.扩展方法是 ac# 编译器技巧,它们不存在于相关类型中。 They (these particular ones) exist in static classes within the System.Linq name spaces.它们(这些特定的)存在于 System.Linq 名称空间内的静态类中。 I'd suggest reflecting this in reflector and then invoking reflection on these types.我建议在反射器中反映这一点,然后对这些类型进行反射。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM