C# Исключение десериализации для объекта, унаследованного от словаря - PullRequest
2 голосов
/ 24 марта 2020

Почему я получаю исключение десериализации "Конструктор для десериализации объекта типа 'SerializeTest.TryMe' не найден" в первой реализации класса, но не во второй?

1-я реализация:

[Serializable]
public class TryMe : IDictionary<int, string>
{
    Dictionary<int, string> _dictionary = new Dictionary<int, string>();
    public TryMe() {}

    public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
    {
        return _dictionary.GetEnumerator();
    }
    // all other IDictionary members are implemented below...
    ......
}

2-я реализация:

[Serializable]
public class TryMe : Dictionary<int, string>
{
    public int Dummy { get; set; } = 5;
    public TryMe() {}
}

Код сериализации / десериализации:

var tryMe = new TryMe();
tryMe[5] = "a"; tryMe[9] = "b"; tryMe[4] = "c";
using (var fs = new FileStream(@"D:\11.obj", FileMode.Create))
{
    var formatter = new BinaryFormatter();
    formatter.Serialize(fs, tryMe);
}

tryMe = null;
using (var fs = new FileStream(@"D:\11.obj", FileMode.Open))
{
    var formatter = new BinaryFormatter();
    tryMe = (TryMe)formatter.Deserialize(fs); // Exception here with 2nd implementation of TryMe!!
}

Также возникает проблема, связанная с сериализацией, когда я должен предпочесть наследовать IDictionary и когда Dictionary?
Есть ли канонический способ / правило для этого?

1 Ответ

1 голос
/ 25 марта 2020

Вопрос:

Почему я получаю исключение десериализации «Не найден конструктор для десериализации объекта типа 'SerializeTest.TryMe'" в первой реализации класса, а не во второй?

Ответ:

Документация интерфейса ISerializable гласит:

Интерфейс ISerializable подразумевает конструктор с подписью constructor (SerializationInfo information, StreamingContext context). Во время десериализации текущий конструктор вызывается только после десериализации данных в SerializationInfo форматером.

(Давайте назовем constructor (SerializationInfo information, StreamingContext context) конструктором десериализации.)

Итак, из документации мы знаем, что для классов, которые реализуют ISerializable, обязателен конструктор десериализации.

Класс Dictionary<TKey, TValue> реализует ISerializable и ваш класс TryMe (из второго образец) наследует Dictionary<TKey, TValue>. Во время десериализации десериализатор рассматривает TryMe как класс, который реализует ISerializable и ищет конструктор десериализации. Десериализатор не может найти его и выдает исключение.

Интерфейс IDictionary<TKey, TValue> не реализует ISerializable, поэтому конструктор десериализации не требуется. В этом случае TryMe может быть десериализовано без конструктора десериализации. Исключение не выдается.


Вопрос:

Кроме проблемы с сериализацией, когда я предпочитаю наследовать IDictionary и когда словарь?

Есть ли канонический способ / правило для этого?

Ответ:

Для сериализации можно наследовать либо Dictionary<TKey, TValue>, либо IDictionary<TKey, TValue>. Но если вы наследуете Dictionary<TKey, TValue>, к вашему классу должен быть добавлен конструктор десериализации.

Когда вы предпочитаете наследовать IDictionary<TKey, TValue>, а когда Dictionary<TKey, TValue>? Это зависит от вашей проблемы. решать. В общем случае мы можем рассмотреть два случая:

  1. Если вам нужно реализовать класс с пользовательской IDictionary<TKey, TValue> функциональностью и функциональностью Dictionary<TKey, TValue>, не удовлетворяющих вашим требованиям, чем вы предпочитаете наследовать IDictionary<TKey, TValue> ,
  2. Если функциональность Dictionary<TKey, TValue> удовлетворяет вашим требованиям и, при необходимости, вам нужно изменить или добавить к ней свою собственную функциональность, чем вы предпочитаете наследовать Dictionary<TKey, TValue>.

Если Вы реализуете IDictionary<TKey, TValue>, делегируя соответствующие методы Dictionary<TKey, TValue>, как в первом примере, что вполне вероятно, что вам не нужно реализовывать IDictionary<TKey, TValue>. В этом случае вы должны наследовать Dictionary<TKey, TValue>.

// Sample, when it is better to inherit Dictionary<TKey, TValue> 
// instead of implementing IDictionary<TKey, TValue>.
[Serializable]
public class TryMe : IDictionary<int, string>
{
    Dictionary<int, string> _dictionary = new Dictionary<int, string>();

    public IEnumerator<KeyValuePair<int, string>> GetEnumerator()
    {
        return _dictionary.GetEnumerator();
    }

    public string this[key]
    {
        get { return _dictionary[key]; }
    }

    // Other IDictionary<TKey, TValue> members are implemented the same way.
}
...