关系模式、逻辑模式和括号模式

Tip

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

关系模式和逻辑模式描述了范围、替代项和排除项。 以下方法将它们组合在一起,对温度进行分类:

static string ClassifyTemperature(int temperature) =>
    temperature switch
    {
        < 0 => "Below freezing",
        >= 18 and <= 24 => "Comfortable",
        (>= 0 and < 10) or > 30 => "Far outside the comfortable range",
        _ => "Cool or warm"
    };

表达式 temperature 是模式输入。 C# 只会对其求值一次,switch 的每个分支都会测试所得的值。 手臂演示了以下测试:

  • < 0 测试一个边界。
  • >= 18 and <= 24 测试一个范围。
  • (>= 0 and < 10) or > 30 测试两个可选范围。

本文同时展示了模式和命令式条件语句,以便你了解每种形式,并比较它们如何表达相同的决策逻辑。 单个条件在任一形式中看起来都类似。 模式可以通过保留其结果旁边的选项,使一系列相关分支更易于阅读。 选择使代码易于理解的表单。

将值与关系模式进行比较

关系模式使用 <、>、<= 或 >= 将其模式输入与编译时常量进行比较。 编译时常量是编译器在编译程序时可以计算的值。 数字和字符文本以及 const 兼容数字或字符类型的变量是具有代表性的示例。 普通变量、属性、方法调用和 static readonly 字段不是编译时常量。 在开头的示例中,>= 18 和 <= 24 都测试同一个求值后的 temperature 值。

同一关系符号可以出现在普通表达式或模式中。 以下示例使用具有温度的两种形式:

static void ShowExpressionAndPattern(int temperature, int threshold)
{
    bool belowThreshold = temperature < threshold;
    bool belowFreezing = temperature is < 0;

    string description = temperature switch
    {
        < 0 => "Freezing",
        0 => "Freezing point",
        > 0 => "Above freezing"
    };

    Console.WriteLine(
        $"Below threshold: {belowThreshold}; " +
        $"below freezing: {belowFreezing}; {description}");
}

temperature < threshold 是 关系表达式。 它计算两个操作数并生成一个 bool。 任一操作数可以是非常量表达式。

在 temperature 中,temperature is < 0 是模式输入表达式。 C# 会对其进行评估,关系模式 < 0 会测试生成的值。 在 switch 分支 < 0 => "Freezing" 中,switch 前面的表达式提供输入值,因此该模式只包含 < 0。

表达式可以与变量threshold进行比较temperature。 关系模式操作数必须是编译时常量,因此当比较值是变量时,请使用关系表达式。 当比较值为常量时,这两种形式都可行。

当右操作数为常量时,主要根据可读性来选择。 关系表达式通常只包含一次直接比较。 关系模式可与其他模式组合,当多个范围映射到切换结果时,它会自然而然地适应。

将条件与逻辑模式组合在一起

逻辑模式使用模式运算符and、or和not来组合或否定模式:

  • 当两个嵌套模式都匹配时,and 模式才会匹配。
  • or 模式会在任一嵌套模式匹配时匹配。
  • not 模式会在其嵌套模式失败时成功。

打开示例用于 and 描述范围和 or 描述替代项。 not模式可以排除值,如中status is not Status.Complete所示。 以下方法显示了这两种形式,以便可以了解其语法并比较它们表达相同测试的方式:

static bool IsWeekendPattern(DayOfWeek day) =>
    day is DayOfWeek.Saturday or DayOfWeek.Sunday;

static bool IsWeekendImperative(DayOfWeek day) =>
    day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;

该模式形式使用由 or 连接的两个常量模式。 命令式使用由条件 OR 运算符 || 连接的两个相等表达式。 这两种形式对于这一单一条件来说都简洁明了。 选择最适合周围代码的形式。 模式通常阐明了一个 switch示例中的多个相关选择,如开例所示。

模式运算符形成模式而不是布尔表达式: and 对应于模式结合、 or 模式替代和 not 模式求反。 布尔表达式使用&&和 ||!。 当多个模式替代项产生相同结果时,请选择 or。 表示排除的模式比列出每个接受的值更清晰时选择 not 。

用括号对模式进行分组

带括号模式使用括号来显示或更改嵌套模式的分组方式。 绑定决定运算符会将哪些模式操作数组合在一起,类似于未写出括号时的隐式分组。 C# 指定以下绑定顺序:

  1. not
  2. and
  3. or

以下测试接受优先级 1 到 3 或特殊优先级 9:

static bool IsAcceptedPriority(int priority) =>
    priority is (>= 1 and <= 3) or 9;

编译器在 or 之前对 and 进行分组。 括号使预期的分组明确,以及易于查看的两种替代方法:范围为 1 到 3 或 9。 为便于阅读,当某个模式同时混用 and 和 or 时,或当 not 适用于复合模式时,请使用括号。 括号还可以更改默认分组,如中所示 not (>= 1 and <= 3)。

未指定嵌套模式的运行时检查顺序,模式运算符遵循模式匹配规则,而不是短路布尔规则。 编写嵌套模式,使其结果独立于检查顺序。

将 when 防护用于单独的条件

当嵌套模式描述输入值本身时,逻辑模式最有效。 when 防护是 case 标签或切换分支上的附加布尔条件。 当决策还依赖于独立于模式输入的信息时,请使用防护。

以下警告取决于温度和单独的 isOutdoors 值:

static string GetHeatWarning(int temperature, bool isOutdoors) =>
    temperature switch
    {
        > 35 when isOutdoors => "High heat outdoors",
        > 35 => "High heat",
        _ => "No heat warning"
    };

关系模式 > 35 描述 temperature 输入。 防护 when isOutdoors 检查单独的值。 当条件需要方法调用或模式语法未明确表达的布尔表达式时,防护也更可取。

当关系模式和逻辑模式能更清晰地体现输入允许的形状或值时,请使用它们,尤其是在 switch 的多个分支中。 当普通布尔表达式能更简洁地直接表达条件时,请使用普通布尔表达式。 当切换选择取决于单独的值,或取决于更适合用布尔表达式表示的条件时,使用 when 防护。

另见