📅 Day 28:C# 源生成器(Source Generators)與編譯時元編程
? 學習目標:
- 理解什么是 源生成器(Source Generator);
- 掌握如何在 編譯階段生成 C# 代碼,而不是運行時動態處理;
- 使用源生成器替代運行時反射和表達式樹,提升性能;
- 實現一個基于源生成器的自動化 DTO 映射器;
- 理解 Roslyn 編譯器的工作流程;
- 掌握源生成器開發的基本結構、語法分析技巧;
- 構建高性能、零運行時開銷的實用工具;
- 了解源生成器的適用場景和局限性。
🧠 一、什么是源生成器?
源生成器(Source Generator) 是 C# 9 引入的一項新特性,它允許你在 編譯階段自動生成 C# 代碼。這些代碼會被加入到你的項目中,并參與正常的編譯流程,就像你手動編寫的一樣。
? 優勢:
特點 | 說明 |
---|---|
零運行時開銷 | 所有邏輯在編譯期完成 |
更快的執行速度 | 替代運行時反射、Expression 樹等慢速操作 |
更好的可讀性 | 自動生成的代碼是靜態的,易于調試和優化 |
IDE 支持好 | 可跳轉、可重構、可提示 |
🔁 二、源生成器 vs 運行時反射
對比項 | 源生成器 | 運行時反射 |
---|---|---|
執行時機 | 編譯期 | 運行期 |
性能 | 零開銷 | 相對較慢 |
可維護性 | 生成代碼可見 | 動態邏輯難以追蹤 |
調試支持 | 完全支持 | 不易調試 |
是否需要額外依賴 | 否 | 可能需要緩存或委托生成 |
🧩 三、源生成器基本結構
要創建一個源生成器,你需要實現 IIncrementalGenerator
接口(.NET 6+ 推薦),或者繼承 ISourceGenerator
(舊版 .NET 5)。
我們以
.NET 6+
的IIncrementalGenerator
為例。
步驟概覽:
- 創建類庫項目并添加 SDK 支持;
- 添加對
Microsoft.CodeAnalysis.CSharp
和Microsoft.CodeAnalysis.Analyzers
的引用; - 實現
IIncrementalGenerator
; - 注冊分析器并生成代碼;
- 在主項目中引用該源生成器 DLL;
- 查看生成的代碼(在
obj/Debug/generators
文件夾下)。
🛠? 四、實戰示例:自動映射 DTO 與實體類
我們來實現一個簡單的 DTO 映射器源生成器,它可以自動生成如下代碼:
public static class PersonMapper
{public static PersonDto ToDto(this Person person){return new PersonDto{Name = person.Name,Age = person.Age};}
}
1?? 定義 Attribute(用于標記需生成映射的類型)
[AttributeUsage(AttributeTargets.Class)]
public class GenerateMappingAttribute : Attribute
{public Type TargetType { get; }public GenerateMappingAttribute(Type targetType){TargetType = targetType;}
}
2?? 創建源生成器類
[Generator]
public class MappingSourceGenerator : IIncrementalGenerator
{public void Initialize(IncrementalGeneratorInitializationContext context){var classes = context.SyntaxProvider.CreateSyntaxProvider(predicate: static (s, _) => IsCandidateForMapping(s),transform: static (ctx, _) => GetSemanticTargetForMapping(ctx)).Where(static m => m is not null)!;context.RegisterSourceOutput(classes, GenerateCode);}private static bool IsCandidateForMapping(SyntaxNode node){return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 };}private static ClassDeclarationSyntax GetSemanticTargetForMapping(GeneratorSyntaxContext context){var classDeclaration = (ClassDeclarationSyntax)context.Node;foreach (var attributeList in classDeclaration.AttributeLists){foreach (var attribute in attributeList.Attributes){if (context.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol attributeSymbol &&attributeSymbol.ContainingType.ToDisplayString() == "GenerateMappingAttribute"){return classDeclaration;}}}return null;}private void GenerateCode(SourceProductionContext context, ClassDeclarationSyntax classDecl){var className = classDecl.Identifier.Text;var namespaceName = classDecl.Parent is NamespaceDeclarationSyntax ns? ns.Name.ToString(): "";var source = $@"
using System;namespace {namespaceName}
{{public static partial class {className}Mapper{{public static {className}Dto ToDto(this {className} model){{return new {className}Dto{{Name = model.Name,Age = model.Age}};}}}}
}}";context.AddSource($"{className}Mapper.g.cs", SourceText.From(source, Encoding.UTF8));}
}
3?? 主項目使用方式
[GenerateMapping(typeof(PersonDto))]
public class Person
{public string Name { get; set; }public int Age { get; set; }
}public class PersonDto
{public string Name { get; set; }public int Age { get; set; }
}
編譯后會自動生成 PersonMapper
類!
🧱 五、Roslyn 編譯器基礎概念
源生成器基于 Roslyn 編譯器平臺,它是 C# 和 VB.NET 的開源編譯器框架。
關鍵組件:
名稱 | 作用 |
---|---|
SyntaxTree | 表示解析后的語法樹 |
SemanticModel | 提供語義信息(如變量類型、方法重載等) |
Compilation | 表示整個項目的編譯過程 |
ISymbol | 表示各種符號(類、方法、屬性等) |
GeneratorExecutionContext | 提供上下文信息用于生成代碼 |
💡 六、源生成器適用場景
場景 | 示例 |
---|---|
DTO 映射 | 自動生成 ToDto() 方法 |
ORM 屬性綁定 | 自動綁定數據庫字段 |
JSON 序列化 | 避免反射,直接生成序列化代碼 |
本地化資源訪問 | 自動生成強類型資源訪問器 |
日志記錄 | 自動生成日志包裝器 |
AOP 攔截器 | 生成代理類,替代運行時代理 |
枚舉擴展 | 自動生成枚舉描述、轉換方法 |
?? 七、源生成器的限制
限制 | 說明 |
---|---|
不能訪問運行時數據 | 無法根據用戶輸入動態生成代碼 |
不適合復雜邏輯 | 復雜業務邏輯更適合運行時處理 |
調試困難 | 生成的代碼位于 obj 文件夾,不易修改 |
學習曲線陡峭 | 需要熟悉 Roslyn API 和語法樹結構 |
僅適用于編譯時已知結構的類型 | 無法處理運行時動態類型 |
🧪 八、查看生成的代碼
生成的代碼會放在你的項目目錄下的:
[obj]\[Debug|Release]\net8.0\generators\run\Your.SourceGenerator\
你可以在這里看到所有由源生成器生成的 .g.cs
文件。
💪 九、構建高性能應用的建議
技術 | 建議 |
---|---|
盡量用源生成器替代反射 | 減少運行時性能損耗 |
用源生成器預計算常量邏輯 | 如路由匹配、配置加載 |
結合 Source Generator + Partial Method | 分離手寫邏輯與生成邏輯 |
使用緩存機制 | 如果必須運行時處理,緩存結果 |
使用 Source Generator 生成測試樁 | 自動生成 Mock 數據、單元測試輔助類 |
📝 小結
今天你學會了:
- 什么是 源生成器(Source Generator);
- 掌握了如何在 編譯階段生成 C# 代碼;
- 實現了一個基于源生成器的 自動 DTO 映射器;
- 理解了 Roslyn 編譯器的基本工作原理;
- 掌握了源生成器開發的基本結構和語法分析技巧;
- 學會了如何構建高性能、零運行時開銷的實用工具;
- 了解了源生成器的適用場景與限制。
源生成器是一項強大的工具,尤其適用于構建高性能框架、減少運行時反射依賴、以及自動完成重復性代碼編寫任務。
🧩 下一步學習方向(Day 29)
明天我們將進入本次 挑戰的 最終總結篇 —— C# 綜合進階知識回顧與職業發展建議,你將學到:
- 所學知識點的系統回顧;
- 如何構建完整的 C# 開發能力體系;
- C# 高級開發者必備技能清單;
- C# 在 Web、桌面、游戲、AI 等領域的應用場景;
- 如何規劃自己的 C# 職業成長路徑;
- 如何準備技術面試、參與開源項目、打造個人影響力。