2011-03-05 4 views
1

J'ai fourni un code minimal pour imiter le scénario. Voici le code:Sérialisation binaire tableau non initialisé de struct

using System; 
using System.IO; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Collections.Generic; 


namespace Serialization 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string[] annotates = { "1", "2"}; 
      Guides[] g1 = new Guides[2]; 
      g1[0].comments = (string[])annotates.Clone(); 
      g1[1].comments = (string[])annotates.Clone(); 

      Guides[] g2 = new Guides[2]; 
      g2[0].comments = (string[])annotates.Clone(); 
      g2[1].comments = (string[])annotates.Clone();//to be commented later 

      arrayStruct arrStr1 = new arrayStruct(); 
      arrStr1.guides_array = g1; 

      arrayStruct arrStr2 = new arrayStruct(); 
      arrStr2.guides_array = g2; 

      using (MoveSaver objSaver = new MoveSaver(@"C:\1.bin")) 
      { 
       MoveAndTime mv1 = new MoveAndTime(); 
       MoveAndTime mv2 = new MoveAndTime(); 
       mv1.MoveStruc = "1"; 
       mv1.timeHLd = DateTime.Now; 
       mv1.arr = arrStr1; 
       objSaver.SaveToFile(mv1); 
       mv2.MoveStruc = "2"; 
       mv2.timeHLd = DateTime.Now; 
       mv2.arr = arrStr2; 
       objSaver.SaveToFile(mv2); 
      } 

      using (MoveSaver svrObj = new MoveSaver()) 
      { 
       List<MoveAndTime> MVTobjs = svrObj.DeSerializeObject(@"C:\1.bin"); 
       foreach (MoveAndTime item in MVTobjs) 
       { 
        Console.WriteLine(item.arr.guides_array[0].comments[0]); 
       } 
      } 
     } 

    } 


    public class MoveSaver : IDisposable 
    { 
     public void Dispose() 
     { 
      if (fs != null) 
      { 
       fs.Close(); 
      } 
     } 
     FileStream fs; 
     StreamWriter sw; 
     public string filename { get; set; } 
     public MoveSaver(string FileName) 
     { 
      this.filename = FileName; 
      fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite); 
     } 

     public MoveSaver() 
     { 

     } 

     ~MoveSaver() 
     { 
      if (fs != null) 
      { 
       fs.Close(); 
      } 

     } 

     public List<MoveAndTime> DeSerializeObject(string filename) 
     { 
      List<MoveAndTime> retList = new List<MoveAndTime>(); 
      MoveAndTime objectToSerialize; 
      Stream stream = File.Open(filename, FileMode.Open); 
      BinaryFormatter bFormatter = new BinaryFormatter(); 
      while (stream.Position != stream.Length) 
      { 
       objectToSerialize = (MoveAndTime)bFormatter.Deserialize(stream); 
       retList.Add(objectToSerialize); 
      } 
      stream.Close(); 
      return retList; 
     } 


     public bool SaveToFile(MoveAndTime moveTime) 
     { 
      try 
      { 
       BinaryFormatter bformatter = new BinaryFormatter(); 
       bformatter.Serialize(fs, moveTime); 
       return true; 
      } 
      catch (Exception) 
      { 
       return false; 
      } 
     } 
    } 

    [Serializable] 
    public struct MoveAndTime 
    { 
     public string MoveStruc; 
     public DateTime timeHLd; 
     public arrayStruct arr; 
    } 

    [Serializable] 
    public struct arrayStruct 
    { 
     public Guides[] guides_array; 
    } 

    [Serializable] 
    public struct Guides 
    { 
     public string[] comments; 
     public string name; 
    } 
} 

Dans ce code, une structure contient plusieurs structures, dont l'une contient un tableau. Essayez le code et il compile bien, mais dans le scénario réel tout le tableau n'est pas rempli, il y aurait donc d'autres éléments de tableau non spécifiés. Pour voir cet effet (en action!) Commentez la ligne g2[1].comments = (string[])annotates.Clone(); et essayez le code maintenant. Vous ferez face à une erreur lors de la désérialisation. Comment puis-je l'éviter? Devrais-je définir la structure contenant le tableau en tant que classe et tous les nouveaux (j'espère que je suis à la recherche d'une solution de type structuré)?

