Lesson 16 of 40 Advanced C# Expert 60 min

Source Generators & Roslyn

In this lesson, you will learn how Roslyn source generators can produce code at compile time, reduce boilerplate, and support advanced tooling scenarios in modern .NET applications.

← Back to Visual Studio 2026 Tutorial Home

What you will learn

Why this matters: Source generators can reduce repetitive code, improve performance by shifting work to compile time, and power advanced framework features behind the scenes.

Part 1: Incremental source generators

An incremental source generator analyzes parts of a compilation and emits generated code in a more efficient way than older generation approaches. It is designed to avoid unnecessary repeated work.

[Generator] public class MyGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext ctx) { var pipeline = ctx.SyntaxProvider .CreateSyntaxProvider(Predicate, Transform); ctx.RegisterSourceOutput(pipeline, Emit); } }

This pattern helps generators scale better in larger solutions where compile-time performance matters.

Part 2: Debugging source generators

Debugging a source generator is different from debugging normal application code because the generator runs during compilation. Visual Studio still gives you ways to inspect and debug this process.

#if DEBUG System.Diagnostics.Debugger.Launch(); #endif

You can also attach to the compiler process to inspect what the generator is doing during build time.

Part 3: Real-world use cases

Source generators are useful when code can be derived from compile-time information instead of being written by hand or discovered with reflection at runtime.

Example: A framework can inspect attributes on your classes and generate support code automatically, saving you from writing repetitive plumbing code.

Part 4: Unit testing generators

Because generators produce code, testing them is important. A good generator test verifies that expected code is emitted under the right conditions.

var driver = CSharpGeneratorDriver.Create(new MyGenerator()); driver = driver.RunGenerators(compilation); var result = driver.GetRunResult(); Assert.Single(result.GeneratedTrees); // Verify generated code matches expected

This helps ensure that changes to your generator do not accidentally break downstream projects.

When to use source generators

Use caseBest used for
Compile-time code emissionReducing boilerplate generated from metadata or attributes
Framework helpersImproving performance by reducing runtime reflection
Strong typingGenerating APIs from known compile-time structures
Tooling supportPowering analyzers, diagnostics, or generated helpers

A practical source generator workflow

Step 1: Define the compile-time problem you want to solve
Step 2: Identify which syntax or symbols the generator should inspect
Step 3: Emit focused generated code clearly
Step 4: Debug the generator during compilation
Step 5: Add tests for generated output
Step 6: Keep the generator maintainable and predictable

Best practices

Summary

In this lesson, you learned how Roslyn source generators work, how incremental generation improves efficiency, how generators can be debugged, and how generated output can be tested.

In the next lesson, you will continue with advanced asynchronous programming concepts.