Выражения коллекции — справочник по языку 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.Span<T> и System.ReadOnlySpan<T>.
- Массивы.
- Любой тип с методом создания , тип
ReadOnlySpan<T>
параметра которого находится в месте неявного преобразования из типаT
выражения коллекции в . - Любой тип, поддерживающий инициализатор коллекции, напримерSystem.Collections.Generic.List<T>. Обычно это требование означает, что тип поддерживает System.Collections.Generic.IEnumerable<T> и есть доступный
Add
метод для добавления элементов в коллекцию. Необходимо неявное преобразование из типа элементов выражения коллекции в тип элемента коллекции. Для распределенных элементов должно быть неявное преобразование из типа элемента spread в тип элемента коллекции. - Любой из следующих интерфейсов:
Внимание
Выражение коллекции всегда создает коллекцию, содержащую все элементы в выражении коллекции независимо от целевого типа преобразования. Например, если целевой объект преобразования равен System.Collections.Generic.IEnumerable<T>, созданный код вычисляет выражение коллекции и сохраняет результаты в коллекции в памяти.
Это поведение отличается от LINQ, где последовательность может не быть создана до перечисления. Выражения коллекции нельзя использовать для создания бесконечной последовательности, которая не будет перечислена.
Компилятор использует статический анализ для определения наиболее эффективного способа создания коллекции, объявленной с помощью выражения коллекции. Например, выражение []
пустой коллекции может быть реализовано так, как Array.Empty<T>() если целевой объект не будет изменен после инициализации. Если целевой объект является System.Span<T> или System.ReadOnlySpan<T>хранилище может быть выделено стеком. Спецификация признаков выражений коллекции указывает правила, которые должен следовать компилятору.
Многие API перегружены несколькими типами коллекций в качестве параметров. Так как выражение коллекции может быть преобразовано в множество различных типов выражений, эти API могут требовать приведения к выражению коллекции для указания правильного преобразования. Следующие правила преобразования разрешают некоторые неоднозначности:
- Преобразование в Span<T>, ReadOnlySpan<T>или другой
ref struct
тип лучше, чем преобразование в тип структуры, отличного от ссылок. - Преобразование в неинтерфесный тип лучше, чем преобразование в тип интерфейса.
При преобразовании Span
ReadOnlySpan
выражения коллекции в безопасный контекст объекта диапазона берется из безопасного контекста всех элементов, включенных в диапазон. Подробные правила см. в спецификации выражения коллекции.
Построитель коллекций
Выражения коллекции работают с любым типом коллекции, который хорошо работает. Хорошо вел себя коллекция имеет следующие характеристики:
- Значение
Count
Length
или в подсчитываемой коллекции создает то же значение, что и количество элементов при перечислении. - Типы в 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 . Второй атрибут предоставляет имя метода построителя.