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 | > | >= | < | <=

啊不,这在浑水摸鱼呢,orand 貌似是新东西,其他有些 不就是 传统布尔表达式 运算符?
No no no, 其细微差别是, 传统布尔表达式 里,像 > 这种是 二元运算符,有个很 Low 的名字 —— 运算
在 豪华版布尔表达式 里,它们 左边是光屁股 没有,有个很骚的名字 —— 模式匹配
对用户来说,其实差别不大,只知道这么折腾一把后,它就是 bool 啦,干净、舒坦。

Logical patterns
Relational patterns
参考 Declaration pattern

(2)异域风情 ==

好看的【来】(求值方式)

在 豪华版布尔表达式 里 ——左边是光屁股 没有—— 是整个 模式匹配 里的 至上心法

对于用户来说 得此 心法,在 道、法、术 体系加持下,模式匹配(装逼的)豪华外表 瞬间 轰然倒塌。

以这种方式来看,有些 花里胡哨的 模式匹配,无非就是 光屁股的更厉害一些,不写 == 运算符而已
(注意注意,这只是为 方便理解,主观概念上【硬塞进来】的一个等价物,不是官方说法啊)

  1. Constant pattern 【 == 】1 => 12.0m —— 让平凡的,归于平凡,但又不平凡
  2. Property pattern is { Year: 2020, Month: 5 } —— 我们 拉上平凡的手,一起不平凡
  3. Positional pattern 【 == 】(0, 0) => "Origin" —— 貌似 无名 property 比较呗,出场顺序很重要(这里的 小括号 不一样 哟)
  4. Parenthesized pattern input is not (float or double) —— 就是优先级控制呗(这里的 小括号 符合 九年制义务教育 直觉,即 常识)
  5. List patterns numbers is [1, 2, 3] —— 类似数组比较,花哨得紧呐 List patterns ,里面还有个 Slice Pattern .. 玩法

此处 is 是 豪华版的 == ,没有用 is 的地方,我给硬塞了个概念上的【 == 】

参考 Declaration pattern

(3)开小世界

好看的【去】(控制流分派)

模式匹配 并非 浪得虚名,其诡异之处,在改变了(貌似又没改变)控制流!

基于 类型运算(Type Pattern)而 开小世界,定义变量的是 Declaration Pattern
无条件,给 临时变量 命名的是 Var Pattern SimulateDataFetch(id) is var results
无条件,永恒弃用的变量是 Discard Pattern _

参考 Declaration 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, arraycollection 都是新的变量,只有当 类型运算 成立后,才会被定义,才会走这个 控制流 分支。
这里 _ 就是那个 永恒弃用的变量 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 分。

posted @ 2025-12-30 22:54  悠洋洋  阅读(8)  评论(0)    收藏  举报