2016-04-26 75 views
1

如果我有一個Expression<Func<Delegate>>形式的表達式,可以確定用於傳遞委託的對象的派生的類型?這個表達是否包含這些信息,還是僅僅是代表的代表。從方法表達式獲取具體類型

一個代碼示例應該使事情更清晰。

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace Sandbox { 

    public interface IContract { 
     void Method(); 
    } 

    public class Concrete : IContract { 
     public void Method() { } 
    } 

    public class Test { 

     private IContract contract = new Concrete(); 
     private Concrete concrete = new Concrete(); 

     public static string GetFullMethodName(Expression<Func<Action>> expression) { 
      var unaryExpression = (UnaryExpression)expression.Body; 
      var methodCallExpression = (MethodCallExpression)unaryExpression.Operand; 

      var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last(); 
      var methodInfo = (MemberInfo)methodInfoExpression.Value; 

      var type = methodInfo.DeclaringType; 
      var name = methodInfo.Name; 

      return String.Format("{0}.{1}", type, name); 
     } 

     public Test() { 
      var strConcrete = GetFullMethodName(() => concrete.Method); // Sandbox.Concrete.Method 
      var strContract = GetFullMethodName(() => contract.Method); // Sandbox.IContract.Method 
     } 

    } 

} 

是否有可能使() => contract.Method產生

Sandbox.Concrete.Method

而不是

Sandbox.IContract.Method

可以改變表達式來支持這個表達式,還是我會被迫將對象的一個​​實例作爲單獨的參數傳遞以確定它的類型?

+0

爲什麼你想這樣做嗎?有什麼幫助調試? – Luaan

回答

2

不,這是不可能的。

表達式樹是基於編譯時信息構建的,在編譯時,調用的方法是虛擬IContract.Method。事實上,在運行時,Concrete.Method實際上被調用是無關緊要的 - 這是在虛擬調度機制中處理的,並且編譯器在任何這個決定之前就完成了它的工作。

您可以做的最好的方法就是嘗試模擬虛擬調度。例如,您可以嘗試在運行時枚舉this實例上的接口實現,並且如果找到與接口匹配的方法,則會返回該實例。然而,這當然是棘手的 - 你需要能夠完美地模擬實際的虛擬調度。

另一個棘手的問題是,您試圖獲取該值的方式已經失效。例如,在我的環境中,methodInfoExpression而不是 a ConstantExpression - 它是封閉類型的字段。你的「解析」非常脆弱。

也就是說,這也是一種不必要和過度複雜 - 你試圖接收一個表達式,該表達式返回一個動作並從表達式中「偷走」動作的值。現在已經有一個簡單的方法 - 直接傳遞參數。沒有必要把它藏在一個表達式樹:

public static string GetFullMethodName(Action action) 
{ 
    return string.Format("{0}.{1}", action.Method.DeclaringType.Name, action.Method.Name); 
} 

var strConcrete = GetFullMethodName(concrete.Method).Dump(); // Sandbox.Concrete.Method 
var strContract = GetFullMethodName(contract.Method).Dump(); // Sandbox.Concrete.Method 

你不需要的東西表述,基本反映已經給你:)

+0

我很害怕。關於複雜性,一個簡單的'Action'將調用者的語法從'a.Method'改爲'a.Method()',這是一個很小的區別,但我試圖提供一點語法糖(有些變體這種方法接受不同的輸入/返回,然後需要提供,雖然沒有使用) –

+0

@RedTaz不,只有當你直接使用一個表達式給委託時才需要(例如'Expression >') 。您確實需要爲所有可能的簽名提供重載 - 但您的代碼中也存在完全相同的問題。那些重載實際上很容易處理 - 只需編寫一個簡單的T4模板並保持所有類型參數都是通用的。 – Luaan

+0

啊,我現在看到了,是的,你說得對,它太複雜了! :) –

相關問題