模式類型
此頁面是有關不同模式種類的參考資料。如需模式如何運作、您可以在 Dart 中何處使用它們以及常見用例的總覽,請造訪主要的模式頁面。
模式優先順序
#與運算子優先順序類似,模式評估遵循優先順序規則。您可以使用括號模式來優先評估較低優先順序的模式。
本文檔列出了依優先順序升序排列的模式類型
邏輯或
#subpattern1 || subpattern2
邏輯或模式通過 ||
分隔子模式,並且在任何分支匹配時匹配。分支從左到右評估。一旦分支匹配,其餘分支將不會評估。
var isPrimary = switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false,
};
邏輯或模式中的子模式可以綁定變數,但分支必須定義相同的變數集,因為當模式匹配時,只會評估一個分支。
邏輯與
#subpattern1 && subpattern2
由 &&
分隔的一對模式僅在兩個子模式都匹配時才匹配。如果左分支不匹配,則不會評估右分支。
邏輯與模式中的子模式可以綁定變數,但每個子模式中的變數不得重疊,因為如果模式匹配,它們都將被綁定
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',
_ => '',
};
}
Cast
#foo as String
cast 模式允許您在解構的中間插入類型轉換,然後再將值傳遞給另一個子模式
(num, Object) record = (1, 's');
var (i as int, s as String) = record;
如果值不具有聲明的類型,則 Cast 模式將拋出錯誤。與null 斷言模式類似,這允許您強制斷言某些解構值的預期類型。
Null 檢查
#subpattern?
Null 檢查模式首先在值不為 null 時匹配,然後針對同一值匹配內部模式。它們允許您綁定一個變數,該變數的類型是正在匹配的可為 null 值的非可為 null 基礎類型。
若要將 null
值視為匹配失敗而不拋出錯誤,請使用 null 檢查模式。
String? maybeString = 'nullable with base type String';
switch (maybeString) {
case var s?:
// 's' has type non-nullable String here.
}
若要在值為 null 時匹配,請使用常數模式 null
。
Null 斷言
#subpattern!
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
值,請使用 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('The middle element is $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
沒有匹配項。
列表
#[subpattern1, subpattern2]
列表模式匹配實作 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');
}
列表模式要求模式中元素的數量與整個列表匹配。但是,您可以使用rest 元素作為佔位符,以考慮列表中任意數量的元素。
Rest 元素
#列表模式可以包含一個 rest 元素 (...
),它允許匹配任意長度的列表。
var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7".
print('$a $b $c $d');
rest 元素也可以具有子模式,該子模式將與列表中其他子模式不匹配的元素收集到一個新列表中
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
#{"key": subpattern1, someConst: subpattern2}
Map 模式匹配實作 Map
的值,然後遞歸地將其子模式與 Map 的鍵匹配以解構它們。
Map 模式不要求模式與整個 Map 匹配。Map 模式會忽略 Map 中包含的任何未被模式匹配的鍵。嘗試匹配 Map 中不存在的鍵將拋出 StateError
final {'foo': int? foo} = {};
紀錄
#(subpattern1, subpattern2)
(x: subpattern1, y: subpattern2)
紀錄模式匹配紀錄物件並解構其欄位。如果值不是具有與模式相同形狀的紀錄,則匹配失敗。否則,欄位子模式將與紀錄中的相應欄位匹配。
紀錄模式要求模式與整個紀錄匹配。若要使用模式解構具有具名欄位的紀錄,請在模式中包含欄位名稱
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: subpattern1, y: subpattern2)
物件模式針對給定的具名類型檢查匹配的值,以使用物件屬性上的 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.7.1 版本。頁面上次更新時間為 2025-02-12。 檢視原始碼 或回報問題。