跳到主內容

模式型別

本頁面是不同型別模式的參考。要了解模式的工作原理、在 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.');
}