Ветвящийся поток управления

I. Базовые ветвления if / if-else

  1. Выражение в скобках if обязательно должно быть логическим; неявное преобразование к логическому типу запрещено.
  2. Можно последовательно записывать несколько блоков else if, в конце опционально добавить else.
  3. При одной строке кода фигурные скобки можно опустить, но рекомендуется использовать их всегда для улучшения читаемости.
void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Взять плащ от дождя");
  } else if (isSnowing) {
    print("Надеть толстую куртку");
  } else {
    print("Ехать на кабриолете");
  }
}Code language: Dart (dart)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Надеть толстую курткуCode language: CSS (css)

Также можно использовать только if без остальных веток

void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Взять плащ от дождя");
  }
}Code language: Dart (dart)

Или просто конструкцию if else

void main() {
  bool isRaining = false;
  bool isSnowing = true;

  if (isRaining) {
    print("Взять плащ от дождя");
  } else {
    print("Ехать на кабриолете");
  }
}Code language: Dart (dart)

II. Ветвление по шаблону if-case (Dart 3.0+)

  1. Если шаблон совпадает, переменные деструктуризируются и привязываются к текущей области видимости.
  2. При несовпадении управление переходит в else. Можно добавить охранник when для дополнительных логических проверок.
  3. Предназначено для сопоставления одного значения с одним шаблоном; для множества шаблонов используйте switch.
void main() {
  final pair = [100, 220];
  // Совпадает со списком из двух целых чисел
  if (pair case [int x, int y]) {
    print("Координаты: $x,$y");
  } else {
    throw FormatException("Некорректные координаты");
  }
}Code language: Dart (dart)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Координаты: 10,20Code language: CSS (css)

2: if-case с охранником when

void main() {
  final data = [8, 9];
  if (data case [int a, int b] when a > 5 && b > 5) {
    print("Оба числа больше 5");
  } else {
    print("Числа не удовлетворяют условию");
  }
}Code language: Dart (dart)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Оба числа больше 5Code language: CSS (css)

III. Оператор switch

Традиционный switch как оператор, не возвращающий значение

  1. В case допустимы любые шаблоны: строки, константы, логическое ИЛИ, реляционные шаблоны
  2. Непустые case автоматически завершаются break, ручной break не нужен; пустые case вызывают неявное проваливание (fallthrough).
  3. Два способа принудительного проваливания: continue метка / последовательные пустые case
  4. Если ни один case не подошёл, выполняется default; поддерживается работа с охранниками when
1: Простое сопоставление строк
void main() {
  String command = "OPEN";
  switch (command) {
    case "CLOSED":
      print("Выполнить логику закрытия");
    case "PENDING":
      print("Выполнить логику ожидания");
    case "OPEN":
      print("Выполнить логику открытия");
    default:
      print("Неизвестная команда");
  }
}
Code language: Dart (dart)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Выполнить логику открытияCode language: CSS (css)

2: Проваливание через пустой case + переход по continue с меткой

String command = "OPEN";
switch (command) {
  case "OPEN":
    print("Операция открытия"); // ① Совпадает с OPEN, вывод текста
    continue newCase; // ② Прямой переход на метку newCase, возврата нет

  case "DENIED": 
  case "CLOSED":
    print("Операция закрытия");

  newCase: // ③ Цель перехода
  case "PENDING":
    print("Общая логика для ожидающих процессов"); // ④ Проверка command == "PENDING" пропускается, код выполняется сразу
}Code language: Dart (dart)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Операция открытия
Общая логика для ожидающих процессовCode language: CSS (css)

В этом примере continue newCase; переходит на метку newCase, но следующий case «PENDING» не подходит — почему всё равно запускается print("Общая логика для ожидающих процессов");?

Этот вопрос задают многие читатели

continue метка; не повторно проверяет сравниваемое значение, а прямо переходит к началу помеченного case-блока, пропускает все проверки условий и немедленно исполняет код внутри блока.

3: switch с охранником when

void main() {
  int score = 88;
  switch (score) {
    case int s when s >= 90:
      print("Отлично");
    case int s when s >= 60:
      print("Зачёт");
    default:
      print("Незачёт");
  }
}
Code language: Dart (dart)

Вывод: Зачёт

Сначала проверяется тип: значение должно быть целым числом. При совпадении значение записывается в переменную s, затем охранник when выполняет вторую проверку; код case работает только при соблюдении условия when. Две записи int s имеют независимые области видимости и не мешают друг другу.

Каждая конструкция case int s — это независимая привязка шаблона, область видимости переменной ограничена только своим case-блоком.

Можно использовать разные имена переменных в двух case, поведение программы не изменится.

switch (score) {
  case int a when a >= 90:
    print("Отлично");
  case int b when b >= 60:
    print("Зачёт");
  default:
    print("Незачёт");
}Code language: Dart (dart)

IV. Выражение switch

Синтаксис доступен с Dart 3.0, возвращает значение, часто используется для присваивания переменным

