Массивы характеризуются таким понятием как ранг или количество измерений. Выше мы рассматривали массивы, которые имеют одно измерение (то есть их ранг равен 1) - такие массивы можно представлять в виде ряда (строки или столбца) элемента. Но массивы также бывают многомерными. У таких массивов количество измерений (то есть ранг) больше 1.

Массивы которые имеют два измерения (ранг равен 2) называют двухмерными. Например, создадим одномерный и двухмерный массивы, которые имеют одинаковые элементы:

int[] nums1 = new int[] { 0, 1, 2, 3, 4, 5 };
int[,] nums2 = { { 0, 1, 2 }, { 3, 4, 5 } };

Визуально оба массива можно представить следующим образом:

Одномерный массив nums1

| 0 | 1 | 2 | 3 | 4 | 5 |

Двухмерный массив nums2

012
345

Поскольку массив nums2 двухмерный, он представляет собой простую таблицу. Все ⁷ способы определения двухмерных массивов:

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

Массивы могут иметь и большее количество измерений. Объявление трехмерного массива могло бы выглядеть так:

int[,,] nums3 = new int[2, 3, 4];

Соответственно могут быть и четырехмерные массивы и массивы с большим количеством измерений. Но на практике обычно используются одномерные и двухмерные массивы.

Определенную сложность может представлять перебор многомерного массива. Прежде всего надо учитывать, что длина такого массива - это совокупное количество элементов.

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

В данном случае длина массива numbers равна 6. И цикл foreach выводит все элементы массива в строку:

1 2 3 4 5 6

Но что если мы хотим отдельно пробежаться по каждой строке в таблице? В этом случае надо получить количество элементов в размерности. В частности, у каждого массива есть метод GetUpperBound(номер_размерности), который возвращает индекс последнего элемента в определенной размерности. И если мы говорим непосредственно о двухмерном массиве, то первая размерность (с индексом 0) по сути это и есть таблица. И с помощью выражения

numbers.GetUpperBound(0) + 1

можно получить количество строк таблицы, представленной двухмерным массивом. А через

numbers.Length / количество_строк

можно получить количество элементов в каждой строке:

int[,] numbers = { { 1, 2, 3 }, { 4, 5, 6 }};
 
int rows = numbers.GetUpperBound(0) + 1;    // количество строк
int columns = numbers.Length / rows;        // количество столбцов
 
// или так
// int columns = numbers.GetUpperBound(1) + 1;
 
for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < columns; j++)
    {
        Console.Write($"{numbers[i, j]} \t");
    }
    Console.WriteLine();
}

Вывод:

1	2	3
4	5	6

Массив массивов

От многомерных массивов надо отличать массив массивов или так называемый “зубчатый массив”:

int[][] nums = new int[3][];
 
nums[0] = new int[2] { 1, 2 };          // выделяем память для первого подмассива
nums[1] = new int[3] { 1, 2, 3 };       // выделяем память для второго подмассива
nums[2] = new int[5] { 1, 2, 3, 4, 5 }; // выделяем память для третьего подмассива

Здесь две группы квадратных скобок указывают, что это массив массивов, то есть такой массив, который в свою очередь содержит в себе другие массивы. Причем длина массива указывается только в первых квадратных скобках, все последующие квадратные скобки должны быть пусты: new int[3][]. В данном случае у нас массив nums содержит три массива. Причем размерность каждого из этих массивов может не совпадать.

Альтернативное определение массива массивов:

int[][] numbers = {
    new int[] { 1, 2 },
    new int[] { 1, 2, 3 },
    new int[] { 1, 2, 3, 4, 5 }
};

Зубчатый массив nums

12
12
12

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

int[][] numbers = new int[3][];
 
numbers[0] = new int[] { 1, 2 };
numbers[1] = new int[] { 1, 2, 3 };
numbers[2] = new int[] { 1, 2, 3, 4, 5 };
 
foreach(int[] row in numbers)
{
    foreach(int number in row)
    {
        Console.Write($"{number} \t");
    }
    Console.WriteLine();
}
 
// перебор с помощью цикла for
for (int i = 0; i < numbers.Length; i++)
{
    for (int j = 0; j < numbers[i].Length; j++)
    {
        Console.Write($"{numbers[i][j]} \t");
    }
    Console.WriteLine();
}

Основные понятия массивов

Суммируем основные понятия массивов:

  • Ранг (rank): количество измерений массива
  • Длина измерения (dimension length): длина отдельного измерения массива
  • Длина массива (array length): количество всех элементов массива

Например, возьмем массив

int[,] numbers = new int[3, 4];

Массив numbers двухмерный, то есть он имеет два измерения, поэтому его ранг равен 2. Длина первого измерения - 3, длина второго измерения - 4. Длина массива (то есть общее количество элементов) - 12.

Примеры массивов:

Массивы в языке C#