模式匹配:声明/类型/常量
本教程共 100 篇 · 第 87 篇 · 更新于 2026-07-31 · 约 7 分钟阅读
87. 模式匹配:声明/类型/常量
本节目标:学完用 is 类型模式和 switch 模式替代繁琐的类型判断与多重分支。
什么是模式匹配
模式匹配(pattern matching)是”拿一个值去对照形状”的语法。对照成功就取出信息,失败就走别的路。它让类型判断、分支选择更紧凑。
早期 C# 判断类型得先 is 再强制转换,啰嗦。模式匹配把”判断 + 转换 + 取值”合成一步。
Note本章讲声明模式、类型模式、常量模式(C# 7–8 引入)。关系与逻辑模式留到第 88 章,属性与位置模式在第 89 章。
为什么要有模式匹配
光看语法可能觉得”不就是个 if 吗”。它的真正价值在两点:把”判断 + 取数”合成一步,以及让分支更短更稳。
打个比方:传统写法像你进快递站,先问”这是我的包裹吗”(is),对方确认后,你还得自己翻标签找收件人名字(强制转换)。模式匹配像工作人员直接把包裹递给你,说”是你的,收件人写的是码上学”——判断和取信息一次完成。
C# 7 之前没有这能力,写类型判断常出现 if (obj is Type) { var t = (Type)obj; ... } 的重复代码。模式匹配就是为消灭这类重复而生。
Note模式匹配不改变程序能做什么,只改变你怎么写。它让”按数据形状分派”读起来更像人话,也更容易被编译器检查是否覆盖所有情况。
声明模式:判断并取出
声明模式写法:表达式 is 类型 变量。若类型匹配,变量被赋值,且可直接在条件里用。
object obj = "hello";
if (obj is string text)
{
Console.WriteLine($"是字符串,长度 {text.Length}");
}
这里 text 在匹配成功后才存在,省去单独强制转换的 var s = (string)obj;。
类型模式:只判断不取
只想确认类型、不需要变量,用弃元 _ 或直接写类型。它检查运行期真实类型是否兼容。
object data = 123;
if (data is int)
{
Console.WriteLine("是个 int");
}
类型模式对继承也有效:派生类对象 is 基类会成功。
Tip用
is检查非 null 也很顺手:if (obj is not null)比!= null更贴近模式风格(not 在第 88 章讲)。
常量模式:等于某常量
常量模式用 == 的语义,但写在模式位置,常出现在 switch 里。null、数字、字符串、枚举、true/false 都能当常量。
string? input = "admin";
switch (input)
{
case "admin":
Console.WriteLine("管理员");
break;
case null:
Console.WriteLine("空输入");
break;
default:
Console.WriteLine("普通用户");
break;
}
Note常量模式本质是”等于判断”。编译器保证
is null不会误用你重载的==运算符,更安全。
switch 模式表达式
C# 8 起,switch 还能写成”表达式”,整体返回一个值,比传统 switch 语句更简洁。
object value = 7;
string label = value switch
{
int n when n > 0 => "正整数",
int => "其他整数",
string => "字符串",
null => "空",
_ => "其他类型"
};
Console.WriteLine(label);
_(弃元)是兜底分支,类似 default。没有它且漏了情况,运行时会抛异常。
when 是守卫子句,对模式再加额外条件,比如 n > 0。
Warningswitch 表达式必须覆盖所有情况,否则编译报警告、运行可能抛异常。用
_兜底最稳妥。
is 与 switch 怎么选
判断单个条件、配合 if,用 is 类型模式更直接。要在一堆分支里挑一个,用 switch 表达式更清爽。
// is 适合简单判断
if (obj is decimal price)
Console.WriteLine($"金额 {price}");
// switch 适合多分支
string kind = obj switch
{
int => "int",
string => "string",
_ => "其它"
};
新手常见坑
坑一:把常量模式当范围用。case 1: 只匹配恰好 1,不能写成 case < 10(那是关系模式,第 88 章)。
坑二:switch 表达式漏 _,编译期就警告,别无视。
坑三:用 is 判断值类型却忘了拆箱,其实声明模式已帮你转换好。
Tip模式匹配不是炫技。当 if/else 里满是
is+ 强制转换,或 switch 全是类型分支时,就该换成模式。
模式匹配不是新类型
要澄清:模式匹配是语法特性,不引入新类型。它背后用的还是 is、==、类型转换这些老机制,只是写法更顺。
// 等价传统写法 vs 模式写法
object o = 5;
if (o is int n) { } // 模式
if (o is int && (n = (int)o) >= 0) { } // 传统(伪代码,仅示意)
编译器会把模式翻译成对应的检查,性能持平,可读性却好很多。
Note所谓”声明模式”的变量
n,作用域只在匹配成功的分支内,匹配失败就不存在,安全。
弃元与 var 模式简述
_ 是弃元,表示”任意值,我不关心”。var x 是 var 模式,匹配任意并捕获到 x。它们在模式里很常用。
object v = 3.14;
string r = v switch
{
var x when x is double => $"双精度 {x}",
_ => "其它"
};
Console.WriteLine(r);
本章先建立常量/类型/声明三种基础模式,var 模式与列表模式等高级玩法留待后续深入。
与多态怎么选
面向对象用”虚方法/重写”做行为分派(第 41 章),模式匹配用”数据形状”做分派。两者都能按类型走不同逻辑。
- 行为随类型固定、且会扩展新类型 → 偏向多态(虚方法)。
- 分派依赖”运行时数据的组合/范围” → 偏向模式匹配(更灵活,不改类型也能加分支)。
// 按"数据"分派,用模式更自然
string Ticket(decimal price, bool vip) => (vip, price) switch
{
(true, >= 100) => "VIP 大单",
(false, < 50) => "普通小单",
_ => "常规"
};
Tip模式匹配不替代继承多态,它是补充:当分支条件由”数据值”而非”类型行为”决定时,模式更轻巧。
switch 语句 vs switch 表达式
传统 switch 语句用 break 分隔分支,适合”执行动作”;switch 表达式(=> 形式)整体返回值,适合”算出结果”。
// 语句:做动作
switch (level)
{
case 1: Console.WriteLine("一"); break;
default: Console.WriteLine("其它"); break;
}
// 表达式:出结果
string text = level switch
{
1 => "一",
_ => "其它"
};
本文建议:要”得到一个值”就用 switch 表达式,配合模式更紧凑;要”执行副作用”再用语句。
Tipswitch 表达式没有 break,靠
=>和逗号分隔,_兜底。漏兜底编译就警告,逼你把情况想全。
var 模式与弃元再探
var x 匹配任何值并捕获,_ 匹配任何值但丢弃。两者在模式里充当”通配”,常与 and/or 组合。
object o = 42;
string r = o switch
{
var x when x is int and > 0 => $"正整数 {x}",
_ => "其它"
};
本章打底的三类模式(声明/类型/常量)加上 var、弃元,已能覆盖绝大多数”按类型与值分派”的需求。
Note列表模式(匹配数组
[1, 2, ..])与when守卫会随你熟练度逐步引入,本章先把基础钉牢。
一道小练习
用本章知识写个”形状面积描述器”:
object shape = 3.14;
string desc = shape switch
{
int n => $"整数边长正方形,边长 {n}",
double d => $"双精度半径圆,半径 {d}",
null => "空形状",
_ => "未知形状"
};
Console.WriteLine(desc);
试着把 shape 换成不同类型和 null,观察每个分支如何被命中。动手是巩固模式的最好方式。
Tip练的时候留意:声明模式
int n既判了类型又取了值,比先is int再强转省一行,也避免强转抛异常。
何时该用模式匹配
不是所有判断都该换成模式。给个实用判断清单:
if里反复is+ 强转 → 换成声明模式,省掉重复转换。switch分支全在比”类型/值/范围” → 用 switch 表达式 + 模式。- 条件简单、一眼能看懂 → 普通
if就够了,不必硬套模式。
一个直觉:当分支里出现”先判断类型,再读它的属性或值”,模式匹配往往能把它压成一行紧凑的 switch 表达式。
Tip模式匹配是工具不是目的。能用它让代码更短更不易漏分支,就用;若只会让同事多想三秒,普通
if反而更体贴。
小结
声明模式 is T x 一步判断并取值,类型模式只判断,常量模式做相等比较。switch 表达式配合 when 守卫和 _ 兜底,让分支既短又安全。下章看关系与逻辑模式,进一步减少手写比较。
与多态的边界再强调
模式匹配擅长”按数据值分派”,多态擅长”按类型行为分派”。当你发现分支条件主要是 obj is 某类型 再读属性,模式更合适;若是”不同类型有不同方法实现”,虚方法更自然。
// 类型+属性混合分派,模式胜出
string Route(object o) => o switch
{
string s when s.Length > 0 => "非空串",
int n when n > 0 => "正整数",
_ => "其它"
};
Note二者不是替代关系。真实项目里常并存:核心行为用多态,临时数据判断用模式,各取所长。