dart-use-pattern-matching

dart-use-pattern-matching

熱門

在適當情況下使用 switch 表達式與模式匹配

392星標
25分支
更新於 2026/7/10
SKILL.md
readonlyread-only
name
dart-use-pattern-matching
description

在適當情況下使用 switch 表達式與模式匹配

實作 Dart 模式

目錄

模式選擇策略

根據資料結構與預期結果套用特定模式類型。請遵循以下條件指引:

  • 若要驗證並從反序列化資料(例如 JSON)中提取: 使用 Map 與 List 模式同時檢查結構並解構鍵值對。
  • 若要處理多個回傳值: 使用 Record 模式將欄位直接解構為區域變數。
  • 若要執行型別特定的行為(代數資料類型): 使用 Object 模式搭配 sealed 類別以確保窮舉性。
  • 若要比對數值範圍或條件: 使用關係模式(>=, <=)與邏輯且(&&)模式。
  • 若多個 case 共用邏輯: 使用邏輯或(||)模式共用單一 case 主體或守衛子句。
  • 若要忽略特定值: 在集合中使用萬用字元模式(_)或不匹配的 Rest 元素(...)。

Switch 陳述式 vs. 表達式

根據執行上下文選擇適當的 switch 結構:

  • 若要產生值: 使用 switch 表達式
    • 語法:switch (value) { pattern => expression, }
    • 規則:每個 case 必須是單一表達式。無隱含貫穿。必須窮舉。
  • 若要執行陳述式或副作用: 使用 switch 陳述式
    • 語法:switch (value) { case pattern: statements; }
    • 規則:空的 case 會貫穿到下一個 case。非空的 case 會隱含 break(不需要 break 關鍵字)。

核心模式實作

使用以下語法與規則實作模式:

  • 邏輯或(||): pattern1 || pattern2。兩個分支必須定義完全相同的變數集合。
  • 邏輯且(&&): pattern1 && pattern2。分支不得定義重疊的變數。
  • 關係: ==, !=, <, >, <=, >= 後接常數表達式。
  • 轉型(as): pattern as Type。若值不符合型別則拋出例外。用於在解構時強制斷言型別。
  • 空值檢查(?): pattern?。若值為 null 則匹配失敗。將變數繫結至非可空基底型別。
  • 空值斷言(!): pattern!。若值為 null 則拋出例外。
  • 變數: var nameType name。將匹配的值繫結至新的區域變數。
  • 萬用字元(_): 匹配任何值並將其丟棄。
  • List: [pattern1, pattern2]。匹配精確長度的列表,除非使用 Rest 元素(......var rest)。
  • Map: {"key": pattern}。匹配包含指定鍵的 Map。忽略未匹配的鍵。
  • Record: (pattern1, named: pattern2)。匹配精確形狀的 Record。使用 :var name 推斷 getter 名稱。
  • Object: ClassName(field: pattern)。匹配 ClassName 的實例。使用 :var field 推斷 getter 名稱。

工作流程

任務進度:實作模式匹配

複製此檢查清單以追蹤實作複雜模式匹配邏輯的進度:

  • [ ] 識別正在評估的資料結構(JSON、Record、Class、Enum)。
  • [ ] 選擇適當的 switch 結構(表達式用於值,陳述式用於副作用)。
  • [ ] 定義所需的模式(Object、Map、List、Record)。
  • [ ] 使用變數模式(var x, :var y)提取所需資料。
  • [ ] 對無法透過模式表達的邏輯套用守衛子句(when condition)。
  • [ ] 使用萬用字元(_)或 default 子句處理未匹配的 case(若未使用 sealed 類別)。
  • [ ] 執行窮舉性驗證器。

回饋迴圈:窮舉性檢查

當對 sealed 類別或列舉進行 switch 時,必須確保所有子型別都已處理。

  1. 執行驗證器: 執行 dart analyze
  2. 檢視錯誤: 尋找「型別 'X' 未被 switch case 窮舉匹配」的錯誤。
  3. 修正: 為未處理的子型別新增缺少的 Object 模式,或者若可接受預設回退則新增萬用字元(_)case。

範例

JSON 驗證與解構

使用 Map 與 List 模式在單一步驟中驗證結構並提取資料。

輸入:

var data = {
  'user': ['Lily', 13],
};

實作:

if (data case {'user': [String name, int age]}) {
  print('使用者 $name 今年 $age 歲。');
} else {
  print('無效的 JSON 結構。');
}

代數資料類型(Sealed 類別)

使用 Object 模式搭配 switch 表達式窮舉處理家族型別。

實作:

sealed class Shape {}

class Square implements Shape {
  final double length;
  Square(this.length);
}

class Circle implements Shape {
  final double radius;
  Circle(this.radius);
}

// switch 表達式因 `sealed` 修飾詞保證窮舉性。
double calculateArea(Shape shape) => switch (shape) {
  Square(length: var l) => l * l,
  Circle(:var radius)   => math.pi * radius * radius,
};

變數交換與解構

使用變數賦值模式交換值或提取 Record 欄位,無需暫存變數。

實作:

var (a, b) = ('left', 'right');
(b, a) = (a, b); // 交換值

// 解構函式回傳值
var (name, age) = getUserInfo();

守衛子句與邏輯或

在模式匹配後使用 when 評估任意條件。

實作:

switch (shape) {
  case Square(size: var s) || Circle(size: var s) when s > 0:
    print('有效的對稱形狀,大小為 $s');
  case Square() || Circle():
    print('無效或空的形狀');
  default:
    print('未知形狀');
}