声明、常量和
模式应用于 输入表达式。 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 模式形式。
如果不需要捕获的值,请使用 放弃模式 _ ,而不是声明变量。