掌握 Dart 模式匹配:3. 解构模式的深入应用

429 阅读4分钟

Dart 3 引入了强大的 模式匹配(Pattern Matching) 特性,通过解构模式(Destructuring Patterns),开发者可以更简洁地处理复杂数据结构,如列表、映射、记录和对象。模式匹配不仅提升代码可读性,还在变量声明、switch 表达式和 if-case 等场景中大放异彩。本章深入探讨 列表模式映射模式记录模式对象模式,通过实战案例展示其应用,帮助你掌握 Dart 3 的现代编程范式。

3.1 列表模式 (List Pattern)

列表模式允许解构 List 数据结构,提取元素或嵌套内容,结合剩余元素模式(...)实现灵活匹配。

3.1.1 基本语法和解构元素

列表模式使用方括号 [],通过位置解构 List 元素。

void main() {
  const point = [1, 2];
  final [x, y] = point; // 解构为变量 x 和 y
  print('x: $x, y: $y'); // 输出: x: 1, y: 2
}
  • 语法[pattern1, pattern2] 匹配列表的对应位置。
  • 要求:列表长度必须匹配模式长度,否则抛出异常。

3.1.2 嵌套列表的解构

列表模式支持嵌套解构,处理多维列表。

void main() {
  const matrix = [[1, 2], [3, 4]];
  final [[a, b], [c, d]] = matrix;
  print('a: $a, b: $b, c: $c, d: $d'); // 输出: a: 1, b: 2, c: 3, d: 4
}
  • 场景:常用于解析 JSON 或多维数据。

3.1.3 ... 剩余元素模式(Rest Pattern)

... 捕获剩余元素,类似 JavaScript 的 Spread Operator。

void main() {
  const numbers = [1, 2, 3, 4, 5];
  final [first, second, ...rest] = numbers;
  print('first: $first, second: $second, rest: $rest'); // 输出: first: 1, second: 2, rest: [3, 4, 5]
}
  • 灵活性...rest 可出现在模式开头、中间或结尾。
  • 限制:一个模式中只能有一个 ...

3.1.4 在不同上下文中的应用

列表模式在多种场景中简化代码:

  • 变量声明
    const coords = [10, 20];
    var [x, y] = coords; // 解构赋值
    
  • switch 表达式
    String describePoint(List<int> point) => switch (point) {
          [0, 0] => 'Origin',
          [int x, 0] => 'On x-axis at $x',
          [0, int y] => 'On y-axis at $y',
          [int x, int y] => 'At ($x, $y)',
          _ => 'Unknown'
        };
    
    void main() {
      print(describePoint([0, 0])); // 输出: Origin
      print(describePoint([5, 0])); // 输出: On x-axis at 5
    }
    
  • if-case
    void checkPoint(List<int> point) {
      if (point case [int x, int y] when x > 0 && y > 0) {
        print('Positive quadrant: ($x, $y)');
      } else {
        print('Not in positive quadrant');
      }
    }
    
    void main() {
      checkPoint([1, 2]); // 输出: Positive quadrant: (1, 2)
      checkPoint([-1, 2]); // 输出: Not in positive quadrant
    }
    

生产建议:在 switch 中使用 when 子句添加条件,增强匹配逻辑;优先使用 if-case 代替嵌套 if

3.2 映射模式 (Map Pattern)

映射模式解构 Map 数据,匹配键值对,适合处理 JSON 或键值数据。

3.2.1 基本语法和解构键值对

映射模式使用大括号 {},指定键和值的模式。

void main() {
  const user = {'name': 'Alice', 'age': 30};
  final {'name': String name, 'age': int age} = user;
  print('Name: $name, Age: $age'); // 输出: Name: Alice, Age: 30
}
  • 语法{'key': pattern} 匹配指定键。
  • 类型检查:模式可指定类型(如 Stringint)。

3.2.2 解构特定键

可以只解构部分键,未匹配的键被忽略。

void main() {
  const user = {'name': 'Bob', 'age': 25, 'email': 'bob@example.com'};
  final {'name': name} = user;
  print('Name: $name'); // 输出: Name: Bob
}
  • 场景:从复杂 JSON 中提取特定字段。

3.2.3 ... 剩余元素模式(Rest Pattern)

... 捕获未匹配的键值对。

void main() {
  const user = {'name': 'Charlie', 'age': 35, 'email': 'charlie@example.com'};
  final {'name': name, ...rest} = user;
  print('Name: $name, Rest: $rest'); // 输出: Name: Charlie, Rest: {age: 35, email: charlie@example.com}
}

3.2.4 在不同上下文中的应用

映射模式在以下场景中简化逻辑:

  • 变量声明
    const config = {'host': 'localhost', 'port': 8080};
    var {'host': host, 'port': port} = config;
    
  • switch 表达式
    String parseUser(Map<String, dynamic> user) => switch (user) {
          {'name': 'Admin', 'role': String role} => 'Admin with role: $role',
          {'name': String name, 'age': >= 18} => 'Adult: $name',
          _ => 'Unknown user'
        };
    
    void main() {
      print(parseUser({'name': 'Admin', 'role': 'superuser'})); // 输出: Admin with role: superuser
      print(parseUser({'name': 'Alice', 'age': 20})); // 输出: Adult: Alice
    }
    
  • if-case
    void checkUser(Map<String, dynamic> user) {
      if (user case {'name': String name, 'age': >= 18}) {
        print('$name is an adult');
      } else {
        print('Not an adult or invalid user');
      }
    }
    
    void main() {
      checkUser({'name': 'Bob', 'age': 25}); // 输出: Bob is an adult
    }
    

