[Flags] 枚举属性在 C# 中是什么意思?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8447/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-07-31 17:09:55  来源:igfitidea点击:

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

提问by Brian Leahy

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

不时我会看到一个像下面这样的枚举:

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

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

采纳答案by andnil

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:

[Flags]只要可枚举表示可能值的集合,而不是单个值,就应该使用该属性。此类集合通常与按位运算符一起使用,例如:

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

Note that the [Flags]attribute doesn'tenable this by itself - all it does is allow 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 notautomatically 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]不会自动使枚举值成为 2 的幂。如果省略数字值,枚举将不会像按位运算预期的那样工作,因为默认情况下,这些值从 0 开始并递增。

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.

如果以这种方式声明,这些值将是 Yellow = 0、Green = 1、Red = 2、Blue = 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.HasFlag(MyColor.Yellow))
{
    // Yellow is allowed...
}

or prior to .NET 4:

或在 .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:

这是有效的,因为您在枚举中使用了 2 的幂。在幕后,您的枚举值在二进制 1 和 0 中如下所示:

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

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

同样,在使用二进制按位 OR运算符将AllowedColors属性设置为 Red、Green 和 Blue 后|AllowedColors如下所示:

myProperties.AllowedColors: 00001110

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

因此,当您检索该值时,您实际上是在&对这些值执行按位 AND 运算:

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

The None = 0 value

None = 0 值

And regarding the use of 0in your 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 作为值为零的标志枚举常量的名称。您不能在按位 AND 运算中使用 None 枚举常量来测试标志,因为结果始终为零。但是,您可以在数值和 None 枚举常量之间执行逻辑比较,而不是按位比较,以确定是否设置了数值中的任何位。

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

您可以找到有关标志属性的详细信息,并在其使用MSDNMSDN上的标志设计

回答by Jay Mooney

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
}

回答by OJ.

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

回答by Orion Edwards

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 等更容易。它对您的代码没有影响,因为它都是在编译时完成的

回答by David Wengier

To add Mode.Write:

添加Mode.Write

Mode = Mode | Mode.Write;

回答by steve_c

@Nidonocu

@尼多诺库

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

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

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

回答by Keith

I asked recentlyabout 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)

如果您使用标志,您可以向枚举添加扩展方法,以便更轻松地检查包含的标志(详情请参阅帖子)

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.

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

回答by ruffin

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

对我来说,这个if ((x & y) == y)...构造有些过于冗长,特别是如果xANDy都是复合标志集,而您只想知道是否有任何重叠。

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 != 0rather than > 0.

[1] 见 Jaime 的评论。如果我们是真正的bitmasking,我们只需要检查结果是否为正。但由于enum可以均为负数,甚至是奇怪的是,当联合[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();
        }
    }
}

回答by drzaus

Combining answers https://stackoverflow.com/a/8462/1037948(declaration via bit-shifting) and https://stackoverflow.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://stackoverflow.com/a/8462/1037948(通过位移位声明)和https://stackoverflow.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

回答by Thorkil Holm-Jacobsen

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

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

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

我认为这种表示方式清楚地说明了这些标志是如何在幕后工作