告别 if-else:C#.NET 模式匹配让代码更优雅的正确方式

Alice
Alice
发布时间:2026-08-24| 120人浏览过
原创
展示模式匹配在数据验证和 API 响应处理中的落地方式信息图
模式匹配的两类常见业务用法用两类高频业务场景说明模式匹配为什么适合集中写规则,以及它如何替代散落的分支判断。
展示 C# 高级模式之间关系和典型用途的对比信息图
四类高级模式的适用边界把属性模式、位置模式、逻辑关系模式、列表模式放在同一张图里,对照它们各自最适合处理的数据形态。
展示 C# 7.0 到 11.0 模式匹配能力扩展的时间线信息图
C# 模式匹配版本演进用时间线梳理 C# 模式匹配从基础类型判断到列表模式的扩展路径。

前言

很多 C# 项目里的分支代码一长,就会堆出类型判断、强制转换和层层判空,维护起来既啰嗦也容易漏条件。模式匹配的价值不只是“少写几行”,而是把判断规则、数据提取和返回结果合并到更清晰的结构里。本文按版本能力、常见模式和业务案例拆开说明,帮助你判断哪些 `if-else` 适合替换,替换后又能带来哪些实际收益。

很多 C# 项目里,分支判断一多就容易堆出冗长的 if-else、类型转换和空值判断。模式匹配的价值,不只是把语法写短,而是把“判断什么、提取什么、返回什么”收束到一处。下面按演进、基础写法、高级模式和实际场景拆开讲,方便你判断哪些代码值得改,哪些地方改了以后会更清楚。

模式匹配是怎么从语法增强变成核心能力的

模式匹配自 C# 7.0 引入后,几乎每个大版本都在补强。早期它主要解决类型检查和变量提取的问题,到了后续版本,已经能覆盖对象结构判断、范围匹配、逻辑组合以及列表形态判断,足以替代大量手工分支。

它最常见的三个入口分别是:

  • is 表达式:用于检查并在匹配成功时直接声明变量。
  • switch 语句:适合保留传统分支结构,但用更强的匹配条件组织逻辑。
  • switch 表达式:C# 8.0 引入,更适合把“输入映射到输出”的逻辑写成表达式。

如果按版本看,演进路径大致如下:

版本新增特性
C# 7.0基本模式匹配:is 表达式、switch 语句增强
C# 8.0递归模式、属性模式、位置模式、switch 表达式
C# 9.0类型模式简化、逻辑模式(and/or/not)、关系模式
C# 10.0扩展属性模式
C# 11.0列表模式、切片模式

这条演进线很重要,因为它决定了你能不能把原本分散在多个判断块里的条件,压缩成一个结构清晰、可读性更高的匹配表达式。

先掌握三类最常用的基础模式

如果你刚从传统分支切换到模式匹配,最值得先掌握的是声明模式、类型模式和常量模式。这三类覆盖了日常代码里最常见的判断场景,也是后面高级模式的基础。

声明模式:判断类型的同时直接拿到变量

声明模式最大的意义,是省掉“先判断、再强转、再使用”的重复动作。对于需要立刻使用匹配结果的代码,这种写法通常比传统写法更直观。

// 检查类型并声明新变量
if (obj is string str)
{
    Console.WriteLine($"字符串长度: {str.Length}");
}

// 传统方式对比
if (obj is string)
{
    string str = (string)obj;
    Console.WriteLine($"字符串长度: {str.Length}");
}

这里的差异不只是少写一行强制转换,更关键的是把“类型成立后才能使用这个变量”的关系表达清楚了。

类型模式:只关心类型归类时更合适

如果你只需要知道对象属于哪一类,而不急着把它提取为新变量,类型模式会更简洁。它很适合做输入归类,尤其适合搭配 switch 表达式使用。

// 只检查类型,不声明变量
if (obj is string)
{
    Console.WriteLine("这是一个字符串");
}

// 在 switch 表达式中使用
var result = obj switch
{
    string => "它是字符串",
    int => "它是整数",
    _ => "其他类型"
};

当分支的目标是“把不同输入映射为不同结果”时,这种写法通常会比多层 if-else 更整齐。

常量模式:把特定值判断收束到统一入口

