Source Generators

Source Generators are part of Roslyn feature that automatically generates code during the compilation process.

Why Source Generators

  • Alternatıve to Reflection

    • It is compile time not runtime

    • Unblock AOT trimming

    • It is just code

Demo

The code produces the below output.

static void Main(string[] args)
{
    var classTypes = Assembly.GetEntryAssembly()
        .GetTypes()
        .Where(c => c.IsClass && c.IsPublic)
        .Select(c => c.FullName);

    foreach (var classType in classTypes)
    {
        Console.WriteLine(classType);
    }
}

PublishAot

If you have enabled PublishAot, it will publish to machine code and remove all unused references. Since our classes (Student, Course) are not reference, they will no be included in the published code and the output will be empty.

Creating Source Generator

To use Source Generator, the project must target netstandard2.0. So, create a new class library with netstandard2.0.

Add reference for the below packages.

Microsoft.CodeAnalysis.Analyzers
Microsoft.CodeAnalysis.CSharp

Add the reference as below

    <ProjectReference Include="..\Generator\Generator.csproj" OutputItemType="Analyzer" />

Make the changes in the Generator class.

namespace Generator;

[Generator]
public class TheGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var provider = context.SyntaxProvider.CreateSyntaxProvider(
            predicate: static(node, _) => node is ClassDeclarationSyntax,
            transform: static (ctx, _) => (ClassDeclarationSyntax)ctx.Node
            ).Where(c => c is not null);

        var compilation = context.CompilationProvider.Combine(provider.Collect());

        context.RegisterSourceOutput(compilation, Execute);
    }

    private void Execute(SourceProductionContext context, (Compilation Left, ImmutableArray<ClassDeclarationSyntax> Right) tuple)
    {
        var (compilation, list) = tuple;

        var namelist = new List<string>();

        foreach (var systax in list) {
            var symbol = compilation.GetSemanticModel(systax.SyntaxTree)
                .GetDeclaredSymbol(systax) as INamedTypeSymbol;

            namelist.Add($"\"{symbol.ToDisplayString()}\"");
        }

        var names = string.Join(",\n ", namelist);

        var theCode = $$"""
            namespace  Generator;

            public static class ClassNames {
                public static List<string> Names = new(){
                    {{ names }}
                };
            }
            """;

        context.AddSource("YourClassList.g.cs", theCode);
    }
}

Reference

https://www.youtube.com/watch?v=Yf8t7GqA6zA&list=PLdo4fOcmZ0oULyHSPBx-tQzePOYlhvrAU&index=11

https://github.com/ibrahimuludag/sourcegenerator