Массивы в C#

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

На данный момент мы знаем, что такое переменная, как задаётся её тип и как переменную инициализировать. Теперь, когда мы знаем какие виды циклов имеются в C# и в чём их отличия, можно детально изучать вопросы, связанные с использованием массивов C#.

Что такое «массив»? Зачем нужны массивы?

Допустим, вам необходимо хранить в программе 10 целочисленных значений. С одной стороны, если вам не лень (и не жалко тех, кто будет смотреть Ваш код) вы можете объявить 10 переменных типа int, инициализировать их и работать. А что делать, если требуется 100 значений или 1000? Опять же, можно сходить с ума и заводить под каждое значение отдельную переменную, а можно сделать проще — объявить переменную-массив и хранить в ней все значения.

Массив — это структура данных, которая содержит ряд переменных одного типа, доступ к которым осуществляется по индексу. Массивы в C# могут быть одномерными, многомерными, пустыми, также в C# массивы могут быть зубчатыми (массивы массивов). Но обо всём по порядку.

Как создать массив в C#

Чтобы объявить массив в C# необходимо использовать следующую конструкцию:

Type[] ArrayName

где Type — это тип элементов массива, а ArrayName — его имя.  То есть, визуально, массив от обычной переменной отличает наличие после названия типа данных квадратных скобок []. Например, вот так можно определить массив целых чисел типа int:

int[] array;

Так, можно объявить массивы строк и вещественных чисел:

double[] doubleArray;
string[] stringArray;

Как мы уже знаем, объявить переменную — это только часть работы. Переменную необходимо инициализировать. При всех своих возможностях, C# не может знать сколько элементов будет содержать наш массив — 0, 10, 1000 и так далее. Сами массивы имеют ссылочный тип, и объявление переменной массива только выделяет память для ссылки на экземпляр массива. Фактические экземпляры массива создаются динамически во время выполнения с помощью оператора new. Операция new указывает длину нового экземпляра массива, которая остается неизменной в течение всего времени существования этого экземпляра.

Например, создадим массив, который будет хранить 5 чисел типа int:

int[] intArray = new int[5];

Теперь у нас есть экземпляр массива, который может хранить пять чисел. По умолчанию C# задал каждому элементу значение 0 (ноль). Проверить это легко с помощью уже известного нам цикла foreach:

int[] intArray = new int[5];
foreach (int i in intArray)
  {
    Console.WriteLine(intArray[i]);
  }

Если создается массив, содержащий элементы ссылочного типа, то всем элементам по умолчанию будет присвоено значение null.  Чтобы задать каждому элементу массива свое значение можно обратиться к нему по его индексу. В C# элементы массива получают индексы от 0 до значение длина-1. Для нашего массива задать элементы можно вот так:

intArray[0] = 10; //задаем значение первого элемента
intArray[1] = 7; //задаем значение второго элемента
intArray[2] = -3; //задаем значение третьего элемента
intArray[3] = 18; //задаем значение четвертого элемента 
intArray[4] = -125; //задаем значение пятого элемента

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

int[] intArray = new int[5] { 10, 7, -3, 18, -125};

Здесь мы объявили переменную-массив, инициализировали её и сразу задали каждому элементы своё значение. Оба варианта: и задание значения каждому элементу в по-отдельности и как в примере выше полностью равнозначны.  Но и на этом разработчики C# не остановились, чтобы дать разработчику максимум удобства работы с этим языком программирования. Ниже я покажу какие ещё варианты создания массивов могут применяться в C# на примере всё того же массива из пяти чисел:

int[] intArray2 = new int[] { 10, 7, -3, 18, -125 };
int[] intArray3 = new[] { 10, 7, -3, 18, -125 };
int[] intArray4 = { 10, 7, -3, 18, -125 };
int[] intArray5 = [10, 7, -3, 18, -125]; //вариант доступен в C# 12 и .NET 8

