基于Roslyn的CSharpSyntaxRewriter:同步修改接口与实现类方法签名
Got it, let's break down how to solve this problem. You've already got a CustomRewriter that modifies method signatures in classes, but now you need to sync those changes to the corresponding interface definitions when the method implements an interface method. Here's a step-by-step approach:
1. Pass the SemanticModel to Your Rewriter
Syntax alone isn't enough to reliably link class methods to their interface counterparts—you need semantic analysis. First, update your CustomRewriter to accept a SemanticModel in its constructor, since we'll need it to resolve symbols:
public class CustomRewriter : CSharpSyntaxRewriter { private readonly SemanticModel _semanticModel; private List<INamedTypeSymbol> _implementedInterfaces; public CustomRewriter(SemanticModel semanticModel) { _semanticModel = semanticModel; _implementedInterfaces = new List<INamedTypeSymbol>(); } }
2. Collect Implemented Interfaces in VisitClassDeclaration
When visiting a class, grab all the interfaces it implements and store their symbols. This lets us check later if a method belongs to any of these interfaces:
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) { // Get the semantic symbol for the class if (_semanticModel.GetSymbolInfo(node).Symbol is INamedTypeSymbol classSymbol) { // Collect all implemented interfaces (including inherited ones) _implementedInterfaces = classSymbol.AllInterfaces.ToList(); } // Proceed with the default class visit, then apply any changes var rewrittenClass = (ClassDeclarationSyntax)base.VisitClassDeclaration(node); return rewrittenClass; }
3. Check if a Method Implements an Interface Method
In VisitMethodDeclaration, use the semantic model to determine if the current method is an implementation of an interface method. This handles both implicit and explicit implementations:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { var rewrittenMethod = node; // First, apply your existing logic to add parameters (your working code) if (IsDataCallMethod(node)) // Replace with your existing check for data call methods { rewrittenMethod = AddParameterToMethod(rewrittenMethod); // Your existing method modification logic // Now check if this method implements an interface method if (_semanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol methodSymbol) { // Handle implicit implementations: find the matching interface method var interfaceMethod = FindCorrespondingInterfaceMethod(methodSymbol); // Handle explicit implementations (e.g., IMyInterface.MyMethod()) if (interfaceMethod == null && methodSymbol.ExplicitInterfaceImplementations.Any()) { interfaceMethod = methodSymbol.ExplicitInterfaceImplementations.First(); } if (interfaceMethod != null) { // Sync the interface's method signature to match the rewritten class method SyncInterfaceMethodSignature(interfaceMethod, rewrittenMethod); } } } return rewrittenMethod; } // Helper to find the interface method a class method implements private IMethodSymbol FindCorrespondingInterfaceMethod(IMethodSymbol classMethod) { foreach (var interfaceSymbol in _implementedInterfaces) { var interfaceMethod = interfaceSymbol.GetMembers(classMethod.Name) .OfType<IMethodSymbol>() .FirstOrDefault(m => m.MatchesSignature(classMethod)); if (interfaceMethod != null) { return interfaceMethod; } } return null; } // Helper to check if two methods have matching signatures private static bool MatchesSignature(this IMethodSymbol method1, IMethodSymbol method2) { if (method1.Name != method2.Name || method1.ReturnType != method2.ReturnType) return false; if (method1.Parameters.Count != method2.Parameters.Count) return false; for (int i = 0; i < method1.Parameters.Count; i++) { if (method1.Parameters[i].Type != method2.Parameters[i].Type) return false; } return true; }
4. Sync the Interface Method Signature
Once you've identified the corresponding interface method, find its syntax node, modify its signature to match the rewritten class method, and replace it in the syntax tree:
private void SyncInterfaceMethodSignature(IMethodSymbol interfaceMethod, MethodDeclarationSyntax rewrittenClassMethod) { // Get the interface method's syntax node from its semantic location var interfaceMethodSyntax = _semanticModel.SyntaxTree.GetRoot() .FindNode(interfaceMethod.Locations.First().SourceSpan) as MethodDeclarationSyntax; if (interfaceMethodSyntax != null) { // Create a new interface method with the updated parameters (and return type if needed) var updatedInterfaceMethod = interfaceMethodSyntax .WithParameterList(rewrittenClassMethod.ParameterList) .WithReturnType(rewrittenClassMethod.ReturnType); // Uncomment if you modify return types too // Replace the old interface method with the updated one in the root tree var newRoot = _semanticModel.SyntaxTree.GetRoot().ReplaceNode(interfaceMethodSyntax, updatedInterfaceMethod); // Note: If rewriting an entire solution, track the updated root or integrate this into your overall tree handling } }
Key Notes
- Explicit Implementations: The
ExplicitInterfaceImplementationsproperty handles cases where methods are written asIMyInterface.MyMethod(). - Semantic Reliability: Using
SemanticModelensures you're linking the correct method symbols, even with overloaded methods or similarly named interfaces. - Immutability: Remember syntax nodes are immutable—always create new nodes and replace them in the tree instead of modifying existing ones.
内容的提问来源于stack exchange,提问作者jrandomuser

