Управляющие конструкции и логические операторы
Как выбирать конструкцию управления
Короткое правило выбора:
if- 1-2 условия с простой логикой;switch- много веток по одному выражению;for- нужен индекс или известно число итераций;foreach- безопасный перебор коллекции;while- число итераций заранее неизвестно;do…while- минимум один проход тела.
Обобщённая шпаргалка — Циклы.
Разбор частых ошибок в условиях
// Плохо: сложное условие в одну строку
if (user != null && user.IsActive && user.Role == "Admin" && user.LastLogin > DateTime.UtcNow.AddDays(-30))
{
// ...
}
Лучше вынести в отдельные булевы переменные:
bool isNotNull = user != null;
bool isActive = isNotNull && user.IsActive;
bool isAdmin = isNotNull && user.Role == "Admin";
bool isRecent = isNotNull && user.LastLogin > DateTime.UtcNow.AddDays(-30);
if (isActive && isAdmin && isRecent)
{
// ...
}
Так код проще читать и тестировать.
Практика для циклов
- Если внутри цикла есть работа с БД/сетью, проверяйте, не лучше ли батч-обработка.
- Для раннего выхода используйте
break, но фиксируйте причину комментарием. - Не меняйте коллекцию внутри
foreach, если не уверены в последствиях.
Смежные статьи
Управляющие конструкции и логические операторы
Разработчику АрхитекторуПеред чтением: Операторы — общие понятия оператора, операнда, приоритетов и типов операций без привязки к языку.
Сначала: Циклы в коде — общая идея повторений, виды циклов и типичные ошибки без привязки к синтаксису языка.
C# — условия, циклы, операторы
Ветвление — if / else, с C# 9+ часто switch по образцу (не только константы). Циклы — for, foreach, while, do-while; для коллекций и IEnumerable — foreach. Логика короткого замыкания: && и || не вычисляют правую часть, если результат уже ясен.
Оператор — символ или ключевое слово над операндами (+, ==, ??). Управляющая конструкция меняет порядок выполнения (if, for, return).
К ним относятся:
- Условные операторы (if, switch);
- Циклы (for, while, foreach);
- Операторы перехода (break, continue, return, goto).
Они позволяют программе "думать" — принимать решения, повторять действия, выходить из блоков и т.д.
Условная операция — это выражение, которое возвращает разные значения в зависимости от условия.
Самый известный пример — тернарный оператор ? :, который является сокращённой формой if-else.
string result = (age >= 18) ? "Доступ разрешён" : "Доступ запрещён";
Разбор:
- Тернарный оператор
?:выбирает одно из двух значений по булевому условию. - Сначала вычисляется
age >= 18. - Если условие истинно, в
resultпопадёт строка"Доступ разрешён", иначе"Доступ запрещён". - Это компактная альтернатива короткому
if/else.
Условные операции позволяют писать лаконичный код без развёрнутых блоков if-else.
Операторы
Play ITЗагрузка интерактивного демо…
Операторы делятся на категории по функциональности.
Арифтетические операторы
Арифметические операторы используются для математических вычислений:
| Оператор | Описание | Пример |
|---|---|---|
+ | Сложение | int sum = a + b; |
- | Вычитание | int diff = a - b; |
* | Умножение | int product = a * b; |
/ | Деление | int quotient = a / b; |
% | Остаток от деления | int remainder = a % b; |
При делении целых чисел результат усекается: 5 / 2 = 2, а не 2.5
Операторы присваивания
Операторы присваивания присваивают значение переменной или изменяют его:
| Оператор | Пример | Аналог |
|---|---|---|
= | x = 5; | нет |
+= | x += 3; | x = x + 3; |
-= | x -= 3; | x = x - 3; |
*= | x *= 3; | x = x * 3; |
/= | x /= 3; | x = x / 3; |
%= | x %= 3; | x = x % 3; |
Такие операторы могут быть составными (комбинируют операцию и присваивание).
Инкремент и декремент
Увеличение и уменьшение значения (++, --) изменяют на 1. Это соответственно инкремент и декремент:
++ увеличивает на 1, x++ или ++x;
-- уменьшает на 1, x-- или --x.
Как обычно, имеется разница между постфиксной и префиксной формами:
int a = 5;
int b = a++; // b = 5, a = 6 (сначала присвоили, потом увеличили)
int c = ++a; // c = 7, a = 7 (сначала увеличили, потом присвоили)
Разбор:
a++— постфиксный инкремент: возвращает старое значение, затем увеличивает переменную.++a— префиксный инкремент: сначала увеличивает, затем возвращает новое значение.- Поэтому
bполучает5, аc—7. - Разница особенно важна, когда инкремент участвует в присваивании или выражениях.
Сравнение
Операторы сравнения возвращают true или false. Используются в условиях.
| Оператор | Описание | Пример |
|---|---|---|
== | Равно | if (a == b) |
!= | Не равно | if (a != b) |
> | Больше | if (a > b) |
< | Меньше | if (a < b) |
>= | Больше или равно | if (a >= b) |
<= | Меньше или равно | if (a <= b) |
Для сравнения содержимого объектов также можно использовать метод .Equals()
string a = "hello";
string b = "hello";
bool isEqual = a.Equals(b); // true
Разбор:
- Создаются две строковые переменные с одинаковым текстом.
a.Equals(b)сравнивает содержимое строк, а не только ссылку.- Для одинаковых символов результат будет
true. - Такой способ явно показывает намерение сравнить значения.
Как можно понять из перевода с английского, equal это равенство, поэтому это сравнение.
Обратите внимание на важную вещь - больше или равно пишется именно >=, а не =>, ведь => это стрелка.
Логические операторы
Логические операторы используются для объединения или инвертирования условий:
| Оператор | Описание | Пример |
|---|---|---|
&& | Логическое И | if (age > 18 && age < 60) |
! | Логическое НЕ | if (!isAuthorized) |
?? | Оператор объединения со значением по умолчанию (null-coalescing) | string displayName = name ?? "Guest"; |
Самый распространённый пример применения это !имя, означающий НЕимя, буквально.
В C# есть дополнительный оператор ?? (null-coalescing), который возвращает первый операнд, если он не null, иначе второй.
string name = null;
string displayName = name ?? "Гость"; // "Гость"
Разбор:
- Оператор
??возвращает левое значение, если оно неnull. - Так как
name == null, берётся правая часть —"Гость". - Это сокращает шаблонные проверки на
null. - Часто используется для задания безопасных значений по умолчанию.
Это полезно для безопасной работы с nullable-значениями.
is и as
Операторы is (проверка типа объекта) и as (безопасное приведение объекта).
object obj = "Hello";
string str = obj as string;
if (obj is string)
{
Console.WriteLine("Это строка");
}
Разбор:
objобъявлен какobject, но фактически содержит строку.as stringпытается безопасно привести тип: вернёт строку илиnull.obj is stringпроверяет совместимость объекта с типомstring.- При успешной проверке выполняется вывод в консоль.
nameof
Оператор nameof возвращает имя переменной, типа или члена в виде строки:
string paramName = nameof(age); // "age"
Console.WriteLine($"Ошибка в параметре: {paramName}");
Разбор:
nameof(age)возвращает имя символа как строку ("age").- Это уменьшает риск опечаток в "магических строках".
- При переименовании переменной IDE обновит
nameofавтоматически. - Интерполяция
$"..."вставляет значениеparamNameв сообщение.
Это удобно для сообщений об ошибках, логирования, когда надо вывести имя чего-то.
Лямбда-оператор
Лямбда-оператор => используется для определения лямбда-выражений:
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25
// В LINQ
var even = numbers.Where(x => x % 2 == 0);
Разбор:
Func<int, int>описывает функцию: принимаетintи возвращаетint.- Лямбда
x => x * xвычисляет квадрат переданного числа. - Вызов
square(5)возвращает25. - В
Where(...)лямбда играет роль фильтра: остаются элементы, гдеx % 2 == 0.
Функции-операторы
Как и в Java, в C# есть методы, которые выполняют функции, аналогичные операторам:
- .Equals() — аналог оператора
==для сравнения содержимого объектов; - .CompareTo() — аналог операторов сравнения (
<,>,<=,>=); - Math класс — аналоги арифметических операций с проверкой на переполнение - Math.Add(), Math.Subtract(), Math.Multiply();
- BitOperations класс — аналоги битовых операций - BitOperations.PopCount() — подсчитывает количество единиц в двоичном представлении числа;
- Convert класс — аналоги операторов приведения типов - Convert.ToInt32(), Convert.ToDouble();
- Object.ReferenceEquals() — проверяет, ссылаются ли два объекта на один и тот же экземпляр (аналог == для ссылок).
Приоритеты выполнения
Операторы выполняются в определённом порядке. Вот основные уровни (от высшего к низшему):
x++,x--(постфикс);++x, --x(префикс),!,(type);*,/,%;+,-;<,>,<=,>=;==,!=;&&;- `;
?:=,+=,-=и прочие.
Циклы
Интерактивное демо — пошаговый цикл на примере JavaScript (
for,while). В C# синтаксис другой (for,while,foreach), но порядок шагов тот же. Обобщённо: циклы в коде.
Play ITЗагрузка интерактивного демо…
Циклы позволяют выполнять блок кода несколько раз, пока выполняется определённое условие.
| Конструкция | Порядок | Когда удобна |
|---|---|---|
for | инициализация → проверка → тело → шаг | известно число повторений или нужен индекс |
foreach | следующий элемент → тело | перебор массива, List, Dictionary |
while | проверка условия → тело | число итераций заранее неизвестно |
do…while | тело → проверка условия | минимум один проход (меню, повтор ввода) |
for
for это цикл со счётчиком, который используется, когда известно количество итераций:
for (int i = 0; i < 5; i++) {
Console.WriteLine($"Итерация {i}");
}
Структура:
for (инициализация; условие; шаг) {
// тело цикла
}
Разбор:
forвключает три части — инициализация, условие продолжения, шаг после итерации.int i = 0выполняется один раз перед циклом.i < 5проверяется перед каждым проходом.i++увеличивает счётчик после каждой итерации.- Внутри тела
Console.WriteLineвыводит текущее значение индекса.
while
while выполняет код, пока условие истинно:
int count = 0;
while (count < 5) {
Console.WriteLine(count);
count++;
}
Разбор:
whileповторяет блок, пока условие истинно.- Счётчик
countстартует с нуля и меняется внутри цикла. - На каждой итерации выводится текущее значение переменной.
count++нужен для прогресса; без изменения условия возможен бесконечный цикл.
Структура:
while (условие) {
//тело цикла
}
Разбор:
- Это базовый шаблон цикла
while. - Условие проверяется перед каждой итерацией.
- Если условие изначально ложно, тело не выполнится ни разу.
- Обычно внутри изменяют состояние, влияющее на условие завершения.
do...while
do…while выполняет код хотя бы один раз, затем проверяет условие:
int number;
do {
Console.Write("Введите число больше 0: ");
number = int.Parse(Console.ReadLine());
} while (number <= 0);
Разбор:
do...whileгарантирует хотя бы одно выполнение тела цикла.- Сначала пользователь вводит значение, потом проверяется условие повторения.
int.Parse(...)преобразует строку в число.- Цикл продолжается, пока введено значение
<= 0.
Структура:
do {
//тело цикла
} while (условие)
Разбор:
- Это шаблон
do...while: "выполнить, затем проверить". - Отличие от
while— первая итерация всегда выполняется. - Удобно для меню, повторного ввода и сценариев с обязательным первым действием.
- После
while (условие)в C# должна стоять точка с запятой.
foreach
foreach, перебор коллекций, удобен для массивов, списков, словарей и других перечисляемых типов:
string[] names = { "Alice", "Bob", "Charlie" };
foreach (string name in names) {
Console.WriteLine(name);
}
Разбор:
string[]создаёт массив строк.foreachпроходит по каждому элементу коллекции без индекса.- На каждой итерации текущий элемент попадает в переменную
name. - Это безопасный и читаемый способ перебора, когда индекс не нужен.
Структура:
foreach(тип переменная in коллекция) {
//тело цикла
}
Разбор:
- Шаблон показывает общий синтаксис
foreach. коллекциядолжна быть перечисляемой (IEnumerable).переменнаяимеет тип элемента коллекции.- Подходит для массива, списка, словаря и других перечисляемых структур.
Управление циклом
Существуют специальные управляющие операторы - break, continue, goto.
Циклы прерываются через ключевое слово break, а продолжаются через continue (переход к следующей итерации). Пример:
for (int i = 0; i < 10; i++) {
if (i == 5) break; // остановится на 5
if (i % 2 == 0) continue; // пропустит чётные числа
Console.WriteLine(i);
}
Разбор:
- Цикл идёт по значениям
0..9. breakполностью завершает цикл приi == 5.continueпропускает текущую итерацию для чётных чисел.- В консоль выведутся только нечётные значения до пяти:
1и3.
В чем разница между "Break" и "Continue" в C#?
break: используется в циклах (for и т. д.) и операторах переключения, завершает итерацию/переключение и пропускает весь оставшийся код в цикле или блоке переключения.
continue: используется только в циклах, пропускает весь оставшийся код в цикле и начинает следующую итерацию с начала цикла.
И есть ещё goto - переход к метке. Он редко используется и может усложнить код. Чаще всего его заменяют нормальными управляющими конструкциями.