CLR via C 15 列舉型別和位標誌

2021-09-06 16:09:23 字數 4788 閱讀 7395

原文:

[clr via c#]15. 列舉型別和位標誌

一、列舉型別

列舉型別(enumerated types)定義了一組"符號名稱/值"配對。

例如,以下color型別定義了一組符號,每個符號都標識一種顏色:

internal

enum

color

使用列舉型別的好處:

1)列舉型別使程式更容易編寫、閱讀和維護。有了列舉型別,符號名稱可在**中隨便使用,開發人員不需要記 住每個硬編碼的含義。而且,一旦與符號名稱對應的值發生變化,**也可以簡單的重新編譯,不需要對源**做出任何修改。除此之外,文件工具和其他實用程式能向開發人員顯示有意義的符號名稱。

2)列舉型別是強型別的。

在microsoft .net framework中,列舉型別不只是編譯器所關心的符號,它在型別系統中還具有"一等公民"的地 位,能實現非常強大的操作。列舉型別都直接從system.enum派生,後者從system.valuetype派生,而system.valuetype用從 

system.object派生。所以,列舉型別是值型別,可表示成未裝箱和已裝箱形式。然而,有別於其他值型別,枚 

舉型別不能定義任何方法、屬性和事件。不過可利用c#的"擴充套件方法"功能模擬向列舉型別新增方法。

編譯列舉型別時,c#編譯器會把每個符號轉換成為型別的乙個常量字段。例如,編譯器會把前面的color列舉型別看成以下**:

internal

struct

color : system.enum

c#編譯器實際上並不編譯這段**,因為它禁止定義從system.enum這一特殊型別派生的型別。不過,可以通過 上述偽型別定義了解內部工作方式。簡單的說,列舉型別只是乙個結構,其中定義了一組常量欄位和乙個例項字 段。常量欄位會嵌入程式集的元資料中,並可以通過反射來訪問。這意味著可以在執行時獲得與列舉型別關聯的 所有符號及其值。還意味著可以將乙個字串符號轉換成對應的數值。這些操作是通過system.enum基型別來提 供的,該型別提供了幾個靜態和例項方法,可利用它們操作列舉型別的乙個例項,從而避免了必須使用反射的麻 

煩。  

編譯時需要。

例如,system.enum型別有乙個getunderlyingtype的靜態方法,而system.type型別有乙個 getenumunderlyingtype的例項方法。

public

static type getunderlyingtype (type enumtype); //

system.enum中定義

public type getenumunderlyingtype (type enumtype); //

system.type中定義

這些方法返回用於容納乙個列舉型別的值的基礎型別。每個列舉型別都有乙個基礎型別,它可以是byte,sbyte, short,ushort,int(最常用,也是c#預設的),uint,long,或ulong。雖然這些c#基元型別都有都有物件的fcl 型別,但c#編譯器為了簡化本身的實現,要求只能指定基元型別名稱。如果使用fcl型別名稱(如int32),就會報 

錯。以下**演示了如何宣告乙個基礎型別為byte(system.byte)的列舉型別:

inter enum color :byte

基於這個color列舉型別,一下**顯示了getunderlyingtype 的返回結果:

//

以下**會顯示"system.byte"

console.writeline(enum.getunderlyingtype(typeof(color)));

c#編譯器將列舉型別視為基元型別。所以,可以運用許多操作符(==,!=,<,>等等)來操作列舉型別的例項。 所有這些操作符實際作用於列舉型別例項內部的value__例項字段。此外,c#編譯器還執行將列舉型別的例項顯式 的轉型為乙個不通過的列舉型別。也可以顯式將乙個列舉型別例項轉型為乙個數值型別。

可以呼叫system.enum的靜態方法getvalue或者system.type的例項方法getenumvalue獲取乙個陣列,該陣列的每乙個元素都對應列舉型別中的乙個符號名稱,每個元素都包含符號名稱的數值:

public

static array getvalues(type enumtype); //

system.enum中定義

public array getenumvalues(type enumtype); //

system.type中定義

這個方法結合tostring方法使用,可顯示列舉型別中所有符號名稱及其對應的數值,如下所示:

public

static

void

go() \t

", color);

}}

以上**產生的輸出如下:

number of symbols defined: 5

value symbol

----- ------

0 while

1 red

2 green

3 blue

4 orange

還有其他列舉型別成員,就不一一敘述了!

