最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

第 45 講:枚舉(一):枚舉的基本機(jī)制

2021-07-02 23:12 作者:SunnieShine  | 我要投稿

前面我們把基本的結(jié)構(gòu)的使用方式、繼承關(guān)系的理論給大家介紹了一遍。說實(shí)話,也沒有多少內(nèi)容,主要是前面類的內(nèi)容學(xué)完了,這邊的結(jié)構(gòu)都是照著用就可以,只是部分細(xì)節(jié)有點(diǎn)不一樣而已。

今天我們繼續(xù)介紹結(jié)構(gòu)。今天我們要介紹的是枚舉類型(Enumeration Type)。

因?yàn)閮?nèi)容非常多,所以我們分若干部分介紹。今天先講解枚舉的基本語(yǔ)法、用法和它的底層邏輯。

Part 1 什么是枚舉類型?

思考一個(gè)問題。我們把全班的人的基本數(shù)據(jù)存儲(chǔ)到一個(gè)表里,其中有一個(gè)信息名字叫做性別(Gender)。我們前面使用的是一個(gè) bool 類型的數(shù)據(jù)代指是不是男生。如果是男生,這個(gè)數(shù)值就是 true;否則用的是 false 表示的。不過這樣有一點(diǎn)小小的問題是,這樣數(shù)據(jù)不夠直觀:因?yàn)閿?shù)據(jù)本身直接用 truefalse,從這兩個(gè)單詞上看,我們是看不出這個(gè)人是男生還是女生的。如果我們 C# 里有一種機(jī)制,可以專門用一個(gè)“可以直接看出來數(shù)據(jù)是什么數(shù)值的”的類型來表達(dá)的話,豈不是美哉。

是的,C# 確實(shí)為了這一點(diǎn)提供了一種機(jī)制,叫做枚舉類型。所謂的“枚舉”,就是將一個(gè)數(shù)據(jù)的所有取值情況全部列出來的一種邏輯。

Part 2 語(yǔ)法

比如前面舉例里說到的“性別”。咱們這里暫時(shí)按照“男”和“女”來表達(dá)的話,那么就只有兩種情況。那么,我們可以通過這樣的代碼表達(dá)出來:

當(dāng)我們看到 Male 的時(shí)候,我們自然而然就可以知道這個(gè)人性別是男生;反之看到 Female 就可以反應(yīng)過來這個(gè)人是女生。這樣的話,我們就可以直接給數(shù)據(jù)的數(shù)值“取名”,來達(dá)到“數(shù)值有意義”的效果。

它的語(yǔ)法很簡(jiǎn)單:

一個(gè)訪問修飾符(可以不寫,和結(jié)構(gòu)、類和接口的聲明里的那個(gè)訪問修飾符一樣,可有可無(wú));然后是 enum 關(guān)鍵字;然后是枚舉的類型名稱;然后是一對(duì)大括號(hào),里面寫的是所有可能的數(shù)值。

稍微注意一下最后這個(gè)分號(hào)。分號(hào)是可以不寫的(而且一般也是不寫的,因?yàn)閷懖粚懚紱]有區(qū)別),和其它數(shù)據(jù)類型的聲明長(zhǎng)一樣;但是不同于其它的數(shù)據(jù)類型,枚舉類型可以在大括號(hào)最后加一個(gè)分號(hào),但類、結(jié)構(gòu)和接口的聲明的最外層大括號(hào)最后是不能加分號(hào)的。這一點(diǎn)只有枚舉類型可以。

其中,枚舉的數(shù)值是需要寫成“名字 = 數(shù)值”的一對(duì)賦值關(guān)系的。當(dāng)然,右側(cè)的這個(gè)賦值關(guān)系可以省略,默認(rèn)是從 0 開始計(jì)算。比如說,這里 MaleFemale 都不寫賦值部分的話,那么:

代碼就變成了這樣。這樣也是 OK 的,它依舊和前面的 0 和 1 等價(jià):從第一個(gè)枚舉數(shù)值開始,默認(rèn)從 0 開始計(jì)算。第一個(gè)元素的數(shù)值是 0、第二個(gè)數(shù)值是 1,以此類推。如果中間給出了某個(gè)元素的數(shù)值,那么后面沒有給出賦值關(guān)系的這些數(shù)值都是依賴于前一個(gè)枚舉數(shù)值的值,然后加 1 得到。

這樣的話,AG 的數(shù)值分別是 0、1、100、101、102、29 和 30??傊褪强辞懊嬉粋€(gè)的數(shù)值,在這個(gè)基礎(chǔ)上增加 1 得到。第一個(gè)元素默認(rèn)是 0(當(dāng)然,如果你寫了數(shù)值,那么就是這個(gè)數(shù)值)。

那么,怎么使用呢?

