CodeGym /Kursy /C# SELF /Omówienie BinaryFormatter

Omówienie BinaryFormatter

C# SELF
Poziom 44, Lekcja 1
Dostępny

1. Wprowadzenie

W wielu starych podręcznikach i przykładach po C# można spotkać serializację za pomocą klasy BinaryFormatter. Kiedyś to był standard: wygodnie, szybko, mniej zmartwień. Przykład ze starych czasów wyglądał mniej więcej tak:

// Niebezpieczny stary kod! Nie rób tak w nowych projektach!
var bf = new BinaryFormatter();
using (var stream = File.Open("player.dat", FileMode.Create))
{
    bf.Serialize(stream, playerObject);
}

Ale w nowoczesnym .NET 9 nie będziesz już nawet w stanie skompilować takiego kodu: BinaryFormatter został usunięty z platformy.

Droga od bohatera do wykluczonego

Początkowo BinaryFormatter był pomyślany jako uniwersalny mechanizm serializacji i deserializacji obiektów .NET, potrafiący zapisywać skomplikowane grafy obiektów „tak jak są”. Z czasem wyszły na jaw poważne problemy z bezpieczeństwem i kompatybilnością, i teraz pozostał w przeszłości — mniej więcej jak magnetofony kasetowe w erze streamingu.

2. Główne powody rezygnacji z BinaryFormatter

Ogromna dziura w bezpieczeństwie

BinaryFormatter potrafi nie tylko serializować dane, ale i odtwarzać typy z wejściowego strumienia. Jeśli bezmyślnie deserializujesz niezaufane dane (np. otrzymane z sieci), atakujący może wstrzyknąć ładunek, prowadzący do zdalnego wykonania kodu (RCE). Dlatego Microsoft przez wiele lat ostrzegał: „Nie używaj BinaryFormatter”. Od .NET 5 był oznaczony jako przestarzały i niebezpieczny ([Obsolete]), a w .NET 9 — usunięty.

Zależność od wewnętrznej struktury klas

Format BinaryFormatter przenosił szczegóły implementacji typów: pola, pakowanie, wersje. Najmniejsza zmiana w klasach (dodanie/usunięcie pola) łamała kompatybilność z już zapisanymi plikami. To czyniło ten format nieodpowiednim do długotrwałego przechowywania i wymiany między wersjami.

Trudności z cross‑platform

Dane zapisane przez BinaryFormatter są powiązane z wewnętrzną reprezentacją obiektów .NET. Odczytanie ich na innej platformie/języku albo nawet innej wersji .NET często jest niemożliwe.

3. Jak wykryć „stary kod” i co robić?

Znaki użycia BinaryFormatter wyglądają tak:

using System.Runtime.Serialization.Formatters.Binary; // <-- podejrzane!
BinaryFormatter bf = new BinaryFormatter();           // <-- niebezpieczne!
bf.Serialize(...);
bf.Deserialize(...);

W nowoczesnych wersjach .NET taki kod się nie skompiluje: narzędzia zwrócą błąd "Typ lub nazwa nie znaleziona". W legacy‑projektach na .NET Framework — przynajmniej mocne ostrzeżenia.

Jeśli natrafisz na taki kod, zamień BinaryFormatter na współczesne, bezpieczne alternatywy.

Jakie formaty i klasy używać dziś?

Dla JSON

  • System.Text.Json — szybki, bezpieczny, wbudowany w .NET. Oficjalna dokumentacja
  • Newtonsoft.Json — popularna biblioteka zewnętrzna dla bardziej złożonych przypadków.

Dla XML

Dla formatów binarnych

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

Dla specyficznych zadań dobieraj specjalizowane formaty i biblioteki — „uniwersalnego” binarnego serializatora w stylu BinaryFormatter już nie ma, i dobrze.

4. Przepisywanie kodu z BinaryFormatter

Do czego nie używać BinaryFormatter

Nie wolno:

  • Zapisywać ustawień użytkownika i ważnych danych.
  • Przesyłać danych po sieci (klient ↔ server).
  • Deserializować danych z niezaufanych źródeł.

Zamiast tego:

  • Do codziennych potrzeb — System.Text.Json.
  • Dla konfiguracji — JSON, XML itd.
  • Dla wymiany między językami — formaty z cross‑platform kompatybilnością (JSON, XML, Protobuf, MessagePack).

Jaki kod teraz pisać?

Serializacja do JSON (zalecany sposób)

using System.Text.Json;
// Twój klasa:
public class Player
{
    public string Name { get; set; } = "";
    public int Health { get; set; }
    public bool IsAlive { get; set; }
}

// Obiekt do serializacji
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };

// Zamieniamy obiekt na string JSON
string json = JsonSerializer.Serialize(player);

// Zapis do pliku — bezpiecznie!
File.WriteAllText("player.json", json);

// Do odtworzenia obiektu z pliku:
string loadedJson = File.ReadAllText("player.json");
Player loadedPlayer = JsonSerializer.Deserialize<Player>(loadedJson)!;

Serializacja do XML (jeśli potrzebny ścisły format)

using System.Xml.Serialization;
// Klasa musi mieć publiczny konstruktor bez parametrów

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);
}

Wizualny schemat „stare vs nowe”

Zadanie Stary sposób (BinaryFormatter) Współczesny sposób
Serializacja do pliku
bf.Serialize(stream, obj)
JsonSerializer.Serialize(...)
XmlSerializer.Serialize(...)
Deserializacja z pliku
bf.Deserialize(stream)
JsonSerializer.Deserialize<T>(...)
XmlSerializer.Deserialize(...)
Bezpieczeństwo Wrażliwe na ataki (RCE) Bezpieczne parsery i ścisłe modele
Cross‑platform Nie Tak (JSON/XML/Protobuf)
Prędkość Szybko, ale niebezpiecznie Szybko i bezpiecznie

5. Typowe błędy przy pracy z XmlSerializer

Błąd nr 1: prywatne właściwości lub pola. XmlSerializer serializuje tylko public właściwości; nawet protected spowoduje błąd.

Błąd nr 2: brak konstruktora domyślnego. Potrzebny publiczny konstruktor bez parametrów, inaczej serializacja/deserializacja nie zadziała.

Błąd nr 3: niezgodność typów danych. XmlSerializer słabo radzi sobie z Dictionary, interface i delegate — użyj wrapperów lub innych formatów.

Błąd nr 4: cykliczne odniesienia (circular reference). XmlSerializer nie obsługuje grafów typu A → B → A; dla takich przypadków częściej używa się JSON z odpowiednimi ustawieniami.

2
Zadanie
C# SELF, poziom 44, lekcja 1
Niedostępne
Porównanie serializacji JSON i XML dla prostego obiektu
Porównanie serializacji JSON i XML dla prostego obiektu
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION