Golang 枚举详解

什么是枚举

枚举(Enumeration)是一种常见的编程概念,它定义了一组命名常量。我们知道在 Go 语言中定义常量是这样的形式:

const (
	A = "a"
	B = "b"

开发者可以使用枚举创建复杂的、有限的常量集,这些常量集具有有意义的名称和唯一的值。

枚举语法示例

在 Go 语言中,没有提供枚举类型,在 python、java、C++ 等语言中,提供了 enum 关键字来创建枚举类型。

但是 Go 语言可以利用 const + iota 实现枚举的效果,const 用来定义常量,[iota](https://github.com/golang/go/wiki/Iota) 是一个增数器,生成连续整数序列。

下面看看如何使用 const + iota 实现枚举

const (
	 Monday int = iota      // Monday  = 0
	 Tuesday               // Tuesday = 1
	 Wednesday             // Wednesday = 2
	 Thursday              // Thursday = 3
	 Friday                // Friday = 4
	 Saturday              // Saturday =5
	 Sunday                // Sunday = 6
)

可以发现 iota 生成了从 0 - 6 的整数序列分别作为常量名称的值。

枚举的使用疑问

对于刚接触枚举的,应该会有以下疑问:

枚举与常量的区别

从上面那个例子可以看出枚举好像就是定义了一堆整形常量而已。既然枚举就是定义常量,那为什么不直接使用 const 来定义常量呢?

首先看一下使用 const 来定义上面枚举实现的常量集:

const (
	 Monday     int = 0            // Monday  = 0
	 Tuesday    int = 1            // Tuesday = 1
	 Wednesday  int = 2            // Wednesday = 2
	 Thursday   int = 3            // Thursday = 3
	 Friday     int = 4            // Friday = 4
	 Saturday   int = 5            // Saturday =5
	 Sunday     int = 6            // Sunday = 6
)

可以发现使用 const 同样可以实现枚举的效果,但是没有枚举优雅。而且 const 并不能保证常量集的值是唯一的,比如 Wednesday int = 1 编译器也不会报错,但是程序可能会出现 Bug。通过使用枚举(const + iota),编译器层面保证了常量集里不会有相同的值。

枚举值的作用

通过上面枚举示例发现枚举包括:枚举名称( Monday )、枚举类型( int )、枚举值( 0 ),枚举名称、枚举类型很好理解,枚举值在编程中的有什么作用:

枚举本身的值没有什么意义,它是用来模拟现实中的某些只能取固定值的变量的,对应一个值只是在计算机中比较好处理。

枚举值只能为整形?

在枚举示例章节看到使用 iota 生成枚举值都是整数,在 Go 语言中枚举只能使用 iota ,所以枚举值只能是整数。

下面看一个 C++ 枚举示例:

//枚举类型必须是整形吗,不能是浮点数或是别的类型?
enum E
    {
        monday=-2,
        tuesday=1.5//有么有非int类型的枚举?
    };

上面的枚举值定义为负数、浮点数,这种写法是错误的。在任何语言中,枚举值只能是整形。

枚举高级用法

从 1 开始枚举

如果不希望枚举值从 0 开始,从1 开始,可以在 Go 这样实现:

const (
	 Monday = iota + 1     // Monday  = 1
	 Tuesday               // Tuesday = 2
	 Wednesday             // Wednesday = 3
	 Thursday              // Thursday = 4
	 Friday                // Friday = 5
	 Saturday              // Saturday = 6
	 Sunday                // Sunday = 7
)

自定义枚举值

iota 默认从 0 开始且依次递增 1,同时也可以使用数学运算自定义枚举值

const (
	 Monday = iota + 1     // Monday = 0 + 1 = 1
	 Tuesday = iota + 2    // Tuesday = 1 + 2 = 3
	 Wednesday = iota * 2  // Wednesday = 2 * 2 = 2
	 Thursday              // Thursday = 3
	 Friday                // Friday = 4
	 Saturday              // Saturday =5
	 Sunday                // Sunday = 6
)

通常不建议这么做,因为容易导致枚举值混乱且重复。
那么可能会有疑问,枚举不是说保证枚举值唯一吗?在其他语言中可以保证唯一,但是 Go 语言没有 enum 关键字,只能使用 iota 来模拟枚举,如果自己使用不当也会导致枚举值重复,比如上面的例子。

跳过值的枚举

如果想要跳过某个值,可以使用 _ 字符,即忽略的意思

const (
	 Monday = iota         // Monday = 0 
   _                     // 1 被跳过
	 Tuesday               // Tuesday = 2
	 Wednesday             // Wednesday = 3
	 Thursday              // Thursday = 4
   _                     // 5 被跳过
	 Friday                // Friday = 6
	 Saturday              // Saturday = 7
	 Sunday                // Sunday = 8
)

枚举使用场景

下面举例几个枚举的使用场景,加深枚举的作用和使用。

限制参数类型

当处理状态码时,如果不使用枚举

const (
	Normal       = 200
	Forbid       = 403
	NetworkError = 502
)

// 该函数形参是整形
func HandlerStatus(statusCode int) {
	fmt.Println(statusCode)
}

// 调用 HandlerStatus 随便什么整数都可以
func main() {
	HandlerStatus(Normal)
	HandlerStatus(404)
}

结果:
200
404

HandlerStatus 这个函数应该只能传 NormalForbidNetworkError 这三个常量,可以发现常量集之外的值也可以被正常调用,会使得程序不严谨。

下面看看使用枚举是否能达到效果?

虽然 Go 中并没有 enum 关键字来定义枚举类型,但是 Go 使用类型别名来定义枚举类型

// int 别名
type Code int

const (
	Normal Code = iota
	Forbid
	NetworkError
)

func HandlerStatus(statusCode Code) {
	fmt.Println(statusCode)
}

func main() {
	HandlerStatus(Normal)
	var notFount int = 1
	// 这里会导致程序编译失败,因为 notFount 不是 Code 类型
	HandlerStatus(notFount)
}

通过枚举就可以限制 HandlerStatus 的入参类型,保证程序的严谨。

使用 string 作为枚举值

我们知道 Go 语言使用 iota 生成连续整数作为枚举值,但是有的时候希望能描述枚举常量的意思,这个时候除了看枚举常量名称,也可以将枚举值转成 string 来达到效果。下面通过例子看看:

// 声明一个 week 类型
type week int

const (
	 Monday week = iota    // Monday  = 0
	 Tuesday               // Tuesday = 1
	 Wednesday             // Wednesday = 2
	 Thursday              // Thursday = 3
	 Friday                // Friday = 4
	 Saturday              // Saturday =5
	 Sunday                // Sunday = 6
)

func main() {
	var w week = Monday
	switch w {
	case Monday:
		fmt.Println(Monday)
	case Tuesday:
		fmt.Println(Tuesday)
	}
}

输出结果:
0

可以发现直接打印出了该枚举常量的枚举值 0,但是 0 并不是很容易理解该枚举常量的意义。

下面借助 Go 中 String 方法的默认约定,针对于定义了 String 方法的类型,默认输出的时候会调用该方法。

// 声明一个 week 类型
type week int

const (
	 Monday week = iota    // Monday  = 0
	 Tuesday               // Tuesday = 1
	 Wednesday             // Wednesday = 2
	 Thursday              // Thursday = 3
	 Friday                // Friday = 4
	 Saturday              // Saturday =5
	 Sunday                // Sunday = 6
)

// 实现 week 类型的 String() 方法
func (w week) String() string {
		return [...]string{"星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"}[w]
}

func main() {
	var w week = Monday
	switch w {
	case Monday:
		fmt.Println(Monday)
	case Tuesday:
		fmt.Println(Tuesday)
	}
}

输出结果:
星期一

通过对枚举类型重写 String 方法,可以对枚举值进行自定义,可以清晰地描述该枚举变量的意义和作用。

总结

枚举定义了一组有限的常量集,像 C++、java 等语言有 enum 关键字来定义枚举类型,但是 Go 并没有提供枚举关键字来定义枚举类型,我们可以利用 const + iota 来达到枚举的效果。

同时很多开发者会忽略枚举的使用,因为枚举并不是必须使用的,完全可以直接常量来替代。但是枚举的使用提高程序可读性,严谨性等,所以在项目中使用枚举最好能够了解枚举的使用场景以及一些特定用法。

在这里插入图片描述
关注微信公众号,可了解更多云原生详情~

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言中的reflect包提供了一种对程序的静态类型进行操作的方法,即可以在程序运行时动态地调用、检查和更改变量、类型和方法。 首先,reflect包提供了两个重要的类型:Type和Value。Type表示变量的类型,Value则表示变量的值。可以通过reflect.TypeOf和reflect.ValueOf来获取一个变量的Type和Value。 使用reflect包,我们可以在运行时获取变量的类型和值的一些基本信息,例如判断一个变量是否是某个特定类型,或者获取一个变量的名称和值。这在某些情况下可能是非常有用的,比如在编写通用的函数时,需要对不同类型的变量做相同的处理。 此外,reflect包还提供了一些函数来获取、设置和调用变量、类型和方法的具体信息。可以使用reflect.Value的相关方法来获取和设置变量的值,也可以使用reflect.Type的相关方法来获取类型的信息。使用reflect包还可以动态地调用某个值的方法。 需要注意的是,使用reflect包可能会导致一些性能上的损失,因为在运行时需要通过反射来获取变量的信息。因此,在性能要求较高的场景下,尽量避免使用反射。 总结而言,reflect包为我们提供了一种在运行时对变量、类型和方法进行操作的方法,可以通过反射来获取、设置和调用它们的信息。但是,需要注意在性能要求较高的情况下,尽量避免使用反射。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值