Поделиться через


Выражения коллекции — справочник по языку C#

Выражение коллекции можно использовать для создания общих значений коллекции. Выражение коллекции — это синтаксис terse, который при вычислении может быть назначен многим различным типам коллекций. Выражение коллекции содержит последовательность элементов между [ и ] квадратными скобками. В следующем примере объявляется System.Span<T>string элемент и инициализирует их в дни недели:

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

Выражение коллекции можно преобразовать в различные типы коллекций. В первом примере показано, как инициализировать переменную с помощью выражения коллекции. В следующем коде показано множество других расположений, где можно использовать выражение коллекции:

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

Нельзя использовать выражение коллекции, в котором ожидается константа во время компиляции, например инициализация константы или значение по умолчанию для аргумента метода.

Оба предыдущих примера использовали константы в качестве элементов выражения коллекции. Можно также использовать переменные для элементов, как показано в следующем примере:

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

Вы используете элемент.. spread для встроенных значений коллекции в выражении коллекции. В следующем примере создается коллекция для полного алфавита, сочетая коллекцию гласных, коллекцию консонантов и букву "y", которая может быть либо:

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

Элемент spread ..vowels, при вычислении, создает пять элементов: "a", , "e", "i""o"и "u". Элемент spread ..consonants создает 20 элементов, число в массиве consonants . Переменная в элементе spread должна быть перечислена с помощью инструкции foreach . Как показано в предыдущем примере, можно объединить элементы с отдельными элементами в выражении коллекции.

Преобразования

Выражение коллекции можно преобразовать в различные типы коллекций, в том числе:

Внимание

Выражение коллекции всегда создает коллекцию, содержащую все элементы в выражении коллекции независимо от целевого типа преобразования. Например, если целевой объект преобразования равен System.Collections.Generic.IEnumerable<T>, созданный код вычисляет выражение коллекции и сохраняет результаты в коллекции в памяти.

Это поведение отличается от LINQ, где последовательность может не быть создана до перечисления. Выражения коллекции нельзя использовать для создания бесконечной последовательности, которая не будет перечислена.

Компилятор использует статический анализ для определения наиболее эффективного способа создания коллекции, объявленной с помощью выражения коллекции. Например, выражение []пустой коллекции может быть реализовано так, как Array.Empty<T>() если целевой объект не будет изменен после инициализации. Если целевой объект является System.Span<T> или System.ReadOnlySpan<T>хранилище может быть выделено стеком. Спецификация признаков выражений коллекции указывает правила, которые должен следовать компилятору.

Многие API перегружены несколькими типами коллекций в качестве параметров. Так как выражение коллекции может быть преобразовано в множество различных типов выражений, эти API могут требовать приведения к выражению коллекции для указания правильного преобразования. Следующие правила преобразования разрешают некоторые неоднозначности:

  • Преобразование в Span<T>, ReadOnlySpan<T>или другой ref struct тип лучше, чем преобразование в тип структуры, отличного от ссылок.
  • Преобразование в неинтерфесный тип лучше, чем преобразование в тип интерфейса.

При преобразовании SpanReadOnlySpanвыражения коллекции в безопасный контекст объекта диапазона берется из безопасного контекста всех элементов, включенных в диапазон. Подробные правила см. в спецификации выражения коллекции.

Построитель коллекций

Выражения коллекции работают с любым типом коллекции, который хорошо работает. Хорошо вел себя коллекция имеет следующие характеристики:

  • Значение CountLength или в подсчитываемойколлекции создает то же значение, что и количество элементов при перечислении.
  • Типы в System.Collections.Generic пространстве имен считаются побочными эффектами без побочных эффектов. Таким образом, компилятор может оптимизировать сценарии, в которых такие типы могут использоваться в качестве промежуточных значений, но в противном случае не предоставляются.
  • Вызов определенного применимого .AddRange(x) элемента в коллекции приведет к тому же окончательному значению, что итерация x и добавление всех перечисленных значений отдельно в коллекцию..Add

Все типы коллекций в среде выполнения .NET хорошо ведут себя.

Предупреждение

Если пользовательский тип коллекции не работает хорошо, то поведение при использовании этого типа коллекции с выражениями коллекции не определено.

Типы выбирают поддержку выражений коллекции, записывая Create() метод и применяя System.Runtime.CompilerServices.CollectionBuilderAttribute его к типу коллекции, чтобы указать метод построителя. Например, рассмотрим приложение, использующее буферы фиксированной длины 80 символов. Этот класс может выглядеть примерно так:

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
}

Вы хотите использовать его с выражениями коллекции, как показано в следующем примере:

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

Тип LineBuffer реализуется, поэтому компилятор распознает IEnumerable<char>его как коллекцию char элементов. Параметр типа реализованного System.Collections.Generic.IEnumerable<T> интерфейса указывает тип элемента. Необходимо внести два дополнения в приложение, чтобы иметь возможность назначать выражения коллекции объекту LineBuffer . Сначала необходимо создать класс, содержащий Create метод:

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

Метод Create должен возвращать LineBuffer объект, и он должен принимать один параметр типа ReadOnlySpan<char>. Параметр типа должен ReadOnlySpan соответствовать типу элемента коллекции. Метод построителя, возвращающий универсальную коллекцию, будет иметь универсальный ReadOnlySpan<T> в качестве его параметра. Метод должен быть доступен и static.

Наконец, необходимо добавить CollectionBuilderAttribute в LineBuffer объявление класса:

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

Первый параметр предоставляет имя класса Builder . Второй атрибут предоставляет имя метода построителя.