欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

簡述C#枚舉高級戰(zhàn)術(shù)

 更新時(shí)間:2020年10月13日 11:02:03   作者:精致碼農(nóng) • 王亮  
這篇文章主要介紹了簡述C#枚舉高級戰(zhàn)術(shù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

文章開頭先給大家出一道面試題:

在設(shè)計(jì)某小型項(xiàng)目的數(shù)據(jù)庫(假設(shè)用的是 MySQL)時(shí),如果給用戶表(User)添加一個(gè)字段(Roles)用來存儲用戶的角色,你會給這個(gè)字段設(shè)置什么類型?提示:要考慮到角色在后端開發(fā)時(shí)需要用枚舉表示,且一個(gè)用戶可能會擁有多個(gè)角色。

映入你腦海的第一個(gè)答案可能是:varchar 類型,用分隔符的方式來存儲多個(gè)角色,比如用 1|2|3 或 1,2,3 來表示用戶擁有多個(gè)角色。當(dāng)然如果角色數(shù)量可能超過個(gè)位數(shù),考慮到數(shù)據(jù)庫的查詢方便(比如用 INSTR 或 POSITION 來判斷用戶是否包含某個(gè)角色),角色的值至少要從數(shù)字 10 開始。方案是可行的,可是不是太簡單了,有沒有更好的方案?更好的回答應(yīng)是整型(int、bigint 等),優(yōu)點(diǎn)是寫 SQL 查詢條件更方便,性能、空間上都優(yōu)于 varchar。但整型畢竟只是一個(gè)數(shù)字,怎么表示多個(gè)角色呢?此時(shí)想到了二進(jìn)制位操作的你,心中應(yīng)該早有了答案。且保留你心中的答案,接著看完本文,或許你會有意外的收獲,因?yàn)閷?shí)際應(yīng)用中可能還會遇到一連串的問題。為了更好的說明后面的問題,我們先來回顧一下枚舉的基礎(chǔ)知識。

枚舉基礎(chǔ)

枚舉類型的作用是限制其變量只能從有限的選項(xiàng)中取值,這些選項(xiàng)(枚舉類型的成員)各自對應(yīng)于一個(gè)數(shù)字,數(shù)字默認(rèn)從 0 開始,并以此遞增。例如:

public enum Days
{
  Sunday, Monday, Tuesday, // ...
}

其中 Sunday 的值是 0,Monday 是 1,以此類推。為了一眼能看出每個(gè)成員代表的值,一般推薦顯示地將成員值寫出來,不要省略:

public enum Days
{
  Sunday = 0, Monday = 1, Tuesday = 2, // ...
}

C# 枚舉成員的類型默認(rèn)是 int 類型,通過繼承可以聲明枚舉成員為其它類型,比如:

public enum Days : byte
{
  Monday = 1,
  Tuesday = 2,
  Wednesday = 3,
  Thursday = 4,
  Friday = 5,
  Saturday = 6,
  Sunday = 7
}

枚舉類型一定是繼承自 byte、sbyte、short、ushort、int、uint、long 和 ulong 中的一種,不能是其它類型。下面是幾個(gè)枚舉的常見用法(以上面的 Days 枚舉為例):

// 枚舉轉(zhuǎn)字符串
string foo = Days.Saturday.ToString(); // "Saturday"
string foo = Enum.GetName(typeof(Days), 6); // "Saturday"
// 字符串轉(zhuǎn)枚舉
Enum.TryParse("Tuesday", out Days bar); // true, bar = Days.Tuesday
(Days)Enum.Parse(typeof(Days), "Tuesday"); // Days.Tuesday

// 枚舉轉(zhuǎn)數(shù)字
byte foo = (byte)Days.Monday; // 1
// 數(shù)字轉(zhuǎn)枚舉
Days foo = (Days)2; // Days.Tuesday

// 獲取枚舉所屬的數(shù)字類型
Type foo = Enum.GetUnderlyingType(typeof(Days))); // System.Byte

// 獲取所有的枚舉成員
Array foo = Enum.GetValues(typeof(MyEnum);
// 獲取所有枚舉成員的字段名
string[] foo = Enum.GetNames(typeof(Days));

另外,值得注意的是,枚舉可能會得到非預(yù)期的值(值沒有對應(yīng)的成員)。比如:

Days d = (Days)21; // 不會報(bào)錯(cuò)
Enum.IsDefined(typeof(Days), d); // false

即使枚舉沒有值為 0 的成員,它的默認(rèn)值永遠(yuǎn)都是 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()); // "成功"
}

上面這些我認(rèn)為已經(jīng)包含了大部分我們?nèi)粘S玫降拿杜e知識了。下面我們繼續(xù)回到文章開頭說的用戶角色存儲問題。

用戶角色存儲問題

我們先定義一個(gè)枚舉類型來表示兩種用戶角色:

public enum Roles
{
  Admin = 1,
  Member = 2
}

