Table of Contents

序列化

初级 程序员

编辑器和序列化系统使用四个特性来确定哪些内容会被序列化,以及在编辑器中可见。

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,因此它在编辑器中是可以设置的。

另请参阅