切换导航
简历
工具
演示
查询
导航菜单
服务端
30
前端
114
数据库
82
进阶开发
8
桌面程序
2
QA
15
软件使用
36
规范协议
5
操作系统
11
AI
0
复用资源
6
硬件
0
标签
C# 基础
Mybatis
ADO.NET
ASP.NET
Python 基础
Linq
Web Forms
.NET
Spring
SpringBoot
SpringCloud
.NET Core 中间件
Maven
.NET 类库
ASP.NET MVC
EF
NHibernate
WebApi
Java
外部链接
.NET Core SDK和运行时安装包
淘宝 NPM 镜像
查询网
Tables Generator线上表格转换工具
引用cdn在线脚本和样式
首页
服务端
C# switch 语句演化
[TOC] ## 1. C#7 之前的版本 ### 1.1. 基础语法 基础的 switch 写法与 if else 的效果一样 ```csharp //switchSugar("2024-01-01") 输出 2024-01-01-工作日 //switchSugar("2024-01-07") 输出 2024-01-07-休息日 public static string switchBase(object date) { string week = Convert.ToDateTime(date).DayOfWeek.ToString(); string msg = ""; switch (week) { case "Monday": msg = "工作日"; break; case "Tuesday": msg = "工作日"; break; case "Wednesday": msg = "工作日"; break; case "Thursday": msg = "工作日"; break; case "Friday": msg = "工作日"; break; case "Saturday": msg = "休息日"; break; case "Sunday": msg = "休息日"; break; default: msg = "星期八"; break; } return $"switchBase:\n{msg}"; } ``` ### 1.2. 多行贯穿执行 当几个连续的 case 执行的是相同的代码或代码段时,可以使用贯穿的写法简写 switch 结构体 ```csharp //switchSugar("2024-01-03") 【3号是周三】 输出 2024-01-03-工作日 //switchSugar("2024-01-07") 输出 2024-01-07-休息日 public static string switchSugar(object date) { string week = Convert.ToDateTime(date).DayOfWeek.ToString(); string msg = ""; switch (week) { case "Monday": case "Tuesday": case "Wednesday": case "Thursday": case "Friday": msg = "工作日"; break; case "Saturday": case "Sunday": msg = "休息日"; break; default: msg = "星期八"; break; } return $"switchSugar:\n{msg}"; } ``` ## 2. C#7 之后的版本 C#7 之后引入了模式匹配,微软文档中介绍了八种模式匹配,C#8之后又出现了 `switch表达式`,下面的示例将多数使用 `switch表达式` 的方式展示 ### 2.1. switch 语句 #### 2.1.1. 在switch 语句中使用模式匹配 下面示例使用了关系模式匹配,switch 语句和switch表达式都可以使用模式匹配的语法, ```csharp //switchStatement("2024-01-03") 输出 2024-01-03-工作日 //switchStatement("2024-01-07") 输出 2024-01-07-休息日 public static string switchStatement(object date) { string msg = ""; int num = (int)(Convert.ToDateTime(date).DayOfWeek); // 周日是0,周六是6 switch (num) { case > 0 and < 6: msg = "工作日"; break; case 0 or 6: msg = "休息日"; break; default: msg = "星期八"; break; } return $"switchStatement:\n{date}-{msg}"; } ``` #### 2.1.2. case guard 模式匹配附件条件 有些时候模式匹配的表达能力不够,需要添加额外的限制条件来增强 case 的规则约束,这时就可以使用 case guard 来添加附加的条件,case guard 必须是布尔表达式。 它是在模式后面使用 when 关键字来指定一个 case guard(附加条件) **示例:**下面的示例中既要判断周几,又要保证日期不能超过10号,超过10号的工作日显示为星期八 ```csharp //switchStatement("2024-01-11") 输出 2024-01-03-星期八 public static string switchStatementGuard(object date) { DateTime curDate = Convert.ToDateTime(date); string msg = ""; int num = (int)(curDate.DayOfWeek); // 周日是0,周六是6 switch (num) { case > 0 and < 6 when curDate.Day < 10: msg = "工作日"; break; case 0 or 6: msg = "休息日"; break; default: msg = "星期八"; break; } return $"switchStatementGuard:\n{date}-{msg}"; } ``` #### 2.1.3. case goto 贯穿执行 **示例:** 正常情况下 switch 是不允许贯穿执行的,如要需要将控制传递给其他 switch 部分,可使用 goto 语句。 ```csharp //switchStatementGoto("2024-01-10") 输出 2024-01-03-超出日期限制了 public static string switchStatementGoto(object date) { DateTime curDate = Convert.ToDateTime(date); string msg = ""; int num = (int)(curDate.DayOfWeek); // 周日是0,周六是6 switch (num) { case 1: msg = "工作日"; break; case 2: msg = "工作日"; break; case 3 when curDate.Day >= 10: goto case 10; case 3: msg = "工作日"; break; case 4: msg = "工作日"; break; case 5: msg = "工作日"; break; case 6: msg = "休息日"; break; default: msg = "星期八"; break; case 0: msg = "休息日"; break; case 10: msg = "超出日期限制了"; break; } return $"switchStatementGuard:\n{date}-{msg}"; } ``` > Tips:default 总是在最后执行,即使将它的位置前置和有 goto case 的情况,default 仍是最后一个执行 ### 2.2. switch 表达式 #### 2.2.1. 声明模式和类型模式 声明模式往往就搭配了类型模式,只有当类型匹配上之后,才会进行后续的结果分配,声明模式首先检查表达式的运行时类型,如果匹配成功,则将表达式结果分配给声明的变量,而类型模式只检查表达式的运行时的类型 ##### 2.2.1.1. 类型模式示例 只要类型匹配成功即可 ```csharp //switchType(new SwitchUse()) 输出 SwitchUse 对象类型 public static string switchType(object obj) { string result = obj switch { string => "string类型", int => "int类型", List<string> => "List<string> 类型", int[] => "int[] 类型", SwitchUse => "SwitchUse 对象类型", _ => "未知类型" }; return result; } public class SwitchUse { } ``` ###### 2.2.1.1.1. 类型为 T 的声明模式 类型为 T 的声明模式在表达式结果为非 null 且满足以下任一条件时与表达式匹配 + 表达式结果的运行时类型为 T。 + 表达式结果的运行时类型派生自类型 T,实现接口 T,或者存在从其到 T 的另一种隐式引用转换。 + 表达式结果可以为null的基础类型,当值为null时可与null匹配,下面示例代码的14行 **示例:**此示例满足上面所说的 ```csharp //switchTypeT(new SwitchUseInterface()); 输出 接口实现类型匹配SwitchUseInterface //switchTypeT(new SwitchUseChild()); 输出 本身类和继承类类型匹配SwitchUseChild //switchTypeT(new SwitchUse()); 输出 本身类和继承类类型匹配SwitchUse //switchTypeT(null); 输出 值为null public static string switchTypeT<T>(T obj) { Type r = obj?.GetType(); string result = obj switch { int => $"整型类型匹配{r.Name}", string => $"字符串类型匹配{r.Name}", ISwitchUse => $"接口实现类型匹配{r.Name}", SwitchUse => $"本身类和继承类类型匹配{r.Name}", null => "值为null" _ => "未知类型" }; ; return $"switchType:\n{result}"; } public class SwitchUse { } public interface ISwitchUse { } public class SwitchUseChild: SwitchUse { } public class SwitchUseInterface : ISwitchUse { } ``` ##### 2.2.1.2. 声明模式示例 类型匹配上之后,会将表达式结果赋值给声明的变量 ```csharp // switchDefine("2024-01-10") 输出 string类型,值是2024-01-10 // switchDefine(0) 输出 int类型,值是0 // switchDefine(new int[] { 11,12}) 输出 List<string> 类型,值是11 // switchDefine(new List<string> {"中国"}) 输出 int[] 类型,值是中国 // switchDefine(new SwitchUse() { p = 10}) 输出 SwitchUse 对象类型,值是10 public static string switchDefine(object obj) { string result = obj switch { string a => $"string类型,值是{a}", int b => $"int类型,值是{b}", List<string> c => $"List<string> 类型,值是{c[0]}", int[] d => $"int[] 类型,值是{d[0]}", SwitchUse e => $"SwitchUse 对象类型,值是{e.p}", _ => throw new NotImplementedException() }; ; return $"switchType:\n{result}"; } public class SwitchUse { public int p { get; set; } } ``` #### 2.2.2. 常量模式 下面的常量类型都可以匹配常量模式:数值文本(整型)、字符型、字符串、布尔值 true 或 false、enum 值、声明常量字段或本地的名称、null ```csharp //switchFiexd("2024-01-05") 输出 执行的是switchFiexd函数,结果是:工作日 public static string switchFiexd(object date) { string week = Convert.ToDateTime(date).DayOfWeek.ToString(); string msg = week switch { "Monday" => "工作日", "Tuesday" => "工作日", "Wednesday" => "工作日", "Thursday" => "工作日", "Friday" => "工作日", "Saturday" => "休息日", "Sunday" => "休息日", _ => "星期八" }; return $"执行的是switchFiexd函数,结果是:{msg}"; } ``` #### 2.2.3. 关系模式 关系模式感觉像是运算模式,是一个运算式子,表达式与一个常量(常量需要是一个可比较的常量,例如时间,数字,字符等),进行运算,在运算范围内则匹配成功,比较常用的基本运算符 =(可省略),>,<,>=,<= ```csharp //switchModelCalc("2024-01-05") 输出 执行的是switchModelCalc函数,结果是:2024-01-05 - 工作日 public static string switchModelCalc(object date) { int num = (int)(Convert.ToDateTime(date).DayOfWeek); // 周日是0,周六是6 string msg = num switch { > 0 and < 6 => "工作日", 0 or 6 => "休息日", _ => "星期八" }; return $"执行的是switchModelCalc函数,结果是:{date}-{msg}"; } ``` > Tips:上面示例代码中还使用到关键 and 和 or 这时使用了逻辑模式 #### 2.2.4. 逻辑模式 逻辑模式是使用 `not`,`and`,`or` 关键字来链接表达式,`not` 取式子的反结果匹配,`and` 需要左右两边的都匹配成功,`or` 左右两边只要有一边满足匹配即可 ```csharp //switchModelBool("2024-01-06") 输出 执行的是switchModelBool函数,结果是:2024-01-06 - 休息日 public static string switchModelBool(object date) { int num = (int)(Convert.ToDateTime(date).DayOfWeek); // 周日是0,周六是6 string msg = num switch { not (0 or 6) => $"[num={num}工作日]", 0 or 6 => $"[num{num}休息日]" }; return $"switchStatement:\n{date}-{msg}"; } ``` > tips:结合上面的关系模式示例看,这里只是补充了一下 not 的应用,还有这里没有使用弃元模式(`\_=>`),这时取反后已经包括了所有可能,没有可以放弃的可能范围了,当表达式中包含了所有可能时,弃元就不需要写了 模式组合器按从最高优先级到最低优先级排序,如下所示:not > and > or 当逻辑模式是表达式 is 的模式时,逻辑模式组合器的优先级高于逻辑运算符的优先级 (位逻辑运算符和 布尔逻辑运算符)。 否则,逻辑模式组合器的优先级低于逻辑运算符和条件逻辑运算符的优先级。 #### 2.2.5. 属性模式 从 C# 10 开始可以使用属性模式将表达式的属性或字段与嵌套模式进行匹配 **示例一** ```csharp // switchModelAttr("2024-01-01") 输出:switchModelAttr:2024-01-01-元旦 public static string switchModelAttr(object date) { DateTime t = Convert.ToDateTime(date); string msg = t switch { { Month: 1, Day: 1 } => "元旦", _ => "非节假日" }; ; return $"switchModelAttr:{date}-{msg}"; } ``` > Tips:在switch中使用属性模式时,记得属性需要使用{}包括 **示例二** ```csharp // switchModelObjectAttr("2024-01-01") 输出:switchModelObjectAttr:2024 // switchModelObjectAttr("2024") 输出:switchModelObjectAttr:(2024)长度小于等于4 // switchModelObjectAttr(new List<int> { 1,2,3,4}) 输出:switchModelObjectAttr:1,2 // switchModelObjectAttr(new People { name = "曹操", content = "三国魏王" }), 输出:switchModelObjectAttr:三国魏王 // switchModelObjectAttr(new People { name = "刘备", content = "汉昭烈帝" }), 输出:switchModelObjectAttr:无效值 public static string switchModelObjectAttr(object info) { string msg = info switch { string { Length: > 4 } s => s.Substring(0, 4), string s => s, List<int> { Count: > 2 } o => string.Join(',', new List<int>(o.Take(2))), People { name: "曹操" } obj => obj.content, _ => "非节假日" }; ; return $"switchModelObjectAttr:{msg}"; } public class People { public string name { get; set; } public string content { get; set; } } ``` #### 2.2.6. 位置模式 位置模式表达式是使用小括号包裹元素,用逗号分开各个元素的式子。在与位置模式进行匹配时,需要与位置模式中的每个成员进行匹配,满足了每个位置上的匹配规则才算这个位置模式匹配成功了,位置模式表达式中的成员也可以使用弃元,例如`(1,_,2)` ##### 2.2.6.1. 自定义类型匹配 ```csharp // switchModelPoint() 输出:switchModelPoint:正向水平面 public static string switchModelPoint() { Point t = new Point(1, 0, 1); string msg = t switch { (0, 0, 0) => "Origin", (1, 0, 1) => "正向水平面", // 返回的结果与这个位置模式匹配上 (0, 1, 1) => "正向纵向垂直面", _ => "Just a point" }; return $"switchModelPoint:{msg}"; } public readonly struct Point { public int X { get; } public int Y { get; } public int Z { get; } public Point(int x, int y, int z) => (X, Y, Z) = (x, y, z); public void Deconstruct(out int x, out int y, out int z) => (x, y, z) = (X, Y, Z); } ``` ##### 2.2.6.2. 元组类型匹配 从位置模式的表达式观察就可以知道它与元组类型很搭,因此使用元组类型表达式和位置模式表达式匹配是比较常用的一种写法 ```csharp public static string switchModelTuple(object obj) { Tuple<int, DayOfWeek> t = obj as Tuple<int, DayOfWeek>; string msg = (t.Item1, t.Item2) switch { ( <= 0, _) => throw new ArgumentException("参数错误"), (_, DayOfWeek.Saturday or DayOfWeek.Sunday) => "休息日", ( >= 5 and < 10, DayOfWeek.Monday) => "第二周的周一", ( >= 10, DayOfWeek.Monday) => "非第一周的周一", ( >= 5 and < 10, _) => "上旬", ( >= 10, _) => "中下旬", _ => "", }; return $"switchModelTuple:[{t.Item1}],[{t.Item2}],[{msg}]"; } // switchModelTuple(Tuple.Create(DateTime.Now.Day,DateTime.Now.DayOfWeek)) // 输出:switchModelTuple:[12],[Friday],[中下旬] ``` > **Tips:**位置模式中,每个位置都还可以其它模式的嵌套,例如上述示例中,嵌套了逻辑模式和关系模式 ##### 2.2.6.3. 类型检查与变量声明 在位置模式下,可以同时检查类型和变量的逻辑判断,例如下面的示例,定义了两个记录,通过匹配记录类型,再判断位置参数是否匹配 ```csharp public record Point2D(int X, int Y); public record Point3D(int X, int Y, int Z); static string PositiveTypeAndLogic(object point) => point switch { Point2D(> 0, > 0) p => p.ToString(), Point3D(> 0, > 0, > 0) p => p.ToString(), _ => string.Empty, }; public static string positionExample(object point) { msg += PositiveTypeAndLogic(new Point2D(10, 10)); msg += PositiveTypeAndLogic(new Point3D(10, 10, 0)); return msg; // 输出:Point2D { X = 10, Y = 10 } } ``` > Tips: record 是记录类型,详细请参见微软关于记录类型的说明 ##### 2.2.6.4. 位置模式与属性模式 在位置模式下,除了可以使用类型模式,逻辑模式,同样也可以使用属性模式 ```csharp public record Cuboid(int width, int height) { public int weight { get; set; } } public static string PositiveTypeAndAttr(object point) => point switch { Cuboid(> 0, > 0) { weight: > 0 } p => p.ToString(), // 位置模式和属性模式 _ => "弃元处理", }; public static string positionExample2(object point) { Cuboid c = new Cuboid(10, 20) { weight = 30 }; Cuboid c2 = new Cuboid(10, 20) { }; msg += PositiveTypeAndAttr(c); msg += PositiveTypeAndAttr(c2); return msg; // 输出:Cuboid { width = 10, height = 20, weight = 30 }弃元处理 } ``` > Tips:位置模式是一种递归模式。 也就是说,可以将任何模式用作嵌套模式。 #### 2.2.6. Var模式 在 swith 中使用 var 模式前,先了解下 var 模式,可使用 var 模式来匹配任何表达式(包括 null),并将其结果分配给新的局部变量 ```csharp public static int[] GetInts() { var rang = new Random(); return Enumerable.Range(0, 5).Select(i => rang.Next(0, 10)).ToArray(); } public static string VarExample(object obj) { if (GetInts() is var req && req.Min() > 2 && req.Max() > 5) { msg = $"最小值为:{req.Min()},最大值为:{req.Max()}"; } return msg; } ``` ##### 2.2.6.1. switch 表达式搭配 var 模式 switch 表达式中使用var模式,可以借助 when 关键字,使用方法有些类似SQL的case when ```csharp public record VarExampleObj(int x,int y); public static string VarSwithExample(object obj) => (obj as VarExampleObj) switch { var (x, y) when x < y => new VarExampleObj(0, 1).ToString(), var (x, y) when x > y => new VarExampleObj(1, 0).ToString(), var (x, y) when x == y => new VarExampleObj(1, 1).ToString(), _ => msg }; ``` > Tips:在 var 模式中,声明变量的类型是与该模式匹配的表达式的编译时类型 #### 2.2.7. 弃元模式 可使用弃元模式 _ 来匹配任何表达式,包括 null,如以下示例所示: ```csharp Console.WriteLine(GetDiscountInPercent(DayOfWeek.Friday)); // output: 5.0 Console.WriteLine(GetDiscountInPercent(null)); // output: 0.0 Console.WriteLine(GetDiscountInPercent((DayOfWeek)10)); // output: 0.0 static decimal GetDiscountInPercent(DayOfWeek? dayOfWeek) => dayOfWeek switch { DayOfWeek.Monday => 0.5m, DayOfWeek.Tuesday => 12.5m, DayOfWeek.Wednesday => 7.5m, DayOfWeek.Thursday => 12.5m, DayOfWeek.Friday => 5.0m, DayOfWeek.Saturday => 2.5m, DayOfWeek.Sunday => 2.0m, _ => 0.0m, }; ``` 示例中,弃元模式用于处理 null 以及没有相应的 DayOfWeek 枚举成员的任何整数值。 这可保证示例中的 switch 表达式可处理所有可能的输入值。 如果没有在 switch 表达式中使用弃元模式,并且该表达式的任何模式均与输入不匹配,则运行时会引发异常。 如果 switch 表达式未处理所有可能的输入值,则编译器会生成警告。 >Tips:弃元模式不能是 is 表达式或 switch 语句中的模式。 在这些案例中,要匹配任何表达式,请使用带有弃元 var _ 的 var 模式。 弃元模式可以是表达式 switch 中的模式。 #### 2.2.8. 括号模式 可在任何模式两边加上括号。 通常,这样做是为了强调或更改逻辑模式中的优先级,如以下示例所示 ```csharp public static string SymbolExample(object obj) => (obj as int[]) switch { var a when (a[0] > 10 || a[4] > 14) && a[2] == 13 => string.Join(',', a).ToString(), _ => msg }; ``` #### 2.2.9. 列表模式 从 C# 11 开始,可以将数组或列表与模式的序列进行匹配,当每个嵌套模式与输入序列的相应元素匹配时,列表模式就会匹配。 可使用列表模式中的任何模式,如以下示例所示 ```csharp public static string ListExample(object obj) => (obj as int[]) switch { //[11, .., 15] => "[11, .., 15]", //[..,14,15] => "[..,14,15]", //[.., 15] => "[..,15]", //[10 or 11 ,..] => "[10 or 11 ,..]", [> 0, .. { Length: 2 or 3 }, > 0] => "[< 0, .. { Length: 2 or 4 }, > 0]", // 列表模式嵌套属性模式 _ => msg }; ``` >Tips:示例将整个输入序列与列表模式匹配。 若要仅匹配输入序列开头或/和结尾的元素,请使用切片模式`..`,切片模式匹配零个或多个元素。 最多可在列表模式中使用一个切片模式。 切片模式只能显示在列表模式中 [========] > **参考资料:**微软文档<br/> [《模式匹配 - 模式中的 is 和 switch 表达式,以及 and、or 和 not 运算符》](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/patterns#relational-patterns)<br/>[《switch 语句》](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/selection-statements#the-switch-statement)
扫一扫手机看
点击排行
windows 文件资源管理器使用
Layui 弹窗open使用说明
SQL Server 中字符串函数使用
C# 使用QQ邮箱代理做邮件接收功能
SQL Server关于apply的两种形式cross apply 和 outer apply
ASP.NET MVC 模型的自定义验证
nvm安装及常用命令
SQL Server 中数据类型转换
SQL Server 中WITH AS短语的使用
IE浏览器下AJAX缓存问题导致数据不更新的解决办法
订阅号