在c#中[Flags] Enum属性是什么意思?

时间:2022-07-25 16:39:57

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

我时不时会看到这样一个enum:

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

I don't understand what exactly the [Flags]-attribute does.

我不理解[Flags]-属性的作用。

Anyone have a good explanation or example they could post?

谁能给出一个好的解释或例子?

10 个解决方案

#1


1693  

The flags attribute should be used whenever the enumerable represents a collection of flags, rather than a single value. Such collections are usually manipulated using bitwise operators, for example:

当枚举表示一个标志集合时,应该使用flags属性,而不是单个值。这样的集合通常使用位运算符进行操作,例如:

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

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

注意,[Flags]本身并没有改变这一点——它所做的只是通过.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"

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.

同样重要的是要注意,[Flags]并不会自动使enum的值为2。如果省略了数字值,枚举将不会像在位操作中期望的那样工作,因为默认值从0开始,然后递增。

Incorrect declaration:

不正确的声明:

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

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

值,如果这样声明,将是黄色= 0,绿色= 1,红色= 2,蓝色= 3。这将使它不能作为标志使用。

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 & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow has been set...
}

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

or, in .NET 4 and later:

或者,在。net 4和之后:

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

Under the covers

在幕后

This works because you previously used powers of two in your enumeration. Under the covers, your enumeration values look like this (presented as bytes, which has 8 bits which can be 1's or 0's)

这是因为您以前在枚举中使用了两个幂。在封面下,您的枚举值看起来像这样(以字节表示,它有8位,可以是1或0)

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

Likewise, after you've set your property AllowedColors to Red, Green and Blue (which values where OR'ed by the pipe |), AllowedColors looks like this

同样地,当您将您的属性设置为红色、绿色和蓝色(该值是由管道|所指定的值)时,AllowedColors是这样的。

myProperties.AllowedColors: 00001110

So when you retrieve the value you are actually bitwise AND'ing the values

因此,当你检索值时,你实际上是位和值。

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

The None = 0 value

None = 0值。

And regarding use 0 in you enumeration, quoting from msdn:

关于在您的枚举中使用0,引用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.

使用None作为标志枚举常量的名称,其值为零。您不能在一个位和操作中使用没有枚举的常量来测试标志,因为结果总是为零。但是,您可以执行一个逻辑的,而不是位的,数值和没有枚举的常量之间的比较,以确定数值中是否设置了任何位。

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

您可以在msdn上找到关于flags属性及其用法的更多信息,并在msdn上设计标志。

#2


656  

You can also do this

你也可以这样做。

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

I find the bit-shifting easier than typing 4,8,16,32 and so on. It has no impact on your code because it's all done at compile time

我发现比特转换比键入4、8、16、32等等更容易。它对代码没有影响,因为它都是在编译时完成的。

#3


82  

Combining answers https://*.com/a/8462/1037948 (declaration via bit-shifting) and https://*.com/a/9117/1037948 (using combinations in declaration) you can bit-shift previous values rather than using numbers. Not necessarily recommending it, but just pointing out you can.

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

Rather than:

而不是:

[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,
}

You can declare

您可以声明

[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:

LinqPad确认:

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

Results in:

结果:

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

#4


43  

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");
            }
        }
    }
}

#5


30  

I asked recently about something similar.

我最近问了一些类似的问题。

If you use flags you can add an extension method to enums to make checking the contained flags easier (see post for detail)

如果您使用标志,您可以向枚举中添加一个扩展方法,以便更容易地检查所包含的标记(请参阅post以获取详细信息)

This allows you to do:

这允许你做:

[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,
    ...
}

Then you can do:

然后你可以做:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

I find this easier to read than the most ways of checking the included flags.

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

#6


18  

@Nidonocu

@Nidonocu

To add another flag to an existing set of values, use the OR assignment operator.

若要将另一个标志添加到现有的值集,请使用或赋值操作符。

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

#7


15  

To add Mode.Write:

添加Mode.Write:

Mode = Mode | Mode.Write;

#8


13  

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.

关于if ((x & y) == y)有一些过于冗长的东西。构造,特别是如果x和y都是复合的标志,你只想知道是否有重叠。

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

在这种情况下,您真正需要知道的是,如果您的位掩码之后有一个非零值[1]。

[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.

[1]看到杰米的评论。如果我们是真正的位掩,我们只需要检查结果是正的。但是,由于enums可以是负数,甚至奇怪的是,当与[Flags]属性相结合时,它是防御性的,代码为!= 0而不是> 0。

Building off of @andnil's setup...

建立@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();
        }
    }
}

#9


11  

In extension to the accepted answer, in C#7 the enum flags can be written using binary literals:

对于已被接受的答案,在c# 7中,enum标志可以用二进制文字书写:

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

I think this representation makes it clear how the flags work under the covers.

我认为这个表述清楚地说明了旗子是如何在封面下工作的。

#10


10  

Flags allow you to use bitmasking inside your enumeration. This allows you to combine enumeration values, while retaining which ones are specified.

