2015-03-31 25 views
1

我有一個問題,一直困擾我一段時間。如何檢索在執行期間(在完成之前)執行表達式樹時創建的變量的運行時值?當然,你可以根據Lambda變量中最後一個表達式的返回類型來獲得最終值,但我有興趣在執行過程中獲取實際變量值。如何在表達式樹中獲取變量的值

下面我創建了一個For循環的簡單例子,我試圖輸出到控制格式化的字符串。對於這個上下文,假設我不能簡單地在這個子外面設置一些被引用的類的屬性。我只是希望獲得lambda執行中隱藏的值。

public static void WriteConsoleLineTemp(string Text, object obj1, object obj2) 
    { 
     Console.WriteLine(Text, obj1.ToString(), obj2.ToString()); 
    } 

    private void TempSub() 
    { 
     LabelTarget label1 = Expression.Label(); 
     ParameterExpression IteratorInt = Expression.Variable(typeof(int), "i"); 
     ParameterExpression TempInteger = Expression.Variable(typeof(int), "int"); 
     ParameterExpression TempRandom = Expression.Variable(typeof(Random), "rand"); 
     MethodInfo ToStringMethod = typeof(object).GetMethod("ToString", Type.EmptyTypes); 
     MethodInfo ConsoleWriteLine1 = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }); 
     MethodInfo ConsoleWriteLine2 = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string), typeof(object[]) }); 
     MethodInfo ConsoleWriteLine3 = typeof(Form1).GetMethod("WriteConsoleLineTemp", new Type[] { typeof(string), typeof(int), typeof(int) }); 
     BlockExpression SuperTemp = Expression.Block(new[] { IteratorInt, TempInteger, TempRandom }, 
       Expression.Assign(TempRandom, Expression.Constant(new Random())), 
       Expression.Loop(
        Expression.Condition(Expression.GreaterThanOrEqual(IteratorInt, Expression.Constant(5)), 
         Expression.Return(label1), 
          Expression.Block(
           Expression.AddAssign(IteratorInt, Expression.Constant(1)), 
           Expression.Assign(TempInteger, Expression.Call(TempRandom, typeof(Random).GetMethod("Next", new Type[] { typeof(int), typeof(int) }), Expression.Constant(0), Expression.Constant(10))), 
           //Expression.Call(null, ConsoleWriteLine1, Expression.Call(IteratorInt, ToStringMethod)), // This Works but only without format paramaters 
           //Expression.Call(null, ConsoleWriteLine1, Expression.Call(TempInteger, ToStringMethod)), //This Works but only without format paramaters 
           Expression.Call(null, ConsoleWriteLine2, Expression.Constant("Iteration {0}, Value = {1}"), Expression.Constant(new object[] { IteratorInt, TempInteger })), 
           Expression.Call(null, ConsoleWriteLine2, Expression.Constant("Iteration {0}, Value = {1}"), Expression.Constant(new object[] { Expression.Call(IteratorInt, ToStringMethod), Expression.Call(TempInteger, ToStringMethod) })), 
           Expression.Call(null, ConsoleWriteLine3, Expression.Constant("Iteration {0}, Value = {1}"), Expression.TypeAs(IteratorInt, typeof(object)), Expression.TypeAs(TempInteger, typeof(object))) // Works, but requires a specific sub 

           ) 
         ), 
       label1) 
      ); 
     Action MyExecutor = (Action)Expression.Lambda(SuperTemp).Compile(); 
     MyExecutor(); 
    } 

,其輸出:

Iteration i, Value = int 
    Iteration i.ToString(), Value = int.ToString() 
    Iteration 1, Value = 6 
    Iteration i, Value = int 
    Iteration i.ToString(), Value = int.ToString() 
    Iteration 2, Value = 8 
    Iteration i, Value = int 
    Iteration i.ToString(), Value = int.ToString() 
    Iteration 3, Value = 1 
    Iteration i, Value = int 
    Iteration i.ToString(), Value = int.ToString() 
    Iteration 4, Value = 8 
    Iteration i, Value = int 
    Iteration i.ToString(), Value = int.ToString() 
    Iteration 5, Value = 0 

第三呼叫表達式輸出正確的結果,但需要的特定子(輸出顯示在每第三行)。三個Call表達式中的第一個接近我的意願。最終,這將是很好,如果我可以使用類似:

Expression.VariableValue(TempInteger) 

,其中輸出類型的對象,然後可以自由類型強制轉換和這樣的,所以我將能夠做到:

Expression.Call(null, ConsoleWriteLine1, Expression.Constant("Iteration " + Expression.VariableValue(IteratorInt).ToString() + ", Value = " + Expression.VariableValue(TempInteger).ToString()));  

這只是一個簡單的例子。其他相關問題包括輸出具有特定類型異常的Catch塊的結果,其中Exception的值可以被適當地訪問和進行類型化,而不是創建僅用於打印Exception info的新子。

有什麼方法可以簡單地檢索運行時間值嗎?

+1

我不確定你的意思。在編譯表達式之後,您有一個常規方法。原始表達式對象與該方法無關;如果它們是,那麼在你編譯表達式兩次或更多次之後,檢查其中一個對象意味着什麼?它們涉及哪種方法?因此,鑑於編譯後的表達式只是一個常規方法,您的問題似乎歸結爲「如何知道方法執行時方法中的局部變量具有什麼值」。對此的答案是,「除非方法傳遞給你,否則你不這樣做」。 – 2015-03-31 03:55:15

回答

1

如果您想獲取變量的值,只需使用表示變量的​​即可。如果你想使用Console.WriteLine(string, object, object)超載,那麼唯一的問題是,你需要從int轉換爲object(C#並含蓄地):

MethodInfo ConsoleWriteLine3 = typeof(Console).GetMethod(
    "WriteLine", new Type[] { typeof(string), typeof(object), typeof(object) }); 

… 

Expression.Call(
    null, ConsoleWriteLine3, 
    Expression.Constant("Iteration {0}, Value = {1}"), 
    Expression.Convert(IteratorInt, typeof(object)), 
    Expression.Convert(TempInteger, typeof(object))) 

如果你想使用Console.WriteLine(string, object[])過載,而不是,你也需要創建params數組(其中C#也適合您):

Expression.Call(
    null, ConsoleWriteLine2, 
    Expression.Constant("Iteration {0}, Value = {1}"), 
    Expression.NewArrayInit(
     typeof(object), 
     Expression.Convert(IteratorInt, typeof(object)), 
     Expression.Convert(TempInteger, typeof(object)))) 
+0

嘆息,當然這是被遺忘的簡單的事情; NewArrayInit實際上將變量傳遞給它們進行轉換,而不是聲明一個只傳遞ParameterExpression對象的新對象[],這是無用的。謝謝,這一直困擾着我一段時間。 – NotJehov 2015-03-31 16:55:40

相關問題