Creating Code Using the Syntax Factory
There are times when it is necessary to generate code and in Visual Studio 2015, the Roslyn compiler gives you a lot of power to do this.
Let's say we want to output Console.WriteLine("A");
. The easiest way to start on this is to open the Syntax Visualizer and see what types of nodes make up the expression that represents our console write line invocation.
As you can see, it is an ExpressionStatement
that contains an InvocationExpression
, which in turn contains a SimpleMemberAccessExpression
and an ArgumentList
.
Now if you look at the methods available on the Microsoft.CodeAnalysis.CSharp.SyntaxFactory
, you will seem methods that can create the nodes we see in the syntax graph. For example, to create the invocation expression, you would use the SyntaxFactory.InvocationExpression
method. Very often when people create these methods you will see a lot of methods calls on a single line of code. For example, the Roslyn Quoter Project on GitHub, generates the following code to do our Console.WriteLine("A");
.
return SyntaxFactory.ExpressionStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(
@"Console"),
SyntaxFactory.IdentifierName(
@"WriteLine"))
.WithOperatorToken(
SyntaxFactory.Token(
SyntaxKind.DotToken)))
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(
SyntaxFactory.Argument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(
SyntaxFactory.TriviaList(),
@"""A""",
@"""A""",
SyntaxFactory.TriviaList())))))
.WithOpenParenToken(
SyntaxFactory.Token(
SyntaxKind.OpenParenToken))
.WithCloseParenToken(
SyntaxFactory.Token(
SyntaxKind.CloseParenToken))));
The code is generated, so it is a little verbose, but it provides us a great starting point to know what work needs to be done. We can refactor that a bit to get something that is more consumable and debuggable:
static void Main(string[] args)
{
var console = SyntaxFactory.IdentifierName("Console");
var writeline = SyntaxFactory.IdentifierName("WriteLine");
var memberaccess = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, console, writeline);
var argument = SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("A")));
var argumentList = SyntaxFactory.SeparatedList(new[] { argument });
var writeLineCall =
SyntaxFactory.ExpressionStatement(
SyntaxFactory.InvocationExpression(memberaccess,
SyntaxFactory.ArgumentList(argumentList)));
var text = writeLineCall.ToFullString();
Console.WriteLine(text);
Console.ReadKey();
}
If you look at the syntax tree image, you'll see that we are just going to build up the syntax nodes (in blue in the tree) from the bottom up. The first two lines are simply creating variables to hold the IdentifierName
syntax nodes in the tree. Next, we create the SimpleMemberAccessExpression
and pass it the two identifiers. Next we create an Argument
with a Literal
of "A"
. We wrap that in a list that we can later pass to the SyntaxFactory.ArgumentList
. Finally we use all of the nodes we created previously to build up the complete syntax tree, by calling the appropriate methods on the SyntaxFactory
.
You are now armed with the power of creating code using Roslyn's Syntax Factory. Use it wisely (and sparingly). Special thanks to Kirill Osenkov for creating the Roslyn Quoter project and making it available. That tools is invaluable when getting started using the SyntaxFactory.