首页 / C# 入门教程 / 模式匹配:声明/类型/常量

C# 入门教程

模式匹配:声明/类型/常量

本教程共 100 篇 · 第 87 篇 · 更新于 2026-07-31 · 约 7 分钟阅读

C#C# 入门教程编程语言模式匹配pattern matching

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

Warning

switch 表达式必须覆盖所有情况,否则编译报警告、运行可能抛异常。用 _ 兜底最稳妥。

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 表达式,配合模式更紧凑;要”执行副作用”再用语句。

Tip

switch 表达式没有 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 再强转省一行,也避免强转抛异常。

何时该用模式匹配

不是所有判断都该换成模式。给个实用判断清单:

  1. if 里反复 is + 强转 → 换成声明模式,省掉重复转换。
  2. switch 分支全在比”类型/值/范围” → 用 switch 表达式 + 模式。
  3. 条件简单、一眼能看懂 → 普通 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

二者不是替代关系。真实项目里常并存:核心行为用多态,临时数据判断用模式,各取所长。