CodeGym /Cours /C# SELF /Immuabilité et expressions w...

Immuabilité et expressions with

C# SELF
Niveau 19, Leçon 2
Disponible

1. C'est quoi l'immuabilité ?

On commence avec une analogie : imagine que t'es comptable et que chaque jour tu fais un rapport de ventes. Comme un vrai pro, tu ne modifies pas le rapport de la semaine dernière, tu en crées un nouveau — basé sur l'ancien, mais avec des données à jour. C'est pareil avec les objets immuables en programmation : une fois créé, l'objet ne change plus, et toute "modification" veut dire créer une nouvelle copie.

Immuabilité (immutability) — c'est le fait qu'un objet ne change pas après son initialisation. Toutes ses propriétés sont "gelées" : si tu veux une autre valeur, tu crées un nouvel objet.

Pourquoi c'est utile ?

  • Ça garantit la sécurité : si personne ne peut modifier ton objet par accident, tes données ne vont pas être bousillées d'un coup. C'est super important dans les programmes multithread où plusieurs threads essaient de tout changer en même temps.
  • Ça simplifie le debug : si l'objet ne change pas, tu sais exactement ce qui s'est passé après sa création.
  • C'est pratique pour transmettre des données, surtout dans les systèmes distribués où les copies peuvent diverger.
  • Ça permet de faire des "snapshots" (snapshots) — l'historique des changements devient super clair.

2. Immuabilité des types record

Quand tu déclares une class classique, ses propriétés sont modifiables (mutable) par défaut. Exemple :

public class UserProfile
{
    public string Name { get; set; }
    public int Age { get; set; }
}

var user = new UserProfile { Name = "Ivan", Age = 25 };
user.Age = 26; // Tout va bien — classe classique : on change l'âge "à la volée"

Avec un record, c'est différent : ils sont faits pour stocker des données immuables.


public record UserProfile(string Name, int Age);

// On crée un objet :
var user = new UserProfile("Ivan", 25);

// On essaie de changer l'âge :
user.Age = 26; // Erreur de compilation : propriété en lecture seule !
record — propriétés en lecture seule (init-only)

Les propriétés d'un record positionnel sont en lecture seule (init-only). Tu ne peux pas les modifier après la création, mais tu peux utiliser une expression with pour créer une nouvelle copie avec une propriété modifiée.

Classes mutables vs records immuables

Classe Record positionnel
Propriétés par défaut
get; set;
Immuables
get; init;
Comment modifier
Accès direct à la propriété
Seulement en créant une nouvelle copie
Comparaison d'objets
Par référence (ReferenceEquals)
Par valeur (Equals)
Pratique pour transmettre des données
Pas toujours
Oui

3. Expressions with

Tu t'es sûrement dit — "les records c'est cool, mais comment on fait si on ne peut pas les modifier ?" C'est là que la magie des expressions with arrive !

with — c'est une syntaxe spéciale qui permet de créer une nouvelle copie d'un record, en ne changeant que les propriétés que tu veux.
En gros : "Prends cet objet, fais-en une copie, mais modifie juste quelques propriétés."

Exemple simple


var user1 = new UserProfile("Anna", 30);
// ... mais la vie avance, et Anna a pris un an
var user2 = user1 with { Age = 31 };
// user1 reste la même, user2 — c'est une copie, mais avec un an de plus

Console.WriteLine(user1); // UserProfile { Name = Anna, Age = 30 }
Console.WriteLine(user2); // UserProfile { Name = Anna, Age = 31 }

Sous le capot

Ce n'est pas un clone mutant, mais un nouvel objet, créé grâce à une méthode auto-générée Clone(), qui fait une copie et remplace les valeurs que tu veux.

Si les expressions with existaient dans la vraie vie, tu pourrais te lever le matin non pas dans "ton vieux corps fatigué", mais dans une copie de toi avec la bonne humeur et des muscles plus gros (mais seulement si t'étais un record).

4. Un peu sur l'imbrication et la copie

Si un record contient d'autres records — tout roule :

public record Address(string City, string Street);
public record Student(string Name, int Age, string Email, Address Home);

var a1 = new Address("Moscou", "Tverskaia");
var s1 = new Student("Lena", 21, "lena@mail.ru", a1);

var s2 = s1 with { Home = a1 with { Street = "Arbat" } };

Ici tout reste vraiment immuable, parce que le Address imbriqué est aussi un record.

5. Dernières subtilités

Records positionnels = compacts

Tu peux déclarer un record en mode “court” (syntaxe positionnelle). Toutes les propriétés sont alors automatiquement init-only.

public record Course(string Name, int Credits);

var c1 = new Course("C#", 5);
var c2 = c1 with { Credits = 6 };

Analogie avec les propriétés en lecture seule (init-only)

Dans un record, tu peux aussi déclarer explicitement les propriétés comme ça :

public record Student
{
    public string Name { get; init; }
    public int Age { get; init; }
}

Ces propriétés ne peuvent être changées qu'à l'initialisation (ou avec with).

6. Pratique : appli démo

On va coder notre "École en ligne" d'apprentissage. Imaginons qu'on a déjà un record pour un étudiant :

public record Student(string Name, int Age, string Email);

Classique : quelqu'un s'est trompé dans l'adresse, mais l'étudiant a déjà créé son compte. Comment faire une "mise à jour" de l'email ? Bien sûr, avec with !


var student = new Student("Ekaterina", 19, "kate@school.com");
var updatedStudent = student with { Email = "ekaterina@school.com" };

// On vérifie les objets :
Console.WriteLine(student);       // Student { Name = Ekaterina, Age = 19, Email = kate@school.com }
Console.WriteLine(updatedStudent); // Student { Name = Ekaterina, Age = 19, Email = ekaterina@school.com }

7. Erreurs typiques et pièges

Un peu de douleur maintenant — là où les étudiants se plantent souvent en jouant avec les records immuables.

  • D'abord, beaucoup pensent que with modifie l'objet d'origine. En vrai, l'objet d'origine reste le même, et un nouveau est créé avec les champs modifiés. Parfois, ça peut piéger et tu perds les nouvelles valeurs.
  • Ensuite, rappelle-toi : si un record contient des objets imbriqués qui sont mutables (genre un tableau ou une List), l'expression with ne fait pas de copie profonde ! Ta collection sera la même pour les deux copies.

public record Student(string Name, int Age, List<string> Subjects);

var s1 = new Student("Oleg", 22, new List<string> { "Math", "Physics" });
var s2 = s1 with { };

s1.Subjects.Add("C#"); // Oups, maintenant s2.Subjects contient aussi "C#"

Voilà pourquoi pour un état vraiment immuable, il vaut mieux utiliser seulement des types simples ou des collections qui sont elles-mêmes immuables (ImmutableList<T> et autres de System.Collections.Immutable).

Si tu veux garantir une vraie immuabilité, utilise ces collections ou fais une copie profonde à la main.

2
Mission
C# SELF, niveau 19, leçon 2
Bloqué
Création d'un record simple et utilisation de l'expression with
Création d'un record simple et utilisation de l'expression with
2
Mission
C# SELF, niveau 19, leçon 2
Bloqué
Records imbriqués et mise à jour d’un objet imbriqué
Records imbriqués et mise à jour d’un objet imbriqué
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION