Массивы

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

тип_переменной[] название_массива;

Например, определим массив целых чисел:

int[] numbers;

После определения переменной массива мы можем присвоить ей определенное значение:

int[] nums = new int[4];

Здесь вначале мы объявили массив nums, который будет хранить данные типа int. Далее используя операцию new, мы выделили память для 4 элементов массива: new int[4]. Число 4 еще называется длиной массива. При таком определении все элементы получают значение по умолчанию, которое предусмотрено для их типа. Для типа int значение по умолчанию - 0.

Также мы сразу можем указать значения для этих элементов:

int[] nums2 = new int[4] { 1, 2, 3, 5 };
int[] nums3 = new int[] { 1, 2, 3, 5 };
int[] nums4 = new[] { 1, 2, 3, 5 };
int[] nums5 = { 1, 2, 3, 5 };

Все перечисленные выше способы будут равноценны.

Подобным образом можно определять массивы и других типов, например, массив значений типа string:

string[] people = { "Tom", "Sam", "Bob" };

Начиная с версии C# 12 для определения массивов можно использовать выражения коллекций, которые предполагают заключение элементов массива в квадратные скобки:

int[] nums1 = [ 1, 2, 3, 5 ];
int[] nums2 = [];   // пустой массив

Индексы и получение элементов массива

Для обращения к элементам массива используются индексы. Индекс представляет номер элемента в массиве, при этом нумерация начинается с нуля, поэтому индекс первого элемента будет равен 0, индекс четвертого элемента - 3.

Используя индексы, мы можем получить элементы массива:

int[] numbers = { 1, 2, 3, 5 };
 
// получение элемента массива
Console.WriteLine(numbers[3]);  // 5
 
// получение элемента массива в переменную
var n = numbers[1];             // 2
Console.WriteLine(n);           // 2

Также мы можем изменить элемент массива по индексу:

int[] numbers = { 1, 2, 3, 5 };
 
// изменим второй элемент массива
numbers[1] = 505;
Console.WriteLine(numbers[1]);  // 505

И так как у нас массив определен только для 4 элементов, то мы не можем обратиться, например, к шестому элементу. Если мы так попытаемся сделать, то мы получим ошибку во время выполнения:

int[] numbers = { 1, 2, 3, 5 };
Console.WriteLine(numbers[6]);  // ! Ошибка - в массиве только 4 элемента

Свойство Length и длина массива

Каждый массив имеет свойство Length, которое хранит длину массива. Например, получим длину выше созданного массива numbers:

int[] numbers = { 1, 2, 3, 5 };
Console.WriteLine(numbers.Length);  // 4

Для получения длины массива после названия массива через точку указывается свойство Length: numbers.Length.

Получение элементов с конца массива

Благодаря наличию свойства Length, мы можем вычислить индекс последнего элемента массива - это длина массива минус 1. Например, если длина массива - 4 (то есть массив имеет 4 элемента), то индекс последнего элемента будет равен 3. И, используя свойство Length, мы можем легко получить элементы с конца массива:

int[] numbers = { 1, 2, 3, 5};
 
Console.WriteLine(numbers[numbers.Length - 1]);  // 5 - первый с конца или последний элемент
Console.WriteLine(numbers[numbers.Length - 2]);  // 3 - второй с конца или предпоследний элемент
Console.WriteLine(numbers[numbers.Length - 3]);  // 2 - третий элемент с конца

Однако при подобном подходе выражения типа numbers.Length - 1, смысл которых состоит в том, чтобы получить какой-то определенный элемент с конца массива, утяжеляют код. И, начиная с версии C# 8.0 в язык был добавлен специальный оператор ^, с помощью которого можно задать индекс относительно конца коллекции.

Перепишем предыдущий пример, применяя оператор ^:

int[] numbers = { 1, 2, 3, 5};
 
Console.WriteLine(numbers[^1]);  // 5 - первый с конца или последний элемент
Console.WriteLine(numbers[^2]);  // 3 - второй с конца или предпоследний элемент
Console.WriteLine(numbers[^3]);  // 2 - третий элемент с конца

Перебор массивов

Для перебора массивов мы можем использовать различные типы циклов. Например, цикл foreach:

int[] numbers = { 1, 2, 3, 4, 5 };
 
foreach (int i in numbers)
{
    Console.WriteLine(i);
}

Здесь в качестве контейнера выступает массив данных типа int. Поэтому мы объявляем переменную с типом int.

Подобные действия мы можем сделать и с помощью цикла for:

int[] numbers = { 1, 2, 3, 4, 5 };
 
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine(numbers[i]);
}

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

int[] numbers = { 1, 2, 3, 4, 5 };
 
for (int i = 0; i < numbers.Length; i++)
{
    numbers[i] = numbers[i] * 2;
    Console.WriteLine(numbers[i]);
}

Также можно использовать и другие виды циклов, например, while:

int[] numbers = { 1, 2, 3, 4, 5 };
int i = 0;
 
while(i < numbers.Length)
{
    Console.WriteLine(numbers[i]);
    i++;
}