生产建议:使用映射模式解析 API 响应,确保键存在(结合 when 子句校验)。

3.3 记录模式 (Record Pattern)

记录(Records)是 Dart 3 引入的不可变数据结构,记录模式用于解构记录。

3.3.1 记录(Records)作为数据类型简介

记录是轻量级、匿名的复合数据类型,支持位置字段和命名字段。

void main() {
  const point = (x: 1, y: 2); // 命名记录
  const pair = (3, 4); // 位置记录
  print(point); // 输出: (x: 1, y: 2)
  print(pair); // 输出: (3, 4)
}

3.3.2 位置记录和命名记录的解构

  • 位置记录
    void main() {
      const pair = (1, 2);
      final (a, b) = pair;
      print('a: $a, b: $b'); // 输出: a: 1, b: 2
    }
    
  • 命名记录
    void main() {
      const point = (x: 3, y: 4);
      final (x: x, y: y) = point;
      print('x: $x, y: $y'); // 输出: x: 3, y: 4
    }
    

3.3.3 混合解构(部分解构)

可以解构部分字段,忽略其他字段。

void main() {
  const data = (id: 1, name: 'Alice', age: 30);
  final (id: id, name: name) = data;
  print('ID: $id, Name: $name'); // 输出: ID: 1, Name: Alice
}

3.3.4 在不同上下文中的应用

记录模式在函数返回多值时特别有用:

  • 变量声明
    (int, String) getUser() => (1, 'Alice');
    final (id, name) = getUser();
    
  • switch 表达式
    String describeRecord((int, String) record) => switch (record) {
          (0, _) => 'Zero ID',
          (int id, String name) => 'ID: $id, Name: $name',
        };
    
    void main() {
      print(describeRecord((1, 'Bob'))); // 输出: ID: 1, Name: Bob
    }
    
  • if-case
    void checkRecord((int, String) record) {
      if (record case (int id, String name) when id > 0) {
        print('Valid user: $name');
      }
    }
    
    void main() {
      checkRecord((1, 'Charlie')); // 输出: Valid user: Charlie
    }
    

生产建议:记录适合函数返回多值(如 (status, data)),用记录模式解构提高代码清晰度。

3.4 对象模式 (Object Pattern)

对象模式解构类实例的属性或 getter,结合类型检查实现精确匹配。

3.4.1 基本语法和解构对象属性

对象模式匹配类的属性或 getter。

class User {
  final String name;
  final int age;

  User(this.name, this.age);
}

void main() {
  const user = User('Alice', 30);
  final User(name: name, age: age) = user;
  print('Name: $name, Age: $age'); // 输出: Name: Alice, Age: 30
}

3.4.2 getter 方法的匹配

可以匹配 getter 方法。

class User {
  final String _name;
  final int _age;

  User(this._name, this._age);

  String get name => _name;
  int get age => _age;
}

void main() {
  const user = User('Bob', 25);
  final User(name: name, age: age) = user;
  print('Name: $name, Age: $age'); // 输出: Name: Bob, Age: 25
}

3.4.3 级联模式(..)与模式匹配的结合

Dart 的级联运算符(..)可与对象模式结合,简化操作。

class User {
  String name;
  int age;

  User(this.name, this.age);
}

void main() {
  final user = User('Charlie', 35);
  if (user case User(name: var name, age: var age)) {
    user..name = 'Updated $name'..age = age + 1;
    print('Updated: ${user.name}, ${user.age}'); // 输出: Updated: Updated Charlie, 36
  }
}

3.4.4 在不同上下文中的应用

对象模式在复杂类型匹配中非常强大:

  • 变量声明
    final user = User('Dave', 40);
    var User(name: name) = user; // 只解构 name
    
  • switch 表达式
    String describeUser(User user) => switch (user) {
          User(name: 'Admin') => 'Administrator',
          User(age: >= 18) => 'Adult user',
          User(name: String name) => 'User: $name',
        };
    
    void main() {
      print(describeUser(User('Admin', 50))); // 输出: Administrator
      print(describeUser(User('Eve', 20))); // 输出: Adult user
    }
    
  • if-case
    void checkUser(User user) {
      if (user case User(age: >= 18)) {
        print('Adult user');
      } else {
        print('Not an adult');
      }
    }
    
    void main() {
      checkUser(User('Frank', 16)); // 输出: Not an adult
    }
    

生产建议:对象模式适合处理复杂类实例,结合 when 子句校验属性值。


小结

Dart 3 的解构模式(列表、映射、记录、对象)极大提升了代码的简洁性和表达力:

  • 列表模式:解构数组,结合 ... 捕获剩余元素,适合处理序列数据。
  • 映射模式:提取键值对,解析 JSON 或配置数据。
  • 记录模式:处理轻量级多值返回,简化函数返回值解构。
  • 对象模式:匹配类属性或 getter,适合复杂类型处理。

通过变量声明、switchif-case,这些模式在多种场景中简化逻辑。建议在生产环境中:

  • 使用类型注解(如 Stringint)增强模式安全性。
  • 结合 when 子句添加条件校验。
  • 优先使用记录模式处理多值返回。