宣言、恆常與 var 模式

Tip

本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你沒用過 C# 模式,建議先從模式 匹配概覽 開始。 完整的語言規則請參閱 patterns 參考資料。

一個模式會套用到 輸入表達式。 C# 先評估表達式,然後模式測試或擷取所得值。 宣言、恆定與 var 模式回答了三個實用問題:

  • 宣告模式: 這個表達式是否產生了相容執行時型別的非空值? 如果是,請為該值宣告一個變數。
  • 持續的模式: 這個表達式產生了一個特定的常數值嗎?
  • var 模式: 這個表達產生了什麼價值? 在不先測試其類型或值的情況下,擷取它。

測試並捕捉具有宣告模式的型別

宣告模式由類型與指定組成。 類型指定要測試的執行時類型。 指定會宣告接收匹配值的變數。

以下範例會收到一個 object,因此該運算式可能會產生許多不同的型別。 宣告模式可讓比對分支直接使用 decimal 數值,而不必另外進行型別測試和轉型:

static void PrintPrice(object value)
{
    if (value is decimal amount)
    {
        Console.WriteLine($"Price: {amount:C}");
    }
}

value is decimal amount 中:

  • value 是輸入表達式。 C# 會先評估它。
  • decimal 是測試型態。 當評估值非空且其執行時型別與 相容 decimal時,模式即為匹配。
  • amount 是指定名稱。 當符合模式時,會宣告 amount 並將十進位值指派給它。

編譯器會追蹤一個本地變數是否在你的程式碼讀取之前就已經接收到該值。 這種追蹤稱為 確定指派。 在區 if 塊內部,編譯器知道該 amount 區塊被指派,因為該區塊只有在模式匹配時才會執行。 如果你的程式碼嘗試存取 amount 區塊外 if ,編譯器會產生錯誤。 如果 value 不是 decimal 值,變數 amount 就不會被指派給值。

當匹配分支需要將結果作為測試型態使用時,選擇宣告模式。 它結合了測試、轉換和變數宣告,避免重複表達式或撰寫獨立的 cast。

當一個表達式可能產生多種有用的型態時,你也可以使用宣告模式:

static string FormatSensorValue(object reading) =>
    reading switch
    {
        int count => $"Count: {count}",
        double temperature => $"Temperature: {temperature:F1}°C",
        string message => $"Message: {message}",
        _ => "Unsupported reading"
    };

每個分支都會宣告一個相符型別的變數,因為結果需要該型別的格式化行為。 宣告模式只有在所評估的值為非 null,且其執行階段型別已透過模式所允許的轉換與受測型別相容時,才會符合。 null 沒有可供該模式比對的執行階段類型。 這個模式也不會執行使用者定義的轉換運算子:它是一個型別測試和擷取,而不是請求將值轉換成其他型別。 完整的相容性規則,請參見 宣告與型態模式

將特定值與恆定模式匹配

常數模式測試表達式是否產生特定常數,例如數字、字串、布林值、枚舉成員、宣告const值或 null

當多個已知值各自產生不同結果時,常數模式適合開關表達式:

static string GetCommandMessage(Command command) =>
    command switch
    {
        Command.Start => "Starting",
        Command.Stop => "Stopping",
        Command.Pause => "Pausing",
        _ => "Unknown command"
    };

Command 是一個列舉型,定義一組命名常數的型別。 Command.StartCommand.StopCommand.Pause 是其列舉成員,因此每個 switch 分支都會使用常數模式來測試某個具名命令值。

當指令可以有多種離散意義時,選擇此形式。 開關臂將數值與結果保持在一起。 對於一個簡單的等式比較, if 像 這樣的 if (command == Command.Start) 陳述通常比較容易閱讀。

常數模式比對使用語言內建的相等規則,而非使用者定義的 == 運算子。 關於詳細的等式與轉換規則,請參見 常數模式參考

null常數模式對於可靠的空檢查非常有用:

static bool HasText(string? text) => text is not null;

檢查 Null 狀態時,請選擇 is nullis not null。 這些模式不會呼叫使用者自訂的相等運算子,即使運算式的型別多載了 ==

為守衛捕捉一個帶有 var 圖案的結果

一個 var 模式 會匹配所有結果,包括 null,並宣告一個變數,其型別為輸入表達式的編譯時型別。 它可以在另一個模式已經匹配物件時擷取計算出的值:

static string GetDeliveryMessage(object delivery) =>
    delivery switch
    {
        ExpressDelivery express
            when EstimateDays(express) is var days && days <= 2
                => $"Arrives in {days} day{(days == 1 ? "" : "s")}",
        ExpressDelivery => "Express delivery for your location takes more than two days",
        _ => "Standard delivery"
    };

static int EstimateDays(ExpressDelivery delivery) =>
    delivery.MilesAway <= 500 ? 1 :
    delivery.MilesAway <= 1_000 ? 2 : 3;

record ExpressDelivery(int MilesAway);

宣告模式 ExpressDelivery express 首先捕捉傳送物件為 express。 方法呼叫 EstimateDays(express)var 模式的輸入運算式。 C# 會評估該方法呼叫,並在不測試其型別或值的情況下,將產生的估計值擷取為 days。 當守衛成功時,估計時間可能只有一到兩天。 arm 結果需要擷取的值,才能回報實際天數。

一般的局部變數無法在開關臂模式與其 when 守衛之間宣告。 再次輸入 EstimateDays(express) 結果會重複計算。 當程式碼已符合比對條件,且保護條件和結果都需要計算出的中介值時,請選擇此 var 模式。

如果你不需要擷取的值,就用 棄牌模式 _ 代替宣告變數。

另請參閱