Introduction aux Generics en C#
Les Generics (types génériques) sont l'une des fonctionnalités les plus puissantes du langage C#, apparue dans .NET Framework 2.0. Ils permettent de créer des classes, des méthodes et des interfaces qui fonctionnent avec n'importe quel type de données, tout en conservant un typage strict. Dans cet article, nous allons voir pourquoi les generics sont nécessaires en C#, comment les utiliser et quels avantages ils offrent au développeur.
Imaginez que vous deviez écrire une méthode pour échanger les valeurs de deux variables. Sans generics, vous devriez créer des méthodes distinctes pour chaque type : SwapInt, SwapString, SwapDouble et ainsi de suite. Avec les generics, cette tâche est résolue par une seule fonction universelle.
Principaux avantages des Generics
1. Sécurité des types au moment de la compilation
Les Generics assurent la vérification des types au moment de la compilation, et non à l'exécution. Cela signifie que les erreurs liées à l'incompatibilité des types seront détectées dès les premières étapes du développement.
// Sans generics - erreur possible à l'exécutionArrayList list = new ArrayList();list.Add(123);list.Add("chaîne");int number = (int)list[1]; // InvalidCastException!
// Avec generics - erreur au moment de la compilationList<int> numbers = new List<int>();numbers.Add(123);numbers.Add("chaîne"); // Erreur de compilation !2. Performance
L'utilisation des generics permet d'éviter les opérations d'encapsulation (boxing) et de désencapsulation (unboxing) pour les types valeur. Cela améliore considérablement les performances lors du travail avec des collections.
// Boxing/Unboxing - lentArrayList list = new ArrayList();list.Add(42); // boxingint value = (int)list[0]; // unboxing
// Generics - rapide, sans boxingList<int> list = new List<int>();list.Add(42); // sans boxingint value = list[0]; // sans unboxing3. Réutilisation du code
Une seule classe ou méthode générique peut fonctionner avec n'importe quel type de données, ce qui évite la duplication de code.
Création et utilisation des Generics
Classes génériques
Examinons un exemple de création d'une classe générique pour stocker une paire de valeurs :
public class Pair<TFirst, TSecond>{ public TFirst First { get; set; } public TSecond Second { get; set; } public Pair(TFirst first, TSecond second) { First = first; Second = second; } public void Display() { Console.WriteLine($"First: {First}, Second: {Second}"); }}
// Utilisationvar pair1 = new Pair<int, string>(1, "un");var pair2 = new Pair<string, double>("PI", 3.14159);
pair1.Display(); // First: 1, Second: unpair2.Display(); // First: PI, Second: 3.14159Méthodes génériques
Les méthodes peuvent également être génériques. Voici un exemple classique — la méthode d'échange de valeurs :
public static void Swap<T>(ref T a, ref T b){ T temp = a; a = b; b = temp;}
// Utilisationint x = 5, y = 10;Swap<int>(ref x, ref y);Console.WriteLine($"x = {x}, y = {y}"); // x = 10, y = 5
string s1 = "Hello", s2 = "World";Swap(ref s1, ref s2); // le type peut être omisConsole.WriteLine($"s1 = {s1}, s2 = {s2}"); // s1 = World, s2 = HelloInterfaces génériques
Les interfaces avec generics permettent de créer des contrats flexibles :
public interface IRepository<T>{ T GetById(int id); void Add(T entity); void Update(T entity); void Delete(int id);}
public class UserRepository : IRepository<User>{ public User GetById(int id) { // Implémentation de la récupération d'utilisateur return new User(); } public void Add(User user) { // Implémentation de l'ajout } public void Update(User user) { // Implémentation de la mise à jour }