類型模式

Tip

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

型別模式會套用至輸入運算式。 C# 會評估表達式,然後測試所得值是否非空值,且其執行時型別是否與指定型別相容。 型態模式僅報告型別測試是否成功。 它不會宣告變數。

提出一個是非題

在配送系統中,IRouteStop 是路線規劃所接受之目的地的能力合約。 CanRoute 測試評估的目的地是否具備該能力,其布林結果決定目的地是否進入路線規劃工作流程。 此工作流程只需要一個是或否的答案,且不會讀取任何 route-stop 成員,因此不含已擷取變數的型態模式很適合。

static bool CanRoute(object? destination) =>
    destination is IRouteStop;

輸入運算式為 destination,而 IRouteStop 是正在測試的類型。 當答案只有「是」或「否」時,選擇一種類型模式。 若相符的分支需要讀取位址,或透過 IRouteStop 呼叫另一個成員,請改為選擇 宣告模式,讓該分支具有該型別的變數。

Note

你也可能看到 destination is IRouteStop _。 該語法是一種宣告模式, _ 意即不保留任何變數。 當兩種形式皆有效時,它執行相同的類型測試,但 destination is IRouteStop 更直接地陳述僅測試意圖。

比對類別與介面

在交付系統中, IRouteStop 定義了路由規劃目的地的能力合約,而 RouteStop 則是一個提供共通路由停止資料與實作的基底類別。 ExpressRouteStop 是從 RouteStop衍生出來的專門類別。 範例測試一個評估過的值,使其輸出能展示三種相容類型。 每個測試只需一個布林結果,因此不需要捕捉變數。

interface IRouteStop { }

abstract class RouteStop(string street) : IRouteStop
{
    public string Street { get; } = street;

    public string GetDisplayName() => Street;
}

sealed class ExpressRouteStop(string street) : RouteStop(street)
{
}

static void ShowCompatibility()
{
    object destination = new ExpressRouteStop("8 Oak Avenue");

    Console.WriteLine($"Exact class: {destination is ExpressRouteStop}");
    Console.WriteLine($"Base class: {destination is RouteStop}");
    Console.WriteLine($"Interface: {destination is IRouteStop}");
}

評估值為 ExpressRouteStop。 輸出顯示,它符合其確切類別、其 RouteStop 基底類別,以及 RouteStop 所實作的 IRouteStop 介面。

如需詳細的相容性規則與邊緣案例,請參閱型態模式參考。

路線類型

在支援系統中, SupportRequest 是專門請求類別(如 PasswordResetRequest 和 BillingQuestion)的基底類別。 切換臂將圖案與結果配對,當該圖案匹配時返回。 每個臂回傳一個處理佇列的名稱,最後一條臂則提供其他評估值的備援佇列。 所選佇列僅依賴於評估值的執行時型別,因此型別模式適合,因為不會讀取請求成員。

static string RouteRequest(object request) =>
    request switch
    {
        PasswordResetRequest => "Identity queue",
        BillingQuestion => "Billing queue",
        SupportRequest => "General support queue",
        _ => "Intake queue"
    };

每個分支都會回答一個類型問題,並回傳處理該請求的佇列。 如果 ARM 需要讀取請求成員,可以使用宣告模式來捕捉變數中的匹配值。

開關臂從上到下都被考慮。 將特化類別置於其基底類別之前。 否則,基礎類別的手臂可以匹配專門類別的每個實例,這使得後面的手臂無法觸及。

可選:使用型態參數作為測試型別

這個可選範例建立在 通用型別與方法之上。 像 這樣的TRequest型別參數是呼叫者提供的型別的佔位符。 一個進來請求批次可以包含多種請求類型,呼叫者會提供 ConfidentialRequest 測試是否有請求需要保密處理。 布林結果會選擇整個批次的機密處理,並產生可見的狀態訊息。 帶有型別參數的型別模式適合,因為只有匹配請求的存在才重要,因此不需要保留匹配的物件。

static void ShowConfidentialBatchHandling()
{
    object[] incomingRequests = [new BillingQuestion(), new ConfidentialRequest()];
    bool requiresConfidentialHandling =
        ContainsRequestOfType<ConfidentialRequest>(incomingRequests);

    Console.WriteLine(requiresConfidentialHandling
        ? "Send the entire batch to confidential handling."
        : "Send the batch to standard handling.");
}

static bool ContainsRequestOfType<TRequest>(IEnumerable<object> requests)
{
    foreach (object request in requests)
    {
        if (request is TRequest)
        {
            return true;
        }
    }

    return false;
}

如果呼叫者本身需要匹配請求,則使用回傳匹配項目的搜尋或篩選操作會更為適當。

另請參閱