C# Expression.Block()能否在lambda闭包中返回值?

C# Expression.Block()能否在lambda闭包中返回值?,c#,.net,expression-trees,C#,.net,Expression Trees,我想将两个独立的表达式树编译成一个已编译的lambda。我有一个double[]输入数组。第一个表达式树(为了简单起见,我们将其称为ExpressionA)创建一个新的长度相同的double[]数组,其中包含输入数组值转换的结果。第二个表达式树(ExpressionB)对转换后的数组进行一些计算,并返回一个double输出,我想返回这个输出 我原以为以下方法行得通,但我遇到了问题: ParameterExpression inputArray = Expression.Parameter(typ

我想将两个独立的表达式树编译成一个已编译的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异常而失败:

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();