Collections

Le runtime .NET fournit de nombreux types de collection qui stockent et gèrent des groupes d’objets associés. Certains types de collection, tels que System.Array, System.Span<T>et System.Memory<T> sont reconnus dans le langage C#. En outre, les interfaces telles que System.Collections.Generic.IEnumerable<T> sont reconnues dans le langage pour énumérer les éléments d’une collection.

Les collections offrent une façon flexible quand il s’agit d’utiliser des groupes d’objets. Vous pouvez classifier différentes collections selon ces caractéristiques :

  • Accès aux éléments : chaque collection peut être énumérée pour accéder à chaque élément dans l’ordre. Certaines collections accèdent à des éléments par index, la position de l’élément dans une collection ordonnée. L’exemple le plus courant est System.Collections.Generic.List<T>. D’autres collections accèdent à des éléments par clé, où une valeur est associée à une clé unique. L’exemple le plus courant est System.Collections.Generic.Dictionary<TKey,TValue>. Vous choisissez entre ces types de collection en fonction de la façon dont votre application accède aux éléments.
  • Profil de niveau de performance : chaque collection a des profils de niveau de performance différents pour les actions telles que l’ajout d’un élément, la recherche d’un élément ou la suppression d’un élément. Vous pouvez choisir un type de collection en fonction des opérations utilisées le plus dans votre application.
  • Développer et réduire dynamiquement : la plupart des collections prenant en charge l’ajout ou la suppression d’éléments dynamiquement. Notamment, Array, System.Span<T>et System.Memory<T> ne le font pas.

En plus de ces caractéristiques, le runtime fournit des collections spécialisées qui empêchent d’ajouter ou de supprimer des éléments ou de modifier les éléments de la collection. D’autres collections spécialisées assurent la sécurité de l’accès simultané dans les applications multithreads.

Vous trouverez tous les types de collection dans la référence de l’API .NET . Pour plus d’informations, voir Types de collection couramment utilisés et Sélection d’une classe de collections.

Remarque

Pour les exemples de cet article, vous devrez peut-être ajouter des directives d’utilisation pour les System.Collections.Generic et System.Linq espaces de noms.

Groupes sont représentées par System.Array et prennent en charge la syntaxe dans le langage C#. Cette syntaxe fournit des déclarations plus concises pour les variables de groupes.

System.Span<T> est un type ref struct qui fournit une capture instantanée sur une séquence d’éléments sans copier ces éléments. Le compilateur applique des règles de sécurité pour s’assurer que les Span ne sont plus accessibles une fois que la séquence qu’il référence n’est plus dans l’étendue. Il est utilisé dans de nombreuses API .NET pour améliorer le niveau de performance. Memory<T> fournit un comportement similaire lorsque vous ne pouvez pas utiliser de type ref struct.

À compter de C# 12, tous les types de collection peuvent être initialisés à l’aide d’une expression de collection.

Collections indexables

Une collection indexable est une collection où vous pouvez accéder à chaque élément à l’aide de son index. Son index est le nombre d’éléments avant celui-ci dans la séquence. Par conséquent, la référence d’élément par index 0 est le premier élément, l’index 1 est le deuxième, et ainsi de suite. Ces exemples utilisent la classe List<T>. Il s’agit de la collection indexable la plus courante.

L’exemple suivant crée et initialise une liste de chaînes, supprime un élément et ajoute un élément à la fin de la liste. Après chaque modification, itère au sein des chaînes à l’aide d’une instruction foreach foreach ou d’une boucle for :

// Create a list of strings by using a
// collection initializer.
List<string> salmons = ["chinook", "coho", "pink", "sockeye"];

// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook coho pink sockeye

// Remove an element from the list by specifying
// the object.
salmons.Remove("coho");


// Iterate using the index:
for (var index = 0; index < salmons.Count; index++)
{
    Console.Write(salmons[index] + " ");
}
// Output: chinook pink sockeye

// Add the removed element
salmons.Add("coho");
// Iterate through the list.
foreach (var salmon in salmons)
{
    Console.Write(salmon + " ");
}
// Output: chinook pink sockeye coho

L’exemple suivant supprime les éléments d’une liste par index. À la place d’une instruction foreach, il utilise une instruction for qui itère dans l’ordre décroissant. La méthode RemoveAt, les éléments après l’élément supprimé ont une valeur d’index moins élevée.

List<int> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

// Remove odd numbers.
for (var index = numbers.Count - 1; index >= 0; index--)
{
    if (numbers[index] % 2 == 1)
    {
        // Remove the element by specifying
        // the zero-based index in the list.
        numbers.RemoveAt(index);
    }
}

// Iterate through the list.
// A lambda expression is placed in the ForEach method
// of the List(T) object.
numbers.ForEach(
    number => Console.Write(number + " "));
// Output: 0 2 4 6 8

Pour le type d’éléments de List<T>, vous pouvez également définir votre propre classe. Dans l’exemple suivant, la classe Galaxy qui est utilisée par List<T> est définie dans le code.

private static void IterateThroughList()
{
    var theGalaxies = new List<Galaxy>
    {
        new (){ Name="Tadpole", MegaLightYears=400},
        new (){ Name="Pinwheel", MegaLightYears=25},
        new (){ Name="Milky Way", MegaLightYears=0},
        new (){ Name="Andromeda", MegaLightYears=3}
    };

    foreach (Galaxy theGalaxy in theGalaxies)
    {
        Console.WriteLine(theGalaxy.Name + "  " + theGalaxy.MegaLightYears);
    }

    // Output:
    //  Tadpole  400
    //  Pinwheel  25
    //  Milky Way  0
    //  Andromeda  3
}

public class Galaxy
{
    public string Name { get; set; }
    public int MegaLightYears { get; set; }
}

Collections de paires clé/valeur

Ces exemples utilisent la classe Dictionary<TKey,TValue>. Il s’agit de la collection de dictionnaire la plus courante. Une collection de dictionnaires vous permet d’accéder aux éléments de la collection en utilisant la clé de chaque élément. Chaque ajout au dictionnaire se compose d’une valeur et de sa clé associée.

L’exemple suivant crée une collection Dictionary et itère au sein du dictionnaire à l’aide d’une instruction foreach.

private static void IterateThruDictionary()
{
    Dictionary<string, Element> elements = BuildDictionary();

    foreach (KeyValuePair<string, Element> kvp in elements)
    {
        Element theElement = kvp.Value;

        Console.WriteLine("key: " + kvp.Key);
        Console.WriteLine("values: " + theElement.Symbol + " " +
            theElement.Name + " " + theElement.AtomicNumber);
    }
}

public class Element
{
    public required string Symbol { get; init; }
    public required string Name { get; init; }
    public required int AtomicNumber { get; init; }
}

private static Dictionary<string, Element> BuildDictionary() =>
    new ()
    {
        {"K",
            new (){ Symbol="K", Name="Potassium", AtomicNumber=19}},
        {"Ca",
            new (){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        {"Sc",
            new (){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        {"Ti",
            new (){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };

L’exemple suivant utilise la méthode ContainsKey et la propriété Item[] de Dictionary pour rechercher rapidement un élément par clé. En C#, la propriété Item vous permet d’accéder à un élément de la collection elements à l’aide d’elements[symbol].

if (elements.ContainsKey(symbol) == false)
{
    Console.WriteLine(symbol + " not found");
}
else
{
    Element theElement = elements[symbol];
    Console.WriteLine("found: " + theElement.Name);
}

L’exemple suivant utilise à la place la méthode TryGetValue pour rechercher rapidement un élément par clé.

if (elements.TryGetValue(symbol, out Element? theElement) == false)
    Console.WriteLine(symbol + " not found");
else
    Console.WriteLine("found: " + theElement.Name);

Itérateurs

Un itérateur est utilisé pour exécuter une itération personnalisée sur une collection. Un itérateur peut être une méthode ou un accesseur get. Un itérateur utilise une instruction yield return pour retourner chaque élément de la collection un par un.

Vous appelez un itérateur en utilisant une instruction foreach. Chaque itération de la boucle foreach appelle l’itérateur. Quand une instruction yield return est atteinte dans l’itérateur, une expression est retournée et la localisation actuelle dans le code est retenue. L’exécution est redémarrée à partir de cet emplacement la prochaine fois que l’itérateur est appelé.

Pour plus d’informations, consultez Itérateurs (C#).

L'exemple suivant utilise une méthode Iterator. La méthode Iterator a une instruction yield return qui se trouve à l’intérieur d’une boucle for. Dans la méthode ListEvenNumbers, chaque itération du corps de l’instruction foreach crée un appel à la méthode Iterator, qui continue sur l’instruction yield return suivante.

private static void ListEvenNumbers()
{
    foreach (int number in EvenSequence(5, 18))
    {
        Console.Write(number.ToString() + " ");
    }
    Console.WriteLine();
    // Output: 6 8 10 12 14 16 18
}

private static IEnumerable<int> EvenSequence(
    int firstNumber, int lastNumber)
{
    // Yield even numbers in the range.
    for (var number = firstNumber; number <= lastNumber; number++)
    {
        if (number % 2 == 0)
        {
            yield return number;
        }
    }
}

Language Integrated Query (LINQ) et collections

Language Integrated Query (LINQ) peut être utilisées pour accéder aux collections. Les requêtes LINQ fournissent des fonctionnalités de filtrage, de classement et de regroupement. Pour plus d’informations, consultez Bien démarrer avec LINQ en C#.

L’exemple suivant exécute une requête LINQ sur un List générique. La requête LINQ retourne une autre collection qui contient les résultats.

private static void ShowLINQ()
{
    List<Element> elements = BuildList();

    // LINQ Query.
    var subset = from theElement in elements
                 where theElement.AtomicNumber < 22
                 orderby theElement.Name
                 select theElement;

    foreach (Element theElement in subset)
    {
        Console.WriteLine(theElement.Name + " " + theElement.AtomicNumber);
    }

    // Output:
    //  Calcium 20
    //  Potassium 19
    //  Scandium 21
}

private static List<Element> BuildList() => new()
    {
        { new(){ Symbol="K", Name="Potassium", AtomicNumber=19}},
        { new(){ Symbol="Ca", Name="Calcium", AtomicNumber=20}},
        { new(){ Symbol="Sc", Name="Scandium", AtomicNumber=21}},
        { new(){ Symbol="Ti", Name="Titanium", AtomicNumber=22}}
    };