Dot-Net

你能從 MethodInfo 對像中獲得 Func<T> (或類似的)嗎?

  • May 29, 2010

我意識到,一般來說,使用反射會影響性能。(實際上,我本人根本不喜歡反思;這是一個純粹的學術問題。)

假設存在一些如下所示的類:

public class MyClass {
   public string GetName() {
       return "My Name";
   }
}

在這裡忍受我。我知道如果我有一個MyClass被呼叫的實例x,我可以呼叫x.GetName()。此外,我可以將Func&lt;string&gt;變數設置為x.GetName.

現在這是我的問題。假設我知道上面的類被呼叫MyClass;我有一些對象,x但我不知道它是什麼。我可以通過執行以下操作來檢查該對像是否具有GetName方法:

MethodInfo getName = x.GetType().GetMethod("GetName");

假設getName不為空。那麼我不能進一步檢查是否getName.ReturnType == typeof(string)getName.GetParameters().Length == 0,並且在這一點上,我不是很確定我的getName對象表示的方法肯定可以以Func&lt;string&gt;某種方式強制轉換為 a 嗎?

我意識到有一個MethodInfo.Invoke,我也意識到我總是可以創建一個Func&lt;string&gt;類似的:

Func&lt;string&gt; getNameFunc = () =&gt; getName.Invoke(x, null);

我想我要問的是是否有任何方法可以一個對MethodInfo到它所代表的實際方法,從而在過程中產生反射的性能成本,但那之後能夠直接呼叫該方法(通過,例如, aFunc&lt;string&gt;或類似的東西)沒有性能損失。

我所設想的可能看起來像這樣:

// obviously this would throw an exception if GetActualInstanceMethod returned
// something that couldn't be cast to a Func&lt;string&gt;
Func&lt;string&gt; getNameFunc = (Func&lt;string&gt;)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) =&gt; ((TestType)o).GetName();
 Func&lt;object, string&gt; result = Expression.Lambda&lt;Func&lt;object, string&gt;&gt;(
   Expression.Call(Expression.Convert(input, o.GetType()), method), input).Compile();

 string str = result(o);
 Assert.AreEqual("hello world!", str);
}

一旦您建構了一次委託 - 您可以將其記憶體在字典中:

Dictionary&lt;Type, Func&lt;object, string&gt;&gt; _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&lt;string&gt; result = Expression.Lambda&lt;Func&lt;string&gt;(
  Expression.Call(Expression.Constant(o), method)).Compile();

 string str = result(); //no parameter this time.
 Assert.AreEqual("hello world!", str);
}

但是請注意,一旦表達式樹被丟棄,您需要確保它o保持在範圍內,否則您可能會得到一些令人討厭的結果。最簡單的方法是在委託的生命週期內保持本地引用(也許在類實例中)。(因 Ben M 的評論而被刪除)

引用自:https://stackoverflow.com/questions/2933221