Go switch 详解

Go 编程实战系列(十八)

上一篇《Go if 判断详解》中,我们学习了如何使用 ifelse ifelse 根据不同条件控制程序执行流程。

如果只有两三个条件,使用 if 非常方便。但是当需要判断大量固定值时,如果一直使用 else if,代码就会变得越来越长。

例如:

lua 复制代码
if status == 1 {
	fmt.Println("待付款")
} else if status == 2 {
	fmt.Println("待发货")
} else if status == 3 {
	fmt.Println("已发货")
} else if status == 4 {
	fmt.Println("已完成")
} else {
	fmt.Println("未知状态")
}

这种场景使用 switch 会更加清晰。

Go 中的 switch 功能比很多语言更加灵活,不仅可以匹配固定值,还可以进行条件判断、多个值匹配以及类型判断。

本文主要介绍:

  • switch 基本语法

  • case 与 default

  • 一个 case 匹配多个值

  • 无表达式 switch

  • switch 与 if 的区别

  • break

  • fallthrough

  • 类型 switch

  • switch 作用域

  • 实际业务案例

一、switch 是什么?

switch 是 Go 中的一种流程控制语句。

它的主要作用是:

根据一个值或者条件,从多个分支中选择一个执行。

基本语法:

arduino 复制代码
switch 表达式 {
case 值1:
	执行代码
case 值2:
	执行代码
default:
	默认代码
}

例如:

go 复制代码
package main

import "fmt"

func main() {
	day := 1

	switch day {
	case 1:
		fmt.Println("星期一")
	case 2:
		fmt.Println("星期二")
	case 3:
		fmt.Println("星期三")
	default:
		fmt.Println("未知")
	}
}

输出:

复制代码
星期一

程序会将:

sql 复制代码
day

依次与不同 case 进行匹配。

因为:

ini 复制代码
day == 1

所以执行:

arduino 复制代码
case 1:

对应的代码。

二、case 分支

case 表示不同的匹配情况。

例如:

css 复制代码
status := 2

switch status {
case 1:
	fmt.Println("待付款")
case 2:
	fmt.Println("待发货")
case 3:
	fmt.Println("已发货")
case 4:
	fmt.Println("已完成")
}

输出:

复制代码
待发货

每一个 case 都可以包含多行代码:

css 复制代码
switch status {
case 1:
	fmt.Println("订单状态:待付款")
	fmt.Println("请尽快完成付款")
case 2:
	fmt.Println("订单状态:待发货")
	fmt.Println("仓库正在处理")
}

三、default 默认分支

如果没有任何 case 匹配,可以使用:

arduino 复制代码
default:

处理默认情况。

例如:

css 复制代码
status := 100

switch status {
case 1:
	fmt.Println("正常")
case 2:
	fmt.Println("禁用")
default:
	fmt.Println("未知状态")
}

输出:

复制代码
未知状态

default 并不是必须存在的。

例如:

arduino 复制代码
switch status {
case 1:
	fmt.Println("正常")
case 2:
	fmt.Println("禁用")
}

如果没有匹配项,程序直接继续执行 switch 后面的代码。

不过在业务状态判断中,增加 default 通常可以帮助我们发现异常数据。

四、字符串 switch

switch 不只能判断整数,也可以判断字符串。

例如:

go 复制代码
role := "admin"

switch role {
case "admin":
	fmt.Println("管理员")
case "user":
	fmt.Println("普通用户")
case "guest":
	fmt.Println("游客")
default:
	fmt.Println("未知角色")
}

输出:

复制代码
管理员

实际开发中非常常见,例如判断:

复制代码
用户角色
请求类型
订单状态
文件类型
支付方式
环境名称

例如:

go 复制代码
method := "GET"

switch method {
case "GET":
	fmt.Println("查询数据")
case "POST":
	fmt.Println("创建数据")
case "PUT":
	fmt.Println("修改数据")
case "DELETE":
	fmt.Println("删除数据")
}

五、一个 case 匹配多个值

Go 的 case 可以同时匹配多个值。

例如:

css 复制代码
day := 6

switch day {
case 1, 2, 3, 4, 5:
	fmt.Println("工作日")
case 6, 7:
	fmt.Println("周末")
default:
	fmt.Println("日期错误")
}

输出:

复制代码
周末

多个值使用逗号分隔:

arduino 复制代码
case 6, 7:

相当于:

ini 复制代码
day == 6
或者
day == 7

再例如:

go 复制代码
ext := ".jpg"

switch ext {
case ".jpg", ".jpeg", ".png", ".gif":
	fmt.Println("图片文件")
case ".mp4", ".avi", ".mov":
	fmt.Println("视频文件")
default:
	fmt.Println("其他文件")
}

