Dart 3.0模式匹配。一文秒懂模式匹配以及在Switch中的应用

4 阅读3分钟

Dart 3.0 引入的模式匹配(Pattern Matching)  是一套强大的语法特性,让你能够按形状解构数据,并根据值的结构或内容执行不同逻辑。它不只是 switch 的升级,而是贯穿变量声明、赋值、控制流的统一解构与匹配机制。

下面从变量声明与解构开始,逐步深入到 switch、if-case 等场景,全面展示模式匹配的用法。


一、核心概念

  • 模式(Pattern) :描述对数据结构的期望形状,例如“是一个大于 0 的整数”、“是一个有两个元素的列表”、“是一个带 name 字段的对象”等。
  • 匹配(Matching) :检查一个值是否符合模式,若符合,通常还会将值中的子部分解构并绑定到变量
  • 解构(Destructuring) :将复合值(列表、Map、对象、记录)拆开,直接提取内部值赋给变量。

二、变量声明与解构:模式匹配的基石

在变量声明或赋值语句中直接使用模式,可以一步完成“检查形状 + 提取数据”

2.1 常量模式与变量模式

dart

// 常量模式:直接匹配字面量(通常与 switch 联用)
// 变量模式:匹配任何值并绑定到新变量
var a = 123;      // 普通变量声明,不是模式
final b = 'hi';

// 在解构语境中,变量模式才体现“模式”角色

2.2 列表解构

dart

// 解构固定长度列表
var [x, y] = [1, 2];
print(x); // 1

// 使用 ... 收集剩余元素
var [first, ...rest] = [10, 20, 30, 40];
print(rest); // [20, 30, 40]

// 忽略某些元素(通配符 _)
var [_, second, _] = ['a', 'b', 'c'];
print(second); // b

// 嵌套解构
var [int a, [int b, int c]] = [1, [2, 3]];

2.3 Map 解构

dart

// 按键提取,键必须是编译时常量
var {'name': String name, 'age': int age} = {'name': 'Alice', 'age': 30};
print(name); // Alice

// 可以只提取需要的键,并重命名变量
var {'id': id, 'score': final score} = {'id': 101, 'score': 95.5, 'grade': 'A'};
print(id); // 101

2.4 记录解构

Dart 3.0 同时引入了记录(Record),模式匹配天然支持解构记录。

dart

// 位置字段记录
var (a, b) = (1, 2);
print(a); // 1

// 命名字段记录
var (x: xVal, y: yVal) = (x: 42, y: 100);
print(xVal); // 42

// 混合
var (name, age: years) = ('Bob', age: 25);

2.5 对象解构

对象解构语法:var 对象(命名字段1 : 变量1 , 命名字段2 : 变量2, ...) = 对象。

get方法也能被解构

dart

class Point {
  final int x, y;
  Point(this.x, this.y);
  int get sum => x+y;
}

final Point(x: px, y: py,sum: sum) = Point(3, 4);
print(px); // 3
print(sum); // 7

三、switch 与 if-case:基于模式的分支控制

模式匹配最亮眼的应用场景是增强的 switch,它现在可以作为表达式返回值,并且支持所有模式类型。

3.1 switch 表达式

dart

String describe(dynamic value) => switch (value) {
  // 常量模式
  0 => 'Zero',
  
  // 关系模式
  >= 1 && <= 10 => 'Small positive',
  
  // 类型模式 + 变量绑定
  String s => 'String of length ${s.length}',
  
  // 列表模式 + 解构
  [int a, int b] => 'List with two ints: $a, $b',
  
  // 记录模式 + 解构
  (x: var a, y: var b) => 'Record with x=$a, y=$b',
  
  // 通配符(默认)
  _ => 'Something else',
};
  • 穷举检查:如果 switch 表达式的值类型是有限集(如枚举、密封类),编译器会要求覆盖所有可能情况,否则报错。
  • 无需 break:每个 case 独立,不会贯穿。

3.2 switch 语句

传统的 switch 语句也获得了模式匹配能力,并且支持多条语句:

dart

switch (shape) {
  case Circle(radius: var r) when r > 10:
    print('Big circle');
  case Circle(radius: var r):
    print('Small circle');
  case Square(size: var s):
    print('Square of size $s');
}

when 子句可以添加额外条件。

3.3 if-case 语句

当你只需要检查一种特定模式时,if-case 更简洁:

dart

if (json case {'user': String name, 'permissions': ['read', ...]}) {
  print('User $name has read permission');
}

这相当于“如果值匹配这个模式,则解构并执行”,是 switch 的单分支特化。


四、常用模式类型一览表

模式类型语法示例说明
常量模式123'hello'truenull匹配具体字面量
变量模式var namefinal age匹配任何值并绑定到新变量
通配符模式_匹配任何值,不绑定
关系模式>= 0== 42用关系运算符匹配
逻辑与/或>=0 && <=10、`'a''b'`组合多个子模式
类型模式int xString s匹配特定类型并绑定变量
列表模式[a, b][first, ...rest]匹配列表结构并解构元素
Map 模式{'key': var v}匹配 Map 并提取特定键的值
记录模式(a, b)(x: var a, y: var b)匹配记录并按位置/字段名解构
对象模式Point(x: var x, y: var y)匹配对象并调用 getter 解构
括号模式(var x)用于控制优先级

五、模式匹配为何重要?

  1. 消除样板代码
    不再需要写 if (value is List) ... as List 再手动索引取值,模式匹配将“类型检查 + 类型转换 + 解构”合成一步。

  2. 提升可读性与意图表达
    代码直接反映数据的预期形状,逻辑扁平化。

  3. 编译时安全

    • switch 表达式的穷举性强制处理所有分支。
    • 配合密封类(sealed class) ,可以构建代数数据类型(ADT),模式匹配保证穷举。
  4. 统一解构语法
    以前解构列表、Map、对象各有一套零散方法,现在全部用统一、声明式的模式语法完成。


六、综合实例:处理 JSON

dart

void parseJson(Map<String, dynamic> json) {
  switch (json) {
    case {'type': 'user', 'name': String name, 'age': int age}:
      print('User $name is $age years old');
    case {'type': 'product', 'id': int id, 'price': num price}:
      print('Product $id costs $$price');
    case {'type': 'order', 'items': List items, 'total': num total}:
      print('Order has ${items.length} items, total $$total');
    case {'type': 'unknown'}:
      print('Unknown type');
    default:
      print('Invalid format');
  }
}

七、注意事项

  • 模式匹配在 Dart 3.0 默认启用,无需修改 analysis_options.yaml
  • 变量模式中可以使用 var 或 final,类型可省略(编译器自动推断)。
  • 对象模式要求类有对应的 getter,且构造函数的参数名与 getter 名一致(可自定义,用 new 前缀)。
  • switch 表达式必须返回一个值,所有分支必须能计算出值,且类型一致。

总结

Dart 3.0 的模式匹配将解构分支控制统一在简洁的语法之下,极大提升了处理嵌套数据结构的表达能力。从变量声明到 switch 表达式,它让代码更安全、更直观,是 Dart 语言向现代化、函数式方向迈出的关键一步。掌握模式匹配,就能用更少的代码处理更复杂的逻辑,同时获得编译器的强力支持。