使用roslyn替换所有类使用

时间:2016-04-05 14:14:48

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

我想做什么: 假设我们在程序集Class1中有一个类OldAssembly。 该程序集由许多未知项目引用。 我将把这个类移动到一个新的程序集NewAssembly,最终名称和命名空间也会改变。现在必须调整所有用法。 我想创建一个自动执行这些代码调整的工具。

到目前为止我做了什么: 我玩过roslyn Renamer

var workspace = MSBuildWorkspace.Create();
var originalSln = workspace.OpenSolutionAsync(@"D:\spikes\ToBeFixed\ToBeFixed.sln").Result;

var project = originalSln.Projects.Single();
var compilation = project.GetCompilationAsync().Result;

var renameFrom = compilation.GetSymbolsWithName(s => s.Contains("Class1")).Single();
const string renameTo = "Class2";
var optionSet = originalSln.Workspace.Options;
var modifiedSln = Renamer.RenameSymbolAsync(originalSln, renameFrom, renameTo, optionSet).Result;

workspace.TryApplyChanges(modifiedSln);

但它也重命名了源类。所以我查看了Renamer代码并尝试根据我的用例进行调整,但由于在那里使用了一些内部结构而失败了。

问题: 在将类从一个程序集移动到另一个程序集后,如何自动执行代码调整。

1 个答案:

答案 0 :(得分:3)

您可以通过以下方式实现您所寻找的目标:

  1. 在整个解决方案中重命名班级的所有用法
  2. 用其原始self替换重命名的类本身。

    public async static Task TestingRenamer()
    {
        var code = @"
        using System;
    
        //We do not want to rename MyClass (we want to keep it the same)
        public class MyClass
        {
            public MyClass()
            {
            }
        }
    
        public class Program
        {
            public static void Main()
            {
                //We want to rename this usage
                var x = new MyClass();  
            }   
        }";
    
        var newClassName = "MY_NEW_CLASS_NAME";
        var document = getDocumentForCode(code);
        var compilation = await document.Project.GetCompilationAsync();
    
        var root = await document.GetSyntaxRootAsync();
        var originalClass = root.DescendantNodesAndSelf().OfType<ClassDeclarationSyntax>().First();
    
        var model = compilation.GetSemanticModel(root.SyntaxTree);
        var originalSymbol = model.GetDeclaredSymbol(originalClass);
    
        //Rename all 
        var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, originalSymbol, newClassName, document.Project.Solution.Workspace.Options);
    
        //Revert the original class
        var newDocument = newSolution.GetDocument(document.Id);
        var newSyntaxRoot = await newDocument.GetSyntaxRootAsync();
        var newClass = newSyntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().Where(n => n.Identifier.ToString() == newClassName).Single();
        newSyntaxRoot = newSyntaxRoot.ReplaceNode(newClass, originalClass);
        newDocument = newDocument.WithSyntaxRoot(newSyntaxRoot);
    
        //We've now renamed all usages and reverted the class back to its original self.
        var finalSolution = newDocument.Project.Solution;
    }
    
    //Helper method to build Document
    private static Document getDocumentForCode(string code)
    {
        var ws = new AdhocWorkspace();
        var Mscorlib = MetadataReference.CreateFromAssembly(typeof(object).Assembly);
        var references = new List<MetadataReference>() { Mscorlib };
        var projInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Default, "MyProject", "MyAssembly", "C#", metadataReferences: references);
        var project = ws.AddProject(projInfo);
    
        var text = SourceText.From(code);
        var myDocument = ws.AddDocument(project.Id, "MyDocument.cs", text);
        return myDocument;
    }