In early 2020, the first preview from Source Generators was introduced.
Source Generators: a new C# compiler feature that lets C# developers inspect user code and generate new C# source files that can be added to a compilation.
The option to dynamically generate C# code in your project was already possible using the
T4 text templating feature.
See the example below which generates three public properties in a class using
T4 text templating:
The generated C# class looks like this:
The main difference between T4 text templates and the new Source Generators is that a T4 template needs to be executed manually before the source code is compiled. Whereas a source generator runs while the code is being compiled. So the generated output becomes part of the project.
📝 Access to compilation object
With source generators your have access to a compilation object that represents all the code that is being compiled. This object can be analyzed and inspected which allows you to write code that uses the syntax and semantic models from the code which is being compiled, just like it’s done with Roslyn Source Analyzers.
✏️ Note that for both options, external inputs (like a text file or a resource file) must be present before/during compile time, because that’s when the generators run.
Overview Source generators
Source generators run as a phase of compilation visualized below:
Example Source Generator
When building the same ExampleClass using Source Generators, follow these steps:
1️⃣ Create a library
✏️ Note that you need to define your Source Generators in a
.NET Standard 2.0 library. However this Source Generator library can be used without any issues in a older full framework like .NET 4.5 or even a older .NET Standard 1.0 project.
Also make sure to add the following NuGet references:
2️⃣ Add a new Source Generator
Add a new C# class which defines the Source Generator.
- The class must implement the
- The class must be annotated with the
[Generator]attribute used to specify the attached class is a source generator that generates the C# sources.
See example below:
When the same ExampleClass code is rebuild using Source Generators, the source generator class looks like this:
In the example above, the C# code is just created using a StringBuilder.
The last statement
context.AddSource(...) actually adds the C# source code as SourceText and generates a C# file with the name
3️⃣ Use the Source Generator project
Create a simple ConsoleApp project, and reference the ExampleClassCodeGenerator project.
When referencing the project, make sure to add the
ReferenceOutputAssembly="false" attributes like this:
When this is correctly defined, you can see that Source Generator project is used in your console app:
And when you double-click the
ExampleClass_Generated.cs, you can see the actual C# code which is generated.
Now you can use the ExampleClass just like this:
✏️ Note that sometimes when adding new functionality to the Source Generator and trying to use that newly generated code, you run into errors in Visual Studio.
The only solution is to restart Visual Studio to make sure that the cache is flushed. So if anyone knows a permanent solution/fix for this, send me a DM via Twitter: @sheyenrath.
To support debugging, you need to make sure that the Debugger is launched (
Debugger.Launch()) when the SourceGenerator is initialized.
Surround this statement with a
#if DEBUG -
#endif to make sure that the debugger is only launched when running in Debug mode.
For more detail on advanced debugging support, see this blog debug-source-generators-with-vs2019.
5️⃣ Using a Source Generator package in a library
When you use a Source Generator as a package reference in your library project, make sure that you define this NuGet package as a Private Asset (
<PrivateAssets>) and define the correct
<IncludeAssets>. This is needed to indicate that this dependency is purely used as a development harness and that you don’t want to expose that to projects that will consume your package.
See example from a library which uses the FluentBuilder as a development dependency:
▶️ A real project: AnyOf
To see how easy (or how difficult) is was to build a real project which is a Source Generator, I decided to start a small project.
Make a new type in C# which can be used to define two or more types as input for a method, much like this TypeScript functionality:
The solution for two different types would be something
AnyOf<TFirst, TSecond>, which can be used like:
Building such a type is straightforward, however this would only support two generic types.
Solution (with Source Generation)
So that’s where Source Generators can be used to generate as much as types as you want. In my case I limited it to 10, which resulted into these generated AnyOf-types:
AnyOf<TFirst, TSecond, TThird>
AnyOf<TFirst, TSecond, TThird, TFourth>
- … and so on …
Which makes it possible to use:
▶️ A real project: FluentBuilder
Based on an interesting blogpost from Tom Phan : “auto-generate-builders-using-source-generator-in-net-5” I created a NuGet package which can be used to automatically generate a FluentBuilder for a class or DTO.
This Source Generator is not just creating new FluentBuilders with ‘inline’ code using a StringBuilder (as is done in
AnyOf), but it’s actually analyzing the sources using a
ISyntaxReceiver to analyse which classes are annotated with the
[AutoGenerateBuilder] attribute, which defines that a class is suitable for auto-generating a FluentBuilder.
Annotate a DTO with
[FluentBuilder.AutoGenerateBuilder] to indicate that a FluentBuilder should be generated for this class:
Using the FluentBuilder
Using the Fluent builder is straightforward, see the excerpt below:
▶️ A real project: ProxyInterfaceGenerator
While building the FluentBuilder I wanted to be able to mock and unit-test the code which generates the files, but there was a dependency on the
GeneratorExecutionContext class which is sealed class does not have an interface and therefore cannot be mocked.
So I decided to investigate if it was possible to build a Source Generator which could generate a Proxy class + Interface from another class.
This was possible and the initial project can be found here.
Given: an external existing class which does not implement an interface
Create a partial interface
And annotate this with
ProxyInterfaceGenerator.Proxy[...] and with the Type which needs to be wrapped / proxied:
When the code is compiled, this source generator creates the following
1️⃣ An additional partial interface
Which defines the same properties and methods as in the external class.
2️⃣ A Proxy class
Which takes the external class in the constructor and wraps all properties and methods.
See list below for some personal takeaways:
➕ This new ‘framework’ to build Source Generators is more powerful when compared to T4 and the order in which the Source Generator runs in the build process is more integrated. Another plus is that you can easily generate multiple source files, which is not possible using T4.
➕ Another thing to keep in mind is that analyzing existing source code files is done using Roslyn, which is released some time ago and had proven itself, so if you are familiar with Roslyn, then writing a Source Generator should be comparable.
➖ I did not yet have the time to investigate if there are any Unit testing frameworks or utilities which make unit testing easy. So for some of my Source Generator projects, I added some minimal unit tests to verify if the generated C# source files were conform the specification.
➖ Quickly hitting F5 to debug a Source Generator is not supported, so you need to use the
#if DEBUG -
#endif construction to launch the debugger.