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?
- Um sich wiederholenden, manuell schwer zu schreibenden Code automatisch zu generieren
- Um in performancekritischen Bereichen Reflection durch eine Compile-Time-Lösung zu ersetzen
- In ORM-, Serializer-, Mapper-, Proxy-, Event- und Attribut-basierten Systemen
- Wenn typsicherer Code generiert werden muss, z. B. für JSON oder API-Clients
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
- Erstellen Sie ein neues Class Library (.NET Standard)-Projekt.
- 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
| Merkmal | Source Generator | Reflection |
|---|---|---|
| Ausführungszeitpunkt | Kompilierzeit (Compile-time) | Laufzeit (Runtime) |
| Leistung | Hoch (generiert Code im Voraus) | Niedriger (Analyse zur Laufzeit) |
| Einsatzbereich | ORM, Serialisierung, API-Client | Plugins, Tests, dynamische Typen |
| Code-Sichtbarkeit | Generierte Dateien sind im Projekt sichtbar | Nur zur Laufzeit wirksam |
Leistung und bewährte Praktiken
- Generatoren arbeiten read-only; sie können keine vorhandenen Dateien ändern, sondern nur neuen Code hinzufügen.
- Generierter Code erscheint mit der Endung
.g.csunter „Analyzers → Generated Files“. - Vermeiden Sie aufwändige Operationen (z. B. Datei-I/O) innerhalb des Generators.
- Erzeugen Sie keinen unnötigen Code, um die Kompilierungszeit nicht zu verlängern.
- Erstellen Sie Quelltexte im UTF-8-Format und fügen Sie sie mit
context.AddSource()hinzu.
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
ISourceGeneratoroderIIncrementalGeneratorimplementiert. - 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
Codeanalyse mit der Roslyn Compiler API in C#
Lernen Sie Codeanalyse in C# mit der Roslyn Compiler API, einschließlich Syntaxbäumen und Codegenerierung.
Generische Strukturen in C# (List<T>, Dictionary<TKey,TValue>)
Lernen Sie Generics in C# (List<T>, Dictionary<TKey,TValue>), um typsicheren, wiederverwendbaren Code zu schreiben.
Leistungsoptimierung mit Span<T> und Memory<T> in C#
Lernen Sie Performanceoptimierung in C# mit Span<T> und Memory<T> für effiziente Speicherverwaltung.
Reflection und Late Binding in C#
Lernen Sie Reflection und Late Binding in C#, um Typen zur Laufzeit zu analysieren und dynamische Systeme zu erstellen.
SOLID-Prinzipien mit C#
SOLID-Prinzipien mit C#-Beispielen: flexiblen, wartbaren und testbaren Code erstellen.