細說枚舉

枚舉是 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) 。通過修改代碼就很容易看出所要表達的意思。dom

  1. 枚舉定義與取值
    定義枚舉有兩種方式,分別是普通方式和自定義方式。無論使用哪一種方式都須要用的關鍵字 enum 來標識這個類型爲枚舉類型,而且枚舉值都是做爲整數常量來實現的。下面咱們就來看一下這兩種方式怎麼定義枚舉的。普通方式是咱們常常用到的,也是默認的方式。這種方式很簡單,代碼以下:性能

    enum Country
    {
        CN,
        UK,
        JP,
        USA
    }

    在上面的代碼段中咱們定義了一個國家枚舉,第一個枚舉值對應的整數常量是 0 ,第二個枚舉值對應的整數常量是 1 ,以此類推後面的枚舉值分別對應的整數常量是 23 。可是在部分狀況下咱們須要自定義枚舉值對應的整數常量,這個時候咱們就須要用到自定義的方式。自定義方式又稱爲爲枚舉值顯式賦值,它的方法以下所示:優化

    enum Country
    {
        CN = 3,
        UK,
        JP = 70,
        USA = 67
    }

    咱們在代碼中將第一個枚舉值對應的整數常量設置爲了 3 ,這時第二個枚舉值的整數常量就不是 1 了,而是 4 ,由於當枚舉值沒有顯示賦值時,將會按照上一個枚舉值對應的整數值加 1 來做爲本身自己對應的整數值。最後兩個枚舉值由於顯式賦值了所以對應的整數值就是所賦值的數值。
    枚舉取值也很簡單,只須要 枚舉名.枚舉值 便可,例如 Country.UK .net

    Tip:這裏我提幾點建議:
    • 枚舉值的名稱不該包含枚舉名稱;
    • 枚舉名稱應以單數的形式出現(除了屬性)。
  2. 枚舉的類型
    到目前爲止咱們定義枚舉類型使用的基礎類型 int 類型,可是枚舉不只僅可使用 int 類型,還可使用除了 char 類型以外的全部基礎類型。咱們可使用繼承語法來指定其餘類型。設計

    enum Country:short
     {
         CN = 3,
         UK,
         JP = 70,
         USA = 67
     }

    上面代碼中咱們顯式定義了枚舉所使用的基礎類型爲 short 。這裏雖然使用了繼承語法可是並無創建繼承關係,全部的枚舉基類都是 System.Enum ,這些類都是密封類,沒法從現有的枚舉類型派生出新的成員。
    對於枚舉類型的變量,值不限於聲明中命名的值,所以值能轉換成基礎類型,那麼就能轉換爲枚舉類型。之因此這麼設計是因在之後的 API 中有很大的可能在不破換老版本的同時爲枚舉添加新的值。可是這其中也存在一個缺陷,枚舉容許在運行時分配未知的值,對於這一點咱們在開發時須要考慮到。而且在後期向枚舉中添加新的枚舉值時應將其添加到全部枚舉值的後面,或者顯示指定枚舉值對應的數值,這樣才能避免因添加新值致使枚舉類型中的枚舉值對應的數值改變。code

    Tip:在開發中咱們應該儘可能使用 int 做爲枚舉的基礎類型,除非因性能問題或互操做方面的考慮時纔會考慮使用較小的類型。

1、枚舉轉換

枚舉轉換主要涉及到了枚舉與枚舉的轉換、枚舉與數字和字符串的轉換。繼承

  1. 枚舉之間轉換
    首先我要說明的是在 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
     }

    在使用這種方法時有可能會出現意外的錯誤或結果,而且相關開發規範中並無說這種方式每次都起做用,所以我不建議這麼使用,除非在一些極端場景中。

  2. 枚舉和字符串之間轉換
    枚舉轉換爲字符串能夠直接使用 ToString() 方法, 枚舉值 ToString 後會直接輸出枚舉值標識符的字符串形式,例如 Country.CN.ToString() 獲得的結果是字符串 CN 。固然,你也能夠利用 Enum.GetNamesEnum.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:字符串向枚舉轉換不可本地化,若是必須本地化,就必須是那些對上層用戶不可見的消息。所以在實際開發中應該儘可能避免枚舉和字符串之間的轉換。
  1. 枚舉和數字之間轉換
    枚舉轉換爲數字咱們可使用強轉,例如 (int)Country.CN 返回結果是 0 。從數字轉換爲枚舉咱們有兩種方法,一種是使用強轉,另外一種是使用 Enum 的靜態方發 ToObject

    • 強轉
      強轉就比較簡單了,Country country = (Country)2
    • ToObject
      ToObject 方法須要傳入枚舉類型和須要轉換的數字,例如 Country country = (Country)Enum.ToObject(typeof(Country),2)
  2. 注意
    字符串轉換爲枚舉和數字轉換爲枚舉都必須先進行判斷所要轉換的值是否包含在枚舉中,判斷的方法也很簡單隻須要調用 Enum 的靜態方法 IsDefined 便可,例如我要將 0 和 HK 轉換爲枚舉,代碼以下:

    Type type = typeof(Country);
     if(Enum.IsDefined(type,0))
     {
         Enum.ToObject(type, 0);
     }
     if(Enum.IsDefined(type,"HK"))
     {
         Enum.Parse(typeof(Country), "HK");
     }

    上述代碼中只有 0 會成功轉換爲枚舉值 CN ,由於 0 所對應的枚舉值是 CN ,而 HK 並無在枚舉中。

3、標誌與屬性

這一小節咱們來說解一下標誌與屬性,標誌和屬性屬於在開發中用的比較少,而且大部分程序員瞭解的也很少。

  1. 標誌
    在開發中有時咱們但願能對枚舉進行組合使用來表示複合值,那麼這時咱們就須要定義標誌枚舉了,標誌枚舉的名稱爲複數形式,表明了一個標誌的集合。通常咱們會使用按位或操做符連接枚舉值,使用 HasFlags 方法或者按位與操做符來判斷特定的位是否存在。比較經典的標誌枚舉是位於 System.IO 命名空間中的 FileAttributes 標誌枚舉,它列出了文件的全部屬性,好比只讀、隱藏、所在磁盤等等,它所包含的全部枚舉值皆可相互組合,例如一個文件既是隱藏文件又是隻讀文件。定義標誌枚舉的方法以下:

    [Flags]
    enum WeekDays
     {
         Monday = 1,
         Tuesday = 2,
         Wednesday = 4,
         Thursday = 8,
         Friday = 16,
         Saturday = 32,
         Sunday = 64
     }

    在上面的代碼中你會發現一個規律,每一個枚舉值對應的整數值都是 2的n次方,這是爲何呢。在標誌枚舉中要求多個枚舉值相互組合後的結果不能包含在標誌枚舉中,而且基於按位運算的特性能夠很方便的使用位運算符來計算一個枚舉值是否包含了另一個枚舉值,這在權限系統中至關有用。

  2. 屬性
    枚舉值上一樣也可使用屬性,例如咱們須要打印輸出枚舉值的中文名,咱們就能夠經過屬性的形式進行設置,首先咱們須要定義一個屬性:

    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();
     }

    經過上面的代碼咱們就能獲取到 CN 對應的中文名稱了,這段代碼並無進行進一步優化,在實際項目中必須進行封裝和優化。

4、小結

這篇文章主要講解了枚舉相關的知識,內容有點瑣碎,可是在實際開發中仍是比較實用的。文章中我所提到的要點和規定在實際開發中已經通過驗證,各位讀者能夠直接拿來使用。

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索