Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
void ChangeStateOne
{
this.status = "муж дворянки";
this.couple.status = "дворянка";
this.couple.wealth = "имение";
}
void ChangeStateTwo
{
this.status = "муж боярыни";
this.couple.status = "боярыня";
this.couple.wealth = "много поместий";
}
void ChangeStateThree
{
this.status = "муж государыни";
this.couple.status = "государыня";
this.couple.wealth = "страна";
}
Начиная с четвертого желания, все возвращается в начальное состояние — выполняется десериализация графа объектов:
void BackState(ref Personage fisher)
{
BinaryFormatter bf = new BinaryFormatter ;
FileStream fs = new FileStream
("State.bin",FileMode.Open, FileAccess.Read);
fisher = (Personage)bf.Deserialize(fs);
fs.Close;
}
Обратите
public void About
{
Console.WriteLine("имя = {0}, возраст = {1},"+
"статус = {2}, состояние ={3}",name,age,status, wealth);
Console.WriteLine("имя = {0}, возраст = {1}," +
"статус = {2}, состояние ={3}", this.couple.name,
this.couple.age,this.couple.status, this.couple.wealth);
}
Для завершения сказки нам нужно в клиентском классе создать ее героев:
public void TestGoldFish
{
Personage fisher = new Personage("рыбак", 70);
Personage wife = new Personage("старуха", 70);
fisher.marry(wife);
Console.WriteLine("До золотой рыбки"); fisher.About;
fisher = fisher.AskGoldFish;
Console.WriteLine("Первое желание"); fisher.About;
fisher = fisher.AskGoldFish;
Console.WriteLine("Второе желание"); fisher.About;
fisher = fisher.AskGoldFish;
Console.WriteLine("Третье желание"); fisher.About;
fisher = fisher.AskGoldFish;
Console.WriteLine("Еще хочу"); fisher.About;
fisher = fisher.AskGoldFish;
Console.WriteLine("Хочу, но уже поздно"); fisher.About;
}
На рис. 19.6 показаны результаты исполнения сказки.
Рис. 19.6. Сказка о рыбаке и рыбке
Что изменится, если перейти к сохранению данных в xml-формате? немногое. Нужно лишь заменить объявление форматера:
void SaveStateXML
{
SoapFormatter sf = new SoapFormatter;
FileStream fs = new FileStream
("State.xml",FileMode.Create, FileAccess.Write);
sf.Serialize(fs,this);
fs.Close ;
}
void BackStateXML(ref Personage fisher)
{
SoapFormatter sf = new SoapFormatter;
FileStream fs = new FileStream
("State.xml",FileMode.Open, FileAccess.Read);
fisher = (Personage)sf.Deserialize(fs);
fs.Close ;
}
Клиент, работающий с объектами класса, этих изменений и не почувствует. Результаты вычислений останутся теми же, что и в предыдущем случае. Правда, файл, сохраняющий данные, теперь выглядит совсем по-другому. Это обычный xml-документ, который мог быть создан в любом из приложений. Вот как выглядит этот документ, открытый
Рис. 19.7. XML-документ, сохраняющий состояние объектов
Интерфейс ISerializable
При необходимости можно самому управлять процессом сериализации. В этом случае наш класс должен быть наследником интерфейса ISerializable. Класс, наследующий этот интерфейс, должен реализовать единственный метод этого интерфейса GetObjectData и добавить защищенный конструктор. Схема сериализации и десериализации остается и в этом случае той же самой. Можно использовать как бинарный форматер, так и soap-форматер. Но теперь метод Serialize использует не стандартную реализацию, а вызывает метод GetObjectData , управляющий записью данных. Метод Deserialize, в свою очередь, вызывает защищенный конструктор, создающий объект и заполняющий его поля сохраненными значениями.
Конечно, возможность управлять сохранением и восстановлением данных дает большую гибкость и позволяет, в конечном счете, уменьшить размер файла, хранящего данные, что может быть крайне важно, особенно если речь идет об обмене данными с удаленным приложением. Если речь идет о поверхностной сериализации, то атрибут NonSerialized, которым можно помечать поля, не требующие сериализации, как правило, достаточен для управления эффективным сохранением данных. Так что управлять имеет смысл только глубокой сериализацией, когда сохраняется и восстанавливается граф объектов. Но, как уже говорилось, это может быть довольно сложным занятием, что будет видно и для нашего простого примера с рыбаком и рыбкой.
Рассмотрим, как устроен метод GetObjectData , управляющий сохранением данных. У этого метода два аргумента:
GetObjectData (Serializedlnfo info, StreamingContext context)
Поскольку самому вызывать этот метод не приходится — он вызывается автоматически методом Serialize, то можно не особенно задумываться о том, как создавать аргументы метода. Более важно понимать, как их следует использовать. Чаще всего используется только аргумент info и его метод AddVaiue (key, field). Данные сохраняются вместе с ключом, используемым позже при чтении данных. Аргумент key, который может быть произвольной строкой, задает ключ, а аргумент field — поле объекта. Например, для сохранения полей name и аде можно задать следующие операторы:
info.AddValue("name",name); infо. AddValue("age", age);
Поскольку имена полей уникальны, то их разумно использовать в качестве ключей.
Если поле son класса Father является объектом класса child и этот класс сериализуем, то для сохранения объекта son следует вызвать метод:
son.GetObjectData(info, context)
Если не возникает циклов, причиной которых являются взаимные ссылки, то особых сложностей с сериализацией и десериализацией не возникает. Взаимные ссылки осложняют картину и требуют индивидуального подхода к решению. На последующем примере мы покажем, как можно справиться с этой проблемой в конкретном случае.