Одной из наиболее часто используемых структур данных является массив. О том как работать с массивами в C# вы узнаете в этом уроке.
- Объявление массивов и инициализация массивов
- Доступ к элементам массива. Обход элементов массива.
- Передача массива в метод
- Многомерные массивы
- Класс System.Array
Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.
Объявление массивов и инициализация массивов
Объявление массивов
Массив – это структура данных для хранения элементом определенного типа, имеющая фиксированный размер. Доступ к элементам массива производится по числовому индексу.
Для объявления массива, после указания типа его элементов, ставятся квадратные скобки:
int[] a1; // массив типа int
Перед использованием, массив обязательно нужно проинициализировать, это можно сделать сразу, при его объявлении:
int[] na2 = new int[5]; // массив из пяти элементов типа int
Либо после объявления:
int[] na3; na3 = new int[5]; // массив из пяти элементов типа int
Для доступа к элементам массива используются числовые индексы. Значения элементов массива будут равны значению по умолчанию для типа, массив которого был создан.
Например, для указанного выше a3 – это будут нули, так как для типа int значение по умолчанию: 0;
Console.WriteLine(na3[0]); // значение: 0 Console.WriteLine(na3[1]); // значение: 0
Если попытаться вывести элементы массива na1:
Console.WriteLine(na1[0]); // ошибка компиляции
то приложение не будет собрано, т.к. массив предварительно нужно проинициализировать.
Инициализация массивов
Рассмотрим различные варианты инициализации массива. Как уже было сказано, можно просто указать количество элементов в массиве, при этом его элементам будут присвоены значения по умолчанию:
bool[] ba1 = new bool[3]; Console.WriteLine("ba1[0]: " + ba1[0].ToString());
После объявления массива значения элементам присваиваются через индекс:
string[] sa1 = new string[3]; sa1[0] = "abc"; sa1[1] = "def"; sa1[2] = "ghi"; Console.WriteLine($"sa1: {sa1[0]}, {sa1[1]}, {sa1[2]}");
Есть возможность задать конкретные значения в момент объявления с использованием ключевого слова new и указанием типа:
double[] da1 = new double[3] {0.1, 0.2, 0.3}; Console.WriteLine($"da1: {da1[0]}, {da1[1]}, {da1[2]}");
Либо без ключевого слова new:
double[] da2 = {0.4, 0.5, 0.6}; Console.WriteLine($"da2: {da2[0]}, {da2[1]}, {da2[2]}");
Неявная типизация
При объявлении массива можно воспользоваться ключевым словом var. При этом тип элементов массива следует задать явно в правой части объявления:
var va2 = new string[3]; va2[0] = "John"; va2[1] = "Mary"; va2[2] = "Mike"; Console.WriteLine($"va2: {va2[0]}, {va2[1]}, {va2[2]}");
Либо предоставить возможность “поработать” системе вывода типов:
var va1 = new[] {1, 2, 3}; Console.WriteLine($"va1: {va1[0]}, {va1[1]}, {va1[2]}");
Доступ к элементам массива. Обход элементов массива.
Как уже было сказано выше, за доступ к элементам массива отвечают числовые индексы:
int[] na4 = {1, 2, 3, 4, 5}; Console.WriteLine($"na4[0]: {na4[0]}");
При этом, если вы укажете индекс больше, чем максимально возможный, то будет выброшено исключение:
Console.WriteLine($"na4[10]: {na4[10]}");
Приведенная выше строка приведет к выбросу следующего исключения:
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
Обход элементов массива можно производить с помощью циклов for, foreach и while, последний самый неудобный для работы с массивами, его мы рассматривать не будем. Если вы работаете с циклом for, то для указания верхней границы инкрементируемой переменной можно воспользоваться свойством Length у массива:
for(int i = 0; i < na4.Length; i++) { Console.Write(na4[i].ToString() + " "); } // 1 2 3 4 5
Более удобным для обхода элементов будет foreach:
foreach(var v in na4) { Console.Write(v.ToString() + " "); } // 1 2 3 4 5
Преимущество цикла for состоит в том, что в нем вы можете модифицировать элементы массива:
for(int i = 0; i < na4.Length; i++) { na4[i] = (na4[i] + 3) * 10; Console.Write(na4[i].ToString() + " "); } // 40 50 60 70 80
Передача массива в метод
Массивы являются ссылочным типом данных, это означает, что их значения хранятся в куче, а имя переменной массива является ссылкой на соответствующую область памяти. При передаче массива в качестве аргумента в метод, происходит присваивание значения переменной массива переменной определяющей аргумент, а так как имя массива – это ссылка, то фактически происходит передача ссылки на значение в куче. Поэтому, если вы передали массив в функцию и внутри этой функции произошла модификация этого массива, то исходный массив тоже изменится.
Создадим метода WorkWithArray, который изменяет содержимое массива:
public static void WorkWithArray(int[] arr) { arr[0] = 123; }
Вызовем его в методе Main:
int[] na5 = {1, 2, 3, 4, 5}; foreach(var v in na5) // 1 2 3 4 5 Console.Write(v + " "); Console.WriteLine(); WorkWithArray(na5); foreach(var v in na5) // 123 2 3 4 5 Console.Write(v + " ");
Ниже приведена иллюстрация того, как массив и ссылки на него располагаются в памяти.
Переменные na5 в методе Main и arr в методе WorkWithArray ссылаются на одну и ту же область памяти в куче, поэтому изменение массива через переменную arr отражается на переменной na5.
Многомерные массивы
Массивы имеющее более одного измерения называются многомерными. До этого мы работали с одномерными массивами. В C# предлагается к использованию два вида многомерных массивов: прямоугольные и зубчатые, которые иногда называются массивы массивов.
Прямоугольные массивы
Прямоугольные массивы могут содержать несколько измерений (два и более), при этом количество элементов в каждом подизмерении (в каждой строке) одинаково.
Рассмотрим на примерах работу с такими массивами:
double[,] dm1 = new double[3, 3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) dm1[i, j] = i + j; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) Console.Write($"{dm1[i, j]} "); Console.WriteLine(); } Console.WriteLine(); double[,] dm2 = { { 1, 2, 3 }, { 4, 5, 6 } }; for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) Console.Write($"{dm2[i, j]} "); Console.WriteLine(); } Console.WriteLine();
Зубчатые массивы
В зубчатых массивах элементами верхнего уровня являются другие массивы, это позволяет создавать многомерные структуры, у которых строки имеют разную длину:
int[][] nm1 = new int[3][]; for(int i = 0; i < nm1.Length; i++) nm1[i] = new int[i+1]; for(int i = 0; i < nm1.Length; i++) { for(int j = 0; j < nm1[i].Length; j++) Console.Write($"{nm1[i][j]} "); Console.WriteLine(); }
Класс System.Array
Класс System.Array является базовым для всех массивов, это позволяет использовать свойства и методы данного класса при работе с массивом. Ниже в таблицах приведены некоторые полезные свойства и методы из System.Array, более полную информацию вы можете найти в официальной документации Microsoft (https://docs.microsoft.com/ru-ru/dotnet/api/system.array).
Свойства класса System.Array
Имя свойства |
Назначение |
Length |
Число элементов в массиве. Учитываются все измерения. |
Rank |
Ранг массива – число измерений. |
int[] na6 = {1, 2, 3, 4, 5, 6, 7}; int[,] nm2 = { {1, 2, 3}, {4, 5, 6}}; int[][] nm3 = new int[3][]; for(int i = 0; i < nm3.Length; i++) nm3[i] = new int[i+1]; Console.WriteLine($"na6: Length={na6.Length}, Rank={na6.Rank}"); // na6: Length=7, Rank=1 Console.WriteLine($"nm2: Length={nm2.Length}, Rank={nm2.Rank}"); // nm2: Length=6, Rank=2 Console.WriteLine($"nm3: Length={nm3.Length}, Rank={nm3.Rank}"); // nm3: Length=3, Rank=1
Методы класса System.Array
Символ * после названия метода означает, что он имеет более одной сигнатуры, за дополнительной информацией обращайтесь к официальной документации.
Имя метода |
Назначение |
BinarySearch(Array, Object)* |
Выполняет поиск элемента в массиве. |
Clear(Array, Int32, Int32) |
Присваивает значение по умолчанию определенному количеству элементов массива начиная с заданного индекса. |
Clone() |
Создает копию массива (неполную). |
Copy(Array, Array, Int32)* |
Копирует данные из одного массива в другой в заданном количестве. |
CopyTo(Array, Int32)* |
Копирует элементы из текущего массива в заданный, начиная с указанного индекса. |
Exists<T>(T[], Predicate<T>) |
Определяет наличие элемента удовлетворяющему предикату. |
GetValue(Int32)* |
Возвращает значение по указанному индексу. |
IndexOf(Array, Object)* |
Возвращает индекс первого вхождения элемента в массиве. |
Reverse(Array)* |
Задает обратный порядок для элементов в массиве. |
Sort(Array)* |
Сортирует элементы массива. |
Для вывода содержимого массива в консоль создадим метод PrintArray:
public static void PrintArray<T>(string txt, T[] arr) { Console.Write($"{txt}: "); foreach(var v in arr) { Console.Write($"{v} "); } }
Ниже приведены примеры использования представленных выше методов и свойств класса System.Array:
int[] na6 = {1, 2, 3, 4, 5, 6, 7}; int[,] nm2 = { {1, 2, 3}, {4, 5, 6}}; int[][] nm3 = new int[3][]; for(int i = 0; i < nm3.Length; i++) nm3[i] = new int[i+1]; Console.WriteLine($"na6: Length={na6.Length}, Rank={na6.Rank}"); // na6: Length=7, Rank=1 Console.WriteLine($"nm2: Length={nm2.Length}, Rank={nm2.Rank}"); // nm2: Length=6, Rank=2 Console.WriteLine($"nm3: Length={nm3.Length}, Rank={nm3.Rank}"); // nm3: Length=3, Rank=1 Console.WriteLine("BinarySearch result: " + Array.BinarySearch(na6, 5).ToString()); // BinarySearch result: 4 var na7 = (int[])na6.Clone(); Array.Clear(na7, 2, 2); PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7 PrintArray<int>("na7", na7); // na7: 1 2 0 0 5 6 7 Array.Copy(na7, na6, 4); PrintArray<int>("na6 after copy", na6); // na6 after copy: 1 2 0 0 5 6 7 (new int[]{1, 2, 3, 4}).CopyTo(na6, 0); PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7 var ans = Array.Exists<int>(na6, v => (v % 2) == 0); Console.WriteLine($"Is even number exists in na6? Answer: {ans}"); Array.Fill<int>(na7, 7); PrintArray<int>("na7", na7); // na7: 7 7 7 7 7 7 7 Console.WriteLine($"Value at 3 index in na6: {na6.GetValue(3)}"); Console.WriteLine($"Index of value=5 in na6: {Array.IndexOf(na6, 5)}"); Array.Reverse(na6); PrintArray<int>("na6", na6); // na6: 7 6 5 4 3 2 1 Array.Sort(na6); PrintArray<int>("na6", na6); // na6: 1 2 3 4 5 6 7
Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.
А как создать публичный массив?
Для создания публичного массива в C# нужно использовать модификатор доступа `public` перед типом массива и перед именем переменной массива, которую вы хотите создать. Например:
“`
public int[] myPublicArray = new int[10];
“`
В данном примере мы создаем публичный массив `myPublicArray`, состоящий из 10 элементов типа `int`. Теперь этот массив будет доступен для использования из любого места вашей программы
Написать public в самом начале вроде
public int[] massive;