一些陷入向后兼容的问题
这个小例子说明了如果你没有提前注意这个问题,你如何在程序中失去向后兼容性。以及如何更好地控制序列化过程
首先,我们将编写该程序的第一个版本的示例:
版本 1
[Serializable]
class Data
{
[OptionalField]
private int _version;
public int Version
{
get { return _version; }
set { _version = value; }
}
}
而现在,让我们假设在该程序的第二个版本中添加了一个新类。我们需要将它存储在一个数组中。
现在代码看起来像这样:
版本 2
[Serializable]
class NewItem
{
[OptionalField]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
[Serializable]
class Data
{
[OptionalField]
private int _version;
public int Version
{
get { return _version; }
set { _version = value; }
}
[OptionalField]
private List<NewItem> _newItems;
public List<NewItem> NewItems
{
get { return _newItems; }
set { _newItems = value; }
}
}
以及用于序列化和反序列化的代码
private static byte[] SerializeData(object obj)
{
var binaryFormatter = new BinaryFormatter();
using (var memoryStream = new MemoryStream())
{
binaryFormatter.Serialize(memoryStream, obj);
return memoryStream.ToArray();
}
}
private static object DeserializeData(byte[] bytes)
{
var binaryFormatter = new BinaryFormatter();
using (var memoryStream = new MemoryStream(bytes))
return binaryFormatter.Deserialize(memoryStream);
}
那么,当你在 v2 程序中序列化数据并尝试在 v1 程序中反序列化时会发生什么?
你得到一个例外:
System.Runtime.Serialization.SerializationException was unhandled
Message=The ObjectManager found an invalid number of fixups. This usually indicates a problem in the Formatter.Source=mscorlib
StackTrace:
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at Microsoft.Samples.TestV1.Main(String[] args) in c:\Users\andrew\Documents\Visual Studio 2013\Projects\vts\CS\V1 Application\TestV1Part2\TestV1Part2.cs:line 29
at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
为什么?
ObjectManager 具有不同的逻辑来解析数组以及引用和值类型的依赖关系。我们添加了一个新的引用类型数组,在我们的程序集中没有。
当 ObjectManager 尝试解析依赖关系时,它会构建图形。当它看到数组时,它无法立即修复它,因此它会创建一个虚拟引用,然后再修复该数组。
并且由于此类型不在程序集中,因此无法修复依赖项。由于某种原因,它不会从修复的元素列表中删除该数组,最后,它会抛出异常 IncorrectNumberOfFixups
。
这是序列化过程中的一些问题。出于某种原因,它仅对新引用类型的数组无法正常工作。
A Note:
Similar code will work correctly if you do not use arrays with new classes
还有第一种解决方法并保持兼容性?
- 使用新结构而不是类的集合或使用字典(可能的类),因为字典是 keyvaluepair 的集合(它的结构)
- 如果你无法更改旧代码,请使用 ISerializable