标志允许您在枚举中使用位屏蔽。这允许您结合枚举值,同时保留指定的值。

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

#1


1693  

The flags attribute should be used whenever the enumerable represents a collection of flags, rather than a single value. Such collections are usually manipulated using bitwise operators, for example:

当枚举表示一个标志集合时,应该使用flags属性,而不是单个值。这样的集合通常使用位运算符进行操作,例如:

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

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

注意,[Flags]本身并没有改变这一点——它所做的只是通过.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"

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.

同样重要的是要注意,[Flags]并不会自动使enum的值为2。如果省略了数字值,枚举将不会像在位操作中期望的那样工作,因为默认值从0开始,然后递增。

Incorrect declaration:

不正确的声明:

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

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

值,如果这样声明,将是黄色= 0,绿色= 1,红色= 2,蓝色= 3。这将使它不能作为标志使用。

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 & MyColor.Yellow) == MyColor.Yellow)
{
    // Yellow has been set...
}

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

or, in .NET 4 and later:

或者,在。net 4和之后:

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

Under the covers

在幕后

This works because you previously used powers of two in your enumeration. Under the covers, your enumeration values look like this (presented as bytes, which has 8 bits which can be 1's or 0's)

这是因为您以前在枚举中使用了两个幂。在封面下,您的枚举值看起来像这样(以字节表示,它有8位,可以是1或0)

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

Likewise, after you've set your property AllowedColors to Red, Green and Blue (which values where OR'ed by the pipe |), AllowedColors looks like this

同样地,当您将您的属性设置为红色、绿色和蓝色(该值是由管道|所指定的值)时,AllowedColors是这样的。

myProperties.AllowedColors: 00001110

So when you retrieve the value you are actually bitwise AND'ing the values

因此,当你检索值时,你实际上是位和值。

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

The None = 0 value

None = 0值。

And regarding use 0 in you enumeration, quoting from msdn:

关于在您的枚举中使用0,引用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.

使用None作为标志枚举常量的名称,其值为零。您不能在一个位和操作中使用没有枚举的常量来测试标志,因为结果总是为零。但是,您可以执行一个逻辑的,而不是位的,数值和没有枚举的常量之间的比较,以确定数值中是否设置了任何位。

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

您可以在msdn上找到关于flags属性及其用法的更多信息,并在msdn上设计标志。

#2


656  

You can also do this

你也可以这样做。

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

I find the bit-shifting easier than typing 4,8,16,32 and so on. It has no impact on your code because it's all done at compile time

我发现比特转换比键入4、8、16、32等等更容易。它对代码没有影响,因为它都是在编译时完成的。

#3


82  

Combining answers https://*.com/a/8462/1037948 (declaration via bit-shifting) and https://*.com/a/9117/1037948 (using combinations in declaration) you can bit-shift previous values rather than using numbers. Not necessarily recommending it, but just pointing out you can.

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

Rather than:

而不是:

[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,
}

You can declare

您可以声明

[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:

LinqPad确认:

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

Results in:

结果:

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

#4


43  

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");
            }
        }
    }
}

#5


30  

I asked recently about something similar.

我最近问了一些类似的问题。

If you use flags you can add an extension method to enums to make checking the contained flags easier (see post for detail)

如果您使用标志,您可以向枚举中添加一个扩展方法,以便更容易地检查所包含的标记(请参阅post以获取详细信息)

This allows you to do:

这允许你做:

[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,
    ...
}

Then you can do:

然后你可以做:

PossibleOptions opt = PossibleOptions.OptionOneTwoAndThree 

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

I find this easier to read than the most ways of checking the included flags.

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

#6


18  

@Nidonocu

@Nidonocu

To add another flag to an existing set of values, use the OR assignment operator.

若要将另一个标志添加到现有的值集,请使用或赋值操作符。

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

#7


15  

To add Mode.Write:

添加Mode.Write:

Mode = Mode | Mode.Write;

#8


13  

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.

关于if ((x & y) == y)有一些过于冗长的东西。构造,特别是如果x和y都是复合的标志,你只想知道是否有重叠。

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

在这种情况下,您真正需要知道的是,如果您的位掩码之后有一个非零值[1]。

[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.

[1]看到杰米的评论。如果我们是真正的位掩,我们只需要检查结果是正的。但是,由于enums可以是负数,甚至奇怪的是,当与[Flags]属性相结合时,它是防御性的,代码为!= 0而不是> 0。

Building off of @andnil's setup...

建立@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();
        }
    }
}

#9


11  

In extension to the accepted answer, in C#7 the enum flags can be written using binary literals:

对于已被接受的答案,在c# 7中,enum标志可以用二进制文字书写:

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

I think this representation makes it clear how the flags work under the covers.

我认为这个表述清楚地说明了旗子是如何在封面下工作的。

#10


10  

Flags allow you to use bitmasking inside your enumeration. This allows you to combine enumeration values, while retaining which ones are specified.

标志允许您在枚举中使用位屏蔽。这允许您结合枚举值,同时保留指定的值。

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