在上一章中,我们介绍了 Dart 模式匹配的宏观概念及其在简化代码中的重要性。本章将深入探讨模式匹配的基石——模式(Patterns) 。通过理解这些基础模式,你将能够灵活运用模式匹配进行数据解构和逻辑处理。本章提供简洁、实用的代码示例,帮助你快速上手。
2.1 模式(Patterns)的基本构成
在 Dart 中,模式是描述值“形状”的语法结构,用于匹配和解构数据。它可以是简单的字面量,也可以是复杂的嵌套结构。例如:
-
42
:一个常量模式。
-
var x
:一个变量模式。
-
[a, b]
:一个列表模式,包含两个变量模式。
-
{'name': n, 'age': a}
:一个映射模式,包含两个变量模式。
-
Point(x: x, y: y)
:一个对象模式,解构对象的属性。
模式适用于以下场景:
-
变量声明:
var (x, y) = point;
-
赋值表达式:
(x, y) = (y, x);
-
switch
语句或表达式:switch (value) { case int i: ... }
-
if-case
语句:if (data case [int a, int b]) { ... }
-
for-in
循环:for (var (key, value) in map.entries) { ... }
匹配与解构:
-
匹配:检查值是否符合模式的结构和内容。
-
解构:匹配成功后,提取值并绑定到变量。
示例:
Dart
void main() {
// 匹配和解构坐标记录
var point = (x: 10, y: 20);
// 在 if-case 中引入新变量,需要使用 `var` 或明确类型
if (point case (x: var x, y: var y)) {
// x 和 y 会自动作为局部变量引入并推断类型(int)
print('Point: ($x, $y)'); // 输出: Point: (10, 20)
}
// 尝试匹配类型不符的列表
var data = [1, 'two'];
// 使用 var 解构,变量 a, b 会推断为 dynamic,模式匹配成功
if (data case [var a, var b]) {
print('Two items: $a, $b');
} else {
print('Not two items.');
}
// 如果想严格匹配类型,则明确指定类型
if (data case [int a, int b]) { // 明确指定 int 类型,此模式匹配失败
print('Two integers: $a, $b');
} else {
print('Not two integers.'); // 输出: Not two integers.
}
}
2.2 基础模式类型
Dart 提供四种基础模式,用于构建复杂模式匹配逻辑。
2.2.1 常量模式 (Constant Pattern)
作用:匹配值是否等于字面量或 const
常量。
语法:字面量(如 1
, 'hello'
, true
, null
)或 const
变量。
示例:
Dart
void checkStatusCode(int code) {
switch (code) {
case 200: // 常量模式
print('Success');
case 404: // 常量模式
print('Not found');
default:
print('Unknown status: $code');
}
}
void main() {
checkStatusCode(200); // 输出: Success
checkStatusCode(500); // 输出: Unknown status: 500
}
2.2.2 变量模式 (Variable Pattern)
作用:绑定匹配到的值到新变量,支持解构。
语法:
var <variableName>
final <variableName>
-
<Type> <variableName>
(类型可推断时通常省略,或利用简写模式在声明/赋值上下文)
示例:
Dart
void main() {
// 解构用户记录(使用简写模式,仅限于变量声明上下文)
var user = (name: 'Alice', age: 25);
// 当解构变量名与记录字段名一致时,使用简写模式 `(:name, :age)`
// name 会被推断为 String,age 会被推断为 int
var (:name, :age) = user;
print('User: $name, Age: $age'); // 输出: User: Alice, Age: 25
// 解构 API 响应列表
var response = [200, 'OK'];
// 列表解构时,变量名自动推断类型
if (response case [var code, var message]) {
print('Response: $code - $message'); // 输出: Response: 200 - OK
}
// switch 中绑定值(变量模式与类型测试模式的结合)
void processInput(Object input) {
switch (input) {
case int value: // 类型测试模式:同时检查类型并绑定到变量 value (value 推断为 int)
print('Number: $value');
case String text: // 类型测试模式:同时检查类型并绑定到变量 text (text 推断为 String)
print('Text: $text');
default:
print('Other: $input');
}
}
processInput(42); // 输出: Number: 42
processInput('Hello'); // 输出: Text: Hello
}
2.2.3 通配符模式 (Wildcard Pattern _
)
作用:忽略不关心的值,保持模式结构完整。
语法:_
示例:
Dart
void main() {
// 忽略记录中的字段(使用简写模式和通配符,仅限于变量声明上下文)
var user = (name: 'Bob', id: 123, role: 'user');
// 解构 name 字段,并忽略 id 和 role 字段
var (:name, id: _, role: _) = user;
print('User: $name'); // 输出: User: Bob
// switch 中忽略子模式(列表模式结合通配符)
void processRequest(List<String> request) {
switch (request) {
case ['GET', var path]: // 解构第一个元素为 'GET',第二个元素绑定到 path
print('Fetching: $path');
case ['POST', var path, _]: // 解构第一个为 'POST',第二个绑定到 path,忽略第三个
print('Posting to: $path');
case _: // 匹配任何其他情况
print('Invalid request.');
}
}
processRequest(['GET', '/api/users']); // 输出: Fetching: /api/users
processRequest(['POST', '/api/data', 'payload']); // 输出: Posting to: /api/data
}
2.2.4 类型测试模式 (Type-Test Pattern)
作用:检查值类型并进行智能类型提升。
语法:<Type> <variableName>
示例:
Dart
void main() {
void processShape(Object shape) {
switch (shape) {
case int radius: // 类型测试模式:检查 shape 是否为 int,并将智能提升后的值绑定到 radius
print('Circle, radius: $radius');
case List<double> rect: // 类型测试模式:检查 shape 是否为 List<double>,并绑定到 rect
print('Rectangle, dimensions: $rect');
default:
print('Unknown shape.');
}
}
processShape(5); // 输出: Circle, radius: 5
processShape([3.0, 4.0]); // 输出: Rectangle, dimensions: [3.0, 4.0]
}
2.2.5 空检查模式 (?
)
作用:匹配非空值,确保空安全处理。
语法:<pattern>?
示例:
Dart
void main() {
// 处理可空用户数据(if-case 中需要明确声明变量)
(String?, int?)? user = ('Alice', null);
// (name: var name, age: var age):仅当 user 非空,且其 `name` 字段非空时才匹配。
// `name` 会被推断为 String (非空),`age` 会被推断为 `int?`。
if (user case (String name, int? age)) {
print('Name: $name, Age: ${age ?? "unknown"}'); // 输出: Name: Alice, Age: unknown
}
// 处理可空坐标(在 switch 表达式中使用空检查模式)
String processNullableCoord((int?, int?)? coords) {
return switch (coords) {
// 记录本身非空且两个字段都非空时匹配 (x, y 会被推断为 int)
(int x, int y)? => 'Point: ($x, $y)',
// 记录非空,第一个字段非空,第二个字段为 null
(int x, null)? => 'Missing y coordinate ($x).',
// 记录非空,第一个字段为 null,第二个字段非空
(null, int y)? => 'Missing x coordinate (y=$y).',
// 记录非空,两个字段都为 null
(null, null)? => 'Both coordinates missing.',
// 记录本身为 null
null => 'No coordinates provided.',
// 对于 (int?, int?)? 类型,如果所有上述具象模式都被覆盖,Dart 编译器可能认为它是穷尽的,
// 不需要 _ 默认分支。如果未来添加更多复杂类型,可能需要 _。
};
}
print(processNullableCoord((10, 20))); // 输出: Point: (10, 20)
print(processNullableCoord(null)); // 输出: No coordinates provided.
print(processNullableCoord((10, null))); // 输出: Missing y coordinate (10).
print(processNullableCoord((null, 20))); // 输出: Missing x coordinate (y=20).
print(processNullableCoord((null, null))); // 输出: Both coordinates missing.
}
章节总结
本章深入讲解了 Dart 模式匹配的四种基础模式:
-
常量模式:用于匹配字面量或
const
常量。
-
变量模式:用于绑定值以便解构,并强调了利用
var
和 final
进行类型推断。同时明确了简写模式 (:name
) 主要用于变量声明和赋值上下文。
-
通配符模式:使用
_
明确表示忽略不关心的值,提升代码可读性。
-
类型测试模式:直接在
case
后指定类型即可进行类型检查和智能类型提升,这是 Dart 3 中推荐的简洁用法。
-
空检查模式:通过
?
后缀确保模式只匹配非空值,提升空安全处理的优雅性。
这些优化后的示例展示了如何在实际场景(如 API 响应、几何形状处理)中使用模式匹配,代码简洁且空安全。
展望下一章
理解了基础模式之后,是时候解锁模式匹配更强大的功能了。下一章将深入探讨解构模式,包括列表模式、映射模式、记录模式和对象模式,帮助你优雅地处理各种复杂数据结构,让你的 Dart 代码更加简洁和富有表现力。