C# Roslyn 替换方法

标签 c# mono roslyn roslyn-code-analysis

我想重构(添加前缀)本地声明的方法及其在 .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();
    }
  }
}

最佳答案

我编写了一些符合您设置的要求的代码。
.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 替换方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69163967/

相关文章:

asp.net - Mono 是 ASP.NET 的可行替代品吗?

c# - 委托(delegate) System.Action<dynamic,int> 不接受 `1' 参数

c# - MONO 客户端和 ASP.NET 服务器之间的序列化/反序列化 - 可能吗?

c# - 将 Reflection.Emit 转换为 Roslyn

c# - 试图理解? C# 中的(空条件)运算符

c# - 处理 .NET IDisposable 对象

c# - Azure 表 InsertOrMerge 更新插入 : Can the to-be-merged object be dynamically created?

c# - 带有 DBQuery 的 Entity Framework Core 2 - 获取对象引用未设置到对象的实例

code-generation - Roslyn 的 SyntaxReceiver - 获取实现接口(interface)的类

c# - TcpClient 在异步读取期间断开连接