我在使用罗斯林的SyntaxRewriter时遇到了一个棘手的情况。我想重写某些类型的语句,包括局部变量声明。该解决方案要求我将有问题的语句转换为多个语句,如下面这个简单的示例所示:
void method()
{
int i;
}变成了
void method()
{
int i;
Console.WriteLine("I declared a variable.");
}我见过使用块来完成类似操作的其他示例,但当然在变量声明的情况下,声明的作用域也会受到影响。我想出了以下解决方案,但我对此犹豫不决。它看起来过于复杂,需要打破访问者模式:
class Rewriter: SyntaxRewriter
{
public override SyntaxList<TNode> VisitList<TNode>(SyntaxList<TNode> list)
{
if (typeof(TNode) == typeof(StatementSyntax))
return Syntax.List<TNode>(list.SelectMany(st => RewriteStatementInList(st as StatementSyntax).Cast<TNode>()));
else
return base.VisitList<TNode>(list);
}
private IEnumerable<SyntaxNode> RewriteStatementInList(StatementSyntax node)
{
if (node is LocalDeclarationStatementSyntax)
return PerformRewrite((LocalDeclarationStatementSyntax)node);
//else if other cases (non-visitor)
return Visit(node).AsSingleEnumerableOf();
}
private IEnumerable<SyntaxNode> PerformRewrite(LocalDeclarationStatementSyntax orig)
{
yield return orig;
yield return Syntax.ParseStatement("Console.WriteLine(\"I declared a variable.\");");
}
}我遗漏了什么?编辑语句并删除它们(通过空语句)似乎比重写成多个语句更直接。
我对这个答案的看法是:
class Rewriter : SyntaxRewriter
{
readonly ListVisitor _visitor = new ListVisitor();
public override SyntaxList<TNode> VisitList<TNode>(SyntaxList<TNode> list)
{
var result = Syntax.List(list.SelectMany(_visitor.Visit).Cast<TNode>());
return base.VisitList(result);
}
private class ListVisitor : SyntaxVisitor<IEnumerable<SyntaxNode>>
{
protected override IEnumerable<SyntaxNode> DefaultVisit(SyntaxNode node)
{
yield return node;
}
protected override IEnumerable<SyntaxNode> VisitLocalDeclarationStatement(
LocalDeclarationStatementSyntax node)
{
yield return node;
yield return Syntax.ParseStatement("Console.WriteLine(\"I declared a variable.\");");
}
}
}发布于 2012-03-20 18:16:10
我认为有一种简单的方法可以让您的Rewriter更像访问者:使用另一个访问者来处理列表中的节点:
class Rewriter: SyntaxRewriter
{
readonly Visitor m_visitor = new Visitor();
public override SyntaxList<TNode> VisitList<TNode>(SyntaxList<TNode> list)
{
var result = Syntax.List(list.SelectMany(m_visitor.Visit).Cast<TNode>());
return base.VisitList(result);
}
}
class Visitor : SyntaxVisitor<IEnumerable<SyntaxNode>>
{
protected override IEnumerable<SyntaxNode> DefaultVisit(SyntaxNode node)
{
return new[] { node };
}
protected override IEnumerable<SyntaxNode> VisitLocalDeclarationStatement(
LocalDeclarationStatementSyntax node)
{
return new SyntaxNode[]
{
node,
Syntax.ParseStatement(
"Console.WriteLine(\"I declared a variable.\");")
};
}
}请注意,这是不安全的,如果您返回的集合包含的对象不是来自Visitor的TNode,则会引发InvalidCastException。
发布于 2012-03-20 04:01:47
我不知道有什么更好的方法来处理这个问题。另一种稍微更像“访问者风格”的方法是使用VisitLocalDeclaration来注释您想要替换为return (base.Visit(node).WithAdditionalAnnoations(myAnnotation);的节点。然后在VisitList中,您只需找到注释所在的子节点,并在该点重写即可。
发布于 2016-06-29 02:30:20
我浏览了Roslyn的源代码,看看Roslyn团队自己是如何做到这一点的。下面是一个例子:http://source.roslyn.codeplex.com/Microsoft.CodeAnalysis.CSharp.Features/R/bcd389b836bf7b4c.html
简而言之,我认为它或多或少是这样的。(这个重写器恰好删除了StatementExpressions,但是您可以看到它是由迭代器方法构建的,所以添加方法也很容易)。
class TreeRewriter : CSharpSyntaxRewriter
{
public override SyntaxNode VisitBlock(BlockSyntax node)
=> node.WithStatements(VisitList(SyntaxFactory.List(ReplaceStatements(node.Statements))));
public override SyntaxNode VisitSwitchSection(SwitchSectionSyntax node)
=> node.WithStatements(VisitList(SyntaxFactory.List(ReplaceStatements(node.Statements))));
IEnumerable<StatementSyntax> ReplaceStatements(IEnumerable<StatementSyntax> statements)
{
foreach (var statement in statements)
{
if (statement is ExpressionStatementSyntax) continue;
yield return statement;
}
}
}下面是我如何驱动这些代码:
var rewriter = new TreeRewriter();
var syntaxTree = await document.GetSyntaxTreeAsync();
var root = await syntaxTree.GetRootAsync();
var newRoot = rewriter.Visit(root);
var newDocument = document.WithSyntaxRoot(newRoot);https://stackoverflow.com/questions/9775335
复制相似问题