我一直在关注Roslyn CTP,虽然它解决了与Expression tree API类似的问题,但两者都是不可改变的,但Roslyn以完全不同的方式这样做:
Expression
个节点没有引用父节点,使用ExpressionVisitor
进行修改,这就是为什么可以重用大部件的原因。
另一方面,Roslyn的SyntaxNode
有一个对其父级的引用,因此所有节点都有效地成为一个无法重用的块。提供诸如Update
,ReplaceNode
等方法以进行修改。
这到底在哪里? Document
? Project
? ISolution
? API促进了树的逐步更改(而不是按钮),但是每个步骤都是完整的副本吗?
他们为什么做出这样的选择?我有什么有趣的伎俩吗?
答案 0 :(得分:170)
更新:这个问题是the subject of my blog on June 8th, 2012。谢谢你提出的好问题!
好问题。我们讨论了你长期以来提出的问题。
我们希望拥有一个具有以下特征的数据结构:
通过持久性我的意思是当对文本缓冲区进行编辑时,重用树中的大多数现有节点的能力。由于节点是不可变的,因此重用它们没有任何障碍。我们需要这个来表现;每次按键时我们都无法重新解析文件的大量文件。我们需要重新解析并重新解析受编辑影响的树的部分。
现在,当您尝试将所有这些内容放入一个数据结构时,您会立即遇到问题:
但在Roslyn团队中,我们经常做不可能的事情。我们实际上通过保留两个解析树来完成不可能的事情。 “绿色”树是不可变的,持久的,没有父引用,是“自下而上”构建的,每个节点都跟踪其宽度而不是绝对位置。当编辑发生时,我们只重建受编辑影响的绿树部分,通常是树中总解析节点的O(log n)。
“红色”树是一个围绕绿树建立的不可变 facade ;它是按需“自上而下”构建的并在每次编辑时丢弃。当您从顶部下降树时,它通过按需制造它来计算父引用。它通过从宽度计算它们来制造绝对位置,再次,当你下降时。
你,用户,只看到红树;绿树是一个实现细节。如果您查看解析节点的内部状态,您实际上会看到在另一个类型中存在对另一个解析节点的引用;这是绿树节点。
顺便提一下,这些被称为“红/绿树”,因为它们是我们在设计会议中用于绘制数据结构的白板标记颜色。颜色没有其他意义。
这种策略的好处是我们可以获得所有这些伟大的东西:不变性,持久性,父引用等等。成本是这个系统很复杂,如果“红色”外墙变大,可能会占用大量内存。我们目前正在做实验,看看我们是否可以在不损失收益的情况下降低部分成本。