关系模式与逻辑模式
本教程共 100 篇 · 第 88 篇 · 更新于 2026-07-31 · 约 7 分钟阅读
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不加括号容易踩优先级坑。
not比and先算,若想”非(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 => "空"
};
比起 != null,is 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守卫搭配,处理”同一枚举值但状态不同”的细分情况。
模式可读性建议
模式强大,但别炫技。几条让代码好维护的准则:
- 复杂条件加括号,明确优先级,别让同事猜。
- 关系模式从大到小(或从小到大)连续排,避免分支互相”挡住”。
- 超过三层嵌套的模式,考虑拆成中间变量或普通 if。
- 用
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 链”的武器。多写几次就顺手了。
它们到底解决了什么
把三类常见痛点对号入座:
- 比大小、判区间 → 关系模式
>= 60。 - 多值合并、取反、区间 → 逻辑模式
or/and/not。 - 边界是运行期变量 → 回到
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留给必须运行时计算的部分。