Part 3 枚舉類型的使用

假設(shè) Person 類型長(zhǎng)這樣:

啰嗦一下。我們觀察一下屬性的書寫格式,顯然可以知道的是 public 后面跟的這個(gè)就是類型名稱,而類型名后跟的才是屬性的名字。那么我把屬性名和類型名稱取名成一樣的,會(huì)不會(huì)造成歧義?肯定不會(huì),對(duì)吧。因?yàn)橄阮愋兔髮傩悦?,那么這樣的關(guān)系一旦出現(xiàn)的話,C# 就能識(shí)別和分辨出來這樣取名是哪個(gè)信息對(duì)應(yīng)哪個(gè)部分。所以 public Gender Gender { get ... } 里的第一個(gè) Gender 表示 Gender 類型,而第二個(gè) Gender 實(shí)際上指的是配套 _gender 字段的 Gender 屬性名字了。

我們?cè)偌僭O(shè)我們已經(jīng)得到了一個(gè)列表叫做 classmates,存儲(chǔ)的是這些人的數(shù)據(jù)信息。

請(qǐng)注意第 6 行代碼。classmate 后我們直接跟上的是 .Gender。我們假設(shè) Person 類型包含這個(gè)實(shí)例屬性,因此我們可以直接通過這樣的語(yǔ)法得到信息;接著,我們使用 classmate.Gender 表達(dá)式,得到的是一個(gè) Gender 類型的結(jié)果,那么我們要使用 == 來比較這個(gè)結(jié)果到底和哪個(gè)枚舉的數(shù)值相等。比較方式也很簡(jiǎn)單:== Gender.Male。右側(cè)用 類型名字.枚舉數(shù)值名 的方式取得結(jié)果,然后用等號(hào)比較數(shù)值是不是一樣的。

如果一致,那么我們就可以認(rèn)為 classmate 的性別是男了,那么我們就給 boysCount 增加 1。一輪 foreach 循環(huán)下來,我們就把整個(gè)數(shù)組全部看完了,這樣就可以達(dá)到統(tǒng)計(jì)男女生分別有多少個(gè)人的效果。

Part 4 那么,枚舉數(shù)值的值到底有什么用?

好像,我們直接用 == 比較數(shù)值是不是一樣,按名字去比較好像跟這個(gè)設(shè)置的 0、1、2 這些數(shù)據(jù)沒有關(guān)系啊。實(shí)際上不是的。它其實(shí)是和枚舉底層實(shí)現(xiàn)是有關(guān)系的。我們這里需要把枚舉看作是一種“可以自己命名數(shù)值信息的整數(shù)類型”。它本質(zhì)上是一種整數(shù)類型,只是它長(zhǎng)相非常不像是整數(shù)類型(什么 intshort 什么的)。確實(shí)不太像,但因?yàn)樗罱K會(huì)通過 = 來賦值,那么最終我們這里使用的 == 比較其實(shí)是看的這個(gè)整數(shù)數(shù)值是不是相同。如果直接上手比較 Gender.Male 這個(gè)字符串是不是一樣的話,肯定比比整數(shù)要慢一點(diǎn)。所以 C# 把枚舉類型比較結(jié)果用一個(gè)整數(shù)表示出來,我們稱為它的特征值(Eigenvalue)。這個(gè)枚舉數(shù)值整體,我們稱為一個(gè)枚舉字段(Enumeration Field)或者叫一個(gè)枚舉成員(Enumeration Member)。

說清楚這個(gè)名字后,我們來說一下枚舉的繼承機(jī)制。枚舉是不能自定義繼承關(guān)系的,你只能直接使用,它也必須表示成一個(gè)整數(shù)。但是整數(shù)的類型也很多,目前在 C# 里有如下這些數(shù)據(jù)類型是整數(shù)的類型:

  • sbyte:帶符號(hào)字節(jié)型

  • byte:無(wú)符號(hào)字節(jié)型

  • short:短整數(shù)型

  • ushort:無(wú)符號(hào)短整數(shù)型

  • int:整數(shù)型

  • uint:無(wú)符號(hào)整數(shù)型

  • long:長(zhǎng)整數(shù)型

  • ulong:無(wú)符號(hào)長(zhǎng)整數(shù)型

這些數(shù)據(jù)類型都可作為枚舉類型的數(shù)值的類型。我們可以規(guī)定枚舉類型到底用的是哪個(gè)整數(shù)類型作為類型的數(shù)值表達(dá),語(yǔ)法是通過類似繼承關(guān)系相同的語(yǔ)法追加到類型聲明最后:

如果這個(gè)枚舉用到的字段很少,我們可以使用 byte,因?yàn)樗耆貌坏侥敲炊嗟那闆r。雖然我們經(jīng)常省略不寫這個(gè)枚舉的“繼承關(guān)系”,但是我們依舊需要了解,其實(shí)這個(gè)數(shù)值類型是可以自己控制的。

