Глава.24 .Сериализация
Если ваш производный тип не имеет дополнительных полей и, следовательно,
не нуждается в особых сериализации и десериализации, реализовывать интерфейс
ISerializable
не нужно. Метод
GetObjectData
, как и все члены интерфейса, яв-
ляется виртуальным и вызывается для корректной сериализации объекта. Кроме
того, модуль форматирования считает специальный конструктор «виртуализиро-
ваным». То есть во время десериализации модуль форматирования проверяет тип,
экземпляр которого он пытается создать. При отсутствии у этого типа специального
конструктора модуль форматирования начинает сканировать базовые классы, пока
не найдет класс, в котором реализован нужный ему конструктор.
ВниМание
Код.специального.конструктора.обычно.извлекает.поля.из.переданного.ему.объекта.
SerializationInfo .Однако.извлечение.полей.не.гарантирует.полной.десериализации.
объекта,.поэтому.код.специального.конструктора.не.должен.модифицировать.объ-
екты,.которые.он.извлекает
Если.вашему.типу.нужен.доступ.к.членам.извлеченного.объекта.(например,.вызов.
методов),.следует.снабдить.тип.методом.с.атрибутом.OnDeserialized.или.заставить.
реализовывать.метод.OnDeserialization.интерфейса.IDeserializationCallback.(как.пока-
зано.в.примере.Dictionary) .Вызов.этого.метода.задает.значения.полей.всех.объектов .
Но.порядок.вызова.методов.OnDeserialized.и.OnDeserialization.для.набора.объектов.
заранее.не.известен .Так.что,.несмотря.на.инициализацию.полей,.если.объект,.на.
который.вы.ссылаетесь,.снабжен.методом.OnDeserialized.или.реализует.интерфейс.
IDeserializationCallback,.нельзя.быть.уверенным.в.его.полной.десериализации
Определение типа, реализующего интерфейс
ISerializable, не реализуемый базовым классом
Как уже упоминалось, интерфейс
ISerializable
является крайне мощным инстру-
ментом, позволяющим типу полностью управлять сериализацией и десериализацией
своих экземпляров. Однако за эту мощь приходится платить ответственностью
типа за сериализацию еще и всех полей базового типа. Если базовый тип реализует
также интерфейс
ISerializable
, нет никаких проблем — достаточно вызвать метод
GetObjectData
базового типа.
Однако иногда требуется определить тип, управляющий сериализацией, при
условии, что его базовый тип не реализует интерфейс
ISerializable
. В этом случае
производный класс должен вручную сериализовать поля базового типа, добавив
их значения в коллекцию
SerializationInfo
. Затем в специальном конструкторе
нужно будет извлечь эти значения и задать поля базового класса. В случае когда
поля базового класса помечены модификатором
public
или
protected
, это не-
сложно, а вот для закрытых полей задача может стать даже вообще неразрешимой.
Следующий код показывает, как правильно реализовать метод
GetObjectData
интерфейса
ISerializable
и его конструктор, обеспечивающий сериализацию
полей базового типа:
685
Управление.сериализованными.и.десериализованными.данными
[Serializable]
internal class Base {
protected String m_name = "Jeff";
public Base() { /* Наделяем тип способностью создавать экземпляры */ }
}
[Serializable]
internal class Derived : Base, ISerializable {
private DateTime m_date = DateTime.Now;
public Derived() { /* Наделяем тип способностью создавать экземпляры */ }
// Если конструктор не существует, выдается SerializationException.
// Если класс не запечатан, конструктор должен быть защищенным.
[SecurityPermissionAttribute(
SecurityAction.Demand, SerializationFormatter = true)]
private Derived(SerializationInfo info, StreamingContext context) {
// Получение набора сериализуемых членов для нашего и базовых классов
Type baseType = this.GetType().BaseType;
MemberInfo[] mi = FormatterServices.GetSerializableMembers(
baseType, context);
// Десериализация полей базового класса из объекта данных
for (Int32 i = 0; i < mi.Length; i++) {
// Получение поля и присвоение ему десериализованного значения
FieldInfo fi = (FieldInfo)mi[i];
fi.SetValue(this, info.GetValue(
baseType.FullName + "+" + fi.Name, fi.FieldType));
}
// Десериализация значений, сериализованных для этого класса
m_date = info.GetDateTime("Date");
}
[SecurityPermissionAttribute(
SecurityAction.Demand, SerializationFormatter = true)]
public virtual void GetObjectData(
SerializationInfo info, StreamingContext context) {
// Сериализация нужных значений для этого класса
info.AddValue("Date", m_date);
// Получение набора сериализуемых членов для нашего и базовых классов
Type baseType = this.GetType().BaseType;
MemberInfo[] mi = FormatterServices.GetSerializableMembers(
baseType, context);
// Сериализация полей базового класса в объект данных
for (Int32 i = 0; i < mi.Length; i++) {
// Полное имя базового типа ставим в префикс имени поля
info.AddValue(baseType.FullName + "+" + mi[i].Name,
((FieldInfo)mi[i]).GetValue(this));
}
}
продолжение
686
Do'stlaringiz bilan baham: |