如何获取表达式树中变量的值
How to get the value of a variable in an Expression Tree
我有一个问题困扰了我一段时间。如何检索在执行期间(完成之前)执行表达式树时创建的变量的 运行 时间值?当然,您可以根据 Lambda 变量中最后一个表达式的 return 类型获取最终值,但我感兴趣的是在执行过程中获取实际变量值。
下面我创建了一个简单的 For 循环示例,我在其中尝试输出以控制格式化字符串。对于此上下文,假设我不能简单地设置此子之外的某些引用 class 的 属性。我只是希望获得隐藏在 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 块的结果,其中可以访问异常的值并适当地进行类型转换,而不是创建一个新的 sub 只是为了打印出异常信息。
有什么方法可以简单地检索 运行 时间值吗?
如果要获取变量的值,直接使用代表变量的ParameterExpression
即可。如果你想使用 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))))
我有一个问题困扰了我一段时间。如何检索在执行期间(完成之前)执行表达式树时创建的变量的 运行 时间值?当然,您可以根据 Lambda 变量中最后一个表达式的 return 类型获取最终值,但我感兴趣的是在执行过程中获取实际变量值。
下面我创建了一个简单的 For 循环示例,我在其中尝试输出以控制格式化字符串。对于此上下文,假设我不能简单地设置此子之外的某些引用 class 的 属性。我只是希望获得隐藏在 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 块的结果,其中可以访问异常的值并适当地进行类型转换,而不是创建一个新的 sub 只是为了打印出异常信息。
有什么方法可以简单地检索 运行 时间值吗?
如果要获取变量的值,直接使用代表变量的ParameterExpression
即可。如果你想使用 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))))