反射的代价
序列化、对象映射、依赖注入这些框架经常用反射在运行时读类型、创建对象。反射灵活,但每次都要查元数据、做绑定,启动慢、还容易把类型错误拖到运行时才爆。源生成器的思路是:把这些工作挪到编译期,直接生成强类型代码。
能在编译期确定的,就别留到运行时。
反射的三个痛点
- 启动慢元数据扫描耗时。
- 类型不安全错误运行时才现形。
- 裁剪受限反射让 AOT 难裁剪。
源生成器是什么
源生成器是一个在编译阶段运行的组件。它可以读取你代码里的语法树、语义模型,然后额外生成一些 .cs 文件一起参与编译。System.Text.Json、Mapperly 这些库已经用它替代了大量反射。
写一个增量源生成器
一个最小源生成器实现 IIncrementalGenerator,注册语法节点或类型的回调,分析后用 SyntaxTree 生成代码。关键是增量——只有相关代码变动才重新生成,别让它拖慢整个编译。
// 伪代码骨架
[Generator]
public class MyGen : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorContext c) {
// 注册对带特性类型的分析并emit代码
}
}
典型应用场景
常见三类:一是序列化替代反射;二是对象映射;三是 INotifyPropertyChanged 这类样板代码自动生成。它们的共同点是——逻辑确定、类型明确,非常适合在编译期写死。
调试与诊断
源生成器生成的代码默认看不见。打开编辑器里的「显示生成器输出」,或在项目里把生成的 .cs 写到磁盘,才能逐行核对它到底生成了什么。生成错了,往往是分析逻辑没考虑到边界类型。
落地建议
- 先小范围从一个样板代码生成起步。
- 开增量保证编译速度。
- 可观测把生成代码落盘核对。
与其他优化配合
源生成器和 Native AOT、裁剪是绝配:编译期生成的代码不需要反射元数据,运行时可以大胆裁剪。再配合 Span、对象池,启动与内存都能下一个台阶。
小结与延伸阅读
把反射能挪到编译期的部分交给源生成器,既能提速又能让错误提前暴露。它是现代 .NET 性能优化工具箱里越来越重要的一件。
- 反射基础读《C# 反射与动态编程实战》。
- 内存优化读《.NET 性能优化与内存管理实战》。
- Web 侧读《.NET 8 Minimal API 与云原生实战》。