【问题标题】:How do I invoke an extension method using reflection?如何使用反射调用扩展方法?
【发布时间】:2009-09-20 23:04:06
【问题描述】:

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

【问题讨论】:

    标签: c# linq reflection delegates lambda


    【解决方案1】:

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

    【讨论】:

    • 感谢您为此付出的努力。非常感谢。
    • 我想没有直接的方法让框架选择合适的方法?那就是我一直在寻找的东西,但找不到任何东西。 +1 解决问题。
    • 我喜欢使用 where 方法找到 where 方法的讽刺 :-)
    【解决方案2】:

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

    【讨论】:

    • 我得到一个 MissingMethodException 使用 var ret = typeof(System.Linq.Enumerable).InvokeMember("Where", BindingFlags.InvokeMethod, null, InstanceList, atts);有任何想法吗?谢谢。
    • 问题是方法是通用的。我发现了一篇关于调用泛型方法的不错的博文 (blogs.microsoft.co.il/blogs/gilf/archive/2008/10/10/…),但还有一个额外的问题:Enumerable.Where 有两个重载,只是其中一个参数的类型不同(Func与 Func) 相比,我还没有找到如何巧妙地选择你想要的。
    【解决方案3】:

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

    IEnumerable&lt;dynamic&gt; test = obj as IEnumerable&lt;dynamic&gt;;

    如果不为空,则可能测试 obj 并且

    int count = test.Count()

    对我来说效果很好。

    【讨论】:

    • 谢谢你,动态改变了游戏规则,我已经改变了我的整个方法!
    【解决方案4】:

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

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

    【讨论】:

    • 很好,我的问题有一个错误(现已更新),我试过 var ret = typeof(System.Linq.Enumerable).InvokeMember("Where", BindingFlags.InvokeMethod, null,实例列表,atts);没有成功。有任何想法吗?谢谢。
    • 第一个参数必须是要执行的枚举。第二个必须是返回 true 或 false 的函数。此外,调用静态函数有点棘手。自从我完成它以来已经有一段时间了 - 我建议调用一个简单的静态来确保您的命令正确。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

      【讨论】:

      • 它们是一种 .NET 技巧,而不是 C# 技巧。 VB.NET 也可以制作和使用它们,尽管语法不同,并且涉及属性。
      • 它们仍然是编译器技巧,只是 VB.NET 编译器也使用了相同的技巧。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 1970-01-01
      相关资源
      最近更新 更多