C#学习笔记 -- 枚举与位标志
开发者使用单个字的不同位表示一组开关的紧凑方法, 称为位标志, 可以使用枚举来实现它, 步骤如下
-
确定需要多少个位标志, 并选择一种有足够多位的无符号类型(uint, ulong)来保存他
-
确定每个个位位置代表什么, 并给他们一个名称. 声明一个选中的整数类型的枚举, 每个成员有一个位位置表示
-
使用按位或 (OR)
|
运算符在持有该位标志的字中设置适当的位 -
使用按位与(AND)
&
运算符或HasFlag方法检查是否设置了特定位标志
例子
-
成员有表示二进制选项的名称
-
每个选项由一个特定的位位置表示, 位位置持有一个0或1
-
因为一个位标志表示一个或开或关的位, 所以别用0作为成员值, 因为它已经有了一个含义: 所有的位标志都是关
-
-
在16进制表示中, 每个16进制数字用4位表示, 由于位模式和16进制表示法之间的这种直接联系, 所以在处理位模式时, 经常使用16进制而不是10进制表示法
-
C#7.0开始, 可以使用2进制
-
使用Flags特性装饰枚举实际上是不必要的, 但可以带来一些额外的便利
[Flags]
enum CardDeckSetting : uint
{
SingleDeck = 0x01, //位0
LargePictures = 0x02, //位1
FancyNumbers = 0x04, //位2
Animation = 0x08 //位3
}
-
要创建一个带有适当的位标志的字, 需要声明一个该枚举类型的变量, 并使用按位或运算符设置需要的位
CardDeckSetting ops = CardDeckSetting.SingleDeck | CardDeckSetting.FancyNumbers | CardDeckSetting.Animation;
-
判断标志字是否包含特定的标志集, 可以使用枚举类型的HasFlag方法, 如果设置了指定的位标志, HasFlag返回true, 否则返回false
bool useFancyNumbers = ops.HasFlag(CardDeckSetting.FancyNumbers);
-
HasFlag还可以检查多个位标志
-
先创建测试字实例
-
将测试实例传入HasFlag
CardDeckSetting testFlags = CardDeckSetting.FancyNumbers | CardDeckSetting.Animation; bool useAnimationAndFanceyNumbers = ops.HasFlag(testFlags);
-
-
使用按位与检查多个位标志
-
把标志字段和位标志相与
-
与位标志比较
-
如果原始标志字中设置这个位, 那么与操作的结果将和位标志具有相同的位
//使用按位或运算符设置需要的位 CardDeckSetting ops = CardDeckSetting.SingleDeck | CardDeckSetting.FancyNumbers | CardDeckSetting.Animation; //判断标志字是否包含特定的标志集, 可以使用枚举类型的HasFlag方法, 如果设置了指定的位标志, HasFlag返回true, 否则返回false bool useFancyNumbers = ops.HasFlag(CardDeckSetting.FancyNumbers); //测试位集, 用于传入HasFlag, 检查位集中是否有此测试集 CardDeckSetting testFlags = CardDeckSetting.FancyNumbers | CardDeckSetting.Animation; //HasFlag检测多个位标志 bool useAnimationAndFanceyNumbers = ops.HasFlag(testFlags); //按位与检测1个位标志 bool useFancyNumbersWithAnd = (ops & CardDeckSetting.FancyNumbers) == CardDeckSetting.FancyNumbers; //检测多个位标志, 使用按位与, 将两个标志位弄成一个测试位集 CardDeckSetting testFlagsWithAnd = CardDeckSetting.FancyNumbers | CardDeckSetting.Animation; bool useAnimationAndFanceyNumbersWithAnd = (ops & testFlagsWithAnd) == testFlagsWithAnd;
-
(1)Flags特性
[Flags]
enum CardDeckSetting : uint
{
...
}
Flags特性不会改变计算结果, 却提供了一些方便的特性
-
它通知编译器, 对象浏览器以及其他查看这段代码的工具, 该枚举的成员不仅可以用作单独的值, 还可以组合成位标志
-
这样浏览器就可以更恰当地解释该枚举类型的变量
-
允许枚举的ToString方法为位标志的值提供更多格式化的信息
-
ToString方法以一个枚举值为参数, 将其与枚举的常量成员相比较, 如果某个成员相匹配, ToString返回该成员的字符串名称
-
//不使用[Flags]
enum CardDeckSetting : uint
{
SingleDeck = 0x01, //位0
LargePictures = 0x02, //位1
FancyNumbers = 0x04, //位2
Animation = 0x08 //位3
}
static void Main(string[] args)
{
CardDeckSetting ops = CardDeckSetting.FancyNumbers;
Console.WriteLine(ops.ToString()); //不加Flags FancyNumbers / 加Flags FancyNumbers
ops = CardDeckSetting.FancyNumbers | CardDeckSetting.Animation;
Console.WriteLine(ops.ToString()); //不加Flags 12 / 加Flags FancyNumbers, Animation
}
Main做了如下事情
-
创建枚举类型CardDeckSetting的变量, 设置一个位标志, 并打印变量的值(FancyNumbers)
-
位变量赋一个包含两个位标志的新值, 并打印它的值
第二次赋值结果的值12是ops的值. FancyNumbers = 4 | Animation = 8
得到结果为12, ToString就去找那个枚举成员有值12, 没找到所有打印出12
如果在枚举声明Flags特性, 他会告知ToString方法位可以分开考虑, 查找值时, ToString就会发现12对应分开的两个位标志
(2)使用位标志的示例
[Flags]
enum CardDeckSetting : uint
{
SingleDeck = 0x01, //位0
LargePictures = 0x02, //位1
FancyNumbers = 0x04, //位2
Animation = 0x08 //位3
}
class MyClass
{
bool UseSinleDeck = false;
bool UseLargePictures = false;
bool UseFancyNumbers = false;
bool UseAnimation = false;
bool UseAnimationAndFancyNumbers = false;
//使用HasFlag设置配置
public void SetOptions(CardDeckSetting ops)
{
UseSinleDeck = ops.HasFlag(SingleDeck);
UseLargePictures = ops.HasFlag(LargePictures);
UseFancyNumbers = ops.HasFlag(FancyNumbers);
UseAnimation = ops.HasFlag(Animation);
CardDeckSetting animationAndFancyNumber = Animation | FancyNumbers;
UseAnimationAndFancyNumbers = ops.HasFlag(animationAndFancyNumber);
}
}