
- •А.А. Волосевич
- •2. Базовые технологии платформы .Net 5
- •2. Базовые технологии платформы .Net 4
- •2. Базовые технологии платформы .Net
- •2.1. Работа с Числами
- •2.2. Дата и время
- •2.3. Работа со строками и текстом
- •2.4. Преобразование информации
- •2.5. Отношения равенства и порядка
- •Сравнение для выяснения равенства
- •Сравнение для выяснения порядка
- •2.6. Жизненный цикл объектов
- •Алгоритм «сборки мусора»
- •Финализаторы и интерфейс iDisposable
- •2.7. Перечислители и итераторы
- •2.8. Интерфейсы стандартных коллекций
- •2.9. Массивы и класс system.Array
- •2.10. Типы для работы с коллекциями-списками
- •2.11. Типы для работы с коллекциями-множествами
- •2.12. Типы для работы с коллекциями-словарями
- •2.13. Типы для создания пользовательских коллекций
- •2.14. Технология linq to objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •2.15. Работа с объектами файЛовой системы
- •2.16. Ввод и вывод информации
- •Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •Адаптеры потоков
- •2.17. Основы xml
- •2.18. Технология linq to xml
- •Создание, сохранение, загрузка xml
- •Запросы, модификация и трансформация xml
- •Пространства имен xml
- •2.19. ДОполнительные возможности обработки xml
- •2.20. Сериализация
- •Сериализация времени выполнения
- •Сериализация контрактов данных
- •2.21. Состав и взаимодействие сборок
- •2.22. Метаданные и получение информации о типах
- •2.23. Позднее связывание и кодогенерация
- •2.24. Динамические типы
- •2.25. Атрибуты
- •2.26. Файлы конфигуРации
- •2.27. Основы мНогопоточноГо программирования
- •2.28. Синхронизация потоков
- •2.29. Библиотека параллельных расширений
- •Параллелизм на уровне задач
- •Параллелизм при императивной обработке данных
- •Параллелизм при декларативной обработке данных
- •Обработка исключений и отмена выполнения задач
- •Коллекции, поддерживающие параллелизм
- •2.30. Асинхронный вызов методов
- •2.31. Процессы и домены
- •2.32. Безопасность
- •Разрешения на доступ
- •Изолированные хранилища
- •Криптография
- •2.33. Диагностика
2.20. Сериализация
Сериализация ‑ это процесс, при котором данные объекта переносятся из памяти в поток данных для сохранения. Десериализация – обратный процесс, заключающийся в восстановлении состояния объекта из потока. При выполнении сериализации следует учитывать несколько нетривиальных моментов. Например, сохранение полей объекта требует сохранения всех данных агрегируемых объектов – то есть требуется сохранить граф зависимых объектов.
Платформа .NET предлагает три основных механизма сериализации:
1. Сериализация времени выполнения.
2. Сериализация контрактов данных.
3. XML-сериализация.
При рассмотрении примеров сериализации будем использовать классы, описывающие одного студента и группу студентов.
public class Student : IComparable<Student>
{
public string Name { get; set; }
public int Age { get; set; }
public double GPA { get; set; } // Grade Point Average, средний балл
public int CompareTo(Student other)
{
return GPA.CompareTo(other.GPA);
}
}
public class Group : Collection<Student>
{
public Student BestStudent; // поле, а не свойство
public double GPA { get; private set; }
public double CalculateGPA()
{
return GPA = Items.Select(student => student.GPA).Average();
}
public Student FindTheBest()
{
return BestStudent = Items.Max();
}
}
// пример использования классов Student и Group
var group = new Group {
new Student {Name = "Smirnov", Age = 18, GPA = 9},
new Student {Name = "Ivanova", Age = 20, GPA = 6.67},
new Student {Name = "Kuznetsov", Age = 18, GPA = 6},
new Student {Name = "Sokolov", Age = 20, GPA = 7.67},
new Student {Name = "Lebedeva", Age = 20, GPA = 9}};
Сериализация времени выполнения
Основные классы, связанные с сериализацией времени выполнения (runtime serialization), размещены в пространствах имён с префиксом System.Runtime.Serialization. Например, сериализацию в двоичном формате обеспечивают классы из System.Runtime.Serialization.Formatters.Binary.
Сериализация времени выполнения применима только к объектам сериализуемых типов. Сериализуемый тип – это тип, помеченный атрибутом [Serializable]1, у которого все поля имеют сериализумый тип. Все базовые типы платформы .NET являются сериализуемыми. Если планируется сериализация объекта group, необходимо добавить атрибут [Serializable] к классам Group и Student. Сериализация некоторых полей может не иметь смысла (например, эти поля вычисляются при работе с объектом или хранят конфиденциальные данные). Для таких полей можно применить атрибут [NonSerialized].
Изменим код классов Group и Student с учетом вышесказанного:
[Serializable]
public class Student : IComparable<Student>
{
// элементы класса не показаны
}
[Serializable]
public class Group : Collection<Student>
{
[NonSerialized] // атрибут применяется к полю, а не к свойству
public Student BestStudent;
// неизменившиеся элементы класса не показаны
}
Сериализуемые типы можно сохранить в поток в различных форматах. Платформа .NET имеет классы, поддерживающие двоичный формат и формата SOAP1. При необходимости разработчик может создать класс для поддержки собственного формата сериализации.
Осуществим сериализацию объекта group в двоичном формате. Используем для этого экземплярный метод Serialize() класса BinaryFormatter. Метод принимает два аргумента: поток сериализации и сериализуемый объект:
var formatter = new BinaryFormatter();
using (Stream s = File.Create("group.dat"))
{
formatter.Serialize(s, group);
}
Метод Deserialize() класса BinaryFormatter выполняет десериализацию:
using (Stream s = File.OpenRead("group.dat"))
{
group = (Group)formatter.Deserialize(s);
}
Метод десериализации размещает объект в памяти и возвращает ссылку на него. При этом конструкторы типа не вызываются. Это может стать проблемой, если нужна особая инициализация объекта и восстановление [NonSerialized]-полей. Тип может реализовать интерфейс IDeserializationCallback, который содержит единственный метод OnDeserialization(), вызываемый автоматически после десериализации объекта.
[Serializable]
public class Group : Collection<Student>, IDeserializationCallback
{
// после десериализации заполним поле BestStudent
// CLR не поддерживает работу с параметром sender
void OnDeserialization(object sender)
{
FindTheBest();
}
// неизменившиеся элементы класса не показаны
}
Альтернативой IDeserializationCallback являются атрибуты [OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized], применимые к методам. Помеченные методы вызываются автоматически до и после сериализации или десериализации соответственно. Метод, который помечен одним из указанных атрибутов, должен принимать в качестве аргумента объект класса StreamingContext1 и не возвращать значений. Каждый из атрибутов может применяться только к одному методу в типе.
[Serializable]
public class Group : Collection<Student>
{
[OnDeserialized]
private void AfterDeserialization(StreamingContext context)
{
FindTheBest();
}
// неизменившиеся элементы класса не показаны
}
Атрибут [OptionalField] применяется к полю и подавляет при десериализации генерацию исключения, если помеченное поле не найдено в потоке данных. Это позволяет сохранять «старые» объекты, затем модифицировать тип, расширяя состав его полей, и десериализовать данные в «новые» объекты типа.
Если программиста не устраивает способ организации потока сериализуемых данных, он может повлиять на этот процесс, реализуя в сериализуемом типе интерфейс ISerializable:
public interface ISerializable
{
void GetObjectData(SerializationInfo info,
StreamingContext context);
}
Интерфейс позволяет выполнить любые действия, связанные с формированием данных для сохранения. Метод GetObjectData() вызывается CLR автоматически при выполнении сериализации. Реализация метода подразумевает заполнение объекта SerializationInfo набором данных вида «ключ-значение», которые (обычно) соответствуют полям сохраняемого объекта. Класс SerializationInfo содержит перегруженный метод AddValue(), набор методов вида GetПримитивныйТип(), а также свойства для указания имени типа и сборки сериализуемого объекта. Если тип реализует интерфейс ISerializable, он должен содержать специальный private-конструктор, который будет вызывать CLR после выполнения десериализации. Конструктор должен иметь параметр типа SerializationInfo и параметр типа StreamingContext.
Рассмотрим пример реализации ISerializable в классе Student.
[Serializable]
public class Student : IComparable<Student>, ISerializable
{
// не показаны свойства и метод CompareTo()
// неявная реализация интерфейса ISerializable
void ISerializable.GetObjectData(SerializationInfo info,
StreamingContext ctx)
{
info.SetType(typeof(Student));
info.AddValue("Name", Name);
info.AddValue("Age", Age);
info.AddValue("Mark", (int)(GPA * 10));
}
private Student(SerializationInfo info, StreamingContext ctx)
{
Name = info.GetString("Name");
Age = info.GetInt32("Age");
GPA = info.GetInt32("Mark") / 10.0;
}
public Student() { }
}