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

发布于 2024-08-05 18:18:20 字数 888 浏览 8 评论 0原文

我很高兴以前有人问过类似的问题,但我很难在下面的代码中调用 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;
    }

I appreciate that similar questions have been asked before, but I am struggling to invoke the Linq Where method in the following code. I am looking to use reflection to dynamically call this method and also dynamically build the delegate (or lambda) used in the Where clause. This is a short code sample that, once working, will help to form part of an interpreted DSL that I am building. 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;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

原来是傀儡 2024-08-12 18:18:20

正如其他人所说,扩展方法是编译器的魔法,您始终可以使用VS右键单击,转到定义来查找实现静态方法的真实类型。

从那里开始,它变得相当多毛Where 已重载,因此您需要找到与您想要的签名匹配的实际定义。 GetMethod 对于泛型类型有一些限制,因此您必须使用搜索来找到实际的类型。

找到该方法后,必须使用 MakeGenericMethod 调用使 MethodInfo 特定。

这是一个完整的工作示例:

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

        }
    }
}

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.

From there, it gets fairly hairy. Where is overloaded, so you need to find the actual definition that matches the signature you want. GetMethod has some limitations with generic types so you have to find the actual one using a search.

Once you find the method, you must make the MethodInfo specific using the MakeGenericMethod call.

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

        }
    }
}
属性 2024-08-12 18:18:20

扩展方法实际上只是水下的静态方法。像 foo.Frob(arguments) 这样的扩展方法调用实际上只是 SomeClass.Frob(foo, arguments)。对于Where 方法,您要查找System.Linq.Enumerable.Where。因此,获取 Enumerable 的类型并在其上调用Where。

Extension methods are really just static methods underwater. An extension method call like foo.Frob(arguments) is really just SomeClass.Frob(foo, arguments). In the case of the Where method, you're looking for System.Linq.Enumerable.Where. So get the typeof Enumerable and invoke Where on that.

深居我梦 2024-08-12 18:18:20

我有点晚了,但是如果您需要调用 IEnumerable 类型未知的 Linq 扩展,这可以帮助您。

IEnumerable<动态> test = obj as IEnumerable;

然后也许测试 obj if not null 和

int count = test.Count()

对我来说效果很好。

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<dynamic> test = obj as IEnumerable<dynamic>;

then maybe test obj if not null and

int count = test.Count()

for me that worked very well.

天气好吗我好吗 2024-08-12 18:18:20

这是方法名称唯一的一般情况的答案(因此与原始问题提出的情况不同,因为 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 });

Here's an answer for a general case where the method name is unique (so not the same case the original question posed, because Enumerable.Where is overloaded).

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.

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 });
不醒的梦 2024-08-12 18:18:20

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

使用反射,您必须调用 System.Linq.Enumerable 上的Where,并传入要执行Where on 的可枚举值。

Your code sample is a little confusing... unless MyObject is an enumerable.

Using reflection you'll have to invoke Where on System.Linq.Enumerable, passing in the enumerable you want to preform Where on.

初吻给了烟 2024-08-12 18:18:20

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

Extention methods are a c# compiler trick, and they don't exist in the type concerned. They (these particular ones) exist in static classes within the System.Linq name spaces. I'd suggest reflecting this in reflector and then invoking reflection on these types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文