我們重點來講解 簡單枚舉和標志枚舉的用法和區別
繼承
Object-> ValueType ->Enum
Object-> ValueType ->struct 包括int float等簡單值類型 Object-> ValueType ->ValueTuple Object-> ValueType ->Nullable
枚舉的簡介:
1、枚舉類型 是由基礎整型數值類型的一組命名常量定義的值類型。
2.枚舉使用enum關鍵字來聲明, 枚舉可以和類並列也可以 寫在類里面,不能寫在方法里。
namespace ConsoleApp1 { class Program { //////////////省略。。。。。。。。。 } [Flags] enum Man { //////////////省略。。。。。。。。。 } }
3、枚舉都是隱式密封的(sealed),不允許作為基類派生子類。枚舉里面的成員只能是默認公共的靜態的(public、static),不能有訪問修飾符;枚舉本身一般不加訪問修飾符,要加的話只能是internal
或public;
public readonly int SOU = 12; public const int seiu = 12; enum n { i=0} 編譯后的iL代碼 .field public initonly int32 SOU .field public static literal int32 seiu = int32(12) .field public static literal valuetype Galaxy.Program/n i = int32(0) //枚舉中字段IL代碼和Const 常量定義的IL代碼是一樣的,編譯時常數,在編譯時是已知的,在程序的生命周期內不會改變
其他整數數值類型作為枚舉類型的基礎類型。 還可以顯式指定關聯的常數值,如下面的示例所示:
enum ErrorCode : int //不是繼承,只是寫法像繼承,查看IL代碼 被編譯成.field public specialname rtspecialname int32 value__,如果是繼承應該顯示extends { None = 0, Unknown = 1, ConnectionLost = 100, OutlierReading = 200 }
System.Enum 類型和枚舉約束
System.Enum 類型是所有枚舉類型的抽象基類。 它提供多種方法來獲取有關枚舉類型及其值的信息。 有關更多信息和示例,請參閱 System.Enum API 參考頁。
從 C# 7.3 開始,你可以在基類約束中使用 System.Enum (稱為枚舉約束),以指定類型參數為枚舉類型。 所有枚舉
類型也都滿足 struct 約束,此約束用於指定類型參數為不可為 null 的值類型。
轉換
對於任何枚舉類型,枚舉類型與其基礎整型類型之間存在顯式轉換。 如果將枚舉值轉換為其基礎類型,則結果為枚舉成員的關聯整數值。
public enum Season { Spring, Summer, Autumn, Winter } public class EnumConversionExample { public static void Main() { Season a = Season.Autumn; Console.WriteLine($"Integral value of {a} is {(int)a}"); // output: Integral value of Autumn is 2 var b = (Season)1; Console.WriteLine(b); // output: Summer } }
使用 Enum.IsDefined 方法來確定枚舉類型是否包含具有特定關聯值的枚舉成員。
對於任何枚舉類型,都存在分別與 System.Enum 類型的裝箱和取消裝箱相互轉換。
1、簡單枚舉:枚舉變落表示一個成員;

枚舉類型的作用是限制其變量只能從有限的選項中取值,這些選項(枚舉類型的成員)各自對應於一個數字,數字默認從 0 開始,並以此遞增。例如:
public enum Days
{
Sunday, Monday, Tuesday, // ...
}
其中 Sunday 的值是 0,Monday 是 1,以此類推。為了一眼能看出每個成員代表的值,一般推薦顯示地將成員值寫出來,不要省略:
public enum Days
{
Sunday = 0, Monday = 1, Tuesday = 2, // ...
}
C# 枚舉成員的類型默認是 int 類型,通過繼承可以聲明枚舉成員為其它類型,比如:
publicenum Days : byte
{
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7
}
枚舉類型一定是繼承自 byte、sbyte、short、ushort、int、uint、long 和 ulong 中的一種,不能是其它類型。下面是幾個枚舉的常見用法(以上面的 Days 枚舉為例):
// 枚舉轉字符串
string foo = Days.Saturday.ToString(); // "Saturday"
string foo = Enum.GetName(typeof(Days), 6); // "Saturday"
// 字符串轉枚舉
Enum.TryParse("Tuesday", out Days bar); // true, bar = Days.Tuesday
(Days)Enum.Parse(typeof(Days), "Tuesday"); // Days.Tuesday
// 枚舉轉數字
byte foo = (byte)Days.Monday; // 1
// 數字轉枚舉
Days foo = (Days)2; // Days.Tuesday
// 獲取枚舉所屬的數字類型
Type foo = Enum.GetUnderlyingType(typeof(Days))); // System.Byte
// 獲取所有的枚舉成員
Array foo = Enum.GetValues(typeof(MyEnum);
// 獲取所有枚舉成員的字段名
string[] foo = Enum.GetNames(typeof(Days));
另外,值得注意的是,枚舉可能會得到非預期的值(值沒有對應的成員)。比如:
Days d = (Days)21; // 不會報錯
Enum.IsDefined(typeof(Days), d); // false
即使枚舉沒有值為 0 的成員,它的默認值永遠都是 0。
var z = default(Days); // 0
枚舉可以通過 Description、Display 等特性來為成員添加有用的輔助信息,比如:
public enum ApiStatus { [Description("成功")] OK = 0, [Description("資源未找到")] NotFound = 2, [Description("拒絕訪問")] AccessDenied = 3 } static class EnumExtensions { public static string GetDescription(this Enum val) { var field = val.GetType().GetField(val.ToString()); var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); if (customAttribute == null) { return val.ToString(); } else { return ((DescriptionAttribute)customAttribute).Description; } } } static void Main(string[] args) { Console.WriteLine(ApiStatus.Ok.GetDescription()); // "成功" }
上面這些我認為已經包含了大部分我們日常用到的枚舉知識了。下面我們繼續回到文章開頭說的用戶角色存儲問題。
2、標記枚舉:枚舉變量可以表示一個集合,每當可枚舉表示可能值的集合而不是單個值時,應使用[Flags]屬性

2.1 標志枚舉的使用准則
避免創建標志枚舉,其中某些值的組合無效。
避免使用值為零的標記枚舉值,除非該值表示 "所有標志均已清除" 並正確命名,如下一個准則所述。
✔️ DO name 標記枚舉的零值 None 。 對於標志枚舉,值始終為 "清除所有標志"。
2.2三種寫法都是等效的;
第一種寫法:位數大了以后不容易察覺錯誤;
第二中寫法:推薦,容易理解 標志枚舉是如何運作的。
第三種寫法:容易閱讀,但是有個bug,當移位超出整形限制范圍時候,編譯器不會報錯,計算結果失效。曹操不是兄弟,但是在int整型范圍之下 1<<62 和1<<30 值是一樣的,導致計算結果出錯。
static void Main(string[] args) { Man xiongdi = Man.關羽 | Man.劉備 | Man.張飛; if((xiongdi.HasFlag(Man.曹操))) Console.WriteLine($"{Man.司馬懿}是兄弟");//司馬懿是兄弟 if ((xiongdi.HasFlag(Man.關羽))) Console.WriteLine($"{Man.關羽}是兄弟");//關羽是兄弟 } [Flags] enum Man { 曹操=1<<30,//IL反編譯顯示:曹操 = 0x40000000, 司馬懿 =1<<31,//IL反編譯顯示:司馬懿 = int.MinValue, 劉備 =1<<32,//IL反編譯顯示:劉備 = 0x1, 關羽 =1<<33,//IL反編譯顯示:關羽 = 0x2, 張飛 =1<<62,//IL反編譯顯示:張飛 = 0x40000000 }
每當可枚舉表示可能值的集合而不是單個值時,應使用[Flags]屬性。此類集合通常與按位運算符一起使用,例如:
var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;
請注意,[Flags]屬性本身並不啟用此功能,它所做的只是允許.ToString()方法的良好表示:
enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 } [Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 } ... var str1 = (Suits.Spades | Suits.Diamonds).ToString(); //"5" var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString(); //"Spades, Diamonds"
還需要注意的是,[Flags]不會自動使枚舉值的冪為2。如果省略數字值,枚舉將不會像在按位操作中預期的那樣工作,因為默認情況下,值以0和增量開頭。
Console.WriteLine(default(Man)); //枚舉的默認值都是0
聲明不正確:
[Flags] public enum MyColors { Yellow, // 0 Green, // 1 Red, // 2 Blue // 3 }
如果以這種方式聲明,值將為黃色=0、綠色=1、紅色=2、藍色=3。這將使其作為標志無效。
以下是正確聲明的示例:
[Flags] public enum MyColors { Yellow = 1, Green = 2, Red = 4, Blue = 8 }
2.3位枚舉使用案例
//1、給用戶創建、讀取,修改和刪除的權限 var parmission = Permission.Create | parmission.Read | parmission.Update | parmission.Delete; //2、去掉用戶的修改和刪除權限 parmission = parmission &~parmission.Update; parmission = parmission &~parmission.Delete; //3、給用戶加上修改的權限 parmission = parmission | parmission.Update; //4、判斷用戶是否有創建的權限 var isCreate =(myProperties.AllowedColors.HasFlag(MyColor.Yellow);
或在.NET 4之前:
if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow) { // Yellow is allowed... } if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green) { // Green is allowed... }
3、簡單枚舉和標記枚舉的區別?
using System; class Example { // Define an Enum without FlagsAttribute. enum SingleHue : short { None = 0, Black = 1, Red = 2, Green = 4, Blue = 8 }; // Define an Enum with FlagsAttribute. [Flags] enum MultiHue : short { None = 0, Black = 1, Red = 2, Green = 4, Blue = 8 }; static void Main( ) { // Display all possible combinations of values. Console.WriteLine( "All possible combinations of values without FlagsAttribute:"); for(int val = 0; val <= 16; val++ ) Console.WriteLine( "{0,3} - {1:G}", val, (SingleHue)val); // Display all combinations of values, and invalid values. Console.WriteLine( "\nAll possible combinations of values with FlagsAttribute:"); for( int val = 0; val <= 16; val++ ) Console.WriteLine( "{0,3} - {1:G}", val, (MultiHue)val); } } // The example displays the following output: // All possible combinations of values without FlagsAttribute: // 0 - None // 1 - Black // 2 - Red // 3 - 3 // 4 - Green // 5 - 5 // 6 - 6 // 7 - 7 // 8 - Blue // 9 - 9 // 10 - 10 // 11 - 11 // 12 - 12 // 13 - 13 // 14 - 14 // 15 - 15 // 16 - 16 // // All possible combinations of values with FlagsAttribute: // 0 - None // 1 - Black // 2 - Red // 3 - Black, Red // 4 - Green // 5 - Black, Green // 6 - Red, Green // 7 - Black, Red, Green // 8 - Blue // 9 - Black, Blue // 10 - Red, Blue // 11 - Black, Red, Blue // 12 - Green, Blue // 13 - Black, Green, Blue // 14 - Red, Green, Blue // 15 - Black, Red, Green, Blue // 16 - 16
前面的示例定義了兩個與顏色相關的枚舉: SingleHue 和 MultiHue 。 后者具有 FlagsAttribute 屬性; 前者不具有屬性。 此示例顯示了在一個整數范圍(包括不表示枚舉類型的基礎值的整數)轉換為枚舉類型及其顯示的字符串表示形式時的行為差異。 例如,請注意,3不能表示為 SingleHue 值,因為3不是任何成員的基礎值 SingleHue ,而特性使你可以將 FlagsAttribute 3 表示為 MultiHue 的值 Black, Red 。
遍歷Enum
foreach (var f in Enum.GetValues(typeof(FileSystemRights))) { Console.WriteLine(f.ToString().PadLeft(28) + Convert.ToString((int)f, 2).PadLeft(32, '0')); }
接下來是權限的運算:
1. 權限的加法, 使用與運算來實現。
0001 | 0100 = 0101, 表示同時具有第一位和第三位的權限管理了, 枚舉表示為:
1 Permissions per = Permissions.Insert | Permissions.Update
2. 權限的減法, 使用與運算+非運算來實現。
如上面要去掉Insert權限, 操作為:
1 Permissions per &= ~Permissions.Insert 2 // 即是 3 0101 & ~0001 = 0101 & 1110 = 0100
3. 權限的判斷, 使用與運算。
判斷是否有操作權限時, 把用戶的的權限與操作權限進行與運算, 如果得到的結果仍是操作權限管理, 則表示用戶具有該權限:
1 Permissions per = Permissions.Insert | Permissions.Update; 2 if((per & Permissions.Insert)== Permissions.Insert) 3 { 4 //有操作權限 5 }
比較過程為 0101 & 0001 = 0001, 0001的0位用與C#位運算把其它位都置成0, 變成只比較1的這一位.
