Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Użyj wyrażenia kolekcji , aby utworzyć wspólne wartości kolekcji.
Wyrażenie kolekcji jest składnią terse, którą można przypisać do wielu różnych typów kolekcji. Wyrażenie kolekcji zawiera sekwencję elementów między nawiasami [ i ] .
Dokumentacja języka C# zawiera ostatnio wydaną wersję języka C#. Zawiera również początkową dokumentację dla funkcjonalności w publicznych wersjach testowych nadchodzącego wydania języka.
Dokumentacja identyfikuje dowolną funkcję po raz pierwszy wprowadzoną w ostatnich trzech wersjach języka lub w bieżącej publicznej wersji zapoznawczej.
Wskazówka
Aby dowiedzieć się, kiedy funkcja została po raz pierwszy wprowadzona w języku C#, zapoznaj się z artykułem dotyczącym historii wersji języka C#.
Poniższy przykład deklaruje element System.Span<T> i string 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 podczas inicjowania 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
Użyj elementu.. spread 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 . Wyrażenie w elemecie spread musi być wyliczane 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, takie jak
int[]lubstring[]. - 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
Adddostę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:
Uwaga / Notatka
Nie można używać wyrażeń kolekcji do inicjowania tablic wbudowanych. Tablice wbudowane wymagają inicjalizacji różnych składni.
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:
- Lepsza konwersja elementów jest preferowana w przypadku lepszej konwersji typu kolekcji. Innymi słowy, typ elementów w wyrażeniu kolekcji ma większe znaczenie niż typ kolekcji. Te reguły są opisane w specyfikacji funkcji w celu lepszej konwersji z wyrażenia kolekcji.
- Konwersja na Span<T>, ReadOnlySpan<T>lub inny
ref structtyp jest lepsza niż konwersja na typ struktury innej niż ref. - Konwersja na typ nieinterface jest lepsza niż konwersja na typ interfejsu.
Podczas konwertowania wyrażenia kolekcji na element Span lub ReadOnlySpanbezpieczny kontekst obiektu span pochodzi 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 jest dobrze zachowywany. Dobrze zachowywana kolekcja ma następujące cechy:
- Wartość
CountlubLengthw kolekcji zliczalnej generuje taką samą wartość jak liczba elementów podczas wyliczania. - Typy w System.Collections.Generic przestrzeni nazw są wolne od skutków ubocznych. Kompilator może zoptymalizować scenariusze, w których te typy mogą być używane jako wartości pośrednie, ale nie uwidacznia ich w inny sposób.
- Wywołanie odpowiedniego
.AddRange(x)elementu członkowskiego w kolekcji powoduje, że ta sama wartość końcowa co iteracjaxi dodanie wszystkich wyliczonych wartości indywidualnie do kolekcji przy użyciu 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 jest niezdefiniowane podczas używania tego typu kolekcji z wyrażeniami kolekcji.
Typy umożliwiają obsługę wyrażeń kolekcji przez napisanie Create() metody i zastosowanie atrybutu System.Runtime.CompilerServices.CollectionBuilderAttribute w typie kolekcji w celu wskazania metody 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;
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
}
Chcesz używać go 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 przyjąć końcowy parametr typu ReadOnlySpan<char>. Parametr type obiektu ReadOnlySpan musi być zgodny z typem elementu kolekcji. Metoda konstruktora zwracająca kolekcję ogólną ma typ ogólny ReadOnlySpan<T> jako jego parametr. Metoda musi być dostępna i static.
Począwszy od języka C# 15, Create metoda może mieć dodatkowe parametry przed parametrem ReadOnlySpan<T> . Wartości tych parametrów można przekazać przy użyciu with(...) elementu w wyrażeniu kolekcji. Aby uzyskać szczegółowe informacje, zobacz Argumenty konstruktora kolekcji .
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.
Argumenty wyrażeń kolekcji
Począwszy od języka C# 15, można przekazać argumenty do konstruktora lub metody fabryki kolekcji bazowej przy użyciu with(...) elementu jako pierwszego elementu w wyrażeniu kolekcji. Ta funkcja umożliwia określanie pojemności, porównań lub innych parametrów konstruktora bezpośrednio w składni wyrażenia kolekcji. Aby uzyskać więcej informacji, zobacz specyfikację funkcji argumentów wyrażeń kolekcji.
Element with(...) musi być pierwszym elementem w wyrażeniu kolekcji. Argumenty zadeklarowane w elememencie with(...) są przekazywane do odpowiedniego konstruktora lub metody create na podstawie typu docelowego. Dla argumentów w elememencie można użyć dowolnego prawidłowego with wyrażenia.
Argumenty konstruktora
Gdy typ docelowy jest klasą lub strukturą, która implementuje System.Collections.IEnumerable, argumenty w with(...) obiekcie są oceniane, a wyniki są przekazywane do konstruktora. Kompilator używa rozpoznawania przeciążenia w celu wybrania najlepszego zgodnego konstruktora:
public void CollectionArgumentsExamples()
{
string[] values = ["one", "two", "three"];
// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];
// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase
// Pass capacity to IList<T> (uses List<T> constructor)
IList<int> numbers = [with(capacity: 100), 1, 2, 3];
}
W poprzednim przykładzie:
- Konstruktor
List<string>z parametremcapacityjest wywoływany za pomocąvalues.Length * 2polecenia . - Konstruktor
HashSet<string>z parametrem System.Collections.Generic.IEqualityComparer<T> jest wywoływany za pomocąStringComparer.OrdinalIgnoreCasepolecenia . - W przypadku typów docelowych interfejsu, takich jak System.Collections.Generic.IList<T>, kompilator tworzy
List<T>obiekt z określoną pojemnością.
Argumenty konstruktora kolekcji
W przypadku typów z System.Runtime.CompilerServices.CollectionBuilderAttributeargumentami zadeklarowanymi w elememencie with(...) argumenty są oceniane, a wyniki są przekazywane do metody create przed parametrem ReadOnlySpan<T> . Ta funkcja umożliwia tworzenie metod akceptowania parametrów konfiguracji:
internal static class MySetBuilder
{
internal static MySet<T> Create<T>(ReadOnlySpan<T> items) => new MySet<T>(items);
internal static MySet<T> Create<T>(IEqualityComparer<T> comparer, ReadOnlySpan<T> items) =>
new MySet<T>(items, comparer);
}
Następnie możesz użyć with(...) elementu , aby przekazać element comparer:
public void CollectionBuilderArgumentsExample()
{
// Pass comparer to a type with CollectionBuilder attribute
// The comparer argument is passed before the ReadOnlySpan<T> parameter
MySet<string> mySet = [with(StringComparer.OrdinalIgnoreCase), "A", "a", "B"];
// mySet contains only two elements: "A" and "B"
}
Metoda create jest wybierana przy użyciu rozpoznawania przeciążeń na podstawie podanych argumentów. Element ReadOnlySpan<T> zawierający elementy kolekcji jest zawsze ostatnim parametrem.
Typy obiektów docelowych interfejsu
Kilka typów obiektów docelowych interfejsu obsługuje argumenty wyrażeń kolekcji. W poniższej tabeli przedstawiono obsługiwane interfejsy i ich odpowiednie podpisy konstruktorów:
| Interfejs | Obsługiwane with elementy |
|---|---|
| IEnumerable<T>, IReadOnlyCollection<T>IReadOnlyList<T> |
() (tylko puste) |
| ICollection<T>, IList<T> |
(), (int capacity) |
W przypadku IList<T> elementu i ICollection<T>kompilator używa System.Collections.Generic.List<T> elementu z określonym konstruktorem.
Restrictions
Element with(...) ma następujące ograniczenia:
- Musi to być pierwszy element w wyrażeniu kolekcji.
- Argumenty nie mogą mieć
dynamictypu. - Nie jest obsługiwana w przypadku tablic ani typów rozpiętości (
Span<T>,ReadOnlySpan<T>).