Конфигурирование объектов сериализации

36

Чтобы сделать объект доступным для служб сериализации .NET, понадобится только декорировать каждый связанный класс (или структуру) атрибутом [Serializable]. Если выясняется, что некоторый тип имеет члены-данные, которые не должны (или не могут) участвовать в схеме сериализации, можно пометить такие поля атрибутом [NonSerialized]. Это помогает сократить размер хранимых данных, при условии, что в сериализуемом классе есть переменные-члены, которые не следует "запоминать" (например, фиксированные значения, случайные значения, кратковременные данные и т.п.).

Для начала создадим новое консольное приложение. Добавим в него новый класс по имени Radio, помеченный атрибутом [Serializable], у которого исключается одна переменная-член (radioID), помеченная атрибутом [NonSerialized] и потому не сохраняемая в специфицированном потоке данных:

[Serializable]
    public class Radio
    {
        public bool hasTweeters;
        public bool hasSubWoofers;
        public double[] stationPresets;
        [NonSerialized]
        public string radioID = "XF-552RF6";
    }

Затем добавим два дополнительных типа, представляющих базовые классы JamesBondCar и Car (оба они также помечены атрибутом [Serializable]), и определим в них следующие поля данных:

[Serializable]
public class Car
{
   public Radio theRadio = new Radio();
   public bool isHatchBack;
}

[Serializable]
public class JamesBondClass : Car
{
   public bool canFly;
   public bool canSubmerge;
}

Имейте в виду, что атрибут [Serializable] не может наследоваться от родительского класса. Поэтому при наследовании типа, помеченного [Serializable], дочерний класс также должен быть помечен [Serializable] или же его нельзя будет сохранить в потоке. Фактически все объекты в графе объектов должны быть помечены атрибутом [Serializable]. Попытка сериализовать несериализуемый объект с использованием BinaryFormatter или SoapFormatter приводит к исключению SerializationException во время выполнения.

Обратите внимание, что в каждом из этих классов поля данных определены как public, это сделано для упрощения примера. Конечно, приватные данные, представленные общедоступными свойствами, были бы более предпочтительны с точки зрения ООП. Также для простоты в этих типах не определились никакие специальные конструкторы, и потому все неинициализированные поля данных получат ожидаемые значения по умолчанию.

Оставив в стороне принципы ООП, можно спросить: какого определения полей данных типа требуют различные форматеры, чтобы сериализовать их в поток? Ответ такой: в зависимости от обстоятельств. Если вы сохраняете состояние объекта, используя BinaryFormatter или SoapFormatter, то разницы никакой. Эти типы запрограммированы для сериализации всех сериализуемых полей типа, независимо от того, представлены они общедоступными полями, приватными полями или приватными полями с соответствующими общедоступными свойствами.

Однако вспомните, что если есть элементы данных, которые не должны сохраняться в графе объектов, можно выборочно пометить общедоступные или приватные поля атрибутом [NonSerialized], как сделано со строковыми полями в типе Radio.

Однако ситуация существенно меняется, если вы собираетесь применять тип XmlSerializer. Этот тип будет сериализовать только сериализуемые общедоступные поля данных или приватные поля, представленные общедоступными свойствами. Приватные данные, не представленные свойствами, просто игнорируются. Например, рассмотрим следующий сериализуемый тип Person:

[Serializable]
public class Person
{
   // Общедоступное поле
   public bool isAlive = true;
   // Приватное поле
   private int personAge = 21;
   // Общедоступное свойство/приватные данные.
   private string fName = string.Empty;
   public string FirstName
   {
      get { return fName; }
      set { fName = value; }
}

При обработке BinaryFormatter или SoapFormatter обнаружится, что поля isAlive, personAge и fName сохраняются в выбранном потоке. Однако XmlSerializer не сохранит значения personAge, поскольку эта часть приватных данных не инкапсулирована в свойство. Чтобы сохранять возраст персоны с помощью XmlSerializer, это поле понадобится определить как public или же инкапсулировать его в общедоступном свойстве.

Пройди тесты
Лучший чат для C# программистов