一次对SyntaxTree进行多次修改

时间:2012-11-17 21:56:38

标签: c# .net roslyn

我想一次对Roslyn语法树进行一些修改,所有修改都在同一个代码区域

tree = tree.ReplaceNodes(oldNode, newNode).RemoveNode(toRemove);
然而,只有第一次修改成功。似乎第一个更改会更改其周围的所有节点,因此RemoveNodes方法不再在结果树中找到toRemove。我真的,真的,不想再重新计算新树中toRemove的工作,并使用单个SyntaxRewriter来执行所有工作(覆盖DefaultVisit方法)是可笑的慢。

我怎样才能做我想做的事?

1 个答案:

答案 0 :(得分:5)

在我提供一些替代方案之前,您对SyntaxRewriter的评论是非常慢的"有点令人惊讶。当你说"慢"你的意思是"它写了很多代码"或者"它的表现非常糟糕"?这是执行多次替换的最快(执行时间)方式,ReplaceNodes和RemoveNode都在内部使用重写器。如果您遇到性能问题,请确保在实施DefaultVisit时,如果您感兴趣的节点位于其所要调用的节点下,则只访问子类型。简单的技巧是比较跨度并确保传递的节点的跨度与您正在处理的节点相交。

无论如何,SyntaxAnnotations提供了一种在修改后定位树中节点的有用方法。您可以只创建该类型的实例,并使用WithAdditionalAnnotations扩展方法将其附加到节点。您可以使用GetAnnotatedNodesOrTokens方法再次找到该节点。

因此解决问题的一种方法是注释你的toRemove,然后当你调用ReplaceNodes时,在同一个调用中做两个替换 - 一个用于执行oldNode - > newNode替换然后一个做toRemove - > toRemoveWithAnnotation替换。然后在结果树中找到带注释的节点并调用RemoveNode。

如果你知道oldNode和tommove不是彼此的祖先(即他们在树的不相关部分),那么另一种选择就是颠倒排序。获取toRemove的父节点(称之为oldNodeParent)并调用RemoveNode,这意味着您将获得更新的父节点(将其称为oldNodeParentRewritten)。然后,调用ReplaceNodes进行两次替换:oldNode - > newNode和oldNodeParent - > oldNodeParentRewritten。不需要注释。