Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

2.7. Сериализация объектов в нестандартном формате

Если программиста не устраивают существующие форматы сериализации или способ организации потока сериализованных данных, он может осуществить сериализацию в собственном формате. Классы, помеченные атрибутом [Serializable], могут дополнительно реализовывать интерфейс ISerializable. Это позволяет «вклиниться» в процесс сериализации и выполнить любые действия, связанные с формированием данных для сохранения.

Интерфейс ISerializable устроен просто:

public interface ISerializable {

void GetObjectData(SerializationInfo info,

StreamingContext context);

}

Метод GetObjectData() вызывается форматером автоматически при выполнении сериализации. Реализация данного метода подразумевает заполнение объекта SerializationInfo набором данных вида «ключ-значение», которые (обычно) соответствуют полям сохраняемого объекта. Класс SerializationInfo содержит несколько перегруженных версий метода AddValue(), а также свойства для указания имени типа, имени сборки и т.п. Фрагмент определения класса SerializationInfo приведен ниже:

public sealed class SerializationInfo {

public SerializationInfo(Type type,

IFormatterConverter converter);

public string AssemblyName { get; set; }

public string FullTypeName { get; set; }

public int MemberCount { get; }

public void AddValue(string name, short value);

public void AddValue(string name, UInt16 value);

public void AddValue(string name, int value);

. . .

}

Если тип реализовывает интерфейс ISerializable, то он должен также содержать специальный конструктор:

[Serializable]

class SomeClass : ISerializable {

// Конструктор с такой сигнатурой необходим,

// чтобы CLR смогла восстановить состояние объекта

private SomeClass(SerializationInfo si,

StreamingContext ctx) { . . . }

. . .

}

Обратите внимание: конструктор объявлен с модификатором private. Первый параметр конструктора – это объект класса SerializationInfo. Второй параметр имеет тип StreamingContext. Наиболее часто используемым элементом данного класса является свойство State из перечисления StreamingContextStates.

Для иллюстрации использования интерфейса ISerializable предположим, что у нас определен класс с двумя текстовыми полями. Далее, пусть требуется сохранять эти поля в верхнем регистре, а считывать в нижнем регистре. Вот код, который выполняет требуемую сериализацию (не забудьте подключить пространство имен System.Runtime.Serialization):

[Serializable]

class MyStringData : ISerializable {

public string dataItemOne, dataItemTwo;

public MyStringData(){}

private MyStringData(SerializationInfo si,

StreamingContext ctx) {

// Получаем значения из потока и преобразовываем их

dataItemOne = si.GetString("First_Item").ToLower();

dataItemTwo = si.GetString("dataItemTwo").ToLower();

}

void ISerializable.GetObjectData(SerializationInfo info,

StreamingContext ctx) {

// Заполняем объект SerializationInfo

info.AddValue("First_Item", dataItemOne.ToUpper());

info.AddValue("dataItemTwo", dataItemTwo.ToUpper());

}

}

Как видим, в конструкторе типа сериализованные значения извлекаются при помощи метода GetString(). В классе SerializationInfo существуют аналогичные методы для других типов данных.

Во второй версии .NET Framework для поддержки собственных форматов сериализации существует ряд атрибутов: [OnSerializing], [OnSerialized], [OnDeserializing], [OnDeserialized]. Этими атрибутами помечаются методы класса. Механизм сериализации будет автоматически вызывать помеченный метод в требуемый момент. Метод, который помечается одним из вышеуказанных атрибутов, должен принимать в качестве параметра объект класса StreamingContext и не возвращать значений.

Для иллюстрации пользовательской сериализации с применением атрибутов рассмотрим класс MoreData, который при сериализации ведет себя аналогично классу MyStringData.

[Serializable]

class MoreData {

public string dataItemOne, dataItemTwo;

// Если метод помечен атрибутом [OnSerializing], то он

// вызывается перед записью данных в поток

[OnSerializing]

internal void OnSerializing(StreamingContext context) {

dataItemOne = dataItemOne.ToUpper();

dataItemTwo = dataItemTwo.ToUpper();

}

// Если метод помечен атрибутом [OnDeserialized], то он

// вызывается после чтения данных из потока

[OnDeserialized]

internal void OnDeserialized(StreamingContext context) {

dataItemOne = dataItemOne.ToLower();

dataItemTwo = dataItemTwo.ToLower();

}

}