当前位置: 首页 > 科技观察

Go真的有枚举吗?

时间:2023-03-13 14:09:19 科技观察

本文转载自微信公众号《Golang技术分享》,作者:机器贝尔斩波。转载本文请联系Golang技术分享公众号。Go中有枚举吗?这是一个模棱两可的问题。有人说会,有人说不会。枚举代??码是从现实中抽象出来的。枚举在程序和生活中的概念是相通的:一个枚举代表了一个对象所有可能取值的集合。例如,代表星期几的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一组枚举值。其实我们可以把Go中所有的原始类型都看成是一种枚举。例如,bool类型可以认为是一个只能为true或false的枚举;字节类型是从0到255的枚举;指针是32位或64位地址空间中所有可能的内存地址的枚举。在Python、Java、C等语言中,一般都有enum关键字或者类提供给开发者实现枚举。一般的伪代码可以表示为:enum枚举名{标识符①[=整型常量],标识符②[=整型常量],...标识符N[=整型常量],}枚举变量;Go没有enum关键字。但是我们可以观察到枚举的特点:同一组枚举值定义后不应该改变;枚举值对应的数据类型应该相同;枚举值是有限制的;枚举值和它们的含义是一一对应的。根据以上特点,在Go中,枚举的需求可以通过const和iota关键字来实现。iotaconst用于定义常量,这些常量是在编译时创建的,不能在运行时修改。并且只有布尔值、数字(整数、浮点数和复数)和字符串类型可以定义为常量。常量声明格式如下constidentifier[type]=valueiota是一个常量计数器,遇到const关键字就重置为0。const中每增加一行常量声明(包括空白标识符_),iota计数将增加1。const(Aint=iota//0_B//2C//3D//4)const(Eint=iota//0F//1)Go枚举实现有了iota的参与,如果要枚举Go中的周值,我们可以定义typeWeekdayintconst(_Weekday=iota//ignorefirstvaluebyassigningtoblankidentifierSundayMondayTuesdayWednesdayThursdayFridaySaturday)如下。在使用枚举值的过程中,经常需要输出打印fmt.Println(Sunday,Monday)//12但是原始结果很不直观,无法体现枚举值背后的含义。我们需要定义Weekday对象的输出。func(wWeekday)String()string{return[...]string{"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}[w-1]在Go中,我们可以将String()方法绑定到任何自定义类型,使其按照String()方法中定义的格式进行打印。funcmain(){varday=Mondayswitchday{caseMonday,Tuesday,Wednesday,Thursday,Friday:fmt.Printf("今天是%s,加油!打工人",day)caseSaturday,Sunday:fmt.Printf("今天是%s,好好休息吧!打工人",day)default:fmt.Println("不存在的一天")}}执行结果今天是星期一,加油!上面的方案看似实现了枚举功能,但实际上也存在一些问题。首先,由于iota是基于int类型的,这意味着在程序中,任何整数都可以转换为枚举类型(这就是为什么我们在上面的switchcase中有一个默认分支),但这不是什么我们想要。funcmain(){fakeNum:=8day:=Weekday(fakeNum)fmt.Println(day)}#gorunmain.go%!v(PANIC=Stringmethod:runtimeerror:indexoutofrange[7]withlength7),善于思考的读者会想到,既然int不行,我们可以用字符串常量来表示枚举值。但是这种方案也存在以上问题,而且相比使用int比较,在比较字符串的时候,需要付出额外的性能代价。另外我们对枚举还有一个很重要的需求,就是枚举。对应Go循环表达式,枚举迭代的期望是fori,day:=rangeWeekday{...}但显然,目前的代码方案不能满足这个需求。总结本文讨论了Go目前通过iota关键字实现枚举的方法,但这种方法并没有实现完整的枚举功能。在官方issue19814中,提出了在Go中加入enum关键字的提议。感兴趣的读者可以详细查看。你对Go中枚举的实现有不同的看法吗?欢迎留言讨论。参考建议:spec:addtypedenumsupport:https://github.com/golang/go/issues/19814