写点什么

Go 学习笔记——Switch(我不是游戏机)

作者:为自己带盐
  • 2022 年 4 月 22 日
  • 本文字数:3056 字

    阅读完需:约 10 分钟

Go 学习笔记——Switch(我不是游戏机)

书接上回《Go学习笔记-Only For》


开篇

这应该是最后一篇关于语法知识的基础篇啦,其实基础知识本来应该很快就过,但因为是从头学起的,所以基础篇花的时间稍微长了一点。还是得提两句,近期的工作压力真的还是挺大的,项目一个接一个,小编我都快烦死了~~,好在最近进展都还不错。今天周五,趁着有点闲功夫,把这篇也给整理整理更了吧!


经典形式

聊回 Switch 这个语法结构,和之前的 for,if 一样,在很多其他类型的编程语言中都有支持,如果之前有学过其他的语言,我个人感觉对 Go 中对应的语法,应该是完全没有理解上的障碍的。

这里我就记几个经典形式

  • switch 语句,由 switch 关键字开始,后面通常接一个表达式;

  • switch 后面的大括号内,是一个个代码执行分支,每个分支以 case 关键字开始,后面也是一个表达式或者逗号分隔的多个表达式;

  • go 编译器会按照 case 的出现顺序,自上而下,由左及右(case 后接多个表达式),逐个匹配各个 case 分支,当遇到匹配项后,则 switch 语句结束,匹配项后边如果还有其他 case,也不再执行;

  • go 里的 switch 语句也有 default 默认分支,当没有匹配到任何 case 时,会执行默认分支,而且,不论它出现在哪里,都只会在没有任何 case 匹配的情况下才被执行;


swtich 在 go 中的特色和灵活性

  • swtich 中的表达式的求值结果可以为各种支持比较操作的类型值

  • 当 switch 表达式的类型为布尔类型时,如果求值结果始终为 true,那么我们甚至可以省略 switch 后面的表达式


// 带有initStmt语句的switch语句(注意,initStmt后的分号不能省!!)switch initStmt; { case bool_expr1: case bool_expr2: ... ...}
// 没有initStmt语句的switch语句switch { case bool_expr1: case bool_expr2: ... ...}
复制代码
  • switch 语句支持声明临时变量,仅作用在 swtich 中的代码块中

  • case 语句支持表达式列表

//case不支持表达式列表时(这是C的例子)void check_work_day(int a) {    switch(a) {        case 1:        case 2:        case 3:        case 4:        case 5:            printf("it is a work day\n");            break;        case 6:        case 7:            printf("it is a weekend day\n");            break;        default:            printf("do you live on earth?\n");    }}
//case支持表达式列表后,代码美观性飙升func checkWorkday(a int) { switch a { case 1, 2, 3, 4, 5: println("it is a work day") case 6, 7: println("it is a weekend day") default: println("are you live on earth") }}
复制代码
  • 取消了默认执行下一个 case 代码逻辑的语义,即每个 case 逻辑结束,不用加 break 关键字了(C 语言系列的语言好像都得加,C 需要,C#也需要,C++应该也需要吧,不太清楚)

  • fallthrough 关键字,显示实现多个 case 都执行的场景

package main
func case1() int { println("eval case1 expr") return 2}
func case2_1() int { println("eval case2_1 expr") return 0}func case2_2() int { println("eval case2_2 expr") return 2}
func switchexpr() int { println("eval switch expr") return 2}
func main() { switch switchexpr() { case case1(): println("exec case1") fallthrough //如果这里没有fallthrough关键字,执行到这里,就结束了,后边虽然也能匹配结果,但不会执行 case case2_1(), case2_2()://增加关键字后,不会计算表达式,而是直接执行匹配项后的代码 println("exec case2") fallthrough //因为这里也有关键字,所以default分支里的代码也会被执行 default: println("exec default") }}
复制代码

上边这段代码,如果我不在 case1()的代码块后加 fallthrough 关键字,虽然后边 case2_2()的表达式也能匹配 Switch 条件,但也不会执行,加上关键字后,它就回去执行 case2_1(),case2_2()里的代码块,但注意,由于 fallthrough 的存在,Go 不会对 case2 的表达式做求值操作,而会直接执行 case2 对应的代码分支。而且,如果分支里都有 fallthrough 关键字,default 分支里的代码也会被执行!


特殊用例-type switch

  • switch 关键字后面跟着的表达式为 x.(type),这种表达式形式是 switch 语句专有的,而且也只能在 switch 语句中使用

  • 表达式中的 x 必须是一个接口类型变量,表达式的求值结果是这个接口类型变量对应的动态类型

func main() {	var x interface{} = 13	switch x.(type) {	case nil:		println("x is nil")	case int:		println("the type of x is int")	case string:		println("the type of x is string")	case bool:		println("the type of x is string")	default:		println("don't support the type")	}}
复制代码



  • 通过 x.(type),我们除了可以获得变量 x 的动态类型信息之外,也能获得其动态类型对应的值信息,上边的例子,(改造后如下)这里我们将 switch 后面的表达式由 x.(type)换成了 v := x.(type)。对于后者,你千万不要认为变量 v 存储的是类型信息,其实 v 存储的是变量 x 的动态类型对应的值信息,这样我们在接下来的 case 执行路径中就可以使用变量 v 中的值信息了

func main() {	var x interface{} = 13	switch v := x.(type) {	case nil:		println("v is nil")	case int:		println("the type of v is int, v =", v)	case string:		println("the type of v is string, v =", v)	case bool:		println("the type of v is bool, v =", v)	default:		println("don't support the type")	}}
复制代码



  • 如果在 switch 后面使用了某个特定的接口类型 I,那么 case 后面就只能使用实现了接口类型 I 的类型了,否则 Go 编译器会报错。下面的例子,只有类型 T 实现了接口类型 I,Go 原生类型 int 与 string 都没有实现接口 I,于是在编译上述代码时,编译器会报错

type I interface {	M()}
type T struct {}
func (T) M() {}
func main() { var t T var i I = t switch i.(type) { case T: println("it is type T") case int: //编辑器报错 println("it is type int") case string: //编辑器报错 println("it is type string") }}
复制代码



跳不出循环的 break

  • 不带 label 的 break 语句中断执行并跳出的,是同一函数内 break 语句所在的最内层的 for、switch 或 select

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) }
复制代码

上边这段,预期的结果应该是输出 s1 中第一个偶数,也就是 6,但实际输出了 12。就是因为 break 并没有跳出 for 循环,只是跳出了 switch。修正后的代码可以是下面这样


func main() { var sl = []int{5, 19, 6, 3, 8, 12} var firstEven int = -1
// find first even number of the interger sliceloop: 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 标签,break 直接的就跳到了 loop:标签外,也就修复了上面那个问题,得到了预期的结果。


ok,今天就这些了。


发布于: 刚刚阅读数: 6
用户头像

学着码代码,学着码人生。 2019.04.11 加入

狂奔的小码农

评论

发布
暂无评论
Go 学习笔记——Switch(我不是游戏机)_Go_为自己带盐_InfoQ写作社区