Why Source Generator
Overview
Section titled “Overview”In the previous chapter, we examined the definition and operating principles of source generators. However, knowing “this technology exists” and deciding “we should choose this technology” are different matters. With existing code generation techniques like Reflection, T4 templates, and Expression Trees already available, why should we use source generators specifically? This chapter covers the concrete advantages of source generators and suitable use scenarios.
Learning Objectives
Section titled “Learning Objectives”Core Learning Objectives
Section titled “Core Learning Objectives”- Understand the advantages of source generators
- Grasp the specific evidence for performance, type safety, debugging, and AOT support
- Identify differences from existing code generation techniques
- Comparison with T4 templates, Reflection.Emit, and Expression Trees
- Determine suitable use scenarios
- Distinguish between cases where source generators are and are not appropriate
Advantages of Source Generators
Section titled “Advantages of Source Generators”1. Performance
Section titled “1. Performance”Runtime cost directly impacts application performance. Source generators generate code at compile time, so there is no runtime overhead.
// Reflection-based (runtime cost incurred)public void LogWithReflection(object obj){ var properties = obj.GetType().GetProperties(); // Reflection call every time foreach (var prop in properties) { var value = prop.GetValue(obj); // Runtime cost Console.WriteLine($"{prop.Name}: {value}"); }}
// Source generator-based (no runtime cost)public void LogWithSourceGenerator(User user){ // Code generated at compile time - direct property access Console.WriteLine($"Id: {user.Id}"); Console.WriteLine($"Name: {user.Name}"); Console.WriteLine($"Email: {user.Email}");}Performance Comparison (Example)================================
Reflection: ~1,000 ns/callSource Generator: ~10 ns/call
-> Approximately 100x performance improvement2. Type Safety
Section titled “2. Type Safety”Beyond performance, safety is also important. Since code is generated at compile time, type errors can be discovered at build time.
// Reflection - error occurs at runtimevar value = prop.GetValue(obj); // Runtime exception if obj is null
// Source generator - error discovered at compile timepublic void Process(User user){ var id = user.Id; // Compile error if User has no Id}3. Ease of Debugging
Section titled “3. Ease of Debugging”Generated code is regular C# code, so you can step into it with a debugger.
Viewing Generated Code in Visual Studio========================================
1. Solution Explorer -> Dependencies -> Analyzers2. Expand source generator project3. View and debug generated .g.cs files4. AOT Compilation Support
Section titled “4. AOT Compilation Support”Fully compatible with .NET 10 Native AOT.
<!-- Enable AOT in .NET 10 project --><PropertyGroup> <TargetFramework>net10.0</TargetFramework> <PublishAot>true</PublishAot></PropertyGroup>Reflection-based code has limitations with AOT, but code generated by source generators is statically compiled with no restrictions.
5. IntelliSense Support
Section titled “5. IntelliSense Support”Generated code is immediately recognized by the IDE’s IntelliSense.
// When the source generator generates a UserPipeline classvar pipeline = new UserPipeline();pipeline. // <- IntelliSense shows generated methodsHaving examined the individual advantages of source generators, let’s now directly compare them with existing code generation technologies to determine specifically which technology is suitable for which situation.
Comparison with Existing Technologies
Section titled “Comparison with Existing Technologies”T4 Templates
Section titled “T4 Templates”| Item | T4 Templates | Source Generator |
|---|---|---|
| Execution Time | Design time (manual) | Compile time (automatic) |
| Input Change Detection | Manual re-execution needed | Auto-detection and regeneration |
| Source Control | Generated files must be included | Not needed |
| IDE Integration | Limited | Full integration |
| .NET 10 Support | Legacy | Official support |
Reflection.Emit
Section titled “Reflection.Emit”| Item | Reflection.Emit | Source Generator |
|---|---|---|
| Output | IL code | C# source code |
| Debugging | Very difficult | Easy (regular C#) |
| AOT Support | Limited | Full support |
| Learning Curve | High (IL knowledge needed) | Low (C# knowledge) |
Expression Trees
Section titled “Expression Trees”| Item | Expression Trees | Source Generator |
|---|---|---|
| Execution Time | Runtime | Compile time |
| Expression Scope | Lambda expressions | Full C# syntax |
| Performance | Caching needed | No overhead |
| Complexity | Medium | Medium |
Having confirmed the position of source generators through comparison, let’s now organize the cases where source generators should and should not be chosen in actual projects.
Suitable Use Scenarios
Section titled “Suitable Use Scenarios”When Source Generators Are Suitable
Section titled “When Source Generators Are Suitable”O Eliminating repetitive boilerplate code Example: DTO mapping, INotifyPropertyChanged implementation
O Attribute-based code generation Example: [Serialize], [Validate], [Log]
O Automating interface implementation Example: Repository pattern, CQRS handlers
O Performance-critical serialization/deserialization Example: JSON, MessagePack, Protocol Buffers
O Projects requiring AOT deployment Example: iOS/Android, WebAssembly, ServerlessWhen Source Generators Are Not Suitable
Section titled “When Source Generators Are Not Suitable”X When types are determined dynamically at runtime Example: Plugin systems, scripting engines
X Very simple code generation Example: One or two line wrapper methods
X Dependence on external data sources Example: Database schema-based generation (not accessible at compile time)Based on these criteria, let’s look at representative examples of how source generators are actually being used in the .NET ecosystem.
Real-World Use Cases
Section titled “Real-World Use Cases”1. System.Text.Json (Microsoft)
Section titled “1. System.Text.Json (Microsoft)”.NET’s official JSON library supports compile-time serialization with source generators.
// .NET 10 - JSON Source Generator[JsonSerializable(typeof(User))][JsonSerializable(typeof(Order))]public partial class AppJsonContext : JsonSerializerContext;
// Usagevar json = JsonSerializer.Serialize(user, AppJsonContext.Default.User);2. LoggerMessage (Microsoft)
Section titled “2. LoggerMessage (Microsoft)”A source generator for high-performance logging.
public static partial class Log{ [LoggerMessage(Level = LogLevel.Information, Message = "User {UserId} logged in at {LoginTime}")] public static partial void UserLoggedIn( ILogger logger, int userId, DateTime loginTime);}3. Functorium ObservablePortGenerator
Section titled “3. Functorium ObservablePortGenerator”The source generator we will implement in this tutorial, which automatically generates adapter pipeline code.
// Input - written by developer[GenerateObservablePort]public class UserRepository(ILogger<UserRepository> logger) : IObservablePort{ public FinT<IO, User> GetUserAsync(int id) => ...;}
// Output - auto-generated by source generatorpublic partial class UserRepositoryObservable{ private readonly ILogger<UserRepository> _logger;
public FinT<IO, User> GetUserAsync(int id) { // Logging, metrics, tracing code automatically included }}Summary at a Glance
Section titled “Summary at a Glance”Source generators provide five key advantages: performance with no runtime overhead, type safety through compile-time error detection, direct debugging of generated C# code, full compatibility with .NET 10 Native AOT, and IDE integration supporting IntelliSense and refactoring. They have a clear advantage over existing technologies, particularly in scenarios requiring repetitive boilerplate code generation and AOT deployment.
Q1: What are the advantages of source generators over T4 templates?
Section titled “Q1: What are the advantages of source generators over T4 templates?”A: T4 templates must be manually executed at design time, and generated files must be included in source control. Source generators automatically execute at compile time, auto-detect input changes, and do not require generated files in source control, significantly reducing maintenance costs.
Q2: In what situations are source generators not suitable?
Section titled “Q2: In what situations are source generators not suitable?”A: They are unsuitable for plugin systems where types are dynamically determined at runtime, or for database schema-based generation that cannot be accessed at compile time. Also, if you only need one or two line simple wrapper methods, the setup cost of source generators outweighs the benefits.
Q3: Why are source generators important in AOT environments?
Section titled “Q3: Why are source generators important in AOT environments?”A: Native AOT restricts runtime code generation and dynamic reflection. Source generators statically generate all code at compile time, allowing the AOT compiler to optimize the generated code just like regular code, making them the only alternative for AOT deployment environments.
Having confirmed the reasons for choosing source generators, let’s now examine the overall structure and design goals of the ObservablePortGenerator project we will actually implement in this tutorial.