Part 5 枚舉類型和整數(shù)類型的互相轉(zhuǎn)化

枚舉和整數(shù)在底層里可以說是基本一樣,但為了約束用戶使用 C# 更為規(guī)范,很多語(yǔ)法處理起來還是不一樣的。比如轉(zhuǎn)換的關(guān)系。雖然說的是兩者底層是基本一樣的,但我們?nèi)匀徊荒苤苯影颜麛?shù)賦值給枚舉,反之亦然。

這是因?yàn)槲覀冑x值的整數(shù)很有可能超出賦值范圍。比如說我 Gender 此時(shí)的整數(shù)表示范圍只可能是 0 和 1,但我可能手殘賦值過去一個(gè) 2:

反之亦然??赡苣銜?huì)問,我不管拿什么整數(shù)的數(shù)據(jù)類型接收,貌似都可以合理。比如 int i = gender;。gender 完全不可能超出 int 范圍,我這么賦值貌似沒有任何問題吧。

這是一種 C# 的語(yǔ)法約束。它為了規(guī)范化你的使用,這兩種類型是不能直接轉(zhuǎn)化的,因?yàn)樗鼈儽憩F(xiàn)出來的類型機(jī)制不同。那么怎么轉(zhuǎn)換呢?強(qiáng)制轉(zhuǎn)換就行了。使用強(qiáng)制轉(zhuǎn)換機(jī)制來告知編譯器,我這么做是我預(yù)期的行為。

假如說 Gender 還是基于 int 類型的話:

這樣的賦值就沒有問題了。但是……思考一點(diǎn)。如果我們嘗試改變這段代碼里 int 變量接收,而改用 short 的話呢?你這個(gè)時(shí)候就需要兩次強(qiáng)制轉(zhuǎn)換了:

是的,你必須要兩次強(qiáng)制轉(zhuǎn)換。這是為什么呢?因?yàn)?Gender 類型是基于 int 的,所以 (int) 強(qiáng)轉(zhuǎn)只是讓 Gender 能夠用 int 類型來表現(xiàn)出來而已(這樣轉(zhuǎn)換沒有問題);然后,int 轉(zhuǎn)換到 short 才是告訴編譯器,我這是預(yù)期行為,所以再次使用 (short) 強(qiáng)轉(zhuǎn)來改變?cè)窘Y(jié)果的類型。

Part 6 枚舉類型的繼承機(jī)制

前文說到,枚舉類型只能定義特征值的賦值類型,而不能改變它的繼承關(guān)系。那么它原始的繼承關(guān)系是如何的呢?

所有的枚舉類型統(tǒng)統(tǒng)從一個(gè)叫 Enum 的抽象類進(jìn)行派生的。這一點(diǎn)和結(jié)構(gòu)從 ValueType 抽象類派生可以說是非常相似,都是從抽象類派生下來的類型。那么,為什么要有這樣的派生機(jī)制呢?這是因?yàn)槊杜e類型有別的類型做不到的事情,就是表達(dá)數(shù)據(jù)語(yǔ)義化。那么,對(duì)于數(shù)值本身來說,顯然就會(huì)有非常多的額外處理機(jī)制,比如說取這個(gè)枚舉的名字啊,獲取這個(gè)特征值是不是在枚舉表達(dá)的范圍里之類的。那么,Enum 這個(gè)抽象類就提供了這些操作。

比如這樣就達(dá)到了一種多態(tài)賦值的過程。不過請(qǐng)注意的是,左側(cè)的 Enum 是引用類型(抽象類嘛那當(dāng)然是引用類型了),右側(cè)的是值類型(枚舉都基于整數(shù)了那還不是值類型?),所以這種賦值會(huì)造成隱式的裝箱行為。當(dāng)然,裝箱也無(wú)傷大雅,執(zhí)行起來也沒問題,只是效率略低一點(diǎn)。

馬上我們來說一下 Enum 類型的基本用法。

Part 7 枚舉類型和字符串之間的轉(zhuǎn)化

前面我們說到,枚舉類型是用整數(shù)數(shù)值(它的特征值)來比較的。那么枚舉類型可否表示成字符串形式呢?可以。它的轉(zhuǎn)換和之前我們整數(shù)和字符串互相轉(zhuǎn)換方式完全一樣,不過類型名字稍微換一下。

我們使用完全和 object 這些類型一樣的 ToString 方法來獲取字符串。最后我們可以得到的結(jié)果是 "Male"。請(qǐng)注意,雖然我們寫成 Gender.Male,但結(jié)果并不會(huì)包含“Gender.”這一部分。

