枚举的本质是什么,我们天天写代码用枚举,那啥是枚举啊。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.
用人话说就是
枚举是强类型编程语言中的一种类型,由一组名称和值组成。通常用来在编程语言中充当常量的标识符。
没毛病,我们也确实是这样使用的。比如上学的时候,经常写c的小玩具代码,c标准里面提供了enum关键字,写起来比较直白,使用的时候和struct类似,需要enum week这样写,c里面默认枚举值是从0开始,int类型,其实c里面就是把枚举当做int类型来用的。
- #include
-
- enum week{Mon, Tue, Wed, Thur, Fri, Sat, Sun};
-
- int main()
- {
- enum week day; // 需要加 enum 关键字
- 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;
- }
上面的例子没问题,在初始化的时候,枚举值默认情况下,编译器会从分配0开始的值,例如上面的Mon=0,Tue=1…,但是也会想不按照编译器的默认分配方式,由我自己分配,那怎么写呢,看下面的例子:
- #include
- 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;
- }
也就是说,枚举里面可以按任何顺序将值分配给某个名称。所有未分配的名称都会把值作为前一个名称的值加一。
其实,定义几个常量的事,是不是用宏这个东西更好呢,比如这么写
- #define sunday 0
- #define monday 1
- #define tuesday 2
但是老师说了,尽量别用宏,不是说宏不好,宏也好,编译器替换,没有运行期啥事,多快啊,但是有几个问题:
1)宏没有作用域一说 2)枚举是类型安全的
扯的有点远了,现在回来看看Go里面的枚举怎么写。当然也很简单了,官方教导我们这么写:
- type ByteSize float64
-
- const (
- _ = iota
- KB ByteSize = 1 << (10 * iota)
- MB
- GB
- )
Go里面更简洁了,直接把enum关键字去掉了,其实从Go的角度看,枚举不就是常量么,搞这么多语法糖干嘛,Go里面提供了一个关键字iota可以实现常量的递增,同时也支持手动赋值,iota和手动赋值结合起来,就可以实现类似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"
或者跳过某个值
- const (
- C1 = iota + 1
- _
- C3
- C4
- )
- fmt.Println(C1, C3, C4) // "1 3 4"
看到这里你或许有个疑问,这里的枚举其实就是常量么,那怎么写是字符串类型的枚举呢,你可能会说,当然是用字符串常量了。但是那只是字符串常量了,没有枚举的性质。我可能想要的是一种字符串到值的枚举类型。思考再三,看我这种写法是否可以:
- 步骤一:创建一个新的int类型
- 步骤二:使用iota表示值
- 步骤三:给这个新的类型一个String的方法
- type Direction int
-
- const (
- North Direction = iota
- East
- South
- West
- )
-
- func (d Direction) String() string {
- return [...]string{"North", "East", "South", "West"}[d]
- }
使用的时候
- 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.")
- }
当然还有一种方法,比较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表示枚举,那其实还可以使用反射的方式,比如下面这样写:
- 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
- }
本文转载自微信公众号「码小菜」,可以通过以下二维码关注。转载本文请联系码小菜公众号。