【问题标题】:Getting run-time value of a ParameterExpression in a expression tree在表达式树中获取 ParameterExpression 的运行时值
【发布时间】:2010-03-09 18:35:58
【问题描述】:

我错过了明显的:如何访问 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");

【问题讨论】:

  • 注释掉的代码似乎是正确的,所以我会这样做。你说你不能对索引进行硬编码。为什么不?在我看来,这样就可以了。
  • 我已经更改了代码。我最初想遍历表达式树 (Expression.Loop) 中的 paramDefs,逐个访问每个参数。由于代码适用于任何委托类型,因此对索引进行硬编码是行不通的。我将循环结构从表达式树中拉出来。对于具有 5 个参数的委托,我现在只需生成五个表达式(最简单的情况是将每个参数值复制到 object[])。但是,最初的问题仍然困扰着我——有没有办法从 ParameterExpression 实例中获取实际值?

标签: c# delegates lambda c#-4.0 expression-trees


【解决方案1】:

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

但实际上 ArrayIndex 并不返回“字符串”。它返回 MethodCallExpression。因此,在这个“分配”表达式中,您实际上有 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()()),

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多