2015-03-31 4 views
1

У меня есть вопрос, который меня наворачивает в течение некоторого времени. Как получить значение времени выполнения переменных, созданных при выполнении дерева выражений ВО ВРЕМЯ выполнения (до завершения)? Конечно, вы можете получить окончательное значение на основе возвращаемого типа последнего выражения в переменной Lambda, но мне интересно получить фактическое значение переменной в середине выполнения.Как получить значение переменной в Дереве выражений

Ниже я создал простой пример цикла For, где я пытаюсь вывести на консоль форматированную строку. Для этого контекста предположим, что я не могу просто установить свойство некоторого ссылочного класса вне этого под. Я просто хочу получить значения, скрытые в выполнении лямбды.

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 

Выражение третьего вызова выдает правильный результат, но требует определенного суб (выход показан на каждой третьей линии). Первый из трех выражений звонка близок к тому, что я хочу. В конце концов, было бы неплохо, если бы я мог бы использовать что-то вроде:

Expression.VariableValue(TempInteger) 

где выход типа объекта, который затем может быть свободно typecasted и такое, так что я смог бы сделать:

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

Это был только один простой пример. Другие релевантные проблемы включают вывод результата блока Catch с особым типом исключения, при котором значения Exception могут быть доступны и соответствующим образом заданы, вместо того, чтобы создавать новый подраздел только для распечатки информации об исключении.

Есть ли способ просто получить значения времени выполнения?

+1

Я не уверен, что вы имеете в виду. После компиляции выражения у вас есть обычный метод. Объекты исходного выражения не относятся к этому методу; если бы они были, то что бы это означало для изучения одного из этих объектов после того, как вы скомпилировали выражение два или более раза? К какому методу они относятся? Итак, учитывая, что скомпилированное выражение является обычным методом, ваш вопрос, похоже, сводится к «как это сделать, я знаю, какое значение имеет локальная переменная в методе во время выполнения метода». И ответ на этот вопрос: «вы не делаете, если метод не передает его вам». –

ответ

1

Если вы хотите получить значение переменной, просто используйте ParameterExpression, который представляет переменную. Если вы хотите использовать перегрузку Console.WriteLine(string, object, object), то единственной проблемой является то, что вам нужно отливать из int в object (что С # неявно):

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