C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
96: Source Generators Overview
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
partialclass namedEmployeeViewModelthat implementsINotifyPropertyChanged. - In the first part of the class, define a private field
_salary. - Create a second
partialclass file (or block) that simulates the "Generated Code." In this block, implement a public propertySalary. - The
Salaryproperty must use thenameofoperator to trigger thePropertyChangedevent, 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.
There are no comments for now.