📅 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# 职业成长路径;
- 如何准备技术面试、参与开源项目、打造个人影响力。