揭秘Nino的Source Generator:编译时代码生成管线深度解析
【免费下载链接】NinoUltimate high-performance binary serialization library for C#.项目地址: https://gitcode.com/gh_mirrors/ni/Nino
Nino 是一款终极高性能的 C# 二进制序列化库,它依托 Roslyn Source Generator,在编译期自动扫描标注了[NinoType]的类型,生成零反射、零运行时开销的序列化代码。本文将带你完整拆解 Nino 的代码生成管线:从增量管道扫描、类型图构建,到六大生成器协作输出,看看"编译时魔法"是如何发生的。
为什么Nino选择编译时代码生成
序列化库大致有两条技术路线:
| 路线 | 代表做法 | 代价 |
|---|---|---|
| 运行时生成 | 反射 + IL 动态发射 | 首次调用慢、AOT/IL2CPP 兼容差、GC 压力高 |
| 编译时生成 | Source Generator 提前产出代码 | 零反射、零分配,Unity IL2CPP 友好 |
Nino 选择了后者:编译时就把每个类型的字段读写逻辑"焊死"成 IL,运行期只剩纯内存拷贝。这也是 Nino 能压上高吞吐、低延迟场景的根基。
Nino Source Generator 代码生成管线全景
整条管线集中在 src/Nino.Generator/ 目录,入口是 GlobalGenerator.cs,它实现了IIncrementalGenerator增量生成接口。管线可以分为五个阶段:
阶段一:增量管道扫描候选类型
GlobalGenerator.cs 中,生成器先建立三条"数据流":
- 编译元数据流:通过
CompilationProvider.Select提取程序集信息(是否引用了 Nino.Core、是否为 Unity 程序集),用于增量缓存,避免重复计算; [NinoType]标记流:用GetTypeSyntaxes()精确捕获显式标注的类型;- 潜在类型流:捕获泛型、数组、可空、元组等类型语法,以及带基类的声明——用于追踪嵌套类型(如
Dictionary<string, MyData[]>)。
三条流.Combine()合并后,才触发真正的生成回调,这是增量管道"改一行代码不重跑全量"的关键设计。
阶段二:递归构建类型依赖图
拿到候选类型后,生成器用一个栈做广度遍历(GlobalGenerator.cs),沿五个方向扩散:
- 泛型实参(
List<T>里的T) - 数组元素类型
- 基类型——若是 NinoType 且来自其他程序集,会整个扫描该程序集
- 接口类型——多态体系同样纳入
- 成员字段/属性类型
这样即使你只标注了一个顶层 DTO,它整棵依赖树都会被自动收集,零手动注册。
阶段三:解析成员与继承链
CSharpParser.cs 把类型符号解析为NinoType元数据模型,重点处理三件事:
- 读取
[NinoExplicitOrder]、[NinoFormerName]、[NinoConstructor]等特性; - 按**"祖父 → 父 → 子"**的顺序收集继承链成员,保证在子类追加字段时不会挪动基类字段的序列化位置(版本兼容的关键);
- 支持 record、record struct 的主构造参数序列化。
随后 NinoGraph.cs 构建类型关系图:维护BaseTypes/SubTypes(多态派生关系)、TopTypes(顶层可序列化类型),并递归检测循环引用类型——循环类型在生成代码时会走引用(ref)路径,避免无限递归。
阶段四:六大生成器协作输出
图构建完成后,GlobalGenerator.cs 按顺序执行六个生成器:
| 生成器 | 产物 | 作用 |
|---|---|---|
NinoBuiltInTypesGenerator | 内置集合序列化器 | 为 List、Dictionary、Queue 等开箱即用 |
TypeConstGenerator | NinoTypeConst常量表 | 为每个多态类型分配稳定整型 ID |
UnsafeAccessorGenerator | 字段直读代码 | unsafe 指针访问,绕过属性开销 |
PartialClassGenerator | partial 成员 | 为私有成员生成受控访问入口 |
SerializerGenerator | 序列化方法 | 逐字段调用写入,极致内联 |
DeserializerGenerator | 反序列化方法 | 支持引用复用,零分配还原 |
内置集合支持非常完整,BuiltInType/ 目录下单独为List<T>、Dictionary<TKey,TValue>、Stack<T>、Queue<T>、元组、Nullable<T>等各写了一个生成器(NinoBuiltInTypesGenerator.cs 负责分发),这也是 Nino 对 Unity 场景(Vector3 等原生类型)无缝兼容的原因。
阶段五:容错与诊断兜底
生成器把"不破坏构建"放在第一位:任何解析或生成异常都会降级为NINO000/NINO998/NINO999警告(GlobalGenerator.cs),并往生成文件里写入错误注释,同时其他生成器继续执行——单个生成器失败不会拖垮整个编译。
配套的 NinoAnalyzer.cs 提供 IDE 实时诊断,NinoCodeFixProvider.cs 还能一键补全标注;发布时 DLL 被打进 NuGet 包的analyzers/dotnet/cs目录(Nino.Generator.csproj),装包即生效。
编译时生成的性能红利
- 零反射:所有字段读写在编译期展开为直接调用,IL2CPP/AOT 完全兼容;
- 稳定 ID 体系:多态类型 ID 由编译期常量表固定,跨程序集反序列化类型信息无损;
- 引用感知:循环依赖类型自动生成 ref 路径,对象图还原不炸栈;
- 增量构建:Roslyn 增量管道让改动只触发受影响类型的重新生成。
快速上手:在你的项目中运行Nino生成器
最快配置方式只需两步:
1️⃣ 安装(.NET 项目):
dotnet add package NinoUnity 项目通过 OpenUPM 安装:openupm add com.jasonxudeveloper.nino。
2️⃣ 标注类型:
[NinoType] public class GameData { public int Score; public string PlayerName; public DateTime LastPlayed; }重新编译后,生成器会自动在obj/.../generated下输出NinoTypeConst、序列化器与反序列化器代码,用 VS/Visual Studio Code 的"生成文件查看器"即可直接检查产物。若需研读生成器源码,可克隆仓库:
git clone https://gitcode.com/gh_mirrors/ni/Nino延伸阅读路径
想继续深挖,建议按管线顺序阅读这几个文件:
- 管道入口:GlobalGenerator.cs
- 类型解析:CSharpParser.cs 与 NinoType.cs
- 核心生成器基类:NinoGenerator.cs
- 序列化/反序列化生成:SerializerGenerator.cs、DeserializerGenerator.cs
- 运行时执行体:NinoSerializer.cs、NinoDeserializer.cs
看懂这套管线你会发现:所谓"高性能序列化库",本质上是一个在编译期替你写好了全部读写代码的代码生成器——这也正是 Source Generator 技术最优雅的应用场景。
【免费下载链接】NinoUltimate high-performance binary serialization library for C#.项目地址: https://gitcode.com/gh_mirrors/ni/Nino
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考