C# Switch 新语法全解析:从语句到表达式的进化
C# Switch 新语法全解析:从语句到表达式的进化
前言
写过 C# 的同学对 switch 肯定不陌生。从 C# 1.0 开始,switch 语句就是处理多分支逻辑的标配。但从 C# 8.0 开始,switch 有了一个全新的面貌——它不再只是一个“语句”,还可以是一个“表达式”。这篇博文就来聊聊新旧语法的区别、反编译后是什么样子、实际开发中怎么用,以及底层的实现原理。
一、传统 switch 语句 (C# 7 及更早)
先回顾一下老朋友:
// 传统的 switch 语句
string result;
switch (score)
{
case 90:
result = "优秀";
break;
case 80:
result = "良好";
break;
case 70:
result = "中等";
break;
default:
result = "继续努力";
break;
}
传统语法的特点很鲜明:
- 必须写
break:每个 case 后面都要有break(或return),否则编译报错 - 不能直接返回值:需要先声明变量,在各分支赋值,最后才能用
- 模式匹配能力有限:主要做常量匹配,类型匹配等高级操作力不从心
二、Switch 表达式 (C# 8.0+)
C# 8.0 引入了 switch 表达式,语法焕然一新:
// switch 表达式
string result = score switch
{
90 => "优秀",
80 => "良好",
70 => "中等",
_ => "继续努力"
};
这不仅仅是“语法糖”,而是编程范式的变化:switch 从“语句”变成了“表达式”,意味着它可以直接求值、直接赋值、直接作为返回值。
三、新旧语法详细对比
| 特性 | 传统 switch 语句 | switch 表达式 |
|---|---|---|
| 本质 | 语句(Statement) | 表达式(Expression) |
| 返回值 | 间接(通过变量赋值) | 直接返回 |
break |
必须写 | 不需要 |
| 模式匹配 | 有限 | 全面支持 |
when 条件 |
支持 | 支持 |
| 多值合并 | case 1: case 2: |
1 or 2 => |
| 编译检查 | 可能漏掉分支 | 强制穷举,漏了会警告 |
3.1 关系模式:按范围匹配
传统写法只能用 if-else 处理范围,而表达式可以直接写:
string grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F"
};
3.2 类型模式:按类型分发
string Describe(object obj) => obj switch
{
int i => $"整数:{i}",
string s => $"字符串,长度:{s.Length}",
null => "空值",
_ => "其他类型"
};
3.3 属性模式:匹配对象属性
string Welcome(User user) => user switch
{
{ Age: < 18 } => "少年你好",
{ Age: >= 18 and <= 60 } => "成年人了",
{ Age: > 60 } => "长辈好",
_ => "未知年龄"
};
3.4 元组/位置模式
string Describe(Point p) => p switch
{
(0, 0) => "原点",
(var x, 0) => $"在 X 轴上,值:{x}",
(0, var y) => $"在 Y 轴上,值:{y}",
(var x, var y) => $"坐标:({x}, {y})"
};
四、反编译看看编译器做了什么
4.1 简单整数的 switch 表达式
对于取值连续的情况,编译器会生成 IL 的 switch 指令——这是一个跳转表,时间复杂度 O(1):
// C# 源码
int n = 2;
string result = n switch
{
1 => "One",
2 => "Two",
3 => "Three",
_ => "Other"
};
对应的 IL 会先把参数减 1,使得 case 值从 0 开始,然后直接用 switch 指令跳转。这种情况下性能极高。
4.2 取值不连续的情况
如果 case 值之间差距不大,编译器会用 default 分支填充空隙,仍然使用跳转表。但如果差距太大(比如 case 10、case 30、case 50),跳转表会造成大量浪费,此时编译器会退化为 beq(条件跳转),效果上类似 if-else if。
4.3 字符串的 switch 表达式
字符串的 switch 更有意思。较老的 C# 编译器会生成 Dictionary<string, int> 进行查找;而 Roslyn(新编译器)会使用一种特殊的哈希函数来计算字符串的哈希值,再使用跳转表。如果遇到哈希冲突,再逐字符串比较。
这意味着:对少量字符串,switch 表达式本质是哈希查找,比一连串 if-else 更快。
4.4 模式匹配的 IL 代码
类型模式和属性模式则更复杂,编译器会生成类似 is 类型检查加上条件判断的 IL 代码,实现运行时模式匹配。
五、实际开发场景与选择建议
5.1 何时用 switch 表达式
状态转换:把枚举转成字符串或另一枚举
string statusText = order.Status switch
{
OrderStatus.Pending => "待处理",
OrderStatus.Shipped => "已发货",
OrderStatus.Delivered => "已送达",
_ => "未知"
};
数据映射:把一个值映射到另一个值
string colorHex = colorName switch
{
"红色" => "#FF0000",
"绿色" => "#00FF00",
"蓝色" => "#0000FF",
_ => "#000000"
};
简化 if-else 链:超过 3 个条件时,switch 表达式可读性明显更好。
5.2 何时继续用传统 switch 语句
- 需要执行多行代码、副作用操作(写日志、调多个方法),而非单纯求值
- 需要
goto case跳转(虽然这类需求很少) - 团队项目统一风格,避免新旧混用造成困惑
5.3 避坑提醒
- 不要漏掉弃元
_:如果没有_分支且所有可能值没被全覆盖,编译器会警告。运行时若没匹配到,会抛SwitchExpressionException - 表达式要求所有分支返回相同类型:如果各分支返回类型不同,要么保证它们有公共基类,要么显式转换
- 顺序很重要:匹配从上到下执行,第一个匹配的生效,所以更具体的条件要放前面
六、底层原理小结
Switch 表达式的性能表现取决于场景:
- 整数 case 值密集 → 跳转表,O(1),极快
- 整数 case 值稀疏 → 退化为 if-else 链,O(n)
- 字符串 → 哈希计算 + 跳转表,通常比 if-else 快
- 类型/属性模式 → 编译为 is 检查和条件跳转,运行时动态匹配
理解这些原理,有助于在性能敏感的场景(比如高频调用的循环内部)做出合适的选择。不过大多数业务代码中,可读性远比这点性能差异重要。
总结
C# 的 switch 表达式是一次漂亮的进化:它把“做一件事”变成了“求一个值”。结合模式匹配能力,代码可以做到简洁、安全且富有表达力。下次写多分支逻辑时,不妨想想:这里能不能用一个 switch 表达式搞定?
更多推荐



所有评论(0)