首页 > 编程语言 >Go语言编程教程4-枚举

Go语言编程教程4-枚举

时间:2023-12-14 22:17:38浏览次数:30  
标签:Status const 编程 我们 Golang 枚举 Go iota

课程要点

  1. 了解Golang中的枚举
  2. 自定义枚举值
  3. 跳过某个枚举值
  4. 枚举的常用惯例
  5. 了解fmt.Stringer接口

Golang中的枚举

在Golang中并没有像其他语言一样,拥有类似于enum的常规枚举类型,而是通过使用一组常量来实现类似枚举的功能。

如下所示,我们定义了三个常量来表示状态语义的枚举值

const (
	StatusSuccess   = 0
	StatusFailed    = 1
	StatusForbidden = 2
)

在Golang项目开发中我们就可以直接使用上面的方式来代表我们的自定义枚举语义。
观察上面的示例以及结合日常的开发经验,可以得到我们的枚举值通常就是一组连续的数字。

所以在Golang中增加了一个iota关键字来简化上面的写法,使我们的代码更加简洁,如下所示

const (
	StatusSuccess = iota // 0
	StatusFailed         // 1
	StatusForbidden      // 2
)

上面的代码与我们之前直接定义常量是等效的,这里的iota相当于一个从0开始的累加器,每一个枚举值都会按照顺序依次加1,
从而接管了我们手动管理枚举值的工作,使代码更加简洁。

换一种更加容易理解的说法,可以把这一组枚举项当作一个数组集合,这里的iota可以理解为每个枚举项所在的索引值。
所以上述代码与下面的代码也是等效的。

const (
	StatusSuccess   = iota // 0
	StatusFailed    = iota // 1
	StatusForbidden = iota // 2
)

所以为了保持代码的简洁,我们只需要保留第一个iota即可。

自定义枚举值

通常情况下,我们的枚举值都是从0开始依次递增的,但是有少数情况下,我们也会有需要自定义枚举值的需求。

比如,我需要枚举值从1开始,而不是0,就可以像下面这么写

const (
	StatusSuccess = iota + 1 // 1
	StatusFailed             // 2
	StatusForbidden          // 3
)

上面的代码可能会让你看着有点疑惑,那我们把代码补全再看,可能会更清晰些

const (
	StatusSuccess   = iota + 1 // 1
	StatusFailed    = iota + 1 // 2
	StatusForbidden = iota + 1 // 3
)

这下是不是就很容易理解了,上面两段代码是等效的,每一个没有被显式赋值的枚举项,都会依次往上寻找最近的一个显示定义的枚举项,
使用它的枚举值表达式作为当前枚举项的值,也就是iota + 1

因为iota等价于枚举项的索引位置,所以上述示例代码的枚举值会出现从1开始逐步递增的效果。

那么举一反三一下,如果我们需要将每个枚举值之间的增长步长由1改为2该怎么实现呢?

聪明的你肯定已经想到了,没错就是将第一个值设置为iota * 2即可,如下所示

const (
	StatusSuccess   = iota * 2 // 0
	StatusFailed               // 2
	StatusForbidden            // 4
)

那如果我们将第一个枚举值设置为一个常量的话会出现什么情况呢?

参考前文所说的,我们很容易就能得出答案:
每一个没有被显式赋值的枚举项,都会依次往上寻找最近的一个显示定义的枚举项,使用它的枚举值表达式作为当前枚举项的值

没错,假设我们将第一个枚举值设置为1,那么后续没有显式赋值的枚举都会参照第一个枚举的表达式,也就是字面量1,如下所示:

const (
	StatusSuccess   = 1 // 1
	StatusFailed        // 1
	StatusForbidden     // 1
)

至此,你应该对Golang的枚举以及iota表达的含义有了比较清晰的认识了,
虽然一开始可能会有点迷糊,但是如果你亲自尝试了所有示例,所有疑惑都会轻易解开。

跳过某个枚举值

在实际开发过程中,我们在定义枚举的时候,可能需要为程序预留一些枚举值,暂时还不会使用,此时我们该怎么办呢?

通常我们可能会先任意定义一批枚举值,并加上一些注释,告诉开发者这是预留的,请暂时不要使用之类的。

显而易见,这是比较糟糕的做法,因为并不是所有人都会认真的去看注释并严格的去遵守相应的规则。

