模式比對總覽

模式 比對是一種技術,可讓您測試運算式以判斷它是否有特定特性。 C # 模式比對可提供更精簡的語法來測試運算式,並在運算式符合時採取動作。 「 is 運算式」支援模式比對來測試運算式,並有條件地將新變數宣告為該運算式的結果。 「運算式」可 switch 讓您根據運算式的第一個相符模式來執行動作。 這兩個運算式支援豐富的 模式詞彙。

本文提供您可以使用模式比對的案例總覽。 這些技術可提升程式碼的可讀性和正確性。 如需您可以套用之所有模式的完整討論,請參閱語言參考中的 模式 文章。

Null 檢查

模式比對最常見的其中一個案例是確保值不是 null 。 您可以使用下列範例測試並將可為 null 的實值型別轉換為其基礎類型 null

int? maybe = 12;

if (maybe is int number)
{
    Console.WriteLine($"The nullable int 'maybe' has the value {number}");
}
else
{
    Console.WriteLine("The nullable int 'maybe' doesn't hold a value");
}

上述程式碼是用來測試變數型別的宣告 模式 ,並將其指派給新的變數。 語言規則讓這項技術比其他許多技巧更安全。 變數 number 只能在子句的 true 部分存取和指派 if 。 如果您嘗試在其他地方(在子句中) else 或在區塊之後存取它, if 則編譯器會發出錯誤。 其次,因為您不是使用 == 運算子,所以當型別多載運算子時,這個模式就會運作 == 。 這讓它成為檢查 null 參考值的理想方式,也就是新增 not 模式:

string? message = "This is not the null string";

if (message is not null)
{
    Console.WriteLine(message);
}

上述範例使用 常數模式 來比較變數和 nullnot是當否定模式不相符時,所符合的 邏輯模式

型別測試

模式比對的另一種常見用法,是測試變數以查看它是否符合指定的型別。 例如,下列程式碼會測試變數是否為非 null,並實作為 System.Collections.Generic.IList<T> 介面。 如果有,則會使用 ICollection<T>.Count 該清單上的屬性來尋找中間索引。 null不論變數的編譯時間類型為何,宣告模式都不符合值。 null除了防止未執行的型別之外,下列程式碼也會受到保護 IList

public static T MidPoint<T>(IEnumerable<T> sequence)
{
    if (sequence is IList<T> list)
    {
        return list[list.Count / 2];
    }
    else if (sequence is null)
    {
        throw new ArgumentNullException(nameof(sequence), "Sequence can't be null.");
    }
    else
    {
        int halfLength = sequence.Count() / 2 - 1;
        if (halfLength < 0) halfLength = 0;
        return sequence.Skip(halfLength).First();
    }
}

您可以在運算式中套用相同的測試 switch ,以針對多個不同的類型測試變數。 您可以使用該資訊,根據特定的執行時間類型建立更好的演算法。

比較離散值

您也可以測試變數來尋找特定值的相符項。 下列程式碼顯示一個範例,其中會針對列舉中宣告的所有可能值測試值:

public State PerformOperation(Operation command) =>
   command switch
   {
       Operation.SystemTest => RunDiagnostics(),
       Operation.Start => StartSystem(),
       Operation.Stop => StopSystem(),
       Operation.Reset => ResetToReady(),
       _ => throw new ArgumentException("Invalid enum value for command", nameof(command)),
   };

先前的範例示範以列舉的值為基礎的方法分派。 最後 _ 一個案例是符合所有值的 捨棄模式 。 它會處理值不符合其中一個已定義值的錯誤狀況 enum 。 如果您省略 switch arm,編譯器會警告您未處理所有可能的輸入值。 在執行時間, switch 如果所檢查的物件不符合任何參數臂,則運算式會擲回例外狀況。 您可以使用數值常數,而不是一組列舉值。 您也可以針對代表命令的常數位串值使用這個類似的技巧:

