У С# є можливість зберігати на зовнішніх носіях не тільки дані примітивних типів, але і об'єкти. Збереження об'єктів називається серіалізацією, а відновлення збережених об'єктів - десеріалізацією. При серіалізації об'єкт перетвориться в лінійну послідовність байтів. Це складний процес, оскільки об'єкт може включати множину успадкованих полів і посилання на вкладені об'єкти, які, у свою чергу, теж можуть складатися з об'єктів складної структури.
На щастя, серіалізація виконується автоматично, досить просто помітити клас, який сериалізується за допомогою атрибуту [Serializable] . Атрибути розглядаються в розділі 12, поки ж достатньо знати, що атрибути - це додаткові відомості про клас, які зберігаються в його метаданих. Ті поля, які зберігати не потрібно, позначаються атрибутом [NonSerialized], наприклад:
[Serializable]
class Demo
{
public int a = 1;
[NonSerialized]
public double y;
public Monster X. Y;
}
Об'єкти можна зберігати в одному з двох форматів: двійковому або SOAP (у вигляді XML-файла). У першому випадку слід підключити до програми простір імен System.Runtime.Serialization.Formatters.Binary, у другому - простір System.Runtime.Serialization.Formatters.Soap.
Розглянемо збереження об'єктів у двійковому форматі. Для цього використовується клас BinaryFormatter, в якому визначено два методи:
Serialize(потік, об'єкт);
Deserialize(потік);
Метод Serialize зберігає заданий об'єкт в заданому потоці, метод Deserialize відновлює об'єкт із заданого потоку.
У лістингу 11.11 об'єкт, приведеного раніше класу Demo зберігається у файлі на диску з ім'ям Demo.bin. Цей файл можна проглянути, відкривши його, наприклад, в Visual Studio.NET.
Лістинг 11.11. Сериалізація об'єкту
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Examp85
{
[Serializable]
abstract class Spirit
{
public abstract void Passport();
}
[Serializable]
class Monster : Spirit
{
string name;
int health, ammo;
public Monster( int health, int ammo, string name )
{
this.health = health;
this.ammo = ammo;
this.name = name;
}
override public void Passport()
{
Console.WriteLine("Monster {0} \t health = {1} ammo = {2} " ,
name, health, ammo );
}}
[Serializable]
class Demo
{
public int a = 1;
[NonSerialized]
public double b;
public Monster X, Y;
}
class Class1
{
static void Main()
{
// Запис об'єкту на диск
Demo d = new Demo();
d.X = new Monster(100, 80, "Вася");
d.Y = new Monster(120, 50, "Петя");
d.a = 2;
d.b = 5;
d.X.Passport();
d.Y.Passport();
Console.WriteLine(d.a);
Console.WriteLine(d.b);
FileStream f = new FileStream("c:\\Demo.bin",
FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(f, d); // збереження об'єкту d в потоці f
f.Close();
// Зчитування з диска
f = new FileStream("c:\\Demo.bin",
FileMode.Open);
bf = new BinaryFormatter();
d = (Demo)bf.Deserialize(f); // відновлення об'єкту
d.X.Passport();
d.Y.Passport();
Console.WriteLine(d.a);
Console.WriteLine(d.b);
f.Close();
}
}
}
Результат роботи програми:
Monster Вася health = 100 ammo = 80
Monster Петя health = 120 ammo = 50
Monster Вася health = 100 ammo = 80
Monster Петя health = 120 ammo = 50
Отже, для збереження об'єкту в двійковому форматі необхідно:
1. Підключити простір імен System.Runtime.Serialization. Formatters.Binary.
2. Помітити клас, що зберігається, і пов'язані з ним класи атрибутом [Serializable].
3. Створити потік і пов'язати його з файлом на диску або з областю оперативної пам'яті.
4. Створити об'єкт класу BinaryFormatter.
5. Зберегти об'єкти в потоці.
6. Закрити файл.
Як видно з лістингу 11.11 після збереження об'єкту на диску він зчитується з файлу.
При серіалізації зберігається все дерево об'єктів. При цьому значення поля у не було збережено, оскільки воно було помічене як таке, що не треба зберігати.