C#学习笔记 -- 枚举与位标志

位标志

开发者使用单个字的不同位表示一组开关的紧凑方法, 称为位标志, 可以使用枚举来实现它, 步骤如下

  1. 确定需要多少个位标志, 并选择一种有足够多位的无符号类型(uint, ulong)来保存他

  2. 确定每个个位位置代表什么, 并给他们一个名称. 声明一个选中的整数类型的枚举, 每个成员有一个位位置表示

  3. 使用按位或 (OR)| 运算符在持有该位标志的字中设置适当的位

  4. 使用按位与(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还可以检查多个位标志

    1. 先创建测试字实例

    2. 将测试实例传入HasFlag

    CardDeckSetting testFlags = CardDeckSetting.FancyNumbers | CardDeckSetting.Animation;
    bool useAnimationAndFanceyNumbers = ops.HasFlag(testFlags);
  • 使用按位与检查多个位标志

    1. 把标志字段和位标志相与

    2. 与位标志比较

    3. 如果原始标志字中设置这个位, 那么与操作的结果将和位标志具有相同的位

    //使用按位或运算符设置需要的位
    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);
    }
}

 

posted on 2023-05-26 00:28  老菜农  阅读(228)  评论(0编辑  收藏  举报

导航