我一直在使用表达式树几天,我很想知道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)
}
答案 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 ......