【问题标题】:Serializing a HashSet序列化 HashSet
【发布时间】:2011-05-10 17:23:43
【问题描述】:

我正在尝试序列化一个 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);
    }
}

运行时会打印出来:

Printing Hashset:
Printing List:
One
Two
Three

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

【问题讨论】:

  • 为什么要自己做序列化?为什么不使用 DataContractSerializer ?

标签: c# .net hash c#-4.0 hashset


【解决方案1】:

更新

作为 Hans Passant stated 有简单的解决方法,只需手动调用 HashSet.OnDeserialization

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

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


据我所知,这可能是HashSet&lt;T&gt; 实现中的错误。 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[]));
  }
}

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

它所做的只是存储SerializationInfo

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

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

类似的问题有Dictionary&lt;TKey,TValue&gt; 或例如LinkedList&lt;T&gt;.

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

Hans Passant 发布了解决方法。

恕我直言,BinaryFormatter 并不是存储价值的真正好和有效的方式。您可以尝试使用DataContractSerializer(它可以处理此类类型)或使用protobuf.net、json.net等序列化助手。请参阅Why is binary serialization faster than xml serialization?Performance Tests of Serializations used by WCF Bindings

【讨论】:

  • 请支持您的意见,为什么它不是很好或没有效率。
  • 这可能是另一篇文章。如果真的需要,我可以在这里发帖或直接发邮件给你。只是几点: 1. 使用int 字段序列化到磁盘对象,您将以~153 字节文件结尾,因为它必须包含所有完整类型名称。将其与 int 本身的 4 字节值进行比较。 2. 检查BinaryFormatter 实现或仅测量它与纯值二进制写入器相比的性能。 3. 不要忘记兼容性问题,所以如果你在服务器上更新程序集,你必须使用一些技巧来避免反序列化旧值失败。
  • 如果您感兴趣:我们考虑使用BinaryFormatter 作为我们的序列化后端,但在多次测试中发现它并不是最优的。我们的分布式系统有 10-50 000 个节点,我们在 2007 年结束了自己的实施,但现在考虑切换到 ProtoBuffers,因为我们的解决方案与它非常相似。
  • 谢谢 - 我刚刚在序列化 HashSet 时调用了 ToList()...
  • @leppie,检查 Marc Gravell 在为什么二进制序列化比 xml 序列化更快? bit.ly/cVmpvG
【解决方案2】:

不同之处在于 HashSet 实现了 ISerializable,而 List 没有。解决方法是显式调用其 OnDeserialization() 方法,尽管我不确定这样做是否正确。

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

【讨论】:

  • 谢谢,很高兴知道简单的方法。我忘记了HashSet&lt;T&gt; 必须实现ISerializable.OnDeserialization() 并且不要检查它。
猜你喜欢
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
相关资源
最近更新 更多