什么是 switch 语句?
switch 是一个条件语句,它计算表达式并将其与可能匹配的列表进行比较,然后根据匹配执行代码块。它可以被认为是书写多个 if else
语句的替代方法。**
Example
让我们从一个简单的例子开始,该例子将手指数作为输入并输出该手指的名称:)。例如1 是拇指,2 是食指,依此类推。
package main
import (
"fmt"
)
func main() {
finger := 4
switch finger {
case 1:
fmt.Println("Thumb")
case 2:
fmt.Println("Index")
case 3:
fmt.Println("Middle")
case 4:
fmt.Println("Ring")
case 5:
fmt.Println("Pinky")
}
}
在上述程序中,switch finger
将 finger
的值与每个 case
语句进行比较。cases 从上到下进行比较,并执行与表达式匹配的第一个 case 。在这种 case 下,finger 的值为 4,因此程序得到打印
Finger 4 is Ring
cases 不允许出现重复
不允许使用相同常数值的重复 case。如果你试图运行下面的程序,编译器会报错 ./prog.go:19:7: duplicate case 4 in switch previous case at ./prog.go:17:7
package main
import (
"fmt"
)
func main() {
finger := 4
fmt.Printf("Finger %d is ", finger)
switch finger {
case 1:
fmt.Println("Thumb")
case 2:
fmt.Println("Index")
case 3:
fmt.Println("Middle")
case 4:
fmt.Println("Ring")
case 4: //duplicate case
fmt.Println("Another Ring")
case 5:
fmt.Println("Pinky")
}
}
default case
我们手中只有 5 个手指。如果输入错误的手指数会发生什么。这是 default case 。当其他 case 都不匹配时,将执行 default case。
package main
import (
"fmt"
)
func main() {
switch finger := 8; finger {
case 1:
fmt.Println("Thumb")
case 2:
fmt.Println("Index")
case 3:
fmt.Println("Middle")
case 4:
fmt.Println("Ring")
case 5:
fmt.Println("Pinky")
default: //default case
fmt.Println("incorrect finger number")
}
}
在上面的程序中,finger
是 8
并且它与任何 case 都不匹配,因此输出 incorrect finger number
。没有必要将 default 放在 switch 语句中的最后。它可以出现在 swich语句中的任何地方。
你可能还注意到了 finger
声明中的一个小变化。它在 switch 中声明。switch 可以包含一个可选语句,该语句在计算表达式之前执行。在这一行中 switch finger := 8; finger
首先被声明,也用于表达式中。在这种 case 下,finger
的范围仅限于 switch 代码块里。
多个表达式在 case 中
通过用逗号分隔多个表达式,可以在一个 case 中包含它们。
package main
import (
"fmt"
)
func main() {
letter := "i"
fmt.Printf("Letter %s is a ", letter)
switch letter {
case "a", "e", "i", "o", "u": //multiple expressions in case
fmt.Println("vowel")
default:
fmt.Println("not a vowel")
}
}
上面的程序可以发现 letter 是否是元音。第 11 行代码 case “a”, “e”, “i”, “o”, “u”: 匹配所有元音。因为 i 是元音,所以这个程序得到打印
Letter i is a vowel
省略的 switch 表达式
switch 中的表达式是可选的,可以省略。如果省略表达式,则认为 switch true
,并对每个case
表达式求真值,并执行相应的代码块。
package main
import (
"fmt"
)
func main() {
num := 75
switch { // expression is omitted
case num >= 0 && num <= 50:
fmt.Println("num is greater than 0 and less than 50")
case num >= 51 && num <= 100:
fmt.Println("num is greater than 51 and less than 100")
case num >= 101:
fmt.Println("num is greater than 100")
}
}
在上面的程序中,switch中没有该表达式,因此它被认为是 true,并对每种 case 求值。第 12 行代码 case num >= 51 && num <= 100:
语句为真,程序得到打印
75 is greater than 51 and less than 100
这种类型的 switch 可以作为多个 if else
语句的替代写法。
Fallthrough
在 Go 中,执行 case 后,control 立即从 switch 语句中出来。fallthrough 语句用于将控制权转移到紧随已执行的 case 之后出现的 case 的第一条语句。
让我们写一个程序来理解 fallthrough。我们的程序会检查输入的数字是否小于 50、100 或 200。例如,如果我们输入 75,程序将打印 75 小于 100 和 200。我们将使用 fallthrough 来实现。
package main
import (
"fmt"
)
func number() int {
num := 15 * 5
return num
}
func main() {
switch num := number(); { //num is not a constant
case num < 50:
fmt.Printf("%d is lesser than 50\n", num)
fallthrough
case num < 100:
fmt.Printf("%d is lesser than 100\n", num)
fallthrough
case num < 200:
fmt.Printf("%d is lesser than 200", num)
}
}
Switch 和 case 表达式不一定只是常量。它们也可以在运行时进行判断。在上面的程序中,第 14 行 num
初始化为函数 number()
的返回值。control 位于 switch 内部,并对 case 进行判断。第 18 行 case num < 100:
为真,程序输出 75 is lesser than 100
。下一个语句是 fallthrough
。当遇到 fallthrough
时,control 将移动到下一个 case 的第一个语句,并输出 75 is lesser than 200
。程序的输出为
75 is lesser than 100
75 is lesser than 200
fallthrough
应该在 case
语句中最后声明, 如果它出现在中间的某个地方,编译器将抛出错误 fallthrough statement out of place
。
即使 case 计算为 false,也会发生 Fallthrough 现象
在使用 fallthrough 时,有一个微妙的地方需要考虑。即使 case 被计算为 false,也会发生 fallthrough。
请思考以下程序
package main
import (
"fmt"
)
func main() {
switch num := 25; {
case num < 50:
fmt.Printf("%d is lesser than 50\n", num)
fallthrough
case num > 100:
fmt.Printf("%d is greater than 100\n", num)
}
}
在上面的程序中,num 为 25,小于 50,因此第 9 行的 case 计算为真。在第 11 行出现了一个 fallthrough。在第 12 行中 case num > 100: ,由于 num < 100,所以为假。但是 fallthrough 没有考虑到这一点。即使 case 的值为 false,也会发生 fallthrough。
上面的程序将打印
25 is lesser than 50
25 is greater than 100
因此,请确保你了解使用 fallthrough 时的操作。
还有一件事是,在没有 switch 的 case 下,不能在 switch 的最后一种 case 下使用 fallthrough。 如果在最后一种 case 下存在 fallthrough,则将导致以下编译错误。
cannot fallthrough final case in switch
退出 switch
break 语句可用于在 switch 完成之前提早终止 switch。 让我们将上面的示例修改为一个人为的示例,以了解 break 的工作原理。
让我们添加一个条件,如果 num 小于0,则 switch 应终止。
package main
import (
"fmt"
)
func main() {
switch num := -5; {
case num < 50:
if num < 0 {
break
}
fmt.Printf("%d is lesser than 50\n", num)
fallthrough
case num < 100:
fmt.Printf("%d is lesser than 100\n", num)
fallthrough
case num < 200:
fmt.Printf("%d is lesser than 200", num)
}
}
在上面的程序中,num 为 -5,当 control 到达第 10 行的 if 语句时,由于 num < 0,所以条件满足。在程序完成之前,break 语句终止了 switch,程序没有打印任何内容:)。
退出外部 for 循环
当 switch case 在 for 循环内部时,可能需要提前终止 for 循环。这可以通过给 for 循环打上标签,并在 switch 语句内使用该标签退出 for 循环来实现。我们来看一个例子。
让我们写一个程序来生成一个随机的偶数。
我们将创建一个无限的 for 循环,并使用 switch 情况来确定生成的随机数是否是偶数。如果是偶数,则打印生成的数字,并使用其标签终止 for 循环。rand 包的 Intn
函数用于生成非负的伪随机数。
package main
import (
"fmt"
"math/rand"
)
func main() {
randloop:
for {
switch i := rand.Intn(100); {
case i%2 == 0:
fmt.Printf("Generated even number %d", i)
break randloop
}
}
}
在上面的程序中,第 9 行 for 循环标签为 randloop。第 11 行使用的 Intn 函数,在 0 到 99 之间生成一个随机数(不包括 100)。如果生成的数字是偶数,则在第 14 行使用标签退出循环。
这个程序打印。
Generated even number 18
请注意,如果使用 break 语句而不使用标签,switch 语句只会被退出,循环会继续运行。所以给循环打上标签,并在 switch 里面的 break 语句中使用,是退出外层 for 循环的必要条件。
本教程到此结束。还有一种类型的 switch 叫做 type switch。我们在学习 interfaces 的时候会研究这个问题。