Итого — целых шесть возможных вариантов того, как задать массив в C#.

Перебор элементов одномерного массива в C#

В цикле foreach

Выше я показал один из способов как перебрать элементы массива с использованием цикла foreach:

int[] intArray = new int[5] { 10, 7, -3, 18, -125};//создали массив
//перебираем все элементы массива
foreach (int i in intArray)
  {
    Console.WriteLine(intArray[i]);
  }

Здесь в цикле foreach мы указали тип элементов int, так как у нас массив содержит элементы этого типа и после ключевого слова in указали массив элементы которого необходимо перебрать. Если бы у нас был массив строк, то цикл выглядел бы, соответственно, следующим образом:

//создаем массив
string[] strAttay = { "Один", "Два", "Три"};
//перебираем элементы
foreach (string i in strArray)
 {
    Console.WriteLine(strArray[i]);
 }

Цикл foreach достаточно удобный для работы, но иногда бывает так, что возможностей цикла не достаточно. Например, мы создаем массив и нам требуется обеспечить доступ только  к элементам с чётными индексами (0, 2, 4, 6 и т.д.). Технически, мы можем использовать тот же foreach, ввести дополнительную переменную и с помощью неё проверять какой индекс у очередного элемента, но это усложнит наш код. Более гибким в этом плане является цикл for в котором мы можем задавать порядок выполнения.

В цикле for

С этим циклом мы тоже уже знакомы. Попробуем реализовать озвученный выше вариант перебора элементов массива — прочитать значения только элементов с чётными индексами. С циклом for это можно сделать, например, вот так:

 //Индексы элементов            0   1  2   3    4   5   6   7   8   9   10
 int[] intArray = new int[11] {  12, 7, 16, 17, -12, 65, 80, 89, 90, 91, 102};
 for (int i=0; i<intArray.Length; i++)
   if (i*2<intArray.Length)
      Console.WriteLine($"Счётчик цикла: {i}. Читаем элемент с индексом {i * 2} значение: {intArray[i * 2]}");

Вывод консоли будет выглядеть следующим образом:

Счётчик цикла: 0. Читаем элемент с индексом 0 значение: 12

Счётчик цикла: 1. Читаем элемент с индексом 2 значение: 16

Счётчик цикла: 2. Читаем элемент с индексом 4 значение: -12

Счётчик цикла: 3. Читаем элемент с индексом 6 значение: 80

Счётчик цикла: 4. Читаем элемент с индексом 8 значение: 90

Счётчик цикла: 5. Читаем элемент с индексом 10 значение: 102

Условие в теле цикла:

if (i*2<intArray.Length)

Требуется для того, чтобы мы не вышли за границы массива. Так, уже на шестом шаге  цикла мы бы получили то, что должны прочитать элемент с индексом 6*2=12 которого не существует (максимальный индекс у нас в примере — 10) и программа бы выдала нам исключение:

System.IndexOutOfRangeException

Сообщение = Index was outside the bounds of the array.

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

В приведенном выше примере остается ещё один, возможно, непонятный для новичков в C# момент, а именно, условие выхода из  цикла:

i<intArray.Length;

Что за Length? Забегая немного вперед, скажу, что это свойство массива, определяющее общее количество элементов массива по всем его измерениям. Чтобы получить значение этого свойства необходима написать имя массива, на клавиатуре точку (.) и в списке выбрать Length. О том, как такая «магия» работает в C# мы поговорим позднее. А пока перейдем к дальнейшей работе с массивами.

Многомерные массивы

До сих пор мы имели дело с так называемыми одномерными массивами, которые можно себе представить в уме как ряд переменных (числовых, строковых, символьных и т.д.). Визуально, наши одномерные массивы выглядели так:

Однако, на одном измерении массива C# не заканчивается. Теоретически мы можем задать массив с любым количеством измерений (которые кстати, называются рангом), но на практике, обычно встречаются одно-, двух- и намного реже — трехмерные массивы. Образно, двумерный массив можно представить как обычную таблицу, а трехмерный массив — как куб переменных. Например, чтобы задать двумерный массив чисел (ранг = 2), необходима сделать вот такое объявление переменной:

