小技巧分享:在 Go 如何實現枚舉?
本文轉載自微信公眾號「腦子進煎魚了」,作者陳煎魚。轉載本文請聯(lián)系腦子進煎魚了公眾號。
大家好,我是煎魚。
在日常的業(yè)務工程開發(fā)中,我們常常會有使用枚舉值的訴求,枚舉控的好,測試值邊界一遍過...
有的小伙伴會說,在 Go 語言不是有 iota 類型做枚舉嗎,那煎魚你這篇文章還講什么?
講道理,Go 語言并沒有 enum 關鍵字,有用過 Protobuf 等的小伙伴知道,Go 語言只是 ”有限的枚舉“ 支持,我們也會用常量來定義,枚舉值也需要有字面意思的映射。
示例
在一些業(yè)務場景下,是沒法達到我們的訴求的。示例如下:
- type FishType int
- const (
- A FishType = iota
- B
- C
- D
- )
- func main() {
- fmt.Println(A, B, C, D)
- }
輸出結果為:“0 1 2 3”。這時候就一臉懵逼了...枚舉值,應該除了鍵以外,還得有個對應的值。也就是這個 “0 1 2 3” 分別對應著什么含義,是不是應該輸出 ”A B C D“
但 Go 語言這塊就沒有直接的支撐了,因此這不是一個完整的枚舉類型的實現。
同時假設我們傳入超過 FishType 類型聲明范圍的枚舉值,在 Go 語言中默認也不會有任何控制,是正常輸出的。
上述這種 Go 枚舉實現,在某種情況下是不完全的,嚴格意義上不能成為 enum(枚舉)。
使用 String 做枚舉
如果要支持枚舉值的對應輸出的話,我們可以通過如下方式:
- type FishType int
- const (
- A FishType = iota
- B
- C
- D
- )
- func (f FishType) String() string {
- return [...]string{"A", "B", "C", "D"}[f]
- }
運行程序:
- func main() {
- var f FishType = A
- fmt.Println(f)
- switch f {
- case A:
- fmt.Println("腦子進煎魚了")
- case B:
- fmt.Println("記得點贊")
- default:
- fmt.Println("別別別...")
- }
- }
輸出結果:
- A
- 腦子進煎魚了
我們可以借助 Go 中 String 方法的默認約定,針對于定義了 String 方法的類型,默認輸出的時候會調用該方法。
這樣就可以達到獲得枚舉值的同時,也能拿到其映射的字面意思。
自動生成 String
但每次手動編寫還是比較麻煩的。在這一塊,我們可以利用官方提供的 cmd/string 來快速實現。
我們安裝如下命令:
- go install golang.org/x/tools/cmd/stringer
在所需枚舉值上設置 go:generate 指令:
- //go:generate stringer -type=FishType
- type FishType int
在項目根目錄執(zhí)行:
- go generate ./...
會在根目錄生成 fishtype_string.go 文件:
- .
- ├── fishtype_string.go
- ├── go.mod
- ├── go.sum
- └── main.go
fishtype_string 文件內容:
- package main
- import "strconv"
- const _FishType_name = "ABCD"
- var _FishType_index = [...]uint8{0, 1, 2, 3, 4}
- func (i FishType) String() string {
- if i < 0 || i >= FishType(len(_FishType_index)-1) {
- return "FishType(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _FishType_name[_FishType_index[i]:_FishType_index[i+1]]
- }
所生成出來的文件,主要是根據枚舉值和映射值做了個映射,且針對超出枚舉值的場景進行了判斷:
- func main() {
- var f1 FishType = A
- fmt.Println(f1)
- var f2 FishType = E
- fmt.Println(f2)
- }
執(zhí)行 go run . 查看程序運行結果:
- $ go run .
- A
- FishType(4)
總結
在今天這篇文章中,我們介紹了如何在 Go 語言實現標準的枚舉值,雖然有些繁瑣,但整體不會太難。
也有小伙伴已經在社區(qū)中提出了 ”proposal: spec: add typed enum support“ 的提案,相信未來有機會能看到 Go 自身支持 enum(枚舉)的那一天。