这种写法比多个 if || 更容易阅读。

六、Go switch 默认不需要 break

这是 Go 和 C、Java 等语言比较明显的区别。

在一些语言中:

arduino 复制代码
case 执行完成

如果没有 break,可能继续执行下一个 case

Go 默认不会这样。

例如:

dart 复制代码
num := 1

switch num {
case 1:
	fmt.Println("一")
case 2:
	fmt.Println("二")
case 3:
	fmt.Println("三")
}

只会输出:

复制代码

不会继续执行:

复制代码
二
三

因此 Go 中通常不需要:

kotlin 复制代码
break

代码更加简洁。

七、switch 中的 break

虽然普通情况下不需要 break,但 Go 仍然支持它。

例如:

arduino 复制代码
switch status {
case 1:
	fmt.Println("开始处理")

	if errorCondition {
		break
	}

	fmt.Println("继续处理")
}

这里的 break 可以提前结束当前 switch

不过普通的 case 分支没有必要这样写:

arduino 复制代码
case 1:
	fmt.Println("正常")
	break

因为 case 执行完成后本来就会退出 switch。

八、无表达式 switch

Go 的 switch 有一个非常实用的功能:

可以不写表达式。

例如:

css 复制代码
score := 85

switch {
case score >= 90:
	fmt.Println("优秀")
case score >= 80:
	fmt.Println("良好")
case score >= 60:
	fmt.Println("及格")
default:
	fmt.Println("不及格")
}

输出:

复制代码
良好

这种写法实际上类似:

arduino 复制代码
if score >= 90 {
	fmt.Println("优秀")
} else if score >= 80 {
	fmt.Println("良好")
} else if score >= 60 {
	fmt.Println("及格")
} else {
	fmt.Println("不及格")
}

无表达式 switch 特别适合多个条件区间判断。

九、switch 条件顺序

else if 一样,无表达式 switch 也需要注意条件顺序。

错误示例:

css 复制代码
score := 95

switch {
case score >= 60:
	fmt.Println("及格")
case score >= 90:
	fmt.Println("优秀")
}

输出:

复制代码
及格

因为:

复制代码
score >= 60

已经成立,后面的条件不会继续判断。

正确:

arduino 复制代码
switch {
case score >= 90:
	fmt.Println("优秀")
case score >= 80:
	fmt.Println("良好")
case score >= 60:
	fmt.Println("及格")
default:
	fmt.Println("不及格")
}

一般应该把范围更严格的条件放在前面。

十、switch 初始化语句

if 类似,switch 也可以包含初始化语句。

语法:

arduino 复制代码
switch 初始化语句; 表达式 {
}

例如:

dart 复制代码
switch num := 2; num {
case 1:
	fmt.Println("一")
case 2:
	fmt.Println("二")
default:
	fmt.Println("其他")
}

输出:

复制代码

这里的:

dart 复制代码
num

只在当前 switch 作用域内有效。

switch 外面不能继续使用。

这种写法适合只需要临时使用一次的变量。

十一、fallthrough

Go 默认一个 case 执行完成后就退出 switch。

如果希望继续执行下一个 case,可以使用:

go 复制代码
fallthrough

例如:

go 复制代码
num := 1

switch num {
case 1:
	fmt.Println("第一步")
	fallthrough
case 2:
	fmt.Println("第二步")
case 3:
	fmt.Println("第三步")
}

输出:

复制代码
第一步
第二步

需要特别注意:

fallthrough 不会判断下一个 case 的条件是否成立,而是直接执行下一个 case。

例如:

go 复制代码
num := 1

switch num {
case 1:
	fmt.Println("A")
	fallthrough
case 100:
	fmt.Println("B")
}

即使:

yaml 复制代码
num != 100

仍然会输出:

css 复制代码
A
B

因此 fallthrough 要谨慎使用。

实际 Go 项目中,它的使用频率并不高。

十二、switch 与逻辑运算

无表达式 switch 可以直接组合复杂条件。

例如:

go 复制代码
age := 20
isLogin := true

switch {
case age < 18:
	fmt.Println("年龄不足")
case !isLogin:
	fmt.Println("请先登录")
case age >= 18 && isLogin:
	fmt.Println("允许访问")
}

输出:

复制代码
允许访问

这里使用了:

diff 复制代码
<
!
>=
&&

等运算符。

十三、switch 判断月份

例如根据月份判断季节:

css 复制代码
month := 8

