声明、常量和 var 模式

Tip

本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果以前未使用 C# 模式,请从 模式匹配概述 开始。 有关完整的语言规则,请参阅 模式参考。

模式应用于 输入表达式。 C# 计算表达式,然后测试模式或捕获生成的值。 声明、常量和 var 模式回答三个实际问题:

  • 声明模式: 表达式是否生成兼容运行时类型的非 null 值? 如果是,请声明该值的变量。
  • 常量模式: 表达式是否生成了一个特定的常量值?
  • 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 是测试的类型。 当评估的值为非 null 且其运行时类型与 decimal 兼容时,模式匹配。
  • amount 是指定名称。 当模式匹配时,它会声明 amount 并向其分配十进制值。

编译器跟踪局部变量在代码读取值之前是否接收值。 这种跟踪机制称为 确定赋值。 在 if 块内,编译器知道 amount 已被赋值,因为只有在模式匹配时,该代码块才会运行。 如果你的代码试图在 amount 块外访问 if,编译器就会报错。 如果 value 不是 decimal 值,则不会将变量 amount 赋给值。

当匹配分支需要将结果用作测试类型时,请选择声明模式。 它将类型测试、类型转换和变量声明结合在一起,从而避免重复编写同一表达式或单独编写一条类型转换。

当一个表达式可能生成多种有用的类型时,还可以使用声明模式:

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.Start、Command.Stop 和 Command.Pause 是它的枚举成员,因此每个 switch 分支都使用常量模式来测试一个已命名的命令值。

当命令可能有多种明确不同的含义时,请选择此项。 开关臂将值及其结果保存在一起。 对于简单的相等比较,像 if 这样的 if (command == Command.Start) 语句通常更容易阅读。

常量模式匹配使用内置语言相等规则,而不是用户定义的 == 运算符。 有关详细相等性和转换规则,请参阅 常量模式参考。

null 常量模式可用于进行可靠的空值检查:

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

在检查 null 状态时,选择 is null 或 is 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# 对该方法调用进行求值,并由 var days 将所得的估计结果捕获为 days,而不测试其类型或值。 当防护措施奏效时,预计需要一到两天。 分支结果需要使用捕获的值来报告实际天数。

不能在开关臂模式与其 when 防护之间声明普通局部变量。 在结果中再次调用 EstimateDays(express) 将重复计算。 如果代码已匹配,并且防护和结果都需要计算的中间值,请选择此 var 模式形式。

如果不需要捕获的值,请使用 放弃模式 _ ,而不是声明变量。

另见