跳到主内容

模式类型

本页面是不同类型模式的参考。要了解模式的工作原理、在 Dart 中的使用位置以及常见用例,请访问主模式页面。

模式优先级

#

类似于运算符优先级,模式求值也遵循优先级规则。您可以使用带括号的模式来优先求值较低优先级的模式。

本文档按优先级升序列出模式类型

  • 逻辑或模式的优先级低于逻辑与模式,逻辑与模式的优先级低于关系模式,依此类推。

  • 后缀一元模式(转换空检查空断言)具有相同的优先级。

  • 其余主要模式具有最高优先级。集合类型(记录列表映射)和对象模式包含其他数据,因此作为外部模式首先进行求值。

逻辑或

#

subpattern1 || subpattern2

逻辑或模式使用 || 分隔子模式,如果任何分支匹配,则匹配成功。分支从左到右求值。一旦一个分支匹配,其余分支将不再求值。

dart
var isPrimary = switch (color) {
  Color.red || Color.yellow || Color.blue => true,
  _ => false,
};

逻辑或模式中的子模式可以绑定变量,但分支必须定义相同的变量集,因为当模式匹配时,只会求值一个分支。

逻辑与

#

subpattern1 && subpattern2

一对由 && 分隔的模式仅在两个子模式都匹配时才匹配。如果左分支不匹配,则右分支不求值。

逻辑与模式中的子模式可以绑定变量,但每个子模式中的变量不能重叠,因为如果模式匹配,它们都将被绑定。

dart
switch ((1, 2)) {
  // Error, both subpatterns attempt to bind 'b'.
  case (var a, var b) && (var b, var c): // ...
}

关系

#

== expression

< expression

关系模式使用任何相等或关系运算符(==!=<><=>=)将匹配值与给定常量进行比较。

当对匹配值调用适当的运算符并以常量作为参数返回 true 时,模式匹配成功。

关系模式对于匹配数字范围很有用,特别是当与逻辑与模式结合使用时。

dart
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

转换模式允许您在解构过程中插入类型转换,然后再将值传递给另一个子模式。

dart
(num, Object) record = (1, 's');
var (i as int, s as String) = record;

如果值不具有指定类型,转换模式将抛出异常。与空断言模式一样,这允许您强制断言某些解构值的预期类型。

空检查

#

subpattern?

空检查模式首先在值不为空时匹配,然后将内部模式与该相同的值进行匹配。它们允许您绑定一个变量,该变量的类型是被匹配的可空值的非空基类型。

要将 null 值视为匹配失败而不抛出异常,请使用空检查模式。

dart
String? maybeString = 'nullable with base type String';
switch (maybeString) {
  case var s?:
  // 's' has type non-nullable String here.
}

要匹配值空的情况,请使用常量模式 null

空断言

#

subpattern!

空断言模式首先在对象不为空时匹配,然后匹配该值。它们允许非空值通过,但如果匹配值为 null,则会抛出异常。

为了确保 null 值不会被静默地视为匹配失败,请在匹配时使用空断言模式。

dart
List<String?> row = ['user', null];
switch (row) {
  case ['user', var name!]: // ...
  // 'name' is a non-nullable string here.
}

要从变量声明模式中排除 null 值,请使用空断言模式。

dart
(int?, int?) position = (2, 3);

var (x!, y!) = position;

要匹配值空的情况,请使用常量模式 null

常量

#

123, null, 'string', math.pi, SomeClass.constant, const Thing(1, 2), const (1 + 2)

当值等于常量时,常量模式匹配成功。

dart
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))

dart
// List or map pattern:
case [a, b]: // ...

// List or map literal:
case const [a, b]: // ...

变量

#

var bar, String str, final int _

变量模式将新变量绑定到已匹配或解构的值。它们通常作为解构模式的一部分出现,用于捕获解构的值。

这些变量的作用域位于代码中,只有当模式匹配时才能访问该区域。

dart
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.
}

类型化变量模式仅在匹配值具有声明类型时才匹配,否则失败。

dart
switch ((1, 2)) {
  // Does not match.
  case (int a, String b): // ...
}

您可以将通配符模式用作变量模式。

标识符

#

foo, _

标识符模式可以像常量模式一样,也可以像变量模式一样,具体取决于它们出现的上下文。

  • 声明上下文:使用标识符名称声明新变量:var (a, b) = (1, 2);
  • 赋值上下文:将值赋给具有标识符名称的现有变量:(a, b) = (3, 4);
  • 匹配上下文:被视为命名常量模式(除非其名称为_
    dart
    const c = 1;
    switch (2) {
      case c:
        print('match $c');
      default:
        print('no match'); // Prints "no match".
    }
  • 任何上下文中的通配符标识符:匹配任何值并将其丢弃:case [_, var y, _]: print('The middle element is $y');

带括号的

#

(subpattern)

与带括号的表达式类似,模式中的括号允许您控制模式优先级,并在期望高优先级模式的位置插入低优先级模式。

例如,假设布尔常量 xyz 分别等于 truetruefalse。尽管以下示例类似于布尔表达式求值,但该示例匹配的是模式。

dart
// ...
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 从左到右开始匹配模式。

  1. 第一个模式匹配 true,因为 x 匹配 true

  2. 第二个模式匹配 true,因为 x 匹配 true

  3. 第三个模式匹配 true,因为 x 匹配 true

  4. 第四个模式 (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 没有匹配项。

列表

#

[subpattern1, subpattern2]

列表模式匹配实现List的值,然后递归地将其子模式与列表的元素进行匹配,以按位置解构它们。

dart
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');
}

列表模式要求模式中的元素数量与整个列表匹配。但是,您可以使用剩余元素作为占位符,以处理列表中任意数量的元素。

剩余元素

#

列表模式可以包含一个剩余元素 (...),这允许匹配任意长度的列表。

dart
var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7".
print('$a $b $c $d');

剩余元素还可以有一个子模式,该子模式将列表中不匹配其他子模式的元素收集到一个新列表中。

dart
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');

映射

#

{"key": subpattern1, someConst: subpattern2}

映射模式匹配实现Map的值,然后递归地将其子模式与映射的键进行匹配以解构它们。

映射模式不要求模式匹配整个映射。映射模式会忽略映射中包含的、不被模式匹配的任何键。尝试匹配映射中不存在的键将抛出StateError

dart
final {'foo': int? foo} = {};

记录

#

(subpattern1, subpattern2)

(x: subpattern1, y: subpattern2)

记录模式匹配记录对象并解构其字段。如果值不是与模式具有相同形状的记录,则匹配失败。否则,字段子模式将与记录中相应的字段进行匹配。

记录模式要求模式匹配整个记录。要使用模式解构带有命名字段的记录,请在模式中包含字段名称。

dart
var (myString: foo, myNumber: bar) = (myString: 'string', myNumber: 1);

getter 名称可以省略,并从字段子模式中的变量模式标识符模式中推断出来。这些模式对是等效的。

dart
// 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: subpattern1, y: subpattern2)

对象模式根据给定命名类型检查匹配值,以使用对象属性上的 getter 解构数据。如果值不具有相同类型,它们将被拒绝

dart
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 名称可以省略,并从字段子模式中的变量模式标识符模式中推断出来。

dart
// Binds new variables x and y to the values of Point's x and y properties.
var Point(:x, :y) = Point(1, 2);

对象模式不要求模式匹配整个对象。如果一个对象有模式不解构的额外字段,它仍然可以匹配。

通配符

#

_

名为 _ 的模式是一个通配符,可以是变量模式标识符模式,它不绑定或赋值给任何变量。

它在需要子模式来解构后续位置值的地方作为占位符很有用。

dart
var list = [1, 2, 3];
var [_, two, _] = list;

带有类型注解的通配符名称在您想要测试值的类型但不想将值绑定到名称时很有用。

dart
switch (record) {
  case (int _, String _):
    print('First field is int and second is String.');
}