Modifier: I Modifié les structures en classe et en ajoutant chaque instance, cela fonctionne correctement. Voici le code:

using System; 
using System.IO; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Collections.Generic; 

namespace Serialization 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string[] annotates = { "1", "2"}; 
      GuidesClass[] g1 = new GuidesClass[2]; 
      g1[0] = new GuidesClass(); 
      g1[0].comments = (string[])annotates.Clone(); 
      g1[1] = new GuidesClass(); 
      g1[1].comments = (string[])annotates.Clone(); 

      GuidesClass[] g2 = new GuidesClass[2]; 
      g2[0] = new GuidesClass(); 
      g2[0].comments = (string[])annotates.Clone(); 
      g2[1] = new GuidesClass(); 
      //g2[1].comments = (string[])annotates.Clone(); 

      array_cls arrStr1 = new array_cls(); 
      arrStr1.guides_array = g1; 

      array_cls arrStr2 = new array_cls(); 
      arrStr2.guides_array = g2; 

      using (MoveSaver objSaver = new MoveSaver(@"C:\1.bin")) 
      { 
       M_T mv1 = new M_T(); 
       M_T mv2 = new M_T(); 
       mv1.MoveStruc = "1"; 
       mv1.timeHLd = DateTime.Now; 
       mv1.arr = arrStr1; 
       objSaver.SaveToFile(mv1); 
       mv2.MoveStruc = "2"; 
       mv2.timeHLd = DateTime.Now; 
       mv2.arr = arrStr2; 
       objSaver.SaveToFile(mv2); 
      } 

      using (MoveSaver svrObj = new MoveSaver()) 
      { 
       List<M_T> MVTobjs = svrObj.DeSerializeObject(@"C:\1.bin"); 
       foreach (M_T item in MVTobjs) 
       { 
        Console.WriteLine(item.arr.guides_array[0].comments[0]); 
       } 
      } 
     } 

    } 


    public class MoveSaver : IDisposable 
    { 
     public void Dispose() 
     { 
      if (fs != null) 
      { 
       fs.Close(); 
      } 
     } 
     FileStream fs; 
     StreamWriter sw; 
     public string filename { get; set; } 
     public MoveSaver(string FileName) 
     { 
      this.filename = FileName; 
      fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite); 
     } 

     public MoveSaver() 
     { 

     } 

     ~MoveSaver() 
     { 
      if (fs != null) 
      { 
       fs.Close(); 
      } 

     } 

     public List<M_T> DeSerializeObject(string filename) 
     { 
      List<M_T> retList = new List<M_T>(); 
      M_T objectToSerialize; 
      Stream stream = File.Open(filename, FileMode.Open); 
      BinaryFormatter bFormatter = new BinaryFormatter(); 
      while (stream.Position != stream.Length) 
      { 
       objectToSerialize = (M_T)bFormatter.Deserialize(stream); 
       retList.Add(objectToSerialize); 
      } 
      stream.Close(); 
      return retList; 
     } 


     public bool SaveToFile(M_T moveTime) 
     { 
      try 
      { 
       BinaryFormatter bformatter = new BinaryFormatter(); 
       bformatter.Serialize(fs, moveTime); 
       return true; 
      } 
      catch (Exception) 
      { 
       return false; 
      } 
     } 
    } 


    [Serializable] 
    public class M_T 
    { 
     public string MoveStruc; 
     public DateTime timeHLd; 
     public array_cls arr; 
    } 

    [Serializable] 
    public class array_cls 
    { 
     public GuidesClass[] guides_array = new GuidesClass[10]; 
    } 

    [Serializable] 
    public class GuidesClass 
    { 
     public string[] comments; 
     public string name; 
    } 
} 
+1

Quelle est l'erreur? – Oded

+1

Par curiosité, ** pourquoi ** sont-ils des structs? Ils ne ressemblent pas à des structures typiques (surtout parce qu'ils sont mutables), et sans une bonne raison, ils devraient être des classes par défaut. –

+0

@Oded: "Le flux d'entrée n'est pas un format binaire valide" et, dans le code actuel, l'erreur concerne l'en-tête binaire. Essayez de compiler le code avec la ligne commentée et vous verrez l'erreur. –

Répondre

1

J'ai eu un jeu avec le code, et je peux repro pour struct et class; Finalement, je soupçonne que le problème ici est que BinaryFormatter n'est pas conçu pour être tout à fait appendable, c'est-à-dire que je soupçonne qu'il interprète mal les données de l'objet suivant comme faisant partie du courant.

FWIW, ceux vraiment ne devraient pas être structs, et vous êtes (IMO) sur-ingénierie horriblement le code de sauvegarde/chargement. Je l'ai modifié pour que la méthode de sauvegarde prenne un List<MoveAndTime>, et le code de chargement a renvoyé un simpleList<MoveAndTime> (c'est-à-dire qu'il n'y a qu'un seul objet le plus à l'extérieur dans le flux) et cela a bien fonctionné, soutenant ma théorie.

Si vous avez besoin d'être en mesure d'ajouter progressivement des objets individuels, je suggère protobuf-net; vous pouvez utiliser SerializeWithLengthPrefix pour écrire un objet d'une manière appropriée pour ajouter, et DeserializeWithLengthPrefix pour lire un objet seul du flux, ou DeserializeItems pour lire (en tant que séquence) tous les éléments du flux.


Par exemple, en utilisant v2 protobuf-net (uniquement disponible sous forme de code pour le moment):

using System; 
using System.Collections.Generic; 
using System.IO; 
using ProtoBuf; 


namespace Serialization 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string[] annotates = { "1", "2" }; 
      Guides[] g1 = new Guides[2]; 
      g1[0].comments = (string[])annotates.Clone(); 
      g1[1].comments = (string[])annotates.Clone(); 

      Guides[] g2 = new Guides[2]; 
      g2[0].comments = (string[])annotates.Clone(); 
      g2[1].comments = (string[])annotates.Clone();//to be commented later 

      arrayStruct arrStr1 = new arrayStruct(); 
      arrStr1.guides_array = g1; 

      arrayStruct arrStr2 = new arrayStruct(); 
      arrStr2.guides_array = g2; 

      using (Stream file = File.Create(@"1.bin")) 
      { 
       MoveAndTime mv1 = new MoveAndTime(); 
       MoveAndTime mv2 = new MoveAndTime(); 
       mv1.MoveStruc = "1"; 
       mv1.timeHLd = DateTime.Now; 
       mv1.arr = arrStr1; 
       Serializer.SerializeWithLengthPrefix(file, mv1, PrefixStyle.Base128, Serializer.ListItemTag); 
       mv2.MoveStruc = "2"; 
       mv2.timeHLd = DateTime.Now; 
       mv2.arr = arrStr2; 
       Serializer.SerializeWithLengthPrefix(file, mv2, PrefixStyle.Base128, Serializer.ListItemTag); 
      } 

      using (Stream file = File.OpenRead(@"1.bin")) 
      { 
       List<MoveAndTime> MVTobjs = Serializer.Deserialize<List<MoveAndTime>>(file); 
       foreach (MoveAndTime item in MVTobjs) 
       { 
        Console.WriteLine(item.arr.guides_array[0].comments[0]); 
       } 
      } 
     } 

    } 

    [ProtoContract] 
    public struct MoveAndTime 
    { 
     [ProtoMember(1)] 
     public string MoveStruc; 
     [ProtoMember(2)] 
     public DateTime timeHLd; 
     [ProtoMember(3)] 
     public arrayStruct arr; 
    } 

    [ProtoContract] 
    public struct arrayStruct 
    { 
     [ProtoMember(1)] 
     public Guides[] guides_array; 
    } 

    [ProtoContract] 
    public struct Guides 
    { 
     [ProtoMember(1)] 
     public string[] comments; 
     [ProtoMember(2)] 
     public string name; 
    } 
} 

v1 (disponible en dll, plus stable) fonctionnerait presque le même, mais doesn ne supporte pas les structures - seulement les classes.

Mais à souligner:

  • ils devraient être des classes
  • champs publics sont une mauvaise idée
+0

pour le code de travail, voir la section d'édition. –