C#筆記 – 枚舉和位標志

枚舉類型
  • 「符號名稱/值」配對

    • public enum TimePeriod
      {
         Morning = 0,
         Afternoon = 1,
         Night = 2
      }
  • 類型安全

  • 每個枚舉類型派生自System.Enum,System.Enum派生自System.ValueType,因此枚舉屬於值類型

  • 枚舉類型內部不能定義任何方法/屬性/事件

    • 編譯枚舉類型時,C#編譯器將每個符號轉換成類型的一個常量字段,以上面的TimePeriod枚舉為例,其內部工作方式是這樣的

      • public struct TimePeriod : System.Enum
        {
           //TimePeriod的符號和值定義
           public const TimePeriod Morning = (TimerPeriod) 0;
           public const TimePeriod Afternoon = (TimerPeriod) 1;
           public const TimePeriod Night = (TimerPeriod) 2;
           
           public int value__; //包含TimePeriod變量的值,不能通過代碼直接引用
        }
      • 簡單而言,枚舉類型只是一個結構,其中定義了一組常量字段和一個實例字段

        • 常量字段會嵌入程序集的元數據中,可通過反射來訪問

        • 因此,支持在運行時獲得與枚舉類型關聯的所有符號及其值/將字符串符號轉換成對應的數值

          • 這些操作由System.Enum的靜態方法提供

            • GetUnderlyingType:返回用於容納一個枚舉類型的值的基礎類型

              • 基礎類型默認為int,也可以聲明為其他的基元值類型

                • public enum EnumBasicType : ushort
                  {
                     Basic,
                     Normal,
                     Hard
                  }
            • GetValues:返回枚舉類型中符號的數組

            • Parse/TryParse:將符號轉換為枚舉類型的實例

            • IsDefined:判斷數值對於枚舉類型是否合法

              • 查找區分大小寫

              • 內部使用了反射,速度很慢

              • 枚舉類型在調用IsDefined的同一程序集中定義才可使用

    • 但可以使用「擴展方法」為枚舉類型提供方法

      • class Program
        {
           static void Main(string[] args)
          {        
               TimePeriod tp = TimePeriod.Morning;
               Console.WriteLine(tp.IsMorning()); //True
          }
        }
        public static class TimePeriodExtension
        {
           public static bool IsMorning(this TimePeriod t)
          {
               return t == TimePeriod.Morning;
          }
        }
  • C#編譯器把枚舉類型視為基元類型,所以可用許多操作符(==、!=、>、<等等)來操縱枚舉類型的實例

    • 所有這些操作符實際作用於每個枚舉類型實例內部的value__實例字段

  • 枚舉類型通常與需要它的類同級

位標志
  • 通常用枚舉類型來表示一組可以組合的位標志,如FileAttributes

    • namespace System.IO
      {
        [Flags]
         public enum FileAttributes
        {
             ReadOnly = 1,
             Hidden = 2,        
             System = 4,
             Directory = 16,
             Archive = 32,
             Device = 64,
             Normal = 128,
             Temporary = 256,
             SparseFile = 512,
             ReparsePoint = 1024,
             Compressed = 2048,
             Offline = 4096,
             NotContentIndexed = 8192,
             Encrypted = 16384,
             IntegrityStream = 32768,        
             NoScrubData = 131072
        }
      }
  • 定義用於標識位標志的枚舉類型時,應顯式為每個符號分配一個數值

    • 每個符號都有單獨的一個位處於on狀態

  • 應定義一個值為0的None符號

  • 最好應用特性System.Flags/System.FlagsAttribute

    • 檢測到Flags特性後,操作就不會把數值視為單獨的值,而是一組位標志,並根據操作對數值和枚舉定義的「符號/值」數進行「按位與」/「按位或」運算

      • ToString:按位與

      • Parse/TryParse:按位或

參考書目

  • 《CLR via C#》(第4版) Jeffrey Richter