2010-01-13 49 views
4

我是表達樹的新手,所以我不知道如何提出這個問題或使用什麼術語。以下是我想要做的過於簡化的版本:表達式樹 - 如何獲取聲明實例?

Bar bar = new Bar(); 
Zap(() => bar.Foo); 

public static void Zap<T>(Expression<Func<T>> source) 
{ 
    // HELP HERE: 
    // I want to get the bar instance and call bar.Zim() or some other method. 
} 

我該如何進入Zap方法內部?

+1

如果我調用'Zap((()=> 1)'會怎麼樣? – 2010-01-13 19:16:10

+1

然後我不會嘗試打電話給bar.Zim()。 :-)真的,我想看看傳入的東西是否是一個Bar實例,如果是,請調用一個實例方法。可能? – 2010-01-13 19:19:28

回答

6

由於傳遞到Zap方法中的表達式是一棵樹,因此只需使用Expression Tree Visitor來遍歷樹,並在表達式中查找第一個ConstantExpression。它可能會在下面的序列:

(((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value 

注意,bar實例由一個閉合,其作爲與該實例的一員,其是其中第二MemberExpression來自內部類實現捕獲。

編輯

然後,你必須從產生的閉合拿到野外,像這樣:

static void Main(string[] args) 
    { 
     var bar = new Bar(); 
     bar.Foo = "Hello, Zap"; 
     Zap(() => bar.Foo); 
    } 

    private class Bar 
    { 
     public String Foo { get; set; }  
    } 

    public static void Zap<T>(Expression<Func<T>> source) 
    { 
     var param = (((source.Body as MemberExpression).Expression as MemberExpression).Expression as ConstantExpression).Value; 
     var type = param.GetType(); 
     // Note that the C# compiler creates the field of the closure class 
     // with the name of local variable that was captured in Main() 
     var field = type.GetField("bar"); 
     var bar = field.GetValue(param) as Bar; 
     Debug.Assert(bar != null); 
     Console.WriteLine(bar.Foo); 
    } 
+0

謝謝!我會試一試。 – 2010-01-13 19:25:20

+0

我試過了。它很接近:上面的表達式給了我內部的編譯器生成的類。我需要訪問存儲在該類中的bar實例。我玩過,無法弄清楚。我錯過了什麼? – 2010-01-13 19:33:51

+0

好的,我更新了答案。 – codekaizen 2010-01-13 19:44:13

5

如果你知道的「酒吧」的類型,你可以做到這一點(我重用從這裏codekaizen的回答有些位):

static void Main(string[] args) 
    { 
     var bar = new Bar(); 
     bar.Foo = "Hello, Zap"; 
     Zap(() => bar.Foo); 

     Console.ReadLine(); 
    } 

    private class Bar 
    { 
     public String Foo { get; set; } 
    } 

    public static void Zap<T>(Expression<Func<T>> source) 
    { 
     var body = source.Body as MemberExpression; 
     Bar test = Expression.Lambda<Func<Bar>>(body.Expression).Compile()(); 
     Console.WriteLine(test.Foo); 
    } 

在大多數情況下,你可以找到一個表達特雷內代表對象的表達式e,然後編譯並執行這個表達式並獲取對象(順便說一句,這不是一個非常快的操作)。所以,你缺少的是Compile()方法。您可以在這裏找到更多信息:How to: Execute Expression Trees

在這段代碼中,我假設你總是傳遞一個像「()=> object.Member」的表達式。對於真實世界的場景,您需要分析您是否擁有需要的表達式(例如,如果它不是MemberExpression,只需引發異常)。或者使用ExpressionVisitor,這很棘手。

我最近回答了一個非常類似的問題在這裏: How do I subscribe to an event of an object inside an expression tree?

1

站在巨人的肩膀之上,我最後的擴展方法來提取所代表的表達式的源如下所示的類的實例:

public static TIn GetSource<TIn, TOut>(this Expression<Func<TIn, TOut>> property) 
     where TIn: class 
{ 
    MemberExpression memberExpression = (MemberExpression)property.Body; 
    TIn instance = Expression.Lambda<Func<TIn>>(memberExpression.Expression).Compile()(); 
    return instance; 
} 

我建立在上述所有答案上,這要感謝所有人。