Menu

C# switch 语句:case、break、default 与多个 case

C# switch 语句的工作方式:对 int、string、char 和 enum 值进行 switch,为什么每个 case 都需要 break(错误 CS0163),叠加 case 标签匹配多个值,goto case,default,字符串的大小写敏感性,以及带 when 的模式 case 标签。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

switch 语句把一个值与一组常量 case 标签进行比较,并执行匹配的那一段。在 C# 中,每一段都必须以 break(或其他能离开该段的语句)结束,所以一个 case 永远不会悄悄地执行到下一个 case。

输出:

200: OK
302: redirect
404: not found
500: unhandled status

括号里的值只计算一次,然后与每个标签比较。没有任何匹配时执行 default;它是可选的,可以出现在列表中的任何位置,不过通常放在最后。如果没有匹配也没有 default,整个 switch 什么也不做。

每一段都需要 break

在 C、C++、Java 和 JavaScript 中,忘记写 break 会让执行滑入下一个 case。C# 把这个错误变成了编译错误:

switch (level)
{
    case 1:
        Console.WriteLine("read");
        // error CS0163: Control cannot fall through from one case label ('case 1:') to another
    case 2:
        Console.WriteLine("write");
        break;
}

规则是:一段语句列表的末尾不能是可达的。break 是满足这条规则的常用方式,但任何能离开该段的语句都可以:

  • return 离开整个方法,在小型辅助方法中这往往是最简洁的写法。
  • throw 针对永远不该出现的值抛出异常。
  • 当 switch 位于循环内时,continue 跳到下一次迭代。
  • goto case 和 goto default 有意跳到另一段。

最后一段也不例外。底部的 default: 如果没有 break,会报错 CS8070 Control cannot fall out of switch from final case label ('default:')。

一个 case 匹配多个值

中间没有语句的多个标签共享它们下面的那一段。这是 C# 唯一允许的贯穿,因为空标签里藏不住任何代码:

输出:

'O' is a vowel
'k' is a consonant or symbol
' ' is a space
'7' is a digit

这里每一段都以 return 结束,所以不需要 break(return 后面的 break 会是无法访问的代码)。两个值相同的标签会报错 CS0152 The switch statement contains multiple cases with the label value '1',而且每个标签都必须是编译时常量:在 case 中使用变量会报错“应输入常量值”。

对字符串进行 switch

除了整数,字符串是最常用来 switch 的值。比较按序号进行并区分大小写,所以在 switch 之前先规范化输入:

输出:

Starting the server
Stopping the server
Server is running
Unknown command: restart
Unknown command: (none)

如果没有 ToLowerInvariant,"STOP" 就会进入 default。对 null 值进行 switch 是合法的:它不匹配任何字符串常量,会落到 default,所以 switch 本身永远不会抛出异常。

对枚举进行 switch

枚举正是 switch 的用武之地:一组封闭的具名值。

输出:

Pending: wait for payment
Paid: pack and ship
Shipped: track the parcel
Delivered: nothing, order is closed
Cancelled: nothing, order is closed
ArgumentOutOfRangeException for value 42

编译器不会检查 switch 语句是否覆盖了每个枚举成员,而且枚举变量可以保存其底层类型的任意整数((OrderStatus)42 是合法的)。会 抛出异常 的 default 能把遗漏的 case 变成立即、明显的失败,而不是悄无声息地什么都不做。

goto case 和 goto default

当一个 case 确实应该继续执行另一个 case 时,C# 要求你把跳转写出来:

输出:

admin:
  manage users
  edit posts
  read posts
editor:
  edit posts
  read posts
guest:
  read posts

goto case 接受一个常量,它必须与同一个 switch 中的某个标签匹配。它在实际代码中很少见:共享的行为通常写成一个供多个 case 调用的方法会更清晰。但它仍然是“C# 中如何贯穿”这个问题的答案。

case 中的变量

整个 switch 块是一个作用域,所以两个 case 不能声明同名的变量:

switch (shape)
{
    case "square":
        int area = side * side;
        break;
    case "rectangle":
        int area = width * height;   // error CS0128: A local variable or function named 'area' is already defined in this scope
        break;
}

