列舉是 C# 中最有意思的一部分,大部分開發人員只了解其中的一小部分,甚至網上絕大多數的教程也只講解了列舉的一部分,那么,我將通過這篇文章向大家具體講解一下列舉的知識,我將從大家都了解的部分開始講解,然后再講解大家所不知道的或者了解很少的部分,
零、基礎知識
列舉是由開發人員宣告的一種 值型別 ,它在編譯時就宣告了一種 具名常量值 ,使用列舉可以使我們的代碼簡單易讀,我們先來看一下兩個代碼段:
// 代碼段 1
void Method(int country)
{
switch (country)
{
case 0:
// more code
break;
case 1:
// more code
break;
case 2:
// more code
break;
case 3:
// more code
break;
default:
// more code
break;
}
}
// 代碼段 2
void Method(Country country)
{
switch (country)
{
case Country.CN:
// more code
break;
case Country.JP:
// more code
break;
case Country.UK:
// more code
break;
case Country.USA:
// more code
break;
default:
// more code
break;
}
}
從上面的兩個代碼段我們可以看到兩者有明顯的區別,第一段代碼中的 case 值我們幾乎完全不知道代表了什么是什么意思,但是第二段代碼我們使用了列舉,通過 case 值馬上就可以知道所要表達的意思,同樣利用列舉值替代布林值也可以改善代碼的可讀性,例如我們要開發控制臺燈打開關閉的程式,代碼可以這么寫 LightOperating(True) ,但是這種代碼我們無法看出具體要干什么,現在我們將代碼改動一下 LightOperating(Light.On) ,經過修改代碼就很容易看出所要表達的意思,
-
列舉定義與取值
定義列舉有兩種方式,分別是普通方式和自定義方式,不管使用哪種方式都需要用的關鍵字 enum 來標識這個型別為列舉型別,并且列舉值都是作為整數常量來實作的,下面我們就來看一下這兩種方式怎么定義列舉的,普通方式是我們經常用到的,也是默認的方式,這種方式很簡單,代碼如下:enum Country { CN, UK, JP, USA }在上面的代碼段中我們定義了一個國家列舉,第一個列舉值對應的整數常量是 0 ,第二個列舉值對應的整數常量是 1 ,以此類推后面的列舉值分別對應的整數常量是 2 和 3 ,但是在部分情況下我們需要自定義列舉值對應的整數常量,這個時候我們就需要用到自定義的方式,自定義方式又稱為為列舉值顯式賦值,它的方法如下所示:
enum Country { CN = 3, UK, JP = 70, USA = 67 }我們在代碼中將第一個列舉值對應的整數常量設定為了 3 ,這時第二個列舉值的整數常量就不是 1 了,而是 4 ,因為當列舉值沒有顯示賦值時,將會按照上一個列舉值對應的整數值加 1 來作為自己本身對應的整數值,最后兩個列舉值因為顯式賦值了因此對應的整數值就是所賦值的數值,
列舉取值也很簡單,只需要 列舉名.列舉值 即可,例如Country.UK,Tip:這里我提幾點建議:
- 列舉值的名稱不應包含列舉名稱;
- 列舉名稱應以單數的形式出現(除了屬性),
-
列舉的型別
到目前為止我們定義列舉型別使用的基礎型別 int 型別,但是列舉不僅僅可以使用 int 型別,還可以使用除了 char 型別之外的所有基礎型別,我們可以使用繼承語法來指定其他型別,enum Country:short { CN = 3, UK, JP = 70, USA = 67 }上面代碼中我們顯式定義了列舉所使用的基礎型別為 short ,這里雖然使用了繼承語法但是并沒有建立繼承關系,所有的列舉基類都是 System.Enum ,這些類都是密封類,無法從現有的列舉型別派生出新的成員,
對于列舉型別的變數,值不限于宣告中命名的值,因此值能轉換成基礎型別,那么就能轉換為列舉型別,之所以這么設計是因在以后的 API 中有很大的可能在不破換老版本的同時為列舉添加新的值,但是這其中也存在一個缺陷,列舉允許在運行時分配未知的值,對于這一點我們在開發時需要考慮到,并且在后期向列舉中添加新的列舉值時應將其添加到所有列舉值的后面,或者顯示指定列舉值對應的數值,這樣才能避免因添加新值導致列舉型別中的列舉值對應的數值改變,Tip:在開發中我們應該盡量使用 int 作為列舉的基礎型別,除非因性能問題或互操作方面的考慮時才會考慮使用較小的型別,
一、列舉轉換
列舉轉換主要涉及到了列舉與列舉的轉換、列舉與數字和字串的轉換,
- 列舉之間轉換
首先我要說明的是在 C# 中不支持不同列舉陣列之間的直接轉換,所以如果想要實作不同列舉陣列之間的轉換我們可以利用 CLR 寬松的賦值兼容性這一特點來進行轉換,需要轉換的兩個列舉必須具有相同的基礎型別,同樣,我們通過一個例子來看一下具體實作方法,
在使用這種方法時有可能會出現意外的錯誤或結果,并且相關開發規范中并沒有說這種方式每次都起作用,因此我不建議這么使用,除非在一些極端場景中,static void Main(string[] args) { CountryAllName[] can = (CountryAllName[])(Array)new Country[4]; } enum Country { CN, UK, JP, USA } enum CountryAllName { China, UnitedKingdom, Japan, UnitedStates } - 列舉和字串之間轉換
列舉轉換為字串可以直接使用 ToString() 方法, 列舉值 ToString 后會直接輸出列舉值識別符號的字串形式,例如Country.CN.ToString()得到的結果是字串 CN ,當然,你也可以利用 Enum.GetNames 和 Enum.GetName 方法來獲取,下面我簡單來講解一下這兩個方法的使用,- GetNames
GetNames 方法需要傳入一個列舉型別,回傳值是一個字串陣列,例如需要獲取到 Country 的第二個國家,那么就可以這么來寫Enum.GetNames(typeof(Country))[1],回傳結果是 UK , - GetName
GetName 方法回傳的是一個字串,這個字串就是需要獲取的指定列舉值的字串形式,同樣我們獲取第二個國家,Enum.GetName(typeof(Country),1),回傳的值同樣是 UK ,
字串轉換為列舉也很簡單,同樣用到了 Enum 基類的一個靜態方法 Parse ,例如我們將 JP 轉換為列舉 Country 的列舉值可以這么做(Country)Enum.Parse(typeof(Country),"JP"),這里有一點需要注意,TryParse 方法是在 .net 4.0 才出現的,因此如果要在 .net 4.0 以下版本中將字串轉換為列舉時,需要進行恰當的錯誤處理防止字串不存在與列舉型別中的列舉值中,
Tip:字串向列舉轉換不可本地化,如果必須本地化,就必須是那些對上層用戶不可見的訊息,因此在實際開發中應該盡量避免列舉和字串之間的轉換,
- GetNames
- 列舉和數字之間轉換
列舉轉換為數字我們可以使用強轉,例如(int)Country.CN回傳結果是 0 ,從數字轉換為列舉我們有兩種方法,一種是使用強轉,另一種是使用 Enum 的靜態方發 ToObject ,- 強轉
強轉就比較簡單了,Country country = (Country)2 - ToObject
ToObject 方法需要傳入列舉型別和需要轉換的數字,例如Country country = (Country)Enum.ToObject(typeof(Country),2)
- 強轉
- 注意
字串轉換為列舉和數字轉換為列舉都必須先進行判斷所要轉換的值是否包含在列舉中,判斷的方法也很簡單只需要呼叫 Enum 的靜態方法 IsDefined 即可,例如我要將 0 和 HK 轉換為列舉,代碼如下:
上述代碼中只有 0 會成功轉換為列舉值 CN ,因為 0 所對應的列舉值是 CN ,而 HK 并沒有在列舉中,Type type = typeof(Country); if(Enum.IsDefined(type,0)) { Enum.ToObject(type, 0); } if(Enum.IsDefined(type,"HK")) { Enum.Parse(typeof(Country), "HK"); }
三、標志與屬性
這一小節我們來講解一下標志與屬性,標志和屬性屬于在開發中用的比較少,并且大部分程式員了解的也不多,
- 標志
在開發中有時我們希望能對列舉進行組合使用來表示復合值,那么這時我們就需要定義標志列舉了,標志列舉的名稱為復數形式,代表了一個標志的集合,一般我們會使用按位或運算子鏈接列舉值,使用 HasFlags 方法或者按位與運算子來判斷特定的位是否存在,比較經典的標志列舉是位于 System.IO 命名空間中的 FileAttributes 標志列舉,它列出了檔案的所有屬性,比如只讀、隱藏、所在磁盤等等,它所包含的所有列舉值皆可相互組合,例如一個檔案既是隱藏檔案又是只讀檔案,定義標志列舉的方法如下:
在上面的代碼中你會發現一個規律,每個列舉值對應的整數值都是 2的n次方,這是為什么呢,在標志列舉中要求多個列舉值相互組合后的結果不能包含在標志列舉中,并且基于按位運算的特性可以很方便的使用位運算子來計算一個列舉值是否包含了另外一個列舉值,這在權限系統中相當有用,[Flags] enum WeekDays { Monday = 1, Tuesday = 2, Wednesday = 4, Thursday = 8, Friday = 16, Saturday = 32, Sunday = 64 } - 屬性
列舉值上同樣也可以使用屬性,例如我們需要列印輸出列舉值的中文名,我們就可以通過屬性的形式進行設定,首先我們需要定義一個屬性:
通過上面的代碼我們就能獲取到 CN 對應的中文名稱了,這段代碼并沒有進行進一步優化,在實際專案中必須進行封裝和優化,public class EnumChineseAttribute : Attribute { private string m_strDescription; public EnumChineseAttribute(string chineseName) { m_strDescription = chineseName; } public string Description { get { return m_strDescription; } } } enum Country { [EnumChinese("中國")] CN, [EnumChinese("英國")] UK, [EnumChinese("日本")] JP, [EnumChinese("美國")] USA } static void Main(string[] args) { Country country = Country.CN; FieldInfo fieldInfo = country.GetType().GetField("CN"); object[] attribArray = fieldInfo.GetCustomAttributes(false); EnumChineseAttribute attrib = (EnumChineseAttribute)attribArray[0]; Console.WriteLine(attrib.Description); Console.Read(); }
四、小結
這篇文章主要講解了列舉相關的知識,內容有點瑣碎,但是在實際開發中還是比較實用的,文章中我所提到的要點和規定在實際開發中已經經過驗證,各位讀者可以直接拿來使用,
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/63580.html
標籤:其他
上一篇:正則運算式過濾html注釋內容
