Serializing a HashSet

后端 未结 2 730
暗喜
暗喜 2020-12-16 05:26

I\'m trying to serialize a Hashset but I\'m having no luck. Whenever I try to open the serialized data, I get an empty HashSet. However, a List works fine. Example code:

相关标签:
2条回答
  • 2020-12-16 06:08

    The difference is that HashSet<> implements ISerializable, List<> doesn't. The workaround is to call its OnDeserialization() method explicitly, albeit that I'm not sure whether that's the right thing to do.

            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 讨论(0)
  • 2020-12-16 06:25

    Update:

    As Hans Passant stated there are simple workaround, just call HashSet.OnDeserialization manually.

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

    It also helps with other Generic collections.


    As far as I can see this is probably bug in HashSet<T> implementation. HashSet correctly serialized into 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[]));
      }
    }
    

    and SerializationInfo correctly restored. You can check also by yourself, take a look to: (((System.Collections.Generic.HashSet<string>)(info.m_data[0]))).m_siInfo.m_data[3] but fails to restore its state:

    All it do is simply stores SerializationInfo:

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

    You can check (hashset).m_siInfo.MemberValues[3], values was correcly restored by formatter but not "interpreted" by HashSet.

    Similar problem has Dictionary<TKey,TValue> or e.g. LinkedList<T>.

    List<T> (or similar array based collections such as Stack<T>) has no problem since they serialized as array (without special logic).

    Workaround was posted by Hans Passant.

    IMHO, BinaryFormatter is not really good and efficient way to store values. You can try to use DataContractSerializer (it can handle such types) or go with serialization helpers such as protobuf.net, json.net etc. See Why is binary serialization faster than xml serialization? and Performance Tests of Serializations used by WCF Bindings

    0 讨论(0)
提交回复
热议问题