這樣,如果某個(gè)用戶同時(shí)擁有 Admin 和 Member 兩種角色,那么 User 表的 Roles 字段就應(yīng)該存 3。那問題來了,此時(shí)若查詢所有擁有 Admin 角色的用戶的 SQL 該怎么寫呢?對于有基礎(chǔ)的程序員來說,這個(gè)問題很簡單,只要用位操作符邏輯與(‘&')來查詢即可。

SELECT * FROM `User` WHERE `Roles` & 1 = 1;

同理,查詢同時(shí)擁有這兩種角色的用戶,SQL 語句應(yīng)該這么寫:

SELECT * FROM `User` WHERE `Roles` & 3 = 3;

對這條 SQL 語句用 C# 來實(shí)現(xiàn)查詢是這樣的(為了簡單,這里使用了 Dapper):

public class User
{
  public int Id { get; set; }
  public Roles Roles { get; set; }
}

connection.Query<User>(
  "SELECT * FROM `User` WHERE `Roles` & @roles = @roles;",
  new { roles = Roles.Admin | Roles.Member });

對應(yīng)的,在 C# 中要判斷用戶是否擁有某個(gè)角色,可以這么判斷:

// 方式一
if ((user.Roles & Roles.Admin) == Roles.Admin)
{
  // 做管理員可以做的事情
}

// 方式二
if (user.Roles.HasFlag(Roles.Admin))
{
  // 做管理員可以做的事情
}

同理,在 C# 中你可以對枚舉進(jìn)行任意位邏輯運(yùn)算,比如要把角色從某個(gè)枚舉變量中移除:

var foo = Roles.Admin | Roles.Member;
var bar = foo & ~Roles.Admin;

這就解決了文章前面提到的用整型來存儲多角色的問題,不論數(shù)據(jù)庫還是 C# 語言,操作上都是可行的,而且也很方便靈活。

枚舉的 Flags 特性

下面我們提供一個(gè)通過角色來查詢用戶的方法,并演示如何調(diào)用,如下:

public IEnumerable<User> GetUsersInRoles(Roles roles)
{
  _logger.LogDebug(roles.ToString());
  _connection.Query<User>(
    "SELECT * FROM `User` WHERE `Roles` & @roles = @roles;",
    new { roles });
}

// 調(diào)用
_repository.GetUsersInRoles(Roles.Admin | Roles.Member);

Roles.Admin | Roles.Member 的值是 3,由于 Roles 枚舉類型中并沒有定義一個(gè)值為 3 的字段,所以在方法內(nèi) roles 參數(shù)顯示的是 3。3 這個(gè)信息對于我們調(diào)試或打印日志很不友好。在方法內(nèi),我們并不知道這個(gè) 3 代表的是什么。為了解決這個(gè)問題,C# 枚舉有個(gè)很有用的特性:FlagsAtrribute。

[Flags]
public enum Roles
{
  Admin = 1,
  Member = 2
}

加上這個(gè) Flags 特性后,我們再來調(diào)試 GetUsersInRoles(Roles roles) 方法時(shí),roles 參數(shù)的值就會顯示為 Admin|Member 了。簡單來說,加不加 Flags 的區(qū)別是:

var roles = Roles.Admin | Roles.Member;
Console.WriteLing(roles.ToString()); // "3",沒有 Flags 特性
Console.WriteLing(roles.ToString()); // "Admin, Member",有 Flags 特性

給枚舉加上 Flags 特性,我覺得應(yīng)當(dāng)視為 C# 編程的一種最佳實(shí)踐,在定義枚舉時(shí)盡量加上 Flags 特性。

解決枚舉值沖突:2 的冪

到這,枚舉類型 Roles 一切看上去沒什么問題,但如果現(xiàn)在要增加一個(gè)角色:Mananger,會發(fā)生什么情況?按照數(shù)字值遞增的規(guī)則,Manager 的值應(yīng)當(dāng)設(shè)為 3。

[Flags]
public enum Roles
{
  Admin = 1,
  Member = 2,
  Manager = 3
}

能不能把 Manager 的值設(shè)為 3?顯然不能,因?yàn)?Admin 和 Member 進(jìn)行位的或邏輯運(yùn)算(即:Admin | Member) 的值也是 3,表示同時(shí)擁有這兩種角色,這和 Manager 沖突了。那怎樣設(shè)值才能避免沖突呢?既然是二進(jìn)制邏輯運(yùn)算“或”會和成員值產(chǎn)生沖突,那就利用邏輯運(yùn)算或的規(guī)律來解決。我們知道“或”運(yùn)算的邏輯是兩邊只要出現(xiàn)一個(gè) 1 結(jié)果就會 1,比如 1|1、1|0 結(jié)果都是 1,只有 0|0 的情況結(jié)果才是 0。那么我們就要避免任意兩個(gè)值在相同的位置上出現(xiàn) 1。根據(jù)二進(jìn)制滿 2 進(jìn) 1 的特點(diǎn),只要保證枚舉的各項(xiàng)值都是 2 的冪即可。比如:

1:  00000001
2:  00000010
4:  00000100
8:  00001000
再往后增加的話就是 16、32、64...,其中各值不論怎么相加都不會和成員的任一值沖突。這樣問題就解決了,所以我們要這樣定義 Roles 枚舉的值:

