C# 语言中的多分支选择器 – Switch 深度探讨
在 C# 编程中,当我们需要根据一个表达式的值执行不同的代码块时,switch
语句提供了一种比多重 if-else if
结构更清晰、更高效的方式。switch
语句评估一个表达式,然后将该表达式的值与多个 case
标签进行比较,并执行与匹配的 case
标签关联的代码块。本文将深入探讨 switch
语句的各个方面,包括其语法、用法、最佳实践、性能考虑以及与 C# 语言版本相关的演变。
1. Switch 语句的基本语法
switch
语句的基本语法如下:
csharp
switch (expression)
{
case constant-expression1:
// 当 expression 的值等于 constant-expression1 时执行的代码
break;
case constant-expression2:
// 当 expression 的值等于 constant-expression2 时执行的代码
break;
// ... 更多的 case 标签 ...
default:
// 当 expression 的值与任何 case 标签都不匹配时执行的代码
break;
}
组成部分:
-
switch (expression)
:expression
: 这是一个要评估的表达式。表达式的结果类型必须是以下类型之一:sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
char
string
bool
(从 C# 7.0 开始)- 枚举类型 (
enum
) - 可为空的类型(如果基础类型是允许的
switch
类型)
- 括号是必需的。
-
case constant-expression:
constant-expression
: 这是一个常量表达式,其类型必须与switch
表达式的类型兼容(可隐式转换)。- 每个
case
标签都必须有一个唯一的常量表达式。不允许重复的case
值。 - 冒号 (
:
) 是必需的。
-
break;
break
语句用于终止switch
语句的执行。它将控制权转移到switch
语句之后的下一条语句。- 在大多数情况下,每个
case
块的末尾都需要一个break
语句。如果没有break
语句,控制流将 “贯穿” 到下一个case
块(见下文的 “贯穿行为” 部分)。 - 除了
break
,还可以使用return
、throw
、goto
等语句来终止case
块。
-
default:
default
标签是可选的。- 如果
switch
表达式的值与任何case
标签的常量表达式都不匹配,则执行default
块中的代码。 default
标签通常放在switch
语句的末尾,但这不是必需的。它可以出现在任何位置。default
块中通常也需要一个break
语句(或其他终止语句)。
2. Switch 语句的用法示例
让我们通过一些示例来了解 switch
语句的用法:
示例 1:根据整数值执行不同的操作
“`csharp
int dayOfWeek = 3; // 假设 1 代表星期一,2 代表星期二,依此类推
switch (dayOfWeek)
{
case 1:
Console.WriteLine(“星期一”);
break;
case 2:
Console.WriteLine(“星期二”);
break;
case 3:
Console.WriteLine(“星期三”);
break;
case 4:
Console.WriteLine(“星期四”);
break;
case 5:
Console.WriteLine(“星期五”);
break;
case 6:
case 7: // 周六和周日执行相同的代码
Console.WriteLine(“周末”);
break;
default:
Console.WriteLine(“无效的星期几”);
break;
}
“`
示例 2:根据字符串值执行不同的操作
“`csharp
string fruit = “apple”;
switch (fruit)
{
case “apple”:
Console.WriteLine(“苹果”);
break;
case “banana”:
Console.WriteLine(“香蕉”);
break;
case “orange”:
Console.WriteLine(“橙子”);
break;
default:
Console.WriteLine(“未知的水果”);
break;
}
“`
示例 3:使用枚举类型
“`csharp
enum Color
{
Red,
Green,
Blue
}
Color myColor = Color.Green;
switch (myColor)
{
case Color.Red:
Console.WriteLine(“红色”);
break;
case Color.Green:
Console.WriteLine(“绿色”);
break;
case Color.Blue:
Console.WriteLine(“蓝色”);
break;
}
“`
3. 贯穿行为 (Fall-through)
在 C# 中,switch
语句默认情况下不允许 “贯穿” 行为。也就是说,如果一个 case
块没有以 break
、return
、throw
或 goto
语句结束,编译器会报错。
不允许的贯穿示例:
“`csharp
int number = 1;
switch (number)
{
case 1:
Console.WriteLine(“One”); // 缺少 break; 会导致编译错误
case 2:
Console.WriteLine(“Two”);
break;
}
“`
显式贯穿 (使用 goto case
)
如果你确实需要从一个 case
块 “贯穿” 到另一个 case
块,可以使用 goto case
语句显式地实现:
“`csharp
int number = 1;
switch (number)
{
case 1:
Console.WriteLine(“One”);
goto case 2; // 显式地跳转到 case 2
case 2:
Console.WriteLine(“Two”);
break;
case 3:
Console.WriteLine(“Three”);
break;
}
“`
注意: 尽管可以使用 goto case
实现贯穿,但通常不建议这样做,因为它会使代码更难理解和维护。在大多数情况下,更好的做法是重构代码以避免贯穿。
4. Switch 表达式 (C# 8.0 及更高版本)
从 C# 8.0 开始,引入了 switch
表达式。switch
表达式是一种更简洁、更具表达性的方式来编写 switch
逻辑,它将 switch
作为表达式而不是语句,可以直接返回值。
switch
表达式的语法:
csharp
variable = expression switch
{
pattern1 => expression1,
pattern2 => expression2,
// ... 更多的模式 ...
_ => defaultExpression // 可选的 discard 模式
};
组成部分:
expression switch
:switch
关键字现在紧跟在要评估的表达式之后。pattern => expression
:pattern
: 这可以是常量模式、类型模式、属性模式、位置模式等(见下文的 “模式匹配” 部分)。=>
: 箭头符号,用于分隔模式和表达式。expression
: 如果模式匹配,则返回此表达式的值。
_
: discard 模式,类似于switch
语句中的default
,用于处理所有其他情况(可选)。
switch
表达式示例:
“`csharp
int number = 2;
string result = number switch
{
1 => “One”,
2 => “Two”,
3 => “Three”,
_ => “Other” // 如果 number 不是 1、2 或 3,则返回 “Other”
};
Console.WriteLine(result); // 输出: Two
“`
与 switch
语句的区别:
switch
表达式返回一个值,而switch
语句不返回值。switch
表达式使用箭头符号 (=>
),而switch
语句使用冒号 (:
)。switch
表达式的每个分支都必须是一个表达式,不能包含语句块。switch
表达式通常更简洁,尤其是对于简单的switch
逻辑。switch
表达式必须穷举所有可能。
5. 模式匹配 (C# 7.0 及更高版本)
从 C# 7.0 开始,switch
语句和 switch
表达式都支持模式匹配。模式匹配允许你根据表达式的类型和结构来匹配 case
标签。
常见的模式类型:
- 常量模式: 与常量值进行比较(与早期版本的
switch
相同)。 - 类型模式: 检查表达式是否为特定类型,如果是,则将表达式转换为该类型的变量。
- 属性模式: 检查对象是否具有特定的属性值。
- 列表模式: 检查集合是否和模板列表匹配。
- 位置模式: 解构对象并将其组成部分与模式进行匹配。
- 关系模式: 将表达式与常量进行比较(
>
,<
,>=
,<=
)。 - 逻辑模式: 使用
and
,or
,not
组合模式。 - Var 模式 捕获表达式的值,并将其赋给一个新变量。
- Discard 模式 (
_
): 匹配任何表达式(通常用作默认情况)。
模式匹配示例:
“`csharp
object obj = “Hello”;
switch (obj)
{
case int i: // 类型模式:如果 obj 是 int,则将其赋值给变量 i
Console.WriteLine($”这是一个整数:{i}”);
break;
case string s: // 类型模式:如果 obj 是 string,则将其赋值给变量 s
Console.WriteLine($”这是一个字符串:{s}”);
break;
case null: // 常量模式:如果 obj 是 null
Console.WriteLine(“对象是 null”);
break;
default:
Console.WriteLine(“未知类型”);
break;
}
“`
switch
表达式中的模式匹配:
“`csharp
object obj = new { Name = “John”, Age = 30 };
string description = obj switch
{
string s => $”这是一个字符串:{s}”,
{ Name: “John”, Age: int age } => $”姓名是 John,年龄是 {age}”, // 属性模式
_ => “未知对象”
};
Console.WriteLine(description); // 输出: 姓名是 John,年龄是 30
“`
6. Switch 语句的最佳实践
以下是一些使用 switch
语句的最佳实践:
- 保持
case
块简短: 每个case
块中的代码应尽可能简短。如果case
块中的逻辑很复杂,请考虑将其提取到一个单独的方法中。 - 使用
default
块进行错误处理: 始终包含一个default
块来处理意外的输入值。这有助于提高代码的健壮性。 - 避免深度嵌套的
switch
语句: 深度嵌套的switch
语句会使代码难以阅读和维护。如果需要多层分支,请考虑使用其他控制流结构,如if-else if
或将逻辑分解为更小的方法。 - 使用枚举类型: 当
switch
表达式的值是有限的几个选项时,使用枚举类型可以使代码更具可读性和可维护性。 - 考虑使用
switch
表达式 (C# 8.0 及更高版本): 对于简单的switch
逻辑,switch
表达式通常更简洁、更具表达性。 - 利用模式匹配 (C# 7.0 及更高版本): 模式匹配可以使
switch
语句更强大、更灵活。
7. Switch 语句的性能考虑
在大多数情况下,switch
语句的性能非常好,尤其是当 case
标签是常量时。编译器通常会将 switch
语句优化为跳转表 (jump table) 或哈希表,这使得查找匹配的 case
标签非常快。
与 if-else if
的比较:
- 当有少量分支时,
if-else if
和switch
的性能差异通常可以忽略不计。 - 当有大量分支时,
switch
语句通常比if-else if
更快,因为switch
可以使用跳转表或哈希表进行优化,而if-else if
需要逐个检查每个条件。
影响 switch
性能的因素:
case
标签的数量:case
标签越多,跳转表或哈希表就越大,查找匹配项所需的时间可能就越长。case
标签的类型: 整数类型的case
标签通常比字符串类型的case
标签更快,因为整数比较比字符串比较更简单。- 模式匹配的复杂性: 复杂的模式匹配(如属性模式、位置模式)可能比简单的常量模式或类型模式需要更多的计算。
优化 switch
性能的技巧:
- 将最常见的
case
放在前面: 如果某些case
标签比其他case
标签更可能匹配,请将它们放在switch
语句的开头。这可以减少平均查找时间。 - 使用整数或枚举类型作为
switch
表达式: 如果可能,请使用整数或枚举类型,而不是字符串类型。 - 避免复杂的模式匹配: 如果性能至关重要,请尽量使用简单的模式匹配。
8. C# 语言版本中的 Switch 演变
switch
语句在 C# 语言的各个版本中不断演变,增加了新的功能和改进:
- C# 1.0: 最初的
switch
语句只支持整数类型和枚举类型。 - C# 2.0: 增加了对字符串类型的支持。
- C# 7.0: 引入了模式匹配,极大地增强了
switch
语句的功能。 - C# 8.0: 引入了
switch
表达式,提供了一种更简洁、更具表达性的方式来编写switch
逻辑。 - C# 9.0: 进一步增强了模式匹配,增加了关系模式和逻辑模式。
- C# 11: 增加了列表模式。
9. 总结
switch
语句是 C# 中一个强大而灵活的多分支选择器。它提供了一种比多重 if-else if
结构更清晰、更高效的方式来根据表达式的值执行不同的代码块。通过了解 switch
语句的语法、用法、最佳实践、性能考虑以及在 C# 语言版本中的演变,你可以编写出更清晰、更易于维护、更高效的代码。
希望这篇文章能够帮助你深入理解 C# 中的 switch
语句。如果你有任何问题,请随时提出。