.NET 中与 BinaryFormatter 的向后兼容性

2023-11-27

我们在 C# 游戏中使用 BinaryFormatter 来保存用户游戏进度、游戏级别等。我们遇到了向后兼容性的问题。

目的:

  • 关卡设计师创建活动(关卡和规则),我们更改代码,活动应该仍然可以正常工作。在发布之前的开发过程中,这种情况每天都可能发生。
  • 用户保存游戏,我们发布游戏补丁,用户应该仍然能够加载游戏
  • 无论两个版本相距多远,隐形数据转换过程都应该有效。例如,用户可以跳过前 5 个小更新并直接获取第 6 个更新。尽管如此,他保存的游戏应该仍然可以正常加载。

该解决方案需要对用户和关卡设计师完全不可见,并且尽量减少想要更改某些内容的编码人员的负担(例如,因为他们想到了更好的名称而重命名字段)。

我们序列化的一些对象图植根于一个类,有些则植根于其他类。不需要向前兼容性。

潜在的重大更改(以及当我们序列化旧版本并反序列化为新版本时会发生什么):

  • 添加字段(默认初始化)
  • 更改字段类型(失败)
  • 重命名字段(相当于删除它并添加新字段)
  • 将属性更改为字段并返回(相当于重命名)
  • 更改自动实现的属性以使用支持字段(相当于重命名)
  • 添加超类(相当于将其字段添加到当前类中)
  • 以不同方式解释字段(例如,以前以度为单位,现在以弧度为单位)
  • 对于实现 ISerialized 的类型,我们可能会更改 ISerialized 方法的实现(例如,对于某些非常大的类型,开始在 ISerialized 实现中使用压缩)
  • 重命名类、重命名枚举值

我读过:

  • 版本容错序列化
  • ID反序列化回调
  • [可选字段(版本已添加)]
  • [OnDeserializing]、[OnDeserialized]、[OnSerializing]、[OnSerialized]。
  • [未序列化]

我目前的解决方案:

  • 通过使用 OnDeserializing 回调等内容,我们尽可能多地进行不间断的更改。
  • 我们每两周安排一次重大更改,因此需要保留的兼容性代码较少。
  • 每次在我们做出重大改变之前,我们都会复制all我们使用的 [Serialized] 类放入名为 OldClassVersions.VersionX 的命名空间/文件夹中(其中 X 是最后一个序数之后的下一个序数)。即使我们不打算很快发布版本,我们也会这样做。
  • 当写入文件时,我们序列化的是该类的一个实例: class SaveFileData { int version;对象数据; }
  • 从文件读取时,我们反序列化 SaveFileData 并将其传递给迭代“更新”例程,该例程执行如下操作:

.

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);
}
  • 为了方便起见,Update()函数在其实现中可以使用CopyOverlappingPart()函数,该函数使用反射将尽可能多的数据从旧版本复制到新版本。这样,Update() 函数只能处理实际更改的内容。

一些问题:

  • 反序列化器反序列化为 Foo 类,而不是 OldClassVersions.Version5.Foo 类 - 因为 Foo 类是被序列化的。
  • 几乎不可能测试或调试
  • 需要保留许多类的旧副本,这很容易出错、脆弱且烦人
  • 当我们想要重命名一个类时,我不知道该怎么做

这应该是一个非常普遍的问题。人们通常如何解决?


