JavaRush /Курси /C# SELF /Обговорюємо BinaryFormatte...

Обговорюємо BinaryFormatter

C# SELF
Рівень 44 , Лекція 1
Відкрита

1. Вступ

У багатьох старих підручниках і прикладах із C# можна натрапити на серіалізацію за допомогою класу BinaryFormatter. Колись це був стандарт: зручно, швидко, мінімум клопоту. Приклад з тих часів виглядав десь так:

// Небезпечний олдскульний код! Таке в нових проєктах не використовуйте!
var bf = new BinaryFormatter();
using (var stream = File.Open("player.dat", FileMode.Create))
{
    bf.Serialize(stream, playerObject);
}

Але в сучасному .NET 9 ви вже не зможете навіть скомпілювати такий код: BinaryFormatter видалено із платформи.

Шлях від героя до вигнанця

Спершу BinaryFormatter створили як універсальний механізм серіалізації та десеріалізації .NET‑обʼєктів, здатний зберігати складні графи обʼєктів «як є». Згодом виявилися серйозні проблеми з безпекою та сумісністю, і тепер він лишився в минулому — приблизно як касетні магнітофони в епоху стримінгу.

2. Головні причини відмови від BinaryFormatter

Величезна дірка в безпеці

BinaryFormatter уміє не лише серіалізувати дані, а й відновлювати типи з вхідного потоку. Якщо бездумно десеріалізувати неперевірені дані (наприклад, отримані з мережі), зловмисник може вбудувати шкідливе навантаження, що призведе до віддаленого виконання коду (RCE). Саме тому Microsoft багато років поспіль попереджала: «Не використовуйте BinaryFormatter». Починаючи з .NET 5 його позначили як застарілий і небезпечний ([Obsolete]), а в .NET 9 — видалили.

Залежність від внутрішньої структури класів

Формат BinaryFormatter тягне за собою деталі реалізації типів: поля, пакування, версії. Найменші зміни в класах (додали/видалили поле) порушують сумісність із уже збереженими файлами. Це робить такий формат непридатним для довготривалого зберігання та міжверсійного обміну.

Складнощі з кросплатформністю

Дані, збережені через BinaryFormatter, привʼязані до внутрішнього подання .NET‑обʼєктів. Прочитати їх на іншій платформі чи мові або навіть іншій версії .NET часто неможливо.

3. Як виявити «старий код» і що з ним робити?

Ознаки використання BinaryFormatter виглядають так:

using System.Runtime.Serialization.Formatters.Binary; // <-- підозріло!
BinaryFormatter bf = new BinaryFormatter();           // <-- небезпечно!
bf.Serialize(...);
bf.Deserialize(...);

У сучасних версіях .NET такий код не компілюється: інструменти повідомлять про помилку «Тип або імʼя не знайдено». У легасі‑проєктах на .NET Framework — принаймні суворі попередження.

Якщо натрапили на такий код, замінюйте BinaryFormatter на сучасні, безпечні альтернативи.

Які формати та класи використовувати сьогодні?

Для JSON

  • System.Text.Json — швидкий, безпечний, вбудований у .NET. Офіційна документація
  • Newtonsoft.Json — популярна стороння бібліотека для складних випадків.

Для XML

Для бінарних форматів

  • System.Formats.Cbor (CBOR)
  • Protobuf.Net (Protocol Buffers), MessagePack for C#

Для специфічних завдань підбирайте спеціалізовані формати й бібліотеки — «універсального» бінарного серіалізатора в стилі BinaryFormatter більше немає, і це добре.

4. Переписуємо код із BinaryFormatter

Для чого не можна використовувати BinaryFormatter

Не можна:

  • Зберігати користувацькі налаштування та важливі дані.
  • Передавати дані мережею (клієнт ↔ сервер).
  • Десеріалізувати дані з неперевірених джерел.

Натомість:

  • Для повсякденних завдань — System.Text.Json.
  • Для конфігурацій — JSON, XML тощо.
  • Для міжмовного обміну — формати з кросплатформною сумісністю (JSON, XML, Protobuf, MessagePack).

Який код тепер писати?

Серіалізація в JSON (рекомендований спосіб)

using System.Text.Json;
// Ваш клас:
public class Player
{
    public string Name { get; set; } = "";
    public int Health { get; set; }
    public bool IsAlive { get; set; }
}

// Обʼєкт для серіалізації
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };

// Перетворюємо обʼєкт на рядок JSON
string json = JsonSerializer.Serialize(player);

// Зберігаємо у файл — безпечно!
File.WriteAllText("player.json", json);

// Для відновлення обʼєкта з файла:
string loadedJson = File.ReadAllText("player.json");
Player loadedPlayer = JsonSerializer.Deserialize<Player>(loadedJson)!;

Серіалізація в XML (якщо потрібен строгий формат)

using System.Xml.Serialization;
// Клас має мати публічний конструктор без параметрів

var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
var serializer = new XmlSerializer(typeof(Player));
using (var fs = File.Create("player.xml"))
{
    serializer.Serialize(fs, player);
}

Візуальна схема «старе vs нове»

Завдання Старий спосіб (BinaryFormatter) Сучасний спосіб
Серіалізація у файл
bf.Serialize(stream, obj)
JsonSerializer.Serialize(...)
XmlSerializer.Serialize(...)
Десеріалізація з файлу
bf.Deserialize(stream)
JsonSerializer.Deserialize<T>(...)
XmlSerializer.Deserialize(...)
Безпека Вразливий до атак (RCE) Безпечні парсери та суворі моделі
Кросплатформність Ні Так (JSON/XML/Protobuf)
Швидкість Швидко, але небезпечно Швидко і безпечно

5. Типові помилки під час роботи з XmlSerializer

Помилка № 1: приватні властивості або поля. XmlSerializer серіалізує лише public властивості; навіть protected призведе до помилки.

Помилка № 2: відсутність конструктора за замовчуванням. Потрібен публічний конструктор без параметрів, інакше серіалізація/десеріалізація не запрацює.

Помилка № 3: невідповідність між типами даних. XmlSerializer погано працює із Dictionary, interface та delegate — використовуйте обгортки або інші формати.

Помилка № 4: циклічні посилання (circular reference). XmlSerializer не підтримує графи виду A → B → A; для таких випадків частіше беруть JSON з відповідними налаштуваннями.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