dart-use-pattern-matching

dart-use-pattern-matching

热门

在合适场景下使用 switch 表达式与模式匹配(pattern matching)。

2792Star
164Fork
更新于 2026/8/5
SKILL.md
只读
名称
dart-use-pattern-matching
描述

在合适场景下使用 switch 表达式与模式匹配(pattern matching)。

Dart 模式匹配实现指南

目录

模式选择策略

根据数据结构与目标需求,灵活选择具体的模式类型。请遵循以下条件准则:

  • 校验并提取反序列化数据(如 JSON): 使用 Map 和 List 模式,在检查结构的同时完成键值解构。
  • 处理多返回值: 使用 Record 模式将字段直接解构到局部变量中。
  • 执行特定类型的行为(代数数据类型 ADT): 使用 Object 模式配合 sealed 类,确保分支穷尽性(exhaustiveness)。
  • 匹配数值范围或条件: 使用关系运算符(>=<=)与逻辑与(&&)模式。
  • 多个 case 共享逻辑: 使用逻辑或(||)模式共享同一个 case 主体或 Guard 条件子句(guard clause)。
  • 忽略特定值: 使用通配符模式(_)或集合中的 Rest 元素(...)忽略不关心的项。

Switch 语句 vs. 表达式

根据执行上下文选择合适的 switch 结构:

  • 需要计算或返回值时: 使用 switch 表达式
    • 语法:switch (value) { pattern => expression, }
    • 规则:每个 case 必须是单条表达式。无隐式贯穿(fallthrough)。必须具备穷尽性。
  • 需要执行语句或副作用时: 使用 switch 语句
    • 语法:switch (value) { case pattern: statements; }
    • 规则:空 case 会贯穿到下一个 case;非空 case 会隐式 break(无需显式写 break 关键字)。

核心模式实现

按照以下语法与规则实现各种模式:

  • 逻辑或(||): pattern1 || pattern2。两个分支定义的变量集合必须完全一致。
  • 逻辑与(&&): pattern1 && pattern2。两个分支不能定义重叠的变量。
  • 关系模式(Relational): ==!=<><=>= 后接常量表达式。
  • 类型转换(as): pattern as Type。如果值与类型不匹配则抛出异常。用于解构过程中强制断言类型。
  • Null 校验(?): pattern?。如果值为 null 则匹配失败;匹配成功时将变量绑定为非空基类型。
  • Null 断言(!): pattern!。如果值为 null 则直接抛出异常。
  • 变量模式(Variable): var nameType name。将匹配到的值绑定到新的局部变量。
  • 通配符(_): 匹配任意值并直接丢弃。
  • List 模式: [pattern1, pattern2]。精确匹配对应长度的列表,除非使用了 Rest 元素(......var rest)。
  • Map 模式: {"key": pattern}。匹配包含指定键的 Map,忽略未显式列出的键。
  • Record 模式: (pattern1, named: pattern2)。精确匹配对应形状的 Record。可使用 :var name 简写来推导 getter 名称。
  • Object 模式: ClassName(field: pattern)。匹配 ClassName 的实例。可使用 :var field 简写来推导 getter 名称。

工作流

任务进度:实现模式匹配

在实现复杂的模式匹配逻辑时,复制此检查清单以追踪进度:

  • [ ] 确认待评估的数据结构(JSON、Record、Class、Enum)。
  • [ ] 选择合适的 switch 结构(计算值用表达式,执行副作用用语句)。
  • [ ] 确定所需的模式(Object、Map、List、Record)。
  • [ ] 使用变量模式提取所需数据(var x:var y)。
  • [ ] 对无法通过单纯模式表达的逻辑添加 Guard 条件子句(when condition)。
  • [ ] 使用通配符(_)或 default 分支处理未匹配的情况(若未声明 sealed 类)。
  • [ ] 运行穷尽性校验工具。

反馈循环:穷尽性检查(Exhaustiveness Checking)

当对 sealed 类或枚举使用 switch 时,必须确保覆盖了所有子类型。

  1. 运行校验器: 执行 dart analyze
  2. 排查错误: 查找形如 "The type 'X' is not exhaustively matched by the switch cases" 的错误。
  3. 修复方案: 补齐缺失子类型的 Object 模式,或者在允许默认兜底时添加通配符(_)分支。

代码示例

JSON 校验与解构

使用 Map 和 List 模式一步到位完成结构校验与数据提取。

输入:

var data = {
  'user': ['Lily', 13],
};

实现:

if (data case {'user': [String name, int age]}) {
  print('User $name is $age years old.');
} else {
  print('Invalid JSON structure.');
}

代数数据类型(Sealed 类)

配合 switch 表达式与 Object 模式,对类型族进行穷尽性处理。

实现:

sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// 得益于 `sealed` 修饰符,switch 表达式可保证穷尽性。
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(:var radius)   => math.pi * radius * radius,
};

变量互换与解构

使用变量赋值模式完成变量互换,或在不引入临时变量的情况下直接解构 Record 字段。

实现:

var (a, b) = ('left', 'right');
(b, a) = (a, b); // 交换变量值

// 解构函数返回值
var (name, age) = getUserInfo();

Guard 条件子句与逻辑或

在模式匹配成功的基础上,使用 when 执行任意自定义条件判断。

实现:

switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('Valid symmetric shape with size $s');
  case Square() || Circle():
    print('Invalid or empty shape');
  default:
    print('Unknown shape');
}