平常定義 Enum 這樣寫
public enum CarBrand
{
None,
Tesla,
Honda,
Ferrari,
Porsche
}
這個寫法沒有定義明確的值,但實際上編譯器會幫妳自動給從 0 開始的值
Console.WriteLine($"{CarBrand.None} = {(int)CarBrand.None}"); //None = 0
Console.WriteLine($"{CarBrand.Tesla} = {(int)CarBrand.Tesla}"); //Tesla = 1
Console.WriteLine($"{CarBrand.Honda} = {(int)CarBrand.Honda}"); //Honda = 2
Console.WriteLine($"{CarBrand.Ferrari} = {(int)CarBrand.Ferrari}");//Ferrari = 3
Console.WriteLine($"{CarBrand.Porsche} = {(int)CarBrand.Porsche}");//Porsche = 4
要不要定義明確的 value 就依照使用情境判斷即可。
通常將值存到 DB 裡面,我都會定義明確的值,以免 enum 順序被其他人改掉採到坑裡。
假設原本 DB 有個欄位是紀錄最愛的汽車品牌,那把值直接填進去沒問題。
我愛 Tesla,我就填 1; 我愛 Honda,我就填 2~
但小孩才做選擇!我愛 Tesla, Honda, Ferrari!
這時候我們就會選擇使用 bit 處裡
以前土炮的寫法,定義 enum 值是次方
public enum CarBrand
{
None = 0, //2^0
Tesla = 1, //2^1
Honda = 2, //2^2
Ferrari = 3,//2^3
Porsche = 4 //2^4
}
我愛 Tesla => 存到 DB 的值為 2^1=2
我愛 Tesla, Honda => 存到 DB 的值為 2^1+2^2=6
使用方式如下
public class BitHelper
{
public static List<int> GetBitList(int value)
{
List<int> bitList = new List<int>();
double @base = 2;
double residualValue = Convert.ToDouble(value);
while (residualValue > 0)
{
double logValue = Math.Log(residualValue, @base);
int exponent = Convert.ToInt32(Math.Floor(logValue));
bitList.Add(exponent);
residualValue -= Math.Pow(@base, exponent);
}
return bitList;
}
public static bool IsContainBit(List<int> bitList, Enum bit)
{
return bitList.Contains(Convert.ToInt32(bit));
}
}
//get value
List<int> bitList = BitHelper.GetBitList(DbCarBrandValue);
bool hasTesla = BitHelper.IsContainBit(bitList, CarBrand.Tesla);
//set value
List<int> bitList = BitHelper.GetBitList(DbCarBrandValue);
int updateValue;
if (!hasTesla && BitHelper.isContainBit(bitList, CarBrand.Tesla))
updateValue = DbCarBrandValue - (int)Math.Pow(2, (int)CarBrand.Tesla);
else if (hasTesla == true && !BitHelper.isContainBit(bitList, CarBrand.Tesla))
updateValue = DbCarBrandValue + (int)Math.Pow(2, (int)CarBrand.Tesla);
認識了 Flags 之後,寫法方便很多,有三種定義 enum 的方式,寫法如下
//寫法一
[Flags]
public enum CarBrand
{
None = 0,
Tesla = 1,
Honda = 2,
Ferrari = 4,
Porsche = 8
}
//寫法二
[Flags]
public enum CarBrand
{
None = 0, //0
Tesla = 1 << 0, //1
Honda = 1 << 1, //2
Ferrari = 1 << 2,//4
Porsche = 1 << 3 //8
}
//寫法三
[Flags]
public enum CarBrand
{
None = 0, //0
Tesla = 1, //1
Honda = Tesla << 1, //2
Ferrari = Honda << 1, //4
Porsche = Ferrari << 1//8
}
其中 << 是左移運算符
1 << 1 表示將十進制的 1 往左移 1 位 => 0010 => 2
1 << 2 表示將十進制的 1 往左移 2 位 => 0100 => 4
//get value
int dbValue = 3;//Tesla, Honda
CarBrand carBrand = (CarBrand)dbValue;
Console.WriteLine($"has {CarBrand.None} = {carBrand.HasFlag(CarBrand.None)}"); //has None = True
Console.WriteLine($"has {CarBrand.Tesla} = {carBrand.HasFlag(CarBrand.Tesla)}"); //has Tesla = True
Console.WriteLine($"has {CarBrand.Honda} = {carBrand.HasFlag(CarBrand.Honda)}"); //has Honda = True
Console.WriteLine($"has {CarBrand.Ferrari} = {carBrand.HasFlag(CarBrand.Ferrari)}");//has Ferrari = False
Console.WriteLine($"has {CarBrand.Porsche} = {carBrand.HasFlag(CarBrand.Porsche)}");//has Porsche = False
//set value
Console.WriteLine($"{CarBrand.Tesla} + {CarBrand.Honda} = {CarBrand.Tesla | CarBrand.Honda}");//Tesla + Honda = Tesla, Honda
Console.WriteLine($"{CarBrand.Tesla} + {CarBrand.Honda} = {(int)(CarBrand.Tesla | CarBrand.Honda)}");//Tesla + Honda = 3
//add value
int dbValue = 3;//Tesla, Honda
CarBrand carBrand = (CarBrand)dbValue;
Console.WriteLine($"{carBrand} + {CarBrand.Honda} = {carBrand | CarBrand.Honda}");//Tesla, Honda + Honda = Tesla, Honda
//0011 | 0010 => 0011
//minus value
int dbValue = 7;//Tesla, Honda, Ferrari
CarBrand carBrand = (CarBrand)dbValue;
Console.WriteLine($"{carBrand} - {CarBrand.Honda} = {carBrand &~ CarBrand.Honda}");//Tesla, Honda, Ferrari - Honda = Tesla, Ferrari
//0111 & 1101 => 0101
「|」是將二進制每個位進行運算,有 1 則得 1
0010 | 0100 => 0110
0110 | 0101 => 0111
「&」是將二進制每個位進行運算,1 & 1 才得 1
0010 & 0100 => 0000
0110 & 0101 => 0100
「~」是將二進制每個位進行運算,0 則變 1;1 則變 0
0010 => 1101
0100 => 1011
參考資料:
https://learn.microsoft.com/en-us/dotnet/api/system.flagsattribute?view=net-7.0