細說枚舉

来源: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
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...