public State PerformOperation(string command) =>
   command switch
   {
       "SystemTest" => RunDiagnostics(),
       "Start" => StartSystem(),
       "Stop" => StopSystem(),
       "Reset" => ResetToReady(),
       _ => throw new ArgumentException("Invalid string value for command", nameof(command)),
   };

上述範例顯示相同的演算法,但使用的是字串值,而不是列舉。 如果您的應用程式回應文字命令,而不是一般資料格式,您可以使用此案例。 在上述所有範例中, 捨棄模式 可確保您處理每個輸入。 編譯器可協助您確認每個可能的輸入值都已處理。

關聯式模式

您可以使用 關聯式模式 來測試值與常數的比較方式。 例如,下列程式碼會根據華氏的溫度來傳回水位的狀態:

string WaterState(int tempInFahrenheit) =>
    tempInFahrenheit switch
    {
        (> 32) and (< 212) => "liquid",
        < 32 => "solid",
        > 212 => "gas",
        32 => "solid/liquid transition",
        212 => "liquid / gas transition",
    };

上述程式碼也會示範組成 and 邏輯模式 ,以檢查兩種關聯式模式是否相符。 您也可以使用 disjunctive or 模式來檢查是否符合任何模式。 這兩種關聯式模式是以括弧括住,因此您可以使用任何模式來清楚清楚。 最後兩個參數 arm 會處理正在融化點和歸結遵循點的案例。 如果沒有這兩個 arm,編譯器會警告您邏輯不會涵蓋每個可能的輸入。

上述程式碼也會示範編譯器針對模式比對運算式提供的另一項重要功能:如果您未處理每個輸入值,編譯器會警告您。 如果交換器 arm 已由先前的交換器 arm 處理,編譯器也會發出警告。 這可讓您自由地重構和重新排序 switch 運算式。 另一種撰寫相同運算式的方式可能是:

string WaterState2(int tempInFahrenheit) =>
    tempInFahrenheit switch
    {
        < 32 => "solid",
        32 => "solid/liquid transition",
        < 212 => "liquid",
        212 => "liquid / gas transition",
        _ => "gas",
};

在這種情況下,還有任何其他重構或重新排序的重要課程,就是編譯器會驗證您是否已涵蓋所有輸入。

多重輸入

您目前為止看到的所有模式都是檢查一個輸入。 您可以撰寫模式來檢查物件的多個屬性。 請考慮下列 Order 記錄:

public record Order(int Items, decimal Cost);

上述的位置記錄型別會在明確的位置宣告兩個成員。 第一個顯示的是 Items ,然後是訂單的 Cost 。 如需詳細資訊,請參閱 記錄

下列程式碼會檢查項目數和訂單的值,以計算折扣價格:

public decimal CalculateDiscount(Order order) =>
    order switch
    {
        (Items: > 10, Cost: > 1000.00m) => 0.10m,
        (Items: > 5, Cost: > 500.00m) => 0.05m,
        Order { Cost: > 250.00m } => 0.02m,
        null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
        var someObject => 0m,
    };

前兩個 arm 會檢查的兩個屬性 Order 。 第三個只會檢查成本。 下一次對 null 進行的檢查,最後會符合任何其他值。 如果 Order 類型定義了適當的 Deconstruct 方法,您可以省略模式的屬性名稱,並使用解構來檢查屬性:

public decimal CalculateDiscount(Order order) =>
    order switch
    {
        ( > 10,  > 1000.00m) => 0.10m,
        ( > 5, > 50.00m) => 0.05m,
        Order { Cost: > 250.00m } => 0.02m,
        null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
        var someObject => 0m,
    };

上述程式碼示範針對運算式解構屬性的位置 模式

本文提供您可以使用 c # 中的模式比對來撰寫之程式碼類型的教學課程。 下列文章顯示在案例中使用模式的更多範例,以及可供使用的完整模式詞彙。

另請參閱