常量模式用于匹配固定值,包括数字、字符串和 null。它适合把零散的相等判断合并进统一的分支结构里。

// 检查特定值
if (value is 5) { /* 值等于5 */ }
if (value is null) { /* 值为null */ }
if (value is "hello") { /* 字符串比较 */ }

// 在 switch 中
string message = value switch
{
    0 => "零",
    1 => "一",
    null => "空值",
    _ => "其他值"
};

这类模式最直接的收益是减少散落的判空和相等比较,让分支处理集中在一个入口里维护。

高级模式能替代哪些复杂分支

从 C# 8.0 到 C# 11.0,模式匹配真正拉开差距的地方,在于它开始能描述对象结构、位置关系、范围条件和集合形态。很多原本要靠嵌套 if、临时变量和多段判断才能写清楚的逻辑,在这里都能收拢。

属性模式:直接按对象结构做判断

属性模式适合处理 DTO、订单、配置对象这类层级清晰的数据。与其逐层判空、逐个属性比较,不如直接写出你要匹配的结构。

// 检查对象属性
if (person is { Name: "John", Age: > 18 })
{
    Console.WriteLine("John 已成年");
}

// 嵌套属性模式
if (order is { Customer: { Address: { City: "Beijing" } } })
{
    Console.WriteLine("北京客户的订单");
}

// C# 10 扩展属性模式
if (order is { Customer.Address.City: "Beijing" })
{
    Console.WriteLine("北京客户的订单(简化写法)");
}

C# 10.0 的扩展属性模式进一步减少了嵌套层级,适合结构深但目标属性明确的判断场景。

位置模式:适合元组和可解构对象

位置模式常见于元组,也能用于实现了 Deconstruct 的自定义类型。它的优势是把“数据形态”和“分支结果”直接绑定到一起。

// 元组解构和匹配
var point = (x: 5, y: 10);
var quadrant = point switch
{
    (0, 0) => "原点",
    (var x, var y) when x > 0 && y > 0 => "第一象限",
    (var x, var y) when x < 0 && y > 0 => "第二象限",
    (var x, var y) when x < 0 && y < 0 => "第三象限",
    (var x, var y) when x > 0 && y < 0 => "第四象限",
    _ => "在坐标轴上"
};

这类写法适合坐标、状态组合、返回值拆分等场景,尤其适合“多个值共同决定结果”的逻辑。

逻辑模式和关系模式:把范围与组合条件写得更集中

C# 9.0 带来的 and、or、not 以及关系模式,是模式匹配真正适合业务判断的重要一步。它让“范围”“排除”“并列条件”都能直接出现在模式本身里。

// 使用逻辑运算符组合模式
bool IsValid(object obj) => obj is not null and (string or int);

// 复杂逻辑组合
var category = value switch
{
    > 0 and < 10 => "小的正数",
    < 0 or > 100 => "超出范围",
    not null => "非空值",
    _ => "其他"
};
// 使用关系运算符
string GetSize(int value) => value switch
{
    < 0 => "负数",
    >= 0 and < 10 => "小",
    >= 10 and < 100 => "中",
    >= 100 => "大"
};

// 与类型模式结合
string Describe(object obj) => obj switch
{
    int i when i < 0 => "负整数",
    int i when i > 0 => "正整数",
    int => "零",
    _ => "其他"
};

当你面对的是区间判断、状态归类、输入过滤时,这两类模式通常比手写连续条件更利于维护。不过也要注意,如果条件本身过长,过度堆叠在一个表达式里同样会影响可读性。

列表模式和字符串模式:面向序列内容做匹配

C# 11.0 的列表模式和切片模式,补上了集合场景的表达能力。对于数组、列表、字符序列这类输入,现在可以直接按开头、结尾、中间片段来写判断。

// 匹配数组或列表的特定模式
int[] numbers = { 1, 2, 3, 4 };

var result = numbers switch
{
    [1, 2, 3, 4] => "精确匹配",
    [1, 2, ..] => "以1,2开头",
    [.., 3, 4] => "以3,4结尾",
    [1, .. var middle, 4] => $"以1开头4结尾,中间有{middle.Length}个元素",
    [] => "空数组",
    _ => "其他模式"
};

