首页 文章

[Flags]枚举属性在C#中意味着什么?

提问于
浏览
1206

我不时会看到如下的枚举:

[Flags]
public enum Options 
{
    None    = 0,
    Option1 = 1,
    Option2 = 2,
    Option3 = 4,
    Option4 = 8
}

我不明白 [Flags] -attribute到底是做什么的 .

任何人都可以发布一个很好的解释或示例?

11 回答

  • 11

    @Nidonocu

    要向现有值集添加另一个标志,请使用OR赋值运算符 .

    Mode = Mode.Read;
    //Add Mode.Write
    Mode |= Mode.Write;
    Assert.True(((Mode & Mode.Write) == Mode.Write)
      && ((Mode & Mode.Read) == Mode.Read)));
    
  • 1858

    要添加 Mode.Write

    Mode = Mode | Mode.Write;
    
  • 1

    标志允许您在枚举中使用位掩码 . 这允许您组合枚举值,同时保留指定的枚举值 .

    [Flags]
    public enum DashboardItemPresentationProperties : long
    {
        None = 0,
        HideCollapse = 1,
        HideDelete = 2,
        HideEdit = 4,
        HideOpenInNewWindow = 8,
        HideResetSource = 16,
        HideMenu = 32
    }
    
  • 90

    请参阅以下示例,其中显示了声明和潜在用法:

    namespace Flags
    {
        class Program
        {
            [Flags]
            public enum MyFlags : short
            {
                Foo = 0x1,
                Bar = 0x2,
                Baz = 0x4
            }
    
            static void Main(string[] args)
            {
                MyFlags fooBar = MyFlags.Foo | MyFlags.Bar;
    
                if ((fooBar & MyFlags.Foo) == MyFlags.Foo)
                {
                    Console.WriteLine("Item has Foo flag set");
                }
            }
        }
    }
    
  • 31

    在接受答案的扩展中,在C#7中,可以使用二进制文字来编写枚举标志:

    [Flags]
    public enum MyColors
    {
        None   = 0b0000,
        Yellow = 0b0001,
        Green  = 0b0010,
        Red    = 0b0100,
        Blue   = 0b1000
    }
    

    我认为这种表述清楚地说明了旗帜如何在幕后工作 .

  • 13

    你也可以这样做

    [Flags]
    public enum MyEnum
    {
        None   = 0,
        First  = 1 << 0,
        Second = 1 << 1,
        Third  = 1 << 2,
        Fourth = 1 << 3
    }
    

    我发现位移比键入4,8,16,32更容易,等等 . 它对您的代码没有影响,因为它们都是在编译时完成的

  • 19

    结合答案https://stackoverflow.com/a/8462/1037948(通过位移声明)和https://stackoverflow.com/a/9117/1037948(使用声明中的组合),您可以对先前的值进行位移,而不是使用数字 . 不一定推荐它,但只是指出你可以 .

    而不是:

    [Flags]
    public enum Options : byte
    {
        None    = 0,
        One     = 1 << 0,   // 1
        Two     = 1 << 1,   // 2
        Three   = 1 << 2,   // 4
        Four    = 1 << 3,   // 8
    
        // combinations
        OneAndTwo = One | Two,
        OneTwoAndThree = One | Two | Three,
    }
    

    你可以申报

    [Flags]
    public enum Options : byte
    {
        None    = 0,
        One     = 1 << 0,       // 1
        // now that value 1 is available, start shifting from there
        Two     = One << 1,     // 2
        Three   = Two << 1,     // 4
        Four    = Three << 1,   // 8
    
        // same combinations
        OneAndTwo = One | Two,
        OneTwoAndThree = One | Two | Three,
    }
    

    Confirming with LinqPad:

    foreach(var e in Enum.GetValues(typeof(Options))) {
        string.Format("{0} = {1}", e.ToString(), (byte)e).Dump();
    }
    

    结果是:

    None = 0
    One = 1
    Two = 2
    OneAndTwo = 3
    Three = 4
    OneTwoAndThree = 7
    Four = 8
    
  • 709

    使用标志时,我经常声明其他无和所有项目 . 这些有助于检查是否设置了所有标志或是否设置了标志 .

    [Flags] 
    enum SuitsFlags { 
    
        None =     0,
    
        Spades =   1 << 0, 
        Clubs =    1 << 1, 
        Diamonds = 1 << 2, 
        Hearts =   1 << 3,
    
        All =      ~(~0 << 4)
    
    }
    

    用法:

    Spades | Clubs | Diamonds | Hearts == All  // true
    Spades & Clubs == None  // true
    
  • 17

    关于 if ((x & y) == y)... 构造,我有一些过于冗长的事情,特别是如果 xy 都是复合的标志集,你只想知道是否有 any 重叠 .

    在这种情况下,您真正需要知道的是 if there's a non-zero value[1] after you've bitmasked .

    [1]见Jaime的评论 . 如果我们是真正的位掩码,我们只需要检查结果是否为正 . 但是,由于枚举可能是负面的,甚至是奇怪的,当与[Flags]属性结合使用时,代码为!= 0而不是> 0是守卫的 .

    Build @ andnil的设置......

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace BitFlagPlay
    {
        class Program
        {
            [Flags]
            public enum MyColor
            {
                Yellow = 0x01,
                Green = 0x02,
                Red = 0x04,
                Blue = 0x08
            }
    
            static void Main(string[] args)
            {
                var myColor = MyColor.Yellow | MyColor.Blue;
                var acceptableColors = MyColor.Yellow | MyColor.Red;
    
                Console.WriteLine((myColor & MyColor.Blue) != 0);     // True
                Console.WriteLine((myColor & MyColor.Red) != 0);      // False                
                Console.WriteLine((myColor & acceptableColors) != 0); // True
                // ... though only Yellow is shared.
    
                Console.WriteLine((myColor & MyColor.Green) != 0);    // Wait a minute... ;^D
    
                Console.Read();
            }
        }
    }
    
  • 45

    asked recently关于类似的事情 .

    如果使用标记,则可以向枚举添加扩展方法,以便更轻松地检查包含的标记(有关详细信息,请参阅帖子)

    这允许你这样做:

    [Flags]
    public enum PossibleOptions : byte
    {
        None = 0,
        OptionOne = 1,
        OptionTwo = 2,
        OptionThree = 4,
        OptionFour = 8,
    
        //combinations can be in the enum too
        OptionOneAndTwo = OptionOne | OptionTwo,
        OptionOneTwoAndThree = OptionOne | OptionTwo | OptionThree,
        ...
    }
    

    然后你可以这样做:

    PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 
    
    if( opt.IsSet( PossibleOptions.OptionOne ) ) {
        //optionOne is one of those set
    }
    

    我发现这比检查包含的标志的大多数方法更容易阅读 .

  • 20

    只要enumerable表示标志集合而不是单个值,就应该使用flags属性 . 这些集合通常使用按位运算符进行操作,例如:

    myProperties.AllowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;
    

    请注意 [Flags] 本身不会更改此 at all - 它所做的只是通过 .ToString() 方法启用一个很好的表示:

    enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
    [Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
    
    ...
    
    var str1 = (Suits.Spades | Suits.Diamonds).ToString();
               // "5"
    var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
               // "Spades, Diamonds"
    

    同样重要的是要注意 [Flags] does not 自动使枚举值的幂为2 . 如果省略数值,则枚举将不会像在按位运算中所期望的那样工作,因为默认情况下,值以0和递增开头 .

    声明不正确:

    [Flags]
    public enum MyColors
    {
        Yellow,
        Green,
        Red,
        Blue
    }
    

    如果以这种方式声明,则值将为Yellow = 0,Green = 1,Red = 2,Blue = 3.这将使其无法用作标志 .

    以下是正确声明的示例:

    [Flags]
    public enum MyColors
    {
        Yellow = 1,
        Green = 2,
        Red = 4,
        Blue = 8
    }
    

    要检索属性中的不同值,可以执行以下操作:

    if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
    {
        // Yellow has been set...
    }
    
    if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
    {
        // Green has been set...
    }
    

    或者,在.NET 4及更高版本中:

    if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
    {
        // Yellow has been set...
    }
    

    Under the covers

    这是有效的,因为您以前在枚举中使用了2的幂 . 在封面下,您的枚举值看起来像这样(以字节为单位,有8位,可以是1或0)

    Yellow: 00000001
     Green:  00000010
     Red:    00000100
     Blue:   00001000
    

    同样,在将属性AllowedColors设置为Red,Green和Blue(其值为管道的OR值)之后,AllowedColors看起来像这样

    myProperties.AllowedColors: 00001110
    

    因此,当您检索该值时,您实际上是按位和值

    myProperties.AllowedColors: 00001110
                 MyColor.Green: 00000010
                 -----------------------
                                00000010 // Hey, this is the same as MyColor.Green!
    

    The None = 0 value

    关于在枚举中使用0,引用msdn:

    [Flags]
    public enum MyColors
    {
        None = 0,
        ....
    }
    

    使用None作为标志枚举常量的名称,其值为零 . 您不能在按位AND运算中使用None枚举常量来测试标志,因为结果始终为零 . 但是,您可以在数值和None枚举常量之间执行逻辑而非按位比较,以确定是否设置了数值中的任何位 .

    您可以在msdndesigning flags at msdn找到有关flags属性及其用法的更多信息 .

相关问题