[Flags]
public enum Roles
{
  Admin = 1,
  Member = 2,
  Manager = 4,
  Operator = 8
}

不過在定義值的時(shí)候要在心中小小計(jì)算一下,如果你想懶一點(diǎn),可以用下面這種“位移”的方法來定義:

[Flags]
public enum Roles
{
  Admin  = 1 << 0,
  Member  = 1 << 1,
  Manager = 1 << 2,
  Operator = 1 << 3
}

一直往下遞增編值即可,閱讀體驗(yàn)好,也不容易編錯(cuò)。兩種方式是等效的,常量位移的計(jì)算是在編譯的時(shí)候進(jìn)行的,所以相比不會有額外的開銷。

總結(jié)

本文通過一道小小的面試題引發(fā)一連串對枚舉的思考。在小型系統(tǒng)中,把用戶角色直接存儲在用戶表是很常見的做法,此時(shí)把角色字段設(shè)為整型(比如 int)是比較好的設(shè)計(jì)方案。但與此同時(shí),也要考慮到一些最佳實(shí)踐,比如使用 Flags 特性來幫助更好的調(diào)試和日志輸出。也要考慮到實(shí)際開發(fā)中的各種潛在問題,比如多個(gè)枚舉值進(jìn)行或(‘|')運(yùn)算與成員值發(fā)生沖突的問題。

到此這篇關(guān)于簡述C#枚舉高級戰(zhàn)術(shù)的文章就介紹到這了,更多相關(guān)C# 枚舉內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C# yield關(guān)鍵字詳解

    C# yield關(guān)鍵字詳解

    這篇文章主要介紹了C# yield關(guān)鍵字詳解,本文講解了yield是一個(gè)語法糖、語法糖的實(shí)現(xiàn)(實(shí)現(xiàn)IEnumerable<T>接口的類)、yield使用中的特殊情況等內(nèi)容,需要的朋友可以參考下
    2015-04-04
  • c#使用微信接口開發(fā)微信門戶應(yīng)用中微信消息的處理和應(yīng)答

    c#使用微信接口開發(fā)微信門戶應(yīng)用中微信消息的處理和應(yīng)答

    這篇文章主要介紹了c#使用微信接口開發(fā)微信門戶中的微信消息的處理和應(yīng)答的過程,需要的朋友可以參考下
    2014-03-03
  • c#裝箱和拆箱知識整理

    c#裝箱和拆箱知識整理

    這篇文章主要介紹了c#裝箱和拆箱知識,裝箱和拆箱是一個(gè)抽象的概念,需要的朋友可以參考下
    2014-03-03
  • C#自定義Attribute值的獲取與優(yōu)化技巧

    C#自定義Attribute值的獲取與優(yōu)化技巧

    C#自定義Attribute值的獲取是開發(fā)中會經(jīng)常用到的,大家通常使用反射進(jìn)行獲取的,代碼也很簡單,今天通過本文給大家講解C#?Attribute值獲取方法,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • c#委托與事件(詳解)

    c#委托與事件(詳解)

    本文中,我將通過兩個(gè)范例由淺入深地講述什么是委托、為什么要使用委托、事件的由來、.Net Framework中的委托和事件、委托和事件對Observer設(shè)計(jì)模式的意義,對它們的中間代碼也做了討論
    2021-07-07
  • C# 實(shí)例化接口對象的方法

    C# 實(shí)例化接口對象的方法

    下面小編就為大家?guī)硪黄狢# 實(shí)例化接口對象的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • c#中虛函數(shù)的相關(guān)使用方法

    c#中虛函數(shù)的相關(guān)使用方法

    若一個(gè)實(shí)例方法聲明前帶有virtual關(guān)鍵字,那么這個(gè)方法就是虛方法。
    2013-02-02
  • C#跨窗體操作(引用傳遞) 實(shí)例代碼

    C#跨窗體操作(引用傳遞) 實(shí)例代碼

    現(xiàn)在給大家介紹一種最簡單的跨窗體操作,WinForm的窗體是一個(gè)類,C#的類是引用類型,那么我們應(yīng)該可以將WinForm窗體類進(jìn)行傳遞,那不就可以進(jìn)行操作了么?
    2013-03-03
  • C#?Winform實(shí)現(xiàn)進(jìn)度條顯示

    C#?Winform實(shí)現(xiàn)進(jìn)度條顯示

    這篇文章主要為大家詳細(xì)介紹了C#?Winform實(shí)現(xiàn)進(jìn)度條顯示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • C#實(shí)現(xiàn)的ZPL條碼打印類完整實(shí)例

    C#實(shí)現(xiàn)的ZPL條碼打印類完整實(shí)例

    這篇文章主要介紹了C#實(shí)現(xiàn)的ZPL條碼打印類,結(jié)合實(shí)例形式詳細(xì)分析了C#實(shí)現(xiàn)條碼打印的原理與使用方法,代碼注釋中備有詳盡的說明,便于理解使用,需要的朋友可以參考下
    2016-06-06

最新評論