Sammlungsausdrücke – C#-Programmiersprachenreferenz
Sie können einen Sammlungsausdruck verwenden, um allgemeine Sammlungswerte zu erstellen. Ein Sammlungsausdruck ist eine nicht ausführliche Syntax, die bei der Auswertung vielen verschiedenen Sammlungstypen zugewiesen werden kann. Ein Sammlungsausdruck enthält eine Abfolge von Elementen zwischen eckigen Klammern ([
und ]
). 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);
}
Ein Sammlungsausdruck kann in viele verschiedene Sammlungstypen konvertiert werden. Im ersten Beispiel wurde veranschaulicht, wie eine Variable mithilfe eines Sammlungsausdrucks 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 Sammlungsausdruck verwenden, in dem eine Kompilierzeitkonstante erwartet wird, z. B. bei der Initialisierung 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
Sie verwenden das Spread-Element ..
, um Sammlungswerte in einem Sammlungsausdruck inline festzulegen. 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
. Die Variable in einem Spread-Element muss mit einer foreach
-Anweisung aufzählbar sein. Wie im vorherigen Beispiel gezeigt, können Sie Spread-Elemente mit einzelnen Elementen in einem Sammlungsausdruck kombinieren.
Konvertierungen
Ein Sammlungsausdruck kann in viele verschiedene Sammlungstypen konvertiert werden, u. a.:
- System.Span<T> und System.ReadOnlySpan<T>.
- Arrays.
- Jeder Typ mit einer create-Methode, deren Parametertyp
ReadOnlySpan<T>
lautet, wobei eine implizite Konvertierung vom Typ des Sammlungsausdrucks inT
erfolgt. - Jeder Typ, der einen Auflistungsinitialisierer unterstützt, z. B. System.Collections.Generic.List<T>. In der Regel bedeutet diese Anforderung, dass der Typ System.Collections.Generic.IEnumerable<T> unterstützt und es eine zugängliche
Add
-Methode zum Hinzufügen von Elementen zur Sammlung gibt. Es muss eine implizite Konvertierung vom Typ der Sammlungsausdruckselemente in den Elementtyp der Sammlung vorhanden sein. Bei Spread-Elementen muss eine implizite Konvertierung vom Typ des Spread-Elements in den Elementtyp der Sammlung vorhanden sein. - Jede der folgenden Schnittstellen:
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:
- 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 ein Sammlungsausdruck in Span
oder ReadOnlySpan
konvertiert wird, wird der sichere Kontext des Span-Objekts aus dem sicheren Kontext aller Elemente übernommen, 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
oderLength
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 offengelegt werden.
- Ein Aufruf einiger anwendbarer
.AddRange(x)
-Member für eine Sammlung führt zu demselben Endwert wie eine Iteration überx
und das einzelne Hinzufügen aller enumerierten Werte zur Sammlung mit.Add
.
Alle Sammlungstypen in der .NET-Laufzeit sind gut konzipiert.
Warnung
Wenn sich ein benutzerdefinierter Sammlungstyp nicht gut konzipiert ist, ist das Verhalten bei Verwendung dieses Sammlungstyps mit Sammlungsausdrücken nicht definiert.
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 = new char[80];
public LineBuffer(ReadOnlySpan<char> buffer)
{
int number = (_buffer.Length < buffer.Length) ? _buffer.Length : buffer.Length;
for (int i = 0; i < number; i++)
{
_buffer[i] = buffer[i];
}
}
public IEnumerator<char> GetEnumerator() => _buffer.AsEnumerable<char>().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _buffer.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 builder-Methode, die eine generische Sammlung zurückgibt, hätte das generische ReadOnlySpan<T>
-Element als Parameter. 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.