Функция Patterns доступна только начиная с версии Dart ≥ 3.0
I. Основные возможности Pattern
Pattern — отдельная синтаксическая категория Dart (равноправна с операторами и выражениями), которая описывает структуру значений. У нее две ключевые возможности, используемые одновременно: сопоставление (Match) и деструктуризация (Destructure).
- Сопоставление Match: проверка соответствия значения ожидаемой структуре
- Совпадает ли с заданной константой
- Принадлежит ли определенному типу
- Имеет ли нужную структуру (List/Map/объект/запись Record)
- Удовлетворяет ли дополнительному условию
- Деструктуризация Destructure: при успешном сопоставлении быстро извлекает поля и элементы значения, привязывая их к переменным
1. Пример сопоставления по константе
void main() {
int number = 1;
switch (number) {
case 1:
print('one'); // Сопоставление по константе: number == 1
default:
print('другое число');
}
}Code language: Dart (dart)
Результат выполнения
one
2. Вложенные подшаблоны для сопоставления
Шаблоны поддерживают вложение подшаблонов для пошаговой проверки внутренней структуры
void main() {
const a = 'a';
const b = 'b';
var obj = [ 'a', 'b'];
switch (obj) {
case [a, b]:
print('$a, $b');
default:
print('совпадений нет');
}
}Code language: Dart (dart)
Результат выполнения
a, b
3. Простой пример деструктуризации
var numList = [1, 2, 3];
// Деструктуризация списка через List-шаблон, привязка к новым переменным
var [x, y, z] = numList;
print(x + y + z); // 6 x=1 y=2 z=3Code language: PHP (php)
4. Вложенное сопоставление + деструктуризация
void main() {
var list = ['b', 99];
switch (list) {
// Логический ИЛИ-шаблон: первый элемент a или b, второй сохраняется в переменную c
case ['a' || 'b', var c]:
print(c); // 99
}
}Code language: Dart (dart)
II. Частые сценарии использования Pattern
1. Объявление локальных переменных
Используется для деструктурирующего присваивания, позволяет создать сразу несколько переменных
// Деструктуризация вложенного шаблона Record + List
var (str, [n1, n2]) = ('text', [10, 20]);
print(str); //text
print(n1 + n2); //30Code language: Dart (dart)
2. Переприсвоение существующих переменных (обмен значений без временной переменной)
var (a, b) = ("лево", "право");
(b, a) = (a, b); // Обмен значений двух переменных
print('$a $b'); // право левоCode language: Dart (dart)
3. Ветвления switch / if-case (самое распространенное применение, защитное условие when)
В каждом case записываются опровержимые шаблоны: при отсутствии совпадения выполняется следующий case
Базовый вариант case для разных типов
void testSwitch(dynamic obj) {
switch (obj) {
case 1:
print('число 1');
case >= 10 && <= 20:
print('число из диапазона 10–20');
case (var a, var b):
print('запись Record: $a,$b');
default:
print('подходящих вариантов нет');
}
}Code language: Dart (dart)
Логический ИЛИ-шаблон: общий код для нескольких веток
enum Color { red, yellow, blue, green }
void main() {
Color color = Color.red;
bool isPrimary = switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false,
};
print(isPrimary); // true
}
Code language: PHP (php)
Защитное условие when (дополнительная фильтрация после сопоставления по шаблону)
sealed class Shape {}
class Square { final double size; Square(this.size); }
class Circle { final double size; Circle(this.size); }
void checkShape(Shape shape) {
switch (shape) {
// Одновременно сопоставляем Square и Circle, добавляем условие size>0
case Square(size: var s) || Circle(size: var s) when s > 0:
print('корректная симметричная фигура');
default:
print('некорректная фигура');
}
}Code language: Dart (dart)
Разница между when и обычным if
void compareWhen((int, int) pair) {
switch (pair) {
case (int a, int b):
if (a > b) print('первое число больше');
// Если условие if не выполняется — весь switch прерывается, последующие case не проверяются
case (int a, int b) when a > b:
print('【защита when】первое число больше');
// Если условие when не выполняется — пропускаем только этот case и переходим к следующему
case (int a, int b):
print('первое число меньше или равно второму');
}
}
void main() {
print("===== Тест (5, 2) a > b =====");
compareWhen((5, 2));
print("\n===== Тест (2, 5) a < b =====");
compareWhen((2, 5));
}Code language: Dart (dart)
4. Циклы for /for-in (удобная деструктуризация MapEntry)
void main() {
Map<String, int> hist = {'a': 23, 'b': 100};
// Полная запись
for (var MapEntry(key: key, value: count) in hist.entries) {
print('$key : $count');
}
// Сокращенный синтаксис: key:key заменяется на :key
for (var MapEntry(:key, value: count) in hist.entries) {
print('$key : $count');
}
}Code language: Dart (dart)
Результат выполнения
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
a : 23
b : 100
a : 23
b : 100
Code language: CSS (css)
III. Практические сценарии применения Pattern в бизнесе
1: Несколько возвращаемых значений функции (деструктуризация Record)
Традиционный вариант (громоздкий код)
(String, int) userInfo() => ("Lily", 13);
void main() {
var info = userInfo();
var name = info.$1;
var age = info.$2;
}
Code language: Dart (dart)
Упрощенная запись через шаблон
var (name, age) = userInfo();
// Деструктуризация именованного Record
final (:name, :age) = getData(); // Возвращает (name: "Doug", age:25)Code language: Dart (dart)
Конкретный пример
(String, int) userInfo() => ("Lily", 18);
({String name, int age}) getData() => (name: "Doug", age: 25);
void main() {
{
var (name, age) = userInfo();
print("Имя: $name, возраст: $age");
}
// Внутренние переменные name/age не конфликтуют с внешними
{
final (:name, :age) = getData();
print("Имя: $name, возраст: $age");
}
}Code language: Dart (dart)
2: Деструктуризация экземпляров классов (объектный шаблон)
class Foo {
final String one;
final int two;
Foo({required this.one, required this.two});
}
void main() {
final Foo myFoo = Foo(one: "test", two: 66);
var Foo(:one, :two) = myFoo;
print('$one $two'); //test 66
}Code language: Dart (dart)
3: Алгебраические типы данных (запечатанные классы + вычисления через switch)
Единая обработка связанных подклассов без разрозненных методов экземпляра
import 'dart:math';
sealed class Shape {}
class Square implements Shape { final double length; Square(this.length); }
class Circle implements Shape { final double radius; Circle(this.radius); }
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => pi * r * r,
};
Code language: Dart (dart)
Полный пример реализации
import 'dart:math';
// Запечатанный родительский класс, подклассы можно определять только в этом файле
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 в переменную l
Square(length: var l) => l * l,
// Совпадение с Circle, извлечение radius в переменную r
Circle(radius: var r) => pi * r * r,
};
void main() {
// Создаем экземпляры фигур
Shape square = Square(5);
Shape circle = Circle(3);
// Рассчитываем площади
double squareArea = calculateArea(square);
double circleArea = calculateArea(circle);
print("Площадь квадрата: $squareArea"); // 25.0
print("Площадь круга: ${circleArea.toStringAsFixed(2)}"); // 28.27
}Code language: Dart (dart)
Результат выполнения
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Площадь квадрата: 25.0
Площадь круга: 28.27Code language: CSS (css)
4: Проверка и деструктуризация JSON (значительно упрощает вложенные условия)
(Без шаблонов) Многоуровневые проверки типов делают код громоздким
void oldCheck(Map data) {
if (data is Map<String, Object?> && data.containsKey('user')) {
var user = data['user'];
if (user is List && user.length == 2) {
String name = user[0] as String;
int age = user[1] as int;
print('$name $age');
}
}
}Code language: Dart (dart)
Шаблон if-case: одна строка для проверки структуры, типа и привязки переменных
void newCheck(Map data) {
if (data case {'user': [String name, int age]}) {
print('Пользователь $name, возраст $age');
}
}Code language: Dart (dart)
Этот шаблон одновременно проверяет все пункты:
- data является Map и не равен null
- Присутствует ключ user
- Значение по ключу user — список из двух элементов
- Первый элемент строка String, второй целое число int
- Автоматическая привязка локальных переменных name и age
Пример реального использования
void main() {
// 1. Корректная JSON структура: совпадение найдено
final validJson = {
'user': ['Lily', 13]
};
newCheck(validJson); // Вывод: Пользователь Lily, возраст 13
// 2. Несоответствие типов в списке: возраст строка, совпадения нет
final wrongTypeJson = {
'user': ['Tom', '16']
};
newCheck(wrongTypeJson); // Вывода нет
// 3. Список user неполный: нет возраста, совпадения нет
final shortJson = {
'user': ['Jack']
};
newCheck(shortJson); // Вывода нет
// 4. Ключа user нет в данных: совпадения нет
final noUserJson = {
'info': ['Bob', 20]
};
newCheck(noUserJson); // Вывода нет
}
// Одношаговая проверка структуры/типа и деструктуризация через шаблон Map+List if-case
void newCheck(Map data) {
if (data case {'user': [String name, int age]}) {
print('Пользователь $name, возраст $age');
} else {
print('Неверный формат данных');
}
}Code language: PHP (php)
Сопоставление по шаблонам Pattern