內容

此頁面是不同類型模式的參考。如需模式運作方式、在 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;

如果值沒有指定的類型,轉型模式會 擲回。如同 空值斷言模式,這讓您可以強制斷言某些解構值的預期類型。

Null 檢查

#

subpattern?

Null 檢查模式會先比對值是否不為 null,然後再比對內部模式與同一個值。它們讓你可以繫結一個變數,其型別是正在比對的 nullable 值的非 nullable 基底型別。

若要將 null 值視為比對失敗而不擲回,請使用 null 檢查模式。

dart
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 值通過,但如果比對的值為 null,則會 擲回

若要確保 null 值不會被靜默視為比對失敗,請在比對時使用 null 斷言模式

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

若要從變數宣告模式中移除 null 值,請使用 null 斷言模式

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

常數模式會在值等於常數時比對

dart
switch (number) {
  // Matches if 1 == number.
  case 1: // ...
}

你可以直接使用簡單的文字和對命名常數的參照作為常數模式

  • 數字文字 (12345.56)
  • 布林文字 (true)
  • 字串文字 ('string')
  • 命名常數 (someConstantmath.pidouble.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('中間元素為 $y');

括號

#

(子模式)

與括號表達式一樣,模式中的括號讓您可以控制模式優先順序,並在預期較高優先順序的情況下插入較低優先順序的模式。

例如,假設布林常數 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 沒有比對結果。

清單

#

[子模式1,子模式2]

清單模式比對實作清單的值,然後遞迴地比對其子模式與清單的元素,以透過位置解構它們

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

清單模式要求模式中的元素數目與整個清單相符。不過,您可以使用剩餘元素作為佔位符,以考量清單中任何數目的元素。

Rest 元素

#

清單模式可以包含一個剩餘元素 (...),這允許比對長度任意的清單。

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": 子模式1, someConst: 子模式2}

Map 樣式比對實作 Map 的值,然後遞迴比對其子樣式與 map 的金鑰,以解構它們。

Map 樣式不需要樣式比對整個 map。Map 樣式會忽略 map 中任何未由樣式比對到的金鑰。

記錄

#

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