2010-03-09 55 views
5

我很明顯缺少:如何訪問lambda表達式表達式樹中的參數值?在表達式樹中獲取參數表達式的運行時間值

場景:對於委託x我動態地創建一個lambda表達式,其表達式樹體具有與委託x相同的簽名。在lamdba的內部,我做了一些驗證,檢查,記錄東西(這只是測試代碼,而不是生產),然後用原始參數調用原始代表x。如果委託具有返回值,則也會返回該值。

工作得很好(包括將參數傳遞給原始代理)。

但是我打磚牆如果我想訪問傳遞給委託/ lambda的原始參數值。

僞代碼:

var del = new Func<string, int>(_=> {return 42;}); 
var paramDefs = Array.ConvertAll<ParameterInfo, ParameterExpression>(del.Method.GetParameters(), _ => { return Expression.Parameter(_.ParameterType, _.Name); }); 
var variableTest = Expression.Variable(typeof(string), "str"); 

var expression = Expression.Block(
    new [] { variableTest }, 
    // this line assigns the actual run time value (which is what I need) of the parameter to the variable - but I cannot hardcode the index. 
    //Expression.Assign(variableTest, paramDefs[0]) 
    // this line would assigns the ParameterExpression object (causing a run-time exception since the type of the variable is string) ... I need the _value_ of the first (or nth) parameter. 
    Expression.Assign(variableTest, Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))) 
); 
var lamdba = Expression.Lambda(del.GetType(), expression, "foo", paramDefs); 
var del2 = lamdba.Compile() as Func<string, int>; 
del2("this is a test"); 
+0

註釋掉的代碼似乎是正確的,所以我會這樣做。你說你不能硬編碼索引。爲什麼不?對我來說,就像那樣會工作得很好。 – 2010-03-09 20:58:10

+0

我已經改變了代碼。我最初想要遍歷表達式樹(Expression.Loop)中的paramDefs,逐個訪問每個參數。由於代碼適用於任何委託類型,因此對索引進行硬編碼將無法工作。 我從表達式樹中抽取了循環構造。對於具有5個參數的委託,現在我只生成五個表達式(最簡單的情況是將每個參數值複製到一個對象[])。 但是,最初的問題仍然讓我感到困惑 - 是否有方法從ParameterExpression實例獲取實際值? – dalo 2010-03-09 21:36:55

回答

3

看起來你混淆了表達式樹編譯器太多(當然,我被這個代碼迷惑過)。我可以看到你想要做什麼:你從數組中獲得了一個元素,然後你決定在數組上循環。但是你不能做數組[ParameterExpression],所以你使用了ArrayIndex。但是...

但是ArrayIndex實際上並沒有返回「string」。它返回MethodCallExpression。所以,在這個「Assign」表達式中,你實際上有ParameterExpression和MethodCallExpression。 ET編譯器非常聰明,可以編譯這些表達式並嘗試分配結果。但是你的MethodCallExpression的結果是ParameterExpression。當你有paramDefs [0]時,你有了ParameterExpression,編譯器可以處理它。但是編譯嵌套表達式更困難,而且你是否真的想編譯這個嵌套表達式還不清楚。

你可以做的是自己編譯和調用MethodCallExpression,因此你將在Assign表達式中擁有ParameterExpression(就像你以前一樣)。 它可能看起來像這樣:

// Replace Assign in your Block expression. 
Expression.Assign(variableTest, Expression.Lambda<Func<ParameterExpression>>(Expression.ArrayIndex(Expression.Constant(paramDefs), Expression.Constant(0))).Compile()()), 

但它也可能在性能方面(加上代碼是醜陋的)是非常沉重的。所以,我堅持把你的循環從表達式樹中拉出來。