代码改变世界

统计一个表达式树拥有的节点数量

2009-10-31 21:05 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏

如果要统计表达式树的节点数量,我们可以编写一个Expression Visitor来完成这个任务:

public class ExpressionNodeCounter : ExpressionVisitor
{
    public int Calculate(Expression expr)
    {
        this.m_count = 0;
        this.Visit(expr);
        return this.m_count;
    }

    private int m_count;

    protected override Expression Visit(Expression exp)
    {
        this.m_count++;

        return base.Visit(exp);
    }
}

我们将表达式树传递给Visit方法后,Visit方法会将参数分派给其它方法,而其它方法又会将子节点交还给Visit方法进行递归调用,因此我们可以得知其实这个表达式树的每个节点都会“经过”Visit方法处理。因此,我们只需要重载Visit方法,查看它调用了几次就行了。值得注意的是,由于ExpressionVisitor只负责“遍历”,因此在进行“统计”、“收集信息”等任务的时候,都需要在子类内部保存临时信息。因此,许多ExpressionVisitor的实现其实都不是线程安全的。这点在使用或设计的时候都值得注意,我在开发FastLambda项目时,也对这个问题疏忽了。因此,虽然我用了一些方式来保证了FastEvaluator的线程安全,但事实上在相当长的时间内其实它都有着严重的问题。

那么,我们现在便可以用ExpressionNodeCounter来统计上次使用不同方式生成URL时所用到的Lambda表达式:

var blog = new Blog();
var post = new Post();

Expression<Action<HomeController>> expr = c => c.Post(blog, post);
Console.WriteLine(new ExpressionNodeCounter().Calculate(expr));

猜猜看结果如何?答案是7个节点,您是否能将它们一一指出?

其实,一般说来,由于自动生成闭包等机制,一个Lambda表达式实际构造出的节点总比我们“看出”的要多一些。