Introducing C# Source Generators
We’re pleased to introduce the first preview of 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. This is done via a new kind of component that we’re calling a Source Generator.
To get started with Source Generators, you’ll need to install the latest .NET 5 preview and the latest Visual Studio preview. Note: to build a source generator, you currently require Visual Studio. This will change in the next .NET 5 preview.
What is a Source Generator?
Unless you’ve been closely following every prototype and proposal related to the C# language and compiler, then there’s a good chance you’re asking, “What is a Source Generator” right now. A Source Generator is a piece of code that runs during compilation and can inspect your program to produce additional files that are compiled together with the rest of your code.
A Source Generator is a new kind of component that C# developers can write that lets you do two major things:
- Retrieve a Compilation object that represents all user code that is being compiled. This object can be inspected and you can write code that works with the syntax and semantic models for the code being compiled, just like with analyzers today.
- Generate C# source files that can be added to a Compilation object during the course of compilation. In other words, you can provide additional source code as input to a compilation while the code is being compiled.
When combined, these two things are what make Source Generators so useful. You can inspect user code with all of the rich metadata that the compiler builds up during compilation, then emit C# code back into the same compilation that is based on the data you’ve analyzed! If you’re familiar with Roslyn Analyzers, you can think of Source Generators as analyzers that can emit C# source code.
Source generators run as a phase of compilation visualized below:
A Source Generator is a .NET Standard 2.0 assembly that is loaded by the compiler along with any analyzers. It is usable in environments where .NET Standard components can be loaded and run.
Now that you know what a Source Generator is, let’s go through some of the scenarios they can improve.
Example scenarios that can benefit from Source Generators
The most important aspect of a Source Generator isn’t what it is, but what it can enable.
Today, there are three general approaches to inspecting user code and generating information or code based on that analysis used by technologies today: runtime reflection, IL weaving, and juggling MSBuild tasks. Source Generators can be an improvement over each approach.
Runtime reflection is a powerful technology that was added to .NET a long time ago. There are countless scenarios for using it. A very common scenario is to perform some analysis of user code when an app starts up and use that data to generate things.
For example, ASP.NET Core uses reflection when your web service first runs to discover constructs you’ve defined so that it can “wire up” things like controllers and razor pages. Although this enables you to write straightforward code with powerful abstractions, it comes with a performance penalty at runtime: when your web service or app first starts up, it cannot accept any requests until all the runtime reflection code that discovers information about your code is finished running! Although this performance penalty is not enormous, it is somewhat of a fixed cost that you cannot improve yourself in your own app.
With a Source Generator, the controller discovery phase of startup could instead happen at compile time by analyzing your source code and emitting the code it needs to “wire up” your app. This could result in some faster startup times, since an action happening at runtime today could get pushed into compile time.
Source Generators can improve performance in ways that aren’t limited to reflection at runtime to discover types as well. Some scenarios involve calling the MSBuild C# task (called CSC) multiple times so they can inspect data from a compilation. As you might imagine, calling the compiler more than once affects the total time it takes to build your app! We’re investigating how Source Generators can be used to obviate the need for juggling MSBuild tasks like this, since Source generators don’t just offer some performance benefits, but also allows tools to operate at the right level of abstraction.
Another capability Source Generators can offer is obviating the use of some “stringly-typed” APIs, such as how ASP.NET Core routing between controllers and razor pages work. With a Source Generator, routing can be strongly typed with the necessary strings being generated as a compile-time detail. This would reduce the amount of times a mistyped string literal leads to a request not hitting the correct controller.
As we flesh out the API and experience writing Source Generators more, we anticipate more scenarios to become evident. We’re also planning on working with partner teams to help them adopt Source Generators if it improves their core scenarios.
Source Generators and Ahead of Time (AOT) Compilation
Another characteristic of Source Generators is that they can help remove major barriers to linker-based and AOT (ahead-of-time) compilation optimizations. Many frameworks and libraries make heavy use of reflection or reflection-emit, such as System.Text.Json, System.Text.RegularExpressions, and frameworks like ASP.NET Core and WPF that discover and/or emit types from user code at runtime.
We’ve also identified that many of the top NuGet packages people make heavy use of reflection to discover types at runtime. Incorporating these packages is essential for most .NET apps, so the “linkability” and ability for your code to make use of AOT compiler optimizations is greatly affected. We’re looking forward to working with our wonderful OSS community to see how these packages could use source generators and improve the overall .NET ecosystem.
Hello World, Source Generator edition
All the previous examples of source generators mentioned earlier are pretty complex. Let’s go through a very basic one to show some of the key pieces you’ll need to write your own Source Generator.
The goal is to let users who have installed this Source Generator always have access to a friendly “Hello World” message and all syntax trees available during compilation. They could invoke it like this:
Over time, we’ll make getting started a lot easier in tools with templates. For now, here’s how to do it manually:
1. Create a .NET Standard library project that looks like this:
The key pieces of this is that the project can generate a NuGet package and it depends on the bits that enable Source Generators.
2. Modify or create a C# file that specifies your own Source Generator like so:
You’ll need to apply the Microsoft.CodeAnalysis.Generator attribute and implement the Microsoft.CodeAnalysis.ISourceGenerator interface.
3. Add generated source code to the compilation!
4. Add the source generator from a project as an analyzer and add preview to the LangVersion to the project file like this:
If you’ve written Roslyn Analyzers before, the local development experience should be similar.
When you write your code in Visual Studio, you’ll see that the Source Generator runs and the generated code is available to your project. You can now access it as if you had created it yourself:
Note: you will currently need to restart Visual Studio to see IntelliSense and get rid of errors with the early tooling experience
There are many more things you can do with Source Generators than just something simple like this:
- Automatically implement interfaces for classes with an attribute attached to them, such as INotifyPropertyChanged
- Generate settings files based on data inspected from a SourceGeneratorContext
- Serialize values from classes into JSON strings
The Source Generators Cookbook goes over some of these examples with some recommended approaches to solving them.
Additionally, we have a set of samples available on GitHub that you can try on your own.
As mentioned earlier, we’re working on making the experience authoring and using Source Generators better in tooling, such as adding templates, allowing for seamless IntelliSense and navigation, debugging, and improving responsiveness and performance in Visual Studio when generating source files.
Source Generators are in preview
As mentioned earlier in this post, this is the first preview of Source Generators. The intention of releasing this first preview is to let library authors try out the feature and give us feedback on what’s missing and what needs to change. From preview to preview, there may be changes in the API and characteristics of source generators. We intend on shipping Source Generators as GA with C# 9, and sometime later this year we intend on stabilizing the API and features it provides.
Calling all C# library developers: try it out!
If you own a .NET library written in C#, now is a great time to evaluate Source Generators and see if they’re a good fit. There’s a good chance that if your library makes heavy use of reflection, you’ll benefit in some way.
To help with that, we recommend reading the following docs:
- Source Generators design document, which explains the Source Generator API and current capabilities
- Source Generators cookbook, which provides examples of different Source Generators that enable different scenarios
Give us your feedback and let us know what you need! We’d love to learn more about how you think Source Generators could improve your code, and what you feel is missing or needs changing.
What’s next for Source Generators
This first preview is is exactly that: a first preview. There is a basic editing experience in Visual Studio, but it is not what we would consider “1.0 quality” right now. We may explore a few different designs over time before we commit to a particular one. One of the biggest areas of focus between now and the .NET 5 release will be improving the editing experience for Source Generators. Additionally, we expect to modify the API to accommodate feedback from partner teams and our OSS community.
Additionally, we’ll ensure a good experience for how Source Generators are distributed. We’re currently designing them to be very similar to Analyzers that can be shipped alongside a package. They currently use the Analyzer infrastructure to handle configuration in editor tooling.
Below is a list of questions we anticipate some people might have. We’ll update this list with more questions as they come.
How do Source Generators compare to other metaprogramming features like macros or compiler plugins?
Source Generators are a form of metaprogramming, so it’s natural to compare them to similar features in other languages like macros. The key difference is that Source Generators don’t allow you _rewrite_ user code. We view this limitation as a significant benefit, since it keeps user code predictable with respect to what it actually does at runtime. We recognize that rewriting user code is a very powerful feature, but we’re unlikely to enable Source Generators to do that.
How do Source Generators compare with Type Providers in F#?
If you’re an F# programmer (or familiar with the language), then you might have heard of Type Providers. Source Generators were inspired in part by Type Providers, but there are several differences that make them distinct. The main difference is that Type Providers are a part of the F# language proper and emit types, properties, and methods in-memory based on an external source. Source Generators are a compiler feature that analyzes C# source code, optionally with other files, emits C# source code to include back into a compilation.
Should I delete all my reflection code?
No! Reflection is an incredibly useful tool to use. Reflection does present some performance and “linkability” challenges that can be solvable with Source Generators in some scenarios. We recommend carefully evaluating if Source Generators fit your scenario.
How are Source Generators this different from analyzers?
Source Generators are similar to analyzers, since both are compiler features that let you plug into a compilation. The key difference is that analyzers ultimately emit diagnostics that can be used to associate with a code fix. Source Generators ultimately emit C# source code that is added to a compilation. There are several other differences discussed in the design document.
Can I modify/rewrite existing code with a Source Generator?
No. As mentioned earlier, Source Generators do not allow you to rewrite user source code. We do not intend on allowing them to this. They can only augment a compilation by adding C# source files to it.
When will Source Generators be out of preview?
We intend on shipping Source Generators with C# 9. However, in the event that they aren’t ready in time, we’ll keep them in preview and ensure that users need to opt in to use them.
Can I change the TFM in a Source Generator?
Technically, yes. Source Generators are .NET Standard 2.0 components, and like any project you can change the TFM. However, they only support being loaded into consuming projects as .NET Standard 2.0 components today.
Will Source Generators come to Visual Basic or F#?
Source Generators are currently a C# only feature. Because this is the first preview, there are many things that can change between now and the released version. We do not intend on adding Source Generators to Visual Basic at this time. If you’re an F# developer and want to see this feature added, please search the suggestions or file a new one in the F# language suggestion repository.
Do Source Generators introduce compatibility concerns for libraries?
This depends on how libraries are authored. Since VB and F# currently don’t support Source Generators, library authors should avoid designing their features such that they require a Source Generator. Ideally, features have fallbacks to runtime reflection and/or reflection emit. This is something that library authors will need to careful consider before adopting Source Generators. We expect most library authors will use Source Generators to augment – rather than replace – current experiences for C# developers.
Why do I not get IntelliSense for generated code? Why does Visual Studio say there’s an error even though it builds?
You will need to restart Visual Studio after building the source generator to make errors go away and IntelliSense appear for generated source code. After you do that, things will work. Currently, Visual Studio integration is very early on. This current behavior will change in the future so that you don’t need to restart Visual Studio.
Can I debug or navigate to generated source in Visual Studio?
Eventually, we’ll support navigation and debugging of generated source in Visual Studio. It is not yet supported in this early preview stage.
How do I ship my own Source Generator?
Source Generators can be shipped as NuGet packages, just like Analyzers today. In fact, they use the same “plumbing” as Analyzers. If you’ve ever shipped an Analyzer, then you can easily ship a Source Generator.
Will there be Microsoft-authored Source Generators?
Eventually, yes. But this is still the first preview of the technology, and a lot of things may need to change to accommodate various scenarios. There is currently no timeline for when Microsoft-authored Source Generators are available.
Why do I need to use the Preview LangVersion to consume a Source Generator?
Although Source Generators are not technically a C# language feature, they are in preview. Rather than introduce a new setting just for Source Generators, we decided it would be easier to just use the existing switch that enables preview language features for the C# compiler.
Cheers, and happy source generation!
Congratulations to the team for this amassing feature! This is the missing piece of Analyzers! This can be used event to improve queries and linq too
Can I use it for aop, method interception, aspnetcore is missing a method interceptor.
I can feel C# at the edge of the AOP. But it still resisting to not to do. Maybe with the C#10?
This is a very promising feature. I’ve been maintaining a code generator for years. We switched over to .Net Core and being a DotNetCliTool, then over to a local tool, and the amount of trial and error and refinement to reasonably well plug into building, cleaning, rebuilding, incremental building, etc. was very high. It will be great if this made all that MSBuild file work and experimentation a thing of the past.
Glad you’re liking the look of it! A goal of Source Generators is absolutely to obviate the need for messing about with MSBuild abstractions.
Reminds me very much of supercharged string mixins from D, which I’ve been wishing for in C# forever. Can’t wait to try it out!
Can I reference an analyzer by project reference instead of path reference?
Currently you have to use the path to the actual assembly, but we fully expect to support project references with the final tooling support.
In addition to what Chris said, we may also look into making referencing Analyzers easier in general. Currently they do require passing the full path, which is definitely not ideal.
Sorry that i reply to you here instead under your post on my suggestion but blog somehow removed reply button under your post there on mobile
What do you think about allowing insertions of pure method calls into existing code if this concept ever makes entrance to .net world? If im not mistaken pure means no side effects and as such they wouldnt hugely affect behaviour of already written code
This of course greatly limits what you can do but it would at least enable some scenarios involving console output (simple autoprofiling for example maybe more but i didnt think about it very thoroughly)
This suggestion is direct response to what you have wrote:
Its a shame it wont be allowed but i can somewhat understand the unwillingness to mess with problems that can pop when auto modifying existing code and behaviour
I see someone making the code for this almost immediately. I think over the years of .NET this has been one of the most searched features.
“Automatically implement interfaces for classes with an attribute attached to them, such as INotifyPropertyChanged”
We have an early sample of an INotifyPropertyChanged generator in the roslyn-sdk here: AutoNotifyGenerator.cs
@Phillip, thx for detailed article.
can you compare the Source Generator with T4 Templates?
I have the same question. I think the answer is that with source generators you can modify/rewrite existing code and with T4 templates you can’t.
Conceptually T4 and source generators are similar; however source generators run inside the compiler pipeline, which means you can do things like introspecting the users code to make decisions about what to emit.
Because they are part of the compiler pipeline, source generators are supported everywhere the compiler is, including inside Visual Studio, command line builds and CI. T4 on the other hand has limited support outside of Visual Studio, and must be manually run before building.
Like T4 source generators do *not* allow for re-writing user code. Source generators are strictly additional source only.
T4 files also allow generation of source code other than C#. You could, for instance, generate a Typescript file based on your C# classes. However, T4 is a Visual Studio technology and therefore dependent on Windows. Also, usually in T4 you walk the Visual Studio CodeDOM – though you could of course load Roslyn in-process. By default, T4 files don’t run in the build process.
Interesting. Are there limitations / sandboxing in Source Generators regarding reading or writing external files? Like, generating C# code based on a JSON file, or generating a .ts file based on C# code? These are my go-to T4 scenarios.
As I see in the example it’s definitely possible
“source generators you can modify/rewrite existing code” Are you sure? so far all i know is that source generator only can add code. Could you give a example. thank you
That’s really awesome. Nevertheless I would really appreciate if code rewrites would be possible although I see your potential risks.
I also don’t see how source generators can add for example INotifyPropertyChanged implementations if you cannot rewrite your code.
As an example I was assuming that in this case I add a [NotifyChanged] attribute the behavior is weaved into the object that has the attribute,
but if you cannot rewrite the code, how would this be possible?
That’s exactly what i was thinking, without replacing the get and set methods of a property how can you auto implement INotifyPropertyChanged?
you might be able to only define the backing field, decorate it with an Attribute, and the Property is auto-generated.
Yes, our current sample requires the ViewModels to define the backing field, and the generator creates the whole property: you can see an example here: UseAutoNotifyGenerator.cs
After looking at the example, it’s all making a lot more sense to me now.
So source generators are essentially just producing new source files on the fly that contain either static or partial classes. In the case of the INotifyPropertyChanged example, it just looks for attributes on the fields of the class the user wrote and then it generates a new partial class with the properties.
It’s good to see beautiful things on .NET
I have a question, is this feature similar to the T4 (text template) feature?
I mean, can this feature do what the T4 does?
See here for a comparison: #comment-5589
micronaut kind of like behaviour. i think this would definitely helps us with performance. Thanks but can i get around with upgrading to latest .net 5 preview?
The tooling and compiler support are currently in preview, and require .NET 5 and the latest visual studio. We expect generators to be able to target lower level runtimes and language versions, but ultimately shipped compilers and tools can’t be retroactively made to understand generators.
after uninstalling .NET 5.0, i still can build without any troubles both generators and code generated..
are you sure that the .NET 5.0 SDK is a prerequiste for this feature ?