万幸的是,Golang的开发者已经想到了这一点,并为我们提供了一个极其简单的方式来处理这个问题。

我们可以使用_作为一个枚举项来表示一个占位,它会拥有自己的枚举数值,但是任何人都不能通过它的名称来进行调用,
也就是说枚举项_对任何人都是不可见的,所以它可以多次使用,如下所示:

const (
	StatusSuccess   = iota // 0
	StatusFailed           // 1
	StatusForbidden        // 2
	_                      // 3
	_                      // 4
)

妙哉,没有比这更简单的了吧?

枚举的常用惯例

因为Golang的枚举是用数字常量来实现的,所以它携带的信息非常少,我们很难理解某一个枚举数值代表着什么含义,
并且我们也很难去限定枚举的取值范围。

自定义枚举类型

所以为了使枚举更加好用,能够携带更多的信息,我们常用的一个做法是以int作为基础类型来自定义一个枚举类型,
从而为其扩展更多的功能,如下所示

type Status int

此时我们用自定义的类型Status来创建枚举,如下所示

const (
	Success   Status = iota // 0
	Failed                  // 1
	Forbidden               // 2
	_                       // 3
	_                       // 4
)

从上面可以看到,对比之前的例子,唯一的区别就是为第一个枚举指定了一个显式类型Status,如果未指定类型,则它的默认类型是int

此时所有枚举项的类型都变成Status了,因为Status的基础类型是int,所以之前的规则依旧有效,完全与之前所说的相兼容。

枚举边界

另外,在使用枚举的过程中,我们经常会通过一些边界值来判断枚举值的有效性,
比如判断用户传入的枚举值是否小于枚举定义的最小值或者大于枚举定义的最大值,如果不符合条件,则传入的枚举不合法。

亦或者在一批枚举值中存在着更加细分语义的分组,比如有一批状态码表示不同的成功语义,另一批状态码表示不同的失败语义等等。

一种常见的做法就是直接使用对应的边界值来进行判断,但是这样存在一个问题,因为随着枚举值的增加或者减少,边界值可能发生变化。

比如我往后增加了一个枚举,那么当前枚举的最大值就变成新增的这个枚举了,所以校验枚举值有效性的方法就必须得修改了。

诸如此类,因为枚举项的变化,很多校验逻辑都会随之变化,对代码来说是一种潜在的风险,我们很可能会忘记修改某个边界判断逻辑。

所以聪明的开发者就想了一个办法,就是通过在枚举中定义一些私有的枚举项来作为枚举的边界值,
也就是将枚举项定义为以小写字母或者_开头的常量,这样外部包就无法看到和使用这些私有的边界值了。

在我们当前包的代码中,就可以通过这些边界值来控制我们上述遇到的边界判断问题,而且当我们的枚举项发生增减的时候,
我们的边界逻辑也不需要随之变更,完整示例如下所示

const (
	minStatus Status = iota // 0
	Success                 // 1
	Failed                  // 2
	Forbidden               // 3
	_                       // 4
	_                       // 5
	maxStatus               // 6
)

fmt.Stringer接口

因为枚举归根到底其实是一个整型数字,所以在实际运行的时候,比如在打印日志时,我们很难理解具体的数字代表着什么含义,
如果每次都要去查看相应的文档可真的太费劲了。

我们可以使用fmt.Printf来尝试打印枚举的信息,这里我们之前定义的枚举值边界就派上用场了,可以用它来循环遍历所有的枚举项

func main() {
	for status := minStatus; status <= maxStatus; status++ {
		fmt.Printf("%d -> %v\n", status, status)
	}
}

输出结果如下,可以看到根据打印结果很难判断其内在的含义

0 -> 0
1 -> 1
2 -> 2
3 -> 3
4 -> 4
5 -> 5
6 -> 6

幸好Golang为我们提供了一个fmt.Stringer接口,我们只要实现这个接口,当我们使用fmt包进行格式化输出的时候,
就会调用对应的方法输出更加有用的自定义信息。

fmt.Stringer接口中只有一个String() string方法,我们只要实现该方法即可,
如下所示,我们通过当前的枚举值直接返回其对应的描述信息

func (s Status) String() string {
	switch s {
	case Success:
		return "Success"
	case Failed:
		return "Failed"
	case Forbidden:
		return "Forbidden"
	default:
		return "Unknown"
	}
}

