国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

C# Idioms: Enum還是Enum Class(枚舉類)

2024-07-21 02:19:53
字體:
來源:轉載
供稿:網友


c# idioms:enum還是enum class(枚舉類)

marshine

(原文排版格式:http://www.marshine.com)

reversion:2004/5/28
修改說明:感謝ninputer提到的cls兼容問題,同時修改了原來版本沒有提及的equals改寫,以及修改"=="重載的不完善代碼,和增加enum struct內容

reversion:2004/6/4

增加kirc提到的enum的flags特性,因為文本超長,新的版本可以在http://www.marshine.com上閱讀。



常量類型的表示

系統中常常有一些屬性的屬性值是固定的一組值,它們的值域是封閉的(有限數量),比如國家代碼(每個國家具有唯一的代碼,而在一定時期國家的數量是確定的)、性別類型(男、女)。在現代 程序語言中,一種典型的表示方式是枚舉類型(enum)。enum表示封閉值域的類型,常常由程序語言作為一種數據類型直接支持,例如c,c#等。c#支持的enum在c的基礎上提供了類型安全的能力,下面是用c#定義的性別枚舉類型:

public enum sex {
male,
female,
}

java不支持enum數據類型,java認為c提供的enum并不是類型安全的,通常使用稱之為typesafe enum class的設計模式來獲得類似的效果(參見[joshua01] p80,item21 :replace enum constructs with classes)。enum class不允許外部構造實例成員(構造函數為private),提供靜態類型成員實例來表示封閉值域。使用enum class方式來表示sex類型可定義如下(c#):

public class sex{
// 私有構造保證值域的封閉性
private sex() {
}

pubic static readonly sex male = new sex():
pubic static readonly sex female = new sex():
}

同enum一樣,可以使用sex.male或sex.female的方式來訪問常量屬性,與靜態常量字段不一樣(如靜態字符串、整數),enum和enum class可以提供強類型的compile time檢查以及提供更好的數據封裝性和代碼可讀性。例如使用常量類型設置和比較屬性值:

// 設置屬性值
sex sex = sex.male;
// 比較
if (sex == sex.male) {
// ... ...
}

如果sex是使用enum定義的,則上面比較的實際上是enum字段的值;如果sex是使用enum class定義的,則比較的是靜態實例成員的引用地址,當然也可以使用equals方法來比較。

雖然enum class是來自于java的設計模式,但在c#中并非沒有意義,因為enum class提供了比enum類型更強大的能力。

enum與enum class的比較

enum與enum class均提供了封裝常量的能力,都能夠實現編譯時的強類型檢查,使用封閉值域防止非法值。不過,因為實現機制的不同,這兩種方式也具有不同的特點。

enum在c#中是一種值類型(value type),其基類型必須是整數類型(如int16),因此enum也具有值類型所具有的優點——比引用類型(reference type)更高的效率,定義簡單。但是其缺點不能實現自定義的行為,無法提供常量更多的屬性。

enum class就沒有這種限制,雖然enum class本身并不設計為可以繼承,但可以修改基類(system.object)的行為以提供更加豐富的能力(如修改tostring方法,根據使用者的本地語言輸出本地化的國家名稱),也可以提供更多的屬性 。例如我們提供一個候選的國家列表,除了能顯示國家名稱外,可以提供國家代碼、語言代碼信息。

enum class的問題

但enum class也有它的缺點,上面的設計中enum class通過進程內靜態成員引用地址相同來進行比較,但是當將一個序列化后的enum class實例反序列化后,clr會創建一個新的實例,從而造成反序列化值不等于序列化前值的現象:

iformatter formatter = new system.runtime.serialization.formatters.binary.binaryformatter();

memorystream stream = new memorystream();
// 序列化sex.male的值
formatter.serialize(stream, sex.male);
stream.seek(0,seekorigin.begin);
// 反序列化
sex sex = (sex)formatter.deserialize(stream);
console.writeline(sex == sex.male);

上面的代碼將輸出false。因此通過引用的方式是有局限性的,在java中這是一個比較棘手的問題,需要修改反序列化的行為(參看[joshua01]p171)。c#與java的實現機制不一樣,無法通過修改反序列化的行為來返回同一個常量實例, 但c#提供了操作符重載的能力,我們可以通過重載操作符“==”來解決這個問題,同時為了保持cls兼容以及與equals的行為一致,還需要改寫equals方法:

[serializable]
public class sex{
// 性別類型名
private string sexname;

// 私有構造保證值域的封閉性
private sex(string sexname) {
this.sexname = sexname;
}

public static readonly sex male = new sex("male");
public static readonly sex female = new sex("female");

// 提供重載的"=="操作符,使用sexname來判斷是否是相同的sex類型
public static bool operator ==(sex op1, sex op2) {
if (object.equals(op1, null)) return object.equals(op2, null);
return op1.equals(op2);
}

public static bool operator !=(sex op1,sex op2) {
return !(op1 == op2);
}

public override bool equals(object obj) {
sex sex = obj as sex;
if (obj == null) return false;
return sexname == sex.sexname;
}

public override int gethashcode() {
return sexname.gethashcode ();
}
}

通過操作符重載,不再使用引用地址來比較常量,而是通過值比較(如上面的sexname),因此要求每個常量實例必須具有唯一的標識值。 在不支持操作符重載的語言中,不能使用"=="來比較兩個常量值是否相等,而應該使用equals方法來代替。

enum class的設計

enum class一般符合下列規則:

私有構造函數,保證外部無法創建類實例(同時也使得類無法繼承)。
靜態只讀實例字段表示常量。
重載操作符"==",保證序列化后的值也能比較相等。當需要在進程間傳遞(如分布式應用)或需要序列化時,必須實現"=="操作符的重載。
改寫equals方法,保持"=="行為和equals一致。(改寫equals一般也同時改寫gethashcode方法 )
除此之外,還通常改寫tostring方法以提供顯示友好的名字,因為java和.net都在綁定或顯示對象時使用tostring方法(java中為tostring方法)輸出作為缺省的對象顯示字符串,比如將sex數組綁定到listbox或者使用console.write輸出時。下面的代碼改寫tostring方法以提供友好顯示的輸出:

public class sex{
... ...
public override string tostring() {
return sexname;
}
}

當然我們也可以利用tostring提供本地化支持,返回本地語言的字符串。

enum class另外一種常見的職責是提供不同值系統之間的類型轉換,如當從數據庫中讀取值時,利用parse方法將數據庫中值轉換為對象系統的常量實例,而在存儲時提供方法轉換為數據庫的值類型:

public class sex{
... ...
// 根據一個符合指定格式的字符串返回類型實例。
public static sex parse(string sexname){
switch (sexname) {
case "male" : return male;
... ...
}
}

// 返回數據存儲的值。
public string todbvalue(){
return sexname;
}
}

使用enum還是enum class?

根據enum和enum class的特點,我們可以根據對常量類型的要求決定使用enum還是enum class。

以下場景適合使用enum:

常量類型用于內部表示,不用于顯示名字。
常量值不需要提供附加的屬性。例如只需要知道國家代碼,而不需要獲得國家的其它屬性
enum class可以適用于更多的場景:

常用于可提供友好信息的類型。如本地化支持的類型名顯示,或者顯示與枚舉名不一致的名字,例如country.chn可顯示為"china"。
提供更多的常量屬性。
提供更加豐富的行為。如parse方法。
對常量進行分組。如country.asia包含亞洲國家。
使用struct來表示枚舉

如果值域不封閉,但希望提供一些常量,也可以使用struct,如system.drawing.color結構中的系統默認顏色設置。采用struct來設計enum值同enum class方式沒有本質的差異,只是struct必須提供無參數構造函數,因此無法實現封閉值域。

參考:

[joshua01]

effective java programming language guide , joshua bloch, pearson education,2001.
java 高效編程指南(中文版),機械工業出版社,2002

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 巴彦淖尔市| 额济纳旗| 莎车县| 玉田县| 抚远县| 遂宁市| 宣武区| 民县| 宁城县| 松滋市| 治县。| 长宁县| 临桂县| 洪洞县| 清丰县| 卫辉市| 梓潼县| 凤山县| 金华市| 武冈市| 桂平市| 连城县| 沾化县| 浠水县| 永登县| 齐河县| 龙陵县| 河北区| 华安县| 宜丰县| 来宾市| 阳朔县| 九寨沟县| 武城县| 永清县| 南华县| 静海县| 封丘县| 沈丘县| 来凤县| 漳浦县|