一般在我們開發時如果能使用列舉羅列的,一般都會定義一個列舉型別。將列舉型別作為方法的引數,可以方便的進行呼叫,給我們帶來不少的遍歷,當然有時候它還不如直接用一個int型別帶來,帶來一定靈活性。但只要能滿足業務我們們就怎麼方便怎麼來吧。
基本使用
我們業務中會經常遇到訂單狀態的列舉,它羅列出了所有訂單狀態的可能值,下面是我剛剛編的一個訂單狀態列舉
public enum OrderStatus
{
/// <summary>
/// 未支付
/// </summary>
WaitPay = 0,
/// <summary>
/// 已支付
/// </summary>
Payed = 1,
/// <summary>
/// 已退款
/// </summary>
Refund = 2,
/// <summary>
/// 已關閉
/// </summary>
Closed = 3,
}
我們都知道C# 列舉成員的型別預設是 int 型別,通過繼承可以宣告列舉成員為其它型別,例如
public enum OrderStatus: byte
{
/// <summary>
/// 未支付
/// </summary>
WaitPay = 0,
/// <summary>
/// 已支付
/// </summary>
Payed = 1,
/// <summary>
/// 已退款
/// </summary>
Refund = 2,
/// <summary>
/// 已關閉
/// </summary>
Closed = 3,
}
還真是“聽君一席話,如聽一席話”,別,乾貨這就來。
搭配Description使用
我相信大部分人都知道這麼玩
public enum OrderStatus
{
[Description("未支付")]
WaitPay = 0,
[Description("已支付")]
Payed = 1,
[Description("已退款")]
Refund = 2,
[Description("已關閉")]
Closed = 3,
}
寫一個擴充套件方法,用於獲取Description
的描述資訊。
public static class EnumExtensions
{
public static string GetDescription(this Enum obj)
{
object[]? array = obj.GetType().GetField(obj.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), inherit: true);
if (array != null)
{
var attr = array.FirstOrDefault(x => x is DescriptionAttribute);
if (attr != null)
{
return ((DescriptionAttribute)attr).Description;
}
}
return string.Empty;
}
}
然後我們就可以很方便的獲取列舉的描述資訊了,這個好像有點用。
搭配Flag屬性使用
在我們對列舉進行或運算時,如
internal enum Jod
{
/// <summary>
/// 老師
/// </summary>
Teacher = 1,
/// <summary>
/// 運動員
/// </summary>
Athletes = 2
}
某人既是老師,又是國家運動員,我們對列舉進行或運算後由於結果是3.
這是因為Jod
中不存在這樣的一個值為3的列舉,所以會輸出3;這一般情況下並不是我們想要的,此時我們只需要對這個列舉加上一個屬性[Flags]
。
[Flags]
internal enum Jod
{
/// <summary>
/// 老師
/// </summary>
Teacher = 1,
/// <summary>
/// 運動員
/// </summary>
Athletes = 2
}
講道理,這個有用,但我很少用~
位運算
上文中一共提到了兩個列舉型別OrderStatus
和Jod
,他們正好分別對應互斥型和非互斥型,訂單的狀態某一時刻只能有一種,而工作可以同時有多個(舉例可能不恰當,知道意思即可)。
列舉型別的值不是所有的情況下都是加單的對新增的成員加1,比如Jod
列舉隨著業務增加,又新增了歌手和舞者
[Flags]
internal enum Jod
{
/// <summary>
/// 老師
/// </summary>
Teacher = 1,
/// <summary>
/// 運動員
/// </summary>
Athletes = 2,
/// <summary>
/// 歌手
/// </summary>
Singer = 3,
/// <summary>
/// 舞者
/// </summary>
Dancer = 4
}
如果你覺得上面的列舉沒問題,那問題就嚴重了,由於對於非互斥關係的列舉,我們可以很方便的進行或運算
來表示同時兼多種列舉值的情況。可以通過與運算
檢查一個列舉值是否包含某個值,可以通過異或
,同或
操作進行更為有趣的操作,為了能夠進行優雅的位運算
,列舉值的分配則不能按照上面的12345累加1進行,而是要按照下例:
[Flags]
internal enum Jod
{
/// <summary>
/// 老師
/// </summary>
Teacher = 1,
/// <summary>
/// 運動員
/// </summary>
Athletes = 2,
/// <summary>
/// 歌手
/// </summary>
Singer = 4,
/// <summary>
/// 舞者
/// </summary>
Dancer = 8,
Jobx = 0x10,
JobY = 0x20,
JobZ = 0x40,
...
}
我們知道int
轉成二進位制是由0和1,一共32位組成的,位運算正是二進位制運算的方法,上面的列舉繼承自int,如果將32位二進位制數的每一位表示一種職業,那麼一共可以表示32個職業。對應關係如下
列舉值 | 十進位制 | 16進位制 | 二進位制 |
---|---|---|---|
Teacher | 1 | 0x1 | 0000 0000 0000 0000 0000 0000 0000 0001 |
Athletes | 2 | 0x2 | 0000 0000 0000 0000 0000 0000 0000 0010 |
Singer | 4 | 0x4 | 0000 0000 0000 0000 0000 0000 0000 0100 |
Dancer | 8 | 0x8 | 0000 0000 0000 0000 0000 0000 0000 1000 |
JobX | 16 | 0x10 | 0000 0000 0000 0000 0000 0000 0001 0000 |
JobY | 32 | 0x20 | 0000 0000 0000 0000 0000 0000 0010 0000 |
... | ... | ... | ... |
常用操作
// 1.基本的或運算,表示同時有多種列舉值的情況
var jobs = Jod.Teacher | Jod.Athletes;
// 2.判斷某個人的職業中是否有Athletes
if ((jobs & Jod.Athletes) == Jod.Athletes)
{
// 是運動員
}
我們可以將enum的數值存到資料庫,寫sql時也可以使用位運算的,從資料庫中查到的資料轉成Model後在業務程式碼中就可以優雅的使用位運算進行判斷了。
資料庫設計中的妙用
最初知道Flags
這個屬性的時候就在想,他為什麼叫Flags
?直到我遇到下面這樣的業務場景(瞎編的,非公司實際業務場景,但可以說明問題)。
一般場景
例如我們電商平臺管理的商戶,最開始我們會有個商戶表merch
,欄位如下
欄位 | 描述 | 型別 |
---|---|---|
merch_id | 商戶id | long |
merch_name | 商戶名 | string |
certified | 已認證? | int(0或1) |
過了幾個月,隨著產品完善,該表又增加了兩個欄位
欄位 | 描述 | 型別 |
---|---|---|
is_vip_merch | vip商戶? | int(0或1) |
is_defect_free | 商品上架免檢 | int(0或1) |
又過了幾個月,又增加了幾個欄位
欄位 | 描述 | 型別 |
---|---|---|
is_frozen | 是否凍結 | int(0或1) |
is_mvp | 是否金牌商戶 | int(0或1) |
優化
每次新的需要來了,就需要增加欄位,最後這張表,光這種標識欄位就好快10來個了,這樣維護起來太難受了吧。如果我說可以將這10來個標識欄位用一個欄位搞定,你會不會驚訝!這裡是跟新手說的,大佬們自然知道我下面要怎麼幹了。
我將上面的表欄位進行了優化,由7個欄位,縮減到3個欄位。
欄位 | 描述 | 型別 |
---|---|---|
merch_id | 商戶id | long |
merch_name | 商戶名 | string |
merch_flags | 各種商戶標識 | int |
並給這個merch_flags
定義了一個列舉
[Flags]
public enum MerchFlags
{
/// <summary>
/// 已認證?
/// </summary>
certified = 1,
/// <summary>
/// vip商戶?
/// </summary>
is_vip_merc = 2,
/// <summary>
/// 商品上架免檢
/// </summary>
is_defect_free = 4,
/// <summary>
/// 是否凍結
/// </summary>
s_frozen = 8,
/// <summary>
/// 是否金牌商戶
/// </summary>
is_mvp = 0x10,
// ...繼續新增各種標誌位
}
到這裡應該明白這是要幹嘛了吧,以後再來新的業務需要加標誌欄位,直接在列舉MerchFlags
加一個就行了,資料庫不需要加欄位了。int型別的列舉可以給你32個標誌可以用,long可以存64個,一般場景是夠用了。
思考一個問題
你知道Flags
屬性為什麼叫Flags
了嗎?