CodeGym /Cours /C# SELF /Avantages des collections Generics

Avantages des collections Generics

C# SELF
Niveau 27, Leçon 1
Disponible

1. Un peu d’histoire

À l’époque, en C#, t’avais deux kiffs pour gérer les collections : utiliser des tableaux ou les fameuses collections "pas vraiment typées" — genre ArrayList, où tu pouvais balancer des objets de n’importe quel type. Liberté, non ? Mais dès que tu foutais une string et un int dans la même collection — c’était le bazar : impossible de récupérer un élément sans faire des checks de type relous (et pas toujours fiables). Et puis — des bugs chelous qui popent de nulle part et des danses avec le casting (conversion de types).

Exemple d’une collection non-generic (ArrayList) :


using System.Collections;

ArrayList trucs = new ArrayList();
trucs.Add(42);
trucs.Add("Salut C#");

int nombre = (int)trucs[0]; // OK
string texte = (string)trucs[1]; // OK

int fail = (int)trucs[1]; // BOUM ! InvalidCastException (erreur à l’exécution)

Ouais, le compilateur dit rien — l’erreur sort seulement à l’exécution ! C’est comme ouvrir le frigo et tomber sur une casserole chaude — surprise !

C’est quoi le délire avec les generics ?

Les collections génériques (Generics) sont arrivées pour qu’on puisse créer des collections avec un type de contenu garanti. Ça donne trois gros avantages :

  • Sécurité des types à la compilation. Le compilateur va pas te laisser foutre n’importe quoi dans la collection.
  • Pratique : plus besoin de caster à la main à chaque fois.
  • Performance : pas de perte de temps avec le "boxing/unboxing" des types valeurs (boxing/unboxing).

2. C’est quoi les Generics en vrai ?

Generics — c’est une façon magique de décrire des structures de données et des méthodes pour qu’elles marchent avec n’importe quel type, tout en restant bien typées.

Imagine une boîte universelle, que tu peux utiliser pour des livres ou des chaussettes, mais un seul type à la fois. Si la boîte est déclarée "que pour les livres" — personne pourra y mettre des chaussettes. Pareil avec les collections génériques : si tu crées une collection de int, tu pourras pas y glisser une string par accident.

Exemple de déclaration d’une classe générique


public class Boite<T>
{
    public T Valeur { get; set; }
}

var boiteDeInt = new Boite<int> { Valeur = 42 };
var boiteDeString = new Boite<string> { Valeur = "Salut Generics !" };

T — c’est le "paramètre de type", qui dit avec quoi ta boîte va bosser. C# va toujours te demander de préciser le type que tu mets dedans.

Collections génériques dans .NET

Dans le .NET Framework, quasi toutes les collections modernes ont une version generic. Genre :

  • List<T> — une liste dynamique d’éléments de type T.
  • Dictionary<TKey, TValue> — un tableau associatif (dico) avec des clés et des valeurs.
  • Queue<T>, Stack<T> — files et piles.
  • et plein d’autres.

3. Comment ça marche : l’intérieur des Generics

Paramètres de type

Quand tu déclares une collection genre List<int>, le compilateur C# crée une version spéciale (specialization) de cette classe juste pour le type int. Si tu déclares List<string>, il crée une autre version, mais pour les strings, etc.

Et pour toi, dev, c’est de la magie :


List<int> nombres = new List<int>();
nombres.Add(1); // Tu peux ajouter que des int

List<string> mots = new List<string>();
mots.Add("salut"); // Tu peux ajouter que des string

Si tu tentes d’ajouter un autre type, le compilateur va râler direct :


nombres.Add("fail"); // ERREUR à la compilation !

Sécurité des types : Compile-time vs. Run-time

Ça veut dire que les erreurs comme celle d’au-dessus n’arriveront même pas à l’exécution (run-time). Ta collection est tellement protégée que personne peut la corrompre — le compilateur veille au grain, comme un cerbère devant la porte.

Sous le capot

  • Pour les types référence (genre string, object), plus besoin de cast relou.
  • Pour les types valeur (int, double), fini les soucis de "boxing/unboxing" (boxing/unboxing).
  • L’utilisation des generics en .NET ne fait pas du code en trop — le CLR optimise tout ça au moment de la JIT-compilation.

4. Comment les generics boostent les perfs

On va encore souligner les super-pouvoirs que les Generics filent à ton code :

Sécurité des types (Type Safety) :

C’est sûrement le plus important. Grâce aux Generics, le compilateur devient ton garde du corps perso, il laisse passer aucun "intrus" dans ta collection. Tu peux être sûr que List<Produit> contiendra que des objets Produit, pas des strings ou des nombres au pif. Ça vire tout un tas de bugs qui, avant, n’apparaissaient qu’à l’exécution et faisaient tout péter (InvalidCastException). Ton code devient plus fiable et prévisible.

Performance :

Comme on l’a dit, pour les types valeur (genre int, double ou DateTime), les Generics permettent de mieux gérer la mémoire et le temps CPU. Pour des collections avec des millions d’éléments ou qui bougent souvent, ça peut tout changer. Au lieu de trimballer des oranges d’une caisse à un sac et retour, tu les mets direct dans la bonne caisse.

Réutilisation du code (Code Reusability) :

Les Generics te permettent d’écrire une seule fois du code qui marche avec plein de types différents, sans copier-coller. Genre, t’as besoin d’une fonction pour échanger deux variables. Sans Generics, tu devrais écrire SwapInt(ref int a, ref int b), SwapString(ref string a, ref string b), SwapProduit(ref Produit a, ref Produit b) et ainsi de suite. Avec les Generics, une seule fonction : Swap<T>(ref T a, ref T b). Pareil pour les collections : pas besoin de IntList, StringList, ProduitList – List<T> suffit. Ton code est plus compact, facile à maintenir et à faire évoluer.

5. Méthodes génériques et tes propres classes generics

Méthodes génériques

Les generics, c’est pas que pour les collections ! Tu peux écrire tes propres méthodes génériques qui marchent avec n’importe quel type. Ça rend ton code super flexible.


//On met le type-paramètre T juste après le nom de la méthode
public static void Swap<T>(ref T x, ref T y)
{
    T temp = x;
    x = y;
    y = temp;
}

// Utilisation :	
int a = 10, b = 20;
Swap(ref a, ref b); // Maintenant a == 20, b == 10

string un = "un", deux = "deux";
Swap(ref un, ref deux); // Ça marche aussi pour les strings !

Note que le compilateur C# peut deviner tout seul le type-paramètre de la méthode selon les variables que tu lui passes. Donc dans l’exemple, pas besoin de préciser le type à Swap.

Tes propres classes generics

Tu peux même créer tes propres "boîtes" (classes génériques). C’est plus simple que tu crois :


public class Paire<TPremier, TSecond>
{
    public TPremier Premier { get; set; }
    public TSecond Second { get; set; }
}

// Utilisation :
var paire = new Paire<int, string> { Premier = 42, Second = "réponse" };

6. Exemple d’utilisation des collections generics

Revenons à notre "liste de tâches" (To-Do List) qu’on a commencé à coder dans les confs précédentes. Avant, on stockait les tâches dans un tableau ou on les affichait juste à l’écran. Maintenant — on les garde dynamiquement dans une List<string>.

Exemple : ajout dynamique de tâches dans la liste


using System;
using System.Collections.Generic;

class Programme
{
    static void Main()
    {
        List<string> taches = new List<string>();

        Console.WriteLine("Tape une tâche (ou une ligne vide pour finir) :");
        string saisie;
        while (!string.IsNullOrWhiteSpace(saisie = Console.ReadLine()))
        {
            taches.Add(saisie);
            Console.WriteLine("Tâche ajoutée ! Tape encore (ou une ligne vide pour finir) :");
        }

        Console.WriteLine("\nTa liste de tâches pour aujourd’hui :");
        foreach (string tache in taches)
        {
            Console.WriteLine("- " + tache);
        }
    }
}

Qu’est-ce qui est cool ici ?

  • La collection s’agrandit toute seule quand tu ajoutes des tâches.
  • Impossible d’ajouter autre chose qu’une string dans la liste.
  • Tu peux facilement parcourir la liste et afficher son contenu.

7. Erreurs classiques des débutants, particularités et tips

Passer des collections non-generic aux generics, ça peut perturber. Voilà quelques situations classiques que les débutants rencontrent :

  • Essayer d’ajouter un élément du mauvais type (List<int> nombres = new List<int>(); nombres.Add("salut"); // Erreur).
  • Vouloir mélanger des types dans une seule collection — là il faut choisir un type de base (genre List<object>) — et après tu devras toujours recaster au bon type.
  • Oublier qu’il existe les constraints, écrire des generics trop "larges" et choper des erreurs bizarres à la compilation.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION