Freigeben über


Sammlungsausdrücke – C#-Programmiersprachenreferenz

Verwenden Sie einen Auflistungsausdruck , um allgemeine Sammlungswerte zu erstellen. Ein Auflistungsausdruck ist eine terse Syntax, die Sie vielen verschiedenen Auflistungstypen zuweisen können. Ein Sammlungsausdruck enthält eine Abfolge von Elementen zwischen eckigen Klammern ([ und ]).

Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie eine erste Dokumentation zu Funktionen in der öffentlichen Vorschau für die kommende Sprachversion.

In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.

Tipp

Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.

Im folgenden Beispiel wird eine System.Span<T>-Struktur von string-Elementen deklariert und als Wochentage initialisiert:

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

Sie können einen Sammlungsausdruck in viele verschiedene Sammlungstypen konvertieren. Im ersten Beispiel wurde veranschaulicht, wie eine Variable mithilfe eines Auflistungsausdrucks initialisiert wird. Der folgende Code zeigt viele andere Einsatzmöglichkeiten für einen Sammlungsausdruck:

// 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]);
}

Sie können keinen Auflistungsausdruck verwenden, in dem eine Kompilierungszeitkonstante erwartet wird, z. B. beim Initialisieren einer Konstante oder als Standardwert für ein Methodenargument.

In den beiden vorherigen Beispielen werden Konstanten als Elemente eines Sammlungsausdrucks verwendet. Sie können auch Variablen für die Elemente verwenden, wie im folgenden Beispiel gezeigt:

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);
}

Spread-Element

Verwenden Sie ein Spread-Element.. , um Inlineauflistungswerte in einem Auflistungsausdruck zu verwenden. Im folgenden Beispiel wird eine Sammlung für das vollständige Alphabet erstellt, indem eine Sammlung der Vokale, eine Sammlung der Konsonanten und der Buchstabe „y“ kombiniert werden, der beides sein kann:

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"];

Das Spread-Element ..vowels erzeugt bei der Auswertung fünf Elemente: "a", "e", "i", "o" und "u". Das Spread-Element ..consonants erzeugt 20 Elemente, die Anzahl im Array consonants. Der Ausdruck in einem Spread-Element muss mithilfe einer foreach Anweisung aufgezählt werden können. Wie im vorherigen Beispiel gezeigt, können Sie Spread-Elemente mit einzelnen Elementen in einem Sammlungsausdruck kombinieren.

Konvertierungen

Sie können einen Sammlungsausdruck in verschiedene Sammlungstypen konvertieren, darunter:

Hinweis

Sie können Sammlungsausdrücke nicht zum Initialisieren von Inlinearrays verwenden. Inlinearrays erfordern unterschiedliche Initialisierungssyntax.

Wichtig

Ein Sammlungsausdruck erstellt immer eine Sammlung, die alle Elemente im Sammlungsausdruck enthält, unabhängig vom Zieltyp der Konvertierung. Wenn beispielsweise das Ziel der Konvertierung System.Collections.Generic.IEnumerable<T> ist, wertet der generierte Code den Sammlungsausdruck aus und speichert die Ergebnisse in einer Speichersammlung.

Dieses Verhalten unterscheidet sich von LINQ, wo eine Sequenz möglicherweise nicht instanziiert wird, bis sie aufgezählt wird. Sie können keine Sammlungsausdrücke verwenden, um eine unendliche Sequenz zu generieren, die nicht aufgezählt wird.

Der Compiler verwendet statische Analysen, um die leistungsstärkste Methode zum Erstellen der Auflistung zu bestimmen, die mit einem Auflistungsausdruck deklariert wurde. Beispielsweise kann der leere Auflistungsausdruck [] als Array.Empty<T>() realisiert werden, wenn das Ziel nach der Initialisierung nicht geändert wird. Wenn das Ziel ein System.Span<T> oder System.ReadOnlySpan<T> ist, kann der Speicher möglicherweise stapelweise zugewiesen werden. Die Featurespezifikation für Auflistungsausdrücke gibt die Regeln an, die der Compiler einhalten muss.

