1. 程式人生 > >列舉型別和位標誌

列舉型別和位標誌

一、列舉型別
  列舉型別(enumerated types)定義了一組"符號名稱/值"配對。
  例如,以下Color型別定義了一組符號,每個符號都標識一種顏色:

複製程式碼
internal enum Color {
While,        //賦值0
Red,          //賦值1
Green,        //賦值2
Blue,         //賦值3
Orange        //賦值4
}
複製程式碼

  使用列舉型別的好處:  

  1)列舉型別使程式更容易編寫、閱讀和維護。有了列舉型別,符號名稱可在程式碼中隨便使用,開發人員不需要記 住每個硬編碼的含義。而且,一旦與符號名稱對應的值發生變化,程式碼也可以簡單的重新編譯,不需要對原始碼做出任何修改。除此之外,文件工具和其他實用程式能向開發人員顯示有意義的符號名稱。

  2)列舉型別是強型別的。

  在Microsoft .NET Framework中,列舉型別不只是編譯器所關心的符號,它在型別系統中還具有"一等公民"的地 位,能實現非常強大的操作。列舉型別都直接從System.Enum派生,後者從System.ValueType派生,而System.ValueType用從 System.Object派生。所以,列舉型別是值型別,可表示成未裝箱和已裝箱形式。然而,有別於其他值型別,枚 舉型別不能定義任何方法、屬性和事件。不過可利用C#的"擴充套件方法"功能模擬向列舉型別新增方法。

  編譯列舉型別時,C#編譯器會把每個符號轉換成為型別的一個常量欄位。例如,編譯器會把前面的Color列舉型別看成以下程式碼:

複製程式碼
internal struct Color : System.Enum
{
  //以下是一些公共常量,它們定義了Color的符號和值
  public const Color While = (Color)0;
  public const Color Red = (Color)1;
  public const Color Green = (Color)2;
  public const Color Bule = (Color)3;
  public const Color Orange = (Color)4;

  //以下是一個公共例項欄位,它包含一個Color變數的值,
  //不能寫程式碼來直接引用這個例項欄位
  public Int32 value__; }
複製程式碼

  C#編譯器實際上並不編譯這段程式碼,因為它禁止定義從System.Enum這一特殊型別派生的型別。不過,可以通過 上述偽型別定義瞭解內部工作方式。簡單的說,列舉型別只是一個結構,其中定義了一組常量欄位和一個例項字 段。常量欄位會嵌入程式集的元資料中,並可以通過反射來訪問。這意味著可以在執行時獲得與列舉型別關聯的 所有符號及其值。還意味著可以將一個字串符號轉換成對應的數值。這些操作是通過System.Enum基型別來提 供的,該型別提供了幾個靜態和例項方法,可利用它們操作列舉型別的一個例項,從而避免了必須使用反射的麻 

煩。  

  提示:列舉型別定義的符號是常量值。所以當編譯器發現程式碼引用了一個列舉型別的符號,就會在編譯時用數值 替換符號,程式碼將不再引用定義了符號的列舉型別。這意味著在執行時可能不需要定義了列舉型別的程式集,在 

編譯時需要。

  例如,System.Enum型別有一個GetUnderlyingType的靜態方法,而System.Type型別有一個 GetEnumUnderlyingType的例項方法。

