Теория
Когато трябва да пазим много стойности от един тип (оценките на цял клас, имената на студентите), отделни променливи не вършат работа. Масивът (Array) е наредена редица от елементи с фиксирана дължина, зададена при създаването му.
Достъпваме елементите по индекс в квадратни скоби: students[0] е първият елемент. Индексите започват от 0, така че последният елемент е на позиция Length - 1. Излизане извън границите хвърля IndexOutOfRangeException — една от най-честите грешки при начинаещи.
Списъкът List<T> е "умен масив" с променлив размер: Add() добавя, Remove() маха, Contains() проверява дали елемент съществува. T в ъгловите скоби е типът на елементите — List<int>, List<string>. За да го ползваме, ни трябва using System.Collections.Generic;.
За обхождане на колекция най-удобен е цикълът foreach — той минава през всеки елемент поред, без да се занимаваме с индекси. Когато обаче ни трябва самият индекс (или искаме да променяме елементи), използваме класическия for.
Три шаблона покриват повечето задачи с колекции: сума/средно (акумулатор от Модул 3), максимум/минимум (започнете с първия елемент и сравнявайте) и търсене с флаг (bool found = false; → вдигнете флага при намиране). Научите ли ги, задачите започват да си приличат.
Съществуват и двумерни масиви — таблици от стойности: int[,] grid = new int[3, 4]; (3 реда × 4 колони), достъп с grid[ред, колона]. Обхождат се с вложени цикли. За начало е достатъчно да знаете, че ги има.
Златни правила
Индексацията започва от 0
Индексите в масивите и списъците винаги започват от 0. Първият елемент е на индекс 0, а последният — на Дължината - 1.
Масивът е с фиксиран размер
Веднъж създаден, масивът (Array) не може да променя броя на елементите си. Ако трябва да добавяте/махате — използвайте List<T>.
Списъците са динамични
Колекцията List<T> е динамична — можете свободно да добавяте (Add) и премахвате (Remove) елементи по всяко време.
Length срещу Count
Масивът има свойство Length, списъкът — свойство Count. Лесно се бъркат — компилаторът ще ви подскаже.
Бърз справочник
Масив срещу Списък
| Характеристика | Масив (Array) | Списък (List) |
|---|---|---|
| Декларация | int[] nums = new int[5]; | List<int> nums = new List<int>(); |
| Размер | Фиксиран (nums.Length) | Динамичен (nums.Count) |
| Добавяне | nums[0] = 5; (само на съществуваща позиция) | nums.Add(5); |
| Премахване | Невъзможно (само презапис) | nums.Remove(5); / nums.RemoveAt(0); |
| Проверка за елемент | ръчно с цикъл (или Array.IndexOf) | nums.Contains(5) |
Полезни методи на List<T>
| Метод | Какво прави? | Пример |
|---|---|---|
Add() | Добавя елемент в края | grades.Add(6); |
Remove() | Премахва първото срещане на стойност | names.Remove("Иван"); |
RemoveAt() | Премахва елемента на даден индекс | names.RemoveAt(0); |
Contains() | Проверява дали стойността съществува (bool) | if (names.Contains("Иван")) |
IndexOf() | Връща индекса на стойност (или -1) | int i = names.IndexOf("Иван"); |
Clear() | Изтрива всички елементи | names.Clear(); |
Код за анализ и пренаписване
Масив, списък и обхождане с foreach
using System;
using System.Collections.Generic;
namespace ModuleFour
{
class Program
{
static void Main(string[] args)
{
// Масив
string[] students = { "Иван", "Мария", "Георги" };
Console.WriteLine($"Първият студент е: {students[0]}");
// Списък
List<int> grades = new List<int>();
grades.Add(5);
grades.Add(6);
grades.Add(4);
// Foreach цикъл - идеален за преминаване през колекции
Console.WriteLine("Оценки:");
foreach (int grade in grades)
{
Console.WriteLine(grade);
}
}
}
}Какво се случва тук?
- Масивът
studentsе създаден със синтаксис за инициализация{ ... }— размерът (3) се определя автоматично от броя на елементите. -
students[0]връща "Иван" — първият елемент е на индекс 0.students[3]би хвърлилIndexOutOfRangeException, защото валидните индекси са 0, 1 и 2. -
List<int> grades = new List<int>();създава празен списък — за разлика от масива, не указваме размер. -
Add()добавя елементи в края, списъкът расте сам. След трите извикванияgrades.Countе 3. -
foreach (int grade in grades)се чете: "за всяка оценкаgradeв списъкаgrades". Променливатаgradeпоема стойността на текущия елемент при всяка итерация — без индекси, без граници, без грешки. - Редът
using System.Collections.Generic;най-отгоре е задължителен, за да работиList<T>.
Трите класически шаблона: максимум, средно, търсене
using System;
namespace ModuleFourExtra
{
class Program
{
static void Main(string[] args)
{
int[] grades = { 4, 5, 6, 3, 5, 6 };
// Шаблон 1 и 2: максимум + сума в едно обхождане
int max = grades[0];
int sum = 0;
foreach (int grade in grades)
{
if (grade > max)
{
max = grade;
}
sum += grade;
}
double average = (double)sum / grades.Length;
Console.WriteLine($"Най-висока: {max}, Средна: {average:F2}");
// Шаблон 3: търсене с флаг
Console.Write("Търсена оценка: ");
int target = int.Parse(Console.ReadLine());
bool found = false;
for (int i = 0; i < grades.Length; i++)
{
if (grades[i] == target)
{
Console.WriteLine($"Намерена на индекс {i}!");
found = true;
break; // спираме при първото съвпадение
}
}
if (!found)
{
Console.WriteLine("Няма такава оценка.");
}
}
}
}Какво се случва тук?
- Максимумът започва от първия елемент (
grades[0]), не от 0 — ако всички оценки бяха отрицателни числа, стартът от 0 щеше да даде грешен отговор. - Сумата и максимумът се събират в едно обхождане — няма нужда от два цикъла за две неща.
-
(double)sum / grades.Length— преобразуването е задължително: иначе23 / 6е целочислено и средната "оценка" става 3 вместо 3.83 (капанът от Модул 1!). - Търсенето ползва
forвместоforeach, защото ни трябва индексът на намерения елемент. - Флагът
foundрешава въпроса "а ако няма такъв елемент?" — проверкатаif (!found)след цикъла отпечатва съобщението само ако флагът така и не е бил вдигнат. -
breakспира търсенето при първото съвпадение — няма смисъл да обикаляме останалите елементи.
Внимавай! Чести грешки
Грешките, които почти всеки прави в тази тема — виж разликата между грешния и правилния код.
Излизане извън границите
Грешно
int[] arr = { 1, 2, 3, 4, 5 };
for (int i = 0; i <= arr.Length; i++)
{
Console.WriteLine(arr[i]); // гърми при i = 5
}Правилно
int[] arr = { 1, 2, 3, 4, 5 };
for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine(arr[i]);
}При 5 елемента валидните индекси са 0–4. <= пуска и i = 5 → IndexOutOfRangeException. Шаблонът е винаги i < arr.Length.
Премахване по време на foreach
Грешно
foreach (string name in names)
{
if (name == "Иван")
{
names.Remove(name); // InvalidOperationException!
}
}Правилно
names.RemoveAll(name => name == "Иван");
// или: for цикъл отзад напредКолекцията не може да се променя, докато foreach я обхожда. RemoveAll е най-краткото решение; иначе — for отзад напред или обхождане на копие (names.ToList()).
Length при списък (и Count при масив)
Грешно
List<int> grades = new List<int>();
Console.WriteLine(grades.Length); // няма такова свойствоПравилно
List<int> grades = new List<int>();
Console.WriteLine(grades.Count);Масив → Length, списък → Count. Бъркането им е безобидно (компилаторът отказва веднага), но е сред най-честите грешки. Просто го запомнете като двойка.
Списък без new
Грешно
List<int> grades;
grades.Add(5); // CS0165: неинициализирана променливаПравилно
List<int> grades = new List<int>();
grades.Add(5);Декларацията само създава променливата-референция — самият списък се ражда чак с new List<int>(). Без него няма къде да се добавя.
Задачи за самостоятелна работа
Опитай първо сам — подсказката е там само ако наистина закъсаш.
Задача 1: Най-голямото число
Въведете 5 числа в масив и намерете най-голямото от тях.
Задача 2: Списък с уникални имена
Направете списък с имена. Нека потребителят да добавя имена. Ако въведе име, което вече съществува, отпечатайте "Името вече е добавено" и го премахнете.
Задача 3: Обърнат масив
Въведете 5 числа в масив и го обърнете наобратно БЕЗ готови методи (без Array.Reverse) — само с цикъл и размени. Отпечатайте резултата.
Мини-тест за проверка
Отговори си наум (или на глас!) и чак тогава разкрий отговора.
1. Кое свойство се използва за намиране размера на масив? А на списък?
2. Какъв ще е резултатът при опит за достъп до arr[5] при масив с точно 5 елемента?
3. Как се премахват елементи от списък, докато го обхождаме?
Речник на термините
Виж пълния речник на курсаint[] nums = new int[5];.Add, Remove, Contains. Изисква using System.Collections.Generic;.Length - 1.arr[2] е третият елемент.T в List<T> е "заместител" за типа на елементите: List<int>, List<string>, List<Student>.int[] a = { 1, 2, 3 }; или new List<int> { 1, 2, 3 }.bool found = false; преди цикъла, вдигане при намиране, проверка след цикъла — отговаря и на въпроса "а ако го няма?".Провери знанията
Бърз тест с избор от отговори върху термините на модула — с точки и моментална обратна връзка.
Провери знанията си
7 въпроса върху термините от модула. Показваме определение — ти избираш правилния термин.
Препоръки за този модул
- Нарисувайте масива като редица от кутийки с номерата (индексите) под тях — нулевата индексация става очевидна.
- Класически шаблон: обхождане с
for (int i = 0; i < arr.Length; i++)— забележете<, не<=! - Не променяйте колекция (Add/Remove), докато я обхождате с
foreach— ще получите изключение. Използвайтеforили направете копие. - Изберете правилната структура: знаете точния брой → масив; броят се променя →
List<T>.