I previously asked this question, which was answered, but someone gave a suggestion that might help me prevent making similar mistakes as I move forward.
Adding Auto-Implemented Property to class using Roslyn
The suggestion was that I build the Syntax Tree from the bottom up and not from the top down. Could someone provide a small demo or a link that shows how I would do this from the ground up?
Here is the code again:
var root = (CompilationUnitSyntax)document.GetSyntaxRoot();
// Add the namespace
var namespaceAnnotation = new SyntaxAnnotation();
root = root.WithMembers(
Syntax.NamespaceDeclaration(
Syntax.ParseName("ACO"))
.NormalizeWhitespace()
.WithAdditionalAnnotations(namespaceAnnotation));
document = document.UpdateSyntaxRoot(root);
// Add a class to the newly created namespace, and update the document
var namespaceNode = (NamespaceDeclarationSyntax)root
.GetAnnotatedNodesAndTokens(namespaceAnnotation)
.Single()
.AsNode();
var classAnnotation = new SyntaxAnnotation();
var baseTypeName = Syntax.ParseTypeName("System.Windows.Forms.Form");
SyntaxTokenList syntaxTokenList = new SyntaxTokenList()
{
Syntax.Token(SyntaxKind.PublicKeyword)
};
var newNamespaceNode = namespaceNode
.WithMembers(
Syntax.List<MemberDeclarationSyntax>(
Syntax.ClassDeclaration("MainForm")
.WithAdditionalAnnotations(classAnnotation)
.AddBaseListTypes(baseTypeName)
.WithModifiers(Syntax.Token(SyntaxKind.PublicKeyword))));
root = root.ReplaceNode(namespaceNode, newNamespaceNode).NormalizeWhitespace();
document = document.UpdateSyntaxRoot(root);
var attributes = Syntax.List(Syntax.AttributeDeclaration(Syntax.SeparatedList(Syntax.Attribute(Syntax.ParseName("STAThread")))));
// Find the class just created, add a method to it and update the document
var classNode = (ClassDeclarationSyntax)root
.GetAnnotatedNodesAndTokens(classAnnotation)
.Single()
.AsNode();
var syntaxList = Syntax.List<MemberDeclarationSyntax>(
Syntax.MethodDeclaration(
Syntax.ParseTypeName("void"), "Main")
.WithModifiers(Syntax.TokenList(Syntax.Token(SyntaxKind.PublicKeyword)))
.WithAttributes(attributes)
.WithBody(
Syntax.Block()));
syntaxList = syntaxList.Add(Syntax.PropertyDeclaration(Syntax.ParseTypeName("System.Windows.Forms.Timer"), "Ticker"));
var newClassNode = classNode
.WithMembers(syntaxList);
root = root.ReplaceNode(classNode, newClassNode).NormalizeWhitespace();
document = document.UpdateSyntaxRoot(root);
So how would I do the same thing, but from the ground up?
Thanks in advance,
Bob
P.S. My property is also missing the "get; set;" text within it. Could someone comment on what I am forgetting to add which causes this text to be added to the property?
Believe it or not, I have written a tool called Roslyn Code Quoter especially to answer this question.
http://roslynquoter.azurewebsites.net
The tool can take any C# program and automatically generate a snippet of code like Matt has written above. Since it also generates everything perfectly including all the whitespace, the code can get rather unwieldy. But you can exclude parts that generate trivia, and then just call NormalizeWhitespace() on the resulting node, it will automatically insert the trivia so that the code is correctly formatted.
For the sake of completeness I'm posting the code in all its gory detail, so that you can see how to construct whitespace and all those little details.
CompilationUnit().WithMembers(
SingletonList<MemberDeclarationSyntax>(
NamespaceDeclaration(
IdentifierName("ACO"))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
ClassDeclaration("MainForm")
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)))
.WithBaseList(
BaseList(
SingletonSeparatedList<BaseTypeSyntax>(
SimpleBaseType(
QualifiedName(
QualifiedName(
QualifiedName(
IdentifierName("System"),
IdentifierName("Windows")),
IdentifierName("Forms")),
IdentifierName("Form"))))))
.WithMembers(
List<MemberDeclarationSyntax>(
new MemberDeclarationSyntax[]{
PropertyDeclaration(
QualifiedName(
QualifiedName(
QualifiedName(
IdentifierName("System"),
IdentifierName("Windows")),
IdentifierName("Forms")),
IdentifierName("Timer")),
Identifier("Ticker"))
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)))
.WithAccessorList(
AccessorList(
List<AccessorDeclarationSyntax>(
new AccessorDeclarationSyntax[]{
AccessorDeclaration(
SyntaxKind.GetAccessorDeclaration)
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken)),
AccessorDeclaration(
SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(
Token(SyntaxKind.SemicolonToken))}))),
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
Identifier("Main"))
.WithAttributeLists(
SingletonList<AttributeListSyntax>(
AttributeList(
SingletonSeparatedList<AttributeSyntax>(
Attribute(
IdentifierName("STAThread"))))))
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)))
.WithBody(
Block())}))))))
.NormalizeWhitespace()
This will build up your entire compilation unit tree in one expression.
var cu = SyntaxFactory.CompilationUnit()
.AddMembers(
SyntaxFactory.NamespaceDeclaration(Syntax.IdentifierName("ACO"))
.AddMembers(
SyntaxFactory.ClassDeclaration("MainForm")
.AddBaseListTypes(SyntaxFactory.ParseTypeName("System.Windows.Forms.Form"))
.WithModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddMembers(
Syntax.PropertyDeclaration(SyntaxFactory.ParseTypeName("System.Windows.Forms.Timer"), "Ticker")
.AddAccessorListAccessors(
SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)),
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))),
SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName("void"), "Main")
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddAttributes(SyntaxFactory.AttributeDeclaration().AddAttributes(SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("STAThread"))))
.WithBody(SyntaxFactory.Block())
)
)
);
Of course, you don't have to do it as a single expression. I could have used separate local variables to collect the pieces I wanted and then added them in the construction of the containing syntax piece.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With