初學 Dart 如果你常常被一些魔法般的程式碼困惑,很可能是沒有深入理解模式的概念。模式(Pattern)是 Dart 3.0 引入的一個重要特性,模式 pattern 的概念主要是涉及「比對」match 和「解構」destructure 。準確的說就是可以實現「比對」或「解構」的語法,類似於語句 statement 和表達式 expression。可以把模式想成是一個資料的模板,具備特定結構,我們可以用來比對是否符合結構,進一步解構取得值。
許多新語言都支援類似模式的概念,例如 Swift ,Kotlin 的解構和
when
。粗略來說我們可以把模式概念理解為一種 switch-case case 條件的進化版。
模式在 Dart 中主要用於兩個目的:比對和解構。
具體來說,模式本身有很多類型可以協助我們比對各種類型的資料:
swtich
比對某個值例如 null
、true
、'abc'
var x
['a' || 'b', var c]
,匹配一個值是一有兩個元素的列表,第一個元素需要是 a 或 b,而 var c
則是變數模式,任何值可以綁定到變數 c。{"name": String name }
匹配特定鍵值對Person(:name)
,也可以進一步比對屬性。物件模式看起來像是預設建構子,甚至該類別沒有預設建構子也是一樣的撰寫方式,更多例子如 String()
,int()
_
任何值都匹配(1, int y)
,一個常數和變數型別模式組合而成的 Record從列表模式的範例我們知道模式是可以和邏輯運算子搭配使用的,包含 &&
, ||
以及 ()
類似表達式。如此可以增加比對匹配的複雜性和靈活性
根據模式出現的位置提供比對或解構的功能:
var nums = [1 ,2 ,3];
var [a, b, c] = nums;
switch (list) {
case ['a' || 'b', var c]:
print(c);
}
接著,我們進一步瞭解模式可能出現的位置:
for
和 for-in
迴圈if-case
和 swtich-case
// 變數宣告
var (a, [b, c]) = ('A', [1, 2]); // 模式變數須使用 `var` 或 `final` 開頭。
(b, a) = (a, b); // 交換
// switch
// 模式在 case 中的解構值會變成區域變數,只限於該 case 範圍內
switch (obj) {
case 1:
// 如果 obj 大於等於 2 小於等於 5
case >= 2 && <= 5:
// 如果 obj 是一個 2 個元素的 Record
case (var x, var y):
}
// 邏輯或模式
var isOdd = switch (n) {
1 || 3 || 5 => true,
_ => false
}
// 搭配 when
switch (shape) {
case Square(size: var s) || Circle(size: var s) when s > 0:
//
}
switch (pair) {
case (int a, int b) when a > b:
//
}
在某些情況下,模式匹配可能失敗,但不會導致錯誤,而是繼續執行下一個選項。這種情況稱為 Refutable context。例如,switch
語句和 if-case
語句中的模式就是在 Refutable context 中。
User? fromJson(Map<String, Object?> data) => switch(data) {
{'username': String name, 'age': int age} => User(
username: name,
age: age,
),
_ => null,
};
bool isUnitCircle(Object? data) {
if (data case Circle(radius: var rad) when rad ==1) {
return true;
}
return false;
}
有了模式的概念很多東西就比較容易理解。例如 if-case 的 case 我們可以想成原本只能用 ==
、!=
等邏輯運算子,現在可以通過 case
直接使用模式比對
// 如果沒有 case
if (pair is List &&
pair.length == 2 &&
pair[0] is int &&
pair[1] is int) {
return Point(pair[0] as int, pair[1] as int);
}
// case 版本
if (pair case [int x, int y]) return Point(x, y);