Printable enums in Go

huangapple go评论84阅读模式
英文:

Printable enums in Go

问题

在某些情况下,为了用户交互和调试目的,将枚举类型转换为可读的字符串表示是很方便的。到目前为止,我想到的最好的方法是:

type ElementType int

const (
    Fire = ElementType(iota)
    Air
    Water
    Earth
)

var elementTypeMap = map[ElementType]string{
    Fire: "The Fiery Fire",
    Air: "The Airy Air",
    Water: "The Watery Water",
    Earth: "The Earthy Earth",
}

func (el ElementType) String() string {
    return elementTypeMap[el]
}

上述代码允许我将枚举作为整数使用和传递,保持其标准性能,并且可以在任何地方轻松打印其字符串表示。唯一的缺点是,如果有多个枚举类型,会有一些样板代码,这可能会增加代码量。我希望能够避免这种情况。

有没有一种方式,最好是一种惯用的方式,可以减少上述的样板代码?

英文:

In some cases, it's convenient for user interaction and debugging purposes to have a human-readable string representation of enums. So far, the best I have come up with is:

type ElementType int

const (
    Fire = ElementType(iota)
    Air
    Water
    Earth
)

var elementTypeMap = map[ElementType]string{
    Fire: "The Fiery Fire",
    Air: "The Airy Air",
    Water: "The Watery Water",
    Earth: "The Earthy Earth",
}

func (el ElementType) String() string {
	return elementTypeMap[el]
}

The above, allows me to use and pass the enum as an int, keeping its standard performance, and to easily print its string representation anywhere. The only drawback is that there is an amount of boilerplate code that adds up if you have many enum types: I would be rather happy to avoid it.

Is there a way, preferably an idiomatic one, to reduce the boilerplate code above?

答案1

得分: 4

这看起来更干燥(和更快):

type ElementType int

const (
    Fire = ElementType(iota)
    Air
    Water
    Earth
)

var elementnames = [...]string {
    "The Fiery Fire",
    "The Airy Air",
    "The Watery Water",
    "The Earthy Earth"
}

func (el ElementType) String() string {
    return elementnames[el]
}

请注意,关于是否提供一种通用解决方案来为枚举常量分配名称的讨论已经在 golang-nuts 上进行过,据我所知,这并不被认为是必要的(请参阅 https://groups.google.com/forum/#!topic/golang-nuts/fCdBSRNNUY8)。

英文:

This looks dryer (and faster) :

type ElementType int

const (
    Fire = ElementType(iota)
    Air
    Water
    Earth
)

var elementnames = [...]string {
    "The Fiery Fire",
    "The Airy Air",
    "The Watery Water",
    "The Earthy Earth"
}

func (el ElementType) String() string {
    return elementnames[el]
}

Note that there was a discussion on golang-nuts on whether giving a generic solution to assign names to enum constants and as far as I know it wasn't seen as necessary (see https://groups.google.com/forum/#!topic/golang-nuts/fCdBSRNNUY8).

答案2

得分: 3

有替代的方法。例如,我想到了使用type elementType struct { name string }var Fire = &elementType{"The Fiery Fire"}。你仍然只需传递和比较指针,但可以附加任意数据。

我不太清楚你的具体用例,但我可能会以类似的方式处理:

type ElementType int

const (
    Fire = ElementType(iota)
    Air
    Water
    Earth
)

func (e ElementType) String() string {
    var names = [...]string{
        Fire:  "The Fiery Fire",
        Air:   "The Airy Air",
        Water: "The Watery Water",
        Earth: "The Earthy Earth",
    }
    return names[e]
}

使用映射在计算上相当昂贵。你可以使用数组,并以任意方式列出元素。此外,将数组作为局部变量可能会使后续更改更容易。如果你的ElementType是公开的,你可能还应该添加一个if e < 0 || e >= len(names)的条件判断。

英文:

There are alternative ways. For example, using a type elementType struct { name string } and var Fire = &amp;elementType{&quot;The Fiery Fire&quot;} comes to my mind. You will still just pass and compare pointers, but you can attach arbitrary data to it.

I do not exactly know your use-case, but I would probably do it in a similar way:

type ElementType int

const (
	Fire = ElementType(iota)
	Air
	Water
	Earth
)

func (e ElementType) String() string {
	var names = [...]string{
		Fire:  &quot;The Fiery Fire&quot;,
		Air:   &quot;The Airy Air&quot;,
		Water: &quot;The Watery Water&quot;,
		Earth: &quot;The Earthy Earth&quot;,
	}
	return names[e]
}

A map is computationally quite expensive. You can use an array and still list the elements in an arbitrary way. Also, making the array a local variable might make later changes easier. And you should probably also add an if e &lt; 0 || e &gt;= len(names) there, especially if your ElementType is exported.

答案3

得分: 1

我不认为数组和映射是这里的最佳选择。无论如何,您都必须手动定义元素,所以我认为switch语句是最清晰的选择。与映射类似,但与数组不同,它允许您使用非密集常量(即除了0,1,2,3...之外的常量)。它还允许您定义一个用于处理出现严重错误并使用非常量的情况;至少您可以定义一个自定义的panic消息。

我还敢打赌编译器可以对此进行一些花哨的优化,而对于数组/映射访问,由于它们是使用var而不是const声明的,编译器无法进行这些优化。

英文:

I don't think arrays and maps are the way to go here. You have to manually define the elements anyway, so I think switch is the clearest. Like a map, and unlike an array, it lets you use non-dense constants (meaning constants other than 0,1,2,3...). It also allows you to define a case in case something goes terribly wrong and you use a non-constant; at the very least you can define a custom panic message.

func (et ElementType) String() string {
    switch et {
    case Water:
        return &quot;The Watery Water&quot;
    case Air:
        return &quot;The Airy Air&quot;
    case Fire:
        return &quot;The Fiery Fire&quot;
    case Earth:
         return &quot;The Earthy Earth&quot;
    default:
         return &quot;Heart has been banned from the elements&quot;
    }
}

I'd also wager that the compiler can do some fancy optimizations with this that it can't do with array/map access since those are declared using var rather than const.

huangapple
  • 本文由 发表于 2014年5月29日 01:12:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/23917916.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定