CodeGym /Cours /C# SELF /Contrat pour une collection : ICollection<T>

Contrat pour une collection : ICollection<T>

C# SELF
Niveau 28, Leçon 2
Disponible

1. Intro

Si avec IEnumerable<T> tu pouvais juste te balader dans le contenu déjà existant d’une collection, l’interface ICollection<T> — c’est ton ticket pour gérer la collection : ajouter, supprimer, checker le nombre d’éléments, copier dans un array et même surveiller les modifs (ouais, un peu comme dans la vraie vie quand tu veux contrôler ce qu’il y a dans ton panier de courses).

ICollection<T> est implémentée par toutes les collections modifiables de .NET, genre List<T>, HashSet<T>, Dictionary<TKey, TValue>.ValueCollection et même les moins hype comme Queue<T>, Stack<T> (ouais ouais, même les queues et stacks !). C’est le contrat de base pour toutes les classes de collections qui te laissent bidouiller leur contenu.

Famille des interfaces de collections


graph TD
    A[IEnumerable<T>]
    B[ICollection<T>]
    C[IList<T>]
    D[IDictionary<TKey, TValue>]
    E[Queue<T>, Stack<T>, List<T>, HashSet<T>...]

    A --> B
    B --> C
    B --> D
    B --> E
ICollection<T> étend IEnumerable<T> et c’est l’interface de base pour la plupart des collections.

2. Qu’est-ce qu’il y a dans l’interface ICollection<T> ?

Contrairement à IEnumerable<T>, qui sert juste à l’itération (foreach), ICollection<T> — c’est un peu le couteau suisse des interfaces : t’as une méthode pour chaque galère !

Voilà le contenu principal :


public interface ICollection<T> : IEnumerable<T>
{
    int Count { get; }
    bool IsReadOnly { get; }
    void Add(T item);
    void Clear();
    bool Contains(T item);
    void CopyTo(T[] array, int arrayIndex);
    bool Remove(T item);
}

Liste des méthodes et propriétés :

  • Count — le nombre d’éléments dans la collection. Comme ta fonction de comptage préférée.
  • IsReadOnly — est-ce qu’on peut modifier la collection ? Si true, c’est lecture seule (genre pour certains wrappers).
  • Add(T item) — ajoute un élément à la collection. Un de tes outils principaux !
  • Clear() — vire tous les éléments. Nettoyage total.
  • Contains(T item) — check rapide : est-ce que cet élément est dans la collection ?
  • CopyTo(T[] array, int arrayIndex) — copie les éléments dans un array classique, à partir d’un index donné. Pratique pour filer des données à des vieux API ou des méthodes "old school".
  • Remove(T item) — supprime un élément (s’il est là).

3. Les vraies galères du dev

Pourquoi c’est important de piger comment cette interface marche ? En vrai, tu vas souvent écrire du code générique qui bosse avec n’importe quelle collection. Genre, une fonction peut prendre une liste, une queue ou un set — et on s’en fout du type précis. Tant que la collection implémente ICollection<T>, tu peux ajouter, supprimer des éléments, checker la taille et même copier dans un array. C’est la base pour des libs flexibles et des algos universels.

Exemple IRL : l’interface ICollection<T> se retrouve souvent dans les paramètres de méthodes et les propriétés d’API qui te filent une collection à modifier. Genre, dans les ORM connus (Entity Framework, Dapper), les propriétés de type ICollection<T> servent à décrire des collections d’objets liés.

Exemples d’utilisation

On va écrire une méthode générique qui prend n’importe quelle collection et y ajoute un élément. Le seul prérequis — elle doit implémenter ICollection<T>.


// Méthode générique pour ajouter un élément
void AppendItem<T>(ICollection<T> collection, T item)
{
    collection.Add(item);
}

Maintenant tu peux utiliser cette méthode avec List<T>, HashSet<T>, ou même ta propre implémentation ! Voilà à quoi ça ressemble :


var numbers = new List<int> { 1, 2, 3 };
AppendItem(numbers, 42);

var uniqueNames = new HashSet<string> { "Alice", "Bob" };
AppendItem(uniqueNames, "Charlie");

C’est marrant — on s’en fiche que ce soit une liste ou un set, tant que c’est une ICollection<T>. T’étonne pas, c’est comme ça que sont faits plein de méthodes génériques dans .NET !

4. Lien avec d’autres collections

ICollection<T> — c’est pas juste un contrat théorique stylé, c’est vraiment la base de quasi toutes les collections que tu vas croiser en .NET.

Pour voir à quel point c’est universel, tente ce code :


void ShowCollectionInfo<T>(ICollection<T> collection)
{
    Console.WriteLine($"Nombre d’éléments : {collection.Count}");
    Console.WriteLine($"Modifiable : {!collection.IsReadOnly}");

    Console.WriteLine("Éléments :");
    foreach (var item in collection)
    {
        Console.WriteLine(item);
    }
}

// Pour List<T>
var list = new List<string> { "pomme", "orange" };
ShowCollectionInfo(list);

// Pour HashSet<T>
var set = new HashSet<string> { "pomme", "orange" };
ShowCollectionInfo(set);

Le résultat sera nickel pour n’importe quel type de collection qui implémente cette interface. Essaie avec une queue, un stack, même un array wrappé !

5. Collections en lecture seule

L’interface ICollection<T> a la propriété IsReadOnly qui dit si tu peux modifier la collection. À ne pas confondre avec la propriété classique des arrays ! Si tu tombes sur une collection où IsReadOnly == true, tenter d’ajouter ou de supprimer un élément va balancer une exception.

Exemple :


// On crée un array et on le wrappe dans une ReadOnlyCollection
var array = new int[] { 1, 2, 3 };
ICollection<int> readOnly = Array.AsReadOnly(array);

// On tente d’ajouter un élément
try
{
    readOnly.Add(4); // Va lancer NotSupportedException
}
catch (NotSupportedException)
{
    Console.WriteLine("Impossible d’ajouter : collection en lecture seule !");
}

Ça arrive par exemple quand tu reçois une collection d’une source externe et que tu peux pas la modifier — mais tu peux lire les éléments et connaître leur nombre.

6. Tableau comparatif des méthodes des collections populaires via ICollection<T>

Collection Add() Remove() Contains() Clear() CopyTo() IsReadOnly
List<T>
Oui Oui Oui Oui Oui Non
HashSet<T>
Oui* Oui Oui Oui Oui Non
Queue<T> / Stack<T>
Non/Non Non/Non Non/Non Oui/Oui Oui/Oui Non
ReadOnlyCollection<T>
Non Non Oui Non Oui Oui
Dictionary<TKey, TValue>.ValueCollection
Non Non Oui Non Oui Oui/Non*

Pour HashSet<T> la méthode Add retourne true si l’élément a été ajouté, sinon false.

7. CopyTo — pourquoi ça peut servir

La méthode CopyTo permet de balancer vite fait le contenu d’une collection dans un array classique. Pratique si tu dois, par exemple, renvoyer des données à une fonction d’un vieux API qui prend que des arrays, ou faire du traitement massif avec des outils d’array.


var contactsArray = new Contact[book.Count];
((ICollection<Contact>)book).CopyTo(contactsArray, 0);
// Maintenant tu peux utiliser contactsArray à l’ancienne

8. Exemple

Dans notre appli console d’apprentissage, imaginons qu’on commence à coder un carnet d’adresses. On a une classe Contact, et le carnet peut être basé sur n’importe quelle collection, tant qu’elle gère l’ajout, la suppression et l’itération.


public class Contact
{
    public string Name { get; set; }
    public string Phone { get; set; }
}

public class AddressBook
{
    private readonly ICollection<Contact> _contacts;

    public AddressBook(ICollection<Contact> contacts)
    {
        _contacts = contacts;
    }

    public void AddContact(Contact contact)
    {
        _contacts.Add(contact);
    }

    public bool RemoveContact(Contact contact)
    {
        return _contacts.Remove(contact);
    }

    public int Count => _contacts.Count;

    public void PrintAll()
    {
        foreach (var contact in _contacts)
        {
            Console.WriteLine($"{contact.Name} — {contact.Phone}");
        }
    }
}

Maintenant notre carnet d’adresses peut tourner avec List<Contact>, HashSet<Contact>, ou même — plus tard — avec ta propre collection basée sur une base de données ou des fichiers ! Exemple d’utilisation :


var book = new AddressBook(new List<Contact>());
book.AddContact(new Contact { Name = "Ivan", Phone = "+7-123-456" });
book.AddContact(new Contact { Name = "Maria", Phone = "+7-987-654" });

Console.WriteLine($"Nombre total de contacts : {book.Count}");
book.PrintAll();

Si tu veux éviter les doublons, tu peux juste passer un set à AddressBook au lieu d’une liste :


var book = new AddressBook(new HashSet<Contact>());

(il faut que Contact gère bien la comparaison, mais ça, c’est pour une autre conf !)

9. Particularités des méthodes et pièges classiques

Attention direct : toutes les collections qui implémentent ICollection<T> ne se comportent pas pareil ! Par exemple, tenter de modifier une collection quand IsReadOnly == true va balancer une exception. Et si tu bosses avec HashSet<T>, la méthode Add retourne true seulement si l’élément a vraiment été ajouté (il était pas déjà là). En plus, selon la collection, les méthodes peuvent être plus ou moins rapides — mais l’interface ne garantit rien là-dessus.

Autre truc fréquent : si la collection est utilisée par plusieurs threads, modifier la collection dans un thread et l’itérer dans un autre peut causer des exceptions. Pour les scénarios multi-thread, il faut des collections spéciales (ConcurrentBag<T>, ConcurrentQueue<T> etc. — on verra ça plus tard).

Encore un piège : si tu copies une collection avec CopyTo, après, les modifs dans l’array et dans la collection seront indépendantes. L’array, c’est une zone mémoire à part.

2
Mission
C# SELF, niveau 28, leçon 2
Bloqué
Création et modification d'une collection en utilisant ICollection
Création et modification d'une collection en utilisant ICollection
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION