序列化
初级 程序员
编辑器和序列化系统使用四个特性来确定哪些内容会被序列化,以及在编辑器中可见。
DataContractAttribute
在 class 或 struct 上添加 DataContractAttribute 会通知序列化器应该序列化它包含的数据,并通知编辑器应该显示该类型的字段和属性,以及可能包含它的场景或资产。
[Stride.Core.DataContract(Inherited = true)]
public class MySerializedClass
{
public float MyValue;
}
// 上面的 'DataContract' 会被继承,派生类无需再指定它
public class MyDerivedSerializedClass : MySerializedClass
{
public string MyName;
}
Note
添加 DataContract 时,你的 IDE 可能会错误地把 System.Runtime.Serialization 添加到你的 using 列表中。
它们不能互换使用。
请确保你的 DataContract 来自 Stride.Core,必要时像上面那样显式指定命名空间。
DataMemberAttribute
DataMemberAttribute 会通知编辑器和序列化器,带有 DataContract 特性的 class 或 struct 上的该属性或字段应该被序列化。请注意,对于大多数公共字段和属性,你可以省略这个特性,因为它们默认就会被包含。具体细节请参见字段和属性。
[Stride.Core.DataContract]
public class MySerializedClass
{
[Stride.Core.DataMember]
internal float MyValue;
}
DataAliasAttribute
DataAliasAttribute 可用于确保在源代码中更改该成员的名称时,不会破坏之前已序列化的数据。
[Stride.Core.DataAlias("PreviousNameOfProp")]
public string MyRenamedProp { get; set; }
Note
别名仅在编辑器中重新映射值;此特性是 YAML 序列化系统特有的。在构建和运行时,别名会被忽略。
DataMemberIgnoreAttribute
DataMemberIgnoreAttribute 会通知编辑器和序列化器,带有 DataContract 特性的 class 或 struct 上的该属性或字段不会被序列化。
[Stride.Core.DataContract]
public class MySerializedClass
{
[Stride.Core.DataMemberIgnore]
public float MyValue { get; set; } // 这个公共属性不会出现在编辑器中
}
待办事项
经验法则
序列化以及编辑器对你属性的访问和视图,反映了 C# 中访问修饰符的工作方式。
可以把你之外的序列化器/编辑器想象成代码库外部的一个类,如果你想让序列化器读写你的属性,就必须确保其 getter 和 setter 的访问修饰符允许序列化器访问它们。
如果你把该属性隐藏在 internal 访问修饰符之后,就必须用该特性对其加以标注,以确保它对序列化器可见。
字段
// 默认可在编辑器中读取和设置
public object obj;
// 通过特性可在编辑器中读取和设置
[DataMember] public internal object obj;
// 只读字段不能被修改为指向另一个对象,但当前设置的对象本身可以被修改
public readonly object obj;
[DataMember] internal readonly object obj;
// 永远不可
private protected/private/protected object obj;
属性
// 在编辑器中读取和设置 ...
// 默认情况
public object obj { get; set; }
public object obj { get => x; set => x = value; }
// 对 'internal' 修饰符,可通过特性强制启用
[DataMember] public object obj { internal get; public/internal set; }
[DataMember] public object obj { internal get => x; public/internal set => x; }
// 只读
public object obj { get; }
// 对任何非 public 的访问修饰符都是只读
public object obj { get; internal/private protected/private/protected set; }
public object obj { get => x; internal/private protected/private/protected set => x = value; }
// internal 属性的只读必须通过特性强制实现
[DataMember] internal object obj { get; }
[DataMember] internal object obj { get => x; }
// 只读;没有后备字段的 get-only 公共属性属于特殊情况
// 它们必须使用该特性才能被反序列化,参见下方的注释
[DataMember] public object obj { get => x; }
// 对比 internal 更严格的访问修饰符,即使有特性也是只读
[DataMember] public object obj { internal get; private protected/private/protected set; }
[DataMember] public object obj { internal get => x; private protected/private/protected set => x; }
// 永远不可
private protected/private/protected object obj { get; set; }
private protected/private/protected object obj { get => x; set => x; }
Note
没有后备字段的 get-only 公共属性(public object obj { get => x; })默认不会被序列化,
因为它们通常只是访问另一个对象的值的快捷方式,或纯粹当作函数使用。
如果你想序列化它,把它改为 { get; } = new MyObject(); 或 { get; init; } 可能更合理,
如果这样仍然不行,欢迎添加该特性来强制序列化。
那 init 呢?
init 访问修饰符在编辑器和序列化系统看来等同于 public set,因此它在编辑器中是可以设置的。