【问题标题】:Add Namespace to class with Roslyn CodeAnalyzer使用 Roslyn CodeAnalyzer 将命名空间添加到类
【发布时间】:2016-04-30 01:10:36
【问题描述】:

小猪退出Add a parameter to a method with a Roslyn CodeFixProvider

我正在创建一个CodeFixProvider,以确保所有异步方法都采用CancellationToken

//Before Code Fix:
public async Task Example(){}

//After Code Fix
public async Task Example(CancellationToken token){}

我可以将参数添加到方法中,但我必须使用Type.FullName 这样做。相反,我想将 System.Threading 的 using 语句添加到类文件的顶部,这样该方法就不需要使用完整的命名空间。换句话说:

// What I have thus far:
public class AClass{
  public async Task Example(System.Threading.CancellationToken token){}
}

// What I want:
using System.Threading;

public class AClass{
  public async Task Example(CancellationToken token){}
}

如何向Document 添加using 语句?

我尝试了几种方法,但是当我替换 SyntaxTree 引用中的多个节点时它会出现接缝丢失(因为树是不可变的,并且在每次更改时都会重建)。

我能够使用下面的代码使其部分工作,但这仅在填充 CompilationUnitSyntax.Using 属性时才有效,当 using 语句出现在命名空间之后 时,情况并非如此。这也依赖于文件中至少有一个using 语句。

有没有更好的方法来做到这一点?

private async Task<Document> HaveMethodTakeACancellationTokenParameter(
        Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
    {
        var syntaxTree = 
            (await document.GetSyntaxTreeAsync(cancellationToken))
                .GetRoot(cancellationToken);

        var method = syntaxNode as MethodDeclarationSyntax;

        #region Add Parameter
        var newParameter =
            SyntaxFactory.Parameter(
                SyntaxFactory.Identifier("cancellationToken")
            )
            .WithType(
                SyntaxFactory.ParseTypeName(
                    typeof(CancellationToken).FullName));

        var updatedMethod = method.AddParameterListParameters(newParameter);

        syntaxTree = syntaxTree.ReplaceNode(method, updatedMethod);

        #endregion

        #region Add Using Statements

        var compilation =
            syntaxTree as CompilationUnitSyntax;                    

        var systemThreadingUsingName =
            SyntaxFactory.QualifiedName(
                SyntaxFactory.IdentifierName("System"),
                SyntaxFactory.IdentifierName("Threading"));

        if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
        {
            syntaxTree = syntaxTree.InsertNodesAfter(compilation.Usings.Last(), new[]
            {
                SyntaxFactory.UsingDirective(
                        systemThreadingUsingName)
            });
        }

        #endregion

        return document.WithSyntaxRoot(syntaxTree);
    }

【问题讨论】:

标签: c# code-analysis roslyn roslyn-code-analysis


【解决方案1】:

一个选项是用注解标记所有方法,添加using语句,找到带有注解的方法,更改所有方法,并删除注解。

正如您所说,树是不可变的,但是在修改过程中注释不会丢失。因此,您需要以下内容:

var annotation = new SyntaxAnnotation();
var newRoot = root.ReplaceNode(
     method,
     method.WithAdditionalAnnotations(annotation));
newRoot = AddUsing(newRoot);
method = newRoot.GetAnnotatedNodes(annotation).First();
var newMethod = ChangeParameters(method);
newRoot = root.ReplaceNode(method, newMethod.WithoutAnnotations(annotation));

全面实施:

 private async Task<Document> HaveMethodTakeACancellationTokenParameter(
        Document document, SyntaxNode syntaxNode, CancellationToken cancellationToken)
    {
        var method = syntaxNode as MethodDeclarationSyntax;

        var cancellationTokenParameter =
           SyntaxFactory.Parameter(
               SyntaxFactory.Identifier("cancellationToken")
           )
           .WithType(
               SyntaxFactory.ParseTypeName(
                   typeof(CancellationToken).Name));

        var root = 
           (await document.GetSyntaxTreeAsync(cancellationToken))
               .GetRoot(cancellationToken);

        var annotation = new SyntaxAnnotation();
        var newRoot = root.ReplaceNode(
             method,
             method.WithAdditionalAnnotations(annotation));

        #region Add Using Statements

        var systemThreadingUsingStatement =
            SyntaxFactory.UsingDirective(
                SyntaxFactory.QualifiedName(
                    SyntaxFactory.IdentifierName("System"),
                    SyntaxFactory.IdentifierName("Threading")));

        var compilation =
            newRoot as CompilationUnitSyntax;

        if (null == compilation)
        {
            newRoot =
                newRoot.InsertNodesBefore(
                    newRoot.ChildNodes().First(),
                    new[] {systemThreadingUsingStatement});
        }
        else if (compilation.Usings.All(u => u.Name.GetText().ToString() != typeof(CancellationToken).Namespace))
        {
            newRoot = 
                newRoot.InsertNodesAfter(compilation.Usings.Last(), 
                new[]{ systemThreadingUsingStatement });
        }

        #endregion

        method = (MethodDeclarationSyntax)newRoot.GetAnnotatedNodes(annotation).First();

        var updatedMethod = method.AddParameterListParameters(cancellationTokenParameter);
        newRoot = newRoot.ReplaceNode(method, updatedMethod.WithoutAnnotations(annotation));

        return document.WithSyntaxRoot(newRoot);
    }

【讨论】:

  • 为什么不先修改方法再添加using?
  • 好点。在这种情况下也可以。如果您必须同时对树进行多项更改,则上述解决方案可以作为标记待修改节点的通用方法。
  • 感谢@Tamas-SonarSourceTeam。我根据您的建议使用完整实施更新了您的答案,以防将来对任何人有所帮助。感谢您的帮助,很抱歉我花了这么长时间才回到这里。
  • 对我来说,如果没有任何用途,newRoot = newRoot.InsertNodesBeforenewRoot = newRoot.InsertNodesAfter 不起作用。我用newRoot = compilationUnit.AddUsings(usingDirective);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多