序列化一个 HashSet

Fre*_*rik 6 .net c# hash hashset c#-4.0

我正在尝试序列化一个 Hashset,但我没有运气。每当我尝试打开序列化数据时,都会得到一个空的 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当运行时,它打印出:

Printing Hashset:
Printing List:
One
Two
Three
Run Code Online (Sandbox Code Playgroud)

所以 List 工作正常,但 HashSet 返回空。有点卡住了 - 谁能看到我做错了什么?谢谢

Nic*_*nko 6

更新

正如Hans Passant 所说,有简单的解决方法,只需手动调用HashSet.OnDeserialization即可。

var hashset = (HashSet<string>)info.GetValue("hashset", typeof(HashSet<string>));
hashset.OnDeserialization(this);
Run Code Online (Sandbox Code Playgroud)

它还有助于其他通用集合。


据我所知,这可能是HashSet<T>实施中的错误。HashSet正确序列化为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[]));
  }
}
Run Code Online (Sandbox Code Playgroud)

SerializationInfo正确恢复。你也可以自己检查一下,看看:(((System.Collections.Generic.HashSet<string>)(info.m_data[0]))).m_siInfo.m_data[3]但无法恢复其状态:

它所做的只是存储SerializationInfo

protected HashSet(SerializationInfo info, StreamingContext context)
{
  this.m_siInfo = info;
}
Run Code Online (Sandbox Code Playgroud)

您可以检查(hashset).m_siInfo.MemberValues[3],值已被格式化程序正确恢复,但未被“解释” HashSet

类似的问题有Dictionary<TKey,TValue>或例如LinkedList<T>

List<T>(或类似的基于数组的集合,例如Stack<T>)没有问题,因为它们序列化为数组(没有特殊逻辑)。

Hans Passant 发布了解决方法。

恕我直言,BinaryFormatter这并不是真正好的和有效的存储价值的方式。您可以尝试使用DataContractSerializer(它可以处理此类类型)或使用序列化助手,例如 protobuf.net、json.net 等。请参阅为什么二进制序列化比 xml 序列化更快?WCF 绑定使用的序列化和性能测试