int[,] TwoDimArray;

обратите внимание на запятую в квадратных скобках. В C# существует простое правило: ранг массива всегда на единицу больше количества запятых в квадратных скобках. Массив с рангом равным 3 (трехмерный) будет объявляться так:

int[,,] ThreeDimArray;

Инициализируются многомерные массивы точно также, как и одномерные — необходимо задать количество элементов в каждом измерении. Например, зададим массив состоящий из двух столбцов и пяти строк:

int[,] array = new int[5, 2] { { 0, 1 }, 
                               { 2, 3 }, 
                               { 4, 5 }, 
                               { 6, 7 }, 
                               { 8, 9 } 
                             };

Теперь, чтобы обратиться как какому-либо элементу массива, нам необходимо указывать два индекса — индекс строки и индекс столбца. Например, получим значение из четвертой строки второго столбца. Так как нумерация элементов в массивах начинается с нуля, то код будет такой:

Console.WriteLine(array[3, 1]);
//OUTPUT: 7

Для создания многомерных массивов могут использоваться те же самые языковые конструкции, которые показаны выше для одномерных массивов. Возможно, что на первом этапе работы с массивами в  C# сложность будет представлять перебор элементов многомерных массивов. На самом деле, ничего сложного в этом нет.

Основные методы и свойства для работы с массивами в C#

Для того, чтобы показать перебор элементов многомерных массивов, нам придётся забежать немного вперед и прояснить для себя один момент. В C# массивы представляют собой объекты со своими свойствами и методами. Если Вы имели дело с другими языками программирования, например, с Delphi, то знаете, что для получения, например, длины массива необходимо использовать отдельный метод — Length(). В C# же, в силу того, что массив — это объект, вы можете вызвать методы и читать свойства этого объекта просто написав его имя и нажав точку. В результате вы увидите вот такой список:

Подобная функциональность C# достаточно сильно упрощает изучение языка — нам не надо держать в голове перечень отдельных методов для работу с тем или иным типов данных, всё, что нам необходимо — это вызвать список методов и свойств объекта и выбрать необходимый. О том, как такое поведение осуществляется в C# мы обязательно обсудим в одной из статей блога, а пока я перечислю основные свойства и методы объектов-массивов в C#.

Свойства массивов C#

Название Тип данных Описание
Length int Возвращает общее число элементов во всех измерениях массива
Rank int Получает ранг (число измерений) массива
LongLength long Возвращает 64-разрядное целое число, представляющее общее число элементов во всех измерениях массива

Методы массивов в C#

Название Тип данных Описание
GetLength() int Возвращает 32-разрядное целое число, представляющее количество элементов в заданном измерении массива
GetLowerBound() int Получает Индекс первого элемента заданного измерения в массиве
GetUpperBound() int Получает Индекс последнего элемента заданного измерения в массиве.

Это далеко не все свойства и методы для работы с массивами в C#, но приводить их сейчас не имеет смысла, так как, во-первых, перечисленных свойств достаточно, чтобы понять как работать с массивами и, во-вторых, даже приведя перечень оставшихся свойств и методов, мы не сможем ими правильно воспользоваться, так как знаний у нас с вами пока не достаточно.

Перебор элементов многомерного массива C#

В цикле foreach

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

int[,] array = new int[5, 2] { { 0, 1 }, 
                               { 2, 3 }, 
                               { 4, 5 }, 
                               { 6, 7 }, 
                               { 8, 9 } 
                             };
foreach (int i in array)
   {
      Console.Write("{0} ", i);;
   }

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

0 1 2 3 4 5 6 7 8 9

