枚举 Enum
枚举默认是int类型,可以通过指定类型来改变,但必须是整形,比如:
enum ShoppingList : ulong
{
Apple,
Orange,
Banana
}
枚举的默认值为0,是通过 (E)0
的方式生成了,即便没有枚举成员被赋值为0。
用法
查看值是否在 Enum 中
C#的枚举在转换时默认不检查值是否已经在枚举中定义,即使是未定义的值也可以转换成枚举类型,但不会匹配到枚举值。
Enum.IsDefined 方法可以检查值是否在枚举中定义。
eg:
public enum Waypoints
{
A,
B,
C
}
Enum.IsDefined(typeof(Waypoints), 3); // False
ref: Large flags enumerations in C# - Stack Overflow
位标志 Bit Flags
使用一个整形不不同比特位来做为一组开关。 例如用这个枚举来表示一个选手的多种牌型:
[Flags]
enum CardDeckSettings : uint
{
SingleDeck = 0x01,
LargePictures = 0x02,
FancyNumbers = 0x04,
Shuffle = 0x08,
}
需要创建枚举时,可以使用逻辑运算符 OR 来添加所需要的多个值,例如选手同时包括 SingleDeck 和 LargePictures:
CardDeckSettings settings = CardDeckSettings.SingleDeck | CardDeckSettings.LargePictures;
当使用条件语句判断 Flags 枚举值,注意不能使用==
,而应该使用HasFlag
。
查找时,可以通过 HasFlag 来检查是否包含某个值,例如:
bool IsSingleDeck = settings.HasFlag(CardDeckSettings.SingleDeck);
Console.WriteLine("Your deck is {0} which has Single Deck is {1}", settings.ToString(), IsSingleDeck);
输出:Your deck is 3 which has Single Deck is True
另一种方法是通过逻辑与&
来检查是否包含某个值,例如:
bool IsSingleDeckAlternative = (settings & CardDeckSettings.SingleDeck) == CardDeckSettings.SingleDeck;
Flags 特性
将枚举做为位标志时,在枚举类型前加上[Flags]
可以告诉编译器位标志的特性,例如可以提供更多的格式化信息:
不加 Flags 特性的枚举:
enum CardDeckSettings
{
SingleDeck = 0x01,
LargePictures = 0x02,
FancyNumbers = 0x04,
Shuffle = 0x08,
}
Console.WriteLine("Your deck is {0} ", settings.ToString());
输出:Your deck is 3
添加 Flags 特性的枚举:
[Flags]
enum CardDeckSettings
{
SingleDeck = 0x01,
LargePictures = 0x02,
FancyNumbers = 0x04,
Shuffle = 0x08,
}
...
Console.WriteLine("Your deck is {0} ", settings.ToString());
输出:Your deck is SingleDeck, LargePictures
扩展方法
判断是否包含某个值
对于 Flags 枚举,可以通过扩展方法来判断是否包含某个值,例如:
/// <summary>
/// 获取枚举值的所持有的标志位
/// Retrieves all individual flag values contained in a Flag type enumeration.
/// </summary>
/// <param name="value">枚举值</param>
/// <typeparam name="T">Flag类型</typeparam>
/// <returns>所有元素的集合</returns>
public static IEnumerable<T> GetValues<T>(this T value) where T : Enum
{
return Enum.GetValues(value.GetType()).Cast<T>().Where(v => value.HasFlag(v));
}
配合 switch 语句使用
如果想要配合 switch 语句使用,实现只命中 Flags 值中的第一个判断时,可以使用 C# 7 引入的 switch pattern matching:
CardDeckSettings settings = CardDeckSettings.SingleDeck | CardDeckSettings.LargePictures;
switch(settings)
{
case var _ when settings.HasFlag(CardDeckSettings.SingleDeck):
Console.WriteLine("Single Deck");
break;
case var _ when settings.HasFlag(CardDeckSettings.LargePictures):
Console.WriteLine("Large Pictures");
break;
case var _ when settings.HasFlag(CardDeckSettings.FancyNumbers):
Console.WriteLine("Fancy Numbers");
break;
case var _ when settings.HasFlag(CardDeckSettings.Shuffle):
Console.WriteLine("Shuffle");
break;
}
// 输出:Single Deck
如果想要命中所有的 Flags 值,最简单的方法是做 if 判断:
if(settings.HasFlag(CardDeckSettings.SingleDeck)) Console.WriteLine("Single Deck");
if(settings.HasFlag(CardDeckSettings.LargePictures)) Console.WriteLine("Large Pictures");
if(settings.HasFlag(CardDeckSettings.FancyNumbers)) Console.WriteLine("Fancy Numbers");
if(settings.HasFlag(CardDeckSettings.Shuffle)) Console.WriteLine("Shuffle");
// 输出:Single Deck
// 输出:Large Pictures
ref:
- C# 7.0 Pattern Matching
- c# - Switch on Enum (with Flags attribute) without declaring every possible combination? - Stack Overflow
注意事项
对于大量的枚举值,当超过默认int
所代表的 32 位范围后,可以使用ulong
来代替,注意将 1 替换为 1UL,比如:
[Flags]
public enum EventType : ulong
{
f1= 1,
f2= 1 << 1,
f3= 1 << 2,
......
f... = 1 << 30,
f... = 1UL << 31,
f... = 1UL << 32
}