我错过了显而易见的事项:如何在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");
答案 0 :(得分:3)
看起来你混淆了表达式树编译器太多了(好吧,我也对这段代码感到困惑)。我可以看到你试图做的事情:你从数组中获得了一个元素,然后你决定遍历数组。但你不能做数组[ParameterExpression],所以你使用了ArrayIndex。但...
但ArrayIndex实际上并不返回“string”。它返回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()()),
但它在性能方面可能非常沉重(加上代码很难看)。所以,我坚持你把循环从表达式树中拉出来的想法。