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 属性。 ✔️
缩短启动时间。 ✔️ ✔️
降低专用内存使用量。 ✔️ ✔️
消除运行时反射。 ✔️ ✔️
有助于缩小可修整的应用的大小。 ✔️ ✔️
增加序列化吞吐量。 ✔️