C# Expression.Block()能否在lambda闭包中返回值?
我想将两个独立的表达式树编译成一个已编译的lambda。我有一个C# Expression.Block()能否在lambda闭包中返回值?,c#,.net,expression-trees,C#,.net,Expression Trees,我想将两个独立的表达式树编译成一个已编译的lambda。我有一个double[]输入数组。第一个表达式树(为了简单起见,我们将其称为ExpressionA)创建一个新的长度相同的double[]数组,其中包含输入数组值转换的结果。第二个表达式树(ExpressionB)对转换后的数组进行一些计算,并返回一个double输出,我想返回这个输出 我原以为以下方法行得通,但我遇到了问题: ParameterExpression inputArray = Expression.Parameter(typ
double[]
输入数组。第一个表达式树(为了简单起见,我们将其称为ExpressionA
)创建一个新的长度相同的double[]
数组,其中包含输入数组值转换的结果。第二个表达式树(ExpressionB
)对转换后的数组进行一些计算,并返回一个double
输出,我想返回这个输出
我原以为以下方法行得通,但我遇到了问题:
ParameterExpression inputArray = Expression.Parameter(typeof(double[]));
ParameterExpression xformArray = Expression.Parameter(typeof(double[]));
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { inputArray, xformArray },
Expression.Assign(xformArray, ExpressionA(inputArray)),
ExpressionB(xformArray)),
inputArray).Compile();
但这个更简单的版本也会因NullReference异常而失败:
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { xformArray },
ExpressionB(xformArray)),
xformArray).Compile();
因此,我的问题是如何在一个编译的lambda中以顺序方式使用这两个表达式树?如果没有好的,特别是没有关于什么是ExpressionA()
和ExpressionB()
以及您实际如何使用它们的详细说明,就不可能确定最佳答案是什么。然而,我看到的最明显的问题是,您正在重新声明inputArray
,这将在块中创建一个新的局部变量。由于没有分配给任何对象,其值当然是null
修复方法是只从块的变量中删除它,只留下xformArray
:
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { xformArray },
Expression.Assign(xformArray, ExpressionA(inputArray)),
ExpressionB(xformArray)),
inputArray).Compile();
当然,在实际代码中,您可能有一个更复杂的表达式。但至少在你文章中的例子中,你甚至不需要block或中间局部变量。添加c#标记以吸引更多的注意力。谢谢,Peter,这确实是我问题的根源(Expression.block的第一个参数用法不正确)。再次感谢您的帮助。出于好奇,在您编译两个输入表达式的第二个到最后一个示例中:我假设这样的函数(由
return a=>transformBCompiled(transformACompiled(a));
)可以像常规方法一样内联,这样的假设正确吗?@Timo:不,我不相信是这样。返回的只是一个委托实例,委托实例调用的代码将是其他两个委托实例的调用,引用存储在两个捕获的局部变量transformACompiled
和transformBCompiled
中。我认为编译器没有机会在那里内联任何内容。内联意味着将要执行的代码复制到调用站点并放弃实际调用,但委托永远不可能做到这一点,因为你永远不知道调用站点实际可能在哪里。@PeterDuniho非常好。额外的间接性是需要注意的一件好事。在性能关键型代码中,我们可能会选择投入额外的精力来创建一个表达式。
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { inputArray, xformArray },
Expression.Constant(0.0)), // stub test
inputArray).Compile();
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { xformArray },
Expression.Assign(xformArray, ExpressionA(inputArray)),
ExpressionB(xformArray)),
inputArray).Compile();
Func<double[], double> MakeExpression(
Func<double[], double[]> transformA,
Func<double[], double> transformB)
{
return a => transformB(transformA(a));
}
Func<double[], double> MakeExpression(
Expression<Func<double[], double[]>> transformA,
Expression<Func<double[], double>> transformB)
{
Func<double[], double[]> transformACompiled = transformA.Compile();
Func<double[], double> transformBCompiled = transformB.Compile();
return a => transformBCompiled(transformACompiled(a));
}
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
ExpressionB(ExpressionA(inputArray)),
inputArray).Compile();