Golang中如何声明枚举类型?

Golang中如何声明枚举类型? 如何在Golang中声明类似Java的枚举:

enum WeekDay {
SATURDAY,
SUNDAY,
... 
}

我找到了一种解决方案:

type WeekDay int
const (
	Saturday WeekDay = 1
	Sunday WeekDay = 2
)

func aFunc(WeekDay WeekDay)  {
...
}

但问题在于,当需要声明参数时,我可以使用显式整数而不是Weekday:

aFunc(55)

如何限制在参数中传递整数?

提前感谢。


更多关于Golang中如何声明枚举类型?的实战教程也可以访问 https://www.itying.com/category-94-b0.html

6 回复

iota 无法限制不当使用。 感谢回复。

更多关于Golang中如何声明枚举类型?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我很抱歉生气,但这只是因为 Go 语言的局限性。

我从未这样说过。我说的是"你不能",并展示了使用 iota 作为更符合语言习惯的方法,仅此而已。

引用 Hamed_Mostafaei:

如何限制在参数中传递整数?

你无法做到。

此外,更符合语言习惯的做法是使用 iota

type WeekDay int

const (
  Saturday WeekDay = iota
  Sunday
  Monday
  Tuesday
  // ...
}

一种(愚蠢的)实现方式是创建新类型并为其实现符合某个接口的方法

package weekday

// 创建新类型 weekday
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")
)

使用此类型/接口时,必须经过严格操作才能为其分配随机值

package main

import (
	"fmt"

	"./weekday"
)

// 将接口用于参数
func print(w weekday.Weekday) {
	fmt.Println("Day is", w)
}

func main() {
	var d1 = weekday.Monday
	var d2 = weekday.Tuesday

	fmt.Println(d1, d2, d1 == d2, d1 == weekday.Monday)

	print(d1)
}

输出结果

$ go run main.go
Monday Tuesday false true
Day is Monday

但这种方式相当笨拙且丑陋…

在Go语言中,没有内置的枚举类型,但可以通过类型定义和常量组来模拟枚举行为。要限制参数只能传递特定的枚举值,可以使用自定义类型并实现严格的类型检查。以下是改进方案:

package main

import "fmt"

// 定义WeekDay类型
type WeekDay int

// 声明枚举常量
const (
    Saturday WeekDay = iota + 1 // 从1开始
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
)

// 验证函数,确保只接受有效的WeekDay值
func (d WeekDay) IsValid() bool {
    switch d {
    case Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday:
        return true
    }
    return false
}

// 业务函数,添加验证
func aFunc(day WeekDay) {
    if !day.IsValid() {
        panic("invalid WeekDay value")
    }
    fmt.Printf("Day value: %d\n", day)
}

// 安全构造器模式
func NewWeekDay(value int) (WeekDay, error) {
    day := WeekDay(value)
    if !day.IsValid() {
        return 0, fmt.Errorf("invalid WeekDay value: %d", value)
    }
    return day, nil
}

func main() {
    // 正确用法
    aFunc(Saturday) // 输出: Day value: 1
    aFunc(Sunday)   // 输出: Day value: 2

    // 错误用法示例(编译通过但运行时报错)
    // aFunc(55) // 运行时panic: invalid WeekDay value

    // 使用安全构造器
    if day, err := NewWeekDay(1); err == nil {
        aFunc(day) // 输出: Day value: 1
    }

    if _, err := NewWeekDay(55); err != nil {
        fmt.Println(err) // 输出: invalid WeekDay value: 55
    }
}

更严格的方案使用未导出类型和构造函数:

package main

import "fmt"

// 未导出类型,外部无法直接创建
type weekDay int

// 枚举常量
const (
    saturday weekDay = iota + 1
    sunday
    monday
    // ... 其他天
)

// 导出变量提供访问
var (
    Saturday = saturday
    Sunday   = sunday
    Monday   = monday
)

// 只能通过预定义值调用
func aFunc(day weekDay) {
    fmt.Printf("Day value: %d\n", day)
}

func main() {
    aFunc(Saturday) // 正确
    aFunc(Sunday)   // 正确
    // aFunc(55)    // 编译错误:cannot use 55 (type int) as type weekDay
}

第一种方案允许类型转换但提供运行时验证,第二种方案通过未导出类型在编译时阻止非法值。选择取决于具体需求:需要灵活性使用第一种,需要编译时安全使用第二种。

回到顶部