Go中你用對枚舉了么?
枚舉的本質(zhì)是什么,我們天天寫代碼用枚舉,那啥是枚舉啊。wiki上是這么說的
- In computer programming, an enumerated type (also called enumeration, enum, or factor in the R programming language, and a categorical variable in statistics) is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language. An enumerated type can be seen as a degenerate tagged union of unit type. A variable that has been declared as having an enumerated type can be assigned any of the enumerators as a value. In other words, an enumerated type has values that are different from each other, and that can be compared and assigned, but are not specified by the programmer as having any particular concrete representation in the computer’s memory; compilers and interpreters can represent them arbitrarily.
用人話說就是
枚舉是強(qiáng)類型編程語言中的一種類型,由一組名稱和值組成。通常用來在編程語言中充當(dāng)常量的標(biāo)識符。
沒毛病,我們也確實(shí)是這樣使用的。比如上學(xué)的時(shí)候,經(jīng)常寫c的小玩具代碼,c標(biāo)準(zhǔn)里面提供了enum關(guān)鍵字,寫起來比較直白,使用的時(shí)候和struct類似,需要enum week這樣寫,c里面默認(rèn)枚舉值是從0開始,int類型,其實(shí)c里面就是把枚舉當(dāng)做int類型來用的。
- #include<stdio.h>
- enum week{Mon, Tue, Wed, Thur, Fri, Sat, Sun};
- int main()
- {
- enum week day; // 需要加 enum 關(guān)鍵字
- day = Wed;
- printf("%d",day); // 輸出 2
- int i;
- for (i=Mon; i<=Sun; i++){ // 可以直接把枚舉類型賦值給int類型
- printf("%d ", i); // 輸出 0,1,2,3,4,5,6
- }
- return 0;
- }
上面的例子沒問題,在初始化的時(shí)候,枚舉值默認(rèn)情況下,編譯器會從分配0開始的值,例如上面的Mon=0,Tue=1…,但是也會想不按照編譯器的默認(rèn)分配方式,由我自己分配,那怎么寫呢,看下面的例子:
- #include <stdio.h>
- enum day {sunday = 1, monday, tuesday = 5,
- wednesday, thursday = 10, friday, saturday};
- int main()
- {
- printf("%d %d %d %d %d %d %d", sunday, monday, tuesday,
- wednesday, thursday, friday, saturday); // 輸出1 2 5 6 10 11 12
- return 0;
- }
也就是說,枚舉里面可以按任何順序?qū)⒅捣峙浣o某個(gè)名稱。所有未分配的名稱都會把值作為前一個(gè)名稱的值加一。
其實(shí),定義幾個(gè)常量的事,是不是用宏這個(gè)東西更好呢,比如這么寫
- #define sunday 0
- #define monday 1
- #define tuesday 2
但是老師說了,盡量別用宏,不是說宏不好,宏也好,編譯器替換,沒有運(yùn)行期啥事,多快啊,但是有幾個(gè)問題:
1)宏沒有作用域一說 2)枚舉是類型安全的
扯的有點(diǎn)遠(yuǎn)了,現(xiàn)在回來看看Go里面的枚舉怎么寫。當(dāng)然也很簡單了,官方教導(dǎo)我們這么寫:
- type ByteSize float64
- const (
- _ = iota
- KB ByteSize = 1 << (10 * iota)
- MB
- GB
- )
Go里面更簡潔了,直接把enum關(guān)鍵字去掉了,其實(shí)從Go的角度看,枚舉不就是常量么,搞這么多語法糖干嘛,Go里面提供了一個(gè)關(guān)鍵字iota可以實(shí)現(xiàn)常量的遞增,同時(shí)也支持手動(dòng)賦值,iota和手動(dòng)賦值結(jié)合起來,就可以實(shí)現(xiàn)類似c里面的效果
- const (
- A0 = iota
- A1 = iota
- A2 = iota
- )
- fmt.Println(A0, A1, A2) // "0 1 2"
可以 簡寫成這樣
- const (
- A0 = iota
- A1
- A2
- )
也可以從1開始
- const (
- A1 = iota + 1
- A2
- A3
- )
- fmt.Println(A1, A2, A3) // "1 2 3"
或者跳過某個(gè)值
- const (
- C1 = iota + 1
- _
- C3
- C4
- )
- fmt.Println(C1, C3, C4) // "1 3 4"
看到這里你或許有個(gè)疑問,這里的枚舉其實(shí)就是常量么,那怎么寫是字符串類型的枚舉呢,你可能會說,當(dāng)然是用字符串常量了。但是那只是字符串常量了,沒有枚舉的性質(zhì)。我可能想要的是一種字符串到值的枚舉類型。思考再三,看我這種寫法是否可以:
- 步驟一:創(chuàng)建一個(gè)新的int類型
- 步驟二:使用iota表示值
- 步驟三:給這個(gè)新的類型一個(gè)String的方法
- type Direction int
- const (
- North Direction = iota
- East
- South
- West
- )
- func (d Direction) String() string {
- return [...]string{"North", "East", "South", "West"}[d]
- }
使用的時(shí)候
- var d Direction = North
- fmt.Print(d)
- switch d {
- case North:
- fmt.Println(" goes up.")
- case South:
- fmt.Println(" goes down.")
- default:
- fmt.Println(" stays put.")
- }
當(dāng)然還有一種方法,比較stupid
- type weekday string
- func (w weekday) isWeekday() weekday {
- return w
- }
- type Weekday interface {
- isWeekday() weekday
- }
- const (
- Monday = weekday("Monday")
- Tuesday = weekday("Tuesday")
- Wendsday = weekday("Wendsday")
- Thursday = weekday("Thursday")
- Friday = weekday("Friday")
- Saturday = weekday("Saturday")
- Sunday = weekday("Sunday")
- )
- // 使用
- func main() {
- var d1 = weekday.Monday
- var d2 = weekday.Tuesday
- fmt.Println(d1, d2, d1 == d2, d1 == weekday.Monday)
- }
如果使用struct表示枚舉,那其實(shí)還可以使用反射的方式,比如下面這樣寫:
- import (
- "reflect"
- )
- type weekday struct {
- Monday, Tuesday, Wendsday, Thursday, Friday, Saturday, Sunday int
- }
- func (c weekday) Get(id string) int {
- vo := reflect.ValueOf(c)
- typeVo := vo.Type()
- for i := 0; i < vo.NumField(); i++ {
- if typeVo.Field(i).Name == id {
- return vo.Field(i).Interface().(int)
- }
- }
- return 0
- }
- var weekdayEnum = weekday {
- Monday: 1,
- Tuesday: 2,
- Wendsday: 3,
- Thursday: 4,
- Friday: 5,
- Saturday: 6,
- Sunday: 7
- }
本文轉(zhuǎn)載自微信公眾號「碼小菜」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系碼小菜公眾號。