Generics et typage en C# : pourquoi sont-ils indispensables ?
Imaginez que vous construisez une maison, mais qu'à chaque fois que vous devez enfoncer un clou, vous sculptez un nouveau marteau spécialement pour ce clou. Absurde ? C'est exactement à quoi ressemble un code sans génériques (Generics). En C#, les Generics sont ce marteau universel qui convient à toutes les tâches, sans perdre en précision.
Aujourd'hui, nous allons voir comment les Generics et le typage en C# transforment votre code d'un ensemble de béquilles disparates en un système cohérent, flexible et sécurisé. Vous apprendrez à écrire des méthodes et des classes universelles qui fonctionnent avec n'importe quel type de données, sans sacrifier les performances. C'est parti !
Que sont les Generics et pourquoi en avez-vous besoin ?
Les Generics (génériques) sont un mécanisme qui permet de créer des classes, des méthodes et des interfaces fonctionnant avec n'importe quel type de données, tout en conservant un typage strict à la compilation. Cela semble compliqué ? En réalité, c'est simple.
Avant l'apparition des Generics en C# (version 2.0, 2005), les développeurs utilisaient deux approches :
- Copier-coller — création d'une classe séparée pour chaque type (int, string, double, etc.).
- object — utilisation du type de base object, entraînant un boxing/unboxing et une perte de performances.
Les deux options sont mauvaises. La première fait perdre du temps, la seconde fait perdre en performances. Les Generics résolvent les deux problèmes à la fois.
À quoi ressemblent les Generics en pratique ?
// Sans Generics : copier-coller pour chaque typepublic class IntList{ private int[] items = new int[10]; public void Add(int item) { /* ... */ }}
public class StringList{ private string[] items = new string[10]; public void Add(string item) { /* ... */ }}
// Avec Generics : une classe pour tous les caspublic class GenericList<T>{ private T[] items = new T[10]; public void Add(T item) { /* ... */ }}
// Utilisationvar intList = new GenericList<int>();var stringList = new GenericList<string>();Remarquez : GenericList<T> est un modèle. À la place de T, vous insérez n'importe quel type (int, string, votre propre classe), et le compilateur crée une version spécifique du code pour ce type. Pas de duplication, pas d'object.
Typage en C# : statique, strict, sécurisé
Avant d'aller plus loin, rappelons que C# est un langage à typage statique. Cela signifie que le type de chaque variable est déterminé à la compilation. Les Generics ne violent pas cette règle, au contraire, ils la renforcent.
Lorsque vous écrivez List<int>, le compilateur sait exactement que la liste ne contient que des entiers. Essayez d'ajouter une chaîne de caractères — vous obtiendrez une erreur de compilation, et non une exception à l'exécution. C'est ce qu'on appelle la sécurité des types.
Generics vs object : bataille des performances
// Mauvais exemple avec objectArrayList badList = new ArrayList();badList.Add(123); // Boxing int -> objectint value = (int)badList[0]; // Unboxing object -> int
// Bon exemple avec GenericsList<int> goodList = new List<int>();goodList.Add(123); // Sans boxingint value = goodList[0]; // Sans unboxingPour les types valeur (int, double, struct), la différence de performances peut atteindre 10 à 20 fois. Pour les types référence (class), il n'y a pas de boxing, mais les Generics gagnent quand même grâce à l'absence de conversions de types.
Méthodes génériques : flexibilité sans code superflu
Les Generics fonctionnent non seulement avec les classes, mais aussi avec les méthodes. Supposons que vous deviez écrire une méthode qui échange deux éléments. Sans Generics, vous devriez écrire de nombreuses surcharges :
// Sans Genericspublic void SwapInt(ref int a, ref int b) { int temp = a; a = b; b = temp; }public void SwapString(ref string a, ref string b) { string temp