roslyn - SyntaxRewriter 和多个语句

标签 roslyn

我在 Roslyn 中使用 SyntaxRewriter 时遇到了一个棘手的情况。我想重写某些类型的语句,包括局部变量声明。该解决方案要求我将有问题的语句转换为多个语句,如以下简单示例所示:

void method()
{
    int i;
}

变成了

void method()
{
    int i;
    Console.WriteLine("I declared a variable.");
}

我见过其他例子,其中使用 block 来完成类似的事情,但是当然在变量声明的情况下,声明的范围会受到影响。我想出了以下解决方案,但我对此犹豫不决。它看起来过于复杂,需要打破访问者模式:

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.\");");
        }
    }
}

最佳答案

我认为有一种简单的方法可以使您的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 .

关于roslyn - SyntaxRewriter 和多个语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9775335/

相关文章:

c# - Roslyn 不优化多个增量是否有原因?

ef-code-first - 使用 Roslyn 创建 EF CodeFirst DbContext

c# - 如何使用 Roslyn 获取方法调用/类声明的全名路径

c# - Roslyn 的发布版本如何实现不可变树?

c# - 如何在基于 Roslyn 的脚本中使用 System.IO.FileSystem?

c# - 如何限制从 Roslyn 脚本引擎访问程序集中的成员?

c# - 如何确定哪个 SemanticModel 实例解析 ExpressionSyntax

c# - 为什么要有 Enumerator 结构和 EnumeratorImpl 类?

c# - 使用 Roslyn 如何使用指令更新类?

visual-studio - 什么取代了 Visual Studio 2019 中的代码分析?