1. Вступ
Ми вже звикли створювати об’єкти в наших програмах. Пам’ятаєте, ми ось-ось почнемо вивчати класи й об’єкти глибше, але вже зараз розуміємо, що змінні, списки, навіть прості рядки — це не просто так, це вже певні «сутності» в нашій програмі. Наприклад, ми можемо створити змінну int age = 30; або string name = "Вася";. А що, як треба зберегти у файл інформацію про користувача, який має імʼя, вік, адресу, список улюблених книжок та багато іншого?
Уявіть: ви створюєте гру. У вас є об’єкт Player з купою характеристик: здоровʼя, рівень, інвентар (список предметів), координати на мапі тощо. Гравець грає, прокачується, знаходить рідкісні артефакти. І ось він вирішує вийти з гри. Що станеться? Усі дані про його пригоди, які зберігалися в памʼяті, зникнуть. Це прикро. Щоб цього не сталося, нам потрібно зберегти стан об’єкта Player у файл, а коли гравець повернеться, завантажити його назад.
І от тут на сцену виходить серіалізація. Вона якраз і займається тим, що будує міст між «живими» об’єктами у памʼяті та «мертвими», але стійкими даними на диску.
2. Процес серіалізації (від об’єкта до файлу)
Розберімося, як відбувається ця «магія» перетворення об’єкта на байти, які можна записати у файл.
Уявімо, що в нас є такий клас Book (Книга):
// Це наше «креслення» або «план» для створення об’єктів-книжок
public class Book
{
// Властивості книги
public string Title { get; set; } // Назва книги
public string Author { get; set; } // Автор
public int Year { get; set; } // Рік видання
// Конструктор — спеціальний метод для створення нових об’єктів Book
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
// Метод для зручного виведення інформації про книгу (поки що не обов’язковий для серіалізації, але корисний)
public void DisplayInfo()
{
Console.WriteLine($"Назва: {Title}, Автор: {Author}, Рік: {Year}");
}
}
Крок 1: Створення об’єкта для серіалізації.
Спочатку нам, звісно, потрібен об’єкт, який хочемо зберегти. Наприклад, ми створили екземпляр Book:
Book myFavoriteBook = new Book("Автостопом Галактикою", "Дуглас Адамс", 1979);
Цей об’єкт myFavoriteBook нині перебуває в оперативній памʼяті.
Крок 2: Вибір інструмента (серіалізатора).
Ми не можемо просто взяти й «скопіювати» об’єкт на диск. Комп’ютер не розуміє об’єкти безпосередньо у файлах — йому потрібні байти. Потрібен спеціальний інструмент — серіалізатор. Його завдання — розібрати наш об’єкт на складові (його властивості: Title, Author, Year) і перетворити ці частини на послідовність байтів або на текстовий рядок (наприклад, у JSON чи XML).
Сьогодні не заглиблюємося у конкретні реалізації — просто тримайте в голові, що це спеціальна «коробка‑перетворювач».
Крок 3: Перетворення об’єкта на потік даних.
Серіалізатор отримує наш об’єкт myFavoriteBook, дивиться на його властивості (Title, Author, Year) і перетворює кожну з них у формат, який можна записати. Усі ці байти (або текстові символи) збираються в єдиний потік даних — довгу «стрічку» інформації.
Крок 4: Запис потоку у файл.
Тепер, коли маємо цю «стрічку байтів», ми використовуємо наших давніх знайомих — FileStream і, можливо, StreamWriter (якщо обрано текстовий формат, такий як JSON) або просто FileStream (для чисто бінарних даних), щоб записати цей потік на диск.
3. Процес десеріалізації (від файлу до об’єкта)
Крок 1: Читання потоку даних із файлу.
Ми знову використовуємо FileStream і за потреби StreamReader (якщо це текстовий формат), щоб прочитати вміст файлу. Дані надходять у вигляді «стрічки» байтів або тексту.
Крок 2: Вибір інструмента (десеріалізатора).
Потрібен зворотний інструмент — десеріалізатор. Він має знати, як інтерпретувати отримані байти/текст і правильно відтворити структуру об’єкта. Дуже важливо: для десеріалізації використовують той самий тип серіалізатора (і зазвичай ту саму бібліотеку), що й для серіалізації, інакше ваш «конструктор» не зрозуміє інструкцію зі збирання.
Крок 3: Перетворення потоку назад в об’єкт.
Десеріалізатор читає дані, розуміє, де міститься Title, потім Author, далі Year, і на основі цього створює новий об’єкт Book у пам’яті, заповнюючи його властивості.
Крок 4: Отримання готового об’єкта.
Вуаля! У нас знову є повноцінний об’єкт Book в оперативній пам’яті, з яким можна працювати.
Приклад: Зберігаємо нашу «Супер‑Книгу» вручну (для розуміння концепції)
Поки що не використовуватимемо спеціалізовані бібліотеки: зробімо найпростішу «ручну» серіалізацію і десеріалізацію за допомогою StreamWriter і StreamReader. Це допоможе зрозуміти принцип.
Наш об’єкт Book:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public int Year { get; set; }
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
public void DisplayInfo()
{
Console.WriteLine($"Назва: \"{Title}\", Автор: {Author}, Рік: {Year}");
}
}
Ручна серіалізація: метод SaveBookToTextFile
Створимо метод, який збереже властивості книги в текстовий файл, кожну на окремому рядку.
void SaveBookToTextFile(Book book, string filePath)
{
using var writer = new StreamWriter(filePath);
writer.WriteLine(book.Title);
writer.WriteLine(book.Author);
writer.WriteLine(book.Year);
}
Що відбувається? Відкриваємо StreamWriter і послідовно записуємо Title, Author, Year — це наша найпростіша схема серіалізації.
Якщо запустити код, вміст файлу буде таким:
Автостопом Галактикою
Дуглас Адамс
1979
Ручна десеріалізація: метод LoadBookFromTextFile
Напишемо метод, який прочитає дані та збере новий об’єкт Book.
Book LoadBookFromTextFile(string filePath)
{
using var reader = new StreamReader(filePath);
string title = reader.ReadLine();
string author = reader.ReadLine();
int year = int.Parse(reader.ReadLine());
return new Book(title, author, year);
}
І використаємо ці методи в Main:
// Створюємо об’єкт
var myBook = new Book("Автостопом Галактикою", "Дуглас Адамс", 1979);
string filePath = "my_favorite_book.txt";
// Зберігаємо його у файл
SaveBookToTextFile(myBook, filePath);
// Читаємо з файлу
Book loadedBook = LoadBookFromTextFile(filePath);
Що відбувається в LoadBookFromTextFile? Ми відкриваємо StreamReader і в тому ж порядку читаємо рядки: спочатку назву, потім автора, далі рік і перетворюємо його через int.Parse. Після цього створюємо новий екземпляр Book.
На практиці варто додавати перевірки (File.Exists) і обробку помилок через try-catch, але тут ми зосереджуємося на самій ідеї.
Чому «ручна» серіалізація — це погано (і чому потрібні бібліотеки)?
- Багато ручного коду. Кожну властивість треба записати, а потім прочитати. Якщо об’єктів і полів багато — код розростається.
- Крихкість до змін. Додали нову властивість Pages (int)? Доведеться змінювати і запис, і читання, і суворо стежити за порядком.
- Складні структури. Вкладені колекції та об’єкти (наприклад, List<Chapter>) перетворять код на «спагеті».
- Формати й ефективність. Текстовий формат простий, але не завжди компактний і безпечний; для бінарних даних доведеться вручну працювати з байтами, BinaryWriter/BinaryReader тощо.
- Немає метаданих. Наш файл не «знає», що перший рядок — це Title, а третій — Year. Спеціалізовані серіалізатори можуть зберігати метадані й бути стійкішими до змін моделей.
Саме тому в реальних проєктах використовують готові бібліотеки‑серіалізатори, які вміють автоматично розбирати/збирати об’єкти, працювати з JSON, XML і бінарними форматами та стійко переносити зміни моделей. Про це — у наступній лекції!
Практичне застосування: навіщо потрібна серіалізація?
- Збереження й завантаження даних. Налаштування, стани ігор, конфігурації.
- Передача даних мережею. Обмін складними об’єктами між сервісами (часто у JSON).
- Кешування. Швидке повторне використання раніше отриманих даних.
- Логування складних об’єктів. Зручно для налагодження й аудиту.
- Глибоке копіювання об’єктів. Серіалізація + десеріалізація як спосіб клонування графа об’єктів.
Серіалізація — один із наріжних каменів сучасного ПЗ: від збереження прогресу в грі до роботи з веб‑сервісами — вона всюди!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