关于 .NET 2.0 Subset 升到 .NET 4.0 后对象的序列化数据不兼容的问题(InvalidCastException: 对象必须实现 IConvertible 接口)

问题

由于需要将 Unity 2018.3.12f1 升级为 Unity 2019.2.17f1,必须将 C# 的版本由 .NET 2.0 Subset 升级为 .Net 4.0。程序中有一处,需要将类已经序列化数据读出,然后再反序列化,从而实现整个类的直接存取。但由于类是在 .NET 2.0 Subset 下进行序列化的,读取时在 .NET 2.0 Subset 自然完全正常,但在 .Net 4.0 下报错:

InvalidCastException: Object must implement IConvertible.
System.Convert.ChangeType (System.Object value, System.Type conversionType, System.IFormatProvider provider) (at <567df3e0919241ba98db88bec4c6696f>:0)
(后文省略)

解决办法【不算良方】:更新序列化数据

经过验证,序列化和反序列化部分(基于 BinaryFormatter 类)的代码在 C# 升级前后并没有出现任何兼容性问题,无需修改直接沿用即可,也不需要按错误提示继承 IConvertible 接口。但序列化数据不向下兼容,需要更新,此后方可解决。

在目前我测试的办法,只有在新版 C# 环境( .Net 4.0)重新序列化,替换原来的序列化数据,才能让反序列化正常进行。

如果已经序列化的数据十分重要或不可再生,则需要回到原版 C# 环境( .Net 2.0 Subset),反序列化到类的实例,再将该类的实例的成员的值以某种形式分别保存出来(当然不能再通过直接序列化类的办法)。然后返回新版 C# 环境( .Net 4.0),实例化该类,分别读入保存的每个值(相当于手动把成员分别赋值一遍)。最后再在新环境下重新序列化,替换原来的序列化数据。

尝试过用 WinHex 对比新旧版数据找规律,然后自己写个类似于格式转换工具的东西。但由于我自己的文件比较少,重新序列化也不算麻烦,而且发现的规律比较复杂(不知道是不是我的类太复杂的缘故)也懒得弄了,估计没等工程建起来早就手动改完了(我错了,手动搞了一上午…)。发现的一些规律写在后文。【以后若有牛人闲来无事搞出了这么个工具,请务必联系我,这里将换成宁的链接。】

原因分析

在 C# 的不同版本中,同一个类的同一个实例也会被认为是不同的,甚至是在类的层面上的不同。故在反序列化时会发生类型不同的错误,不能转换。

探索过程

最开始以为是代码在升级前后发生了不兼容的问题。后来怎么改都不对,突然意识到是不是序列化数据出的问题。在新版下重新序列化,并用未修改的代码读取,没有抛出任何异常,读出来的数据也完全正确,从而原因找到。

尝试过直接修改旧版序列化文件。由于是字节流,显然用文本文档等文本编辑工具作为文档直接编辑不可取。用 WinHex 打开,对比多组同一个类的同一个实例的新、旧版序列化数据,发现新版序列化数据,始终比旧版序列化数据长35个字节。但这35个字节,分布在文件的多处,尤其是文件头部和尾部,并不是连续在一处;且新版较旧版有多处细小变化,比如某个字节的值突然改变,但其附近并未变化,若不仔细观察极难发现;再者就是其中的 2.0.0.0 版本号都变为了 4.0.0.0,统一替换即可。(注:上述数据仅仅用我自己的类测试了,不知是否具有一般性。)

另附 .NET 序列化对象的三种方法

  1. 二进制序列化:对象序列化之后是二进制形式的,通过 BinaryFormatter 类来实现的,此类位于System.Runtime.Serialization.Formatters.Binary 命名空间下。

  2. SOAP序列化:对象序列化之后的结果符合 SOAP 协议,也就是可以通过 SOAP 协议传输,通过 System.Runtime.Serialization.Formatters.Soap 命名空间下的 SoapFormatter 类来实现的。

  3. XML序列化:对象序列化之后的结果是XML形式的,通过 XmlSerializer 类来实现的,此类位于 System.Xml.Serialization 命名空间下。XML序列化不能序列化私有数据。

详情参考:https://www.cnblogs.com/deepalley/p/10287217.html

我在本文中遇到问题的序列化和反序列化方法,是基于二进制序列化(BinaryFormatter)的。其他方法并未涉及也未测试。

你可能感兴趣的:(C#)