Цикл foreach «разворачивает» массив в строку, наращивая вначале индексы самого правого измерения (в нашем случае, это измерение имеет длину 2), а затем смещается влево и перебирает все индексы этого измерения. Это очень простой способ добраться до всех элементов многомерного массива. Но, что делать, если нам необходимо пройти только по крайнему правому столбцу? То есть получить значения 1, 3, 5, 7, 9. Для этого мы можем воспользоваться циклом for.

В цикле for

Как я уже говорил выше, цикл for даёт более гибкий механизм работы с массивами C#. Итак, получим все значения элементов из последнего столбца. Приведу только два варианта. Вариант 1 — когда ранг массива известен и мы знаем размерность (количество элементов) последнего измерения:

for (int i=0; i<array.GetLength(0); i++)
   Console.Write("{0} ", array[i,1]);

здесь в условии цикла мы воспользовались методом GetLength(), чтобы получить количество элементов по первому измерению (5), а уже в теле цикла получаем значения элементов в каждой строке во втором столбце (помним, что индекс первых элементов в массиве всегда равен 0).

Второй вариант — нам надо прочитать значения в последнем столбце массива, но, при этом мы не знаем размерность второго измерения. Чтобы было по-понятнее, я объявил в коде две дополнительные переменные:

int rank = array.Rank-1;
int maxIndex = array.GetUpperBound(0) + 1;
for (int i=0; i<maxIndex; i++)
  Console.Write("{0} ", array[i, array.GetUpperBound(rank)]);

В цикле  мы запрашиваем элемент с индексами:

array[i, //текущая строка 
      array.GetUpperBound(rank)]) //последний столбец

Соответственно, если необходимо пройти по всем элементам массива, то можно использовать вложенные циклы for и организовать доступ к элементам массива в любом порядке — построчно, по столбцам…да хоть по диагонали — тут всё будет зависеть от задачи.

Массивы массивов в C#

Массив массивов — это массив, элементы которого сами являются массивами. Элементы массива массивов могут иметь различные измерения и размеры. Массив массивов также иногда называется нерегулярным или зубчатым массивом. Объявление массива массивов выглядит следующим образом:

int[][] array = new int[3][];

здесь мы объявили массив массивов на три элемента где каждый элемент — это отдельный массив. Так как массивы в C# относятся к ссылочным типам данных, то по умолчанию каждый элемент массива равен null и, чтобы использовать элементы такого массива, нам необходимо их также инициализировать. Например, так:

array[0] = new int[2];
array[1] = new int[3];
array[2] = new int[4];

Теперь наш массив содержит три массива на 2, 3 и 4 элемента соответственно и каждый из этих массивов будет содержать целые числа. Чтобы заполнить массив значениями элементов, можно воспользоваться любым из способов, которые мы рассмотрели для одномерных массивов, например, так:

int[][] array = new int[][]
{
    new int[] { 1, 2},
    new int[] { 3, 4, 5 },
    new int[] { 6, 7, 8, 9 }
};

Для доступа к элементам массива массивов необходимо использовать вот такую языковую конструкцию:

array[0][1]
//2

Неявно типизированные массивы

Как и обычные переменные, C# допускает объявление неявно типизированных массивов. При этом, компилятор сам определить наиболее подходящий тип элементов такого массива. Например,

var a = new[] { 1, 10, 100, 1000 }; // int[]
var b = new[] { "hello", null, "world" }; // string[]

При этом, если Вы попытаетесь объявить вот такой массив:

var с = new[] { 1, null, "world" };

то компилятор C# вернет следующую ошибку:

Ошибка CS0826 Нет подходящего типа для неявно типизированного массива.

Работать с такими массивами можно точно также, как и со всеми другими типами массивов, которые были рассмотрены выше.

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

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

int[] arr = [1, 2, 3, 4, 5];
int a = arr[^1]; //5. ПЕРВЫЙ элемент С КОНЦА
int b = arr[^2]; //4. ВТОРОЙ элемент С КОНЦА

Итого

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

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
guest
1 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии