System.Text.Json 中的反射与源生成
本文介绍反射与源生成之间的差异,因为它与 System.Text.Json
序列化相关。 它还提供有关如何针对方案选择最佳方法的指导。
元数据收集
若要对类型进行序列化或反序列化,JsonSerializer 需要关于如何访问该类型的成员的信息。 JsonSerializer
需要以下信息:
- 如何访问属性 getter 和字段以进行序列化。
- 如何访问构造函数、属性资源库和字段以进行序列化。
- 有关用于自定义序列化或反序列化的特性的信息。
- JsonSerializerOptions 中的运行时配置。
此信息被称为“元数据”。
反射
默认情况下,JsonSerializer 使用反射在运行时收集元数据。 每当 JsonSerializer
需要首次对类型进行序列化或反序列化时,它将收集并缓存此元数据。 元数据收集进程将耗费时间并占用内存。
源生成
作为替代方法,System.Text.Json
可以使用 C# 源生成功能来提高性能、降低专用内存使用量以及推动程序集修整,从而缩小应用大小。 此外,某些反射 API 不能用于本机 AOT 应用程序,因此必须为这些应用使用源生成。
源生成可用于两种模式:
基于元数据的模式
在编译过程中,
System.Text.Json
收集序列化所需的信息,并生成用于填充所请求类型的 JSON 协定元数据的源代码文件。序列化优化(快速路径)模式
自定义序列化输出的 JsonSerializer 功能(如命名策略和引用保留)会产生性能开销。 在序列化优化模式下,System.Text.Json 会生成直接使用
Utf8JsonWriter
的优化序列化代码。 这种优化代码或快速路径代码可提高序列化吞吐量。快速路径反序列化当前不可用。 有关详细信息,请参阅 dotnet/runtime 问题 55043。
System.Text.Json
的源生成需要 C# 9.0 或更高版本。
功能比较
基于每个模式的如下优点来选择反射或源生成模式:
好处 | 反射 | 源生成 (基于元数据的模式) |
源生成 (序列化优化模式) |
---|---|---|---|
代码更简单。 | ✔️ | ❌ | ❌ |
调试更简单。 | ❌ | ✔️ | ✔️ |
支持非公共成员。 | ✔️ | ✔️ | ✔️ |
支持所有可用的序列化自定义。 | ✔️ | ||
缩短启动时间。 | ❌ | ✔️ | ✔️ |
降低专用内存使用量。 | ❌ | ✔️ | ✔️ |
消除运行时反射。 | ❌ | ✔️ | ✔️ |
有助于缩小可修整的应用的大小。 | ❌ | ✔️ | ✔️ |
增加序列化吞吐量。 | ❌ | ❌ | ✔️ |
*源生成器支持某些非公共成员,例如同一程序集中的内部类型。 †可以使用协定自定义 API 修改源生成的协定。
好处 | 反射 | 源生成 (基于元数据的模式) |
源生成 (序列化优化模式) |
---|---|---|---|
代码更简单。 | ✔️ | ❌ | ❌ |
调试更简单。 | ❌ | ❌ | ✔️ |
支持非公共访问器。 | ✔️ | ❌ | ❌ |
支持所需的属性。 | ✔️ | ❌ | ❌ |
支持 init-only 属性。 | ✔️ | ❌ | ❌ |
缩短启动时间。 | ❌ | ✔️ | ✔️ |
降低专用内存使用量。 | ❌ | ✔️ | ✔️ |
消除运行时反射。 | ❌ | ✔️ | ✔️ |
有助于缩小可修整的应用的大小。 | ❌ | ✔️ | ✔️ |
增加序列化吞吐量。 | ❌ | ❌ | ✔️ |