switch month {
case 3, 4, 5:
	fmt.Println("春季")
case 6, 7, 8:
	fmt.Println("夏季")
case 9, 10, 11:
	fmt.Println("秋季")
case 12, 1, 2:
	fmt.Println("冬季")
default:
	fmt.Println("月份错误")
}

输出:

复制代码
夏季

这种固定值分类非常适合 switch。

十四、switch 判断订单状态

实际 Web 项目中,经常会遇到状态码。

例如:

css 复制代码
status := 2

switch status {
case 0:
	fmt.Println("订单已取消")
case 1:
	fmt.Println("等待付款")
case 2:
	fmt.Println("等待发货")
case 3:
	fmt.Println("运输中")
case 4:
	fmt.Println("交易完成")
default:
	fmt.Println("未知订单状态")
}

相比大量:

ini 复制代码
if status == 0 {
} else if status == 1 {
} else if status == 2 {
}

switch 明显更加清晰。

十五、switch 判断 HTTP 状态码

例如:

css 复制代码
statusCode := 404

switch statusCode {
case 200:
	fmt.Println("请求成功")
case 400:
	fmt.Println("请求参数错误")
case 401:
	fmt.Println("未登录")
case 403:
	fmt.Println("没有权限")
case 404:
	fmt.Println("资源不存在")
case 500:
	fmt.Println("服务器错误")
default:
	fmt.Println("其他状态")
}

输出:

复制代码
资源不存在

这也是实际后端开发中非常典型的使用场景。

十六、类型 switch

Go 的 switch 还有一种特殊形式:

go 复制代码
switch value := x.(type) {
}

叫作:

Type Switch,类型 switch。

它通常配合接口使用。

例如:

go 复制代码
package main

import "fmt"

func printType(value any) {
	switch v := value.(type) {
	case int:
		fmt.Println("整数:", v)
	case string:
		fmt.Println("字符串:", v)
	case bool:
		fmt.Println("布尔值:", v)
	case float64:
		fmt.Println("浮点数:", v)
	default:
		fmt.Println("其他类型")
	}
}

func main() {
	printType(100)
	printType("Go")
	printType(true)
}

输出:

arduino 复制代码
整数: 100
字符串: Go
布尔值: true

这里:

bash 复制代码
value.(type)

用于判断接口中实际保存的数据类型。

类型 switch 在以后学习:

typescript 复制代码
interface
any
JSON
反射

时会经常遇到。

目前先理解基本用法即可。

十七、switch 和 if 如何选择?

这是实际开发中经常遇到的问题。

如果判断的是几个复杂条件:

scss 复制代码
if age >= 18 && isLogin && !disabled {
}

通常使用:

复制代码
if

更加自然。

如果判断的是同一个变量的多个固定值:

ini 复制代码
status = 1
status = 2
status = 3
status = 4

通常使用:

arduino 复制代码
switch

更加清晰。

例如:

arduino 复制代码
switch status {
case 1:
case 2:
case 3:
}

如果是多个范围:

复制代码
score >= 90
score >= 80
score >= 60

可以使用:

arduino 复制代码
switch {
case score >= 90:
case score >= 80:
case score >= 60:
}

也可以使用 if else

最终应该以代码可读性为主要判断标准。

十八、避免过长 switch

虽然 switch 很方便,但也不要把所有业务逻辑都塞进去。

不推荐:

arduino 复制代码
switch status {
case 1:
	// 几十行业务代码
case 2:
	// 几十行业务代码
case 3:
	// 几十行业务代码
}

可以拆成函数:

arduino 复制代码
switch status {
case 1:
	handlePending()
case 2:
	handlePaid()
case 3:
	handleFinished()
}

这样 switch 只负责:

复制代码
选择业务分支

具体逻辑交给对应函数处理,代码更加容易维护。

十九、完整实战案例

我们写一个简单的用户权限判断程序:

go 复制代码
package main

import "fmt"

func checkRole(role string) {
	switch role {
	case "admin":
		fmt.Println("管理员")
		fmt.Println("拥有全部权限")

	case "manager":
		fmt.Println("经理")
		fmt.Println("拥有管理权限")

	case "user":
		fmt.Println("普通用户")
		fmt.Println("拥有基础权限")

	case "guest":
		fmt.Println("游客")
		fmt.Println("只能浏览")

	default:
		fmt.Println("未知用户角色")
	}
}

func main() {
	checkRole("admin")
}

输出:

复制代码
管理员
拥有全部权限

以后开发后台管理系统时,经常会遇到类似逻辑。

不过真正的权限系统通常不会直接使用 switch 写死权限,而会使用:

复制代码
数据库
RBAC
Casbin
权限表

等方式进行管理。

二十、常见错误

