Иногда методов преобразования в последовательную форму (сериализации), предоставляемых каркасом, недостаточно. В таком случае можно предусмотреть специальную сериализацию класса. Для этого необходимо реализовать интерфейс ISerializable и добавить к классу конструктор, как это показано в проекте Serialization (Сериализация) в папке ISerializable. Интерфейс ISerializable имеет один член: GetObj ectData. Этот метод используется во время сериализации данных.
Пример ISerializable демонстрирует, как это можно сделать. Как и прежде, класс должен быть отмечен как Serializable (Преобразуемый в последовательную форму).
[Serializable]
// [Преобразуемый в последовательную форму]
public _gc class HotelBroker :
// класс сборщика мусора HotelBroker:
public Broker, // общедоступный Брокер
public iHotellnfo,
public IHotelAdmin,
public IHotelReservation,
public ISerializable
{
};
Чтобы сохранить все данные, которые должны быть сохранены в методе ISerializable: : GetObjectData, используется класс Serializationlnfo. Для управления сохранением различных типов данных, включая Object*, перегружается метод AddValue класса Serializationlnfo. Когда сохраняется тип, необходимо назначить ему имя так, чтобы можно было позже его восстановить. Класс StreamingContext предоставляет информацию о потоке, используемом при сериализации. Например, можно узнать, является ли используемый поток файлом или удаленным потоком, который посылается на другой компьютер.
public:
void GetObjectData(Serializationlnfo *info,
StreamingContext context) // контекст
{
long numberHotels = units->Count;
info->AddValue("NumberHotels", numberHotels) ;
info->AddValue("Hotels", units); // Гостиницы
}
Нужно также реализовать специальный конструктор, который используется каркасом, когда объект преобразуется из последовательной формы в параллельную. Он принимает те же параметры, что и GetObjectData. В нем для восстановления данных используются различные методы GetXXX класса Serializationlnfo. Обращаем внимание читателя на то, что, поскольку мы не сохраняли поле cities (города), то были вынуждены восстанавливать его вручную. Конструктор объявляется приватным, потому что использует его только каркас. Если забыть добавить конструктор, то при попытке восстановить объект будет запущено исключение SerializationException.
private: // частный
HotelBroker(Serializationlnfo *info,
StreamingContext context) // контекст
: Broker(366, 10), MAXDAY(366), MAXUNIT(IO) // Брокер
{
long numberHotels =
info->Get!nt32("NumberHotels");
units = dynamic_cast<ArrayList *>(
info->GetValue(
"Hotels", units->GetType())); // Гостиницы
if (numberHotels == units->Count)
Console::WriteLine("All hotels deserialized.");
// "Все гостиницы преобразованы из последовательной
// формы в параллельную. "
else
Console::WriteLine("Error in deserialization.");
// "Ошибка при преобразовании из последовательной
// формы в параллельную."
cities = new ArrayList;
// города
lEnumerator *pEnum = units->GetEnumerator();
while (pEnum->MoveNext())
{
Hotel *h = // Гостиница
dynamic_cast<Hotel *>(pEnum->Current); // Гостиница
AddCity(h->City); // Город
}
}
Необходимо помнить, что после внесения любых изменений и последующей компоновки проекта Hotel (Гостиница), нужно скопировать Hotel. dll в папку, где находится программа-клиент Serialization.exe. Это не происходит, как в С#, автоматически, если не добавить в проект специальный шаг компоновки.
В данном примере мы выполняли пользовательскую сериализацию только объекта HotelBroker. Для всех других объектов мы использовали сериализацию, предоставляемую каркасом. Этот пример работает так же, как и пример Serialization (Сериализация). Поэтому вывод программы выглядит аналогично.