把每一段的主体用花括号包起来,就能给它一个独立的作用域:case "square": { int area = side * side; ... break; }。

循环中的 break

case 中的 break 离开的是 switch,而不是外面的循环。continue 正好相反:switch 不是循环,所以 continue 会进入循环的下一次迭代。

输出:

end marker found
Total: 134

循环在 "end" 之后继续执行,并加上了 99。要在 switch 内部停止循环,可以设置一个标志并在 switch 之后检查它,或者把循环移到一个方法里再用 return。break 和 continue 页面对这些做法进行了比较。

模式 case 标签与 when

从 C# 7.0 开始,case 标签可以是模式而不只是常量:它可以检查值的类型,还可以加上 when 条件。这种形式在现代 C# 代码中很常见:

static string Describe(object value)
{
    switch (value)
    {
        case null:
            return "nothing";
        case int n when n < 0:
            return $"negative int {n}";
        case int n:
            return $"int {n}";
        case string s when s.Length == 0:
            return "empty string";
        case string s:
            return $"string \"{s}\"";
        default:
            return $"other: {value.GetType().Name}";
    }
}

使用模式时,顺序很重要:第一个匹配的 case 胜出,而且如果某个 case 因为前面的 case 已经覆盖了它而永远无法到达,编译器会报错(把 case int n: 放在 case int n when n < 0: 上面就会失败)。when 子句也适用于常量 case:case 0 when isAdmin:。C# 9 新增了关系模式和逻辑模式,所以 case 可以写成 case >= 90: 或 case 'a' or 'e' or 'i':。

在没有模式的 C# 7 中,同样的类型分派要写成使用 is 类型模式的一串 if:

输出:

negative int -3
int 12
empty string
string "hi"
other: Decimal
nothing

当 switch 的每个分支都只是计算一个值时,switch 表达式还要更短:var grade = score switch { >= 90 => "A", >= 80 => "B", _ => "C" };。switch 表达式页面介绍了它的语法、模式和穷尽性规则。

可以对哪些类型进行 switch

值的类型常量 case说明
int、long、byte 及其他整数支持最经典的用法
char支持case 'a':
string支持按序号、区分大小写,null 进入 default
bool支持合法,但用 if 更清晰
任意 enum支持不检查是否穷尽
double、decimal从 C# 7 起只能用常量;范围需要 C# 9 的关系模式
任意其他类型从 C# 7 起通过类型模式,case Circle c:

常见问题

C# switch 中必须写 break 吗?

每个 case 段都必须以一条离开该段的语句结束:通常是 break,但 return、throw、continue(在循环内)或 goto case 也可以。从一个段的末尾落入下一个段是编译错误 CS0163 Control cannot fall through from one case label ('case 1:') to another,最后一个段也不例外。

C# switch 中如何让一个 case 处理多个值?

把标签叠在一起,中间不写任何内容:case "sat": case "sun": Console.WriteLine("weekend"); break;。空的 case 标签可以贯穿到下一个标签;只有包含语句的段才必须以 break 结束。在 C# 9 及更高版本中,模式 case 还可以写成 case 1 or 2 or 3:。

C# 中对字符串 switch 区分大小写吗?

区分。字符串 case 按序号、区分大小写的方式比较是否相等,所以 "Yes" 不会匹配 case "yes":。先用 switch (input.Trim().ToLowerInvariant()) 规范化值。null 字符串不匹配任何常量 case,会进入 default。

C# 的 switch 能像 C 或 Java 那样贯穿吗?

不能隐式贯穿。当你希望一个 case 继续执行另一个 case 时,要用 goto case <value>; 或 goto default; 明确写出来。这样每一次贯穿在代码里都清晰可见,这也是 C# 禁止隐式贯穿的原因。

C# 中 switch 语句和 switch 表达式有什么区别?

switch 语句执行语句,需要 case、break 和 default。switch 表达式(C# 8 及更高版本)产生一个值:var label = n switch { 0 => "zero", > 0 => "positive", _ => "negative" };。当每个分支都只是计算一个结果时,用表达式。

Coddy 编程语言插图

和 Coddy 一起学编程

开始