# 序列化规则

> Conditions that determine whether fields in your scripts are serialized.

Unity 中的序列化器专门设计为在运行时高效运行。因此，Unity 中的序列化与其他编程环境中的序列化具有不同的行为。Unity 中的序列化程序直接作用于 C# 类的**字段**而不是属性，因此 Unity 仅在字段满足特定条件时才对其进行序列化。以下部分概述了如何在 Unity 中使用字段序列化。

要使用字段序列化，必须确保字段：

* 是 `public`，或具有 [SerializeField](/engine/6000.0/script-reference/unity-engine/serialize-field.md) 属性
  * **注意**：在某些情况下，`private` 字段被序列化，请参阅[热重载](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/how-unity-uses.md#hot-reload)

* 非 `static`

* 非 `const`

* 非 `readonly`

* 具有可序列化的字段类型：

  * 原始数据类型（`int`、`float`、`double`、`bool`、`string` 等）
  * 枚举类型（32 位或更小）
  * 固定大小的缓冲区
  * Unity 内置类型，例如 Vector2、Vector3、Rect、Matrix4x4、Color、AnimationCurve
  * 具有 Serializable 属性的自定义结构
  * 对从 [UnityEngine.Object](/engine/6000.0/script-reference/unity-engine/object.md) 派生的对象的引用
  * 具有 Serializable 属性的自定义类。（请参阅[自定义类的序列化](#CustomClasses)）。
  * 上述字段类型的数组
  * 上述字段类型的 `List<T>`

**注意**：Unity 不支持多级类型（多维数组、交错数组、词典和嵌套容器类型）的序列化。如果要序列化这些类型，可使用两种方法：

* 将嵌套类型放在类或结构中
* 通过实现 [ISerializationCallbackReceiver](/engine/6000.0/script-reference/unity-engine/i-serialization-callback-receiver.md)，使用序列化回调来执行[自定义序列化](#CustomSerialization)。

## 自定义类的序列化##field-serialization-rules

Unity 要序列化自定义类，必须确保该类：

* 具有 [Serializable](ScriptRef:Serializable) 属性
* 非静态。

将派生自 `UnityEngine.Object` 的类的实例分配给字段并且 Unity 保存该字段时，Unity 会将该字段序列化为对该实例的引用。Unity 会单独序列化实例本身，因此在为实例分配多个字段时不会重复。但对于不派生自 `UnityEngine.Object` 的自定义类，Unity 会直接将实例的状态包含在引用这些类的 MonoBehaviour 或 ScriptableObject 的序列化数据中。有两种方法可以做到这一点：**内联**和\*\*`[SerializeReference]`\*\*。

* **内联序列化**：默认情况下，如果未在引用了类的字段上指定 `[SerializeReference]`，Unity 会按值将自定义类内联序列化。如果在多个不同的字段中存储对自定义类的实例的引用，则在序列化时它们将成为单独的对象。然后，当 Unity 反序列化这些字段时，它们将包含具有相同数据的不同对象。
* **`[SerializeReference]` 序列化**：如果确实指定了 `[SerializeReference]`，Unity 会将对象设定为托管引用。主机对象仍然将对象直接存储在其序列化数据中，但在一个专门的注册表部分。

`[SerializeReference]` 会增加一些开销，但支持以下情况：

* 字段可为 null。内联序列化不能表示 null，而是用具有未分配字段的内联对象替换 null。
* 对同一对象的多次引用。如果在多个不同的字段中存储对自定义类的实例的引用而没有使用 `[SerializeReference]`，则在序列化时它们将成为单独的对象。
* 图形和循环数据（例如，具有可回传到自身的引用的对象）。内联类序列化不支持 null 或共享引用，因此数据中的任何循环都可能导致意外结果，例如奇怪的 Inspector 行为、控制台错误或无限循环。
* 多态。如果创建一个派生自父类的类，并将其分配给一个使用父类作为其类型的字段而没有使用 `[SerializeReference]`，则 Unity 只会序列化属于父类的字段。Unity 反序列化类实例时，它会实例化父类而不是派生类。
* 当数据结构需要一个稳定的标识符来指向特定对象，而不通过硬编码对象的数组位置或搜索整个数组时。请参阅 [Serialization.ManagedReferenceUtility.SetManagedReferenceIdForObject](/engine/6000.0/script-reference/unity-engine-serialization/managed-reference-utility/set-managed-reference-id-for-object.md)。

**注意**：内联序列化更高效，除非特别需要 `[SerializeReference]` 支持的功能之一，否则应该使用内联序列化。有关如何使用 `[SerializeReference]` 的完整详细信息，请参阅 [SerializeReference](/engine/6000.0/script-reference/unity-engine/serialize-reference.md) 文档。

## 属性的序列化##scrollpoint-id=customclasses-/serialization-of-custom-classes

Unity 通常不会序列化属性，但以下情况除外：

* 如果属性具有显式后备字段，Unity 将根据常规序列化规则对其进行序列化。例如：

```
public int MyInt
{
get => m_backing;
private set => m_backing = value;
}
[SerializeField] private int m_backing;
```

* Unity 仅在热重载期间序列化具有自动生成字段的属性。

```lang-C#
public int MyInt { get; set; }
```

如果不希望 Unity 使用自动生成的字段序列化属性，请使用 `[field: NonSerialized]` 属性。

## 其他资源##serialization-of-properties

* [自定义序列化](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/custom-serialization.md)
* [Unity 如何使用序列化](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/how-unity-uses.md)
* [JSONSerialization](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/json-serialization.md)
* [序列化错误](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/errors.md)
* [序列化最佳实践](/engine/6000.0/manual/scripting/compilation-and-code-reload/script-serialization/best-practices.md)
