DEV Community

Cover image for The C# Source Generators revolution
Matteo Bortolazzo
Matteo Bortolazzo

Posted on

The C# Source Generators revolution

Along with .NET 5, Microsoft released C# Source Generators.

And after creating my first generator, I consider it one of the coolest features .NET developers can use.

But what are Source Generators?

They are C# programs that can analyze code, dynamically generate files and inject them into the build pipeline.

Source Generator Pipeline Diagram

Use cases?!

  • Generate C# classes from JSON
  • Extend a POCO class with INotifyPropertyChanged
  • Generate builders from POCO classes
  • Generate mapping extension methods to replace AutoMapper

Super basic example

Each generator has 2 methods: one to analyze the existing code and one to generate new code.

[Generator]
public class MyGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context) { }

    public void Execute(GeneratorExecutionContext context) { }
}
Enter fullscreen mode Exit fullscreen mode

To generate a C# class:

var source = @"
namespace Dev.To {{
    public class Post {{
        public string Title {{ get; set; }}
    }}
}}";
context.AddSource("Post.cs", SourceText.From(source, Encoding.UTF8));
Enter fullscreen mode Exit fullscreen mode

It might not seem much; however, think about what you can do with partial classes or extensions methods.

How I tried to use them

If you work with .NET Core you might be familiar with appsettings.json.
We often need to create POCO classes representing the JSON and then register them in Startup to be injected later with IOptions<>.

Why don't we let source generators do the heavy lifting for us?

So, I create a preview NuGet that does exactly that!

  • It reads appsettings content
  • For each object property it creates a new C# class
  • It creates a partial Startup class with a private RegisterOptions method that register the new classes with services.Configure<PropertyName>(Configuration.GetSection(nameof(PropertyName)));
  • The only thing people need to do is making their Startup partial and call RegisterOptions in ConfigureServices method.

Given this JSON:

{
  "MyOtherOptions": {
    "MyString": "any",
    "MyInt": 1,
    "MyDouble": 1.1,
    "MyBool": true,
    "MyObject": {
      "MyObjectString": "any"
    },
    "MyArray": [ "any" ]
  }
}
Enter fullscreen mode Exit fullscreen mode

It generates these classes:

Solution Tree Example

Generated content:

// MyOtherOptions.cs
public class MyOtherOptions
{
    public string MyString { get; set; }
    public int MyInt { get; set; }
    public double MyDouble { get; set; }
    public bool MyBool { get; set; }
    public MyObject MyObject { get; set; }
    public string[] MyArray { get; set; }
}

// Startup.Generated.cs
public partial class Startup
{
    private void RegisterOptions(IServiceCollection services)
    {
        services.Configure<MyOtherOptions>(Configuration.GetSection(nameof(MyOtherOptions)));
    }
}
Enter fullscreen mode Exit fullscreen mode

Final words

As you can see, there's a lot of potential behind this new feature.

It will not replace Reflection completely but it can drastically improve performance for a lot of libraries!

However, since it's in preview, there are limitations, and things might change, so keep that in mind!

The code for my example can be found here on GitHub.

Useful links

Discussion (0)