Tip
本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果模式不熟悉,请从 模式匹配概述 开始。 有关完整的语言规则,请参阅语言参考中的 属性模式 和 位置模式 。
属性和位置模式都测试值的各个部分。 区别在于它们如何识别这些部分:
- 属性模式命名要测试的属性或字段。
- 位置模式按其顺序标识值。
析构公开一组有序的组件值。 元组已具有元素顺序;请参阅 析构元组。 对于另一种类型,方法Deconstruct定义公开哪些组件值及其顺序。
比较名称和职位
以下属性模式测试天气读数的两个命名属性,温度值以摄氏度为单位:
static bool IsHotAndHumid(WeatherReading reading) =>
reading is { TemperatureC: > 30, HumidityPercent: > 70 };
sealed record WeatherReading(int TemperatureC, int HumidityPercent);
以下位置模式匹配一个信号值,后跟一个布尔值:
static string GetCrossingInstruction(
PedestrianSignal signal, bool crossingIsClear) =>
(signal, crossingIsClear) switch
{
(PedestrianSignal.Walk, true) => "Cross now",
(PedestrianSignal.Walk, false) => "Wait for the crossing to clear",
_ => "Wait for the walk signal"
};
enum PedestrianSignal
{
Stop,
Walk
}
属性模式通过名称 TemperatureC 和 HumidityPercent 来标识其输入。 crossing 代码根据单独的 signal 和 crossingIsClear 值创建一个元组。 然后,元组模式按顺序识别这些值:首先是 signal,其次是 crossingIsClear。 位置模式非常适用,因为这个新建的元组只有两个值,而且它们的顺序在交叉判断中具有明确含义。
当成员名称帮助解释测试时,请选择属性模式。 对于类、结构和记录,属性模式通常更清晰。 当顺序已经使这些值的含义一目了然时,请选择位置模式。 位置模式最适用于元组,这些元组将多个相关值合并为一个具有固定顺序的值。
沿递归模式跟踪嵌套输入
属性和位置模式是 递归模式:它们将另一种模式应用于他们选择的每个属性、字段或位置。 所选值将成为该嵌套模式的输入。
在 IsHotAndHumid 中,reading 表达式是属性模式的输入。 C# 在匹配之前计算该表达式。 该模式从生成的对象获取两个值:
- 关系模式
> 30用于测试TemperatureC的值。 - 关系模式
> 70用于测试HumidityPercent的值。
外部类型测试是可选的,递归模式子句可以为空。 例如,空属性模式 { } 与任何非 null 计算值匹配。
属性模式和位置模式仅匹配求值结果为非空的值。 当 null 属于输入域时,请选择先检查非 null 值的递归模式:
static string DescribeNullableInput(object? value)
{
if (value is not { } nonNullValue)
{
return "No value";
}
return nonNullValue switch
{
DateTime => "Date",
string => "Text",
_ => "Another type"
};
}
输入表达式为 value. C# 会对其进行求值,而 { } 属性模式会在将其赋给 nonNullValue 之前先测试所得值是否为非 null。 然后,以下 switch 表达式可以测试多个可能的运行时类型。 它 DateTime 和 string 类型模式没有指定,因为该方法只需要标识每个类型,而不需要捕获其值。
当递归模式子句包含嵌套模式时,每个选定的属性、字段或位置将成为其嵌套模式的输入。
当输入表达式可以生成不同类型的时,可以在大括号前添加类型测试。 还可以使用成员路径测试嵌套属性:
static string DescribeDate(object? value) =>
value switch
{
DateTime { Date.DayOfWeek:
DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
DateTime => "Weekday date",
null => "No date",
_ => "Not a date"
};
value 是输入表达式。 C# 首先计算它,并测试结果值是否为 DateTime。 然后,Date 属性值将成为 DayOfWeek 成员访问的输入。 最后,该值 DayOfWeek 将成为测试两个常量逻辑模式的输入。 当外部值具有指定的类型且成员路径上所需的每个对象为非 null 时,匹配会成功。
将模式与分支语句进行比较
前面的 DescribeDate 方法将四个结果表示为模式:
static string DescribeDate(object? value) =>
value switch
{
DateTime { Date.DayOfWeek:
DayOfWeek.Saturday or DayOfWeek.Sunday } => "Weekend date",
DateTime => "Weekday date",
null => "No date",
_ => "Not a date"
};
以下方法使用一系列命令性分支语句生成相同的结果:
static string DescribeDateWithBranches(object? value)
{
if (value is DateTime date)
{
if (date.DayOfWeek == DayOfWeek.Saturday ||
date.DayOfWeek == DayOfWeek.Sunday)
{
return "Weekend date";
}
return "Weekday date";
}
if (value is null)
{
return "No date";
}
return "Not a date";
}
当多个分支测试值的类型和形状时,基于模式的版本将可能的结果一起保留。 命令性版本使每个测试和返回步骤都明确。 对于某一种情况,这两种形式中的任一种看起来都可能很相似。 随着相关分支的数量的增长,模式可以使替代方法更易于比较。