类型模式

Tip

本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果模式不熟悉,请从 模式匹配概述 开始。 有关完整的兼容性规则,请参阅语言参考中的 声明和类型模式 。

类型模式应用于输入表达式。 C# 计算表达式,然后测试生成的值是否为非 null,并且其运行时类型与指定类型兼容。 类型模式仅报告类型测试是否成功。 它不声明变量。

提出一个是非题

在交付系统中,IRouteStop 是被路径规划接受的目的地能力契约。 CanRoute 测试评估的目标是否具有该功能,并且其布尔结果确定目标是否进入路由规划工作流。 该工作流只需要一个是或否的答案,并且不会读取任何 route-stop 成员,因此不带捕获变量的类型模式很合适。

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

输入表达式是 destination,并且 IRouteStop 是要测试的类型。 当答案为“是”或“否”时,请选择类型模式。 如果匹配的分支需要读取地址或调用另一个成员 IRouteStop,请改为选择 声明模式 ,以便分支具有该类型的变量。

注释

你可能还会看到 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)的基类。 切换臂将模式与结果配对,以在模式匹配时返回。 每个 arm 返回处理队列的名称,最后一个 arm 为其他评估的值提供回退队列。 所选队列仅取决于已求值的值的运行时类型,因此类型模式适用,因为未读取任何请求成员。

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

每个分支回答一个类型问题,并返回处理该请求的队列。 如果 arm 需要读取请求成员,请使用声明模式捕获变量中的匹配值。

开关臂按从上到下的顺序考虑。 将专用类置于其基类之前。 否则,基类 arm 可以匹配专用类的每个实例,这使得后面的 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;
}

如果调用方需要匹配请求本身,则返回匹配项的搜索或筛选操作将更合适。

另见