如何在C# Roslyn中实现不同类之间的静态成员迁移?
1 静态成员判断方法
你可以选择两种方式实现判断:
- 语法层面直接判断修饰符:不需要调用语义模型接口,性能更高,适合快速过滤
代码示例:if (member.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword))) - 语义符号层面判断:判断结果更准确,可覆盖所有特殊场景(比如隐式静态的成员场景)
代码示例:var symbol = semanticModel.GetDeclaredSymbol(member); if (symbol != null && symbol.IsStatic)
2 静态成员迁移与引用更新方案
实现逻辑分为四步:先预创建目标存储类、迁移成员到目标类、遍历更新所有引用、提交所有变更,完整修改后的代码如下:
// 先定义你的目标静态成员存储类名,可根据需求修改 private const string TargetStaticClassName = "ObfuscationStaticContainer"; public async Task Run(string solutionPath) { m_workspace = MSBuildWorkspace.Create(); Solution solution = await m_workspace.OpenSolutionAsync(solutionPath); var project = solution.Projects.FirstOrDefault(); if (project == null) return; // 第一步:先找到或创建目标存储类 ClassDeclarationSyntax targetClass = null; Document targetClassDocument = null; // 这里示例是先找项目里已有的目标类,你也可以直接创建新的文档和类 foreach (var doc in project.Documents) { var root = await doc.GetSyntaxRootAsync(); targetClass = root.DescendantNodes().OfType<ClassDeclarationSyntax>() .FirstOrDefault(c => c.Identifier.Text == TargetStaticClassName); if (targetClass != null) { targetClassDocument = doc; break; } } // 如果没找到就创建新的目标类,这里简化实现,你可根据需求调整命名空间、访问修饰符 if (targetClass == null) { targetClass = SyntaxFactory.ClassDeclaration(TargetStaticClassName) .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword)); var root = SyntaxFactory.CompilationUnit() .AddMembers(SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("YourProjectNamespace")) .AddMembers(targetClass)); targetClassDocument = project.AddDocument($"{TargetStaticClassName}.cs", root); solution = targetClassDocument.Project.Solution; targetClass = (await targetClassDocument.GetSyntaxRootAsync()).DescendantNodes().OfType<ClassDeclarationSyntax>().First(); } // 第二步:遍历所有文档找静态成员 foreach (var document in project.Documents) { if (document == targetClassDocument) continue; // 跳过目标类本身 var syntaxTree = await document.GetSyntaxTreeAsync(); var semanticModel = await document.GetSemanticModelAsync(); var root = await syntaxTree.GetRootAsync(); var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>(); foreach (var c in classes) { foreach (var member in c.Members) { // 用刚才的方法判断静态成员 if (!member.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword))) continue; var memberSymbol = semanticModel.GetDeclaredSymbol(member); if (memberSymbol == null) continue; // 第三步:迁移成员到目标类 // 复制成员节点,可根据需求调整修饰符,比如把private改成internal var copiedMember = member; // 更新目标类的语法树,把成员加进去 var newTargetClass = targetClass.AddMembers(copiedMember); var targetRoot = await targetClassDocument.GetSyntaxRootAsync(); var newTargetRoot = targetRoot.ReplaceNode(targetClass, newTargetClass); solution = targetClassDocument.WithSyntaxRoot(newTargetRoot).Project.Solution; // 重新获取更新后的目标类,方便下一次添加 targetClass = newTargetClass; targetClassDocument = solution.GetDocument(targetClassDocument.Id); // 第四步:更新所有引用 var references = await SymbolFinder.FindReferencesAsync(memberSymbol, solution); foreach (var reference in references.SelectMany(r => r.Locations)) { var refDoc = solution.GetDocument(reference.Document.Id); var refRoot = await refDoc.GetSyntaxRootAsync(); var refNode = refRoot.FindNode(reference.Location.SourceSpan); // 把引用替换成 目标类名.成员名 的形式 var newRefNode = SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(TargetStaticClassName), SyntaxFactory.IdentifierName(memberSymbol.Name)); var newRefRoot = refRoot.ReplaceNode(refNode, newRefNode); solution = refDoc.WithSyntaxRoot(newRefRoot).Solution; } // 从原类中删除该静态成员 var newClass = c.RemoveNode(member, SyntaxRemoveOptions.KeepNoTrivia); var newRoot = root.ReplaceNode(c, newClass); solution = document.WithSyntaxRoot(newRoot).Project.Solution; // 更新当前文档的root,避免后续遍历出错 root = newRoot; } } } // 提交所有变更 m_workspace.TryApplyChanges(solution); }
注意事项
- 上述代码为简化实现,实际使用时需要处理泛型成员、重载成员、重名成员、命名空间引用补全等边缘场景
- 访问修饰符可以根据你的混淆需求调整,比如将原私有静态成员改为内部访问,保证所有引用位置可访问
- 如果要处理多个项目的情况,需要对所有项目都执行上述遍历逻辑
内容的提问来源于stack exchange,提问作者F Chopin
相关产品推荐
相关产品推荐

