我想将两个单独的表达式树编译成单个编译的lambda。我有一个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运行时异常(堆栈跟踪没有用,因为它没有进入lambda_method()
)。
然而,这个更简单的版本运行得很好(只是传入xformed数组):
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
ExpressionB(xformArray)), xformArray).Compile();
但是这个更简单的版本也因NullReference异常而失败:
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { xformArray },
ExpressionB(xformArray)),
xformArray).Compile();
最后,我也尝试了这个概念验证版本,它也有效(让我相信一个Block
在概念上应该在lambda中是正常的):
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
Expression.Block(new ParameterExpression[] { inputArray, xformArray },
Expression.Constant(0.0)), // stub test
inputArray).Compile();
所以我的问题是如何在单个编译的lambda中以顺序方式使用两个表达式树?
答案 0 :(得分:2)
没有好的Minimal, Complete, and Verifiable code example,特别是没有关于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();
同样,如果没有一个好的MCVE,就不可能确切知道你的选择是什么。但恕我直言,总是更喜欢在代码中表达表达式而不是手动构建它们。 E.g:
Func<double[], double> MakeExpression(
Func<double[], double[]> transformA,
Func<double[], double> transformB)
{
return a => transformB(transformA(a));
}
如果变换本身由于某种原因需要表达为表达式,你仍然可以在构建lambda的其余部分之前单独编译它们:
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));
}
但是,如果必须明确地使用Expression
类完成整个事情,那么上面第一个代码示例中的更正应该可以解决您的问题。
最后,我会指出您的原始代码可以大大简化:
Func<double[], double> compiled = Expression.Lambda<Func<double[], double>>(
ExpressionB(ExpressionA(inputArray)),
inputArray).Compile();
当然,在您的真实代码中,您可能会有更复杂的表达方式。但至少对于帖子中的示例,您甚至不需要块或中间局部变量。