Индексы (Indices) в C# 8

—

от автора

Всем привет. Начиная с этой статьи начинаю цикл статей про нововведения в C# 8 версии.

Сейчас мы рассмотрим работу с индексами (Indices). Забегая вперед, скажу, что теперь мы, C# разработчики, можем работать с индексами как в Python.

Пристегнитесь. Начинаем?

Пусть в нашей программе есть массив целых чисел numbers:

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 }; }

Перед нами стоит задача получить последний элемент массива.

Мы с вами знаем, что доступ к элементам массива осуществляется по индексу. Следует помнить, что индексация начинается с нуля – первый элемент массива имеет индекс 0, а последний – n - 1, где n – размер массива: 

Наглядный вид массива
Наглядный вид массива

Если мы заранее знаем количество элементов в массиве, то можно чуть упростить себе жизнь ? Для нашего примера, мы знаем, что в массиве 6 элементов, следовательно, чтобы получить последний элемент, нужно обратиться к элементу с индексом 5, то есть написать следующим образом:

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 }; var lastNumber = numbers[5]; }

Это конечно же временное решение. Не всегда мы заранее знаем количество элементов в массиве. Например, есть некоторый метод Test, внутри которого для получения результата нужно получить последний элемент переданного массива: 

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 }; Test(numbers); }  static void Test(int[] numbers) {   // Так уже не напишешь.    // Мы внутри метода не знаем какое количество элементов в массиве   var lastNumber = numbers[5]; }

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

Как мы до этого уже говорили, можно рассчитать последний элемент с помощью длины массива, так как последний элемент имеет индекс – n - 1, где n – размер/длина массива. Для того, чтобы получить размерность массива, нужно воспользоваться функцией Length:

static void Test(int[] numbers) {    // Получаем длину массива  var length = numbers.Length;    // Получаем последний элемент</span> var lastNumber = numbers[length - 1]; }

Какие есть в  этом коде минусы:  

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

  2. Код становится менее читаемым. 

Решение

В C# 8 версии добавили дополнительную функциональность для работы с индексами. Я напомню, что была индексация слева направо, начинающаяся с 0. Теперь добавили новую индексацию справа налево (начинается с конца массива), начинающаяся  с 1. Для лучшего понимания рассмотрим таблицу: 

Индексация слева направо

0

1

2

3

4

5

Массив

5

1

4

2

3

7

Индексация справа налево

6

5

4

3

2

1

Жирным выделен сам массив. Над ним привычная нам индексация, а под ним новая индексация. 

Для использования новой индексации необходимо перед значением индекса поставить символ ^. Пишется данный символ на английской раскладке с помощью комбинации клавиш shift + 6:

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 };   Console.WriteLine(numbers[^1]); // 7   Console.WriteLine(numbers[^5]); // 1   Console.WriteLine(numbers[^6]); // 5 }

Таблица получения каждого элемента двумя способами: 

Выражение

Выражение

Результат

Пояснение

numbers[0]

numbers[^6]

5

Первый элемент

numbers[1]

numbers[^5]

1

Второй элемент

numbers[2]

numbers[^4]

4

Третий элемент

numbers[3]

numbers[^3]

2

Четвертый элемент

numbers[4]

numbers[^2]

3

Пятый элемент

numbers[5]

numbers[^1]

7

Шестой элемент

​Запомните: 

Теперь появился универсальный, удобный и логически понятный способ получения последнего элемента массива. В массиве любого размера для получения последнего элемента нужно обратиться к индексу ^1, например, numbers[^1]. 

Теперь зная про новый способ индексации перепишем нашу функцию Test:

static void Test(int[] numbers) {      // Получаем последний элемент</span> var lastNumber = numbers[^1]; }

​ВНИМАНИЕ!

Отсчет индексации справа налево начинается с 1, а не с 0. Если указать значение меньше 1 или больше индекса первого элемента, то возникнет исключение — ArgumentOutOfRangeException:

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 };  Console.WriteLine(numbers[^0]); // ArgumentOutOfRangeException Console.WriteLine(numbers[^7]); // ArgumentOutOfRangeException }

Что там под капотом?

На самом деле любой индекс с появлением C# 8 версии можно хранить в новом типе данных Index. Он находится в пространстве имен (namespace) System, следовательно, никакой дополнительный using при его использовании не нужно писать. 

У Index существует два конструктора:

  • Index() – создает индекс, указывающий на первый элемент массива (с индексом 0). 

  • Index(int value, bool fromEnd = false) – позволяет задать значение индекса (value) и определить ведется ли отчет от начала (fromEnd = false) или от конца массива (fromEnd = true). Соответственно важно помнить, что для индексов с отсчетом от конца массива счет начинается с 1 (а не с 0 как в обычном случае). Заметьте, что второй параметр по умолчанию имеет значение false.

Рассмотрим на примерах:

static void Main() { var numbers = new int[] {5, 1, 4, 2, 3, 7};  var indexFirst = new Index(0); // индекс первого элемента Console.WriteLine(numbers[indexFirst]); // 5  indexFirst = new Index(); // индекс первого элемента Console.WriteLine(numbers[indexFirst]); // 5  var indexLast = new Index(1, fromEnd: true); // индекс последнего элемента Console.WriteLine(numbers[indexLast]); // 7  var index1 = new Index(3); // индекс 3 элемента Console.WriteLine(numbers[index1]); // 2  index1 = new Index(3, fromEnd: false); // индекс 3 элемента Console.WriteLine(numbers[index1]); // 2  var index2 = new Index(5, fromEnd: true); // индекс 5 элемента с конца Console.WriteLine(numbers[index2]); // 1 }

Заметьте, что объект типа Index передается в качестве индекса в квадратные скобки ([]). 

Проведем соответствие между двумя разными записями:

Укороченная версия

Версия c Index

numbers[2]

numbers[new Index(2)] 

или 

numbers[new Index(2, fromEnd: false)]

numbers[^2]

numbers[new Index(2, fromEnd: true)]

В Index реализовано неявное преобразование целого числа (int) к Index. Вот как это работает: 

static void Main() { var numbers = new int[] { 5, 1, 4, 2, 3, 7 };  Index index1 = 2; Console.WriteLine(numbers[index1]); // 4  Index index2 = ^2; Console.WriteLine(numbers[index2]); // 3 }

У Index переопределен метод Equals:

static void Main() { var index1 = new Index(3); var index2 = new Index(3); Console.WriteLine(index1.Equals(index2)); // true }

А можно вообще вот так: 

static void Main() { var index1 = new Index(3); Console.WriteLine(index1.Equals(3)); // true }

Здесь сначала происходит неявное преобразование 3 к Index, а потом вызов Equals. 

У Index переопределен также метод ToString:

static void Main() { var index1 = new Index(3); var index2 = new Index(3, fromEnd: true); Console.WriteLine(index1.ToString()); // 3 Console.WriteLine(index2.ToString()); // ^3 }

Заметьте, что для индексации с конца выводится ^ перед индексом. 

Так как теперь мы можем индекс хранить в переменной типа Index, то можно чуть переписать нашу функцию Test:

static void Test(int[] numbers) { // Сохраняем последний индекс Index lastIndex = ^1;  // Получаем последний элемент var lastNumber = numbers[lastIndex];  // Какие то действия, которые меняют элементы массива  lastNumber = numbers[lastIndex];  // Какие то действия, которые меняют элементы массива  lastNumber = numbers[lastIndex]; }

Мы сохранили в переменной lastIndex индекс последнего элемента и использовали во всех местах.

Также теперь мы можем в методы передавать индекс:

static void Test(int[] numbers, Index index) { // логика }

Выводы

  1. Структура Index позволяет создать экземпляр, к которому можно обращаться многократно;

  2. Код становится более короткий и читаемый;

  3. Удобно обращаться к элементам массива в обратном порядке.


PS. Написано с любовью вместе со своими учениками. Они у меня лучшие ?

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

Понравилась данная фича?

21.35% Нет41
41.67% Да80
36.98% Даже не знаю71

Проголосовали 192 пользователя. Воздержались 9 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/649043/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *