2010-11-16 3 views
2

Я пытаюсь сериализовать Hashset, но мне не повезло. Всякий раз, когда я пытаюсь открыть сериализованные данные, я получаю пустой 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 

Так что список работает отлично, но HashSet возвращается пустым. Немного застрял - может ли кто-нибудь увидеть, что я делаю неправильно? Благодаря

+0

Зачем вы сериализуете себя? Почему бы не использовать DataContractSerializer? –

ответ

3

Update:

Как Hans Passantstated есть простое решение, просто позвоните HashSet.OnDeserialization вручную.

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

Он также помогает с другими коллекциями Generic.


Насколько я могу видеть, что это, вероятно, ошибка в 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[])); 
    } 
} 

и 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; 
} 

Вы можете проверить (hashset).m_siInfo.MemberValues[3] значение было correcly восстановлено форматировщиком но не «интерпретируется» HashSet.

Аналогичная проблема имеет Dictionary<TKey,TValue> или, например, LinkedList<T>.

List<T> (или аналогичные массивы на основе массива, такие как Stack<T>) не имеет проблем, поскольку они сериализованы как массив (без специальной логики).

Обходной путь был отправлен Хансом Пассантом.

IMHO, BinaryFormatter не очень хороший и эффективный способ хранения ценностей. Вы можете попробовать использовать DataContractSerializer (он может обрабатывать такие типы) или идти с сериализации хелперов, такие как protobuf.net, json.net и т.д. См Why is binary serialization faster than xml serialization? и Performance Tests of Serializations used by WCF Bindings

+1

Пожалуйста, подтвердите свое мнение, почему это не очень хорошо или эффективно. – leppie

+0

Наверное, это будет другой пост. Если это действительно нужно, я могу опубликовать здесь или просто отправить вам электронное письмо. Всего несколько пунктов: 1.Сериализуйте дискобъект с полем 'int', вы закончите файл ~ 153 байта, так как он должен содержать все полные имена типов. Сравните его с 4 байтовым значением 'int'. 2. Проверьте реализацию «BinaryFormatter» или просто измерьте ее производительность в сравнении с двоичной записью простого значения. 3. Не забывайте о проблемах совместимости, поэтому, если вы обновляете сборку на сервере, вам нужно использовать некоторые трюки, чтобы не сработать с десериализацией старых значений. –

+0

В случае, если вам интересно: мы думаем об использовании 'BinaryFormatter' в качестве нашего сервера сериализации, но обнаружили, что он не является оптимальным во время тестирования. Наша распределенная система насчитывает 10-50 000 узлов, в конце концов мы закончили свой собственный имидж в 2007 году, но подумали о переходе на ProtoBuffers сейчас, так как наше решение очень похоже на него. –

2

Разница заключается в том, что HashSet <> реализует ISerializable, Список <> не делает. Обходным решением является вызов его метода 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.. 
+0

Спасибо, приятно знать, как легко. Я забыл, что 'HashSet ' должен реализовать 'ISerializable.OnDeserialization()' и не проверять его. –

Смежные вопросы