1. Introduction
Nous avons déjà l'habitude de créer des objets dans nos programmes. Souvenez-vous, nous allons bientôt commencer à étudier les classes et les objets plus en profondeur, mais déjà maintenant nous comprenons que les variables, les listes, même les chaînes simples — ce ne sont pas juste des trucs, ce sont des « entités » dans notre programme. Par exemple, on peut créer une variable int age = 30; ou string name = "Vasya";. Mais que faire si on doit sauvegarder dans un fichier les informations sur un utilisateur entier qui a un nom, un âge, une adresse, une liste de livres préférés et bien plus ?
Imaginez : vous écrivez un jeu. Vous avez un objet Player avec plein de caractéristiques : santé, niveau, inventaire (liste d'objets), coordonnées sur la carte, etc. Le joueur joue, monte de niveau, trouve des artefacts cool. Et là il décide de quitter le jeu. Que va-t-il se passer ? Toutes les données de ses aventures qui étaient en mémoire vont disparaître ! Triste ! Pour éviter ça, il faut sauvegarder l'état de l'objet Player dans un fichier, et quand le joueur reviendra, le recharger.
Et là entre en scène la sérialisation. C'est exactement ce qui construit le pont entre les objets « vivants » en mémoire et les données « mortes » mais persistantes sur le disque.
2. Processus de sérialisation (de l'objet vers le fichier)
Analysons comment se passe cette "magie" qui transforme un objet en octets qu'on peut écrire dans un fichier.
Imaginez que nous avons cette classe Book (Livre) :
// Ceci est notre "plan" ou "gabarit" pour créer des objets-livres
public class Book
{
// Propriétés du livre
public string Title { get; set; } // Titre du livre
public string Author { get; set; } // Auteur
public int Year { get; set; } // Année de publication
// Constructeur - méthode spéciale pour créer de nouveaux objets Book
public Book(string title, string author, int year)
{
Title = title;
Author = author;
Year = year;
}
// Méthode pour afficher facilement les infos du livre (pas nécessaire pour la sérialisation, mais utile)
public void DisplayInfo()
{
Console.WriteLine($"Titre : {Title}, Auteur : {Author}, Année : {Year}");
}
}
Étape 1 : Créer l'objet à sérialiser.
D'abord, bien sûr, il nous faut l'objet qu'on veut sauvegarder. Par exemple, on a créé une instance de Book :
Book myFavoriteBook = new Book("Autostop à travers la Galaxie", "Douglas Adams", 1979);
Cet objet myFavoriteBook se trouve maintenant en mémoire vive.
Étape 2 : Choisir l'outil (le serializer).
On ne peut pas juste « copier » l'objet sur le disque. L'ordinateur ne comprend pas directement les objets dans les fichiers — il a besoin d'octets. Il faut un outil spécial — le serializer. Sa tâche est de décomposer notre objet en parties (ses propriétés : Title, Author, Year) et de transformer ces parties en une séquence d'octets ou en une chaîne de texte (par exemple JSON ou XML).
On ne rentre pas aujourd'hui dans les implémentations concrètes — gardons juste en tête que c'est une "boîte-transformeur" spéciale.
Étape 3 : Transformer l'objet en flux de données.
Le serializer reçoit notre objet myFavoriteBook, regarde ses propriétés (Title, Author, Year) et convertit chacune d'elles dans un format qu'on peut écrire. Tous ces octets (ou caractères) sont rassemblés en un flux de données unique — une longue "bande" d'information.
Étape 4 : Écrire le flux dans un fichier.
Maintenant qu'on a cette "bande d'octets", on utilise nos vieux amis FileStream et peut-être StreamWriter (si on choisit un format texte comme JSON) ou juste FileStream (pour des données purement binaires), pour écrire ce flux sur le disque.
3. Processus de désérialisation (du fichier vers l'objet)
Étape 1 : Lire le flux de données depuis le fichier.
On utilise à nouveau FileStream et si nécessaire StreamReader (si c'est un format texte), pour lire le contenu du fichier. Les données arrivent sous forme de "bande" d'octets ou de texte.
Étape 2 : Choisir l'outil (le deserializer).
Il faut l'outil inverse — le deserializer. Il doit savoir comment interpréter les octets/texte reçus et recréer correctement la structure de l'objet. Important : pour désérialiser on utilise généralement le même type de serializer (et souvent la même bibliothèque) que pour la sérialisation, sinon votre "constructeur" ne comprendra pas les instructions d'assemblage.
Étape 3 : Transformer le flux de nouveau en objet.
Le deserializer lit les données, comprend où se trouve Title, puis Author, ensuite Year, et sur cette base crée un nouvel objet Book en mémoire, en remplissant ses propriétés.
Étape 4 : Obtenir l'objet prêt à l'emploi.
Voilá ! On a de nouveau un objet complet Book en mémoire vive, avec lequel on peut travailler.
Exemple : On sauvegarde notre "Super-Livre" « manuellement » (pour comprendre le concept)
Pour l'instant on n'utilise pas de bibliothèques spécialisées : faisons la plus simple des sérialisations et désérialisations « manuelles » avec StreamWriter et StreamReader. Ça aide à comprendre le principe.
Notre objet 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($"Titre : \"{Title}\", Auteur : {Author}, Année : {Year}");
}
}
Sérialisation manuelle : la méthode SaveBookToTextFile
Créons une méthode qui sauvegardera les propriétés du livre dans un fichier texte, une propriété par ligne.
void SaveBookToTextFile(Book book, string filePath)
{
using var writer = new StreamWriter(filePath);
writer.WriteLine(book.Title);
writer.WriteLine(book.Author);
writer.WriteLine(book.Year);
}
Que se passe-t-il ? On ouvre un StreamWriter et on écrit successivement Title, Author, Year — c'est notre schéma de sérialisation le plus simple.
Si vous lancez le code, le contenu du fichier sera :
Autostop à travers la Galaxie
Douglas Adams
1979
Désérialisation manuelle : la méthode LoadBookFromTextFile
Écrivons une méthode qui lira les données et reconstruira un nouvel objet 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);
}
Et utilisons ces méthodes dans Main :
//on crée l'objet
var myBook = new Book("Autostop à travers la Galaxie", "Douglas Adams", 1979);
string filePath = "my_favorite_book.txt";
//on le sauvegarde dans un fichier
SaveBookToTextFile(myBook, filePath);
//on lit depuis le fichier
Book loadedBook = LoadBookFromTextFile(filePath);
Que se passe-t-il dans LoadBookFromTextFile ? On ouvre un StreamReader et dans le même ordre on lit les lignes : d'abord le titre, ensuite l'auteur, puis l'année et on la convertit via int.Parse. Après ça on crée une nouvelle instance de Book.
En pratique il vaut mieux ajouter des vérifications (File.Exists) et gérer les erreurs avec try-catch, mais ici on se concentre sur l'idée principale.
Pourquoi la sérialisation « manuelle » c'est nul (et pourquoi on a besoin de bibliothèques) ?
- Beaucoup de code manuel. Il faut écrire puis lire chaque propriété. Si il y a beaucoup d'objets et de champs — le code grossit vite.
- Sensible aux changements. Ajout d'une nouvelle propriété Pages (int) ? Il faudra modifier l'écriture et la lecture et veiller strictement à l'ordre.
- Structures complexes. Les collections imbriquées et objets (par exemple, List<Chapter>) transforment le code en « spaghettis ».
- Formats et efficacité. Le format texte est simple mais pas toujours compact ni sûr ; pour des données binaires il faudra manipuler les octets à la main, BinaryWriter/BinaryReader, etc.
- Pas de métadonnées. Notre fichier ne « sait » pas que la première ligne est Title et la troisième est Year. Les serializers spécialisés peuvent conserver des métadonnées et être plus résistants aux changements du modèle.
C'est justement pour cela que dans les projets réels on utilise des bibliothèques-serializers prêtes à l'emploi, qui savent automatiquement décomposer/recomposer les objets, travailler avec JSON, XML et des formats binaires et porter les changements des modèles de manière robuste. On en parlera dans la prochaine leçon !
Application pratique : pourquoi la sérialisation est utile ?
- Sauvegarde et chargement de données. Paramètres, états de jeu, configurations.
- Transmission de données sur le réseau. Échange d'objets complexes entre services (souvent en JSON).
- Cache. Réutilisation rapide de données précédemment obtenues.
- Logging d'objets complexes. Pratique pour le debug et l'audit.
- Copie profonde d'objets. Sérialisation + désérialisation comme moyen de cloner un graphe d'objets.
La sérialisation est un des piliers du logiciel moderne : de la sauvegarde de la progression dans un jeu au travail avec des web‑services — elle est partout !
GO TO FULL VERSION