C# Roslyn 替换方法

Posted

技术标签:

【中文标题】C# Roslyn 替换方法【英文标题】:C# Roslyn replace methods 【发布时间】:2021-11-08 19:44:00 【问题描述】:

我想重构(添加前缀)本地声明的方法及其在.cs 文件中的用法

实现这一目标的最佳做法是什么?

我当前的代码只处理声明

using System;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace CodeScanner  
  internal sealed class Fixer : CSharpSyntaxRewriter 
    public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node) 
      base.VisitInvocationExpression(node);
      // replace usages
      return node;
    
    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) 
      base.VisitMethodDeclaration(node);
      return node.ReplaceNode(node, SyntaxFactory.MethodDeclaration(
          node.AttributeLists,
          node.Modifiers,
          node.ReturnType,
          node.ExplicitInterfaceSpecifier,
          SyntaxFactory.Identifier("prefix_" + node.Identifier.Value),
          node.TypeParameterList,
          node.ParameterList,
          node.ConstraintClauses,
          node.Body,
          node.ExpressionBody));
    
  

  class Program 
    static void Main(string[] args) 
      var tree = CSharpSyntaxTree.ParseText(File.ReadAllText("./test.cs"));
      var rewriter = new Fixer();
      var result = rewriter.Visit(tree.GetRoot());
      Console.WriteLine(result.ToFullString());
    
  

输入文件

using System;

namespace TopLevel

  class Bar 
    public void test1()

    public void test2() Console.WriteLine("str"); 

    public void Fizz() 
      Console.WriteLine(test1());
      Console.WriteLine(test1(test2()));
      test2();
    
  

输出

using System;

namespace TopLevel

  class Bar 
    public void prefix_test1()

    public void prefix_test2() Console.WriteLine("str"); 

    public void prefix_Fizz() 
      Console.WriteLine(test1());
      Console.WriteLine(test1(test2()));
      test2();
    
  

所需的输出(更改@ Fizz):

using System;

namespace TopLevel

  class Bar 
    public void prefix_test1()

    public void prefix_test2() Console.WriteLine("str"); 

    public void prefix_Fizz() 
      Console.WriteLine(prefix_test1());
      Console.WriteLine(prefix_test1(prefix_test2()));
      prefix_test2();
    
  

【问题讨论】:

你试过什么?确切的问题是什么? @KrisVandermotten:我看到他的问题是方法定义已重命名,但对它的引用却没有。我认为应该改变Console.WriteLine(test1()); -> Console.WriteLine(prefix_test1()); @LeVu 我明白了,只是我不确定编写代码的问题是什么。这个问题不够准确。 我要重命名所有声明的方法,不清楚吗? @whoopdedoo 您的要求很明确。我不清楚是什么具体问题阻止您编写代码。 【参考方案1】:

我写了一些符合你设定要求的代码。

见.NET Fiddle

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using static Globals;

tree = CSharpSyntaxTree.ParseText(File.ReadAllText("Test.cs"));
compilation = CSharpCompilation.Create("HelloWorld").AddSyntaxTrees(tree);
model = compilation.GetSemanticModel(tree);
new Finder().Visit(tree.GetRoot());
Console.WriteLine(new Rewriter().Visit(tree.GetRoot()).NormalizeWhitespace().ToFullString());

public static class Globals

    public static SyntaxTree tree;
    public static CompilationUnitSyntax root;
    public static CSharpCompilation compilation;
    public static SemanticModel model;
    public static Dictionary<IMethodSymbol, string> renames = new();


public sealed class Finder : CSharpSyntaxWalker

    public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
    
        base.VisitMethodDeclaration(node);
        renames.Add(model.GetDeclaredSymbol(node), "prefix_" + node.Identifier.Value);
    


public sealed class Rewriter : CSharpSyntaxRewriter

    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax mds)
    
        IMethodSymbol symbol = model.GetDeclaredSymbol(mds);
        mds = (MethodDeclarationSyntax)base.VisitMethodDeclaration(mds);
        if (renames.TryGetValue(symbol, out string newName))
            mds = mds.ReplaceToken(mds.Identifier, SyntaxFactory.Identifier(newName));
        return mds;
    

    [return: NotNullIfNotNull("node")]
    public override SyntaxNode Visit(SyntaxNode node)
    
        node = base.Visit(node);
        if (node is SimpleNameSyntax sns &&
            model.GetSymbolInfo(sns) is  Symbol: IMethodSymbol ms  && renames.TryGetValue(ms, out string newName)
        )
            node = sns.ReplaceToken(sns.Identifier, SyntaxFactory.Identifier(newName));
        return node;
    

【讨论】:

以上是关于C# Roslyn 替换方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 Roslyn 生成格式良好的语法

Roslyn

Roslyn - OutOfMemoryException 由于内存中加载的程序集

使用 Roslyn 编译 C# 项目时,如何避免完全重新编译?

Roslyn 准备好迎接 c# 10 了吗?

C# Roslyn 更改注释类型