很难的一个。我会转储二进制文件并使用 XML 序列化(更易于管理,能够容忍不太极端的更改 - 例如添加/删除字段)。在更极端的情况下,编写从一个版本到另一个版本的转换(也许是 xslt)并保持类干净会更容易。如果需要不透明度和较小的磁盘占用空间,您可以尝试在写入磁盘之前压缩数据。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.NET 中与 BinaryFormatter 的向后兼容性 的相关文章

  • 如何使用 WebResponse 下载 .wmv 文件

    我使用以下代码通过 WebResponse 获取 wmv 文件 我正在使用一个线程来调用这个函数 static void GetPage object data Cast the object to a ThreadInfo ThreadI
  • CLSCompliant(true) 拖入未使用的引用

    任何人都可以explain以下行为 总之 如果您创建多个符合 CLS 标准Visual Studio 2008 中的库并让它们共享公共命名空间根 引用另一个库的库将require对该库的引用的引用 即使它不消耗它们 用一句话来解释是相当困难
  • 点击浏览器后退按钮时如何刷新 ASP .NET MVC 页面

    我刚刚发现 当我单击任何 ASP NET MVC 页面上的浏览器后退按钮时 没有任何反应 并且页面不会更新 并且只有当您单击 F5 时才会更新 主要问题是我对页面的 DOM 进行了一些更改 即添加表格行 选择单选按钮等 当我通过点击浏览器后
  • 在 .Net 应用程序中使用 Active Directory Web 服务

    我正在尝试构建一个 Net 应用程序来询问 Active Directory 编辑 我需要使用 Web 服务来执行此操作 因为我将使用需要使用 Web 服务的第三方工作流工具从 Sharepoint 工作流与 AD 进行通信 根据我的研究
  • C# 中 DLL 和命名空间的关系

    这里有一个高级问题 今天我花了很多时间自学基本的高级概念 例如 API 静态和动态库 DLL 以及 C 中的编组 获得所有这些知识让我想到了一个看起来非常基本的问题 并且可能表明我对这些概念的理解存在漏洞 我知道的 DLL 可能包含类 这些
  • Wix - 自定义安装目录

    我使用的是 Wix 3 x 用户应该能够选择目标目录 我的Setup wxs目前是这样的 http pastebin com uH1EjbDQ http pastebin com uH1EjbDQ 询问用户自定义目标目录的最简单方法是什么
  • TextBox 焦点的 WinForms 事件?

    我想添加一个偶数TextBox当它有焦点时 我知道我可以用一个简单的方法来做到这一点textbox1 Focus并检查布尔值 但我不想那样做 我想这样做 this tGID Focus new System EventHandler thi
  • 调试内存不足异常

    在修复我制作的小型 ASP NET C Web 应用程序的错误时 我遇到了 OutOfMemoryException 没有关于在哪里查看的提示 因为这是一个编译时错误 如何诊断此异常 我假设这正是内存分析发挥作用的地方 有小费吗 Thank
  • 获取从属性构造函数内部应用到哪个属性的成员?

    我有一个自定义属性 在自定义属性的构造函数内 我想将属性的属性值设置为属性所应用到的属性的类型 是否有某种方式可以访问该属性所应用到的成员从我的属性类内部 可以从 NET 4 5 using CallerMemberName Somethi
  • 事件日志写入错误

    很简单 我想向事件日志写入一些内容 protected override void OnStop TODO Add code here to perform any tear down necessary to stop your serv
  • .Net 中是否有与 HTML 等效的 XmlReader?

    我用过Html敏捷包 http html agility pack net z codeplex过去在 Net 中解析 HTML 但我不喜欢它只使用 DOM 模型 在大型文档和 或具有大量嵌套的文档上 可能会遇到堆栈溢出或内存不足异常 另外
  • 捕获特定的 WebException (550)

    假设我创建并执行一个System Net FtpWebRequest 我可以用catch WebException ex 捕获此请求引发的任何与 Web 相关的异常 但是 如果我有一些逻辑只想在由于以下原因引发异常时执行 550 file
  • 从 .net 应用程序登录 OpenID 站点

    我一直在考虑编写一个小工具来登录 SO 并定期使用一些主题 当前信息更新我的个人资料信息 例如我最新的博客文章或我需要帮助的问题等 为了让它工作 我需要以某种方式从控制台应用程序登录到SO 是否有一个 Net 库可以简化使用原始 http
  • WPF Datagrid 循环/选择具有特定属性的单元格

    全新的 WPF 对 WinForms 非常熟悉 这可能会让过渡变得更加困难 我正在尝试将旧 WinForms 项目中的一些功能移植到 WPF 中作为学习体验 目标是在 DataGrid 中查找与 TextBox 中的字符串匹配的单元格值 我
  • 删除 TableLayoutPanel 中的特定行

    我有 TableLayoutPanel 我以编程方式添加行 用户基本上选择一个属性 然后与一些控件一起显示在表中 我想我在这里有一个一般性的理解问题 我会尽力解释它 每行中的控件之一是 删除 按钮 该按钮应该删除它所在的行 我所做的是将事件
  • 如何检查 NTAccount 对象代表组还是用户?

    使用返回的访问规则时 GetAccessRules True True GetType System Security Principal NTAccount 如何判断每个规则中引用的 NTAccount 对象是用户帐户还是组 Update
  • 检查 DBNull 会引发 StrongTypingException

    我正在使用数据集从数据库中提取数据 一行中的一个字段是NULL 我知道这个 但是 以下 vb net 代码会抛出StrongTypingException 在数据集设计器中自动生成的 get SomeField 方法中 If Not IsD
  • 使用 NServiceBus FileShareDataBus 时清理文件

    我在 NServiceBus 3 中使用 FileShareDataBus 是否支持清理数据总线文件 如果不是 实现此目的的最佳实践是什么 例如如何确保该文件不是错误队列中消息的一部分 鉴于不可能知道消息在错误队列中停留多长时间 我通常会在
  • 什么可以解释托管堆上超过 5,000,000 个 System.WeakReference 实例?

    我一直在针对生产 ASP NET Web 应用程序运行负载测试 并且看到在堆上创建了大量 System WeakReference 在大约 15 分钟内 负载管理堆内存已飙升至大约 3GB 并且我有大约 5 000 000 个对 Syste
  • 在.rdlc报告的底部设置一个文本框

    我在 rdlc 报告中使用 tablix 有一个文本框 其中包含文本 签名 我想将此文本框放置在报告最后一页的底部 就在页脚之前 我已经用谷歌搜索了这个解决方案 但没有找到满意的结果 我的环境是VS2010 framework 4 0 有什

