迁移到 protobuf-net(二进制)

protobuf-net 库是适用于 .NET 的基于协定的序列化程序,它使用二进制协议缓冲区serialization格式。 API 遵循典型的 .NET 模式,与 XmlSerializerDataContractSerializer 大致相当。

在从 BinaryFormatter 迁移的过程中,需要注意 protobuf-net 的某些行为和功能,许多方案需要将特性应用于成员。

  • 默认情况下,公共和非公共类型都是可序列化的,序列化程序需要无参数的构造函数。
  • protobuf-net 要求使用 [ProtoContract] 特性注释每个可序列化类型;此特性可以选择指定 SkipConstructor = true 属性,从而不需要任何特定的构造函数。
  • 每个可序列化的非静态字段和属性都需要使用 [ProtoMember(int identifier)] 特性进行批注。 成员名称不会在数据中编码。 相反,用户必须选取一个正整数来标识该类型内必须唯一的每个成员。
  • 必须通过具有已知子类型的每个类型上的 [ProtoInclude(...)] 特性显式声明继承
  • 默认情况下支持只读字段。
  • 或者,构造函数模式可识别某些无特性类元组类型;若类型具有与所有已声明的公共成员匹配的参数的构造函数,则类型将被解释为元组,参数顺序将用于推断该成员的标识符。
  • 强烈建议使用 protobuf-net.BuildTools 设计时,这能提供常见错误的编译时警告。

分步迁移

  1. 查找 BinaryFormatter 的所有用法。
  2. 确保 serialization 代码路径被测试覆盖,以便你可以验证你的更改并避免引入 bug。
  3. 安装 protobuf-net 包(或者 protobuf-net.BuildTools)。
  4. 查找正在通过 BinaryFormatter 进行序列化的所有类型。
  5. 对于可以修改的类型:
    • 使用 [ProtoContract] 特性对所有标有 [Serializable] 或实施 ISerializable 接口的类型进行批注。 如果未向其他可能使用不同序列化程序(例如 DataContractSerializer)的应用公开这些类型(例如:你正在编写库),则可以可以删除 [Serializable]ISerializable 注释。
    • 对于派生类型,请将 [ProtoInclude(...)] 应用于其基类型(请参阅以下示例)。
    • 对于声明任何接受参数的构造函数的每个类型,请添加无参数构造函数或在 [ProtoContract] 特性上指定 SkipConstructor = true。 写下注释,解释 protobuf-net 要求(以便没有人意外删除它)。
    • 标记要通过 [ProtoMember(int identifier)] 序列化的所有成员(字段和属性)。 所有标识符在单个类型中必须是唯一的,但如果启用继承,则可以在子类型中重新使用相同的数字。
  6. 对于无法修改的类型:
    • 对于 .NET 本身提供的类型,可以使用 ProtoBuf.Meta.RuntimeTypeModel.Default.CanSerialize(Type type) API 检查它们是否由 protobuf-net 本机支持。
    • 可以创建专用数据传输对象 (DTO) 并相应地映射它们(可以使用隐式强制转换运算符)。
    • 使用 RuntimeTypeModel API 定义特性允许的所有内容。
  7. BinaryFormatter 的用法替换为 ProtoBuf.Serializer
-[Serializable]
+[ProtoContract]
+[ProtoInclude(2, typeof(Point2D))]
public class Point1D
{
+   [ProtoMember(1)]
    public int X { get; set; }
}

-[Serializable]
+[ProtoContract]
public class Point2D : Point1D
{
+   [ProtoMember(2)]
    public int Y { get; set; }
}