一些陷入向后兼容的问题

这个小例子说明了如果你没有提前注意这个问题,你如何在程序中失去向后兼容性。以及如何更好地控制序列化过程

首先,我们将编写该程序的第一个版本的示例:

版本 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