你能從 MethodInfo 對像中獲得 Func<T> (或類似的)嗎?
我意識到,一般來說,使用反射會影響性能。(實際上,我本人根本不喜歡反思;這是一個純粹的學術問題。)
假設存在一些如下所示的類:
public class MyClass { public string GetName() { return "My Name"; } }在這裡忍受我。我知道如果我有一個
MyClass被呼叫的實例x,我可以呼叫x.GetName()。此外,我可以將Func<string>變數設置為x.GetName.現在這是我的問題。假設我不知道上面的類被呼叫
MyClass;我有一些對象,x但我不知道它是什麼。我可以通過執行以下操作來檢查該對像是否具有GetName方法:MethodInfo getName = x.GetType().GetMethod("GetName");假設
getName不為空。那麼我不能進一步檢查是否getName.ReturnType == typeof(string)和getName.GetParameters().Length == 0,並且在這一點上,我不是很確定我的getName對象表示的方法肯定可以以Func<string>某種方式強制轉換為 a 嗎?我意識到有一個
MethodInfo.Invoke,我也意識到我總是可以創建一個Func<string>類似的:Func<string> getNameFunc = () => getName.Invoke(x, null);我想我要問的是是否有任何方法可以從一個對
MethodInfo像到它所代表的實際方法,從而在過程中產生反射的性能成本,但在那之後能夠直接呼叫該方法(通過,例如, aFunc<string>或類似的東西)沒有性能損失。我所設想的可能看起來像這樣:
// obviously this would throw an exception if GetActualInstanceMethod returned // something that couldn't be cast to a Func<string> Func<string> getNameFunc = (Func<string>)getName.GetActualInstanceMethod(x);(我意識到這不存在;我想知道是否有類似的東西。)
這種替代了我之前的答案,因為儘管它是一條稍長的路線,但它為您提供了一個快速的方法呼叫,並且與其他一些答案不同,它允許您通過不同的實例(以防您遇到多個實例同類型)。如果您不希望這樣,請查看底部的更新(或查看 Ben M 的答案)。
這是一個可以滿足您要求的測試方法:
public class TestType { public string GetName() { return "hello world!"; } } [TestMethod] public void TestMethod2() { object o = new TestType(); var input = Expression.Parameter(typeof(object), "input"); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); //you should check for null *and* make sure the return type is string here. Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //now build a dynamic bit of code that does this: //(object o) => ((TestType)o).GetName(); Func<object, string> result = Expression.Lambda<Func<object, string>>( Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile(); string str = result(o); Assert.AreEqual("hello world!", str); }一旦您建構了一次委託 - 您可以將其記憶體在字典中:
Dictionary<Type, Func<object, string>> _methods;然後您所做的就是將其添加到字典中,使用傳入對象的類型(來自 GetType())作為鍵。將來,您首先檢查字典中是否有現成的委託(如果有,則呼叫它),否則您首先建構它,添加它,然後呼叫它。
順便說一句,這是 DLR 為它的動態調度機制所做的事情的一個非常簡化的版本(在 C# 術語中,這就是您使用“動態”關鍵字的時候)。
最後
如果,正如一些人所提到的,您只是想烘焙一個直接綁定到您收到的對象的 Func,那麼您可以這樣做:
[TestMethod] public void TestMethod3() { object o = new TestType(); var method = o.GetType().GetMethod("GetName", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); Assert.IsFalse(method == null && !method.ReturnType.Equals(typeof(string))); //this time, we bake Expression.Constant(o) in. Func<string> result = Expression.Lambda<Func<string>( Expression.Call(Expression.Constant(o), method)).Compile(); string str = result(); //no parameter this time. Assert.AreEqual("hello world!", str); }但是請注意,一旦表達式樹被丟棄,您需要確保它
o保持在範圍內,否則您可能會得到一些令人討厭的結果。最簡單的方法是在委託的生命週期內保持本地引用(也許在類實例中)。(因 Ben M 的評論而被刪除)