C# Language Quelques pièges dans la compatibilité ascendante


Exemple

Ce petit exemple montre comment vous pouvez perdre la rétrocompatibilité dans vos programmes si vous ne vous en souciez pas à l’avance. Et des moyens de mieux contrôler le processus de sérialisation

Au début, nous allons écrire un exemple de la première version du programme:

Version 1

[Serializable]
class Data
{
    [OptionalField]
    private int _version;
    
    public int Version
    {
        get { return _version; }
        set { _version = value; }
    }
}

Et maintenant, supposons que dans la deuxième version du programme ajouté une nouvelle classe. Et nous devons le stocker dans un tableau.

Maintenant, le code ressemblera à ceci:

Version 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; }
    }
}

Et code pour sérialiser et désérialiser

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);
}

Et alors, que se passerait-il lorsque vous sérialisez les données dans le programme de v2 et que vous essayez de les désérialiser dans le programme de v1?

Vous obtenez une exception:

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()

Pourquoi?

ObjectManager a une logique différente pour résoudre les dépendances pour les tableaux et pour les types de référence et de valeur. Nous avons ajouté un tableau de nouveau le type de référence qui est absent dans notre assemblage.

Lorsque ObjectManager tente de résoudre des dépendances, il génère le graphique. Lorsqu'il voit le tableau, il ne peut pas le réparer immédiatement, de sorte qu'il crée une référence factice, puis corrige le tableau ultérieurement.

Et puisque ce type n'est pas dans l'assemblage et que les dépendances ne peuvent pas être corrigées. Pour une raison quelconque, il ne supprime pas le tableau de la liste des éléments pour les correctifs et à la fin, il génère une exception «IncorrectNumberOfFixups».

Ce sont des «pièges» dans le processus de sérialisation. Pour une raison quelconque, il ne fonctionne pas correctement uniquement pour les tableaux de nouveaux types de référence.

A Note:
Similar code will work correctly if you do not use arrays with new classes

Et le premier moyen de résoudre ce problème et de maintenir la compatibilité?

  • Utiliser une collection de nouvelles structures plutôt que des classes ou utiliser un dictionnaire (classes possibles), car un dictionnaire est une collection de keyvaluepair (sa structure)
  • Utilisez ISerializable, si vous ne pouvez pas changer l'ancien code