一些陷入向後相容的問題

這個小例子說明了如果你沒有提前注意這個問題,你如何在程式中失去向後相容性。以及如何更好地控制序列化過程

首先,我們將編寫該程式的第一個版本的示例:

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