C#语言中的多分支选择器-Switch深度探讨 – wiki基地

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,还可以使用 returnthrowgoto 等语句来终止 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 块没有以 breakreturnthrowgoto 语句结束,编译器会报错。

不允许的贯穿示例:

“`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 ifswitch 的性能差异通常可以忽略不计。
  • 当有大量分支时,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 语句。如果你有任何问题,请随时提出。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部