二、位標誌

程式設計師經常要與位標識(bit flag)集合打交道。呼叫system.io.file型別的getattributes方法,會返回fileattributes型別的乙個例項。fileattributes型別是基本型別為int32的列舉型別,其中每一位都反映了檔案的一項屬性。fileattibutes型別在fcl中的定義如下:

[flags]

[serializable]

[comvisible (

true

)]public

enum

fileattributes

為了判斷乙個檔案是否隱藏,可執行下面這樣的**:

string file =assembly.getentryassembly().location;

fileattributes attributes =file.getattributes(file);

console.writeline(

"is hidden?

",file,(attributes & fileattributes.hidden) !=0);

以下**演示了如何將乙個檔案的屬性改為唯讀和隱藏:

file.setattributes(file,fileattributes.readonly | fileattribute.hidden);

正如fileattributes型別展示的那樣,經常都要用列舉型別來表示一組可以組合的位標誌。不過,雖然列舉型別和

位標誌相似,但它們的語義不盡相同。例如,列舉型別表示單個數值,而位標識表示一組位,其中有些位是1,有 

些位是0.

定義用於標識位標誌的列舉型別時,當然應該顯式為每個符號分配乙個數值。通常,每個符號都有單獨的乙個位處於on(1)狀態.此外,經常都要定義乙個值為0的none符號。還可以定義一些代表常用位組合的符號。另外,強烈建議向列舉型別應用system.flags.attribute這個定製的attribute型別,如下所示

[flags] 

public

enum

actions

因為actions是列舉型別,所以在操作位標誌列舉型別時,可以使用上一節描述的所有方法。

actions actions = actions.read | actions.delete; //

0x0005

console.writeline(actions.tostring()); //

"read,delete"

呼叫tostring時,它會檢視將數值轉換為對應的符號。現在的數值是0x0005,它沒有對應的符號。不過,tostring

方法檢測到actions型別上存在[flags]這個attribute,所以tostring方法現在不會將該數值視為單獨的值。相反,

會將它視為一組位標誌。由於0x0005有0x0001和0x0004組合而成,所以tostring會生成字串"read,delete", 

如果從actions型別中刪除[flags]這個attribute,tostring方法返回"5"。

永遠不要對位標誌列舉型別使用isdefined方法,理由如下:

1)如果向isdefined方法傳遞乙個字串,它不會將這個字串拆分為單獨的token來進行查詢,而是檢視查詢整個字串,把它看成是包含逗號的乙個更大的符號。由於不能在列舉型別中定義含有逗號的符號,所以這個符號永遠找不到。

2)如果向isdefined方法傳遞乙個數值,它會檢查列舉型別是否定義了乙個其對應數值和傳入數值匹配的符號。由於位標誌不能這樣簡單匹配,所以isdefined通常會返回flase。

三、向列舉型別新增方法

現在,可以使用c#的擴充套件方法功能向列舉型別模擬新增方法。

如果想為fileattributes列舉型別新增一些方法,可以定義乙個包含了擴充套件方法的靜態類,如下所示:

public

static boolean set(this

fileattributes flags, fileattributes testflags)

從表面看,我似乎真的在列舉型別上呼叫這些方法:

fileattributes fa =fileattributes.system;

fa = fa.set(fileattributes.readonly);

15 列舉型別和標誌位

g常規 d十進位制 x十六進製制 mycontrol.style styles.showborder styles.showcaption 這時mycontrol.style列舉的值將變成 1 2 3,它的tostring 將變成 styles.showborder styles.showcapti...

掌握JDK1 5列舉型別

enum作為sun全新引進的乙個關鍵字,看起來很象是特殊的class,它也可以有自己的變數,可以定義自己的方法,可以實現乙個或者多個介面。當我們在宣告乙個enum型別時,我們應該注意到enum型別有如下的一些特徵。1 它不能有public的建構函式,這樣做可以保證客戶 沒有辦法新建乙個enum的例項...

9 4 列舉型別

乙個變數只有幾種可能的取值,可以一一枚舉出來,變數的取值僅限於列舉的值的有限範圍內,而不適宜用整型 實型 字元型直接表示,c語言提供了這種列舉資料型別。例如,一年的四季有春夏秋冬 乙個星期有星期日 星期一到星期六等。定義列舉型別的一般形式為 enum 列舉型別名 如 enum weekday 1.列...