Pattern模式是Dart 3.0發佈的3個高級特性之一,在第09天我們學習了模式的概覽和用法,對模式的強大之處有了基本的認識,今天我們來看看Dart中的全部模式類型,總共有15種,它們包括邏輯或、邏輯與、關係、值轉換、空檢測、空斷言、常量、變數、標識符、括弧、List列表、Map映射、Recor... ...
Dart官方文檔:https://dart.dev/language/pattern-types
重要說明:本博客基於Dart官網文檔,但並不是簡單的對官網進行翻譯,在覆蓋核心功能情況下,我會根據個人研發經驗,加入自己的一些擴展問題和場景驗證。
和操作符一樣,模式運算也遵循一定的優先順序規則,我們可以通過增加括弧()
讓低優先順序規則的模式優先運算:
- 邏輯或模式低於邏輯與模式,邏輯與模式低於關係模式:
邏輯或 < 邏輯與 < 關係
。 - 一元模式優先順序相同:值轉換、空檢測、空斷言。
- 其他的模式都具有最高的優先順序,集合類型(List列表、Map映射和Record記錄)和對象模式包含了其他數據,因此作為外部模式優先運算。
邏輯或模式(Logical-or)
模式語法:子模式1 || 子模式2
模式規則:邏輯或模式通過||
分割子模式,從左到右,任何一個子模式匹配則本模式匹配,且後面的子模式不在運算。
子模式可以綁定變數,但是每個子模式綁定的變數必須相同,因為任一子模式匹配則後面的子模式不在運算。
var isPrimary = switch (color) {
Color.red || Color.yellow || Color.blue => true,
_ => false
};
邏輯與模式(Logical-and)
模式語法:子模式1 && 子模式2
模式規則:邏輯與模式通過&&
分隔子模式,從左到右,任何一個子模式未匹配則本模式未匹配,且後面的子模式不在運算。
子模式可以綁定變數,且每個子模式綁定的變數不能重疊,因為本模式匹配代表每個子模式都必須匹配運算,如果重疊則意味著變數被賦值多次。
switch ((1, 2)) {
case (var a, var b) && (var c, var d): // ...
}
關係模式(Relational)
模式規則:關係模式通過和給定的常量進行比較完成匹配(比較操作符:==
,!=
,<
,>
,<=
,>=
),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)
模式語法:變數 as 類型
,如:foo as String
模式規則:值轉換模式允許在對象數據解構過程中進行類型轉換,如果類型無法轉換,則會產生錯誤,建議在類型轉換之前,進行類型斷言。
(num, Object) record = (1, 's');
var (i as int, s as String) = record;
空檢測模式(Null-check)
模式語法:子模式?
模式規則:如果檢測的值不為NULL,則模式匹配。它允許綁定一個變數,變數的類型是該不可為NULL值類型基類。
String? maybeString = 'nullable with base type String';
switch (maybeString) {
case var s?:
// 's' has type non-nullable String here.
}
空斷言模式(Null-assert)
模式語法:子模式!
模式規則:首先檢測對象不為NULL,然後檢測對象數據值。如果匹配的值為NULL,則會拋出錯誤。它常用於解構並賦值場景,且保證所賦值非NULL。
List<String?> row = ['user', null];
switch (row) {
case ['user', var name!]: // ...
// 'name' is a non-nullable string here.
}
(int?, int?) position = (2, 3);
var (x!, y!) = position;
常量模式(constant)
當值為常量時,常量模式匹配,常量包括: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 (const (1 + 2))
// List or map pattern:
case [a, b]: // ...
// List or map literal:
case const [a, b]: // ...
變數模式(variable)
模式規則:變數模式一般在解構和賦值中,它匹配模式、解構對象並完成賦值,如:var bar
, String str
, final int _
。變數的作用域為模式所在的作用域。如果變數指定了類型,那麼當對象類型和值均匹配時,模式才被匹配。通配符模式是一個特殊的變數模式。
switch ((1, 2)) {
// 'var a'和'var b'是變數模式,它們值分別為`1`和`2`
case (var a, var b): // ...
// 'a'和'b'的作用域在case代碼塊
}
switch ((1, 2)) {
// `2`是數字類型,與'String b'不匹配,因此本模式為匹配
case (int a, String b): // ...
}
標識符模式(identifier)
標識符模式與常量模式或變數模式類似:
- 變數申明上下文:給變數申明一個標識符,如:
var (a, b) = (1, 2);
- 變數賦值上下文:給已經存在的標識符賦值,如:
(a, b) = (3, 4);
- 模式匹配上下文:當作一個命名常量模式(除名字是
_
外) - 任意上下文中的通配符標識符:能匹配任何值且忽略該值,如:
case [_, var y, _]: print('The middle element is $y');
括弧模式
模式語法:(子模式)
代碼樣例:如下代碼,和表達式一樣,增加括弧()
目的是提高模式的優先順序。
final (x, y, z) = (true, true, false);
// 沒有括弧:true
x || y && z => 'matches true',
// 增加括弧:false
(x || y) && z => 'matches false',
列表模式(List)
模式語法:[子模式1, 子模式2]
List列表模式首先匹配List
類型,然後匹配列表元素值,併進行解構和賦值。List列表模式必須匹配整個列表模式元素,我們可以使用...
占位符匹配剩餘的列表元素。
// 全列表元素匹配
const a = 'a';
const b = 'b';
switch (obj) {
case [a, b]:
print('$a, $b');
}
// 占位符匹配剩餘元素,且忽略
var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
print('$a $b $c $d'); // 1 2 6 7
// 占位符當作一個子列表
var [a, b, ...rest, c, d] = [1, 2, 3, 4, 5, 6, 7];
print('$a $b $rest $c $d'); // 1 2 [3, 4, 5] 6 7
Map映射模式
模式語法:{"key": subpattern1, someConst: subpattern2}
Map映射模式首先匹配Map
類型,然後匹配元素內容,併進行解構和賦值。Map映射模式不需要匹配所有元素,忽略未被匹配到的元素。
Record記錄模式
模式語法:(subpattern1, subpattern2)
或者(x: subpattern1, y: subpattern2)
Record記錄模式首先匹配記錄,然後解構其欄位。欄位數量、類型和值未匹配,則模式匹配失敗。Record記錄模式必須匹配所有欄位。欄位getter可由變數和標識符模式推導得到。
var (myString: foo, myNumber: bar) = (myString: 'string', myNumber: 1);
// 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 wih null-check and null-assert subpatterns:
switch (record) {
case (checked: var checked?, asserted: var asserted!): // ...
case (:var checked?, :var asserted!): // ...
}
// Record pattern wih cast subpattern:
var (untyped: untyped as int, typed: typed as String) = record;
var (:untyped as int, :typed as String) = record;
Object對象模式
模式語法:SomeClass(x: subpattern1, y: subpattern2)
對象模式首先對象類型和屬性類型,並完成對象屬性解構,調用getter方法完成賦值。如果類型不一致,則匹配失敗。對象的屬性名可以忽略,它可以通過變數模式和標識符模式進行推導。和Map映射模式一樣,對象模式不需要匹配所有屬性,忽略未被匹配到的屬性。
switch (shape) {
// Matches if shape is of type Rect, and then against the properties of Rect.
case Rect(width: var w, height: var h): // ...
}
// 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.');
}
我的本博客原地址:https://ntopic.cn/p/2023100501
本文作者:奔跑的蝸牛,轉載請註明原文鏈接:https://ntopic.cn