获取表达式树中ParameterExpression的运行时值

dal*_*alo 5 c# lambda delegates expression-trees c#-4.0

我错过了显而易见的事项:如何在lambda表达式表达式树中访问参数的值?

场景:对于委托x,我动态创建一个lambda表达式,其表达式树体与委托x具有相同的签名.在lamdba的主体内部,我做了一些验证,检查,记录东西(这只是测试代码,而不是生产),然后我用原始参数调用原始委托x.如果委托具有返回值,则返回该值.

这非常有效(包括将参数传递给原始委托).

但是如果我想访问传递给delegate/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");
Run Code Online (Sandbox Code Playgroud)

Ale*_*ina 3

看起来你对表达式树编译器感到困惑太多(好吧,我也被这段代码弄糊涂了)。我可以看到您尝试执行的操作:您从数组中获取了一个元素,然后决定循环遍历该数组。但你不能做 array[ParameterExpression],所以你使用了 ArrayIndex。但...

但 ArrayIndex 实际上并不返回“字符串”。它返回 MethodCallExpression。因此,在这个“分配”表达式中,您实际上有 ParameterExpression 和 MethodCallExpression。ET 编译器足够聪明,可以编译这些表达式并尝试分配结果。但是 MethodCallExpression 的结果是 ParameterExpression。当你有 paramDefs[0] 时,你马上就有了 ParameterExpression,编译器可以处理它。但是编译嵌套表达式比较困难,并且完全不清楚您是否真的要编译这个嵌套表达式。

您可以做的是自己编译并调用 MethodCallExpression,因此您将在赋值表达式中拥有 ParameterExpression(就像以前一样)。它可能看起来像这样:

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

但它在性能方面可能会非常沉重(而且代码很丑陋)。所以,我会坚持你的想法,将循环从表达式树中拉出来。