Generics en C# : pourquoi sont-ils nécessaires et comment les utiliser pour les débutants

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Que sont les Generics en C# et pourquoi sont-ils nécessaires



Imaginez que vous écrivez une méthode qui doit fonctionner avec différents types de données : des nombres entiers, des chaînes de caractères, des objets. Sans Generics (génériques), vous devriez soit dupliquer le code pour chaque type, soit utiliser object avec un cast de type ultérieur. Les deux approches entraînent des problèmes : la première conduit à la redondance, la seconde à des erreurs d'exécution.



Les Generics (types génériques) sont un mécanisme en C# qui permet de créer des classes, des méthodes, des interfaces et des délégués fonctionnant avec n'importe quel type de données, tout en conservant un typage fort. Au lieu d'écrire du code pour un type spécifique, vous utilisez un paramètre de type (généralement désigné par la lettre T), qui est remplacé par le type réel lors de l'utilisation.



Les principaux avantages des Generics :



  • Sécurité des types — le compilateur vérifie la validité de l'utilisation des types à la compilation
  • Performance — absence d'encapsulation/désencapsulation (boxing/unboxing) pour les types valeur
  • Réutilisation du code — une seule classe/méthode fonctionne avec plusieurs types
  • Lisibilité — le code devient plus propre et plus compréhensible


Problème sans Generics : exemple avec object



Avant l'apparition des Generics, les développeurs utilisaient souvent le type object pour créer des méthodes universelles. Regardons un exemple :



public class Stack{    private object[] _items = new object[10];    private int _count = 0;        public void Push(object item) { _items[_count++] = item; }    public object Pop() { return _items[--_count]; }        // Utilisation    Stack stack = new Stack();    stack.Push(123);           // Encapsulation de int en object    stack.Push("hello");       // N'importe quel type peut être ajouté    int number = (int)stack.Pop(); // Désencapsulation et cast}


Les problèmes sont évidents :

  • Lors de l'extraction, un cast explicite est nécessaire
  • On peut accidentellement ajouter une valeur d'un autre type
  • Pour les types valeur (int, double), il y a encapsulation/désencapsulation, ce qui réduit les performances


Fondamentaux des Generics en C#



Création d'une classe générique



Reprenons le même exemple de pile, mais avec les Generics :



public class Stack<T>{    private T[] _items = new T[10];    private int _count = 0;        public void Push(T item) { _items[_count++] = item; }    public T Pop() { return _items[--_count]; }}

// UtilisationStack<int> intStack = new Stack<int>();intStack.Push(123);intStack.Push(456);int number = intStack.Pop(); // Pas besoin de désencapsulation, le type est connu

Stack<string> stringStack = new Stack<string>();stringStack.Push("Hello");// stringStack.Push(123); // Erreur de compilation ! Sécurité des types


Le paramètre <T> est spécifié entre chevrons. Lors de la création d'une instance, vous spécifiez le type concret, et le compilateur génère une version spécialisée de la classe pour ce type.



Méthodes génériques



Les Generics fonctionnent non seulement avec les classes, mais aussi avec des méthodes individuelles :



public class Utils{    public static T Max<T>(T a, T b) where T : IComparable<T>    {        return a.CompareTo(b) > 0 ? a : b;    }}

// Utilisationint maxInt = Utils.Max(5, 10); // 10string maxStr = Utils.Max("apple", "banana"); // "banana"


Contraintes dans les Generics



Parfois, il est nécessaire de limiter les types pouvant être utilisés avec un générique. Pour cela, on utilise des contraintes (constraints) avec le mot-clé where :



// Contrainte : T doit être un type référencepublic class Repository<T> where T : class{                

Blogs

Book Recommendations