聯合列舉型別:從C語言看列舉與聯合型別到TypeScript/Python
列舉,還是從hello world 開獎,大部分的人應該是從C開始的,比如我。當然,這部分也可以跳過。
詳說列舉型別:
C語言中的enum
計算機入門時候有點印象:
enum是C語言中的一個關鍵字,enum叫列舉資料型別。
列舉型別(enumerated type)是一種代表整數常量的資料型別。通過關鍵字enum,可以建立一個新“型別”並指定它的值。列舉型別的語法與結構體的語法相類似。
為什麼需要使用列舉型別
在實際程式設計中,有些資料的取值往往是有限的,只能是非常少量的整數,並且最好為每個值都取一個名字,以方便在後續程式碼中使用,比如一個星期只有七天,一年只有十二個月,一個班每週有六門課程等。一個訂單隻有n個狀態。
這是我們我們一一定義,導致巨集名過多,程式碼鬆散,看起來總有點不舒服。
#defineMon1varMon=1 #defineTues2varTues=2 #defineWed3varWed=3 ..... #defineSun7varSun=7
設計列舉型別的目的在於提高程式的可讀性。
列舉型別的定義形式
enum typeName{ valueName1, valueName2, valueName3, ...... };
-
enum是一個新的關鍵字,專門用來定義列舉型別,這也是它在C語言中的唯一用途;
-
typeName是列舉型別的名字;valueName1, valueName2, valueName3, ......是每個值對應的名字的列表。
-
花括號裡面的元素(列舉成員)是常量而不是變數,這個一定要搞清楚,因為列舉成員的是常量,所以不能對它們賦值,只能將它們的值賦給其他的變數
例如,列出一個星期第幾天對應星期幾:
enumweek{Mon,Tues,Wed,Thurs,Fri,Sat,Sun};
可以看到,我們僅僅給出了名字,卻沒有給出名字對應的值,這是因為列舉值預設從 0 開始,往後逐個加 1(遞增);也就是說,week 中的 Mon、Tues ...... Sun 對應的值分別為 0、1 ...... 6。
不顯式說明列舉常量的值,在沒有顯示說明的情況下,列舉常量預設第一個列舉常量的值為0,往後每個列舉常量依次遞增1
我們也可以給每個名字都指定一個值:
enumweek{Mon=1,Tues=2,Wed=3,Thurs=4,Fri=5,Sat=6,Sun=7};
更為簡單的方法是隻給第一個名字指定值
enumweek{Mon=1,Tues,Wed,Thurs,Fri,Sat,Sun};
這樣列舉值就從 1 開始遞增,跟上面的寫法是等效的。
未指定的列舉名的值將依著最後一個指定值向後依次遞增(注意是最後一個指定值)
總結:
-
在沒有顯示說明的情況下,列舉常量(也就是花括號中的常量名)預設第一個列舉常量的值為0,往後每個列舉常量依次遞增1
-
在部分顯示說明的情況下,未指定的列舉名的值將依著之前最有一個指定值向後依次遞增
-
一個整數不能直接賦值給一個列舉變數,必須用該列舉變數所屬的列舉型別進行型別強制轉換後才能賦值
-
同一列舉型別中不同的列舉成員可以具有相同的值
-
同一個程式中不能定義同名的列舉型別,不同的列舉型別中也不能存在同名的列舉成員(列舉常量)
列舉型別變數
列舉型別變數不是一個包含若干個成員的集合,列舉型別變數和int、char型別的變數其實差不多,只不過列舉型別變數的賦值只能用自身的列舉成員來賦值,以上面的例子來說,
變數a、b、c等的賦值就只能用列舉成員Mon、Tues、Wed、Thurs,而不能用其他列舉型別的列舉成員來賦值
列舉是一種型別,通過它可以定義列舉變數:enum week a, b, c;定義列舉型別的同時定義列舉變數
也可以在定義列舉型別的同時定義變數:enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;先定義列舉型別,再定義列舉變數
有了列舉變數,就可以把列表中的值賦給它:
enumweek{Mon=1,Tues,Wed,Thurs,Fri,Sat,Sun}; enumweeka=Mon,b=Wed,c=Sat;
其它語言的列舉型別,都跟C語言差不多
聯合(union)
聯合(union)又稱聯合體或是共用體,是一個能在同一塊儲存空間中(但非同時)儲存不同型別資料的資料型別。也就是說幾種成員資料“共用”了同一塊儲存空間。
聯合體的作用不光是節省儲存空間那麼簡單,更重要的是為資料提供了一個統一的封裝和外部訪問的介面。C語言編譯器保證了union的共用體的長度等於最長的成員的長度。
union的定義
union的定義形式為:
union 共用體名 { 型別名 成員名1;型別名 成員名2;型別名n 成員名n;};
與列舉很像對不?聯合型別,就是擴大了列舉的類型範圍。union也可以宣告為變數、陣列、指標等等。比如:
uniongroup{ intdigit; doublemyfloat; charletter; };
聯合型別共用體型別union group,它有3個成員,分別是int, char, double。由於double成員的長度最大,為8 Bytes,因此,共用體的長度也應為8 Bytes。
C語言編譯器保證了union的共用體的長度等於最長的成員的長度。
Typescript列舉型別enum
Typescript相比C語言,還有如如下特性
反向對映
我們可以通過 Enum[key] 或者 Enum.key 的方式獲取到對應的值。typescript 還支援反向對映,即可以通過值來獲取鍵,不過反向對映只支援數字列舉。下面是個例子:
enumStatus{ Success=200, NotFound=404, Error=500} console.log(Status.Success)//200 console.log(Status[200])//Success console.log(Status[Status.Success])//Success
比如狀態碼,相比 Map型別,列舉的值也可以取健,這是列舉的一個特殊用法
異構列舉
異構列舉是指,列舉可以混合字串和數字成員,如:
enumEnum{ A=0, B='hello'} console.log(Enum.A)//0console.log(Enum.B)//hello
這個只有弱型別語言才會有異構列舉。C語言列舉支援整形。python 可以通過引入from enum import IntEnum 限定。其實,這個C語言的Union 聯合型別不是很像嗎。
列舉成員型別和聯合列舉型別
如果一個列舉裡所有成員的值都是字面量型別的值,那麼這個列舉的每個成員和列舉本身都可以作為型別來使用。
字面量列舉成員需滿足以下條件:
-
不帶初始值的列舉成員,例如 enum E { A }
-
值為字串字面量,例如 enum E { A = 'hello' }
-
值為數字字面量,或者帶有一元運算子 +, -, ~其中之一的符號的數字字面量,例如 enum E { A = 1 },enum E { A = -1 }
-
當以上的條件均不滿足的情況下,列舉成員被當作是需要計算得出的值:enum OrderStatus{ A, B = 1 << 1,C=A||B ,G = "s".length }
列舉成員型別
把符合條件的列舉成員作為型別來使用,例子:
enumShapeKind{ Circle, Square } interfaceCircle{ kind:ShapeKind.Circle//使用ShapeKind.Circle作為型別,指定介面須有kind欄位,且型別為ShapeKind.Circle radius:number } interfaceSquare{ kind:ShapeKind.Square//同上 sideLength:number } letc:Circle={ kind:ShapeKind.Square,//Error!因為介面Circle的kind被指定為ShapeKind.Circle型別,所以這裡會報錯 radius:100 }
我的理解就是列舉的值 不是基本變數。
聯合列舉型別
C語言中,我是沒有使用過聯合列舉型別。聯合就是聯合,列舉就是列舉。這個是TypeScript的雜交品種。我也是不求甚解,照搬一下例子
//列舉Status裡有兩個狀態 enumStatus{ Off, On } //列舉Animal裡有兩個動物 enumAnimal{ Cat, Dog } //介面Light中定義status欄位,它是Status型別,可以是Off或者On狀態 interfaceLight{ status:Status } letlg1:Light={ status:Status.Off//正確 } letlg2:Light={ status:Animal.Cat//error不能將型別Animal.Cat分配給型別Status }
求大神賜教
執行時的列舉-Enums at runtime||compile time
列舉是在執行時真正存在的物件,可以把列舉當作物件使用:
enumE{ A, B } functionfunc(obj:{A:number}):number{ returnobj.A } console.log(func(E))//0
程式碼中,聲明瞭一個函式 func,它的引數是一個物件,且必須包含屬性名為 A 的屬性,A 的值為數值型別。當呼叫函式 func 時,把列舉 E 當作符合條件的實參傳入,正確執行。
常量列舉-const enum
在某種情況下,列舉和列舉成員都可以作為一種單獨的型別存在(列舉成員沒有初始值 / 所有成員都為數字列舉 / 所有成員均為字串列舉)
其定義的列舉,在經過編譯器編譯後是一個物件,這個物件我們可以在程式執行時使用,前面有說到。但有時定義列舉可能只是為了讓程式可讀性更好,而不需要編譯後的程式碼,即不需要編譯成物件。typescript中考慮到這種情況,所以加入了 const enum (完全嵌入的列舉)。typescript官網有一個線上編譯器,來看看下面的例子:
enumStatus{ Off, On } constenumAnimal{ Dog, Cat } conststatus=Status.On constanimal=Animal.Dog
這段程式碼編譯成JavaScript後是這樣的:
varStatus; (function(Status){ Status[Status["Off"]=0]="Off"; Status[Status["On"]=1]="On"; })(Status||(Status={})); varstatus=Status.On; varanimal=0/*Dog*/;
可以看到編譯後的程式碼中並沒有像建立Status一樣建立了Animal,而是直接把 Animal 中 Dog 值 0 替換到表示式中 Animal.Dog 的位置,這樣就節省了生成程式碼的開銷。
python列舉型別
enum模組是系統內建模組,可以直接使用import匯入,但是在匯入的時候,不建議使用import enum將enum模組中的所有資料都匯入,一般使用的最多的就是enum模組中的Enum、IntEnum、unique這幾項
#匯入列舉類 fromenumimportEnum #繼承列舉類 classcolor(Enum): YELLOW=1 BEOWN=1 #注意BROWN的值和YELLOW的值相同,這是允許的,此時的BROWN相當於YELLOW的別名 RED=2 GREEN=3 PINK=4
列舉和我們在物件中定義的類變數時一樣的,每一個類變數就是一個列舉項,訪問列舉項的方式為:類名加上類變數。但是不適用系統自帶的列舉,而是普通類。會存在如下問題:
-
列舉類中,不應該存在key相同的列舉項(類變數)
-
不允許在類外直接修改列舉項的值
python列舉不像C語言,列舉成員變數值只能是整形,同為弱型別語言,與TypeScript不同的是,沒有異構列舉,
-
如果要列舉類中的Value只能是整型數字,那麼,可以匯入IntEnum,然後繼承IntEnum即可
from enum import IntEnum //注意,此時,如果value為字串的數字,也不會報錯
-
如果要列舉類中的key也不能相同,那麼在匯入Enum的同時,需要匯入unique函式
from enum import Enum, uniqu
參考資料:
C語言列舉型別(C語言enum用法)詳解 c.biancheng.net/view/2034.html
列舉型別enum詳解——C語言https://www.cnblogs.com/lanhaicode/p/10620028.html
【C語言】聯合與列舉型別https://blog.csdn.net/tracer9/article/details/50382370
關於typescript中的列舉你需要知道這些 https://www.cnblogs.com/wjaaron/p/11672764.html
轉載本站文章《聯合列舉型別:從C語言看列舉與聯合型別到TypeScript/Python》,
請註明出處:https://www.zhoulujun.cn/html/webfront/ECMAScript/typescript/2020_0410_8368.html