W poprzednim wpisie przedstawiłem klasę CSharpSyntaxWalker – przydatną przy analizie drzewa kodu. Dzięki niej, automatycznie bez pisania kodu rekurencyjnego jesteśmy w stanie przejść przez każdy element kodu.
Dzisiaj o analogicznym rozwiązaniu ale służącym do przepisywania kodu a nie tylko jego analizowania. Mechanizm działa bardzo podobnie do CSharpSyntaxWalker. Wystarczy, że stworzymy klasę dziedziczącą po CSharpSyntaxRewriter:
public class CustomRewriter : CSharpSyntaxRewriter { public override SyntaxNode VisitEmptyStatement(EmptyStatementSyntax node) { return null; } }
Następnie, wystarczy wywołać Visit na węźle, który chcemy przepisać:
SyntaxTree tree = CSharpSyntaxTree.ParseText(@" public class Class1 { public void TestMethod1() { } public void TestMethod1(string a, string b, int c) { if(c==5 { ; ; } } } "); var rewritter = new CustomRewriter(); SyntaxNode rewrittenNode = rewritter.Visit(tree.GetRoot());
Po przepisaniu, nowy kod nie będzie zawierał dwóch pustych instrukcji, które widzimy w powyższej instrukcji warunkowej (if(c==5)):
Na przykład, w celu wstawienia komentarza przed każdym IF’em możemy:
public class CustomRewriter : CSharpSyntaxRewriter { public override SyntaxNode VisitIfStatement(IfStatementSyntax node) { return base.VisitIfStatement(node).WithLeadingTrivia(SyntaxFactory.Comment("//test")); } }
W narzędziach generujących kod, powyższa klasa jest bardzo przydatna. Na przykład, jeśli chcemy automatycznie wygenerować komentarze dla naszego kodu, wystarczy przeciążyć VisitMethodDeclaration:
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { return base.VisitMethodDeclaration(node); }
CSharpSyntaxWalker oraz CsharpSyntaxRewritter są pomocniczymi klasami. Za pomocą LINQ i standardowego API możemy osiągnąć to samo. Jak wspomniałem jednak, wymagałoby to pisania rekurencyjnego kodu i dla skomplikowanych projektów jest to po prostu niewygodne oraz może spowodować problemy z wydajnością.