1. 程式人生 > 實用技巧 >聯合列舉型別:從C語言看列舉與聯合型別到TypeScript/Python

聯合列舉型別:從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 開始遞增,跟上面的寫法是等效的。

未指定的列舉名的值將依著最後一個指定值向後依次遞增(注意是最後一個指定值)

總結:

  1. 在沒有顯示說明的情況下,列舉常量(也就是花括號中的常量名)預設第一個列舉常量的值為0,往後每個列舉常量依次遞增1

  2. 在部分顯示說明的情況下,未指定的列舉名的值將依著之前最有一個指定值向後依次遞增

  3. 一個整數不能直接賦值給一個列舉變數,必須用該列舉變數所屬的列舉型別進行型別強制轉換後才能賦值

  4. 同一列舉型別中不同的列舉成員可以具有相同的值

  5. 同一個程式中不能定義同名的列舉型別,不同的列舉型別中也不能存在同名的列舉成員(列舉常量)

列舉型別變數

列舉型別變數不是一個包含若干個成員的集合,列舉型別變數和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