我们在C#游戏中使用BinaryFormatter,以保存用户游戏进度,游戏关卡等。我们遇到了向后兼容性问题。
目标:
解决方案需要对用户和关卡设计人员完全不可见,并且最低限度地要求改变某些内容的编码人员(例如,因为他们想要更好的名称而重命名字段)。
我们序列化的一些对象图根植于一个类中,一些在其他类中。不需要向前兼容。
潜在地破坏了变化(当我们序列化旧版本并反序列化为新版本时会发生什么):
我读过:
我目前的解决方案:
for(int i = loadedData.version; i < CurrentVersion; i++)
{
// Update() takes an instance of OldVersions.VersionX.TheClass
// and returns an instance of OldVersions.VersionXPlus1.TheClass
loadedData.data = Update(loadedData.data, i);
}
有些问题:
这应该是一个非常普遍的问题。人们通常如何解决它?
答案 0 :(得分:3)
艰难的一个。我会转储二进制文件并使用XML序列化(更容易管理,容忍不太极端的更改 - 比如添加/删除字段)。在更极端的情况下,更容易从一个版本到另一个版本编写转换(可能是xslt)并保持类清洁。如果要求不透明度和小磁盘占用,则可以在写入磁盘之前尝试压缩数据。
答案 1 :(得分:2)
我们的应用程序在存储用户配置文件数据时遇到了同样的问题(网格列排列,过滤器设置......)。
在我们的案例中,问题是AssemblyVersion。
对于这个问题,我创建了一个SerializationBinder
来读取实际的程序集版本
程序集(所有程序集在新部署时获得新版本号)
与Assembly.GetExecutingAssembly().GetName().Version
。
在覆盖方法BindToType
中,使用新的程序集版本创建类型信息。
反序列化是“手动”实现的,这意味着
使用我们的所有数据以及三到四个版本。
答案 2 :(得分:0)
这是一个非常老的问题,但是无论如何它都需要一个最新的答案。我知道这有点偏离主题,所以请耐心等待。今天,在2019年:我建议那些碰巧在您的项目中某个合理可行阶段的人读到 的人,认真考虑使用Protobuf而不是BinaryFormatter
。它具有二进制格式的大部分优点(但确实如此),但其缺点较少。
它具有经过深思熟虑的策略,可以以某种方式处理重大更改(添加/删除字段等),这意味着软件的“版本 x ”要容易得多处理“版本 y ”生成的数据,反之亦然。是的,这确实是对的:您的应用程序的较旧版本将能够处理使用Protobuf .proto
接口定义的较新版本序列化的数据。 (反序列化时,不存在的字段将被忽略。)
通过比较,运行新版本的代码并反序列化旧数据时,数据中的“不存在”字段将设置为特定于类型的默认值。从这种意义上说,处理旧数据并不是“完全自动化”的,但是比使用Java和.NET等平台随附的默认二进制序列化库时,它们简单得多。
如果您喜欢非二进制格式,则JSON通常是合适的选择。对于RPC和此类情况,Protobuf更好,甚至在当今,Microsoft都正式提及/认可:Introduction to gRPC on ASP.NET Core。 (gRPC是基于Protobuf构建的技术堆栈)