在 Go 中枚举常量的最佳方法
Best way to enumerate constants in Go
我正在学习其他语言之后开始学习 Go。 Go 有一个非常 elegant way of creating constants 的数值,例如:
const (
_ = iota // 0 and is skipped
Sunday // 1
Monday // 2
...
)
这个写起来很容易,但是真的容易维护吗?例如,如果你突然在 between present 中插入新的值,所有后续的值都会改变。而且很难找到,只有仔细阅读差异才能揭示它。或者其他部分的错误。我怎样才能用名称提取这些值并在程序的其他部分或数据库中使用?
例如对于 PostgreSQL 我可以定义:
CREATE TYPE color AS ENUM ('', 'Sunday', 'Monday');
只是为了说明一个想法。例如,Python has Enum 类型:
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
那你就可以像Color.RED
一样使用它了。接下来我可以取所有值:
list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
这使我能够 "introspect" 在数据库中模块化和创建易于阅读的枚举。例如对于 PostgreSQL 我可以定义:
CREATE TYPE color AS ENUM ('RED', 'GREEN', 'BLUE');
我怎样才能:
- 反映 golang 常量名称?
- 制作不会漂移的防错常量?只能手动修复它们吗?
- 是否有更好的惯用方法?
谢谢。
1) 您可以使用 stringer 生成名称 https://godoc.org/golang.org/x/tools/cmd/stringer
2) 不确定你的意思?大多数语言都允许你漂移值,如果你想让数字保持不变,你应该总是添加到列表的末尾,或者像 python 一样,你可以明确地将每个值设置为一个数字,而不是使用 iota。
3) 不完全是,枚举在 golang 中不是很好
只是一个建议,但可能对您的情况有所帮助:我发现如果很明显这些值看起来像位掩码,那么以后常量不太可能是 changed/broken,您可以在像这样:
const (
Red = 1 << iota
Green
Blue
) // values = 1, 2, 4
而且,即使它不是最漂亮的声明,您也可以包含掩码常量
const (
Red, RedMask = 1 << iota, 1<< iota - 1 // Red = 1, RedMask = 0
Green, GreenMask // Green = 2, mask = 1
Blue, BlueMask // 4, 3
RGB, RGBMask // 8, 7
)
这与为这些常量指定的 type
相结合可能会有用:
type ColourConst int
const (
Red, RMask ColourConst = 1 << iota, 1 << iota-1
// ...
_, All
)
// something like this (untested, might not be correct)
func (c ColourConst) validate() error {
mask := int(c) & (-1 * int(c))
if mask != int(c) {
return errors.New("Colour is not a single bit value")
}
if s := c & All; s != c {
return errors.New("Colour is not in range")
}
}
我知道星期几不太可能用作位掩码,但它可以降低人们破解密码的可能性。至少,它表明常量的顺序很重要,这就是 iota
IMO 的作用。
解决方案。
有优秀的模块 Enumer 和 Enumelinter
我正在学习其他语言之后开始学习 Go。 Go 有一个非常 elegant way of creating constants 的数值,例如:
const (
_ = iota // 0 and is skipped
Sunday // 1
Monday // 2
...
)
这个写起来很容易,但是真的容易维护吗?例如,如果你突然在 between present 中插入新的值,所有后续的值都会改变。而且很难找到,只有仔细阅读差异才能揭示它。或者其他部分的错误。我怎样才能用名称提取这些值并在程序的其他部分或数据库中使用? 例如对于 PostgreSQL 我可以定义:
CREATE TYPE color AS ENUM ('', 'Sunday', 'Monday');
只是为了说明一个想法。例如,Python has Enum 类型:
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
那你就可以像Color.RED
一样使用它了。接下来我可以取所有值:
list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]
这使我能够 "introspect" 在数据库中模块化和创建易于阅读的枚举。例如对于 PostgreSQL 我可以定义:
CREATE TYPE color AS ENUM ('RED', 'GREEN', 'BLUE');
我怎样才能:
- 反映 golang 常量名称?
- 制作不会漂移的防错常量?只能手动修复它们吗?
- 是否有更好的惯用方法?
谢谢。
1) 您可以使用 stringer 生成名称 https://godoc.org/golang.org/x/tools/cmd/stringer
2) 不确定你的意思?大多数语言都允许你漂移值,如果你想让数字保持不变,你应该总是添加到列表的末尾,或者像 python 一样,你可以明确地将每个值设置为一个数字,而不是使用 iota。
3) 不完全是,枚举在 golang 中不是很好
只是一个建议,但可能对您的情况有所帮助:我发现如果很明显这些值看起来像位掩码,那么以后常量不太可能是 changed/broken,您可以在像这样:
const (
Red = 1 << iota
Green
Blue
) // values = 1, 2, 4
而且,即使它不是最漂亮的声明,您也可以包含掩码常量
const (
Red, RedMask = 1 << iota, 1<< iota - 1 // Red = 1, RedMask = 0
Green, GreenMask // Green = 2, mask = 1
Blue, BlueMask // 4, 3
RGB, RGBMask // 8, 7
)
这与为这些常量指定的 type
相结合可能会有用:
type ColourConst int
const (
Red, RMask ColourConst = 1 << iota, 1 << iota-1
// ...
_, All
)
// something like this (untested, might not be correct)
func (c ColourConst) validate() error {
mask := int(c) & (-1 * int(c))
if mask != int(c) {
return errors.New("Colour is not a single bit value")
}
if s := c & All; s != c {
return errors.New("Colour is not in range")
}
}
我知道星期几不太可能用作位掩码,但它可以降低人们破解密码的可能性。至少,它表明常量的顺序很重要,这就是 iota
IMO 的作用。
解决方案。 有优秀的模块 Enumer 和 Enumelinter