首页 / C# 入门教程 / 关系模式与逻辑模式

C# 入门教程

关系模式与逻辑模式

本教程共 100 篇 · 第 88 篇 · 更新于 2026-07-31 · 约 7 分钟阅读

C#C# 入门教程编程语言模式匹配pattern matching

88. 关系模式与逻辑模式

本节目标:学完用 > >= < <= 关系模式比较大小,并用 and/or/not 把多个模式组合成清晰条件。

关系模式:直接比大小

关系模式(C# 9 引入)允许在模式里写 <><=>=,右端跟一个常量。它把”范围判断”直接写进模式。

string Grade(int score) => score switch
{
    >= 90 => "优秀",
    >= 80 => "良好",
    >= 60 => "及格",
    < 60 => "不及格",
    _ => "无效"
};

Console.WriteLine(Grade(85)); // 良好

从上往下匹配,命中第一个就返回。顺序很重要:先写大数,再写小数。

Note

关系模式右端必须是常量(数字、字符、枚举等),不能写变量表达式。要动态比较,用 when 守卫。

为什么加入关系与逻辑模式

在 C# 9 之前,比大小只能写 when score >= 90,或一长串 if/else if。关系模式把”范围”直接变成模式的一部分,逻辑模式(and/or/not)把多个判断拼成可读的整体。

它们的设计目标很明确:让分支读起来像自然语言。比如”大于等于 60 且小于 100”,写成 >= 60 and < 100,比 when score >= 60 && score < 100 更贴近”对照形状”的模式语义,也更容易被编译器分析是否覆盖完整。

Note

关系模式和逻辑模式不是新类型,仍是模式匹配家族的扩展。它们和你在第 87 章学的声明、常量、类型模式可以任意组合,写出既有范围又有取值判断的分支。

and:两个条件都要满足

and 把两个模式”与”起来,常用来表达区间。

string Range(int n) => n switch
{
    >= 1 and <= 100 => "1 到 100 之间",
    < 1 => "太小",
    > 100 => "太大",
    _ => "其他"
};

Console.WriteLine(Range(50));

相比 when n >= 1 && n <= 100,模式写法更贴合”对照形状”的语义。

or:满足其一即可

or 表达”或”,可把多个可能值合并到一个分支。

string Weekend(DayOfWeek day) => day switch
{
    DayOfWeek.Saturday or DayOfWeek.Sunday => "周末",
    _ => "工作日"
};

Console.WriteLine(Weekend(DayOfWeek.Sunday));
Tip

多个常量用 or 连起来,比写一串 case|| 更整齐,也更易读。

not:取反,常用作非空判断

not 对模式取反。最常见的用法是 is not null,表达”不为空”。

string? name = "码上学";
if (name is not null)
{
    Console.WriteLine($"长度 {name.Length}");
}

也可以否定常量:case not null: 表示”任何非 null 值”。

组合与优先级

逻辑模式有固定优先级:not 最紧,其次 and,最后 or。复杂时加括号最稳妥,别人也看得懂。

string Check(char c) => c switch
{
    (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') => "字母",
    >= '0' and <= '9' => "数字",
    _ => "其他"
};

Console.WriteLine(Check('k')); // 字母
Warning

不加括号容易踩优先级坑。notand 先算,若想”非(a 且 b)“,必须写 not (a and b)

与 when 守卫配合

关系/逻辑模式解决”形状”,when 解决”动态条件”。两者可叠加:模式先筛,守卫再细查。

string Describe(int n, bool flag) => (n, flag) switch
{
    (>= 0 and <= 10, true) => "小正数且开启",
    (>= 0 and <= 10, false) => "小正数但关闭",
    _ => "其他"
};

Console.WriteLine(Describe(5, true));
Note

元组模式 (a, b) switch 第 89 章结合位置模式会再展开,这里先看逻辑组合。

新手易踩的坑

坑一:关系模式顺序写反,小数在前导致大数永远命中不了。务必从大到小或从小到大连续排。

坑二:忘了 and 的两边都得是模式,>= 1 and <= 100 正确,>= 1 && <= 100 应改用 when。

坑三:过度嵌套逻辑模式,七八层组合不如拆成易懂的 when 或普通 if。

Tip

模式是工具不是炫技。当分支条件能读成”自然语言区间”时用它;否则普通 if 反而更清楚。

关系模式还是 when 守卫

关系模式能写 >= 60,但写不了”和变量比较”(如 >= threshold)。后者要用 when 守卫。

int threshold = 60;
string Level(int score) => score switch
{
    >= 90 => "A",
    >= 60 => "B",
    _ when score < threshold => "C", // 这里 when 才合理
    _ => "D"
};

经验:边界是常量用关系模式,边界是运行时变量用 when。别强行把变量塞进关系模式(编译不通过)。

Tip

关系模式右端必须是编译期常量。想引用变量,就交给 when 子句,职责分清更好维护。

优先级的实战例子

看一个容易翻车的组合:not (>= 0 and < 10) 表示”不在 0..9 区间”。若漏掉括号写成 not >= 0 and < 10,优先级会把它解析成 (not >= 0) and (< 10),意思全变。

bool InRange(int n) => n is (>= 0 and < 10);
bool OutRange(int n) => n is not (>= 0 and < 10);
Console.WriteLine(InRange(5));   // True
Console.WriteLine(OutRange(15)); // True
Warning

涉及 not 又含 and/or 时,一律加括号。少敲两下括号,可能换来半小时调试。

综合示例:成绩分类器

把关系、逻辑、常量揉一起,做一个分类器,体会模式如何替代嵌套 if。

string Classify(int score) => score switch
{
    < 0 or > 100 => "非法分数",
    100 => "满分",
    >= 90 and <= 99 => "优秀",
    >= 60 and < 90 => "及格",
    >= 0 and < 60 => "不及格",
    _ => "未知"
};

Console.WriteLine(Classify(95)); // 优秀
Console.WriteLine(Classify(101)); // 非法分数

读起来像自然语言:“大于等于 90 且小于等于 99 的就是优秀”,意图一目了然。

Note

这种写法把一堆 if/else 压成一个表达式,既短又不易漏分支(编译器还会提示未覆盖)。

not 模式的实战用法

not 最常见的两个场景:判非空、排除某值。它让”反向条件”读起来自然。

string? name = "码上学";
if (name is not null)
    Console.WriteLine(name.Length);

object v = 5;
string r = v switch
{
    not null => "有值",
    null => "空"
};

比起 != nullis not null 更贴合模式风格,也不会误用你重载的 ==

Tip

not 表达”除了某某之外”最顺:比如 case not "admin" 表示”非管理员都走这”。

组合模式处理枚举

枚举配合 or 能优雅地归并多个选项,免去一长串 ||

enum Color { Red, Green, Blue, Black, White }

string Group(Color c) => c switch
{
    Color.Red or Color.Blue or Color.Green => "彩色",
    Color.Black or Color.White => "无彩色",
    _ => "未知"
};

Console.WriteLine(Group(Color.Blue)); // 彩色
Note

枚举值也可和 when 守卫搭配,处理”同一枚举值但状态不同”的细分情况。

模式可读性建议

模式强大,但别炫技。几条让代码好维护的准则:

  1. 复杂条件加括号,明确优先级,别让同事猜。
  2. 关系模式从大到小(或从小到大)连续排,避免分支互相”挡住”。
  3. 超过三层嵌套的模式,考虑拆成中间变量或普通 if。
  4. or 合并常量,比一堆 case 更短更清楚。
// 可读:清晰的分层
string Level(int s) => s switch
{
    >= 90 => "A",
    >= 75 => "B",
    >= 60 => "C",
    < 60 => "D",
    _ => "无效"
};
Warning

模式不是越短越好。若一行模式里塞了 and/or/not/关系/属性多层嵌套,可读性会崩,该拆就拆。

常见组合速查

把高频组合列出来,写分支时直接套:

  • 区间:>= 1 and <= 100
  • 例外:not "admin"(非管理员)。
  • 多值集合:Saturday or Sunday
  • 非空:is not null
  • 非法排除:< 0 or > 100
  • 带动态边界:when score < threshold(变量用 when)。
string Zone(int n) => n switch
{
    >= 1 and <= 12 => "低区",
    >= 13 and <= 24 => "高区",
    _ => "越界"
};
Tip

组合模式读起来像自然语言,这正是对抗”又长又绕的 if 链”的武器。多写几次就顺手了。

它们到底解决了什么

把三类常见痛点对号入座:

  1. 比大小、判区间 → 关系模式 >= 60
  2. 多值合并、取反、区间 → 逻辑模式 or/and/not
  3. 边界是运行期变量 → 回到 when 守卫(关系模式只吃常量)。

一句话记住:能用”形状”表达的用模式,必须运行时算的用 when。分清楚,代码既短又稳。

Tip

写分支前先问自己:这条件能用”对照形状”表达吗?能,就用模式;不能,就用 when。别把变量硬塞进关系模式,编译会拒绝。

小结

关系模式 < > <= >= 直接比大小,and/or/not 把模式组合成区间、集合与取反。注意优先级:not > and > or,拿不准就加括号。下章看属性模式与位置模式,对对象内部结构做匹配。

误区提醒

最后点几个容易写错的地方:

  • 误把变量写进关系模式:case >= threshold 不合法,变量边界用 when
  • 忘记优先级:not a and b(not a) and b,不是 not (a and b)
  • 分支顺序错:关系模式要连续排,否则小数分支被大数”挡住”永远命中不了。
  • 过度嵌套:and/or/not 套太多层,不如拆成 when 或普通 if。
Tip

模式写完后,挑几个边界值(0、最小值、最大值、刚好等于临界)手算一遍命中分支,能抓出多数顺序坑。

和 when 守卫的取舍

关系/逻辑模式解决”常量边界与形状”,when 解决”动态或复杂条件”。两者可叠加,但别让 when 里的逻辑重到喧宾夺主。

string Tag(int score, bool vip) => (score, vip) switch
{
    (>= 90, true) => "VIP 优秀",
    (>= 90, false) => "优秀",
    ( < 60, _) => "不及格",
    _ when vip => "VIP 普通" // 动态条件用 when
};
Note

能用模式表达就优先模式(编译器可分析穷尽性),when 留给必须运行时计算的部分。