這個(gè)是轉(zhuǎn)字符串。反過來呢?反過來的話,語(yǔ)法有些超綱,我們需要使用一個(gè)叫做 typeof 的表達(dá)式來表示。這一點(diǎn)有些麻煩。

滿天飛的小括號(hào)。首先,我們需要學(xué)習(xí)的是 Enum.Parse 這個(gè)靜態(tài)方法。這個(gè)方法后需要跟兩個(gè)參數(shù),第一個(gè)參數(shù)是需要你使用 typeof 這個(gè)稍微超綱一點(diǎn)的語(yǔ)法來表達(dá)是什么類型。因?yàn)?Enum.Parse 最終還是 Enum 類型,但是我們的枚舉類型是我們自己定義的,所以我們自己定義的枚舉類型和 int 這樣的類型還有所不同。int、short 好歹是單個(gè)的個(gè)體的類型,這樣 Parse 無(wú)需指定類型名字就可以直接轉(zhuǎn)換,而且非常方便;但是問題就出在 Enum 本身機(jī)制上。

總之,我們是無(wú)法從抽象類型 Enum 上知道我們自己的枚舉類型到底是如何的。因此,我們必須要制定 Parse 方法到底轉(zhuǎn)換成什么類型才可以。但是,要指明類型是什么,我們目前學(xué)到的語(yǔ)法還做不到這一點(diǎn),所以用到了這里的超綱語(yǔ)法 typeof。這個(gè)表達(dá)式的寫法是 typeof(類型)。我們直接在小括號(hào)里寫上這個(gè)類型名字,這樣就可以指示類型的基本信息了。這樣,我們就可以把這個(gè)東西傳過去,Enum.Parse 就知道我們要轉(zhuǎn)成什么類型的數(shù)據(jù)了。那么,第二個(gè)參數(shù)自然就是我們需要的字符串了。

另外,C# 還沒有這么智能,智能到參數(shù)用的這個(gè) typeof 就能暗示返回值的類型,所以返回值 Enum.Parse 方法是 object 來表示的。這就體現(xiàn)出了 object 的好處了。如果沒有 C# 面向?qū)ο蟮倪@一些繼承機(jī)制,就不可能存在 object 這樣的頂級(jí)數(shù)據(jù)類型。如果沒有頂級(jí)數(shù)據(jù)類型的話,我們就無(wú)法通過語(yǔ)法實(shí)現(xiàn)這里 Enum.Parse 返回值的類型確定。正是因?yàn)?object 類型是任何數(shù)據(jù)類型都可以直接賦值過去的機(jī)制,所以這樣就可以表達(dá)所有想要表示的結(jié)果,這就是 object 帶來的好處。

那么,得到 obj 變量后,我們顯然不能直接用。所以我們要向精確類型上進(jìn)行轉(zhuǎn)換。首先是 objEnum 類型上轉(zhuǎn),表示它實(shí)際上是一個(gè)枚舉類型的結(jié)果;然后再次轉(zhuǎn)為 Gender 這個(gè)精確的枚舉類型。前面的 objEnum 類型上轉(zhuǎn)是因?yàn)椋覀兿纫凳?obj 實(shí)際上是一個(gè)枚舉類型,然后才可以往下繼續(xù)轉(zhuǎn)換為 Gender。

實(shí)際上你寫成 Gender result = (Gender)obj; 也沒多大問題。但是這一點(diǎn)和前文描述的 (short)(int)gender 雙重類型轉(zhuǎn)換語(yǔ)法就不統(tǒng)一了,可能會(huì)造成理解上的困惑。初學(xué)為了了解類型的基本轉(zhuǎn)換規(guī)則,我們建議養(yǎng)成好習(xí)慣,先走 Enum 上轉(zhuǎn)一下,然后再繼續(xù)往下轉(zhuǎn)。

這里的 (Gender)(Enum)obj 實(shí)際上是和 (short)(int)gender 的轉(zhuǎn)換機(jī)制是不一樣的。但是這一點(diǎn)很難馬上就描述清楚,所以我干脆就不在這里說了。到時(shí)候你們直接看視頻吧。


第 45 講:枚舉(一):枚舉的基本機(jī)制的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
襄城县| 嘉荫县| 前郭尔| 白山市| 宣化县| 富裕县| 互助| 荣成市| 泰和县| 沁水县| 长治县| 宿迁市| 武汉市| 涪陵区| 辽源市| 太仓市| 伊宁县| 淄博市| 张家港市| 吉木萨尔县| 横山县| 利辛县| 扶沟县| 屏东县| 沙湾县| 锡林郭勒盟| 十堰市| 阳春市| 青阳县| 祁阳县| 辽中县| 苍梧县| 剑阁县| 清水县| 宁河县| 莱西市| 武川县| 大田县| 西青区| 平定县| 云浮市|