模式类型
此页面是各种模式的参考。有关模式如何工作、可以在 Dart 中使用它们的位置以及常见用例的概述,请访问主要的模式页面。
模式优先级
#与运算符优先级类似,模式评估也遵循优先级规则。您可以使用带括号的模式来首先评估优先级较低的模式。
本文档按优先级升序列出模式类型
逻辑或
#子模式1 || 子模式2
逻辑或模式使用 ||
分隔子模式,如果任何分支匹配,则匹配。分支从左到右进行评估。一旦分支匹配,其余分支将不再评估。
var isPrimary = switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false
};
逻辑或模式中的子模式可以绑定变量,但分支必须定义相同的变量集,因为当模式匹配时,只会评估一个分支。
逻辑与
#子模式1 && 子模式2
由 &&
分隔的一对模式仅当两个子模式都匹配时才匹配。如果左分支不匹配,则不评估右分支。
逻辑与模式中的子模式可以绑定变量,但每个子模式中的变量不能重叠,因为如果模式匹配,它们都会被绑定
switch ((1, 2)) {
// Error, both subpatterns attempt to bind 'b'.
case (var a, var b) && (var b, var c): // ...
}
关系
#== 表达式
< 表达式
关系模式使用任何相等或关系运算符:==
、!=
、<
、>
、<=
和 >=
,将匹配的值与给定的常量进行比较。
当使用常量作为参数调用匹配值的适当运算符返回 true
时,模式匹配。
关系模式对于匹配数值范围非常有用,尤其是在与逻辑与模式结合使用时
String asciiCharType(int char) {
const space = 32;
const zero = 48;
const nine = 57;
return switch (char) {
< space => 'control',
== space => 'space',
> space && < zero => 'punctuation',
>= zero && <= nine => 'digit',
_ => ''
};
}
类型转换
#foo as String
类型转换模式允许您在解构的中间插入类型转换,然后再将值传递给另一个子模式
(num, Object) record = (1, 's');
var (i as int, s as String) = record;
如果值没有声明的类型,类型转换模式会抛出错误。与空断言模式类似,这使您可以强制断言某些解构值的预期类型。
空检查
#子模式?
如果值不为 null,则空检查模式首先匹配,然后针对该相同值匹配内部模式。它们允许您绑定一个变量,该变量的类型是要匹配的可为空值的不可为空的基础类型。
要将 null
值视为匹配失败而不抛出错误,请使用空检查模式。
String? maybeString = 'nullable with base type String';
switch (maybeString) {
case var s?:
// 's' has type non-nullable String here.
}
要在值是 null 时匹配,请使用常量模式 null
。
空断言
#子模式!
如果对象不为 null,则空断言模式首先匹配,然后匹配该值。它们允许非 null 值通过,但如果匹配的值为 null,则会抛出错误。
要确保 null
值不会被静默地视为匹配失败,请在匹配时使用空断言模式
List<String?> row = ['user', null];
switch (row) {
case ['user', var name!]: // ...
// 'name' is a non-nullable string here.
}
要从变量声明模式中消除 null
值,请使用空断言模式
(int?, int?) position = (2, 3);
var (x!, y!) = position;
要在值是 null 时匹配,请使用常量模式 null
。
常量
#123、null、'string'、math.pi、SomeClass.constant、const Thing(1, 2)、const (1 + 2)
当值等于常量时,常量模式匹配
switch (number) {
// Matches if 1 == number.
case 1: // ...
}
您可以直接使用简单文字和对命名常量的引用作为常量模式
- 数字文字(
123
、45.56
) - 布尔文字(
true
) - 字符串文字(
'string'
) - 命名常量(
someConstant
、math.pi
、double.infinity
) - 常量构造函数(
const Point(0, 0)
) - 常量集合文字(
const []
、const {1, 2}
)
更复杂的常量表达式必须用括号括起来并以 const
作为前缀(const (1 + 2)
)
// List or map pattern:
case [a, b]: // ...
// List or map literal:
case const [a, b]: // ...
变量
#var bar、String str、final int _
变量模式将新变量绑定到已匹配或解构的值。它们通常作为解构模式的一部分出现,以捕获解构值。
变量在只有在模式匹配时才能访问的代码区域中有效。
switch ((1, 2)) {
// 'var a' and 'var b' are variable patterns that bind to 1 and 2, respectively.
case (var a, var b): // ...
// 'a' and 'b' are in scope in the case body.
}
只有当匹配的值具有声明的类型时,类型化变量模式才匹配,否则会失败
switch ((1, 2)) {
// Does not match.
case (int a, String b): // ...
}
您可以将通配符模式用作变量模式。
标识符
#foo、_
标识符模式的行为可能类似于常量模式或变量模式,具体取决于它们出现的上下文
- 声明上下文:使用标识符名称声明新变量:
var (a, b) = (1, 2);
- 赋值上下文:赋值给具有标识符名称的现有变量:
(a, b) = (3, 4);
- 匹配上下文:被视为命名常量模式(除非其名称为
_
)dartconst c = 1; switch (2) { case c: print('match $c'); default: print('no match'); // Prints "no match". }
- 任何上下文中的通配符标识符:匹配任何值并丢弃它:
case [_, var y, _]: print('中间元素是 $y');
带括号
#(子模式)
与带括号的表达式类似,模式中的括号允许您控制模式优先级,并在需要较高优先级的地方插入较低优先级的模式。
例如,假设布尔常量 x
、y
和 z
分别等于 true
、true
和 false
。虽然以下示例类似于布尔表达式求值,但该示例匹配的是模式。
// ...
x || y => 'matches true',
x || y && z => 'matches true',
x || (y && z) => 'matches true',
// `x || y && z` is the same thing as `x || (y && z)`.
(x || y) && z => 'matches nothing',
// ...
Dart 从左到右开始匹配模式。
第一个模式匹配
true
,因为x
匹配true
。第二个模式匹配
true
,因为x
匹配true
。第三个模式匹配
true
,因为x
匹配true
。第四个模式
(x || y) && z
没有匹配项。x
匹配true
,因此 Dart 不会尝试匹配y
。- 尽管
(x || y)
匹配true
,但z
不匹配true
- 因此,模式
(x || y) && z
不匹配true
。 - 子模式
(x || y)
不匹配false
,因此 Dart 不会尝试匹配z
。 - 因此,模式
(x || y) && z
不匹配false
。 - 总而言之,
(x || y) && z
没有匹配项。
列表
#[子模式1, 子模式2]
列表模式匹配实现 List
的值,然后递归地将其子模式与列表的元素进行匹配,以按位置解构它们
const a = 'a';
const b = 'b';
switch (obj) {
// List pattern [a, b] matches obj first if obj is a list with two fields,
// then if its fields match the constant subpatterns 'a' and 'b'.
case [a, b]:
print('$a, $b');
}
列表模式要求模式中的元素数量与整个列表匹配。但是,您可以使用剩余元素作为占位符来表示列表中任意数量的元素。
剩余元素
#列表模式可以包含一个剩余元素 (...
),这允许匹配任意长度的列表。
var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7".
print('$a $b $c $d');
剩余元素也可以拥有一个子模式,该子模式可以将不匹配列表中其他子模式的元素收集到一个新列表中
var [a, b, ...rest, c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 [3, 4, 5] 6 7".
print('$a $b $rest $c $d');
Map
#{"键": 子模式1, someConst: 子模式2}
映射模式匹配实现 Map
的值,然后递归地将其子模式与映射的键进行匹配,以解构它们。
映射模式不要求模式匹配整个映射。映射模式会忽略映射中包含的任何模式未匹配的键。
记录
#(子模式1, 子模式2)
(x: 子模式1, y: 子模式2)
记录模式匹配一个记录对象并解构其字段。如果该值不是具有与模式相同的形状的记录,则匹配失败。否则,字段子模式将与记录中对应的字段进行匹配。
记录模式要求模式匹配整个记录。要使用模式解构具有命名字段的记录,请在模式中包含字段名称
var (myString: foo, myNumber: bar) = (myString: 'string', myNumber: 1);
可以省略 getter 名称,并从字段子模式中的变量模式或标识符模式推断。这些模式对是等效的
// Record pattern with variable subpatterns:
var (untyped: untyped, typed: int typed) = record;
var (:untyped, :int typed) = record;
switch (record) {
case (untyped: var untyped, typed: int typed): // ...
case (:var untyped, :int typed): // ...
}
// Record pattern with null-check and null-assert subpatterns:
switch (record) {
case (checked: var checked?, asserted: var asserted!): // ...
case (:var checked?, :var asserted!): // ...
}
// Record pattern with cast subpattern:
var (untyped: untyped as int, typed: typed as String) = record;
var (:untyped as int, :typed as String) = record;
对象
#SomeClass(x: 子模式1, y: 子模式2)
对象模式根据给定的命名类型检查匹配的值,以使用对象属性上的 getter 解构数据。如果该值不具有相同的类型,则会被反驳。
switch (shape) {
// Matches if shape is of type Rect, and then against the properties of Rect.
case Rect(width: var w, height: var h): // ...
}
可以省略 getter 名称,并从字段子模式中的变量模式或标识符模式推断
// Binds new variables x and y to the values of Point's x and y properties.
var Point(:x, :y) = Point(1, 2);
对象模式不要求模式匹配整个对象。如果对象具有模式未解构的额外字段,它仍然可以匹配。
通配符
#_
名为_
的模式是一个通配符,它要么是变量模式,要么是标识符模式,它不绑定或赋值给任何变量。
它在需要子模式以便稍后解构位置值时用作占位符很有用
var list = [1, 2, 3];
var [_, two, _] = list;
带有类型注释的通配符名称在您只想测试值的类型而不将该值绑定到名称时很有用
switch (record) {
case (int _, String _):
print('First field is int and second is String.');
}
除非另有说明,本网站上的文档反映了 Dart 3.6.0。页面最后更新于 2024-06-27。 查看源文件 或报告问题。