随机推荐

  • SQL 中的动态 Like 语句

    我已经绞尽脑汁思考如何做到这一点有一段时间了 我知道这个网站上的一些天才会找到答案 基本上我正在尝试这样做 SELECT column FROM table WHERE table column LIKE string1 OR table
  • 使用接口作为分部视图的模型类型+数据注释

    我遇到的情况是 复杂的局部视图需要根据局部视图的使用位置进行不同的字段验证 我认为我可以通过使分部视图采用接口作为模型类型并基于该接口实现两个不同的 ViewModel 来解决这个问题 两个 ViewModel 中的数据注释会不同 然后 我
  • 删除如何处理指针常量性?

    我正在读这个问题删除常量指针并想了解更多关于delete行为 现在 根据我的理解 delete expression分两步进行 调用析构函数 然后释放内存 通常通过调用free 通过调用操作符删除 operator delete接受一个vo
  • 在 Yarn 集群上运行的 Spark 作业 java.io.FileNotFoundException:文件不存在,即使文件在主节点上存在

    我对 Spark 相当陌生 我尝试搜索但找不到合适的解决方案 我已经在两个机器上安装了 hadoop 2 7 2 一个主节点和另一个工作节点 我已经通过以下链接设置了集群http javadev org docs hadoop centos
  • 如何使用QTestLib模拟鼠标滚轮事件[Qt5]

    我很高兴使用QTestLib为我的编写测试Qt5基于小部件的用户界面 直到现在 当我试图找到一种模拟鼠标滚轮事件的方法时 似乎并不缺乏特性和便利功能 我看过官方文档 和官方示例但我似乎不知道如何模拟鼠标滚轮事件 这不存在吗 或者我错过了什么
  • 使用 jQuery 插件验证图像尺寸

    我有一个表单 其中验证发生在 jQuery 的验证插件中 我的表单包含两个元素 一个输入类型 文件和一个提交按钮 用户将选择一个图像 如果该图像小于 500 像素 则不会接受该图像 并应显示错误消息 我为此制定了一种新方法 称为宽度 但由于
  • Java 中的 0.0 和 -0.0 (IEEE 754)

    Java 与 IEEE 754 完全兼容 对吗 但我对java如何决定浮点加法和减法的符号感到困惑 这是我的测试结果 double a 1 5 double b 0 0 double c 0 0 System out println b a
  • Q流程问题,流程输出

    我想弄清楚 QProcess 的用途 我查看了 Qt 文档 但没有运气 http doc qt io qt 4 8 qprocess html 问题的例子 示例1 下面的代码有效 include
  • ApplePay iOS 错误 - 此设备无法付款

    将 ApplePay 与我的测试应用程序集成时 出现错误 该设备无法付款 我已经设置了一张要使用的卡 并在销售点终端上对其进行了测试 在那里工作得很好 应该工作 但没有 的代码行是 PKPaymentAuthorizationViewCon
  • 我如何在我的应用程序中共享 apk 文件(发送应用程序本身)

    我正在尝试使用此代码将我的应用程序 apk 文件发送到另一台设备 public static void sendAppItself Activity paramActivity throws IOException PackageManag
  • 如何在python中删除仅包含数字的单词?

    我有一些 Python 文本 由数字和字母组成 像这样的东西 s 12 word word2 从字符串 s 中 我想删除所有包含的单词只有数字 所以我希望结果是 s word word2 这是我的正则表达式 但它适用于字母表 即它用空格替换
  • 当工作区中有多个 xcode 项目时,如何将 cocoapods cordova 插件 src 链接到 .framework 头文件?

    独特的情况是 这是一个 Ionic 应用程序 通过 pod 引入未编译的插件源 问题是编译时 插件标头和实现找不到 framework 的标头 实现 演示 Cordova 应用程序找到 framework 但它们仅位于单个项目中 我已经尝试
  • 计算numpy数组的周长

    我想计算给定 numpy 数组结构的周长 周长是指 numpy 数组中结构的精确周长 该结构可以包括孔 我目前的方法是这样的 import numpy a numpy zeros 6 6 dtype numpy int a 1 5 1 5
  • MKMapView 的用户位置在启动或恢复时错误

    当我重新启动我的应用程序或在很长一段时间后恢复时 MKMapView 用户位置的概念是错误的 并显示我在海中央 我正在使用以下代码 self mapView centerCoordinate self mapView userLocatio
  • Robotframework Listener 抛出“无法访问执行上下文”错误

    为了支持替代的日志记录格式 我开始开发一个自定义 Robotframework 监听器 使用指南中的示例我已经能够复制简单的Python监听器示例 该示例可以使用以下命令成功运行 python exe m robot run listene
  • iOS 如何查找用户电话号码的国家代码?

    我有一个应用程序需要找出用户电话号码的国家 地区代码 我的理解是 我不能只获取用户的电话号码 但是 有没有办法 例如 如果我有美国电话号码来获取国家 地区代码 1 我找到了多个答案 声称使用核心电话可以获取国家 地区代码 1 但我尝试了一些
  • 为什么我收到错误“表达式必须有一个父元素”,如何解决此问题?

    我对 React 比较陌生 我想知道这里的标准是什么 想象一下我有一个像这样的反应路由器
  • 从多个线程调用时 Thread.sleep() 如何工作

    sleep 是 Thread 类的静态方法 从多个线程调用时它是如何工作的 以及它如何确定当前的执行线程 或者可能是一个更通用的问题是如何从不同线程调用静态方法 不会有并发问题吗 它是如何计算出当前的 执行线程 没必要 它只是调用操作系统
  • Android MediaRecorder 流式传输

    是否可以 流式传输 MediaRecorder 的结果 我可以看到的独特方法是 mediaRecorder setOutputFile 接收 FileDescriptor 所以我可以将结果写入文件或通过套接字发送到接收器 我尝试了第二种解决
  • .NET 中与 BinaryFormatter 的向后兼容性

    我们在 C 游戏中使用 BinaryFormatter 来保存用户游戏进度 游戏级别等 我们遇到了向后兼容性的问题 目的 关卡设计师创建活动 关卡和规则 我们更改代码 活动应该仍然可以正常工作 在发布之前的开发过程中 这种情况每天都可能发生