
4.4 switch语句
像许多C派生语言一样,Go也有switch
语句。在一些语言中,开发者通常避免使用switch
语句,但Go的switch
语句不同,它没有对条件值的限制,并且不会连续执行case
语句[1],这使得switch
语句在Go语言中非常有用。
本节介绍
switch
语句,如果你已经熟悉Go,可以在第7章学习接口时深入了解type-switch
语句。
初看,Go中的switch
语句与在C/C++、Java或JavaScript中的switch
并没有什么不同,但Go语言的switch
还是给我们带来了些许小惊喜。你可以在The Go Playground(https://oreil.ly/VKf4N)上运行示例4-19中的代码。
示例4-19:switch语句

输出如下所示:

让我们根据switch
语句的特性来分析输出。与if
语句一样,在switch
中值比较语句周围不加括号。且与if
语句一样,也可以声明一个作用域仅在switch
语句所有分支内的变量。在上面的例子中,我们将变量word
的作用域限定在switch
语句的所有分支内。
所有case
从句(包括可选的default
从句)都包含在一组大括号中。但是需要注意,case
从句的内容周围没有大括号。case
从句(或default
从句)中的代码可以有多行,它们都是分支逻辑的一部分。
在case 5:
分支中,我们声明了一个新的变量wordLen
。这是一个新的代码块,可以在里面声明新的变量。就像任何其他代码块一样,在case
从句块中声明的任何变量只在该代码块中可见。
值得高兴的是,在switch
语句中不再需要将break
语句放在每一个case
从句的末尾。默认情况下,Go中的switch
语句里的case
不会连续执行。这比较符合Ruby或Pascal中的编程方式。
那么第一个问题是:如果case
分支不会连续执行,那么如何处理多个分支具有相同逻辑的情况呢?在Go中,可以使用逗号将多个匹配的值分开,在以上代码示例中,我们希望匹配1,2,3,4或者6,7,8,9,所以当字面是a
和cow
时执行的case
分支是一样的。
紧接着第二个问题:如果case
分支不会连续执行,那么空case
分支(例如上面代码示例中参数是6,7,8或9个字符的情况)会发生什么呢?在Go中,空case
分支意味着什么也不做。这就是为什么我们看到octopus
或gopher
作为参数的时候没有任何输出。
为了完整起见,Go确实包含了一个
fallthrough
关键字,它会接着执行下一个case
块。在使用它实现代码逻辑之前请三思,如果需要使用fallthrough
,请尝试重新组织代码逻辑,以消除case
分支彼此的依赖项。
我们在示例程序的switch
语句中使用了一个整型的值,但并不是只能这样。任何可以使用==
比较的类型均可以在switch
语句中使用,包括所有内置类型,除了切片、映射、通道、函数以及包含以上这些类型字段的结构体。
尽管不需要在每个case
从句的末尾使用break
语句,但还是可以为了提前退出case
分支而使用break
语句[2]。不过break
语句的使用可能表明代码逻辑太复杂了,需要考虑重构代码或者删除它。
在switch
语句的case
中还有其他情况可能会用到break
语句。如果for
循环中有一个switch
语句,而你想跳出for
循环时,可以在for
语句上加一个标签,并在break
语句处使用标签。如果你不使用标签,Go会假定只是结束一个case
分支。让我们看一个简单的例子,你可以在The Go Playground(https://oreil.ly/o2xg2)上运行示例4-20中的代码。
示例4-20:case分支缺少标签

运行这段代码会产生以下输出:

这明显不是我们的本意,我们的目的是当值为7的时候退出for
循环。因此我们需要引入一个标签,就像在跳出嵌套的for
循环时所做的那样。首先,对for
语句进行标记:

在break
语句处使用标签:

你可以在The Go Playground(https://oreil.ly/gA0O3)上看到新的代码。当再次运行它时,得到了我们期望的输出:

[1]Go中的switch
默认相当于每个case
最后带有break
,匹配成功后不会自动向下执行其他case
语句,而是跳出整个switch
,但是可以使用fallthrough
强制执行后面的case
语句中的代码。
[2]case
分支的最后一行使用break
与否效果是一样的。