Skip to Content
Course content

96: Source Generators Overview

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you've spent any significant time building UIs in C#—whether it's WPF, MAUI, or Avalonia—you've probably encountered the soul-crushing repetition of INotifyPropertyChanged. You create a private field, a public property, and then you write that same OnPropertyChanged("PropertyName") call over and over. It's tedious, it's prone to typos, and it clutters your business logic with noise.

The "Magic" of Runtime Reflection

For years, the "smart" way to solve this was to use reflection. You've likely used libraries that did this: they'd scan your class at runtime, find properties with specific attributes, and use reflection or IL emission to trigger notifications automatically. At first glance, this feels like a win because your code looks clean. You just add an attribute, and the "magic" happens.

But here is where the trade-off bites you. Reflection is slow. Even with caching, you're paying a performance tax every time a property changes. More importantly, you lose the safety of the compiler. If you rename a property but forget to update a string-based notification, the compiler won't blink, but your UI will simply stop updating. You're trading compile-time certainty for a slightly cleaner-looking file, and in a professional codebase, that's usually a bad deal.

// The naive "magic" approach (Conceptual)
public class UserProfile : ObservableObject 
{
    [Notify] 
    public string Username { get; set; } // The library uses reflection at runtime to make this work.
}

Moving the Work to the Compiler

Source Generators, introduced in .NET 5, flip this script. Instead of trying to figure out what your code does while the app is already running, a Source Generator hooks directly into the Roslyn compiler. It looks at your code while it's being compiled, recognizes patterns, and then literally writes the boring boilerplate code for you in the background.

The key here is the partial keyword. You mark your class as partial, and the generator creates another partial class in a separate, hidden file. When the compiler finishes, it merges those two pieces together. You get the cleanliness of the "magic" approach, but the final binary contains plain, old-fashioned C# that is just as fast as if you'd typed it all by hand.

// What you write:
public partial class UserProfile : ObservableObject 
{
    [ObservableProperty]
    private string _username;
}

// What the Source Generator writes (behind the scenes):
public partial class UserProfile 
{
    public string Username 
    {
        get => _username;
        set 
        {
            if (_username != value) 
            {
                _username = value;
                OnPropertyChanged(nameof(Username));
            }
        }
    }
}

When to actually use them

I'll be honest: writing a Source Generator is significantly harder than writing a standard library. You're no longer just writing C#; you're writing code that analyzes C# syntax trees. It's a steep learning curve involving ISyntaxReceiver and ISourceGenerator interfaces.

Because of that complexity, don't reach for this tool just to save a few lines of code. Use Source Generators when you have a repetitive pattern that is used across your entire project and where runtime performance is critical. If you're building a high-performance JSON serializer, a dependency injection container, or a massive MVVM framework, Source Generators are your best friend. If you're just trying to avoid writing three getters and setters, just write them—or use the newer required and init keywords.




📋 Practical Task

Implementing the Partial Pattern for a Mock Generator

While we aren't writing a full Roslyn Generator in this exercise, you need to understand the "contract" between a developer and a generator. You will simulate the output of a generator to ensure you understand how partial classes and nameof() prevent the runtime errors associated with reflection.

Your Task:

  • Create a partial class named EmployeeViewModel that implements INotifyPropertyChanged.
  • In the first part of the class, define a private field _salary.
  • Create a second partial class file (or block) that simulates the "Generated Code." In this block, implement a public property Salary.
  • The Salary property must use the nameof operator to trigger the PropertyChanged event, ensuring that if the property is renamed, the code will fail to compile rather than failing silently at runtime.
  • Instantiate the class and change the salary, verifying that the event fires with the correct property name.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.