Уявіть, що ви створюєте додаток для онлайн-магазину. Користувач додає товар до кошика, і програма має прийняти рішення: чи достатньо товару на складі? Якщо так — додати в кошик, якщо ні — показати повідомлення про відсутність. Далі програма має перевірити всі товари в кошику, обчислити загальну суму з урахуванням знижок та податків. Кожна з цих дій потребує потоку керування (control flow) — механізму, який визначає, які інструкції виконуються, в якому порядку та за яких умов.
Потік керування — це фундаментальна концепція програмування, яка дозволяє створювати динамічні та інтелектуальні програми. Без нього код виконувався б лише послідовно, рядок за рядком, що унеможливлювало б будь-яку складну логіку.
У цьому розділі ви опануєте:
if, else, switch, тернарний оператор)for, foreach, while, do-while)break, continue, return)switch виразах для елегантної обробки складних умовПеред вивченням цього розділу рекомендується ознайомитися з:
Умовні оператори дозволяють програмі приймати рішення на основі певних умов. Це фундамент логіки будь-якої програми.
ifНайпростіший спосіб виконати код залежно від умови — це оператор if.
Синтаксис:
if (умова)
{
// Код, що виконується, якщо умова істинна
}
Приклад:
int temperature = 25;
if (temperature > 20)
{
Console.WriteLine("Сьогодні тепло!");
}
{} навіть для однорядкових блоків коду. Це покращує читабельність та запобігає помилкам при подальшому редагуванні коду.if-elseДля обробки альтернативного сценарію використовується else:
int age = 16;
if (age >= 18)
{
Console.WriteLine("Ви можете голосувати.");
}
else
{
Console.WriteLine("Ви ще не досягли віку для голосування.");
}
else ifДля перевірки кількох умов послідовно:
int score = 85;
if (score >= 90)
{
Console.WriteLine("Оцінка: Відмінно");
}
else if (score >= 75)
{
Console.WriteLine("Оцінка: Добре");
}
else if (score >= 60)
{
Console.WriteLine("Оцінка: Задовільно");
}
else
{
Console.WriteLine("Оцінка: Незадовільно");
}
Умовні оператори можна вкладати один в одного:
int temperature = 22;
bool isRaining = false;
if (temperature > 20)
{
if (isRaining)
{
Console.WriteLine("Тепло, але дощить. Візьміть парасольку!");
}
else
{
Console.WriteLine("Чудова погода для прогулянки!");
}
}
else
{
Console.WriteLine("Холодно. Одягніться тепліше.");
}
Тернарний оператор ?: — це скорочена форма if-else, яка повертає значення:
Синтаксис:
умова ? значення_якщо_істина : значення_якщо_хиба
Приклад:
int age = 20;
string status = age >= 18 ? "Дорослий" : "Неповнолітній";
Console.WriteLine(status); // Виведе: Дорослий
int number = 42;
string result;
if (number % 2 == 0)
{
result = "Парне";
}
else
{
result = "Непарне";
}
int number = 42;
string result = number % 2 == 0 ? "Парне" : "Непарне";
if-else для покращення читабельності.switchОператор switch використовується для багатоваріантного вибору на основі значення виразу.
Синтаксис:
switch (вираз)
{
case значення1:
// Код для значення1
break;
case значення2:
// Код для значення2
break;
default:
// Код за замовчуванням
break;
}
Приклад:
int dayOfWeek = 3;
switch (dayOfWeek)
{
case 1:
Console.WriteLine("Понеділок");
break;
case 2:
Console.WriteLine("Вівторок");
break;
case 3:
Console.WriteLine("Середа");
break;
case 4:
Console.WriteLine("Четвер");
break;
case 5:
Console.WriteLine("П'ятниця");
break;
case 6:
Console.WriteLine("Субота");
break;
case 7:
Console.WriteLine("Неділя");
break;
default:
Console.WriteLine("Некоректний день тижня");
break;
}
caseМожна об'єднувати кілька міток case для виконання одного блоку коду:
int dayOfWeek = 6;
switch (dayOfWeek)
{
case 1:
case 2:
case 3:
case 4:
case 5:
Console.WriteLine("Робочий день");
break;
case 6:
case 7:
Console.WriteLine("Вихідний");
break;
default:
Console.WriteLine("Некоректний день");
break;
}
case, як у деяких інших мовах. Кожен case має закінчуватися оператором break, return, goto або throw.| Конструкція | Коли використовувати | Переваги | Недоліки |
|---|---|---|---|
if-else | Умови на основі булевих виразів, складна логіка | Універсальність, зрозумілість | Багато коду для простих випадків |
| Тернарний оператор | Прості умови з поверненням значення | Компактність | Погана читабельність при вкладанні |
switch | Перевірка одного виразу на множину значень | Читабельність для багатьох варіантів | Обмежений тип виразу (до C# 7) |
switch вираз | Як switch, але з поверненням значення | Компактність, pattern matching | Потребує C# 8.0+ |
Цикли дозволяють повторювати блок коду кілька разів. Це критично важливо для обробки колекцій, повторення дій та реалізації алгоритмів.
forЦикл for використовується, коли кількість ітерацій відома заздалегідь.
Синтаксис:
for (ініціалізація; умова; ітератор)
{
// Код, що повторюється
}
Компоненти:
Приклад:
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"Ітерація: {i}");
}
// Виведе:
// Ітерація: 0
// Ітерація: 1
// Ітерація: 2
// Ітерація: 3
// Ітерація: 4
forМожна використовувати кілька змінних:
for (int i = 0, j = 10; i < j; i++, j--)
{
Console.WriteLine($"i = {i}, j = {j}");
}
for ідеально підходить для роботи з масивами, коли потрібен доступ до індексу елемента.foreachЦикл foreach використовується для ітерації по колекціях без необхідності маніпулювати індексом.
Синтаксис:
foreach (тип змінна in колекція)
{
// Код, що використовує змінну
}
Приклад:
string[] fruits = { "Яблуко", "Банан", "Апельсин", "Груша" };
foreach (string fruit in fruits)
{
Console.WriteLine($"Фрукт: {fruit}");
}
varКомпілятор може автоматично визначити тип:
var numbers = new[] { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
Console.WriteLine(number * 2);
}
foreach працює з будь-яким типом, що реалізує інтерфейс IEnumerable або IEnumerable<T>. Елементи колекції доступні лише для читання всередині циклу.int[] numbers = { 10, 20, 30, 40, 50 };
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"Індекс {i}: {numbers[i]}");
}
int[] numbers = { 10, 20, 30, 40, 50 };
foreach (int number in numbers)
{
Console.WriteLine($"Значення: {number}");
}
whileЦикл while виконується доки умова істинна. Він використовується, коли кількість ітерацій невідома наперед.
Синтаксис:
while (умова)
{
// Код, що повторюється
}
Приклад:
int count = 0;
while (count < 5)
{
Console.WriteLine($"Лічильник: {count}");
count++;
}
while колись стане хибною, інакше програма зависне.// НЕ РОБІТЬ ТАК!
while (true)
{
// Цей код виконуватиметься вічно
}
do-whileЦикл do-while схожий на while, але гарантує виконання тіла циклу принаймні один раз, оскільки умова перевіряється після виконання блоку коду.
Синтаксис:
do
{
// Код, що повторюється
} while (умова);
Приклад:
int userInput;
do
{
Console.Write("Введіть число більше 0: ");
userInput = int.Parse(Console.ReadLine());
} while (userInput <= 0);
Console.WriteLine($"Ви ввели: {userInput}");
int count = 10;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
// Код не виконається жодного разу
int count = 10;
do
{
Console.WriteLine(count);
count++;
} while (count < 5);
// Виведе: 10 (один раз)
| Тип циклу | Коли використовувати | Особливості |
|---|---|---|
for | Кількість ітерацій відома, потрібен індекс | Компактний синтаксис, ініціалізація + умова + ітератор |
foreach | Ітерація по колекції без індексу | Найпростіший синтаксис, лише для читання |
while | Кількість ітерацій невідома, перевірка в початку | Умова перевіряється перед виконанням |
do-while | Те саме що while, але потрібне мінімум 1 виконання | Умова перевіряється після виконання |
Оператори переходу (jump statements) дозволяють змінювати нормальний потік виконання програми. Вони можуть переривати цикли, пропускати ітерації або виходити з методів.
breakОператор break припиняє виконання найближчого циклу або оператора switch.
Використання в циклі:
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break; // Вихід з циклу при i = 5
}
Console.WriteLine(i);
}
// Виведе: 0 1 2 3 4
Використання у вкладених циклах:
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (j == 1)
{
break; // Виходить лише з внутрішнього циклу
}
Console.WriteLine($"i={i}, j={j}");
}
}
break припиняє лише найближчий цикл або switch. Для виходу з кількох вкладених циклів використовуйте прапорець (flag) або винесіть код у метод з return.continueОператор continue пропускає решту коду в поточній ітерації циклу та переходить до наступної ітерації.
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
{
continue; // Пропустити парні числа
}
Console.WriteLine(i);
}
// Виведе: 1 3 5 7 9
Практичний приклад:
string[] files = { "document.txt", "image.png", "data.csv", "photo.jpg", "report.txt" };
foreach (string file in files)
{
if (!file.EndsWith(".txt"))
{
continue; // Пропустити файли, що не є текстовими
}
Console.WriteLine($"Обробка текстового файлу: {file}");
}
// Виведе:
// Обробка текстового файлу: document.txt
// Обробка текстового файлу: report.txt
continue для покращення читабельності коду, щоб уникнути глибокого вкладання умовних операторів у циклах.returnОператор return припиняє виконання методу та повертає керування викликаючому коду. Він може повертати значення або бути порожнім (для методів типу void).
Повернення значення:
int Add(int a, int b)
{
return a + b; // Повертає суму
}
int result = Add(5, 3); // result = 8
Використання для раннього виходу:
void ProcessOrder(Order order)
{
if (order == null)
{
Console.WriteLine("Помилка: замовлення відсутнє");
return; // Ранній вихід з методу
}
if (order.Items.Count == 0)
{
Console.WriteLine("Помилка: замовлення порожнє");
return; // Ранній вихід
}
// Обробка замовлення
Console.WriteLine($"Обробка замовлення {order.Id}");
}
breakПовністю виходить з циклу або switch. Виконання продовжується з наступного оператора після циклу.
for (int i = 0; i < 10; i++)
{
if (i == 5) break;
Console.Write(i + " ");
}
// Виведе: 0 1 2 3 4
continueПропускає решту коду в поточній ітерації, але цикл продовжується. Переходить безпосередньо до наступної ітерації.
for (int i = 0; i < 10; i++)
{
if (i == 5) continue;
Console.Write(i + " ");
}
// Виведе: 0 1 2 3 4 6 7 8 9
returnВиходить з усього методу, незалежно від циклів. Може повертати значення.
int FindFirst(int[] arr, int target)
{
for (int i = 0; i < arr.Length; i++)
{
if (arr[i] == target)
{
return i; // Повертає індекс
}
}
return -1; // Не знайдено
}
Починаючи з C# 8.0, мова отримала потужний функціонал зіставлення шаблонів (pattern matching), який революціонізував роботу з умовною логікою. Switch вирази (switch expressions) — це сучасна альтернатива традиційним операторам switch, яка є більш виразною та компактною.
string GetSeasonName(int month)
{
string season;
switch (month)
{
case 12:
case 1:
case 2:
season = "Зима";
break;
case 3:
case 4:
case 5:
season = "Весна";
break;
case 6:
case 7:
case 8:
season = "Літо";
break;
case 9:
case 10:
case 11:
season = "Осінь";
break;
default:
season = "Невідомо";
break;
}
return season;
}
string GetSeasonName(int month) => month switch
{
12 or 1 or 2 => "Зима",
3 or 4 or 5 => "Весна",
6 or 7 or 8 => "Літо",
9 or 10 or 11 => "Осінь",
_ => "Невідомо"
};
switch, яка повертає значення. Основні відмінності:=> замість : та break=>_ (discard) замість defaultПеревірка на точну відповідність значенню:
string GetDayType(int day) => day switch
{
6 => "Субота",
7 => "Неділя",
_ => "Робочий день"
};
Використання операторів порівняння <, >, <=, >=:
string GetWaterState(int temperature) => temperature switch
{
< 0 => "Лід",
0 => "Точка замерзання",
> 0 and < 100 => "Рідина",
100 => "Точка кипіння",
> 100 => "Пара",
_ => "Невідомий стан"
};
Комбінування шаблонів за допомогою and, or, not:
bool IsWeekday(int day) => day switch
{
>= 1 and <= 5 => true,
6 or 7 => false,
_ => throw new ArgumentException("Некоректний день")
};
string ClassifyAge(int age) => age switch
{
< 0 => "Некоректний вік",
>= 0 and < 13 => "Дитина",
>= 13 and < 18 => "Підліток",
>= 18 and < 65 => "Дорослий",
>= 65 => "Пенсіонер"
};
Перевірка властивостей об'єктів:
public record Person(string Name, int Age);
string GetCategory(Person person) => person switch
{
{ Age: < 18 } => "Неповнолітній",
{ Age: >= 18, Name: "Admin" } => "Адміністратор",
{ Age: >= 18 } => "Дорослий користувач",
_ => "Невідомо"
};
Використання деконструкції для перевірки кортежів або типів з деконструктором:
public record Point(int X, int Y);
string GetQuadrant(Point point) => point switch
{
(0, 0) => "Початок координат",
(> 0, > 0) => "Перший квадрант",
(< 0, > 0) => "Другий квадрант",
(< 0, < 0) => "Третій квадрант",
(> 0, < 0) => "Четвертий квадрант",
(_, 0) => "На осі X",
(0, _) => "На осі Y"
};
Використання _ для ігнорування значень:
string GetInfo((string name, int age, string city) person) => person switch
{
("John", _, _) => "Це John, незалежно від віку та міста",
(_, > 65, _) => "Пенсіонер",
(_, _, "Kyiv") => "Житель Києва",
_ => "Інший користувач"
};
whenДодаткові умови для більш точного зіставлення:
decimal CalculateDiscount(int age, bool isMember) => (age, isMember) switch
{
( < 18, _) => 0.20m, // 20% для дітей
( >= 65, _) => 0.15m, // 15% для пенсіонерів
(_, true) when age >= 18 && age < 65 => 0.10m, // 10% для членів
_ => 0m // Без знижки
};
Складніший приклад з when:
public record Order(int ItemCount, decimal TotalPrice, bool IsPriority);
string GetShippingMethod(Order order) => order switch
{
{ IsPriority: true } => "Експрес доставка",
{ ItemCount: > 10, TotalPrice: > 1000 } => "Вантажна доставка",
{ TotalPrice: > 500 } when order.ItemCount <= 5 => "Стандартна доставка з упаковкою",
{ ItemCount: <= 3 } => "Кур'єрська служба",
_ => "Звичайна пошта"
};
Компілятор C# перевіряє, чи охоплюють шаблони всі можливі значення. Якщо ні, виникне попередження:
enum TrafficLight { Red, Yellow, Green }
// Попередження CS8509: Не всі значення охоплені
string GetAction(TrafficLight light) => light switch
{
TrafficLight.Red => "Стоп",
TrafficLight.Green => "Рух"
// Відсутній Yellow!
};
// Правильна версія:
string GetActionCorrect(TrafficLight light) => light switch
{
TrafficLight.Red => "Стоп",
TrafficLight.Yellow => "Підготуватися",
TrafficLight.Green => "Рух",
_ => throw new ArgumentException("Невідомий сигнал")
};
| Аспект | Традиційний switch | Switch вираз |
|---|---|---|
| Синтаксис | Оператор з case, :, break | Вираз з => |
| Повернення значення | Потрібна окрема змінна | Пряме повернення |
| Компактність | Багатослівний | Лаконічний |
| Pattern matching | Обмежений (C# 7+) | Повна підтримка |
| Перевірка вичерпності | Ні | Так (попередження) |
| Версія C# | Всі версії | C# 8.0+ |
public record HttpResponse(int StatusCode, string Body);
string HandleResponse(HttpResponse response) => response switch
{
{ StatusCode: 200, Body: not null } => $"Успіх: {response.Body}",
{ StatusCode: 200, Body: null } => "Успіх, але немає даних",
{ StatusCode: >= 200 and < 300 } => "Успішна відповідь",
{ StatusCode: 400 } => "Поганий запит",
{ StatusCode: 401 } => "Не авторизовано",
{ StatusCode: 404 } => "Не знайдено",
{ StatusCode: >= 400 and < 500 } => "Помилка клієнта",
{ StatusCode: >= 500 } => "Помилка сервера",
_ => "Невідомий статус"
};
void ProcessUser(User user)
{
if (user != null)
{
if (user.IsActive)
{
if (user.HasPermission)
{
// Основна логіка
}
}
}
}
void ProcessUser(User user)
{
if (user == null) return;
if (!user.IsActive) return;
if (!user.HasPermission) return;
// Основна логіка
}
for — коли потрібен індекс або конкретна кількість ітераційforeach — для простої ітерації по колекціїwhile — коли кількість ітерацій невідомаdo-while — коли потрібне мінімум одне виконанняswitch, коли це доцільно.foreach (var x in items)
{
// Що таке x?
}
foreach (var product in products)
{
// Зрозуміло, що це продукт
}
// Помилка: лічильник не змінюється
int i = 0;
while (i < 10)
{
Console.WriteLine(i);
// Забули i++
}
var numbers = new List<int> { 1, 2, 3, 4, 5 };
// Помилка: зміна списку під час ітерації
foreach (var num in numbers)
{
if (num % 2 == 0)
{
numbers.Remove(num); // InvalidOperationException!
}
}
for в зворотному порядку або створіть нову колекцію.// Правильно: ітерація у зворотному порядку
for (int i = numbers.Count - 1; i >= 0; i--)
{
if (numbers[i] % 2 == 0)
{
numbers.RemoveAt(i);
}
}
break у switch// Помилка компіляції в C#
switch (value)
{
case 1:
Console.WriteLine("One");
// Забули break - помилка компіляції!
case 2:
Console.WriteLine("Two");
break;
}
break, return або goto в кінці кожного case.continue та break// Помилка: плутанина між continue та break
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
continue; // Пропускає лише 5
}
Console.WriteLine(i);
}
// Очікували вихід на 5, але отримали пропуск
break — виходить з циклуcontinue — пропускає поточну ітераціюСимптом: Попередження компілятора про завжди істинну або хибну умову.
int x = 5;
if (x = 5) // Помилка: присвоєння замість порівняння
{
// ...
}
Вирішення:
int x = 5;
if (x == 5) // Правильно: оператор порівняння
{
// ...
}
Симптом: Код після return, break або безумовного переходу ніколи не виконується.
int GetValue()
{
return 42;
Console.WriteLine("Це ніколи не виконається"); // Попередження
}
Вирішення: Видаліть недосяжний код або перегляньте логіку.
Симптом: Компілятор повідомляє, що метод не завжди повертає значення.
int GetSign(int number)
{
if (number > 0)
{
return 1;
}
else if (number < 0)
{
return -1;
}
// Що якщо number == 0?
}
Вирішення:
int GetSign(int number)
{
if (number > 0) return 1;
if (number < 0) return -1;
return 0; // Обробка всіх випадків
}
Симптом: Цикл виконується на одну ітерацію більше або менше, ніж очікувалось.
int[] arr = { 1, 2, 3, 4, 5 };
// Помилка: IndexOutOfRangeException
for (int i = 0; i <= arr.Length; i++) // Повинно бути <, а не <=
{
Console.WriteLine(arr[i]);
}
Вирішення:
for (int i = 0; i < arr.Length; i++) // Правильно
{
Console.WriteLine(arr[i]);
}
Напишіть програму, яка запитує у користувача ціле число та визначає, чи є воно парним або непарним, використовуючи оператор if-else.
%. Якщо number % 2 == 0, число парне.Console.Write("Введіть ціле число: ");
int number = int.Parse(Console.ReadLine());
if (number % 2 == 0)
{
Console.WriteLine($"{number} є парним числом.");
}
else
{
Console.WriteLine($"{number} є непарним числом.");
}
Напишіть програму, яка обчислює суму всіх цілих чисел від 1 до N (включно) за допомогою циклу for.
Console.Write("Введіть число N: ");
int n = int.Parse(Console.ReadLine());
int sum = 0;
for (int i = 1; i <= n; i++)
{
sum += i;
}
Console.WriteLine($"Сума чисел від 1 до {n} = {sum}");
Створіть масив з 5 елементів та виведіть всі його елементи за допомогою циклу foreach.
int[] numbers = { 10, 20, 30, 40, 50 };
Console.WriteLine("Елементи масиву:");
foreach (int number in numbers)
{
Console.WriteLine(number);
}
Створіть простий калькулятор, який приймає два числа та оператор (+, -, *, /) і виконує відповідну операцію, використовуючи switch вираз.
switch вираз для визначення операції на основі введеного оператора.Console.Write("Введіть перше число: ");
double num1 = double.Parse(Console.ReadLine());
Console.Write("Введіть оператор (+, -, *, /): ");
string op = Console.ReadLine();
Console.Write("Введіть друге число: ");
double num2 = double.Parse(Console.ReadLine());
double result = op switch
{
"+" => num1 + num2,
"-" => num1 - num2,
"*" => num1 * num2,
"/" when num2 != 0 => num1 / num2,
"/" => throw new DivideByZeroException("Ділення на нуль!"),
_ => throw new InvalidOperationException("Невідомий оператор")
};
Console.WriteLine($"Результат: {num1} {op} {num2} = {result}");
Напишіть програму, яка виводить таблицю множення для числа від 1 до 10, використовуючи вкладені цикли for.
for (int i = 1; i <= 10; i++)
{
for (int j = 1; j <= 10; j++)
{
Console.Write($"{i * j,4}"); // Форматування з вирівнюванням
}
Console.WriteLine(); // Новий рядок після кожного ряду
}
Напишіть програму, яка знаходить всі прості числа від 2 до N. Використовуйте оператори continue або break для оптимізації.
Console.Write("Введіть число N: ");
int n = int.Parse(Console.ReadLine());
Console.WriteLine($"Прості числа від 2 до {n}:");
for (int i = 2; i <= n; i++)
{
bool isPrime = true;
for (int j = 2; j * j <= i; j++)
{
if (i % j == 0)
{
isPrime = false;
break; // Знайшли дільник - не просте
}
}
if (isPrime)
{
Console.Write(i + " ");
}
}
Console.WriteLine();
Створіть ієрархію класів для геометричних фігур (коло, прямокутник, трикутник) та напишіть метод, який використовує pattern matching для обчислення площі.
public abstract record Shape;
public record Circle(double Radius) : Shape;
public record Rectangle(double Width, double Height) : Shape;
public record Triangle(double Base, double Height) : Shape;
public class ShapeCalculator
{
public static double CalculateArea(Shape shape) => shape switch
{
Circle { Radius: var r } => Math.PI * r * r,
Rectangle { Width: var w, Height: var h } => w * h,
Triangle { Base: var b, Height: var h } => 0.5 * b * h,
_ => throw new ArgumentException("Невідомий тип фігури")
};
public static string GetDescription(Shape shape) => shape switch
{
Circle { Radius: < 5 } => "Маленьке коло",
Circle { Radius: >= 5 and < 10 } => "Середнє коло",
Circle { Radius: >= 10 } => "Велике коло",
Rectangle { Width: var w, Height: var h } when w == h => "Квадрат",
Rectangle => "Прямокутник",
Triangle => "Трикутник",
_ => "Невідома фігура"
};
}
// Використання:
var shapes = new Shape[]
{
new Circle(5),
new Rectangle(4, 6),
new Triangle(3, 4),
new Rectangle(5, 5)
};
foreach (var shape in shapes)
{
double area = ShapeCalculator.CalculateArea(shape);
string description = ShapeCalculator.GetDescription(shape);
Console.WriteLine($"{description}: Площа = {area:F2}");
}
Напишіть метод бінарного пошуку в відсортованому масиві. Використовуйте цикл while та оператори переходу для ефективного пошуку.
public class BinarySearch
{
public static int Search(int[] sortedArray, int target)
{
int left = 0;
int right = sortedArray.Length - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (sortedArray[mid] == target)
{
return mid; // Знайдено
}
if (sortedArray[mid] < target)
{
left = mid + 1; // Шукаємо в правій половині
}
else
{
right = mid - 1; // Шукаємо в лівій половині
}
}
return -1; // Не знайдено
}
}
// Використання:
int[] numbers = { 2, 5, 8, 12, 16, 23, 38, 45, 56, 67, 78 };
int target = 23;
int index = BinarySearch.Search(numbers, target);
if (index != -1)
{
Console.WriteLine($"Елемент {target} знайдено на індексі {index}");
}
else
{
Console.WriteLine($"Елемент {target} не знайдено");
}
Створіть систему обробки HTTP запитів з використанням pattern matching для маршрутизації та обробки різних типів запитів.
public record HttpRequest(string Method, string Path, Dictionary<string, string> Headers);
public class RequestHandler
{
public static string HandleRequest(HttpRequest request) => request switch
{
// API ендпоінти
{ Method: "GET", Path: "/api/users" } => GetAllUsers(),
{ Method: "GET", Path: var p } when p.StartsWith("/api/users/")
=> GetUserById(ExtractId(p)),
{ Method: "POST", Path: "/api/users" } => CreateUser(),
{ Method: "PUT", Path: var p } when p.StartsWith("/api/users/")
=> UpdateUser(ExtractId(p)),
{ Method: "DELETE", Path: var p } when p.StartsWith("/api/users/")
=> DeleteUser(ExtractId(p)),
// Статичні ресурси
{ Path: var p } when p.EndsWith(".css") => ServeStaticFile(p, "text/css"),
{ Path: var p } when p.EndsWith(".js") => ServeStaticFile(p, "application/javascript"),
{ Path: var p } when p.EndsWith(".html") => ServeStaticFile(p, "text/html"),
// Особливі випадки
{ Headers: var h } when h.ContainsKey("Authorization") && !IsAuthorized(h)
=> "401 Unauthorized",
{ Method: "OPTIONS" } => HandleOptionsRequest(),
// За замовчуванням
_ => "404 Not Found"
};
private static string ExtractId(string path) => path.Split('/').Last();
private static string GetAllUsers() => "Список всіх користувачів";
private static string GetUserById(string id) => $"Користувач з ID: {id}";
private static string CreateUser() => "Створено нового користувача";
private static string UpdateUser(string id) => $"Оновлено користувача {id}";
private static string DeleteUser(string id) => $"Видалено користувача {id}";
private static string ServeStaticFile(string path, string contentType)
=> $"Serving {path} as {contentType}";
private static string HandleOptionsRequest() => "CORS headers sent";
private static bool IsAuthorized(Dictionary<string, string> headers) => true;
}
// Використання:
var requests = new[]
{
new HttpRequest("GET", "/api/users", new()),
new HttpRequest("GET", "/api/users/123", new()),
new HttpRequest("POST", "/api/users", new()),
new HttpRequest("GET", "/styles/main.css", new()),
new HttpRequest("GET", "/unknown", new())
};
foreach (var request in requests)
{
string response = RequestHandler.HandleRequest(request);
Console.WriteLine($"{request.Method} {request.Path} => {response}");
}
У цьому розділі ви вивчили потік керування (control flow) — фундаментальні механізми, які дозволяють програмам приймати рішення та повторювати дії:
Умовні оператори:
if, else, else if для базових умовних розгалужень? : для компактних умовних виразівswitch для множинного виборуЦикли:
for — для фіксованої кількості ітерацій з доступом до індексуforeach — для зручної ітерації по колекціяхwhile — для циклів з попередньою перевіркою умовиdo-while — для циклів з гарантованим першим виконаннямОператори переходу:
break — вихід з циклу або switchcontinue — пропуск поточної ітерації циклуreturn — вихід з методу з можливим поверненням значенняPattern Matching:
when для додаткових умовОпанування потоку керування — це ключ до написання ефективної та елегантної логіки програми. Практикуйте різні підходи, обирайте найбільш читабельні рішення та використовуйте сучасні можливості C# для створення чистого коду!