// 切片模式
if (numbers is [var first, .. var rest])
{
    Console.WriteLine($"第一个元素: {first}, 剩余元素: {rest.Length}");
}
string text = "Hello, World";

if (text is ['H','e','l','l','o', ..])
{
    Console.WriteLine("以Hello开头");
}

// 与Span<char>模式匹配结合
ReadOnlySpan<char> span = text.AsSpan();
if (span is ['H','e','l','l','o', ..])
{
    Console.WriteLine("匹配Span");
}

这类写法对协议头判断、命令前缀识别、轻量文本解析尤其有用。相比手工下标访问,它更接近“按结构描述输入”。

自定义类型和表达式树也能用模式匹配

如果类型支持 Deconstruct,就可以自然接入位置模式;如果你在做元编程或规则分析,也可能会遇到表达式树中的模式匹配。

// 通过 Deconstruct 方法支持位置模式
public class Point
{
    public int X { get; }
    public int Y { get; }
    
    public void Deconstruct(out int x, out int y)
    {
        x = X;
        y = Y;
    }
}

// 现在可以对 Point 使用位置模式
var quadrant = point switch
{
    (0, 0) => "原点",
    (var x, var y) when x > 0 && y > 0 => "第一象限",
    // ...
};w
Expression<Func<object, bool>> expr = o => o switch
{
    int i when i > 0 => true,
    string s when s.Length > 5 => true,
    _ => false
};

// 解析表达式树
if (expr.Body is SwitchExpression switchExpr)
{
    foreach (SwitchCase @case in switchExpr.Cases)
    {
        // 处理每个case
    }
}

这部分不一定是日常业务代码的主战场,但它说明模式匹配已经不只是便捷写法,而是能嵌入类型设计和表达式分析流程中的语言能力。

在业务代码里,模式匹配最适合落在哪些地方

模式匹配真正有价值的地方,不在演示代码,而在业务分支。凡是“输入结构清楚、输出分支明确”的逻辑,通常都值得优先考虑。

数据验证:把失败条件集中到一个出口

订单校验、请求参数校验、状态检查都很适合用 switch 表达式重写。这样做的好处是每个失败条件都以独立模式出现,顺序和优先级也一目了然。

public ValidationResult Validate(Order order)
{
    return order switch
    {
        null => ValidationResult.Failure("订单不能为空"),
        { TotalAmount: <= 0 } => ValidationResult.Failure("金额必须大于0"),
        { Customer: null } => ValidationResult.Failure("客户信息缺失"),
        { Items: [] } => ValidationResult.Failure("订单项不能为空"),
        { Status: OrderStatus.Cancelled } => ValidationResult.Failure("订单已取消"),
        _ => ValidationResult.Success()
    };
}

和传统的多段 if 相比,这种方式更容易维护,也更方便后续补充新规则。

API 响应处理:按状态和内容统一归类

接口响应处理也是模式匹配非常典型的应用点。状态码、错误对象、返回数据是否为空,原本往往分散在多层判断里,现在可以直接按响应结构写。

public string HandleResponse(ApiResponse response)
{
    return response switch
    {
        { StatusCode: 200, Data: not null } => "成功",
        { StatusCode: 404 } => "资源未找到",
        { StatusCode: >= 400 and < 500 } => "客户端错误",
        { StatusCode: >= 500 } => "服务器错误",
        { Error: not null } => $"错误: {response.Error.Message}",
        _ => "未知响应"
    };
}

这类代码的关键收益是规则集中。以后你要增加 401、429 或特定错误结构的处理,只需要在原有分支表中追加规则即可。

什么时候该用,什么时候别硬套

模式匹配确实能让代码更简洁,但“更短”不总是“更好”。判断它是否值得上模式匹配,可以看三个标准。

  • 如果你在做类型判断、空值分流、固定值映射,优先考虑基础模式。
  • 如果你在处理对象结构、范围区间、集合形态,高级模式通常比嵌套分支更清楚。
  • 如果一个模式表达式已经长到需要读者反复停顿,应该考虑拆分方法、提炼变量,或者退回更朴素的写法。

从实际工程经验看,模式匹配最适合承担“规则明确、结构稳定、分支清晰”的逻辑。它不是要消灭所有 if-else,而是把本来就在做结构判断的代码,写成更接近业务意图的样子。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。