c#中的 Int 強轉標記了Flags的枚舉Enum

最近在利用這個枚舉做一些狀態管理,遇到了int強轉枚舉的情況。但有時候內部情況還是不太清楚,因此,研究了這個強轉的過程,以及標記了Flags的枚舉的強轉。

一、Enum基本特性:

  1. 定義枚舉時,若不顯式指定值,則默認爲從0開始
  2. 當指定了某枚舉項的值,而緊隨其後的新項又不指定值,則該新項的值默認爲前一項的值+1
  3. Enum項的值可以重複。例如定義兩個項的值都顯式指定爲=1,是允許的。
  4. Enum的元素不能重複。
  5. int轉Enum遇到重複值時,優先匹配按名稱排序最前面的值。

舉例:

public enum expEnum
{
 p0,  //首項不指定值,則默認 =0
 p1,  //不指定值,則默認爲上一項 +1,此處 =1
 p2,  //同上,默認 +1=2
 q1=1 //值可以重複,此處顯式指定 q1=1
 q2,  //同上,默認 +1=2
 q1=2, //error: 類型已包含 q1 的定義
 a1=1,
}
……
int status=1;
expEnum StatusEnum=(expEnum)status;
// 此時可以看到 StatusEnum==expEnum.a1 依字幕排序,a1 是排在 p1 和q1 前面,所以優先匹配。

二、帶Flags標記的 Enum

  (1)基本特性:

  1. 表示該枚舉值,是可以進行位運算的
  2. 一般而言,添加了這個標記,枚舉則定義爲2的正整數冪,例如:1,2,4...。但這個是手動保證的,並不強制限定。
  3. 帶了Flags標記的Enum依然遵守上述Enum的所有基本特性,並不特殊。枚舉定義時,不指定首項的值時,首項依然默認是從0開始。
  4. 從int轉換爲Enum時,可以自動轉爲枚舉值的疊加。例如,6可以轉換爲4+2。不添加Flags則不能疊加,保留該int值。

舉例:

[Flags]
public enum expFlagsEnum
{
  p0,//p0=0, 首項不指定值,依然是從 0 開始
  p1,//p1=1, 默認 +1
  p2,//p2=2
  p4=4,
  p8=8,
}

……
int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 轉換後 可以看到 StatusEnum==expFlagsEnum.p1|expFlagsEnum.p2|expFlagsEnum.p4
……

//[Flags]---------------------------------去掉 Flags 標記
public enum expFlagsEnum
{
  p0,//p0=0, 首項不指定值,依然是從0開始
  p1,//p1=1, 默認+1
  p2,//p2=2
  p4=4,
  p8=8,
}

……
int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 轉換後 可以看到 StatusEnum==7
……

(2)特殊情形:非2正整數冪的枚舉值

網上的資料一般都不涉及這種特殊情形,只討論是2的整數冪即:1,2,4的情形,而不討論非2的整數冪的情況。

這裏的特殊主要特殊在從int轉Enum的時候,比較複雜。有這麼幾個原則:

  1. 最大值優先。
  2. int值按位拆分,再重組,以匹配枚舉項。拆分後的枚舉項,用二進制表示時,每一位上的1不能有重疊。(實際上就是按位或的逆運算)

   舉例:

1、最大值優先

//================最大匹配原則================
[Flags]
public enum expFlagsEnum
{
  p0,//p0=0, 首項不指定值,依然是從 0 開始
  p1,//p1=1, 默認 +1
  p2,//p2=2
  p4=4,
  p8=8,
  p16=16,
  p31=31,//非 2 的整數冪
  p32=32
}

……
int status=47;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 轉換後 可以看到 StatusEnum==expFlagsEnum.p1|expFlagsEnum.p2|expFlagsEnum.p4|expFlagsEnum.p8|expFlagsEnum.p32
……
//47 有多種匹配方式,例如:
//47 = 32+8+4+2+1
//47 = 31+16
//根據最大匹配原則,優先匹配的是包含 32 的方案,儘管 31+16 看上去更簡潔

2、按位拆分

//================最大匹配原則================
[Flags]
public enum expFlagsEnum
{
  p0,//p0=0, 首項不指定值,依然是從 0 開始
  p1,//p1=1, 默認 +1
  p2,//p2=2
  p3,
  p4=4,
  p8=8,
}

……
int status=6;
expFlagsEnum StatusEnum=(expFlagsEnum)status;
// 轉換後 可以看到 StatusEnum==6 即沒有轉爲枚舉值,直接保留了 int

……
//直覺上,應該是 6=3+2+1
// 但實際上6表示爲二進制即爲 110,即表示爲 6=100+010,於是6只能匹配 4+2 或者6自身

// 再舉例


int status=7;
expFlagsEnum StatusEnum=(expFlagsEnum)status;

// 轉換後 可以看到 StatusEnum==expFlagsEnum.p4|expFlagsEnum.p3
//拆分後的兩個數字,在轉換爲“二進制”時,每一位上不能有重疊。
// 7=111 可以分成 100和011 這兩個數字在每一位上的 1 不重疊,於是7 可以重組成4+3
// 至於爲什麼是 4+3而不是 4+2+1 上面已述,是最大值原則。
// 


可以看到這樣的枚舉定義爲Flags情況會比較複雜,因此上,一般而言,標註了Flags,其項都是用2的整數冪,這樣是比較清晰的定義。

而一個複雜用法的例子就是Keys,標記了Flags,主要是爲了任意鍵和Ctrl,Alt,Shift的組合。這三個鍵定義的是2的整數冪,而且是大整數,以保證不會有其他枚舉項與之有重疊。

至於其他鍵,則不是很建議將其合併處理。合起來常常會產生衝突。

比如:A=65,B=66 按位或起來是67,成了Keys.C=67鍵了。

namespace System.Windows.Forms
{
[System.Flags]
public enum Keys
{
....
}
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章