What does the [Flags] Enum Attribute mean in C#?

前端 未结 13 2277
慢半拍i
慢半拍i 2020-11-21 04:21

From time to time I see an enum like the following:

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


        
相关标签:
13条回答
  • 2020-11-21 04:50

    Apologies if someone already noticed this scenario. A perfect example of flags we can see in reflection. Yes Binding Flags ENUM.

    [System.Flags]
    [System.Runtime.InteropServices.ComVisible(true)]
    [System.Serializable]
    public enum BindingFlags
    

    Usage

    // BindingFlags.InvokeMethod
    // Call a static method.
    Type t = typeof (TestClass);
    
    Console.WriteLine();
    Console.WriteLine("Invoking a static method.");
    Console.WriteLine("-------------------------");
    t.InvokeMember ("SayHello", BindingFlags.InvokeMethod | BindingFlags.Public | 
        BindingFlags.Static, null, null, new object [] {});
    
    0 讨论(0)
  • 2020-11-21 04:52

    The [Flags] attribute should be used whenever the enumerable represents a collection of possible values, rather than a single value. Such collections are often used with bitwise operators, for example:

    var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;
    

    Note that the [Flags] attribute doesn't enable this by itself - all it does is allow a nice representation by the .ToString() method:

    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"
    

    It is also important to note that [Flags] does not automatically make the enum values powers of two. If you omit the numeric values, the enum will not work as one might expect in bitwise operations, because by default the values start with 0 and increment.

    Incorrect declaration:

    [Flags]
    public enum MyColors
    {
        Yellow,  // 0
        Green,   // 1
        Red,     // 2
        Blue     // 3
    }
    

    The values, if declared this way, will be Yellow = 0, Green = 1, Red = 2, Blue = 3. This will render it useless as flags.

    Here's an example of a correct declaration:

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

    To retrieve the distinct values in your property, one can do this:

    if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
    {
        // Yellow is allowed...
    }
    

    or prior to .NET 4:

    if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
    {
        // Yellow is allowed...
    }
    
    if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
    {
        // Green is allowed...
    }    
    

    Under the covers

    This works because you used powers of two in your enumeration. Under the covers, your enumeration values look like this in binary ones and zeros:

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

    Similarly, after you've set your property AllowedColors to Red, Green and Blue using the binary bitwise OR | operator, AllowedColors looks like this:

    myProperties.AllowedColors: 00001110
    

    So when you retrieve the value you are actually performing bitwise AND & on the values:

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

    The None = 0 value

    And regarding the use of 0 in your enumeration, quoting from MSDN:

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

    Use None as the name of the flag enumerated constant whose value is zero. You cannot use the None enumerated constant in a bitwise AND operation to test for a flag because the result is always zero. However, you can perform a logical, not a bitwise, comparison between the numeric value and the None enumerated constant to determine whether any bits in the numeric value are set.

    You can find more info about the flags attribute and its usage at msdn and designing flags at msdn

    0 讨论(0)
  • 2020-11-21 04:54

    When working with flags I often declare additional None and All items. These are helpful to check whether all flags are set or no flag is set.

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

    Usage:

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

     
    Update 2019-10:

    Since C# 7.0 you can use binary literals, which are probably more intuitive to read:

    [Flags] 
    enum SuitsFlags { 
    
        None =     0b0000,
    
        Spades =   0b0001, 
        Clubs =    0b0010, 
        Diamonds = 0b0100, 
        Hearts =   0b1000,
    
        All =      0b1111
    
    }
    
    0 讨论(0)
  • 2020-11-21 04:56

    There's something overly verbose to me about the if ((x & y) == y)... construct, especially if x AND y are both compound sets of flags and you only want to know if there's any overlap.

    In this case, all you really need to know is if there's a non-zero value[1] after you've bitmasked.

    [1] See Jaime's comment. If we were authentically bitmasking, we'd only need to check that the result was positive. But since enums can be negative, even, strangely, when combined with the [Flags] attribute, it's defensive to code for != 0 rather than > 0.

    Building off of @andnil's setup...

    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();
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 04:58

    Please see the following for an example which shows the declaration and potential usage:

    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");
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 04:58

    To add Mode.Write:

    Mode = Mode | Mode.Write;
    
    0 讨论(0)
提交回复
热议问题