Expression.Reduce()有什么作用?

时间:2010-01-10 21:44:35

标签: c# .net lambda expression-trees

我一直在使用表达式树几天,我很想知道Expression.Reduce()的作用。 msdn documentation不是很有用,因为它只表示它“减少”了表达式。为了以防万一,我尝试了一个例子(见下文)来检查这种方法是否包括数学减少,但似乎并非如此。

有谁知道这种方法的作用,是否有可能提供一个快速示例显示它的实际效果?那里有什么好资源吗?

static void Main(string[] args)
{
    Expression<Func<double, double>> func = x => (x + x + x) + Math.Exp(x + x + x);
    Console.WriteLine(func);
    Expression r_func = func.Reduce();
    Console.WriteLine(r_func); // This prints out the same as Console.WriteLine(func)
}

4 个答案:

答案 0 :(得分:29)

您需要查看的文件是expr-tree-spec.doc

这是表达式树的规范。阅读“2.2 Redusible Nodes”和“4.3.5 Reduce Method”部分。

基本上,此方法适用于将动态语言实现或移植到.NET的人员。这样他们就可以创建自己的节点,可以“减少”到标准表达式树节点并可以编译。表达式树API中有一些“可简化”节点,但我不知道你是否可以得到任何实际的例子(因为所有标准表达式节点无论如何编译,因为最终用户你可能不关心它们是否“减少” “幕后与否”。

是的,MSDN文档在这方面非常基础,因为语言实现者的主要信息和文档来源是http://dlr.codeplex.com/

答案 1 :(得分:23)

稍微拆解后,我发现Expression.CanReduce总是重新false,而Expression.Reduce()总是返回this。但是,有一些类型可以覆盖它们。 LambdaExpression继承了默认实现,这解释了为什么到目前为止已经尝试过的表达式不起作用。

覆盖Reduce()的其中一种类型是MemberInitExpression,这使我进行了以下成功的实验:

class ReduceFinder : ExpressionVisitor {
    public override Expression Visit(Expression node) {
        if (node != null && node.CanReduce) {
            var reduced = node.Reduce();
            Console.WriteLine("Found expression to reduce!");
            Console.WriteLine("Before: {0}: {1}", node.GetType().Name, node);
            Console.WriteLine("After: {0}: {1}", reduced.GetType().Name, reduced);
        }
        return base.Visit(node);
    }
}

class Foo {
    public int x;
    public int y;
}

static class Program {
    static void Main() {
        Expression<Func<int, Foo>> expr = z => new Foo { x = (z + 1), y = (z + 1) };
        new ReduceFinder().Visit(expr);
    }
}

输出:

Found expression to reduce!  
Before: MemberInitExpression: new Foo() {x = (z + 1), y = (z + 1)}  
After: ScopeN: { ... }  

答案 2 :(得分:2)

Nick Guerrera的答案外,我发现以下表达式已覆盖CanReduce方法:

*根据JustDecompile

表示内部派生类型BinaryExpression

答案 3 :(得分:1)

我猜测不同的linq提供者可以使用它们将某些节点类型转换为更简单的表示形式。

由于文档很少,可以用于common subexpression elimination来消除冗余表达式。如果您的函数在不更改局部x的情况下多次计算x + x,则可以通过将第一个表达式的结果保存到临时表达式来简化它。也许由linq提供者来选择性地实现这些转换。

或者如果您嵌套的BlockExpressions不包含任何代码(类似{{{}}}的表达式),那么这些代码可以被删除,或者是空的ConditionalExpression ......