Partage via


Expressions de collection – Référence du langage C#

Utilisez une expression de collection pour créer des valeurs de collection courantes. Une expression de collection est une syntaxe terse que vous pouvez affecter à de nombreux types de collection différents. Une expression de collection contient une séquence d’éléments entre [ et ] crochets.

La documentation de référence du langage C# décrit la version la plus récente du langage C#. Il contient également la documentation initiale des fonctionnalités dans les préversions publiques pour la prochaine version du langage.

La documentation identifie toute fonctionnalité introduite en premier dans les trois dernières versions de la langue ou dans les préversions publiques actuelles.

Conseil / Astuce

Pour savoir quand une fonctionnalité a été introduite en C#, consultez l’article sur l’historique des versions du langage C#.

L’exemple suivant déclare une System.Span<T> d’éléments string et les initialise aux jours de la semaine :

Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
    Console.WriteLine(day);
}

Vous pouvez convertir une expression de collection en plusieurs types de collection différents. Le premier exemple montre comment initialiser une variable à l’aide d’une expression de collection. Le code suivant montre la plupart des autres emplacements où vous pouvez utiliser une expression de collection :

// Initialize private field:
private static readonly ImmutableArray<string> _months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

// property with expression body:
public IEnumerable<int> MaxDays =>
    [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

public int Sum(IEnumerable<int> values) =>
    values.Sum();

public void Example()
{
    // As a parameter:
    int sum = Sum([1, 2, 3, 4, 5]);
}

Vous ne pouvez pas utiliser une expression de collection où une constante au moment de la compilation est attendue, par exemple lors de l’initialisation d’une constante ou comme valeur par défaut pour un argument de méthode.

Les deux exemples précédents utilisaient des constantes comme éléments d’une expression de collection. Vous pouvez également utiliser des variables pour les éléments, comme illustré dans l’exemple suivant :

string hydrogen = "H";
string helium = "He";
string lithium = "Li";
string beryllium = "Be";
string boron = "B";
string carbon = "C";
string nitrogen = "N";
string oxygen = "O";
string fluorine = "F";
string neon = "Ne";
string[] elements = [hydrogen, helium, lithium, beryllium, boron, carbon, nitrogen, oxygen, fluorine, neon];
foreach (var element in elements)
{
    Console.WriteLine(element);
}

Élément Spread

Utilisez un élément.. de propagation pour les valeurs de collection inline dans une expression de collection. L’exemple suivant crée une collection pour l’alphabet complet en combinant une collection de voyelles, une collection des consonnes et la lettre « y », qui peut être :

string[] vowels = ["a", "e", "i", "o", "u"];
string[] consonants = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
                       "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"];
string[] alphabet = [.. vowels, .. consonants, "y"];

L’élément de propagation ..vowels, lorsqu’il est évalué, produit cinq éléments : "a", "e", "i", "o"et "u". L’élément de propagation ..consonants produit 20 éléments, le nombre dans le groupeconsonants. L’expression d’un élément de propagation doit être énumérable à l’aide d’une foreach instruction. Comme illustré dans l’exemple précédent, vous pouvez combiner des éléments répartis avec des éléments individuels dans une expression de collection.

Conversions

Vous pouvez convertir une expression de collection en différents types de collection, notamment :

Remarque

Vous ne pouvez pas utiliser d’expressions de collection pour initialiser des tableaux inline. Les tableaux inline nécessitent une syntaxe d’initialisation différente.

Important

Une expression de collection crée toujours une collection qui comprend tous les éléments de l’expression de collection, quel que soit le type cible de la conversion. Par exemple, lorsque la cible de la conversion est System.Collections.Generic.IEnumerable<T>, le code généré évalue l’expression de collection et stocke les résultats dans une collection en mémoire.

Ce comportement est distinct de celui de LINQ, où une séquence peut ne pas être instanciée tant qu’elle n’est pas énumérée. Vous ne pouvez pas utiliser d’expressions de collection pour générer une séquence infinie qui ne sera pas énumérée.

Le compilateur utilise l’analyse statique pour déterminer le moyen le plus performant de créer la collection déclarée avec une expression de collection. Par exemple, l’expression de collection vide [] peut être écrite sous la forme Array.Empty<T>() à condition que la cible ne soit pas modifiée après l’initialisation. Quand la cible est un System.Span<T> ou un System.ReadOnlySpan<T>, le stockage peut être alloué par la pile. La spécification de la fonctionnalité des expressions de collection spécifie les règles que le compilateur doit suivre.

