細說枚舉

来源:https://www.cnblogs.com/gangzhucoll/archive/2020/02/18/12325511.html
-Advertisement-
Play Games

枚舉是 C 中最有意思的一部分,大部分開發人員只瞭解其中的一小部分,甚至網上絕大多數的教程也只講解了枚舉的一部分。那麼,我將通過這篇文章向大傢具體講解一下枚舉的知識。我將從大家都瞭解的部分開始講解,然後再講解大家所不知道的或者瞭解很少的部分。 零、基礎知識 枚舉是由開發人員聲明的一種 值類型 ,它在 ...


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

  1. 枚舉定義與取值
    定義枚舉有兩種方式,分別是普通方式和自定義方式。不管使用哪種方式都需要用的關鍵字 enum 來標識這個類型為枚舉類型,並且枚舉值都是作為整數常量來實現的。下麵我們就來看一下這兩種方式怎麼定義枚舉的。普通方式是我們經常用到的,也是預設的方式。這種方式很簡單,代碼如下:
    csharp enum Country { CN, UK, JP, USA }
    在上面的代碼段中我們定義了一個國家枚舉,第一個枚舉值對應的整數常量是 0 ,第二個枚舉值對應的整數常量是 1 ,以此類推後面的枚舉值分別對應的整數常量是 23 。但是在部分情況下我們需要自定義枚舉值對應的整數常量,這個時候我們就需要用到自定義的方式。自定義方式又稱為為枚舉值顯式賦值,它的方法如下所示:
    csharp enum Country { CN = 3, UK, JP = 70, USA = 67 }
    我們在代碼中將第一個枚舉值對應的整數常量設置為了 3 ,這時第二個枚舉值的整數常量就不是 1 了,而是 4 ,因為當枚舉值沒有顯示賦值時,將會按照上一個枚舉值對應的整數值加 1 來作為自己本身對應的整數值。最後兩個枚舉值因為顯式賦值了因此對應的整數值就是所賦值的數值。
    枚舉取值也很簡單,只需要 枚舉名.枚舉值 即可,例如 Country.UK

    Tip:這裡我提幾點建議:

    • 枚舉值的名稱不應包含枚舉名稱;
    • 枚舉名稱應以單數的形式出現(除了屬性)。
  2. 枚舉的類型
    到目前為止我們定義枚舉類型使用的基礎類型 int 類型,但是枚舉不僅僅可以使用 int 類型,還可以使用除了 char 類型之外的所有基礎類型。我們可以使用繼承語法來指定其他類型。

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

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

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

    一、枚舉轉換

    枚舉轉換主要涉及到了枚舉與枚舉的轉換、枚舉與數字和字元串的轉換。
  3. 枚舉之間轉換
    首先我要說明的是在 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
     }
    在使用這種方法時有可能會出現意外的錯誤或結果,並且相關開發規範中並沒有說這種方式每次都起作用,因此我不建議這麼使用,除非在一些極端場景中。
  4. 枚舉和字元串之間轉換
    枚舉轉換為字元串可以直接使用 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:字元串向枚舉轉換不可本地化,如果必須本地化,就必須是那些對上層用戶不可見的消息。因此在實際開發中應該儘量避免枚舉和字元串之間的轉換。

  5. 枚舉和數字之間轉換
    枚舉轉換為數字我們可以使用強轉,例如 (int)Country.CN 返回結果是 0 。從數字轉換為枚舉我們有兩種方法,一種是使用強轉,另一種是使用 Enum 的靜態方發 ToObject
    • 強轉
      強轉就比較簡單了,Country country = (Country)2
    • ToObject
      ToObject 方法需要傳入枚舉類型和需要轉換的數字,例如 Country country = (Country)Enum.ToObject(typeof(Country),2)
  6. 註意
    字元串轉換為枚舉和數字轉換為枚舉都必須先進行判斷所要轉換的值是否包含在枚舉中,判斷的方法也很簡單隻需要調用 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 並沒有在枚舉中。

    三、標誌與屬性

    這一小節我們來講解一下標誌與屬性,標誌和屬性屬於在開發中用的比較少,並且大部分程式員瞭解的也不多。
  7. 標誌
    在開發中有時我們希望能對枚舉進行組合使用來表示覆合值,那麼這時我們就需要定義標誌枚舉了,標誌枚舉的名稱為複數形式,代表了一個標誌的集合。一般我們會使用按位或操作符鏈接枚舉值,使用 HasFlags 方法或者按位與操作符來判斷特定的位是否存在。比較經典的標誌枚舉是位於 System.IO 命名空間中的 FileAttributes 標誌枚舉,它列出了文件的所有屬性,比如只讀、隱藏、所在磁碟等等,它所包含的所有枚舉值皆可相互組合,例如一個文件既是隱藏文件又是只讀文件。定義標誌枚舉的方法如下:

    [Flags]
    enum WeekDays
     {
         Monday = 1,
         Tuesday = 2,
         Wednesday = 4,
         Thursday = 8,
         Friday = 16,
         Saturday = 32,
         Sunday = 64
     }
    在上面的代碼中你會發現一個規律,每個枚舉值對應的整數值都是 2的n次方,這是為什麼呢。在標誌枚舉中要求多個枚舉值相互組合後的結果不能包含在標誌枚舉中,並且基於按位運算的特性可以很方便的使用位運算符來計算一個枚舉值是否包含了另外一個枚舉值,這在許可權系統中相當有用。
  8. 屬性
    枚舉值上同樣也可以使用屬性,例如我們需要列印輸出枚舉值的中文名,我們就可以通過屬性的形式進行設置,首先我們需要定義一個屬性:

    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 對應的中文名稱了,這段代碼並沒有進行進一步優化,在實際項目中必須進行封裝和優化。

    四、小結

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

    本文由博客一文多發平臺 OpenWrite 發佈!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7772184.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講結構型設計模式的第五個模式--外觀模式。先從名字上來理解一下外觀模式,當看到“外觀”這個詞時,很容易想到“外表”這個詞語,兩者有著 ...
  • 《C# 6.0 本質論》 [作者] (美) Mark Michaelis (美) Eric Lippert[譯者] (中) 周靖 龐燕[出版] 人民郵電出版社[版次] 2017年02月 第5版[印次] 2017年02月 第1次 印刷[定價] 108.00元 【前言】 成功學習 C# 的關鍵在於,要盡 ...
  • 項目gitHub地址 點我跳轉 今天給大家帶來一個C#裡面的時間工具類,具體的直接看下麵代碼 1 using System; 2 3 namespace ToolBox.DateTimeTool 4 { 5 public static class DateTimeExtend 6 { 7 /// < ...
  • 一、前言 Entity Framework(後面簡稱EF)作為微軟家的ORM,自然而然從.NET Framework延續到了.NET Core。 二、程式包管理器控制台 為了能夠在控制臺中使用命令行來操作EF,需要先安裝Microsoft.EntityFrameworkCore.Tools。 安裝 ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7743118.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講結構型設計模式的第四個模式--組合模式。當我們談到這個模式的時候,有一個物件和這個模式很像,那就是“俄羅斯套娃”。“俄羅斯套娃”是 ...
  • Regex.Replace("<!--(.|[\r\n])*?-->",string.Empty) ...
  • 最近因為” 新冠” 疫情在家辦公,學習了 ASP.NET Core MVC 網站的一些知識,記錄如下。 ...
  • 一. elasticsearch on windows 1.下載地址: https://www.elastic.co/cn/downloads/elasticsearch 如果瀏覽器下載文件慢,建議使用迅雷下載,速度很快。下載版本為7.5.2 2. 修改配置文件 下載後解壓,找到config\jvm ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...