最后我们再次运行我们的代码,可以看到从输出结果中很容易就能知道每个枚举值的具体含义了

0 -> Unknown
1 -> Success  
2 -> Failed   
3 -> Forbidden
4 -> Unknown  
5 -> Unknown  
6 -> Unknown  

小结

通过本章的学习,我们了解了Golang中枚举的用法,它不像常规的编程语言一样拥有独立的枚举类型,而是通过常量配合上iota
关键字来实现枚举的功能。

经过上述一系列的优化,我们的枚举功能已经越来越完善了,当然这都不是必须的,
开发者可以根据实际的需求来定制自己的枚举写法,可以使用最精简的写法来定义枚举,也可以为其添加更加强大的功能,
所有的选择权都在开发者自己手中。

标签:Status,const,编程,我们,Golang,枚举,Go,iota
From: https://www.cnblogs.com/mstmdev/p/17895634.html

相关文章

  • Go语言编程教程3-变量与常量
    课程要点了解Golang中的变量的使用了解Golang中的常量的使用了解变量和常量的可访问性变量定义变量Golang是一门十分简约的语言,它的变量定义与其他主流语言有一定的相似之处,但是却没有像其他语言一样拥有很多可选的修饰符。在Golang的变量定义中,我们只会用到var这一个修......
  • Go语言编程教程2-Hello World
    课程要点创建并编译运行第一个Golang程序了解GoModule包管理系统使用fmt.Println与println函数分别打印helloworld,并了解它们的异同了解fmt.Printf格式化输出将Golang代码编译为汇编代码对Golang二进制文件进行反汇编了解交叉编译课程内容创建项目按照Go语言的约定,......
  • 实验6_C语言结构体、枚举应用编程
    4.task_41#include<stdio.h>2#defineN1034typedefstruct{5charisbn[20];6charname[80];7charauthor[80];8doublesales_price;9intsales_count;10}Book;1112voidoutput(Bookx[],intn);13voids......
  • Google Bard背单词
    promptActasifyouareanexpertonwritingpromptsthathelpuserstolearnvocabulary.Youwillexaminethispromptanduseyourexpertisetotaketheseinstructionsandusethemtoallowuserstolearnvocabulary.Youwillnevertestusers'know......
  • C++和QML混合编程
    一、QML访问C++方法Qt元对象系统中注册C++类,在QML中实例化、访问。C++中实例化并设置为QML上下文属性,在QML中直接使用。      比较:方法1可以使C++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。二、QML访......
  • Django添加models.py的ForeignKey数据迁移后找不到,各种报错
    Django添加models.py的ForeignKey数据迁移后找不到,各种报错在创建DJango项目时候,已经实现文件列表功能,在添加用户绑定文件列表功能时候,添加user作为外键关联,添加后执行数据库迁移,一直报错找不到userid报错信息django.db.utils.OperationalError:nosuchcolumn:student_chec......
  • 使用Github+PicGo+JsDelivr搭建图床
    本文主要记录使用PicGo+Github搭建图床,并使用jsDelivrCDN加速;下载安装PicGo#安装包下载地址https://github.com/Molunerfinn/PicGo/releases#官方文档地址https://picgo.github.io/从github上选择稳定版本进行下载,我这里因为使用的是M1芯片的Mac,故选择的是Pi......
  • Amazon CodeWhisperer:AI 编程助手
    文章作者:prigioni1.什么是AmazonCodeWhisperer?AmazonCodeWhisperer能够理解以自然语言(英语)编写的注释,并能实时生成多条代码建议,以此提高开发人员生产力。该服务可以直接在集成开发环境(IDE)的代码编辑器中给出关于整个功能和逻辑代码块(通常包含多达10-15行代码)的建议。生成......
  • Django中常遇到的错误
    这条是我今晚遇到的错误,下面的是借鉴别的大佬的经验的。今晚遇到的错误是You'reseeingthiserrorbecauseyouhaveDEBUG=TrueinyourDjangosettin于是我就把setting配置文件中的DEBUG改成了False但是在我改完以后又出现了CommandError:Youmustsetsettings.ALLOWED_......
  • 实验6 C语言结构体、枚举应用编程
    实验任务41#include<stdio.h>2#defineN1034typedefstruct{5charisbn[20];//isbn号6charname[80];//书名7charauthor[80];//作者8doublesales_price;//售价9intsales_count;......