public static Type GetUnderlyingType (Type enumType); //System.Enum中定義
public Type GetEnumUnderlyingType (Type enumType); //System.Type中定義

  這些方法返回用於容納一個列舉型別的值的基礎型別。每個列舉型別都有一個基礎型別,它可以是byte,sbyte, short,ushort,int(最常用,也是C#預設的),uint,long,或ulong。雖然這些C#基元型別都有都有物件的FCL 型別,但C#編譯器為了簡化本身的實現,要求只能指定基元型別名稱。如果使用FCL型別名稱(如Int32),就會報 

錯。
  以下程式碼演示瞭如何宣告一個基礎型別為byte(System.Byte)的列舉型別:

複製程式碼
inter enum Color :byte {
    While, 
    Red, 
    Green, 
    Blue, 
    Orange 
}
複製程式碼

  基於這個Color列舉型別,一下程式碼顯示了GetUnderlyingType 的返回結果:

//以下程式碼會顯示"System.Byte"
Console.WriteLine(Enum.GetUnderlyingType(typeof(Color)));

  C#編譯器將列舉型別視為基元型別。所以,可以運用許多操作符(==,!=,<,>等等)來操作列舉型別的例項。 所有這些操作符實際作用於列舉型別例項內部的value__例項欄位。此外,C#編譯器還執行將列舉型別的例項顯式 的轉型為一個不通過的列舉型別。也可以顯式將一個列舉型別例項轉型為一個數值型別。

  可以呼叫System.Enum的靜態方法GetValue或者System.Type的例項方法GetEnumValue獲取一個數組,該陣列的每一個元素都對應列舉型別中的一個符號名稱,每個元素都包含符號名稱的數值:

public static Array GetValues(Type enumType); //System.Enum中定義
public Array GetEnumValues(Type enumType); //System.Type中定義

  這個方法結合ToString方法使用,可顯示列舉型別中所有符號名稱及其對應的數值,如下所示:

複製程式碼
public static void Go() {
    Color[] colors = (Color[])Enum.GetValues(typeof(Color));
    Console.WriteLine("Number of symbols defined: " + colors.Length);
    Console.WriteLine("Value\tSymbol\n-----\t------");
    foreach (Color color in colors) {
  // 以十進位制和常規格式顯示每個符號
    Console.WriteLine("{0,5:D}\t{0:G}", color);
    }
}
複製程式碼

  以上程式碼產生的輸出如下:

Number of symbols defined: 5
Value Symbol
----- ------
0 While
1 Red
2 Green
3 Blue
4 Orange

  還有其他列舉型別成員,就不一一敘述了!

二、位標誌
  程式設計師經常要與位標識(bit flag)集合打交道。呼叫System.IO.File型別的GetAttributes方法,會返回FileAttributes型別的一個例項。FileAttributes型別是基本型別為Int32的列舉型別,其中每一位都反映了檔案的一項屬性。FileAttibutes型別在FCL中的定義如下:

複製程式碼
[Flags]
[Serializable]
[ComVisible (true)]
public enum FileAttributes
{
    Archive = 0x00020,
    Compressed = 0x00800, 
    Device = 0x00040, 
    // Reserved for future use (NOT the w32 value). 
    Directory = 0x00010,
    Encrypted = 0x04000, // NOT the w32 value
    Hidden = 0x00002,
    Normal = 0x00080,
    NotContentIndexed = 0x02000,
    Offline = 0x01000,
    ReadOnly = 0x00001,
    ReparsePoint = 0x00400,
    SparseFile = 0x00200,
    System = 0x00004,
    Temporary = 0x00100,
    #if NET_4_5
    IntegrityStream = 0x8000,
    NoScrubData = 0x20000,
    #endif
}
複製程式碼

  為了判斷一個檔案是否隱藏,可執行下面這樣的程式碼:

String file = Assembly.GetEntryAssembly().Location;
FileAttributes attributes = File.GetAttributes(file);
Console.WriteLine("Is {0} hidden? {1}",file,(attributes & FileAttributes.Hidden) !=0);

  以下程式碼演示瞭如何將一個檔案的屬性改為只讀和隱藏:

File.SetAttributes(file,FileAttributes.ReadOnly | FileAttribute.Hidden);

  正如FileAttributes型別展示的那樣,經常都要用列舉型別來表示一組可以組合的位標誌。不過,雖然列舉型別和位標誌相似,但它們的語義不盡相同。例如,列舉型別表示單個數值,而位標識表示一組位,其中有些位是1,有 些位是0.

  定義用於標識位標誌的列舉型別時,當然應該顯式為每個符號分配一個數值。通常,每個符號都有單獨的一個位處於on(1)狀態.此外,經常都要定義一個值為0的None符號。還可以定義一些代表常用位組合的符號。另外,強烈建議向列舉型別應用System.Flags.Attribute這個定製的attribute型別,如下所示

複製程式碼
[Flags] 
public enum Actions {
    Read = 0x0001,
    Write = 0x0002,
    ReadWrite = Actions.Read | Actions.Write,
    Delete = 0x0004,
    Query = 0x0008,
    Sync = 0x0010
}
複製程式碼

  因為Actions是列舉型別,所以在操作位標誌列舉型別時,可以使用上一節描述的所有方法。

Actions actions = Actions.Read | Actions.Delete; //0x0005
Console.WriteLine(actions.ToString()); //"Read,Delete"

  呼叫ToString時,它會檢視將數值轉換為對應的符號。現在的數值是0x0005,它沒有對應的符號。不過,ToString方法檢測到Actions型別上存在[Flags]這個attribute,所以ToString方法現在不會將該數值視為單獨的值。相反,會將它視為一組位標誌。由於0x0005有0x0001和0x0004組合而成,所以ToString會生成字串"Read,Delete", 如果從Actions型別中刪除[Flags]這個attribute,ToString方法返回"5"。

  永遠不要對位標誌列舉型別使用IsDefined方法,理由如下:
  1)如果向IsDefined方法傳遞一個字串,它不會將這個字串拆分為單獨的token來進行查詢,而是檢視查詢整個字串,把它看成是包含逗號的一個更大的符號。由於不能在列舉型別中定義含有逗號的符號,所以這個符號永遠找不到。
  2)如果向IsDefined方法傳遞一個數值,它會檢查列舉型別是否定義了一個其對應數值和傳入數值匹配的符號。由於位標誌不能這樣簡單匹配,所以IsDefined通常會返回flase。


三、向列舉型別新增方法
  現在,可以使用C#的擴充套件方法功能向列舉型別模擬新增方法。
  如果想為FileAttributes列舉型別新增一些方法,可以定義一個包含了擴充套件方法的靜態類,如下所示:

public static Boolean Set(this FileAttributes flags, FileAttributes testFlags) {
return flags | testFlags;
}
從表面看,我似乎真的在列舉型別上呼叫這些方法:
FileAttributes fa = FileAttributes.System;
fa = fa.Set(FileAttributes.ReadOnly);

轉載地址:

        http://www.cnblogs.com/zxj159/p/3566616.html