Начиная с 8 версии языка C#, разработчикам доступно использование индексов и диапазонов. В ряде случае это предоставляет новые возможности, упрощает код и делает его более читабельным. |
Описываемые ниже возможности поддерживаются в C# 8 для массивов, Span<T> и ReadOnlySpan<T>. Для краткости все эти типы будут пониматься далее под словом "массивы".
Индексы и диапазоны требуют изменения не только со стороны C#, но и .NET, т.к. добавляются новые типы в пространство имен System. Поэтому их использование возможно только с платформами поддерживающими .NET Standard 2.1 и выше. В данный момент это .NET Core начиная с версии 3.0.
Индексы
Индекс это номер элемента в массиве. Это понятие присутствовало в языке и раньше, но в качестве значения всегда выступало целое число, которое указывало на номер элемента от начала массива. Например: myArray[42] или myArray[index].
Начиная с C# 8 у индекса появился новый вариант записи, больше возможностей и свой тип: System.Index
У Index существует два конструктора:
- Index() – создает индекс, указывающий на первый элемент массива (с индексом 0).
- Index(int value, bool fromEnd = false) – позволяет задать значение индекса (value) и определить ведется ли отчет от начала (fromEnd = true) или от конца массива (fromEnd = false).
Для индексов с отсчетом от конца массива счет начинается с 1 (а не с 0 как в обычном случае).
Синтаксис получения значения по индексу не изменился. Только теперь можно использовать не только число, но и экземпляр объекта Index:
var value = array[index];
В C# 8 также существует упрощенная форма записи для числовых индексов ведущих отсчет от конца массива. Для этого перед значением индекса необходимо поставить символ "^". По сути это синтаксический сахар для создания экземпляра класса Index с параметром fromEnd равным true.
var value = array[^N];
При использовании индекса с конкретным массивом, если индекс указывает за его пределы, будет выброшено исключение IndexOutOfRangeException.
Также стоит отметить, что у класса Index определены:
- метод Equals() для сравнения индексов.
- оператор приведения int к Index, который позволяет исп��льзовать целочисленные индексы наряду с экземплярами класса Index.
Примеры (в комментариях приведен результат операции с индексом):
var indexFirst = new Index(0); // индекс первого элемента
var indexLast = new Index(1, fromEnd: true); // индекс последнего элемента
var index1 = new Index(5); // индекс 5 элемента
var index2 = new Index(2, fromEnd: true); // индекс 2 элемента с конца
var values = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int v1 = values[indexFirst]; // 0
int v2 = values[0]; // 0
bool areSame1 = indexFirst.Equals(0); // true
int v3 = values[indexLast]; // 9
int v4 = values[^2]; // 8
bool areSame2 = indexLast.Equals(index2); // false
Диапазоны
Диапазон определяет коллекцию элементов массива, расположенных от начального (включительно) до конечного индекса (не включая его). В C# 8 для этого добавлен новый синтаксис и новый тип System.Range, у которого существует два конструктора:
- Range() – создает диапазон от 0 до 0 элемента.
- Range(Index start, Index end) – создает диапазон от индекса start (включительно) и до индекса end.
Использование диапазона применительно к массиву создает новый массив из элементов, попадающих в диапазон:
T[] values = arrayOfT[range];
где range - перемнная типа Range.
Для записи диапазона с использованием двух индексов существует следующая форма записи:
T[] values = arrayOfT[index1 .. index2];
Если необходимо включить в диапазон первый или последний элемент, то его индекс можно не указывать:
T[] values1 = arrayOfT[.. index2]; // диапазон от начала массива до index2
T[] values2 = arrayOfT[index1 ..]; // диапазон от index1 до конца массива
Поскольку элемент массива с последним индексом из диапазона в результат не включается, то индекс "^0" считается корректным в качестве конечного. Записи "N .." и "N .. ^0" равнозначны.
Применение диапазона к массиву может привести к выбросу исключения OverflowException, если один из индексов диапазона указывает за пределы этого массива.
У Range определены:
- Метод Equals() – для сравнение с другим диапазоном.
- Свойства Start и End типа Index – индексы, определяющие диапазон.
Примеры (в комментариях приведен результат использования диапазона):
var values = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var range1 = new Range(4, 8);
int[] arr1 = values[range1]; // [4, 5, 6, 7]
int[] arr2 = values[..^5]; // [0, 1, 2, 3, 4]
int[] arr3 = values[^2..]; // [8, 9]
string name = "Fox News"[0..3]; // "Fox"
foreach (var val in values[3..6])
Console.WriteLine(val); // выведет в консоль 3, 4 и 5
В предыдущих версиях C# значения заданного диапазона можно было получить используя методы LINQ Skip() и Take().
В заверении стоит еще раз отметить следующее: индексы и диапазоны в C# 8 не привязаны к конкретным массивам и типам этих массивов. Они абстрактно определяют позиции элементов относительно начала или конца массива. Как следствие, они могут быть использованы с массивами любого типа и длинны. Например, использование диапазона "^2 .." всегда вернет 2 последних элемента любого массива (если в нем 2 и более элементов).