Выбрать главу

[Serializable]

public class Personage: ISerializable

{…}

Добавим в наш класс специальный метод, вызываемый при сериализации — метод сохранения данных:

//Специальный метод сериализации

public void GetObjectData(Serializationlnfо info,

     StreamingContext context)

         {

            info.AddValue("name",name); infо. AddValue("age", age);

            infо.AddValue("status",status);

            infо.AddValue("wealth", wealth);

            info.AddValue("couplename",couple.name);

            info.AddValue("coupleage", couple.age);

            infо.AddValue("couplestatus",couple.status);

            infо. AddValue("couplewealth", couple.wealth);

         }

В трех первых строках сохраняются значимые поля объекта и тут все ясно. Но вот запомнить поле, хранящее объект couple класса Personage, напрямую не удается. Попытка рекурсивного вызова

couple.GetobjectData(info,context);

привела бы к зацикливанию, если бы раньше из-за повторяющегося ключа не возникала исключительная ситуация в момент записи поля name объекта couple. Поэтому приходится явно сохранять поля этого объекта уже с другими ключами. Понятно, что с ростом сложности структуры графа объектов задача существенно осложняется.

Добавим в наш класс специальный конструктор, вызываемый при десериализации — конструктор восстановления состояния:

//Специальный конструктор сериализации

protected Personage (Serializationlnfo info,

         StreamingContext context)

   {

         name = infо. GetString("name"); age = infо. Getlnt32("age");

         status = infо. GetString("status");

         wealth = infо. GetString("wealth");

         couple = new Personage(infо. GetString("couplename"),

                      infо. Getlnt32("coupleage"));

         couple.status = infо. GetString("couplestatus");

         couple.wealth = infо. GetString("couplewealth");

        this.couple = couple; couple.couple = this;

   }

Опять первые строки восстановления значимых полей объекта прозрачно ясны. А с полем couple приходится повозиться. Вначале создается новый объект обычным конструктором, аргументы которого читаются из сохраняемой памяти. Затем восстанавливаются значения других полей этого объекта, а затем уже происходит взаимное связывание двух объектов.

Кроме введения конструктора класса и метода GetObjectData, никаких других изменений в проекте не понадобилось — ни в методах класса, ни на стороне клиента. Внешне проект работал совершенно идентично ситуации, когда не вводилось наследование интерфейса сериализации. Но с внутренних позиций изменения произошли: методы форматеров Serialize и Deserialize в процессе своей работы теперь вызывали созданный нами метод и конструктор класса. Небольшие изменения произошли и в файлах, хранящих данные.

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

Когда в нашем примере вводилось собственное управление сериализацией, то не ставилась цель минимизации объема хранимых данных, в обоих случаях сохранялись одни и те же данные. Тем не менее представляет интерес взглянуть на таблицу, хранящую объемы создаваемых файлов.

Таблица 19.1. Размеры файлов при различных случаях сериализации

Формат ∙ Сериализация ∙ Размер файла

Бинарный поток ∙ Стандартная ∙ 355 байтов

Бинарный поток ∙ Управляемая ∙ 355 байтов

XML-документ ∙ Стандартная ∙ 1,14 Кб.

XML-документ ∙ Управляемая ∙ 974 байта

Преимуществами XML-документа являются его читабельность и хорошо развитые средства разбора, но зато бинарное представление выигрывает в объеме и скорости передачи тех же данных.

20. Функциональный тип в С#. Делегаты

Новое слово для старого понятия. Функциональный тип. Функции высших порядков. Вычисление интеграла и сортировка. Два способа взаимодействия частей при построении сложных систем. Функции обратного вызова. Наследование и функциональные типы. Сравнение двух подходов. Класс Delegate. Методы и свойства класса. Операции над делегатами. Комбинирование делегатов. Список вызовов.

Как определяется функциональный тип и как появляются его экземпляры

Слово делегат (delegate) используется в C# для обозначения хорошо известного понятия. Делегат задает определение функционального типа (класса) данных. Экземплярами класса являются функции. Описание делегата в языке C# представляет собой описание еще одного частного случая класса.