Отличия от оператора switch

  1. Отсутствует ключевое слово case, символ => разделяет шаблон и возвращаемое выражение
  2. Каждая ветка обязана возвращать выражение, ветви разделяются запятыми
  3. Нет неявного проваливания, все ветви независимы. При отсутствии совпадений используйте подчёркивание _ вместо default
  4. Допустимо прямое использование в присваиваниях, вызовах print, return и любых контекстах выражений

1: Простое сопоставление символьных операторов (шаблон логического ИЛИ)

void main() {
  const slash = 47, star = 42, comma = 44;
  int charCode = 42;

  final token = switch (charCode) {
    slash || star => "Оператор",
    comma => "Знак препинания",
    _ => "Недопустимый символ"
  };
  print(token);
}
Code language: Dart (dart)

Результат выполнения

Оператор

В примере заданы константы с ASCII-кодами трёх символов, переменная charCode классифицирует символ как оператор, знак препинания или недопустимый знак.

2: Выражение switch с охранником when

void main() {
  int age = 22;
  String level = switch (age) {
    int a when a < 18 => "Несовершеннолетний",
    int a when a < 30 => "Молодой взрослый",
    _ => "Взрослый"
  };
  print(level);
}Code language: Dart (dart)

Результат выполнения

Молодой взрослый

3: Прямое использование выражения switch в return функции

void main() {
  print(getDesc(3));
}

String getDesc(int num) => switch (num) {
  1 => "Один",
  2 => "Два",
  _ => "Другое число"
};
Code language: Dart (dart)

Результат выполнения

Другое число

Примечание: здесь switch является выражением, getDesc — стрелочная функция. Стрелочные функции — уникальный синтаксис Dart, отсутствующий в других языках семейства C.

V. Проверка полноты покрытия (Exhaustiveness Checking)

При отсутствии case для возможных входных значений возникает ошибка компиляции, компилятор требует добавить недостающие ветви. Запечатанные классы sealed и перечисления enum поддерживают полную проверку из коробки без использования _ или default.

1: Ошибка неполного покрытия для nullable логического типа (закомментируйте/раскомментируйте для просмотра ошибки)

void main() {
  bool? nullableBool = null;
  // Раскомментирование кода ниже вызовет ошибку компиляции: нет case для null и отсутствует запасной вариант _/default
  /*
  switch (nullableBool) {
    case true:
      print("Истина");
    case false:
      print("Ложь");
  }
  */
  // Исправление: добавить case null или запасной вариант через _
  switch (nullableBool) {
    case true:
      print("Истина");
    case false:
      print("Ложь");
    case null:
      print("Значение null");
  }
}Code language: PHP (php)

Результат выполнения

Значение nullCode language: JavaScript (javascript)

2: Запечатанный класс sealed

Алгебраический тип данных, принудительно требующий полного сопоставления шаблонов

import 'dart:math' as math;

// sealed-класс: подклассы можно определять только в текущем файле
sealed class Shape {}

// Подкласс Квадрат: длина стороны
class Square implements Shape {
  final double length;
  Square(this.length);
}

//Подкласс Круг: радиус
class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// Стрелочная функция, вычисляющая площадь через сопоставление switch по полученному объекту подкласса
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(radius: var r) => math.pi * r * r
  // Добавление нового подкласса Shape вызовет ошибку компиляции из-за отсутствия соответствующего case
};

void main() {
  final Shape circle = Circle(5);
  print(calculateArea(circle));
  final Shape square = Square(4);
  print(calculateArea(square));
}
Code language: Dart (dart)

Результат выполнения

78.53981633974483
16.0Code language: CSS (css)

3: Полное сопоставление с enum

Запасное подчёркивание _ не требуется

void main() {
  final Animal animal = Animal.cat;
  String name = switch (animal) {
    Animal.dog => "Собака",
    Animal.cat => "Кот"
  };
  print(name);
}

enum Animal { dog, cat }
Code language: Dart (dart)

Вывод: Кот

Перечисления enum имеют фиксированный конечный набор значений, поэтому запасная ветвь не нужна.

VI. Охранники when

Полностью поддерживаются в if-case, операторах switch и выражениях switch

Роль when: дополнительная логическая проверка после успешного сопоставления шаблона. Если охранник возвращает false, переход на следующую ветвь без выхода из switch.

1. if-case + when

void main() {
  List<int> arr = [10, 20];
  if (arr case [int x, int y] when x + y > 20) {
    print("Сумма двух чисел больше 20");
  }
}Code language: Dart (dart)
Сумма двух чисел больше 20

Сначала проверяется, является ли переменная списком из двух целых чисел, при совпадении when проверяет, превышает ли их сумма 20.

2. Оператор switch + when

void main() {
  int num = 15;
  switch(num) {
    case int n when n % 2 == 0:
      print("Чётное число");
    case int n when n % 2 != 0:
      print("Нечётное число");
  }
}
Code language: PHP (php)

Вывод: Нечётное число

Сначала подтверждается целочисленный тип, затем проверяется условие внутри when.

3. Выражение switch + when

void main() {
  int num = 15;
  String res = switch(num) {
    int n when n % 2 == 0 => "Чётное число",
    _ => "Нечётное число"
  };
  print(res);
}
Code language: PHP (php)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *