Wyrażenia kolekcji — dokumentacja języka C#
Możesz użyć wyrażenia kolekcji, aby utworzyć wspólne wartości kolekcji. Wyrażenie kolekcji jest składnią terse, która po ocenie może być przypisana do wielu różnych typów kolekcji. Wyrażenie kolekcji zawiera sekwencję elementów między nawiasami [
i ]
. Poniższy przykład deklaruje element string
i System.Span<T> inicjuje je do dni tygodnia:
Span<string> weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
foreach (var day in weekDays)
{
Console.WriteLine(day);
}
Wyrażenie kolekcji można przekonwertować na wiele różnych typów kolekcji. W pierwszym przykładzie pokazano, jak zainicjować zmienną przy użyciu wyrażenia kolekcji. Poniższy kod przedstawia wiele innych lokalizacji, w których można użyć wyrażenia kolekcji:
// 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]);
}
Nie można użyć wyrażenia kolekcji, w którym oczekiwana jest stała czasu kompilacji, na przykład inicjowanie stałej lub jako wartość domyślna argumentu metody.
Oba poprzednie przykłady używały stałych jako elementów wyrażenia kolekcji. Można również użyć zmiennych dla elementów, jak pokazano w poniższym przykładzie:
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);
}
Element rozłożony
Element spread ..
służy do wbudowanych wartości kolekcji w wyrażeniu kolekcji. Poniższy przykład tworzy kolekcję dla pełnego alfabetu, łącząc kolekcję ślubów, kolekcję spółgłosek i literę "y", która może być następująca:
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"];
Element spread ..vowels
, podczas oceny, generuje pięć elementów: "a"
, , "e"
"i"
, "o"
, i "u"
. Element ..consonants
spread tworzy 20 elementów— liczbę w tablicy consonants
. Zmienna w elemecie spread musi być wyliczana przy użyciu instrukcji foreach
. Jak pokazano w poprzednim przykładzie, można połączyć elementy rozłożone z poszczególnymi elementami w wyrażeniu kolekcji.
Konwersje
Wyrażenie kolekcji można przekonwertować na różne typy kolekcji, w tym:
- System.Span<T> i System.ReadOnlySpan<T>.
- Tablice.
- Dowolny typ z metodą create, której typ parametru to
ReadOnlySpan<T>
miejsce niejawnej konwersji z typu wyrażenia kolekcji naT
. - Dowolny typ obsługujący inicjator kolekcji, taki jak System.Collections.Generic.List<T>. Zwykle to wymaganie oznacza, że typ obsługuje System.Collections.Generic.IEnumerable<T> i istnieje
Add
dostępna metoda dodawania elementów do kolekcji. Musi istnieć niejawna konwersja typu elementów wyrażeń kolekcji na typ elementu kolekcji. W przypadku elementów rozłożonych musi istnieć niejawna konwersja typu elementu spread na typ elementu kolekcji. - Dowolny z następujących interfejsów:
Ważne
Wyrażenie kolekcji zawsze tworzy kolekcję zawierającą wszystkie elementy w wyrażeniu kolekcji, niezależnie od typu docelowego konwersji. Na przykład gdy celem konwersji jest System.Collections.Generic.IEnumerable<T>, wygenerowany kod oblicza wyrażenie kolekcji i przechowuje wyniki w kolekcji w pamięci.
To zachowanie różni się od LINQ, gdzie sekwencja może nie być tworzone, dopóki nie zostanie wyliczona. Nie można użyć wyrażeń kolekcji do wygenerowania nieskończonej sekwencji, która nie zostanie wyliczona.
Kompilator używa analizy statycznej do określania najbardziej wydajnego sposobu tworzenia kolekcji zadeklarowanej za pomocą wyrażenia kolekcji. Na przykład puste wyrażenie kolekcji , można zrealizować tak, []
jakby Array.Empty<T>() obiekt docelowy nie został zmodyfikowany po zainicjowaniu. Gdy element docelowy to obiekt System.Span<T> lub System.ReadOnlySpan<T>, magazyn może zostać przydzielony stos. Specyfikacja funkcji wyrażeń kolekcji określa reguły, które musi przestrzegać kompilator.
Wiele interfejsów API jest przeciążonych wieloma typami kolekcji jako parametrami. Ponieważ wyrażenie kolekcji można przekonwertować na wiele różnych typów wyrażeń, te interfejsy API mogą wymagać rzutowania w wyrażeniu kolekcji w celu określenia poprawnej konwersji. Następujące reguły konwersji rozpoznają niektóre niejednoznaczności:
- Konwersja na Span<T>, ReadOnlySpan<T>lub inny
ref struct
typ jest lepsza niż konwersja na typ struktury innej niż ref. - Konwersja na typ nieinterface jest lepsza niż konwersja na typ interfejsu.
Gdy wyrażenie kolekcji jest konwertowane na element Span
lub ReadOnlySpan
, bezpieczny kontekst obiektu span jest pobierany z bezpiecznego kontekstu wszystkich elementów zawartych w tym zakresie. Aby uzyskać szczegółowe reguły, zobacz specyfikację wyrażeń kolekcji.
Konstruktor kolekcji
Wyrażenia kolekcji działają z dowolnym typem kolekcji, który działa prawidłowo. Dobrze zachowywana kolekcja ma następujące cechy:
- Wartość
Count
lubLength
w kolekcji zliczalnej generuje taką samą wartość jak liczba elementów podczas wyliczania. - Typy w System.Collections.Generic przestrzeni nazw są uważane za wolne od skutków ubocznych. W związku z tym kompilator może zoptymalizować scenariusze, w których takie typy mogą być używane jako wartości pośrednie, ale w przeciwnym razie nie mogą być uwidocznione.
- Wywołanie niektórych odpowiednich
.AddRange(x)
elementów członkowskich w kolekcji spowoduje, że ta sama ostateczna wartość co iteracjax
i dodanie wszystkich wyliczonych wartości indywidualnie do kolekcji za pomocą polecenia.Add
.
Wszystkie typy kolekcji w środowisku uruchomieniowym platformy .NET są dobrze zachowywane.
Ostrzeżenie
Jeśli typ kolekcji niestandardowej nie jest dobrze zachowywany, zachowanie podczas używania tego typu kolekcji z wyrażeniami kolekcji jest niezdefiniowane.
Typy decydują się na obsługę wyrażeń kolekcji, zapisując metodę Create()
i stosując System.Runtime.CompilerServices.CollectionBuilderAttribute element w typie kolekcji, aby wskazać metodę konstruktora. Rozważmy na przykład aplikację, która używa o stałej długości 80 znaków. Ta klasa może wyglądać podobnie do następującego kodu:
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
}
Chcesz go używać z wyrażeniami kolekcji, jak pokazano w poniższym przykładzie:
LineBuffer line = ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'];
Typ LineBuffer
implementuje IEnumerable<char>
element , więc kompilator rozpoznaje go jako kolekcję char
elementów. Parametr typu zaimplementowanego System.Collections.Generic.IEnumerable<T> interfejsu wskazuje typ elementu. Aby móc przypisywać wyrażenia kolekcji do obiektu, musisz dodać dwa dodatki do LineBuffer
aplikacji. Najpierw należy utworzyć klasę zawierającą metodę Create
:
internal static class LineBufferBuilder
{
internal static LineBuffer Create(ReadOnlySpan<char> values) => new LineBuffer(values);
}
Metoda Create
musi zwrócić LineBuffer
obiekt i musi mieć jeden parametr typu ReadOnlySpan<char>
. Parametr type obiektu ReadOnlySpan
musi być zgodny z typem elementu kolekcji. Metoda konstruktora zwracająca kolekcję ogólną będzie miała typ ogólny ReadOnlySpan<T>
jako parametr . Metoda musi być dostępna i static
.
Na koniec należy dodać element CollectionBuilderAttribute do deklaracji LineBuffer
klasy:
[CollectionBuilder(typeof(LineBufferBuilder), "Create")]
Pierwszy parametr zawiera nazwę klasy Builder . Drugi atrybut zawiera nazwę metody konstruktora.