func readByExt(ext string) {
if ext == "json" {
println("read json file")
} else if ext == "jpg" || ext == "jpeg" || ext == "png" || ext == "gif" {
println("read image file")
} else if ext == "txt" || ext == "md" {
println("read text file")
} else if ext == "yml" || ext == "yaml" {
println("read yaml file")
} else if ext == "ini" {
println("read ini file")
} else {
println("unsupported file extension:", ext)
}
}
如果用 switch 改写上述例子代码,我们可以这样来写:
func readByExtBySwitch(ext string) {
switch ext {
case "json":
println("read json file")
case "jpg", "jpeg", "png", "gif":
println("read image file")
case "txt", "md":
println("read text file")
case "yml", "yaml":
println("read yaml file")
case "ini":
println("read ini file")
default:
println("unsupported file extension:", ext)
}
}
Go 语言中 switch 语句的一般形式:
switch initStmt; expr {
case expr1:
// 执行分支1
case expr2:
// 执行分支2
case expr3_1, expr3_2, expr3_3:
// 执行分支3
case expr4:
// 执行分支4
... ...
case exprN:
// 执行分支N
default:
// 执行默认分支
}
Go 先对 switch expr 表达式进行求值,然后再按 case 语句的出现顺序,从上到下进行逐一求值。
无论 default 分支出现在什么位置,它都只会在所有 case 都没有匹配上的情况下才会被执行的。
只要类型支持比较操作,都可以作为 switch 语句中的表达式类型。比如整型、布尔类型、字符串类型、复数类型、元素类型都是可比较类型的数组类型,甚至字段类型都是可比较类型的结构体类型,也可以。下面就是一个使用自定义结构体类型作为 switch 表达式类型的例子:
type person struct {
name string
age int
}
func main() {
p := person{"tom", 13}
switch p {
case person{"tony", 33}:
println("match tony")
case person{"tom", 13}:
println("match tom")
case person{"lucy", 23}:
println("match lucy")
default:
println("no match")
}
}
首先,switch 语句各表达式的求值结果可以为各种类型值,只要它的类型支持比较操作就可以了。
第二点:switch 语句支持声明临时变量。
第三点:case 语句支持表达式列表。
第四点:取消了默认执行下一个 case 代码逻辑的语义。
type switch
switch 关键字后面跟着的表达式为x.(type),这种表达式形式是 switch 语句专有的,而且也只能在 switch 语句中使用。这个表达式中的 x 必须是一个接口类型变量,表达式的求值结果是这个接口类型变量对应的动态类型。
跳不出循环的 break
func main() {
var sl = []int{5, 19, 6, 3, 8, 12}
var firstEven int = -1
// find first even number of the interger slice
for i := 0; i < len(sl); i++ {
switch sl[i] % 2 {
case 0:
firstEven = sl[i]
break
case 1:
// do nothing
}
}
println(firstEven)
}
我们运行一下这个修改后的程序,得到结果为 12。
这就是 Go 中 break 语句与 switch 分支结合使用会出现一个“小坑”。和我们习惯的 C 家族语言中的 break 不同,Go 语言规范中明确规定,不带 label 的 break 语句中断执行并跳出的,是同一函数内 break 语句所在的最内层的 for、switch 或 select。所以,上面这个例子的 break 语句实际上只跳出了 switch 语句,并没有跳出外层的 for 循环,这也就是程序未按我们预期执行的原因。
要修正这一问题,我们可以利用上节课学到的带 label 的 break 语句试试。这里我们也直接看看改进后的代码:
func main() {
var sl = []int{5, 19, 6, 3, 8, 12}
var firstEven int = -1
// find first even number of the interger slice
loop:
for i := 0; i < len(sl); i++ {
switch sl[i] % 2 {
case 0:
firstEven = sl[i]
break loop
case 1:
// do nothing
}
}
println(firstEven) // 6
}
在改进后的例子中,我们定义了一个 label:loop,这个 label 附在 for 循环的外面,指代 for 循环的执行。当代码执行到“break loop”时,程序将停止 label loop 所指代的 for 循环的执行。关于带有 label 的 break 语句,你可以再回顾一下第 19 讲,这里就不多说了。
在改进后的例子中,我们定义了一个 label:loop,这个 label 附在 for 循环的外面,指代 for 循环的执行。当代码执行到“break loop”时,程序将停止 label loop 所指代的 for 循环的执行。关于带有 label 的 break 语句,你可以再回顾一下第 19 讲,这里就不多说了。
此文章为3月Day11学习笔记,内容来源于极客时间《Tony Bai · Go 语言第一课》。