簡體   English   中英

如何使用反射調用擴展方法?

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

我很感激之前有人問過類似的問題,但我很難在以下代碼中調用 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;
    }

正如其他人所說,擴展方法是編譯器魔術,您可以隨時使用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();

        }
    }
}

擴展方法實際上只是水下的靜態方法。 像 foo.Frob( arguments ) 這樣的擴展方法調用實際上只是 SomeClass.Frob(foo, arguments )。 在 Where 方法的情況下,您正在尋找 System.Linq.Enumerable.Where。 因此,獲取 Enumerable 的 typeof 並調用 Where 。

我有點晚了,但是如果您需要調用 IEnumerable 類型未知的 Linq 擴展,這可以幫助您。

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

然后也許測試 obj 如果不是 null 和

int count = test.Count()

對我來說效果很好。

您的代碼示例有點令人困惑……除非 MyObject 是可枚舉的。

使用反射,您必須調用 System.Linq.Enumerable 上的 Where,傳入您想要執行 Where on 的枚舉。

這是方法名稱唯一的一般情況的答案(因此與原始發布的情況不同,因為 Enumerable.Where 已重載)。

假設您有一個擴展類型的目標對象targetObject ,其中擴展方法在類TargetClassExtensions定義,其擴展方法的名稱是ExtensionMethod ,它接受一個整數參數並且是您想要為類TargetGenericClass調用的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 });

擴展方法是 ac# 編譯器技巧,它們不存在於相關類型中。 它們(這些特定的)存在於 System.Linq 名稱空間內的靜態類中。 我建議在反射器中反映這一點,然后對這些類型進行反射。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM