Référence collections
En C#, les collections de référence constituent un élément fondamental pour la gestion et la manipulation efficace des données en mémoire. Contrairement aux types valeur, les collections de référence stockent des objets dans le heap, permettant une gestion dynamique et flexible des données. Elles sont essentielles pour les applications modernes nécessitant le stockage, l’accès et le traitement d’ensembles de données complexes. L'utilisation appropriée des collections de référence, telles que List
Les collections de référence en C# s'intègrent parfaitement avec les concepts clés du langage, incluant la syntaxe C# stricte, les structures de données avancées, les algorithmes optimisés et les principes de la programmation orientée objet (POO). Elles permettent de résoudre des problèmes variés, depuis le tri et la recherche jusqu’à la gestion de flux de données en temps réel dans des architectures logicielles complexes. Maîtriser ces collections est crucial pour concevoir des applications robustes, éviter les fuites mémoire et garantir une manipulation efficace des ressources.
Dans ce guide, vous apprendrez à créer, manipuler et optimiser les collections de référence en C#, à comprendre leurs performances et leurs limites, et à appliquer les meilleures pratiques pour les projets professionnels. Vous découvrirez également comment ces collections s’intègrent dans des modèles de conception, favorisant une architecture scalable et maintenable. Ce contenu est conçu pour renforcer votre capacité à résoudre des problèmes complexes en C# et à prendre des décisions éclairées sur l’utilisation des structures de données dans des systèmes réels.
Exemple de Base
textusing System;
using System.Collections.Generic;
namespace ReferenceCollectionsExample
{
class Program
{
static void Main(string\[] args)
{
// Création d'une liste de chaînes
List<string> fruits = new List<string> { "Pomme", "Banane", "Cerise" };
// Ajout d'un élément
fruits.Add("Orange");
// Accès et modification
fruits[1] = "Mangue";
// Parcours de la collection
Console.WriteLine("Liste des fruits:");
foreach (string fruit in fruits)
{
Console.WriteLine(fruit);
}
// Recherche d'un élément
if (fruits.Contains("Cerise"))
{
Console.WriteLine("Cerise est présente dans la liste.");
}
}
}
}
L’exemple ci-dessus illustre l’utilisation de collections de référence avec List
Le foreach parcourt chaque élément de manière sécurisée, évitant les erreurs d’index et favorisant une syntaxe claire et lisible. L’appel à fruits.Contains("Cerise") montre un exemple simple de recherche dans une collection, utilisant un algorithme interne optimisé pour List
Dans un contexte réel, ces concepts permettent de gérer des ensembles de données dynamiques, tels que des listes d’utilisateurs, des commandes ou des événements. L’exemple met en évidence l’importance des collections de référence pour la POO en C#, car chaque élément est un objet, pouvant encapsuler des propriétés et des méthodes, facilitant ainsi la conception de systèmes modulaires et maintenables.
Exemple Pratique
textusing System;
using System.Collections.Generic;
namespace AdvancedReferenceCollections
{
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
// Création d'un dictionnaire pour stocker des objets Person avec un identifiant unique
Dictionary<int, Person> people = new Dictionary<int, Person>();
people.Add(1, new Person { Name = "Alice", Age = 30 });
people.Add(2, new Person { Name = "Bob", Age = 25 });
people.Add(3, new Person { Name = "Charlie", Age = 35 });
// Recherche d'une personne par clé
if (people.TryGetValue(2, out Person foundPerson))
{
Console.WriteLine($"Personne trouvée: {foundPerson.Name}, {foundPerson.Age} ans");
}
// Parcours du dictionnaire
foreach (var entry in people)
{
Console.WriteLine($"ID: {entry.Key}, Nom: {entry.Value.Name}");
}
// Suppression sécurisée d'une entrée
people.Remove(3);
}
}
}
Advanced C# Implementation
textusing System;
using System.Collections.Generic;
using System.Linq;
namespace EnterpriseCollections
{
class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
class Program
{
static void Main(string[] args)
{
try
{
// Création d'un HashSet pour éviter les doublons
HashSet<Product> products = new HashSet<Product>(new ProductComparer())
{
new Product { Name = "Laptop", Price = 1200 },
new Product { Name = "Tablet", Price = 450 },
new Product { Name = "Laptop", Price = 1200 } // sera ignoré
};
// Utilisation de LINQ pour filtrer et trier
var expensiveProducts = products
.Where(p => p.Price > 500)
.OrderByDescending(p => p.Price)
.ToList();
foreach (var product in expensiveProducts)
{
Console.WriteLine($"Produit: {product.Name}, Prix: {product.Price}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Erreur: {ex.Message}");
}
}
}
class ProductComparer : IEqualityComparer<Product>
{
public bool Equals(Product x, Product y)
{
return x.Name == y.Name && x.Price == y.Price;
}
public int GetHashCode(Product obj)
{
return obj.Name.GetHashCode() ^ obj.Price.GetHashCode();
}
}
}
Les exemples pratiques démontrent des concepts avancés des collections de référence en C#. Le dictionnaire (Dictionary\
Ces exemples abordent également les bonnes pratiques : gestion des exceptions via try-catch pour éviter les plantages, utilisation de types génériques pour un typage sûr, et application d’algorithmes LINQ pour optimiser les opérations sur les collections. Les développeurs doivent également considérer la performance en choisissant la structure appropriée (List
En pratique, ces concepts s’appliquent à la manipulation de données en mémoire, à la conception de systèmes modulaires et à l’implémentation de modèles de conception tels que Repository ou Observer. Comprendre et maîtriser ces collections permet de résoudre efficacement des problèmes complexes, de maintenir une architecture performante et de minimiser les risques de fuites mémoire ou d’erreurs de logique.
C# best practices and common pitfalls
text// Best practices et pièges courants
* Utiliser des collections génériques pour éviter les conversions de type coûteuses et les exceptions
* Choisir la structure de données adaptée selon les besoins : List<T>, Dictionary\<TKey,TValue>, Queue<T>, Stack<T>, HashSet<T>
* Toujours gérer les exceptions lors des manipulations de collections pour éviter les plantages
* Préférer l’utilisation de foreach pour le parcours sécurisé des collections
* Éviter les boucles imbriquées inutiles pour optimiser les performances
* Utiliser LINQ avec parcimonie et optimiser les requêtes pour de grandes collections
* Supprimer les références aux objets inutilisés pour permettre au garbage collector de libérer la mémoire
* Tester les performances avec des collections volumineuses pour détecter les goulets d’étranglement
* Assurer la sécurité des données lors de l’accès concurrent aux collections (Collections.Concurrent si nécessaire)
* Préférer les comparateurs personnalisés pour HashSet ou Dictionary afin de garantir l’unicité et l’intégrité des données
📊 Référence Complète
C# Element/Method | Description | Syntax | Example | Notes |
---|---|---|---|---|
List<T> | Collection dynamique ordonnée | List<int> numbers = new List<int>(); | numbers.Add(10); | Accès indexé, redimensionnement automatique |
List<T>.Add() | Ajoute un élément à la liste | numbers.Add(10); | numbers.Add(5); | Complexité O(1) en moyenne |
List<T>.Remove() | Supprime un élément | numbers.Remove(10); | numbers.Remove(5); | Retourne true si supprimé |
List<T>.Contains() | Vérifie la présence d’un élément | numbers.Contains(10); | bool exists = numbers.Contains(5); | Recherche linéaire |
List<T>.Count | Nombre d’éléments | numbers.Count | int total = numbers.Count; | Lecture seule, actualisé dynamiquement |
Dictionary\<TKey,TValue> | Collection clé-valeur | Dictionary\<int,string> dict = new Dictionary\<int,string>(); | dict.Add(1,"Alice"); | Accès rapide par clé |
Dictionary\<TKey,TValue>.Add() | Ajoute une paire clé-valeur | dict.Add(1,"Alice"); | dict.Add(2,"Bob"); | Lève une exception si clé existe |
Dictionary\<TKey,TValue>.TryGetValue() | Récupère la valeur en toute sécurité | dict.TryGetValue(2,out string val); | bool found = dict.TryGetValue(1,out string name); | Evite les exceptions de clé inexistante |
Dictionary\<TKey,TValue>.Remove() | Supprime une paire clé-valeur | dict.Remove(1); | dict.Remove(2); | Retourne true si supprimé |
Dictionary\<TKey,TValue>.Keys | Collection des clés | var keys = dict.Keys; | foreach(var key in dict.Keys) | Lecture seule |
Dictionary\<TKey,TValue>.Values | Collection des valeurs | var values = dict.Values; | foreach(var val in dict.Values) | Lecture seule |
Queue<T> | FIFO collection | Queue<int> q = new Queue<int>(); | q.Enqueue(10); | First In First Out |
Queue<T>.Enqueue() | Ajoute à la queue | q.Enqueue(10); | q.Enqueue(20) | O(1) |
Queue<T>.Dequeue() | Retire de la queue | q.Dequeue(); | int first = q.Dequeue(); | O(1), exception si vide |
Queue<T>.Peek() | Accède au premier élément sans retirer | q.Peek(); | int peek = q.Peek(); | Exception si vide |
Stack<T> | LIFO collection | Stack<int> s = new Stack<int>(); | s.Push(10); | Last In First Out |
Stack<T>.Push() | Ajoute au sommet | s.Push(10); | s.Push(20) | O(1) |
Stack<T>.Pop() | Retire du sommet | s.Pop(); | int top = s.Pop(); | O(1), exception si vide |
Stack<T>.Peek() | Accède au sommet sans retirer | s.Peek(); | int top = s.Peek(); | Exception si vide |
HashSet<T> | Collection unique | HashSet<int> hs = new HashSet<int>(); | hs.Add(10); | Évite les doublons |
HashSet<T>.Add() | Ajoute un élément | hs.Add(10); | hs.Add(5); | Retourne false si déjà présent |
HashSet<T>.Remove() | Supprime un élément | hs.Remove(10); | hs.Remove(5); | Retourne true si supprimé |
HashSet<T>.Contains() | Vérifie la présence | hs.Contains(10); | bool exists = hs.Contains(5); | O(1) en moyenne |
IEnumerable<T> | Interface de parcours | IEnumerable<int> numbers | foreach(var n in numbers) | Base pour foreach et LINQ |
ICollection<T> | Interface de collection | ICollection<int> coll | coll.Count | Supporte Count, Add, Remove |
IList<T> | Interface de liste | IList<int> list | list\[0] = 10 | Accès indexé |
IReadOnlyCollection<T> | Lecture seule | IReadOnlyCollection<int> readOnly | int total = readOnly.Count | Pas de modification |
IReadOnlyList<T> | Lecture seule indexée | IReadOnlyList<int> readOnlyList | int first = readOnlyList\[0] | Lecture seule |
ObservableCollection<T> | Collection avec notification | ObservableCollection<int> oc = new ObservableCollection<int>(); | oc.CollectionChanged += Handler | Utile pour MVVM |
ConcurrentDictionary\<TKey,TValue> | Thread-safe dictionnaire | ConcurrentDictionary\<int,string> cd = new ConcurrentDictionary\<int,string>(); | cd.TryAdd(1,"A"); | Sécurisé multi-thread |
ConcurrentQueue<T> | Thread-safe queue | ConcurrentQueue<int> cq = new ConcurrentQueue<int>(); | cq.TryDequeue(out int val); | FIFO multi-thread |
ConcurrentStack<T> | Thread-safe stack | ConcurrentStack<int> cs = new ConcurrentStack<int>(); | cs.TryPop(out int val); | LIFO multi-thread |
SortedList\<TKey,TValue> | Dictionnaire trié par clé | SortedList\<int,string> sl = new SortedList\<int,string>(); | sl.Add(1,"A"); | Tri automatique |
SortedDictionary\<TKey,TValue> | Dictionnaire trié | SortedDictionary\<int,string> sd = new SortedDictionary\<int,string>(); | sd.Add(1,"A"); | Plus performant pour insertions fréquentes |
LinkedList<T> | Liste doublement chaînée | LinkedList<int> ll = new LinkedList<int>(); | ll.AddLast(10); | Accès séquentiel |
LinkedList<T>.AddFirst() | Ajoute en début | ll.AddFirst(5); | ll.AddFirst(1) | O(1) |
LinkedList<T>.AddLast() | Ajoute à la fin | ll.AddLast(10); | ll.AddLast(20) | O(1) |
LinkedList<T>.Remove() | Supprime un élément | ll.Remove(10); | ll.Remove(20) | O(n) |
LinkedList<T>.Find() | Recherche un élément | ll.Find(10); | var node = ll.Find(10) | Retourne LinkedListNode<T> |
Stack<T>.Clear() | Vide la pile | s.Clear(); | s.Clear() | Libère références |
Queue<T>.Clear() | Vide la queue | q.Clear(); | q.Clear() | Libère références |
List<T>.Clear() | Vide la liste | numbers.Clear(); | numbers.Clear() | Libère références |
Dictionary\<TKey,TValue>.Clear() | Vide le dictionnaire | dict.Clear(); | dict.Clear() | Libère références |
List<T>.IndexOf() | Retourne l’index | numbers.IndexOf(5); | int idx = numbers.IndexOf(5) | -1 si absent |
ArrayList | Collection non générique | ArrayList arr = new ArrayList(); | arr.Add(10); | Legacy, éviter en C# moderne |
Queue<T>.Contains() | Vérifie la présence | q.Contains(10); | bool exists = q.Contains(5) | O(n) |
Stack<T>.Contains() | Vérifie la présence | s.Contains(10); | bool exists = s.Contains(5) | O(n) |
HashSet<T>.UnionWith() | Union de collections | hs1.UnionWith(hs2); | hs1.UnionWith(hs2) | Mutatif |
HashSet<T>.IntersectWith() | Intersection | hs1.IntersectWith(hs2); | hs1.IntersectWith(hs2) | Mutatif |
HashSet<T>.ExceptWith() | Différence | hs1.ExceptWith(hs2); | hs1.ExceptWith(hs2) | Mutatif |
HashSet<T>.SymmetricExceptWith() | Différence symétrique | hs1.SymmetricExceptWith(hs2); | hs1.SymmetricExceptWith(hs2) | Mutatif |
Dictionary\<TKey,TValue>.ContainsKey() | Vérifie la clé | dict.ContainsKey(1); | bool exists = dict.ContainsKey(2) | O(1) |
Dictionary\<TKey,TValue>.ContainsValue() | Vérifie la valeur | dict.ContainsValue("Alice"); | bool exists = dict.ContainsValue("Bob") | O(n) |
Queue<T>.TryPeek() | Lecture sécurisée | q.TryPeek(out int val); | bool ok = q.TryPeek(out val) | Évite exceptions |
Stack<T>.TryPeek() | Lecture sécurisée | s.TryPeek(out int val); | bool ok = s.TryPeek(out val) | Évite exceptions |
Stack<T>.TryPop() | Retrait sécurisé | s.TryPop(out int val); | bool ok = s.TryPop(out val) | Évite exceptions |
Queue<T>.TryDequeue() | Retrait sécurisé | q.TryDequeue(out int val); | bool ok = q.TryDequeue(out val) | Évite exceptions |
List<T>.Insert() | Insère à un index | numbers.Insert(1, 99); | numbers.Insert(0,10) | Décalage des éléments |
List<T>.RemoveAt() | Supprime par index | numbers.RemoveAt(1); | numbers.RemoveAt(0) | Décalage des éléments |
Dictionary\<TKey,TValue>.Count | Nombre de paires | dict.Count | int total = dict.Count | Lecture seule |
HashSet<T>.Count | Nombre d’éléments uniques | hs.Count | int total = hs.Count | Lecture seule |
Queue<T>.Count | Nombre d’éléments | q.Count | int total = q.Count | Lecture seule |
Stack<T>.Count | Nombre d’éléments | s.Count | int total = s.Count | Lecture seule |
SortedList\<TKey,TValue>.Count | Nombre d’éléments | sl.Count | int total = sl.Count | Lecture seule |
LinkedList<T>.Count | Nombre d’éléments | ll.Count | int total = ll.Count | Lecture seule |
ObservableCollection<T>.Count | Nombre d’éléments | oc.Count | int total = oc.Count | Lecture seule |
ConcurrentDictionary\<TKey,TValue>.Count | Nombre d’éléments | cd.Count | int total = cd.Count | Lecture seule |
Nombre d’éléments | cq.Count | int total = cq.Count | Lecture seule | |
ConcurrentStack<T>.Count | Nombre d’éléments | cs.Count | int total = cs.Count | Lecture seule |
List<T>.Find() | Recherche un élément | numbers.Find(x => x > 5); | int found = numbers.Find(n => n==5) | Retourne l’élément ou défaut |
List<T>.FindAll() | Recherche multiple | numbers.FindAll(x => x>5); | var results = numbers.FindAll(n => n>10) | Retourne List<T> |
List<T>.Sort() | Tri de la liste | numbers.Sort(); | numbers.Sort(); | Utilise QuickSort interne |
List<T>.Reverse() | Inverse la liste | numbers.Reverse(); | numbers.Reverse(); | In-place |
List<T>.ToArray() | Conversion en tableau | numbers.ToArray(); | int\[] arr = numbers.ToArray(); | Copie |
List<T>.ForEach() | Parcours avec action | numbers.ForEach(n => Console.WriteLine(n)); | numbers.ForEach(n=>Console.WriteLine(n)); | Expression lambda |
List<T>.Exists() | Vérifie condition | numbers.Exists(n => n>5); | bool exists = numbers.Exists(n=>n>10) | Retourne bool |
Dictionary\<TKey,TValue>.Clear() | Vide le dictionnaire | dict.Clear(); | dict.Clear(); | Supprime toutes paires |
List<T>.AddRange() | Ajoute plusieurs éléments | numbers.AddRange(new List<int>{1,2}); | numbers.AddRange(new List<int>{3,4}); | O(n) |
Queue<T>.Clear() | Vide la queue | q.Clear(); | q.Clear() | Libère références |
Stack<T>.Clear() | Vide la pile | s.Clear(); | s.Clear() | Libère références |
HashSet<T>.Clear() | Vide le HashSet | hs.Clear(); | hs.Clear() | Libère références |
LinkedList<T>.Clear() | Vide la liste chaînée | ll.Clear(); | ll.Clear() | Libère références |
ObservableCollection<T>.Clear() | Vide la collection | oc.Clear(); | oc.Clear() | Notifie les abonnés |
ConcurrentDictionary\<TKey,TValue>.Clear() | Vide le dictionnaire | cd.Clear(); | cd.Clear() | Thread-safe |
ConcurrentQueue<T>.Clear() | Vide la queue | cq.Clear(); | cq.Clear() | Thread-safe |
ConcurrentStack<T>.Clear() | Vide la pile | cs.Clear(); | cs.Clear() | Thread-safe |
📊 Complete C# Properties Reference
Property | Values | Default | Description | C# Support |
---|---|---|---|---|
Count | int | 0 | Nombre d’éléments dans la collection | All C# collections |
Capacity | int | 4 | Nombre d’éléments que la collection peut contenir avant redimensionnement automatique | List<T> |
IsReadOnly | bool | false | Indique si la collection est en lecture seule | ICollection<T>, IList<T> |
Keys | TKey collection | N/A | Retourne les clés d’un dictionnaire | Dictionary\<TKey,TValue> |
Values | TValue collection | N/A | Retourne les valeurs d’un dictionnaire | Dictionary\<TKey,TValue> |
Comparer | IEqualityComparer | Default comparer | Comparateur utilisé pour HashSet ou Dictionary | HashSet<T>, Dictionary\<TKey,TValue> |
SyncRoot | object | null | Objet pour synchronisation multi-thread | ICollection |
IsSynchronized | bool | false | Indique si la collection est thread-safe | ICollection |
CountChanged | Event | N/A | Événement déclenché lors du changement de Count | ObservableCollection<T> |
CollectionChanged | Event | N/A | Événement déclenché lors d’une modification | ObservableCollection<T> |
IsFixedSize | bool | false | Indique si la taille est fixe | ArrayList, IList |
Capacity | int | 0 | Capacité maximale actuelle de la collection | ArrayList |
La maîtrise des collections de référence en C# offre un contrôle avancé sur la gestion des données et la conception des systèmes. Vous avez appris à manipuler des listes, dictionnaires, files, piles et ensembles uniques, à utiliser LINQ pour transformer et filtrer les données et à gérer les exceptions pour une sécurité et une performance optimales. Ces compétences permettent de construire des applications modulaires, performantes et maintenables, en intégrant les principes de la POO et les bonnes pratiques C#.
Les prochaines étapes incluent l’étude des collections concurrentes, l’optimisation des algorithmes de tri et de recherche, ainsi que l’intégration de ces collections dans des patterns de conception tels que Repository ou Observer. Il est recommandé d’expérimenter avec des collections volumineuses pour mesurer les performances et comprendre l’impact des choix de structure sur l’architecture logicielle. Pour approfondir vos connaissances, explorez la documentation officielle Microsoft, les articles sur LINQ et les ressources avancées sur les structures de données en C#. Appliquer ces concepts dans des projets réels consolide votre expertise et prépare à développer des systèmes d’entreprise robustes et évolutifs.
🧠 Testez Vos Connaissances
Test Your Knowledge
Test your understanding of this topic with practical questions.
📝 Instructions
- Lisez chaque question attentivement
- Sélectionnez la meilleure réponse pour chaque question
- Vous pouvez refaire le quiz autant de fois que vous le souhaitez
- Votre progression sera affichée en haut