De nombreuses API sont surchargées avec plusieurs types de collection en tant que paramètres. Étant donné qu’une expression de collection peut être convertie en divers types d’expression différents, ces API peuvent nécessiter des casts au niveau de l’expression de collection pour spécifier la conversion correcte. Les règles de conversion suivantes résolvent certaines ambiguïtés :

  • Une meilleure conversion d’élément est préférée à une meilleure conversion de type de collection. En d’autres termes, le type d’éléments de l’expression de collection a plus d’importance que le type de la collection. Ces règles sont décrites dans la spécification de fonctionnalité pour une meilleure conversion à partir de l’expression de collection.
  • La conversion en Span<T>, ReadOnlySpan<T>ou un autre type ref struct est préférable à une conversion vers un type de struct non ref.
  • La conversion en type noninterface est préférable à une conversion en type d’interface.

Lorsque vous convertissez une expression de collection en un Span ou ReadOnlySpan, le contexte sécurisé de l’objet span provient du contexte sûr de tous les éléments inclus dans l’étendue. Pour obtenir des règles détaillées, consultez la spécification d’expression Collection.

Générateur de collection

Les expressions de collection fonctionnent avec n’importe quel type de collection à comportement adéquat. Une collection à comportement adéquat présente les caractéristiques suivantes :

  • La valeur de Count ou de Length dans une collection countable produit la même valeur que le nombre d’éléments lorsque ceux-ci sont énumérés.
  • Les types dans l’espace de noms System.Collections.Generic sont censés ne pas avoir d’effets secondaires. Par conséquent, le compilateur peut optimiser les scénarios dans lesquels de tels types peuvent être utilisés comme valeurs intermédiaires, mais sinon ils ne sont pas exposés.
  • Un appel à un membre applicable .AddRange(x) sur une collection entraîne la même valeur finale que l’itération x et l’ajout de toutes ses valeurs énumérées individuellement à la collection..Add

Tous les types de collection du runtime .NET ont un comportement adéquat.

Avertissement

Si un type de collection personnalisé n’est pas bien comportementé, le comportement n’est pas défini lorsque vous utilisez ce type de collection avec des expressions de collection.

Vos types optent pour la prise en charge des expressions de collection en écrivant une méthode Create() et en appliquant le System.Runtime.CompilerServices.CollectionBuilderAttribute au type de collection pour indiquer la méthode du générateur. Par exemple, considérez une application qui utilise des mémoires tampons de longueur fixe de 80 caractères. Cette classe pourrait ressembler au code suivant :

public class LineBuffer : IEnumerable<char>
{
    private readonly char[] _buffer;
    private readonly int _count;

    public LineBuffer(ReadOnlySpan<char> buffer)
    {
        _buffer = new char[buffer.Length];
        _count = buffer.Length;
        for (int i = 0; i < _count; i++)
        {
            _buffer[i] = buffer[i];
        }
    }

    public int Count => _count;
    
    public char this[int index]
    {
        get
        {
            if (index >= _count)
                throw new IndexOutOfRangeException();
            return _buffer[index];
        }
    }

    public IEnumerator<char> GetEnumerator()
    {
        for (int i = 0; i < _count; i++)
        {
            yield return _buffer[i];
        }
    }
    
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    // etc
}

Vous souhaitez l’utiliser avec des expressions de collection, comme indiqué dans l’exemple suivant :

LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];

Le LineBuffertype d’impléments IEnumerable<char>, de sorte que le compilateur le reconnaît comme une collection d’éléments char. Le paramètre de type de l’interface System.Collections.Generic.IEnumerable<T> implémentée indique le type d’élément. Vous devez ajouter deux ajouts à votre application pour pouvoir attribuer des expressions de collection à un objet LineBuffer. Tout d’abord, vous devez créer une classe qui contient une méthode Create :

internal static class LineBufferBuilder
{
    internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}

La méthode Create doit retourner un objet LineBuffer, et elle doit prendre un paramètre unique du type ReadOnlySpan<char>. Le paramètre de type du ReadOnlySpan doit correspondre au type d’élément de la collection. Une méthode de générateur qui retourne une collection générique a le générique ReadOnlySpan<T> comme paramètre. La méthode doit être accessible et static.

Enfin, vous devez ajouter le CollectionBuilderAttribute à la LineBuffer déclaration de classe  :

[CollectionBuilder(typeof(LineBufferBuilder), "Create")]

Le premier paramètre fournit le nom de la classe Builder. Le deuxième attribut fournit le nom de la méthode du générateur.