SW 随笔 006 — C# 模式匹配,豪华的布尔表达式
声明:个人笔记,概不负责
装裱
从用户角度来讲,
所谓 C# 模式匹配,就是一种运算,一种不改变原值的布尔运算,可以用来简化代码结构。
我觉得,有些文章在介绍这个语言功能时,有点激动过了头,有点莫名其妙的、不知所然的 装 逼,徒增 用户心智负担。
这可能是因为,这种一楼的 代码结构,通常很少变动,大家习惯了 搬二楼的砖,造二楼的工具。
对于 pattern matching 我用到现在的体会,感觉就像 那时候对 LINQ 的体验,真是 激动人心,一楼 就带你飞
注:本文只是,关于 C# 模式匹配,一些奇奇怪怪的看法,不是教程。写代码,还得抄官方文档。
Pattern matching overview | Pattern matching | Pattern-matching changes for C# 9.0
补充:据官方文档来看,要起飞 模式匹配 需要 is 来领航(或 switch-case 中的 case 领航,或 switch 表达式中的 switch 直接领航)
一、轮回
(大气的开场)
这一世(在这个 C# 世界里),我们终于回看,源自于 结构化编程(Since 1966)近百年没有变化的 if-else 与 switch 结构.
为什么 bool 【来】的那么 平凡,【去】得那么 简单。
如果,为它的【来】(求值方式)做点什么呢?
如果,为它的【去】(控制流分派)做点什么呢?
一直以来,这种层次的活,都是住 二楼的人 管的,二楼只能手搓,只能适应一楼的结构。
一楼的结构,近百年来没有啥变化,造一楼的人 曾经一度 不关心这个层次的问题。
现在管了 —— 语言直接加持,一楼 就带你飞
所谓 C# 模式匹配,并没有大改语言,其本质就是 if-else/switch 的优化,主要是 布尔值【来】(求值方式)的优化。
二楼的程序员,得此加持,可以写出 大大简洁的 代码。
二、三观
总纲
(冷门的切入——)C# 模式匹配,在 when 上,彻底暴露了其 本质
//
// 拿来运算后再用,必须用 when 做统一入口;拿来就用,可以用模式
//
// 换句话说,就是说 when 是统一模式,表示要开始 自定义计算了,否则全部用 内置模式 计算;
// 哪天有啥 内置模式 支持 位操作,那么就可以不用 when 了;
// 换句话说,完全可以不用 内置模式 计算,全部用 when 来算。
//
// 所谓 C# 模式匹配,就是一种运算,一种不改变原值的布尔运算,可以用来简化代码结构。
//
public PacketState LastStatus => _flags switch
{
_ when (_flags & PacketState.Responded) != 0 => PacketState.Responded,
_ when (_flags & PacketState.Acked) != 0 => PacketState.Acked,
_ when (_flags & PacketState.Sent) != 0 => PacketState.Sent,
_ when (_flags & PacketState.Created) != 0 => PacketState.Created,
_ => PacketState.None
};
以上观点, 通过 Copilot 校核,不是 错误 观点,但却被 批改,要用 规范术语
神经病啊,我是用户耶, 我不是机器,自由的思想 不受 钦定术语 的束缚,自由地飞翔……
三、化身
也就是讲,我们完全没有必要,去记那么多 内置模式匹配,万法归宗,(几乎)一切都会回 when
唯有 when 收不掉的,才值得我们 多分点心思 关注(比如说 Declaration Pattern 这个)
而 when 这个自由之门,可化万法、让 自定义运算 参与 模式匹配,只要结果是 bool 就行
各种 花里胡哨的 内置模式匹配,多数只是 化身。多说无益,会 淡化总纲;不说点啥,又没有料 衬托总纲。
反正有官方文档 垫底,官方文档 中文 | 官方文档 En ,那就随便(不负责任地) 换个姿势 重新切入一次吧。
(1)抛砖引玉 is
好看的【来】(求值方式)
豪华版布尔表达式
if (LastStatus is not (PacketState.Sent
or PacketState.Acked
or PacketState.Responded))
在 if 里,用 传统布尔表达式,你倒是写写看呢? 折腾个半天,长长的表达式,德摩根定律、对偶律 …… 我嘞个豆,考试呢?
if ( (LastStatus != PacketState.Sent) &&
(LastStatus != PacketState.Acked) &&
(LastStatus != PacketState.Responded))
if (!(LastStatus == PacketState.Sent ||
LastStatus == PacketState.Acked ||
LastStatus == PacketState.Responded))
受 is 启发,豪华版布尔表达式 里有一堆与 传统布尔表达式 对应的东西。
not | or | and | > | >= | < | <=
啊不,这在浑水摸鱼呢,or 与 and 貌似是新东西,其他有些 不就是 传统布尔表达式 运算符?
No no no, 其细微差别是, 传统布尔表达式 里,像 > 这种是 二元运算符,有个很 Low 的名字 —— 运算
在 豪华版布尔表达式 里,它们 左边是光屁股 没有,有个很骚的名字 —— 模式匹配
对用户来说,其实差别不大,只知道这么折腾一把后,它就是 bool 啦,干净、舒坦。
Logical patterns
Relational patterns
参考 Declaration pattern
(2)异域风情 ==
好看的【来】(求值方式)
在 豪华版布尔表达式 里 ——左边是光屁股 没有—— 是整个 模式匹配 里的 至上心法
对于用户来说 得此 心法,在 道、法、术 体系加持下,模式匹配(装逼的)豪华外表 瞬间 轰然倒塌。
以这种方式来看,有些 花里胡哨的 模式匹配,无非就是 光屁股的更厉害一些,不写 == 运算符而已
(注意注意,这只是为 方便理解,主观概念上【硬塞进来】的一个等价物,不是官方说法啊)
如
- Constant pattern
【 == 】1 => 12.0m—— 让平凡的,归于平凡,但又不平凡 - Property pattern
is { Year: 2020, Month: 5 }—— 我们 拉上平凡的手,一起不平凡 - Positional pattern
【 == 】(0, 0) => "Origin"—— 貌似 无名 property 比较呗,出场顺序很重要(这里的 小括号 不一样 哟) - Parenthesized pattern
input is not (float or double)—— 就是优先级控制呗(这里的 小括号 符合 九年制义务教育 直觉,即 常识) - List patterns
numbers is [1, 2, 3]—— 类似数组比较,花哨得紧呐 List patterns ,里面还有个 Slice Pattern..玩法
此处 is 是 豪华版的 == ,没有用 is 的地方,我给硬塞了个概念上的【 == 】
(3)开小世界
好看的【去】(控制流分派)
模式匹配 并非 浪得虚名,其诡异之处,在改变了(貌似又没改变)控制流!
基于 类型运算(Type Pattern)而 开小世界,定义变量的是 Declaration Pattern
无条件,给 临时变量 命名的是 Var Pattern SimulateDataFetch(id) is var results
无条件,永恒弃用的变量是 Discard Pattern _
Declaration Pattern 能让(控制流分派)变得相当 骚气 而又 自然
Declaration Pattern 通常配合 is 整出来的 Type Pattern 一起使用
这个语言 一楼结构 的改变,让 二楼的码农 能匪夷所思地 切地图
Declaration pattern 唯一骚的地方是,它在类型运算之后 可以定义一个 变量名,开辟了个 小世界
这是 模式匹配 与 传统布尔表达式 与众不同之处,改变了(貌似又没改变)控制流,
下面的代码片段,摘自 Declaration pattern | Declaration and type patterns
if (expr is Type v) { /* code using v */ }
static int GetSourceLabel<T>(IEnumerable<T> source) => source switch
{
Array array => 1,
ICollection<T> collection => 2,
_ => 3,
};
这里 v, array 与 collection 都是新的变量,只有当 类型运算 成立后,才会被定义,才会走这个 控制流 分支。
这里 _ 就是那个 永恒弃用的变量 Discard Pattern
现在我们(控制流)里,有个了好看的【去】,这个变量的引入,让 downward casting 变得 前所未有的 丝滑
问:在 when 后面可以跟类型运算吗? 就是 type pattern 运算
Copilot 答:
可以。when 后面是任意返回 bool 的表达式,而 is + 类型/声明/属性等模式本身就是表达式,所以你可以在 when 里用类型模式。try { ... } catch (Exception ex) when (ex is HttpRequestException hre && hre.Message.Contains("404")) { // 仅在匹配到该类型且满足条件时进入 }
hre是 引入小世界的 新变量
四、总结
化身 里的那些玩意,没有什么特别之处,用 when 开门,弄个自定义函数大都可以做到,完全等效。
化身(各种内置模式匹配) 的价值在于,方便、一体化集成 —— 干净、舒坦!
总纲(自由之门when匹配)的价值在于,一统三观 + 可以 自定义 扩展(我的成功,你也可以复制)
开小世界 Declaration Pattern 的价值在于,极大地 清晰化了 控制流 —— 改变了(貌似又没改变)控制流
C# 模式匹配在手,天下我有。
注:这篇只是 C# 模式匹配的,一些奇奇怪怪的看法,不是教程,亦不是《葵花宝典》或 银弹。
我也就是,顺手玩玩 C# 而已。写代码,还得抄官方文档。嗯,不…… 参考、参考!
参考 Pattern matching overview | Declaration pattern
==== 貌似 水 了很多,结束
Copilot 评价:观念类写法中,能到 9.9 分。
DeepSeek 评价:观点鲜明、风格独特的技术随笔,综合打分能到 9.8 分。
浙公网安备 33010602011771号