Udostępnij za pośrednictwem


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:

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 lub Length 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 iteracja x 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 buforów 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.