性質與位置模式

小提示

本文屬於 基礎部分, 適合已經至少懂一種程式語言並正在學習 C# 的開發者。 如果你不熟悉模式,可以先從 模式比對概觀 開始。 完整的語言規則請參閱語言參考中的 屬性模式 與 位置模式 。

屬性與位置模式都測試價值的部分。 差別在於他們如何辨識這些部分:

  • 屬性模式會命名要測試的屬性或欄位。
  • 位置模式透過順序來識別價值。

解構會揭示一組有序的成分值。 元組本身就有元素順序;參見 解構元組。 另一種類型中,方法Deconstruct定義哪些元件值被暴露及其順序。

比較姓名與職位

以下性質模式測試兩個氣象讀數的命名性質,溫度值以攝氏度為單位:

static bool IsHotAndHumid(WeatherReading reading) =>
    reading is { TemperatureC: > 30, HumidityPercent: > 70 };

sealed record WeatherReading(int TemperatureC, int HumidityPercent);

以下位置模式測試訊號值後接布林值:

static string GetCrossingInstruction(
    PedestrianSignal signal, bool crossingIsClear) =>
    (signal, crossingIsClear) switch
    {
        (PedestrianSignal.Walk, true) => "Cross now",
        (PedestrianSignal.Walk, false) => "Wait for the crossing to clear",
        _ => "Wait for the walk signal"
    };

enum PedestrianSignal
{
    Stop,
    Walk
}

屬性模式以名稱 TemperatureC 和 HumidityPercent來識別其輸入。 交叉程式碼會從分開的 signal 和 crossingIsClear 值建立元組。 元組模式則依序識別這些值: signal 第一和 crossingIsClear 第二。 位置模式之所以強擬合,是因為這個新建立的元組只有兩個值,且它們的順序在交叉決策中具有明確的意義。

當成員名稱有助於說明測試時,請選擇屬性模式。 類別、結構與記錄的屬性模式通常較為清晰。 選擇位置模式時,順序已經賦予數值明顯意義。 位置模式在搭配元組時最有用,因為元組會將多個相關的值組合成一個具有固定順序的值。

以遞迴模式追蹤巢狀輸入

性質模式與位置模式是 遞迴模式:它們對每個屬性、域或位置套用另一種模式。 所選值成為該巢狀模式的輸入。

在 IsHotAndHumid 中,reading 運算式是屬性模式的輸入。 C# 在匹配前會評估該表達式。 圖案從所得物件中獲得兩個值:

  • 關係模式 > 30 會測試 TemperatureC 的值。
  • 關係模式 > 70 會測試 HumidityPercent 的值。

外部型別測試是可選的,遞迴模式子句也可以是空的。 例如,空屬性模式 { } 與任何非空值值相符。

性質與位置模式僅匹配非空值評估值。 當 null 屬於輸入域時,選擇一個遞迴模式,先檢查非空值:

static string DescribeNullableInput(object? value)
{
    if (value is not { } nonNullValue)
    {
        return "No value";
    }

    return nonNullValue switch
    {
        DateTime => "Date",
        string => "Text",
        _ => "Another type"
    };
}

輸入表達式為 value。 C# 會評估它,屬性 { } 模式會先測試所得值是否非空,然後將其指派給 nonNullValue。 接著,以下的切換表達式可以測試多種可能的執行時類型。 它 DateTime 和 string 型態模式沒有指定,因為方法只需識別每個型別,而不必捕捉其值。

當遞迴模式子句包含巢狀模式時,每個選擇的屬性、欄位或位置都會成為其巢狀模式的輸入。

當輸入表達式產生不同類型時,你可以在大括號前加一個型別測試。 你也可以使用成員路徑來測試巢狀屬性:

static string DescribeDate(object? value) =>
    value switch
    {
        DateTime { Date.DayOfWeek:
            DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
        DateTime => "Weekday date",
        null => "No date",
        _ => "Not a date"
    };

value 是輸入表達式。 C# 首先評估並測試所得值是否為 DateTime。 接著,Date 該屬性值會成為 DayOfWeek 成員存取的輸入。 最後,該 DayOfWeek 值成為測試兩個常數的邏輯模式的輸入。 當外部值具有指定型別,且沿成員路徑所需的所有物件皆非空時,匹配即告成功。

比較模式與分支語句

早期 DescribeDate 方法將四個結果表示為模式:

static string DescribeDate(object? value) =>
    value switch
    {
        DateTime { Date.DayOfWeek:
            DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
        DateTime => "Weekday date",
        null => "No date",
        _ => "Not a date"
    };

以下方法透過一系列命令式分支語句產生相同結果:

static string DescribeDateWithBranches(object? value)
{
    if (value is DateTime date)
    {
        if (date.DayOfWeek == DayOfWeek.Saturday ||
            date.DayOfWeek == DayOfWeek.Sunday)
        {
            return "Weekend date";
        }

        return "Weekday date";
    }

    if (value is null)
    {
        return "No date";
    }

    return "Not a date";
}

基於模式的版本則在多個分支測試值的類型與形狀時,將可能結果合併在一起。 命令式版本將每個測試與返回步驟明確化。 就其中一個狀況而言,兩種形式可能看起來相似。 隨著相關分支數量增加,模式能使替代方案更容易比較。

另請參閱