C#的switch语句有哪些新特性?如何模式匹配?

C#的switch语句通过引入模式匹配和switch表达式,实现了从简单值比较到复杂数据形状匹配的跃迁,支持类型、属性、关系等多种模式,结合执行顺序与穷尽性检查,显著提升代码可读性与安全性。

c#的switch语句有哪些新特性?如何模式匹配?

C#的

switch

语句在近年来的版本迭代中,已经从一个相对简单的值比较工具,演变为一个功能强大的模式匹配表达式,极大地提升了代码的可读性和表达力。它不再仅仅是判断一个变量的离散值,而是能够根据数据的形状、类型、属性甚至关系进行复杂的匹配,让原本冗长的

if-else if

链变得简洁优雅。

C#的

switch

语句在最近几个版本中迎来了翻天覆地的变化,最核心的莫过于引入了模式匹配(Pattern Matching)以及

switch

表达式(Switch Expressions)。这些新特性让代码处理复杂逻辑时,变得更加清晰和富有表现力。

解决方案

我们来具体看看这些新特性是如何工作的,以及如何利用它们进行模式匹配:

1.

switch

表达式(C# 8.0)

这是最直观的变化。传统的

switch

语句主要用于执行副作用(如打印消息),而

switch

表达式则用于计算并返回一个值。它的语法更紧凑,使用

=>

来连接模式和结果,并且通常不需要

break

关键字,因为每个分支都必须返回一个值。

// 传统switch语句string GetDayTypeTraditional(DayOfWeek day){    switch (day)    {        case DayOfWeek.Saturday:        case DayOfWeek.Sunday:            return "Weekend";        case DayOfWeek.Monday:            return "Start of Week";        default:            return "Weekday";    }}// switch表达式string GetDayTypeModern(DayOfWeek day) => day switch{    DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend", // C# 9.0 逻辑模式    DayOfWeek.Monday => "Start of Week",    _ => "Weekday" // _ 是丢弃模式,匹配任何其他情况};

2. 模式匹配的种类

switch

表达式和

switch

语句(从C# 7.0开始)都可以使用多种模式:

  • 类型模式(Type Pattern – C# 7.0)用于检查表达式的运行时类型,并将其转换为该类型的新变量。

    object data = "Hello, C#!";string result = data switch{    string s => $"这是一个字符串,长度是 {s.Length}",    int i => $"这是一个整数,值为 {i}",    _ => "未知类型"};// 也可以在传统的switch语句中使用:// if (data is string s) { /* ... */ }
  • var

    模式(Var Pattern – C# 7.0)匹配任何非

    null

    值,并将其绑定到一个新的局部变量。

    object item = new List { 1, 2 };string description = item switch{    var list when list is List => $"这是一个整数列表,包含 {((List)list).Count} 个元素",    _ => "其他类型"};// 注意:var模式通常与when子句结合使用,否则它会匹配所有非null值。
  • 属性模式(Property Pattern – C# 8.0)允许你根据对象的属性值进行匹配。这在处理复杂对象时特别有用。

    record Order(int OrderId, decimal Amount, string Status);Order order = new(101, 150.75m, "Processing");string orderStatusMessage = order switch{    { Status: "Processing", Amount: > 100m } => "大额订单正在处理中。",    { Status: "Completed" } => "订单已完成。",    { Status: "Cancelled", OrderId: var id } => $"订单 {id} 已取消。",    _ => "未知订单状态。"};
  • 位置模式(Positional Pattern – C# 8.0)用于匹配支持解构(Deconstruct)的对象。你可以根据解构后的值进行匹配。

    record Point(int X, int Y); // records 默认支持解构Point p = new(10, 20);string pointDescription = p switch{    (0, 0) => "原点",    (> 0, > 0) => "第一象限",    (int x, int y) when x  0 => "第二象限", // 结合when子句    _ => "其他位置"};
  • 关系模式(Relational Pattern – C# 9.0)允许你使用关系运算符(

    <

    ,

    >

    ,

    <=

    ,

    >=

    )直接在模式中比较值。

    int temperature = 25;string weatherAdvice = temperature switch{     "极寒,注意保暖!",    >= 0 and  "寒冷,多穿衣服。", // C# 9.0 逻辑模式结合    >= 10 and  "舒适宜人。",    >= 25 and  "炎热,注意防晒。",    _ => "酷暑,避免外出!"};
  • 逻辑模式(Logical Pattern – C# 9.0)使用

    and

    or

    not

    运算符组合其他模式,创建更复杂的匹配条件。

    bool IsWorkingDay(DayOfWeek day) => day switch{    DayOfWeek.Saturday or DayOfWeek.Sunday => false,    not DayOfWeek.Friday => true, // 除了周五以外的工作日    _ => true // 周五也是工作日};

这些模式可以灵活组合,形成非常强大和富有表达力的匹配逻辑。在我看来,

switch

表达式和模式匹配的引入,是C#在处理复杂条件分支方面的一次质的飞跃,它让代码不仅更短,而且更容易理解。

switch

表达式与

switch

语句有何不同,我该如何选择?

在我个人的编程实践中,

switch

表达式和

switch

语句虽然都叫

switch

,但它们的设计哲学和应用场景是截然不同的。简单来说,

switch

语句更多地是用来执行一系列操作或副作用,而

switch

表达式则专注于计算并返回一个单一的结果。

传统的

switch

语句(C# 7.0及更早版本)更像是一个控制流结构。你可以在每个

case

块中写多行代码,执行数据库操作、日志记录、UI更新等等,并且需要

break

来防止“穿透”(fall-through)。它不直接返回一个值,而是通过修改外部变量或执行某些动作来达成目的。

// switch 语句:执行副作用void ProcessCommand(string command){    switch (command)    {        case "start":            Console.WriteLine("Starting service...");            // 更多启动逻辑            break;        case "stop":            Console.WriteLine("Stopping service...");            // 更多停止逻辑            break;        default:            Console.WriteLine("Unknown command.");            break;    }}

switch

表达式(C# 8.0引入)则是一个真正的表达式,它必须计算并返回一个值。它的语法更紧凑,通常每个模式后面直接跟着一个

=>

和要返回的值。它天然地避免了穿透问题,因为每个分支都必须提供一个结果,并且整个表达式最终会有一个确定的返回值。这让它在处理映射、转换或根据不同输入返回不同配置等场景时,显得异常简洁。

// switch 表达式:计算并返回一个值string GetServiceStatus(ServiceState state) => state switch{    ServiceState.Running => "Service is active.",    ServiceState.Stopped => "Service is inactive.",    ServiceState.Error => "Service encountered an error.",    _ => "Unknown service state."};

如何选择?

我的经验是:

  1. 如果你需要根据输入值执行不同的“动作”或“副作用”(如打印日志、修改状态、调用其他方法),并且这些动作本身不直接返回一个有用的值供后续使用,那么
    switch

    语句是更合适的选择。 它提供了更大的自由度来编写多行逻辑。

  2. 如果你需要根据输入值“计算”或“映射”出一个结果,并且这个结果会立即被后续代码使用,那么
    switch

    表达式无疑是更优的选择。 它能够将复杂的条件逻辑浓缩成一行或几行,极大地提高了代码的简洁性和可读性。在很多情况下,它能替代冗长的

    if-else if

    链,让代码意图一目了然。

当然,两者并非完全互斥。有时候,你可能在

switch

表达式的某个分支中,还需要调用一个执行副作用的方法。但总体原则是,关注你最终想要的是一个“动作”还是一“结果”。

C#模式匹配在实际开发中能解决哪些常见的痛点?

在我看来,C#的模式匹配并非仅仅是语法上的“锦上添花”,它实实在在地解决了许多在日常开发中遇到的痛点,让代码变得更具弹性、更安全,也更容易维护。

  1. 处理多态类型集合时的繁琐判断:想象一下,你有一个

    List
    

    ,里面混合了各种类型的对象(字符串、整数、自定义类)。传统上,你需要写一堆

    if (obj is TypeA a) { ... } else if (obj is TypeB b) { ... }

    。这不仅冗长,而且容易遗漏

    else

    分支,导致逻辑不完整。模式匹配的解决方案:

    List items = new() { 10, "hello", new DateTime(2023, 1, 1), 3.14f };foreach (var item in items){    string description = item switch    {        int i => $"整数: {i}",        string s => $"字符串: '{s}' ({s.Length} chars)",        DateTime dt => $"日期: {dt.ToShortDateString()}",        float f => $"浮点数: {f:F2}",        _ => "未知类型"    };    Console.WriteLine(description);}
    

    这让处理异构集合变得异常优雅,代码结构清晰,且编译器能帮助检查是否覆盖了所有可能的类型(对于

    switch

    表达式)。

  2. 复杂的业务规则或状态机逻辑:当你的业务逻辑涉及到多个条件组合时,比如一个订单的状态、金额、用户等级等共同决定某个操作的权限或结果,传统的

    if-else if

    会迅速膨胀成“意大利面条”式的代码。模式匹配的解决方案:利用属性模式、关系模式和逻辑模式,可以清晰地表达这些复杂的组合条件。

    record User(string Name, UserRole Role, bool IsActive);record Order(int Id, decimal Amount, OrderStatus Status);enum UserRole { Guest, Member, Admin }enum OrderStatus { Pending, Processing, Completed, Cancelled }bool CanProcessOrder(User user, Order order) => (user, order) switch{    ({ Role: UserRole.Admin }, _) => true, // 管理员可以处理任何订单    ({ IsActive: true, Role: UserRole.Member }, { Status: OrderStatus.Pending or OrderStatus.Processing, Amount:  true, // 活跃会员可以处理小额待处理或处理中订单    ({ IsActive: false }, _) => false, // 非活跃用户不能处理    (_, { Status: OrderStatus.Completed or OrderStatus.Cancelled }) => false, // 已完成或已取消的订单不能再处理    _ => false // 其他情况均不能处理};

    这种方式将复杂的条件判断逻辑集中在一个地方,并且可读性极高,一眼就能看出不同组合下的行为。

  3. 空值(null)检查的简化:虽然C# 8.0引入了可空引用类型,但显式的

    null

    检查依然是必要的。模式匹配可以很自然地融入

    null

    检查。

    object? value = null; // 或者 new object();string info = value switch{    null => "值为空。",    string s => $"这是一个字符串:{s}",    _ => "非空但非字符串。"};

    这避免了单独写

    if (value is null)

    的语句,让逻辑更加紧凑。

    Magic AI Avatars

    Magic AI Avatars

    神奇的AI头像,获得200多个由AI制作的自定义头像。

    Magic AI Avatars 47

    查看详情 Magic AI Avatars

  4. 模式匹配的这些能力,让我在编写业务逻辑时,能够更专注于“数据是什么样子”,而不是“如何一步步判断它”,从而写出更声明式、更易于理解和维护的代码。

    深入理解C#模式匹配的执行顺序和潜在陷阱

    模式匹配虽然强大,但在使用时,如果不了解其执行顺序和一些潜在的“坑”,可能会导致意料之外的行为。我个人在实践中就遇到过一些情况,所以深入理解这些细节非常重要。

    1. 模式的执行顺序

    switch

    表达式或

    switch

    语句中的模式是从上到下、按顺序评估的。这意味着,一旦一个模式匹配成功,其对应的表达式或代码块就会被执行,并且后续的模式将不再被评估。这是一个非常关键的规则。

    object data = 100;string result = data switch{    int i => "匹配到 int 类型", // 会匹配到这里    > 50 => "匹配到大于 50 的值", // 这行永远不会被匹配到,因为它在 int i 之后    _ => "其他"};Console.WriteLine(result); // 输出: 匹配到 int 类型

    在这个例子中,即使

    100

    也满足

    > 50

    的条件,但因为

    int i

    模式在它之前且已经匹配成功,所以

    > 50

    的模式就“失效”了。因此,编写模式时,更具体的、更严格的模式应该放在前面,而更通用的、更宽泛的模式应该放在后面。

    2. 编译器的穷尽性检查(Exhaustiveness Checking)

    对于

    switch

    表达式,C#编译器会尝试进行穷尽性检查。这意味着,如果你的

    switch

    表达式没有覆盖所有可能的输入值,编译器会发出警告,甚至在某些情况下(如处理枚举类型时),会直接报错。这是一个非常棒的安全特性,它强制你考虑所有可能的场景,避免了运行时因未处理情况而导致的错误。

    // 假设有一个枚举enum TrafficLight { Red, Yellow, Green }TrafficLight light = TrafficLight.Red;string action = light switch{    TrafficLight.Red => "Stop",    // 如果这里缺少 Yellow 或 Green,编译器会警告甚至报错,因为它不是穷尽的    _ => "Proceed" // _ 模式可以作为捕获所有未显式处理情况的最终手段};

    但需要注意的是,对于

    object

    类型或其他开放类型,编译器很难知道所有可能的派生类,所以穷尽性检查会依赖于

    _

    (discard pattern)来确保所有情况都被覆盖。

    3.

    when

    子句与模式的结合

    when

    子句允许你在模式匹配成功后,添加额外的条件。这个条件是在模式匹配成功后才评估的。

    int value = 15;string category = value switch{    int i when i  "小于10",    int i when i >= 10 && i  "10到19之间", // 会匹配到这里    int i => "大于等于20",    _ => "其他"};Console.WriteLine(category); // 输出: 10到19之间

    这里,

    int i

    模式本身会匹配所有整数,但

    when

    子句进一步筛选了条件。如果第一个

    when

    子句不满足,则会继续尝试下一个

    int i when ...

    4.

    null

    值的处理

    模式匹配对

    null

    值有特殊的处理。一个模式(除了

    null

    模式本身)通常不会匹配

    null

    object? item = null;string description = item switch{    string s => "字符串", // 不会匹配null    int i => "整数",     // 不会匹配null    null => "空值",      // 专门匹配null    _ => "其他非空类型" // 匹配所有非null且未被前面模式匹配的类型};Console.WriteLine(description); // 输出: 空值

    如果你不显式处理

    null

    ,并且

    item

    null

    ,那么它会跳过所有非

    null

    模式,最终可能由

    _

    模式捕获,或者如果

    _

    模式也不存在,

    switch

    表达式会抛出

    System.Runtime.CompilerServices.SwitchExpressionException

    5. 潜在的性能考量

    虽然编译器对模式匹配进行了大量优化,但在极端复杂的模式组合下,尤其是涉及大量属性模式和

    when

    子句时,可能会有轻微的性能开销。不过,对于绝大多数日常应用,这种开销可以忽略不计,模式匹配带来的代码清晰度和安全性收益远大于此。如果真的遇到性能瓶颈,通常是算法或数据结构层面的问题,而不是模式匹配本身。

    总而言之,模式匹配是C#语言的一个巨大进步,但理解其内在机制,特别是模式的评估顺序和穷尽性检查,能帮助我们写出更健壮、更可预测的代码。在遇到不符合预期的情况时,首先检查模式的顺序,往往能找到问题的根源。

    以上就是C#的switch语句有哪些新特性?如何模式匹配?的详细内容,更多请关注创想鸟其它相关文章!

    版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
    发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1439460.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2025年12月17日 16:13:13
    下一篇 2025年12月17日 16:13:24

    相关推荐

    • C#的break和continue关键字如何控制循环?有什么区别?

      break用于终止当前循环,continue用于跳过当前迭代;前者在找到目标或出错时退出循环,后者在过滤无效数据时跳过单次循环,二者在嵌套循环中均只作用于最内层循环。 在C#中, break 和 continue 是两个非常核心的控制流关键字,它们都用于修改循环的正常执行路径,但作用机制截然不同。简…

      好文分享 2025年12月17日
      000
    • .NET的AssemblyAlgorithmIdAttribute类的作用是什么?

      AssemblyAlgorithmIdAttribute用于指定程序集哈希算法ID,确保强命名程序集的完整性验证。它在构建时将算法ID写入清单,运行时CLR据此计算并比对哈希值,防止篡改。该特性与强命名紧密关联,决定签名中哈希的生成算法。现代.NET开发中较少手动设置,因SDK默认采用SHA256等…

      2025年12月17日
      000
    • C#的Entity Framework Core是什么?如何使用?

      EF Core是.NET平台的ORM框架,通过C#对象映射数据库表,提升开发效率与代码可维护性;其核心流程包括定义实体模型、创建DbContext、配置连接、使用迁移管理数据库结构,并通过LINQ实现CRUD操作;相比ADO.NET,EF Core在多数业务场景下更高效,支持跨数据库、类型安全和自动…

      2025年12月17日
      000
    • 在c语言中怎么用 换行符 在c语言中的使用场景

      在 c 语言中, 用于创建新行,广泛应用于格式化输出和文件处理。1) 格式化输出:如打印日志和生成报告。2) 文件处理:如读取csv文件。3) 跨平台开发需注意不同系统对换行符的处理。 在 C 语言中,换行符 是一个非常常见且重要的字符,它用于在输出时创建新行。简单来说, 告诉编译器在输出时跳到下一…

      2025年12月17日
      000
    • C语言中的内联函数是什么?怎么定义?

      内联函数用于减少函数调用开销,通过将函数体插入调用处来提升效率。频繁调用的小函数适合内联,如数学运算函数,相比宏定义更安全。定义时在函数前加inline关键字,并通常放在头文件中,但static inline可用于单个源文件。注意:非所有函数都能成功内联,复杂逻辑、递归或循环函数可能无法展开;滥用会…

      2025年12月17日
      000
    • C#脚本编写工具推荐

      c#脚本编写首选vs code因其轻量灵活,适合快速开发;复杂项目则选visual studio。1. vs code启动快、扩展丰富(如c# dev kit和omnisharp),提供智能感知、代码补全等功能,配合内置终端可高效运行dotnet命令,适合小脚本开发。2. visual studio…

      2025年12月17日
      000
    • ASP.NET Core中的应用程序设置是什么?如何管理?

      ASP.NET Core通过IConfiguration接口和多种配置提供者实现灵活的应用程序设置管理,支持从JSON文件、环境变量、用户秘密、Azure Key Vault等来源加载配置,并按优先级覆盖,确保不同环境下的安全与可维护性;推荐使用强类型的Options模式进行配置绑定,避免硬编码,提…

      2025年12月17日
      000
    • C#的out变量声明如何简化代码?有什么限制?

      C# 7.0 引入的 out 内联变量声明允许在方法调用时直接声明变量,如 int.TryParse(“123”, out int parsedValue),无需提前声明,提升了代码的局部性与可读性,减少了冗余代码,尤其在 TryParse 模式和多返回值场景中显著简化逻辑,…

      2025年12月17日
      000
    • 如何在WinForms应用中实现窗体的动态加载?

      答案:WinForms窗体动态加载通过实例化、嵌入容器或反射实现,支持按需加载、模块化和插件架构,提升性能与用户体验。 在WinForms应用中实现窗体的动态加载,核心在于运行时创建和管理窗体实例,而非在设计时固定。这通常通过直接实例化窗体类、将其嵌入到现有容器,或更高级地通过反射机制从外部程序集加…

      2025年12月17日
      000
    • WPF中的DataContext属性应该如何正确设置?

      DataContext是WPF数据绑定的核心,通过继承机制从父元素向下传递,使UI元素能自动获取数据源;可在View中显式设置为ViewModel,实现MVVM架构中视图与逻辑的解耦;利用继承、显式赋值或模板设置,结合RelativeSource、ElementName等技巧,可高效构建灵活、可维护…

      2025年12月17日
      000
    • InvalidProgramException是什么?如何调试?

      invalidprogramexception通常由编译产物损坏、il代码被非法修改或运行时环境不匹配引起,解决方案包括:1. 清理并重建项目,删除bin和obj文件夹;2. 检查依赖项版本一致性,避免框架或库的不兼容;3. 使用反编译工具如ilspy检查程序集il结构是否异常;4. 排查il织入工…

      2025年12月17日
      000
    • WPF中如何实现语音识别与合成?

      答案:WPF中语音识别与合成依赖System.Speech,核心为SpeechRecognitionEngine和SpeechSynthesizer;需构建语法、处理异步事件、管理音频设备以实现识别,通过SSML优化合成效果,并注意多语言支持与用户隐私保护。 在WPF应用中实现语音识别与合成,我们主…

      2025年12月17日
      000
    • C#的CryptographicException是什么?加密异常处理

      c#抛出cryptographicexception的主要原因是加密解密上下文不一致或数据问题;2. 常见原因包括密钥或iv不匹配、数据损坏、填充模式不一致、算法模式不匹配、数据长度错误、权限不足及密钥过期;3. 诊断时应检查innerexception、详细日志、输入数据一致性、逐步调试、隔离问题…

      2025年12月17日
      000
    • Visual Studio社区版安装指南

      安装visual studio社区版需先下载官方安装器,再按需求选择工作负载和组件。1.访问微软官网下载安装器,确保来源可靠;2.运行安装器后进入visual studio installer界面;3.选择必要的工作负载如“.net桌面开发”避免全选;4.通过“单个组件”补充特定工具;5.根据硬盘情…

      2025年12月17日
      000
    • 如何为WinForms应用添加脚本支持?

      核心思路是嵌入脚本引擎提升WinForms应用灵活性,可通过IronPython或Roslyn实现;IronPython适合非开发者使用Python脚本调用.NET对象,示例中执行Python代码更新控件并返回结果;C#脚本基于Roslyn,支持直接运行C#代码片段,通过ScriptOptions引…

      2025年12月17日
      000
    • C#的??和??=运算符在空值处理中有何作用?

      ?? 运算符在左侧为 null 时返回右侧默认值,否则返回左侧值;2. ??= 运算符仅在左侧为 null 时才将右侧值赋给左侧;3. 两者通过延迟计算避免不必要的性能开销且编译为高效 il 代码;4. 适用于简化 null 检查、默认值赋值、链式 null 判断、属性初始化及避免重复计算;5. 替…

      好文分享 2025年12月17日
      000
    • WPF中的动画效果应该怎么制作?

      WPF动画通过操纵依赖属性实现,利用声明式语法和GPU加速,以Storyboard编排动画,相比WinForms的手动重绘更高效流畅,支持路径与关键帧动画,并可通过优化渲染方式提升性能。 WPF中的动画效果,本质上是通过操纵元素的依赖属性(Dependency Properties)在一段时间内平滑…

      好文分享 2025年12月17日
      000
    • C#代码混淆工具怎么用

      c#代码混淆工具通过重命名、控制流混淆等方式保护代码,防止逆向工程。具体步骤包括:1.选择合适的工具如dotfuscator或obfuscar;2.将混淆集成到构建流程中;3.设置排除规则避免破坏公共api、反射、序列化等关键部分;4.执行混淆并进行功能与性能测试。尽管混淆不能完全阻止逆向工程,但能…

      好文分享 2025年12月17日
      000
    • C#的指针操作在桌面开发中是否安全?

      C#中的指针操作在特定场景下可提升性能,但需谨慎使用。它适用于与非托管代码互操作、极致性能需求的内存处理或自定义数据结构,但会牺牲安全性,带来缓冲区溢出、空指针解引用等风险。推荐优先使用Span和Memory等安全替代方案,在保证性能的同时维持代码稳定性。 C#中的指针操作在桌面开发中,如果严格遵循…

      2025年12月17日
      000
    • C#的配置文件App.config应该如何读写?

      答案:C#中读写App.config需用ConfigurationManager读取,通过OpenExeConfiguration修改并保存。读取时直接访问AppSettings或ConnectionStrings;写入时需加载配置对象,修改后调用Save()并刷新。权限不足可能导致写入失败,建议用…

      2025年12月17日
      000

    发表回复

    登录后才能评论
    关注微信