Course program← C#: Базовый курс
1Знакомство с C# и .NET
2Данные и типы
3Ветвления и циклы
4Методы и ошибки
5Коллекции и строки
6Классы и объекты

Условия и логические операторы

This lesson is not translated yet – the Russian original is below.

Пока программа выполняет строки подряд, она умеет ровно одно – то, что записано. Ветвление даёт ей выбор: сделать так или иначе, в зависимости от данных.

if и else

int age = 20;

if (age >= 18)
{
    Console.WriteLine("Совершеннолетний");
}
else
{
    Console.WriteLine("Несовершеннолетний");
}

В скобках после if – условие, выражение типа bool. Не число, не строка, не объект: только истина или ложь.

В C и JavaScript условием может быть что угодно: ноль – ложь, непустая строка – истина. В C# так нельзя, и это к лучшему. Опечатка if (x = 5), годами кормившая отладчики в других языках, здесь просто не соберётся.

Когда веток больше двух, их выстраивают цепочкой:

if (score >= 90)
{
    Console.WriteLine("отлично");
}
else if (score >= 75)
{
    Console.WriteLine("хорошо");
}
else if (score >= 60)
{
    Console.WriteLine("удовлетворительно");
}
else
{
    Console.WriteLine("неудовлетворительно");
}

Проверки идут сверху вниз, и первая подошедшая выигрывает – остальные даже не вычисляются. Поэтому во второй ветке не нужно писать score >= 75 && score < 90: если бы значение было больше 90, до сюда бы не дошли.

Операторы сравнения

Оператор Значение
== равно
!= не равно
> < больше, меньше
>= <= больше или равно, меньше или равно

Двойное равенство – это сравнение, одинарное – присваивание. Перепутать в условии не выйдет: компилятор сообщит, что не может преобразовать int в bool.

Для дробных чисел == ненадёжен – см. урок про точность. Для строк работает нормально: == сравнивает содержимое, а не адреса, потому что для string это поведение переопределено специально.

Логические операторы

Оператор Название Истинно, когда
&& и истинны обе части
|| или истинна хотя бы одна
! не часть ложна
if (age >= 18 && hasPassport)
{
    Console.WriteLine("Можно оформлять");
}

if (day == 6 || day == 7)
{
    Console.WriteLine("Выходной");
}

if (!isBlocked)
{
    Console.WriteLine("Доступ открыт");
}

Отрицание читается тяжелее, чем утверждение, поэтому if (!isBlocked) лучше, чем if (isBlocked == false), но ещё лучше – назвать переменную так, чтобы отрицание не требовалось: isAllowed.

Короткое замыкание

&& и || вычисляют правую часть только при необходимости:

  • в a && b: если a ложно, результат уже ложь – b не вычисляется;
  • в a || b: если a истинно, результат уже истина – b не вычисляется.

Это не оптимизация, а рабочий приём. На нём строится защита от null:

if (text != null && text.Length > 0)
{
    Console.WriteLine(text);
}

Поменяйте проверки местами – получите NullReferenceException: text.Length вычислится раньше, чем проверка на null.

Есть ещё & и | – они всегда вычисляют обе части. В условиях они почти не нужны, зато нужны при работе с битами. Если увидели одиночный знак в if, чаще всего это опечатка.

Фигурные скобки

Для одной инструкции скобки формально не обязательны:

if (age >= 18)
    Console.WriteLine("Совершеннолетний");

Так писать не стоит. Когда через месяц в ветку добавляют вторую строку, отступ создаёт иллюзию, что она тоже внутри условия, – а она снаружи:

if (age >= 18)
    Console.WriteLine("Совершеннолетний");
    Console.WriteLine("Доступ открыт");    // выполнится всегда

Ошибка невидима глазами и стоила индустрии дорого: в 2014 году именно такая пара строк без скобок в коде Apple обернулась уязвимостью в проверке SSL-сертификатов.

Ставьте скобки всегда, даже ради одной строки. Это соглашение по умолчанию почти во всех командах, и спорить с ним себе дороже.

Тернарный оператор

Когда обе ветки просто дают значение, тремя строчками писать не обязательно:

string status = age >= 18 ? "взрослый" : "ребёнок";

Читается как вопрос: условие, потом ?, потом ответ «да», потом :, потом ответ «нет».

Тернарный оператор хорош для короткого выбора значения и плох для всего остального. Вложенные тернарники превращаются в ребус:

string result = a > b ? a > c ? "a" : "c" : b > c ? "b" : "c";    // так не надо

Условия внутри интерполяции

Мелочь, которая экономит промежуточную переменную:

Console.WriteLine($"Товаров: {count} {(count == 1 ? "штука" : "штук")}");

Внутренние скобки обязательны: без них двоеточие тернарного оператора будет принято за начало формата вывода, и компилятор запутается.