Wird geladen...

Das Konzept von Source Generators in C# (C# 9+)

Lernen Sie Source Generators in C#, um Code zur Compile-Zeit zu erzeugen und Performance zu verbessern.

Mit C# 9 wurden die Source Generators eingeführt – eine leistungsstarke Roslyn-Funktion, die zusätzlichen C#-Code zur Kompilierzeit erzeugt. Dieser Mechanismus erstellt neue Quelldateien während der Kompilierung und nicht zur Laufzeit. Dadurch wird wiederholender Code automatisch generiert, ohne Laufzeitkosten, was die Leistung erhöht und den Wartungsaufwand reduziert.


Was ist ein Source Generator?

Ein Source Generator ist eine Komponente, die dem Compiler hinzugefügt wird. Der Compiler (Roslyn) analysiert den Quellcode Ihres Projekts und fügt automatisch neue Klassen oder Methoden hinzu, wo sie benötigt werden. Der generierte Code wird während der Kompilierung in das Projekt integriert und kann wie normaler C#-Code verwendet werden.


// Zusammenfassung: Ein Source Generator ist ein Compiler-Plugin.
// Der Entwickler schreibt benutzerdefinierte Logik; der Generator erzeugt Code.
// Am Ende der Kompilierung wird der generierte Code automatisch dem Projekt hinzugefügt.

Wann wird er verwendet?


Ein einfaches Source-Generator-Beispiel

Im folgenden Beispiel sehen Sie einen minimalen Source Generator. Dieser Generator erzeugt während der Kompilierung eine neue Klasse HelloGenerated.


// HelloGenerator.csproj (sollte eine separate Klassenbibliothek sein)
// Paket: Microsoft.CodeAnalysis.CSharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;

[Generator]
public class HelloGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context)
    {
        // Wird beim Start der Analyse ausgeführt (z. B. Syntax-Listener)
    }

    public void Execute(GeneratorExecutionContext context)
    {
        string code = @"
namespace HelloGen
{
    public static class HelloGenerated
    {
        public static void SayHello() =>
            System.Console.WriteLine(""Hallo! Diese Klasse wurde zur Kompilierzeit generiert."");
    }
}";
        context.AddSource("HelloGenerated.g.cs", SourceText.From(code, Encoding.UTF8));
    }
}

// Verwendung im Hauptprojekt
using HelloGen;

class Program
{
    static void Main()
    {
        HelloGenerated.SayHello(); // Ruft den zur Kompilierzeit generierten Code auf
    }
}

Diese Quelldatei wird während der Kompilierung automatisch erstellt. In Visual Studio kann sie unter „Analyzers → HelloGenerator → HelloGenerated.g.cs“ angezeigt werden.


Struktur: Erstellen eines Generator-Projekts

  1. Erstellen Sie ein neues Class Library (.NET Standard)-Projekt.
  2. Fügen Sie der Projektdatei die folgenden Eigenschaften hinzu:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>latest</LangVersion>
    <IncludeBuildOutput>false</IncludeBuildOutput>
    <AnalyzerLanguage>C#</AnalyzerLanguage>
    <OutputItemType>Analyzer</OutputItemType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
  </ItemGroup>
</Project>

Mit dieser Konfiguration kann das Projekt als „Analyzer/Generator“ verwendet werden. Anschließend kann die DLL im Hauptprojekt als „Analyzer“ referenziert werden.


Attributbasierte Codegenerierung

In der Praxis werden Generatoren häufig über Attribute ausgelöst. Zum Beispiel kann das Hinzufügen des Attributs [AutoToString] zu einer Klasse während der Kompilierung automatisch eine ToString()-Methode generieren.


// Benutzer-Code
[AutoToString]
public partial class Person
{
    public string Name { get; set; }
    public int Alter { get; set; }
}

// Vom Generator erzeugter Code (zur Kompilierzeit)
public partial class Person
{
    public override string ToString() =>
        $""Person: Name={Name}, Alter={Alter}"";
}

Dieser Ansatz eliminiert Boilerplate-Code und erleichtert die Wartung.


Incremental Source Generator (C# 10+)

Mit C# 10 wurde der Incremental Generator eingeführt, der Leistungs- und inkrementelle Build-Unterstützung bietet. Der Code wird nur für geänderte Dateien neu generiert.


using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;

[Generator]
public class EinfacherIncrementalGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var klassen = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (syntaxNode, _) => syntaxNode is ClassDeclarationSyntax,
                transform: (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
            .Collect();

        context.RegisterSourceOutput(klassen, (spc, klassenListe) =>
        {
            foreach (var klasse in klassenListe)
            {
                string name = klasse.Identifier.Text;
                string code = $@"
namespace AutoGen
{{
    public static class {name}Info
    {{
        public static void Schreiben() =>
            System.Console.WriteLine(""Klasse: {name}"");
    }}
}}";
                spc.AddSource($"{name}Info.g.cs", SourceText.From(code, Encoding.UTF8));
            }
        });
    }
}

Mit diesem Modell verbessert sich die Kompilierleistung in großen Projekten erheblich. Generatoren laufen nun nur noch für die betroffenen Dateien.


Source Generator vs. Reflection

MerkmalSource GeneratorReflection
AusführungszeitpunktKompilierzeit (Compile-time)Laufzeit (Runtime)
LeistungHoch (generiert Code im Voraus)Niedriger (Analyse zur Laufzeit)
EinsatzbereichORM, Serialisierung, API-ClientPlugins, Tests, dynamische Typen
Code-SichtbarkeitGenerierte Dateien sind im Projekt sichtbarNur zur Laufzeit wirksam

Leistung und bewährte Praktiken


Beispiel: Automatische DTO-Generierung

Angenommen, Sie möchten DTO-Klassen aus Modellen erzeugen, die Datenbanktabellen entsprechen. Der folgende Source Generator erstellt für jedes Modell eine Klasse {ModelName}Dto.


// Modellklasse
[AutoDto]
public class Benutzer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

// Generierter Code
public class BenutzerDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Dadurch entfällt das manuelle Schreiben von DTOs, und Änderungen am Modell werden automatisch übernommen.


TL;DR

  • Source Generators sind Roslyn-basierte Tools, die Code während der Kompilierung erzeugen.
  • Sie haben keine Laufzeitkosten wie Reflection; sie sind effizienter in Bezug auf Leistung und Sicherheit.
  • Sie werden über die Schnittstellen ISourceGenerator oder IIncrementalGenerator implementiert.
  • Der generierte Code wird in den Build integriert und funktioniert wie normaler C#-Code.
  • Weit verbreitet für Serializer, Mapper, DTO-Generatoren und API-Client-Erzeugung.

Ähnliche Artikel