.NET.Net CoreC#

C# Source Generators – a very useful new feature of C# compiler

csharp-roslyn-logo

A C# Source Generators, as Microsoft defines it, is “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.”

Think of C# Source generator as your colleague who writes C# code at compile time from some meta-data or from existing type inspection or some external information source (e.g. text files, config files, resource files, etc..).

Source generators run as a phase of compilation as visualized below (source: Microsoft):

cs-source-generators-flow

C# Source Generator can generate new source code at compile time, but it can not change existing one. This means that you can not change the body of the method or change other structures from existing classes.

Before digging into code, let’s take at few examples where this awesome feature can be very useful.

Example #1: Getting assembly info – Reflection vs. C# Source Generation version

Reflection is very popular and useful mechanism to retrieve application type system metadata at runtime. On the other hand, Reflection is generally very slow. And, if possible, should be avoided – at least on snappy, fast applications.

Yet, in almost every C# application you can find this code:

AssemblyInfo class is using Reflection to retrieve meta information of the executing assembly.

All this info is also available at compile time. Moreover, all this data can be collected at compile time and stored/persisted in some collection or similar. This way, performance could be enhanced quite a bit.

This is exactly how ThisAssembly works. At compile time it uses C# Source Generator to collect metadata of the assembly, store it for later – runtime usage. Runtime only gets this data from “static cached in-code storage”, there is no performance degradation as with Reflection.

Example #2: Compile Time Dependency Injection For C#

Generally, Dependency Injection heavily rely on the Reflection. It resolves registered types at runtime, creates new instances and inject them into new objects via constructors or properties. As said, all this is done by Reflection, so performance hit is normally very huge. Therefore, many existing DI containers/injectors suffers for performance issues.

On the contrary, Strong Type Inject does compile time Dependency Injection for C# by using C# Source Generators. There is no type Container dictionary lookups, no Reflection, no runtime type resolving. All is done at compile time. Application speed is greatly improved. Furthermore, type checking is done at compile, which is far safer then runtime type checking.

Here I presented only two cases how C# ecosystem can benefit from C# Source Generators. This feature is new in Roslyn compiler, let’s wait and see how developers will accept and use it.

Let’s do some coding

To start experimenting with C# Source Generators, I created solution with two projects in Visual Studio (16.8+):

  1. NETStandard 2.0 class library which will contain C# Code generators and
  2. Consuming library (in my case .NET 5 Console app ) which will consume source code generated by C# generators in my class library.

C# Source Generator class library

First, to create C# Source Generators, I used Visual Studio template for creating new .NETStandard 2.0 class library. Next, I marked my generator class with Generator attribute. Moreover, my class must implement ISourceGenerator interface. And…that’s all. My C# Source Generator is as follows:

It’s straightforward cognitive flow :): this class is C# Source Generator which will generate C# source code at compile time, inject it into my executing application by C# compiler. It will be compiled into intermediate language (IL) and executed by Common Language Runtime.

All this functionality is contained in two NuGet packages, which I reference in my project:

To make things even clearer, my project now looks like this:

My C# Source Generator is ready. Let’s check how can I use it.

Consuming app, .NET 5 Console

For my purpose, I created very simple .NET 5 Console application.

Obviously, I don’t have CompileTimeGenerated class in my project. Here is the magic, this class will be injected and compiled via my NETStandard class library containing my source generator.

There is one additional thing to do: when referencing project with C# Source Generators I need to put into ProjectReference MSBuild/csproj element two additional attributes OutputItemType="Analyzer" and ReferenceOutputAssembly="false". E.g.:

First attribute instructs compiler that referencing project is source generator (basically Code Analyzer). Second attribute instructs C# compiler not to include compiled dll into output – this is due a fact that source generator inject code into consuming assembly. Thus, there is no need for this assembly to go to application output . Later on, I will show disassembled code and all this will be a little more clearer.

Anyway, I use Visual Studio as my IDE. In Visual Studio I can see my project with source generator also in the Analyzers section, as shown below.

visual-studio-cs-source-generators

Ok, everything is on place, I compile my solution and run my app. Here is the output:

app-output-source-generators1

For you, dear reader, below I attach runtime screenshot of my app running on some 3rd Windows machine. You can observe interesting DateTime and HostName code magic/execution.

Interesting output, right?

app-output-source-generators2

As I promised before, this is my disassembled code:

dissassembly-source-generators

Disassembler shows how nicely my code was emitted into my consuming application by my C# Source Generator. Super awesome!

Read file and generate C# code

As an extra, in the next section, I will present C# Source Generator which will read content of the text file and generate source code based on the content of that file.

In my Console application, I will add new text file named “Person.txt” with the following content:

Very important: in Solution Explorer I set build action for this file to “C# analyzer additional file”.

And now, the most important part: my C# Source Generated class which will read text file and dynamically – at compile time – generate C# source code and send it to compilation.

Consuming of this is class is simple:

Output of my application after adding my second source generator:

app-output-source-generators3

Below you can see disassembled code. C# code is generated and executed – no runtime reading of the file or anything.

dissassembly-source-generators2

That’s it. I presented two simple C# Source Generators. These two generators are quite simple, but they are good examples to start with and to understand C# Source Generators.

Summary

In my opinion, C# Source Generators is one of the best features introduced into Roslyn C# compiler (as of 16.8 Preview 3).

Nevertheless, it can not change existing code, but with some tricks (e.g. partial methods), emitting generated code at compile time can get very powerful. It can greatly enrich your application building at compile time.

Don’t wait, try C# Source Generators. It’s really super awesome feature of the latest C# compiler.

Source code

Happy coding.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.