Урок 5.1 — Массив (T[])
Фиксированная коллекция одного типа. Создание, сортировка, поиск и обход.
Массив (array) — это набор элементов одного типа, расположенных в памяти последовательно друг за другом. У каждого элемента есть индекс — номер позиции. Массивы — это фундаментальная структура данных, которая есть во всех языках программирования. В C# массив — это ссылочный тип, а не примитив.
Создание массива
В C# есть несколько способов создать массив. Выбор зависит от того, знаешь ли ты элементы заранее или планируешь заполнить их позже.
Способ 1: new тип[размер]
Создаёт массив заданного размера. Все элементы получают значение по умолчанию (0 для чисел, false для bool, null для ссылочных типов):
int[] numbers = new int[5]; // { 0, 0, 0, 0, 0 }
string[] names = new string[3]; // { null, null, null }
bool[] flags = new bool[2]; // { false, false }
double[] prices = new double[4]; // { 0.0, 0.0, 0.0, 0.0 }
// После создания можно заполнить
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
Способ 2: Инициализатор { ... }
Сразу перечисляешь все элементы в фигурных скобках. Размер определяется автоматически:
int[] numbers = { 10, 20, 30, 40, 50 }; // длина 5
string[] fruits = { "яблоко", "банан", "вишня" }; // длина 3
double[] temps = { 36.6, 37.0, 38.2, 39.5 };
// Можно и с new, но это избыточно:
int[] nums = new int[] { 1, 2, 3 }; // то же самое
Способ 3: new тип[] { ... } (явный тип)
Используется, когда нужно явно указать тип:
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
// Чаще всего используется сокращённая форма без new:
int[] shortForm = { 1, 2, 3, 4, 5 };
new int[5] ты не сможешь сделать массив длиннее или короче. Если нужно менять размер — используй List<T> (следующий урок).
Доступ к элементам и свойство Length
Доступ к элементам осуществляется через квадратные скобки с индексом. Свойство .Length возвращает количество элементов:
int[] numbers = { 10, 20, 30, 40, 50 };
Console.WriteLine(numbers.Length); // 5
Console.WriteLine(numbers[0]); // 10 — первый
Console.WriteLine(numbers[^1]); // 50 — последний (C# 8+)
Console.WriteLine(numbers[numbers.Length - 1]); // 50 — последний (классика)
// Изменение элемента
numbers[2] = 999;
Console.WriteLine(numbers[2]); // 999
// Срез
int[] slice = numbers[1..4]; // { 20, 999, 40 }
Свойство Length доступно только для чтения — ты не можешь его изменить.
Перебор: for и foreach
Цикл for даёт доступ к индексу каждого элемента:
int[] numbers = { 10, 20, 30, 40, 50 };
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"numbers[{i}] = {numbers[i]}");
}
// Вывод:
// numbers[0] = 10
// numbers[1] = 20
// numbers[2] = 30
// numbers[3] = 40
// numbers[4] = 50
Цикл foreach проще, но не даёт доступа к индексу:
foreach (int num in numbers)
{
Console.WriteLine(num);
}
// Вывод: 10 20 30 40 50 (каждый на новой строке)
Статические методы Array
Класс System.Array предоставляет множество полезных статических методов для работы с массивами:
Array.Sort() — сортировка
Сортирует элементы массива по возрастанию. Для чисел — от меньшего к большему, для строк — в алфавитном порядке:
int[] numbers = { 5, 2, 8, 1, 9, 3 };
Array.Sort(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 3, 5, 8, 9
string[] names = { "Вика", "Анна", "Борис" };
Array.Sort(names);
Console.WriteLine(string.Join(", ", names)); // Анна, Борис, Вика
Array.Reverse() — обратный порядок
Переворачивает массив задом наперёд:
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Reverse(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 5, 4, 3, 2, 1
// Комбинация: сортировка по убыванию
Array.Sort(numbers);
Array.Reverse(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 9, 8, 5, 3, 2, 1
Array.IndexOf() — поиск индекса
Ищет элемент в массиве и возвращает его индекс. Если элемент не найден — возвращает -1:
int[] numbers = { 10, 20, 30, 40, 50, 30 };
int index = Array.IndexOf(numbers, 30);
Console.WriteLine(index); // 2 (первое вхождение)
index = Array.IndexOf(numbers, 99);
Console.WriteLine(index); // -1 (не найдено)
// Поиск с указанием начального индекса:
index = Array.IndexOf(numbers, 30, 3);
Console.WriteLine(index); // 5 (ищем с индекса 3)
Array.Find() — поиск по условию
Находит первый элемент, удовлетворяющий условию (предикату). Если ничего не найдено — возвращает default (0 для int, null для string):
int[] numbers = { 10, 25, 30, 45, 50, 65 };
// Найти первое число больше 40
int firstBig = Array.Find(numbers, n => n > 40);
Console.WriteLine(firstBig); // 45
string[] names = { "Анна", "Борис", "Вика", "Алексей" };
// Найти первое имя длиннее 4 символов
string longName = Array.Find(names, name => name.Length > 4);
Console.WriteLine(longName); // "Борис"
n => n > 40 — это лямбда-выражение. Пока воспринимай это как краткую запись условия «для каждого n проверь, больше ли оно 40». Мы подробно разберём лямбды в уроках про делегаты и LINQ.
Array.FindAll() — все подходящие элементы
Возвращает новый массив со всеми элементами, удовлетворяющими условию:
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] evenNumbers = Array.FindAll(numbers, n => n % 2 == 0);
Console.WriteLine(string.Join(", ", evenNumbers)); // 2, 4, 6, 8, 10
int[] bigNumbers = Array.FindAll(numbers, n => n > 5);
Console.WriteLine(string.Join(", ", bigNumbers)); // 6, 7, 8, 9, 10
Array.Exists() и Array.TrueForAll()
Проверяют наличие элемента по условию и выполнение условия для всех элементов:
int[] numbers = { 1, 2, 3, 4, 5, 6 };
bool hasEven = Array.Exists(numbers, n => n % 2 == 0);
Console.WriteLine(hasEven); // True (есть чётные: 2,4,6)
bool allPositive = Array.TrueForAll(numbers, n => n > 0);
Console.WriteLine(allPositive); // True (все положительные)
bool allBig = Array.TrueForAll(numbers, n => n > 3);
Console.WriteLine(allBig); // False (1,2,3 не больше 3)
Копирование массивов
Простое присваивание не копирует массив — оно копирует ссылку. Если изменить массив через новую переменную, старый тоже изменится:
int[] original = { 1, 2, 3 };
int[] wrongCopy = original; // ❌ Это не копия, а ещё одна ссылка!
wrongCopy[0] = 999;
Console.WriteLine(original[0]); // 999 — original тоже изменился!
Чтобы создать настоящую копию, используй:
int[] original = { 1, 2, 3 };
// Способ 1: Clone()
int[] clone = (int[])original.Clone();
clone[0] = 999;
Console.WriteLine(original[0]); // 1 — оригинал не изменился!
// Способ 2: Array.Copy()
int[] copy = new int[original.Length];
Array.Copy(original, copy, original.Length);
// Способ 3: Срез (C# 8+)
int[] slice = original[..];
// Способ 4: Manual
int[] manual = new int[original.Length];
for (int i = 0; i < original.Length; i++)
manual[i] = original[i];
Многомерные массивы
В C# есть два вида многомерных массивов: прямоугольные (int[,]) и зубчатые (int[][]).
Прямоугольный массив int[,]
Все строки одинаковой длины. Представь себе таблицу Excel: строки и колонки. Запись int[,] — массив из двух измерений (ряды, колонки):
// Создание: 3 строки, 4 колонки
int[,] matrix = new int[3, 4];
// Инициализация
int[,] grid = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
// Доступ: [строка, колонка]
Console.WriteLine(grid[0, 0]); // 1 — первый элемент
Console.WriteLine(grid[1, 2]); // 7 — вторая строка, третья колонка
Console.WriteLine(grid[2, 3]); // 12 — последний элемент
// Размеры
Console.WriteLine(grid.GetLength(0)); // 3 — количество строк
Console.WriteLine(grid.GetLength(1)); // 4 — количество колонок
// Перебор вложенными циклами
for (int row = 0; row < grid.GetLength(0); row++)
{
for (int col = 0; col < grid.GetLength(1); col++)
{
Console.Write($"{grid[row, col]} ");
}
Console.WriteLine();
}
// Вывод:
// 1 2 3 4
// 5 6 7 8
// 9 10 11 12
Зубчатый массив int[][]
«Массив массивов». Каждая строка может быть своей длины. Объявляется как int[][] — это массив, каждый элемент которого — тоже массив:
// Создание: 3 строки, но колонки пока не заданы
int[][] jagged = new int[3][];
// Каждая строка — свой массив своей длины
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5, 6 };
jagged[2] = new int[] { 7, 8, 9 };
// Доступ
Console.WriteLine(jagged[0][1]); // 2
Console.WriteLine(jagged[1][3]); // 6
Console.WriteLine(jagged[2][0]); // 7
// Инициализация в одной строке
int[][] data = {
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};
// Перебор
for (int i = 0; i < data.Length; i++)
{
Console.Write($"Строка {i}: ");
for (int j = 0; j < data[i].Length; j++)
{
Console.Write($"{data[i][j]} ");
}
Console.WriteLine();
}
// Вывод:
// Строка 0: 1 2
// Строка 1: 3 4 5 6
// Строка 2: 7 8 9
Когда что использовать?
Большой пример — работа с массивом студентов
Давай объединим всё, что узнали, в одну программу:
using System;
class Program
{
static void Main()
{
// Создаём массив оценок
int[] grades = { 85, 92, 78, 95, 88, 76, 91, 84, 73, 89 };
Console.WriteLine($"Всего оценок: {grades.Length}");
Console.WriteLine($"Первая: {grades[0]}, последняя: {grades[^1]}");
// Сортировка
Array.Sort(grades);
Console.WriteLine($"После сортировки: {string.Join(", ", grades)}");
// Поиск
int index = Array.IndexOf(grades, 85);
Console.WriteLine($"Индекс оценки 85: {index}");
// Поиск по условию — найти первую оценку >= 90
int highScore = Array.Find(grades, g => g >= 90);
Console.WriteLine($"Первая высокая оценка: {highScore}");
// Все отличные оценки (>= 90)
int[] excellent = Array.FindAll(grades, g => g >= 90);
Console.WriteLine($"Отличные оценки: {string.Join(", ", excellent)}");
// Средний балл (без LINQ)
int sum = 0;
foreach (int grade in grades)
sum += grade;
double average = (double)sum / grades.Length;
Console.WriteLine($"Средний балл: {average:F2}");
// Проверка: все ли сдали (оценка >= 60)?
bool allPassed = Array.TrueForAll(grades, g => g >= 60);
Console.WriteLine($"Все сдали: {allPassed}");
// Копия массива
int[] backup = grades[..];
Console.WriteLine($"Резервная копия: {string.Join(", ", backup)}");
}
}
Полезные свойства массивов
Что важно запомнить
new int[5] или { 1, 2, 3 }.Length - 1.Clone(), Array.Copy() или срез original[..].int[,] — прямоугольный (таблица), int[][] — зубчатый (массив массивов).Тест: 5.1: Массив (T[])
3 вопроса