2010-11-16 5 views
2

J'essaie de sérialiser un Hashset mais je n'ai pas de chance. Chaque fois que j'essaie d'ouvrir les données sérialisées, j'obtiens un HashSet vide. Cependant, une liste fonctionne bien. Exemple de code:Sérialiser un HashSet

[Serializable()] 
public class MyClass : ISerializable 
{ 
    public MyClass(SerializationInfo info, StreamingContext ctxt) 
    { 
     HashSet<string> hashset = (HashSet<string>)info.GetValue("hashset", typeof(HashSet<string>)); 
     List<string> list = (List<string>)info.GetValue("list", typeof(List<string>)); 
     Console.WriteLine("Printing Hashset:"); 
     foreach (string line in hashset) 
     { 
      Console.WriteLine(line); 
     } 
     Console.WriteLine("Printing List:"); 
     foreach (string line in list) 
     { 
      Console.WriteLine(line); 
     } 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext ctxt) 
    { 
     HashSet<string> hashset = new HashSet<string>(); 
     hashset.Add("One"); 
     hashset.Add("Two"); 
     hashset.Add("Three"); 
     info.AddValue("hashset", hashset); 
     List<string> list = new List<string>(); 
     list.Add("One"); 
     list.Add("Two"); 
     list.Add("Three"); 
     info.AddValue("list", list); 
    } 
} 

Et lors de son exécution, il imprime:

Printing Hashset: 
Printing List: 
One 
Two 
Three 

Ainsi la liste fonctionne très bien, mais le HashSet revient vide. Un peu coincé - quelqu'un peut-il voir ce que je fais mal? Merci

+0

Pourquoi faites-vous vous-même la sérialisation? Pourquoi ne pas utiliser DataContractSerializer? –

Répondre

3

Mise à jour:

Comme Hansstated il Passant sont solution simple, il suffit d'appeler HashSet.OnDeserialization manuellement.

var hashset = (HashSet<string>)info.GetValue("hashset", typeof(HashSet<string>)); 
hashset.OnDeserialization(this); 

Il aide également avec d'autres collections génériques.


Pour autant que je peux voir c'est probablement bogue dans HashSet<T> la mise en œuvre. HashSet sérialisés correctement dans SerializationInfo:

public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 
{ 
    if (info == null) 
    { 
    throw new ArgumentNullException("info"); 
    } 
    info.AddValue("Version", this.m_version); 
    info.AddValue("Comparer", this.m_comparer, typeof(IEqualityComparer<T>)); 
    info.AddValue("Capacity", (this.m_buckets == null) ? 0 : this.m_buckets.Length); 
    if (this.m_buckets != null) 
    { 
    T[] array = new T[this.m_count]; 
    this.CopyTo(array); 
    info.AddValue("Elements", array, typeof(T[])); 
    } 
} 

et SerializationInfo restauré correctement. Vous pouvez également vérifier par vous-même, jetez un oeil à: (((System.Collections.Generic.HashSet<string>)(info.m_data[0]))).m_siInfo.m_data[3] mais ne parvient pas à rétablir son état:

Tout ce qu'il fait est tout simplement magasins SerializationInfo:

protected HashSet(SerializationInfo info, StreamingContext context) 
{ 
    this.m_siInfo = info; 
} 

Vous pouvez vérifier (hashset).m_siInfo.MemberValues[3], les valeurs ont été correcly restaurées par formatter mais non "interprété" par HashSet.

Un problème similaire a Dictionary<TKey,TValue> ou par ex. LinkedList<T>.

List<T> (ou des collections similaires basées sur des matrices telles que Stack<T>) n'a aucun problème car elles sont sérialisées en tant que matrice (sans logique spéciale).

La solution a été publiée par Hans Passant.

IMHO, BinaryFormatter n'est pas vraiment bon et efficace pour stocker des valeurs. Vous pouvez essayer d'utiliser DataContractSerializer (il peut gérer ces types) ou rendez-vous avec des aides de sérialisation tels que protobuf.net, json.net etc. Voir Why is binary serialization faster than xml serialization? et Performance Tests of Serializations used by WCF Bindings

+1

S'il vous plaît sauvegarder votre opinion pourquoi ce n'est pas vraiment bon ou efficace. – leppie

+0

Probablement ce sera un autre post. Si c'est vraiment nécessaire, je peux poster ici ou simplement vous envoyer un email. Juste quelques points: 1.Sérialiser à l'objet disque avec le champ 'int', vous finirez avec ~ 153 octets fichier, car il doit contenir tous les noms de type complet. Comparez-le avec la valeur de 4 octets de 'int' lui-même. 2. Vérifiez l'implémentation de 'BinaryFormatter' ou mesurez-la simplement par rapport à un programme binaire simple. 3. Ne pas oublier les problèmes de compatibilité, donc si vous mettez à jour l'assemblage sur le serveur, vous devez utiliser quelques astuces pour ne pas échouer en désérialisant les anciennes valeurs. –

+0

Si vous êtes intéressé: nous pensons utiliser 'BinaryFormatter' comme notre backend de sérialisation mais nous l'avons trouvé tout à fait non optimal pendant le nombre de tests. Notre système distribué a 10-50 000 nœuds nous avons terminé avec notre propre implémentation en 2007 mais nous envisageons maintenant de passer à ProtoBuffers puisque notre solution est assez similaire. –

2

La différence est que HashSet <> implémente ISerializable, Liste <> ne le fait pas. La solution de contournement consiste à appeler sa méthode OnDeserialization() explicitement, bien que je ne sache pas si c'est la bonne chose à faire.

 var hashset = (HashSet<string>)info.GetValue("hashset", typeof(HashSet<string>)); 
     hashset.OnDeserialization(this); 
     var list = (List<string>)info.GetValue("list", typeof(List<string>)); 
     // etc.. 
+0

Merci, bon à savoir façon facile. J'oublie que 'HashSet ' doit implémenter 'ISerializable.OnDeserialization()' et ne pas le vérifier. –