JavaRush /Курсы /C# SELF /Обсуждаем BinaryFormatter<...

Обсуждаем 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 с подходящими настройками.

2
Задача
C# SELF, 44 уровень, 1 лекция
Недоступна
Сравнение сериализации JSON и XML для простого объекта
Сравнение сериализации JSON и XML для простого объекта
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