1. Go 真的有枚舉嗎?

        共 2435字,需瀏覽 5分鐘

         ·

        2022-01-02 12:54

        Go 中有枚舉嗎?這是一個(gè)模棱兩可的問題。有人說它有,有人說它沒有。

        什么是枚舉

        代碼抽象于現(xiàn)實(shí)。程序與生活中關(guān)于枚舉的概念是相通的:枚舉代表一個(gè)對象所有可能取值的集合。例如,表示星期的 SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY 就是一組枚舉值。

        實(shí)際上,我們可以將 Go 中所有原始類型視為一種枚舉。例如 bool 類型可以被認(rèn)為是一個(gè)只能為 true 或 false 的枚舉;byte 類型是 0 至 255 的枚舉;指針是 32 位或 64 位地址空間所有可能的內(nèi)存地址的枚舉。

        在例如 Python、Java、C 等語言中,一般都會有enum關(guān)鍵字或類提供于開發(fā)者實(shí)現(xiàn)枚舉。

        通用偽代碼可表達(dá)如下

        enum?枚舉名{
        ????標(biāo)識符①[=整型常數(shù)],
        ????標(biāo)識符②[=整型常數(shù)],
        ????...
        ????標(biāo)識符N[=整型常數(shù)],
        }枚舉變量;

        Go 沒有enum關(guān)鍵字。但我們可以觀察枚舉的特征:同一組枚舉值在定義后不應(yīng)被改變;枚舉值對應(yīng)的數(shù)據(jù)類型應(yīng)該相同;枚舉值是有限的;枚舉值與其含義是一一對應(yīng)的。

        根據(jù)以上特征,在 Go 中可通過const與 ?iota關(guān)鍵字來實(shí)現(xiàn)枚舉的訴求。

        iota

        const用于定義常量,它們在編譯期創(chuàng)建,在運(yùn)行時(shí)不能被修改。且僅有布爾型、數(shù)字型(整數(shù)型、浮點(diǎn)型和復(fù)數(shù))和字符串型能被定義為常量。

        常量聲明格式如下

        const?identifier?[type]?=?value

        而 iota 是常量計(jì)數(shù)器,它在遇到 const 關(guān)鍵字時(shí),就被重置為 0。當(dāng) const 中每增一行常量聲明(包括空白標(biāo)識符_),iota 計(jì)數(shù)將加1。

        const?(
        ?A?int?=?iota???//?0
        ?_???????????
        ?B??????????????//?2
        ?C??????????????//?3
        ?D??????????????//?4
        )

        const?(
        ?E?int?=?iota???//?0
        ?F??????????????//?1
        )

        Go 枚舉實(shí)現(xiàn)

        有了iota的參與,在 Go 中想要枚舉星期值,我們可以如下定義

        type?Weekday?int

        const?(
        ?_?Weekday?=?iota?//?ignore?first?value?by?assigning?to?blank?identifier
        ?Sunday
        ?Monday
        ?Tuesday
        ?Wednesday
        ?Thursday
        ?Friday
        ?Saturday
        )

        在使用枚舉值過程中,往往有輸出打印的需求

        fmt.Println(Sunday,?Monday)??//?1?2

        但原始的結(jié)果很不直觀,它不能反映出枚舉值背后的含義。我們需要為 Weekday 對象定義輸出。

        func?(w?Weekday)?String()?string?{
        ?return?[...]string{"Sunday",?"Monday",?"Tuesday",?"Wednesday",?"Thursday",?"Friday",?"Saturday"}[w-1]
        }

        在 Go 中,我們可以為任意自定義類型綁定String()方法,使其按照String()方法中定義的格式進(jìn)行打印。

        func?main()?{
        ?var?day?=?Monday
        ?switch?day?{
        ?case?Monday,?Tuesday,?Wednesday,?Thursday,?Friday:
        ??fmt.Printf("今天是%s,加油!打工人",?day)
        ?case?Saturday,?Sunday:
        ??fmt.Printf("今天是%s,好好休息!打工人",?day)
        ?default:
        ??fmt.Println("不存在的一天")
        ?}
        }

        執(zhí)行結(jié)果

        今天是Monday,加油!打工人

        Go 枚舉實(shí)現(xiàn)的不足

        上述方案看似已經(jīng)實(shí)現(xiàn)了枚舉功能,但其實(shí)存在一些問題。

        首先,由于 iota 基于 int 類型,這意味著在程序中,任何整數(shù)都可以轉(zhuǎn)為枚舉類型(這也是為何我們上文switchcase 中會有default分支),但這并不是我們想要的。

        func?main()?{
        ?fakeNum?:=?8
        ?day?:=?Weekday(fakeNum)
        ?fmt.Println(day)
        }

        #?go?run?main.go
        %!v(PANIC=String?method:?runtime?error:?index?out?of?range?[7]?with?length?7)

        那善于思考的讀者就會想到,既然 int 不行,那我們可以采用字符串常量來表示枚舉值啊。但這個(gè)方案同樣存在上述的問題,而且相較于使用 int 比較,當(dāng)比較字符串時(shí),需要付出額外的性能成本。

        另外,我們對于枚舉還有一個(gè)很重要的訴求,就是迭代。對應(yīng)于 Go 循環(huán)表達(dá)式,枚舉迭代的期望是這樣

        ?for?i,?day?:=?range?Weekday?{
        ??...
        ?}

        但顯然,現(xiàn)在的代碼方案滿足不了這種訴求。

        總結(jié)

        本文討論了 Go 目前通過 iota 關(guān)鍵字實(shí)現(xiàn)枚舉的做法,但這種方式并沒有實(shí)現(xiàn)完整的枚舉功能。在官方 issue 19814 中提出了 Go 中應(yīng)該增加 enum 關(guān)鍵字的提案,感興趣的讀者可以詳細(xì)查看。

        關(guān)于 Go 中的枚舉實(shí)現(xiàn),你有不一樣的觀點(diǎn)嗎,歡迎留言討論。

        參考

        proposal: spec: add typed enum support: https://github.com/golang/go/issues/19814


        ? ?


        喜歡明哥文章的同學(xué)
        歡迎長按下圖訂閱!

        ???


        瀏覽 37
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 超碰在线导航 | 国产人妻 精品无码免费 | 激情文学一区二区三区 | 日本三级韩国三级美三级91 | 西西444www无码大胆国产超碰 |