1. 忘记 case 后面的冒号

错误:

arduino 复制代码
switch status {
case 1
	fmt.Println("正常")
}

正确:

arduino 复制代码
switch status {
case 1:
	fmt.Println("正常")
}

2. 误以为必须写 break

没有必要:

arduino 复制代码
switch status {
case 1:
	fmt.Println("正常")
	break
}

直接:

arduino 复制代码
switch status {
case 1:
	fmt.Println("正常")
}

即可。

3. 滥用 fallthrough

例如:

go 复制代码
case 1:
	fallthrough
case 2:
	fallthrough
case 3:

这种代码很容易产生难以理解的执行流程。

除非确实需要,否则尽量不要使用 fallthrough

4. 条件顺序错误

例如:

arduino 复制代码
switch {
case score >= 60:
	fmt.Println("及格")
case score >= 90:
	fmt.Println("优秀")
}

应该把更严格的条件放前面。

二十一、switch 最佳实践

实际 Go 项目中可以遵循几个原则。

第一,同一个变量存在多个固定值时优先考虑 switch:

arduino 复制代码
switch status {
case 1:
case 2:
case 3:
}

第二,多个 case 执行相同逻辑时直接合并:

arduino 复制代码
case 1, 2, 3:

第三,业务状态尽量处理 default:

arduino 复制代码
default:
	return fmt.Errorf("未知状态:%d", status)

第四,不要为了使用 switch 而使用 switch。简单条件:

复制代码
if age >= 18 {
}

通常没有必要改成:

arduino 复制代码
switch {
case age >= 18:
}

第五,尽量少使用 fallthrough,保持每个 case 独立、明确。

二十二、本章小结

本篇详细介绍了 Go 中的 switch 条件判断。

需要重点掌握:

go 复制代码
switch
case
default
fallthrough
Type Switch

核心知识包括:

  1. switch 可以匹配整数、字符串等值;
  2. 一个 case 可以同时匹配多个值;
  3. Go 的 case 默认执行完成后自动退出;
  4. 普通情况下不需要写 break;
  5. switch 可以不写表达式;
  6. 无表达式 switch 可以替代部分 else if;
  7. fallthrough 会强制执行下一个 case;
  8. 类型 switch 可以判断接口中的实际类型;
  9. 固定状态判断通常使用 switch 更清晰。

学习完 ifswitch 后,我们已经能够控制程序"选择执行哪一段代码"。

接下来还有另外一种非常重要的流程控制:

让一段代码重复执行。

这就是循环。

本章练习

  1. 使用 switch 根据数字 1~7 输出星期。
  2. 根据月份判断春夏秋冬。
  3. 根据订单状态码输出订单状态。
  4. 使用一个 case 同时匹配 JPG、PNG、GIF 图片格式。
  5. 使用无表达式 switch 判断成绩等级。
  6. 使用 switch 判断 HTTP 状态码。
  7. 编写一个用户角色判断程序。
  8. 使用 Type Switch 判断 int、string、bool 和 float64。
  9. 尝试使用 fallthrough,观察下一个 case 是否判断条件。

下一篇预告

《Go for 循环详解》

下一篇将学习 Go 中最重要的循环语句 for,包括:

  • for 基本语法;

  • 传统三段式 for;

  • while 风格循环;

  • 无限循环;

  • break;

  • continue;

  • range;

  • 遍历字符串;

  • 遍历数组和切片;

  • 嵌套循环;

  • 循环标签;

  • 常见死循环问题;

  • 实际开发中的批量数据处理案例。

Go 没有单独的 whiledo while,一个 for 就可以完成几乎所有循环任务。

相关推荐
程序员爱钓鱼1 小时前
Rust Struct结构体详解:定义自己的复杂数据类型
后端·面试·rust
风流 少年1 小时前
Spring AI 2.0:MCP
java·后端·spring
北斗落凡尘1 小时前
LangGraph 入门实战(7)
后端·langchain
uzong2 小时前
业务新老系统数据迁移-负责人经验总结和复盘
后端
dogstarhuang2 小时前
大模型 API 停服怎么办:用 API 网关实现多模型统一接入与可切换架构
人工智能·后端·架构·大模型·api·数字化转型·ai应用
程序猿DD2 小时前
OctaFuse Gateway 2.5.0:接入 Responses 端点、更易理解的路由配置
后端·agent
Lzh编程小栈3 小时前
【嵌入式底层】SPI超透彻详解:通信原理、四线结构、四种模式、寄存器底层 + 面试全集
c语言·stm32·单片机·面试·职场和发展
2651940511264804 小时前
07-告警引擎与状态机
go·监控