Viele APIs werden mit mehreren Sammlungstypen als Parameter überladen. Da ein Sammlungsausdruck in viele verschiedene Ausdruckstypen konvertiert werden kann, erfordern diese APIs möglicherweise Umwandlungen in den Sammlungsausdruck, um die richtige Konvertierung anzugeben. Die folgenden Konvertierungsregeln beheben einige der Mehrdeutigkeiten:

  • Eine bessere Elementkonvertierung wird gegenüber einer besseren Sammlungstypkonvertierung bevorzugt. Mit anderen Worten, der Typ der Elemente im Auflistungsausdruck hat mehr Bedeutung als der Typ der Auflistung. Diese Regeln werden in der Featurespezifikation beschrieben, um eine bessere Konvertierung aus dem Sammlungsausdruck zu ermöglichen.
  • Die Konvertierung in Span<T>, ReadOnlySpan<T> oder einen anderen ref struct-Typ ist besser als eine Konvertierung in einen Typ, der kein ref struct-Typ ist.
  • Die Konvertierung in einen noninterface-Typ ist besser als eine Konvertierung in einen interface-Typ.

Wenn Sie einen Auflistungsausdruck in einen Span oder ReadOnlySpaneine Auflistung konvertieren, stammt der sichere Kontext des Span-Objekts aus dem sicheren Kontext aller Elemente, die in der Spanne enthalten sind. Ausführliche Regeln finden Sie in der Spezifikation zu Sammlungsausdrücken.

Sammlungsgenerator

Sammlungsausdrücke funktionieren mit jedem Auflistungstyp, der gut konzipiert ist. Eine gut konzipierte Sammlung weist folgende Merkmale auf:

  • Der Wert von Count oder Length für eine zählende Auflistung erzeugt denselben Wert wie die Anzahl der Elemente, wenn sie aufgezählt werden.
  • Es wird angenommen, dass die Typen im System.Collections.Generic-Namespace keine Nebeneffekte aufweisen. Daher kann der Compiler Szenarien optimieren, in denen solche Typen als Zwischenwerte verwendet werden, andernfalls aber nicht verfügbar gemacht werden.
  • Ein Aufruf an ein bestimmtes Element .AddRange(x) für eine Auflistung führt zu demselben Endwert wie das Durchlaufen x und Hinzufügen aller enumerierten Werte einzeln zur Auflistung mit .Add.

Alle Sammlungstypen in der .NET-Laufzeit sind gut konzipiert.

Warnung

Wenn sich ein benutzerdefinierter Sammlungstyp nicht gut verhält, ist das Verhalten nicht definiert, wenn Sie diesen Sammlungstyp mit Auflistungsausdrücken verwenden.

Ihre Typen aktivieren die Unterstützung von Sammlungsausdrücken, indem eine Create()-Methode geschrieben und System.Runtime.CompilerServices.CollectionBuilderAttribute auf den Sammlungstyp angewendet wird, um die builder-Methode anzugeben. Betrachten Sie beispielsweise eine Anwendung, die Puffer mit einer festen Länge von 80 Zeichen verwendet. Diese Klasse würde etwa wie der folgende Code aussehen:

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
}

Sie möchten sie mit Sammlungsausdrücken verwenden, wie im folgenden Beispiel gezeigt:

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

Der Typ LineBuffer implementiert das IEnumerable<char>-Element, sodass der Compiler es als Sammlung von char-Elementen erkennt. Der Typparameter der implementierten System.Collections.Generic.IEnumerable<T>-Schnittstelle gibt den Elementtyp an. Sie müssen Ihrer Anwendung zwei Ergänzungen hinzufügen, um einem LineBuffer-Objekt Sammlungsausdrücke zuzuweisen. Zunächst müssen Sie eine Klasse erstellen, die eine Create-Methode enthält:

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

Die Create-Methode muss ein LineBuffer-Objekt zurückgeben und einen einzelnen Parameter des Typs ReadOnlySpan<char> verwenden. Der Typparameter von ReadOnlySpan muss mit dem Elementtyp der Sammlung übereinstimmen. Eine Generatormethode, die eine generische Auflistung zurückgibt, weist das Generische ReadOnlySpan<T> als Parameter auf. Die Methode muss zugänglich und vom Typ static sein.

Zum Schluss müssen Sie CollectionBuilderAttribute zur LineBuffer-Klassendeklaration hinzufügen:

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

Der erste Parameter stellt den Namen der Klasse Builder bereit. Das zweite Attribut stellt den Namen der builder-Methode bereit.