在以前寫程式時,我們常會定義一些常數的使用,然後集中到一個Class上面。
class Window
{
int Close = 0;
int Open = 1;
}
但其實這樣的效能和記憶體在使用上會有點肥(如果大量使用的話),後來程式的演進後,發現這樣的需求變多了,因為同樣的型別我們可以直接集中控管,然後定義一次就好,後來就演進了Enum這樣的一個型別存在。例如,你可能需要一個稱為“等級”的相同屬性選項,且僅可被賦值為“A”,“B”,“C”,“F”等值的類型,任何其他值在此類型中都是非法的。
Enum是Java第一個使用的一個型別,後來C#也開始跟進。
Enum 通稱為「列舉型別」,對於許多初學者來說, 他們知道列舉型別, 也會用, 卻不一定知道列舉型別用在什麼地方、有什麼好用之處。所以在這篇文章裡, 我除了向各位介紹 Enum 如何使用之外, 也會告訴你它的好用之處。
enum 可以用來定義常數。
Enum 的宣告
在 .Net 中 Enum 的使用是很容易、很直覺(其實跟你在用class直接定義很像):
enum windowStatus : int
{
Close,
Open
};
其實 Enum 是一種蠻特殊的型別; 當你宣告一個 Enum 型別的時候, 它實際上和建立一群列舉的數字常數沒什麼兩樣。我們也可以直接去指定 Enum 數列中個別的數值:
enum windowStatus : int
{
Close=0,
Open=1
};
上面是很基本的使用方式,但是以.Net目前的演進上,還是跟Java有些許的不同,例如:
public enum Window {
close(0, "Close"), open (1, "Open") ;
int value = 0;
String text = "";
private Window(int value, String text) {
this.value = value;
this.text = text;
}
public int getValue() {
return this.value;
}
public String getText() {
return this.text;
}
public static Window ofValue(int value) {
for (LawMasterRole r : values()) {
if (r.getValue() == value) {
return r;
}
}
return undefined;
}
}
上面的例子我們可以看到Java可以在 enum 內再加上方法。而且Java Enum下有values()方法,這方法其實是取Enum裡的所有值,很奇妙。我去查了JAVA DOC可知道java.lang.Enum<ElementType>下根本沒有這個方法,該方法在java.lang.annotation.ElementType類下(public
enum ElementType extends Enum<ElementType>)。Enum的聲明是:Enum<? extends Enum<E>>,即後面這個泛型裡必須是Enum子類別。所以合理的推理是這樣的:在解譯它時會看到一個Test,Test是Enum的子類別,ElementType也是Enum的子類別。為什麼ElementType會有values()和valueOf()方法,是因為ElementType本身也是Enum的子類別,編譯時自動添加了這些方法。任何類別也不像是ElementType的子類別,因為任何enum編譯後都是final修飾的,除非它的某個enum有class body,而ElementType沒有,final修飾的類別不可繼承,這感覺是編譯器自動產生的。
這些是目前C#的Enum沒有的,一個假設是,我們設計了一個Enum。
enum windowStatus : int
{
Close = 0,
Open
};
這時我們在使用時,一般來說沒有太大問題,就是直取出Enum裡面的參數,而它是int沒有錯。但是人的需求是無止盡的,這時候我們會想說,那能不能也能實現一邊是int的對應,可以取出它的名稱呢?
當然,其實我們可以自已做一個Enum來實現一些方法,例如,自行定義Enum裡條目的字串名稱,並取出它的值:
public sealed class Window
{
private string name;
private int value;
public static readonly Window Close = new Window("Close",0);
public static readonly Window Open = new Window("Open", 1);
private Window(string name, int value)
{
this.name = name;
this.value = value;
}
public override string ToString()
{
return this.name;
}
}
上面也是可以呈現把Enum轉換成String,但是如果你有實際這樣跑過,會發現有一個問題。
那就是結果永遠只會呈現String,而不會呈現class本身的value。
Console.WriteLine("Window
Enum:" + Window.Close);
Console.WriteLine("Window Enum
ToString:" + Window.Close.ToString());
Console.WriteLine("Window
Enum:" + Window.Open);
Console.WriteLine("Window Enum
ToString:" + Window.Open.ToString());
其它關鍵的地方就是override
string ToString()。這地方會將一個物件產生之後,定義此類別的型態。所以我不管丟了幾個參數,結果就是只能呈現一種型別,這樣用不用ToString都沒有作用,但是山不轉入轉,我們可以這樣寫就好了:
public sealed class Window
{
private string name;
private int value;
public static readonly Window Close = new Window("Close",0);
public static readonly Window Open = new Window("Open", 1);
private Window(string name, int value)
{
this.name = name;
this.value = value;
}
public override string ToString()
{
return this.value.ToString();
}
public string GetEnumName()
{
return this.name;
}
}
有看到了嗎? 我只要稍微做一個手腳,就是把ToString的地方改用value的值,並且新增一個Funtion實作就好了,俗話說,凡事不用太執著,我們不要去改那完全沒辦法搬動的ToString,只要新增一個Function,就可以同時實現要讓Enum兼具怎樣的條件,就有什樣的條件。
Console.WriteLine("Window
Enum:" + Window.Close);
Console.WriteLine("Window Enum
ToString:" + Window.Close.ToString());
Console.WriteLine("Window Enum
GetEnumName:" + Window.Close.GetEnumName());
Console.WriteLine("Window
Enum:" + Window.Open);
Console.WriteLine("Window Enum
ToString:" + Window.Open.ToString());
Console.WriteLine("Window Enum
GetEnumName:" + Window.Open.GetEnumName());
然後我們只要在value這個帶入的變數動一下手腳,就可以讓Window跟Enum一樣,設定它的型態。
public sealed class Window
{
private string name;
private object value;
public static readonly Window Close = new Window("Close",0);
public static readonly Window Open = new Window("Open", 1);
private Window(string name, object value)
{
this.name = name;
this.value = value;
}
public override string ToString()
{
return this.value.ToString();
}
public string GetEnumName()
{
return this.name;
}
}