在Go中声明`enum`的方法

786 阅读3分钟

在Go中,没有 enum 的数据类型,如C++、Java或Python语言。然而,这并不意味着 enums 不能被创建。如果你声明自定义类型的常量和一些辅助方法,你会得到一个非常类似于 enum 在其他编程语言中。

请看下面的例子,看看如何在Go中声明 enum 在Go中。

在这个例子中,我们用iota 来声明常量。

package main
import (
"fmt"
)
type Season int
const (
Spring Season = iota + 1
Summer
Autumn
Winter
)
func (s Season) String() string {
seasons := [...]string{"spring", "summer", "autumn", "winter"}
if s < Spring || s > Winter {
return fmt.Sprintf("Season(%d)", int(s))
}
return seasons[s-1]
}
func (s Season) IsValid() bool {
switch s {
case Spring, Summer, Autumn, Winter:
return true
}
return false
}
func main() {
mySeasons := []Season{Spring, Summer, Autumn, Winter, 6}
for _, s := range mySeasons {
fmt.Printf("season: %s, is valid: %t\n", s, s.IsValid())
}
}

输出

season: spring, is valid: true
season: summer, is valid: true
season: autumn, is valid: true
season: winter, is valid: true
season: Season(6), is valid: false

它是如何工作的

  1. 声明我们的enum的自定义类型和元素

    type Season int
    const (
    Spring Season = iota + 1
    Summer
    Autumn
    Winter
    )
    

    作为第一步,我们声明新的整数类型Season ,并使用关键字创建连续的常量值。 iota关键字创建连续的常量值。

    为什么我们使用int类型而不是字符串?

    常量通常被用来与一个变量相比较,比如说

    if Spring != season {
    // do something
    }
    

    通常我们不需要它们的具体数值,只需要知道常量和变量是不同的这一事实。在这种情况下,比较两个int 值要比比较两个字符串快。int 类型的常量也占用较少的内存空间。

    为什么我们要声明自定义类型Season ,而不是使用无类型的常量?

    自定义类型可以防止在编译阶段就传递无效的值。如果我们将季节声明为非类型常量,下面将无效的季节值6分配给变量的代码将无任何错误地执行。

    var d int = 6
    season := Spring
    season = d
    

    当使用Season 类型时,为了使这段代码无任何错误地运行,你需要明确地转换d 变量,所以不可能意外地做到这一点。

    var d int = 6
    season := Spring
    season = Season(d)
    

    另外,自定义类型的 **enum**使得代码更加简洁。例如,在声明函数

    func foo(s Season)
    

    你马上就可以看到,该函数需要季节作为参数,在

    func foo(s int)
    

    你只知道它需要一个整数参数,没有任何语义。多亏了自定义类型,我们还可以将方法添加到 enum 元素添加方法,比如String()IsValid()

  2. 创建String() 方法

    func (s Season) String() string {
    seasons := [...]string{"spring", "summer", "autumn", "winter"}
    if s < Spring || s > Winter {
    return fmt.Sprintf("Season(%d)", int(s))
    }
    return seasons[s-1]
    }
    

    String() 方法增加了将 常量打印为 ,而不是 ,所以对于语句 ,我们得到 ,而不是 。对于范围之外的变量 ,我们打印 ,其中 是 类型变量的整数值。Season string int fmt.Println(Spring) spring 1 Spring..Winter Season(X) X Season

  3. 创建IsValid() 方法

    func (s Season) IsValid() bool {
    switch s {
    case Spring, Summer, Autumn, Winter:
    return true
    }
    return false
    }
    

    IsValid() 是一个辅助方法。这个函数应该总是在用户输入时被调用,以验证用户没有传递一个无效的值。Season Spring Summer Autumn Winterenum.

  4. main() 功能

    func main() {
    mySeasons := []Season{Spring, Summer, Autumn, Winter, 6}
    for _, s := range mySeasons {
    fmt.Printf("season: %s, is valid: %t\n", s, s.IsValid())
    }
    }
    

    main() 函数呈现了当你使用String()IsValid() 方法来处理Season 常量的定义值以及超出范围的值时,你会得到什么。