Zweitägiger Workshop zur Roslyn-basierten Compile-Time-Code-Generierung: inkrementelle Source Generators und Analyzers erstellen, Generator-Unit-Tests schreiben, externe Daten zur Compile-Zeit einlesen und C#-Interceptors implementieren — zur Eliminierung von Boilerplate und Verbesserung der Anwendungsperformance.
.NET Source Generators
Dauer: 2 Tage
Zielgruppe
.NET-Entwickler, die Compile-Time-Code-Generierung einsetzen möchten, um Boilerplate zu eliminieren, Muster durchzusetzen und die Anwendungsperformance zu verbessern. Der Workshop richtet sich an Entwickler, die Frameworks, Bibliotheken oder umfangreiche Anwendungen erstellen, bei denen wiederkehrende Code-Muster eine Wartungslast darstellen.
Voraussetzungen
- Fundierte Kenntnisse in C# und .NET (C# 10+ empfohlen)
- Grundkenntnisse der Roslyn Compiler API sind hilfreich, aber nicht erforderlich
- Visual Studio 2022/2026 oder Rider mit installiertem .NET 10 SDK
Lernziele
Nach Abschluss dieses Workshops werden die Teilnehmer in der Lage sein:
- Zu verstehen, was Source Generators sind, wie sie in die Roslyn-Kompilierungspipeline integriert sind und wann sie gegenüber Alternativen wie Reflection oder T4-Templates vorzuziehen sind
- Vorhandene Source Generators aus NuGet-Paketen zu konsumieren und in einem Projekt zu konfigurieren
- Inkrementelle Source Generators mit der
IIncrementalGenerator-API von Grund auf zu erstellen - Unit Tests für Source Generators mit den
Microsoft.CodeAnalysis.Testing-Bibliotheken zu schreiben - Externe Daten (XML, JSON) zur Compile-Zeit zu lesen, um die Code-Generierung zu steuern
- C# Interceptors zu implementieren, um Methodenaufrufe zur Compile-Zeit zu ersetzen, ohne vorhandenen Quellcode zu ändern
- Performance-Best-Practices anzuwenden: inkrementelle Pipelines, Caching, Diagnose-Reporting und Vermeidung unnötiger Neugenerierung
Kursübersicht
Tag 1 – Vormittag: Source Generators verstehen und verwenden
1. Die Roslyn-Kompilierungspipeline
- Funktionsweise des C#-Compilers: Syntax Trees, Semantic Models und Symbole
- Einordnung von Source Generators in die Kompilierungspipeline
- Source Generators vs. Reflection, T4-Templates und Fody/IL-Weaving: Abwägungen und Anwendungsfälle
- Anatomie einer generierten Datei: Was wird wann emittiert und wie ist es für den Rest des Projekts sichtbar?
2. Source Generators konsumieren
- Generator-Pakete aus NuGet einbinden (z.B.
System.Text.Json,Microsoft.Extensions.Logging,AutoMapper,Refit) - Analyzer- und Generator-Ausgaben in der IDE: Generierte Dateien in Visual Studio und Rider anzeigen
<EmitCompilerGeneratedFiles>und<CompilerGeneratedFilesOutputPath>: Generierte Ausgaben zur Inspektion persistieren- Generatoren über
[assembly: ...]-Attribute undAdditionalFileskonfigurieren - Generierten Code debuggen: Breakpoints in generierten Dateien, einzelne Generatoren deaktivieren
3. Den ersten inkrementellen Source Generator schreiben
- Projekt-Setup:
netstandard2.0-Target, NuGet-Referenz aufMicrosoft.CodeAnalysis.CSharp,IsRoslynComponent-Property IIncrementalGeneratorimplementieren und mit[Generator]registrieren- Der
IncrementalGeneratorInitializationContext:SyntaxProvider,CompilationProvider,AdditionalTextsProvider - Syntax-Knoten mit
CreateSyntaxProviderund Prädikat/Transformations-Paaren filtern - Quellcode mit
context.RegisterSourceOutputemittieren - Einen einfachen
ToString-Helper aus Klassen-Attributen generieren
Lab: Einen Generator erstellen, der Klassen mit dem Attribut [GenerateDto] liest und ein entsprechendes DTO-Record mit gemappten Properties emittiert.
Tag 1 – Nachmittag: Source Generators testen
4. Unit-Test-Strategie
- Warum Source Generator Tests sich von regulären Unit Tests unterscheiden: kein Runtime, Compiler als Test-Host
- Die NuGet-Pakete
Microsoft.CodeAnalysis.CSharp.TestingundMicrosoft.CodeAnalysis.Testing CSharpSourceGeneratorTest<TGenerator, TVerifier>: Anatomie eines Generator-Tests- Eingabe-Quellcode bereitstellen, erwartete generierte Ausgabe angeben und Diagnosen prüfen
- Testen, dass bei nicht passenden Eingaben keine Ausgabe emittiert wird
- Snapshot-Testing mit
Verify.SourceGeneratorsals alternativer Ansatz
5. Diagnosen und Fehlerfälle testen
- Diagnosen aus einem Generator melden:
DiagnosticDescriptor, Schweregrade, Positionen - Testen, dass der Generator bei ungültiger Eingabe die korrekte Warnung oder den richtigen Fehler emittiert
- Inkrementelles Verhalten testen: Sicherstellen, dass unveränderte Eingaben keine Neugenerierung auslösen
- Integrationstests: Ein Beispiel-Consumer-Projekt bauen und erfolgreiche Kompilierung prüfen
6. Testgetriebene Generator-Entwicklung
- Tests vor der Implementierung der Generator-Logik schreiben
- Generator-Ausgaben durch Aktualisieren erwarteter Snapshots iterativ verbessern
- CI/CD-Setup: Generator-Tests in einer CI-Pipeline ohne spezielle Werkzeuge ausführen
Lab: Eine vollständige Test-Suite für den Generator vom Vormittag schreiben: Happy Path, fehlendes Attribut, ungültiger Typ und Tests zur Diagnose-Emission.
Tag 2 – Vormittag: Erweiterte Code-Generierung
7. Code aus externen Dateien generieren (XML und JSON)
AdditionalFilesin MSBuild:.xml- und.json-Dateien als Generator-Eingaben hinzufügenAdditionalTextsProvider: Dateiinhalte und Pfade zur Compile-Zeit abrufen- XML innerhalb des Generators mit
System.Xml.Linqparsen - JSON mit
System.Text.Json(source-generation–sicherer Teilbereich) oderNewtonsoft.Jsonparsen AdditionalTextsProvidermitCompilationProviderfür typbewusste Generierung kombinieren- Externe Datei-Reads cachen:
WithTrackingName,Collectund Vermeidung von Neuparsen bei unabhängigen Änderungen - Stark typisierte Konfigurationsklassen, API-Client-Stubs oder Enum-Definitionen aus Schema-Dateien generieren
8. Attribute und Semantic-Model-Daten lesen
- Das Semantic Model traversieren:
INamedTypeSymbol,IMethodSymbol,IPropertySymbol - Attribut-Konstruktor-Argumente und benannte Parameter extrahieren
- Partielle Klassen und die Anforderung des
partial-Schlüsselworts behandeln - Extension Methods, Interfaces und verschachtelte Typen aus Semantic-Daten generieren
- Generics, Nullable Reference Types und Zugriffsmodifikatoren korrekt behandeln
Lab: Einen Generator erstellen, der eine api-schema.xml-AdditionalFile liest und eine stark typisierte C#-Client-Klasse mit je einer Methode pro definiertem Endpunkt emittiert, inklusive XML-Dokumentationskommentare.
Tag 2 – Nachmittag: Interceptors und Performance-Optimierung
9. C# Interceptors
- Was Interceptors sind: ein Compiler-Feature, das einen bestimmten Aufruf zur Compile-Zeit auf eine andere Methode umleitet
- Interceptors aktivieren:
<Features>interceptors</Features>in der Projektdatei; das[InterceptsLocation]-Attribut [InterceptsLocation]aus einem Source Generator generieren: Dateipfad, Zeile und Spalte ausSyntaxReferenceermitteln- Anwendungsfälle: Logging-Aufrufe durch Zero-Allocation-Structured-Logging ersetzen, Reflection-basierte Serialisierung durch generierten Code ablösen, Konfigurations-Reads inlinen
- Einschränkungen und Vorbehalte: ein Interceptor pro Call Site, kein Abfangen von virtuellem Dispatch, Stabilität bei Code-Änderungen
- Interceptors mit inkrementellen Generatoren für eine vollautomatisierte Optimierungs-Pipeline kombinieren
Lab: Einen Generator schreiben, der alle Aufrufe einer mit [SlowPath] annotierten Methode abfängt und auf eine generierte Fast-Path-Implementierung umleitet, die auf Reflection verzichtet.
10. Source Generators für Build-Performance optimieren
- Das inkrementelle Generator-Modell: Warum
IIncrementalGeneratorISourceGeneratorabgelöst hat - Die Pipeline für maximales Caching strukturieren: unveränderliche Value Objects,
IEquatable<T>auf Pipeline-Eingaben,EquatableArray<T>-Wrapper WithTrackingNamefür Build-Performance-Diagnosen mit RoslinsGeneratorDriverRunResult- Aufwändige Arbeit im Transform-Schritt vermeiden: Semantic-Lookups in eine separate Pipeline-Stufe auslagern
- Generator-Ausführungszeit profilieren: BenchmarkDotNet, Build Binary Logs (
-bl) und MSBuild Structured Log Viewer - Häufige Fehler:
Compilationdirekt erfassen, mutablen Zustand verwenden,IEquatable<T>auf Modell-Typen vergessen - Parallele und Multi-Output-Generatoren:
RegisterImplementationSourceOutputvs.RegisterSourceOutput
11. Diagnosen, Versionierung und Packaging
- Einen Generator als NuGet-Paket ausliefern:
buildTransitive-Assets, Ordner-Layoutanalyzers/dotnet/cs - Generatoren unabhängig von der ergänzten Bibliothek versionieren
- Spezifische Generator-Warnungen pro Projekt unterdrücken
- Multi-Targeting: Generator auf
netstandard2.0ausliefern, während die Laufzeitbibliothek aufnet10.0zielt - Einen verpackten Generator debuggen:
IsRoslynComponent, Source Link und Symbol-Pakete
Lab: Den Generator vom ersten Tag in eine NuGet-fähige Struktur umbauen, Build-Performance-Tracking hinzufügen, inkrementelles Caching in einer Multi-Projekt-Solution verifizieren und die Regenerierungskosten mit und ohne IEquatable<T> auf dem Pipeline-Modell messen.
Praktische Übungen
- Die Ausgabe der Source Generators
System.Text.JsonundMicrosoft.Extensions.Loggingin einem bestehenden Projekt konsumieren und inspizieren. - Einen inkrementellen
[GenerateDto]-Generator erstellen, der DTO-Records aus annotierten Domain-Klassen emittiert. - Eine vollständige Unit-Test-Suite mit
Microsoft.CodeAnalysis.CSharp.Testingschreiben: Happy Path, Diagnosen und No-Output-Fälle. - Einen Generator erstellen, der eine
api-schema.xml-AdditionalFile parst und eine stark typisierte HTTP-Client-Klasse mit XML-Dokumentationskommentaren emittiert. - Einen C#-Interceptor-Generator implementieren, der Reflection-basierte Methodenaufrufe durch generierte, Zero-Allocation-Äquivalente ersetzt.
- Eine inkrementelle Generator-Pipeline profilieren und optimieren:
IEquatable<T>zu Modell-Typen hinzufügen, Caching-Verhalten verifizieren und Build-Zeit-Verbesserung messen. - Den fertigen Generator als NuGet-Paket mit korrektem
buildTransitive-Layout verpacken und aus einem separaten Consumer-Projekt testen.
Ergebnisse
- In der Lage, zu beurteilen, wann Source Generators das richtige Werkzeug sind, und die Abwägungen gegenüber Reflection, T4 und IL-Weaving zu erläutern
- Versiert in der Implementierung von
IIncrementalGeneratormit korrekt strukturierten, cache-freundlichen Pipelines - Kompetent im Testen von Generatoren mit
Microsoft.CodeAnalysis.CSharp.Testinginklusive Diagnose- und No-Output-Assertions - Fähig, Code-Generierung aus externen XML- und JSON-Schema-Dateien über
AdditionalFileszu steuern - Erfahren im Schreiben von C#-Interceptors, um Aufrufstellen zur Compile-Zeit transparent zu ersetzen
- In der Lage, Generator-Pipelines für Build-Performance mit
IEquatable<T>,WithTrackingNameund gestuften Transforms zu optimieren - Bereit, Generatoren als NuGet-Pakete mit korrektem Multi-Targeting und Asset-Layout auszuliefern
Erweiterte Lernpfade
- Roslyn-Analyzer und Code Fixes: Generatoren mit Diagnosen kombinieren, die Entwickler zu generierten APIs leiten
Scriban- undT4-artige Templates innerhalb von Generatoren für komplexe Multi-File-Ausgaben- Integration mit
Verify.SourceGeneratorsfür Golden-File-Snapshot-Tests - Source Generators in Blazor und MAUI einsetzen: Compile-Time-Routen-Generierung und plattformspezifischer Code