L’immutabilité est une pierre angulaire des applications robustes, particulièrement dans les architectures modernes (fonctionnelles, distribuées, multi-threadées). Avec record et record struct, C# permet de modéliser des objets immuables simplement et efficacement.
Dans cet article, je vous montre comment exploiter ces types pour construire des modèles de données propres, prévisibles et faciles à manipuler.
📦 Pourquoi préférer l’immutabilité ?
- Empêche les effets de bord
- Facilite le débogage et les tests
- Rend les données thread-safe
- Représente fidèlement des états figés (DTO, snapshots)
🔖 record : le modèle de données immuable de référence
public record Person(string FirstName, string LastName);
Avantages :
- Propriétés init seulement
- Supporte l’opérateur with
- Comparaison par valeur (et non par référence)
var p1 = new Person("Alice", "Martin");
var p2 = p1 with { FirstName = "Bob" };
Console.WriteLine(p1); // Person { FirstName = Alice, LastName = Martin }
Console.WriteLine(p2); // Person { FirstName = Bob, LastName = Martin }
💡 record struct : version plus légère, sur la stack
Depuis C# 10, vous pouvez utiliser :
public readonly record struct Point(int X, int Y);
- Immuable par défaut
- Moins d’allocations, fonctionne sur la stack
- Idéal pour les structures géométriques, vecteurs, cases, etc.
⚠️ Différences entre record, class, struct, record struct
|
Type
|
Référence ou valeur ?
|
Immuable ?
|
Comparaison
|
|
class
|
Référence
|
Non (par défaut)
|
Par référence
|
|
struct
|
Valeur
|
Non (par défaut)
|
Par valeur
|
|
record
|
Référence
|
Oui (init)
|
Par valeur
|
|
record struct
|
Valeur
|
Oui (init)
|
Par valeur
|
🧪 Exemple avancé : créer un modèle métier
public record Invoice(int Id, DateTime Date, decimal Amount);
var original = new Invoice(1, DateTime.Today, 100m);
var corrected = original with { Amount = 120m };
Console.WriteLine(original); // Invoice { Id = 1, Date = ..., Amount = 100 }
Console.WriteLine(corrected); // Invoice { Id = 1, Date = ..., Amount = 120 }
🧠 Et les init properties ?
Même avec class, vous pouvez forcer l’immuabilité à l’initialisation :
public class User
{
public string Name { get; init; }
public DateTime Created { get; init; } = DateTime.Now;
}
var user = new User { Name = "Claire" };
Mais cela n’offre ni with, ni égalité par valeur.
✅ Bonnes pratiques
- Préférez record pour vos modèles métier simples
- Utilisez record struct pour les structures performantes sans GC
- Combinez avec with, init, readonly pour des objets solides et testables
- Pensez au pattern matching, très efficace avec les record
🔚 Conclusion
Les record et record struct modernisent la façon de penser vos modèles en C#. En misant sur l’immutabilité, vous construisez des applications plus maintenables, moins sujettes aux bugs et prêtes pour le multithreading.
Par expérience je sais qu'il n'est pas toujours facile de penser immutabilité, cela se rapproche beaucoup plus de l'esprit de la programmation fonctionnelle comme le permet F# sur lequel j'ai produit une série d'article à sa sortie. Et je sais que ce type de langage (et leur approche) obtient souvent un succès d'estime inversement proportionnel à leur adoption... Tout le monde en parle mais personne ne s'en sert, en tout cas dans de l'entreprise, des Apps de type LOB.
Sans basculer totalement dans le côté obscur de la force de F#, essayez d'introduire plus de fonctionnel dans votre façon de développer. Et cela peut commencer humblement par l'utilisation des types immutables...
Stay Tuned !