Andrey on .NET | C# 8 – Индексы и диапазоны

C# 8 – Индексы и диапазоны

C# logoНачиная с 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 и более элементов).

Добавить комментарий