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

发布于 2024-07-05 02:19:32 字数 238 浏览 7 评论 0原文

有时我会看到如下所示的枚举:

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

我不明白 [Flags] 属性到底是做什么的。

有人可以发布一个很好的解释或例子吗?

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.

Anyone have a good explanation or example they could post?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

随梦而飞# 2024-07-12 02:19:32

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

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

请注意,[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"

还需要注意的是,[Flags] 不会自动使枚举值成为 2 的幂。 如果省略数值,则枚举将无法按位运算中的方式工作,因为默认情况下,值从 0 开始并递增。

错误的声明:

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

如果以这种方式声明,这些值将为黄色 = 0、绿色 = 1、红色 = 2、蓝色 = 3。这将使其作为标志无用。

下面是正确声明的一个示例:

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

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

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

或在 .NET 4 之前:

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

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

在幕后

这之所以有效,是因为您在枚举中使用了 2 的幂。 在幕后,您的枚举值在二进制 1 和 0 中看起来像这样:

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

同样,在使用二进制按位 OR |AllowedColors 设置为 Red、Green 和 Blue 后code> 运算符,AllowedColors 看起来像这样:

myProperties.AllowedColors: 00001110

因此,当您检索值时,您实际上是在对值执行按位 AND &

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

None = 0 值< /strong>

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

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

使用 None 作为值为 0 的标志枚举常量的名称。 您不能在按位 AND 运算中使用 None 枚举常量来测试标志,因为结果始终为零。但是,您可以在数值与None 枚举常量,用于确定是否设置了数值中的任何位。

您可以在 msdn在 msdn 上设计标志

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

巴黎夜雨 2024-07-12 02:19:32
  • 当可枚举值表示一组值时,将使用标志
    枚举成员。

  • 这里我们使用按位运算符| 和&

  • 示例

    <前><代码> [标志]
    公共枚举 Sides { 左 = 0,右 = 1,顶部 = 2,底部 = 3 }

    边左右 = Sides.Left | 侧面.右;
    Console.WriteLine(leftRight);//左、右

    字符串 stringValue = leftRight.ToString();
    Console.WriteLine(stringValue);//左、右

    边 s = Sides.Left;
    s |= Sides.Right;
    Console.WriteLine(s);//左、右

    s ^= Sides.Right; // 切换 Sides.Right
    Console.WriteLine(s); //左边

  • Flags are used when an enumerable value represents a collection of
    enum members.

  • here we use bitwise operators, | and &

  • Example

                 [Flags]
                 public enum Sides { Left=0, Right=1, Top=2, Bottom=3 }
    
                 Sides leftRight = Sides.Left | Sides.Right;
                 Console.WriteLine (leftRight);//Left, Right
    
                 string stringValue = leftRight.ToString();
                 Console.WriteLine (stringValue);//Left, Right
    
                 Sides s = Sides.Left;
                 s |= Sides.Right;
                 Console.WriteLine (s);//Left, Right
    
                 s ^= Sides.Right; // Toggles Sides.Right
                 Console.WriteLine (s); //Left
    
权谋诡计 2024-07-12 02:19:32

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

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

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
}
幸福%小乖 2024-07-12 02:19:32

如果有人已经注意到这种情况,我们深表歉意。 我们可以在反射中看到一个完美的标志示例。 是 绑定标志 ENUM

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

使用

// 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 [] {});

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 [] {});
西瑶 2024-07-12 02:19:32

对于我来说,关于 if ((x & y) == y)... 构造有些过于冗长,尤其是 if x AND y 都是复合标志集,您只想知道是否有任何重叠。

在这种情况下,您真正​​需要知道的是位屏蔽后是否存在非零值[1]

[1] 请参阅 Jaime 的评论。 如果我们真的位掩码,我们会
只需要检查结果是否呈阳性。 但自从enums
[Flags] 结合使用时,甚至可以为负数,甚至很奇怪]
属性
,
编写 != 0 而不是 > 是防御性的。 0.

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

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();
        }
    }
}
濫情▎り 2024-07-12 02:19:32

添加Mode.Write

Mode = Mode | Mode.Write;

To add Mode.Write:

Mode = Mode | Mode.Write;
jJeQQOZ5 2024-07-12 02:19:32

定义问题

让我们定义一个表示用户类型的枚举:

public enum UserType
{
    Customer = 1,
    Driver = 2,  
    Admin = 3,
}

我们定义包含三个值的 UserType 枚举:Customer、Driver 和 Admin。

但是如果我们需要怎么办?代表值的集合?

例如,在一家送货公司,我们知道管理员和司机都是员工。 那么让我们添加一个新的枚举项Employee。 稍后,我们将向您展示如何用它来表示管理员和驱动程序:

public enum UserType
{   
    Customer = 1,
    Driver = 2,  
    Admin = 3,
    Employee = 4
}

定义并声明标志属性

标志是一个属性,允许我们将枚举表示为值的集合而不是单一的值。 那么,让我们看看如何在枚举上实现 Flags 属性:

[Flags]
public enum UserType
{   
    Customer = 1,
    Driver = 2,  
    Admin = 4,
}

我们添加 Flags 属性并用 2 的幂对值进行编号。如果两者都没有,这将无法工作。

现在回到我们之前的问题,我们可以使用 | 运算符来表示 Employee

var employee = UserType.Driver | UserType.Admin;

另外,我们可以将其定义为枚举内的常量以直接使用它:

[Flags]
public enum UserType
{                
    Customer = 1,             
    Driver = 2,               
    Admin = 4,                
    Employee = Driver | Admin
}

幕后花絮

为了更好地理解 Flags 属性,我们必须回到数字的二进制表示形式。 例如,我们可以将 1 表示为二进制 0b_0001,将 2 表示为 0b_0010

[Flags]
public enum UserType
{
    Customer = 0b_0001,
    Driver = 0b_0010,
    Admin = 0b_0100,
    Employee = Driver | Admin, //0b_0110
}

我们可以看到每个值都以有效位表示。 这就是用 2 的幂对值进行编号的想法的由来。 我们还可以注意到,Employee 包含两个活动位,即它是两个值 Driver 和 Admin 的组合。

对标志属性的操作

我们可以使用按位运算符来处理标志

初始化一个值

对于初始化,我们应该使用名为 None 的值 0,这意味着集合为空:

[Flags]
public enum UserType
{
    None = 0,
    Customer = 1,
    Driver = 2,
    Admin = 4,
    Employee = Driver | Admin
}

现在,我们可以定义一个变量:

var flags = UserType.None;

添加一个值

我们可以使用 | 运算符添加值:

flags |= UserType.Driver;

现在,flags 变量等于 Driver。

删除值

我们可以使用&, ~ 运算符删除值:

flags &= ~UserType.Driver;

现在,flagsvariable 等于 None。

我们可以使用 & 运算符检查该值是否存在:

Console.WriteLine((flags & UserType.Driver) == UserType.Driver);

结果为 False

另外,我们可以使用 HasFlag 方法来做到这一点:

Console.WriteLine(flags.HasFlag(UserType.Driver));

此外,结果将为 False。

正如我们所看到的,使用 & 运算符和 HasFlag 方法这两种方法都会给出相同的结果,但是我们应该使用哪一种呢? 为了找到答案,我们将在几个框架上测试性能。

衡量性能

首先,我们将创建一个控制台应用程序,并在 .csproj 文件中将 TargetFramwork 标记替换为 TargetFramworks 标签:

<TargetFrameworks>net48;netcoreapp3.1;net6.0</TargetFrameworks>
We use the TargetFramworks tag to support multiple frameworks: .NET Framework 4.8, .Net Core 3.1, and .Net 6.0.

其次,我们引入 BenchmarkDotNet 库来获取基准测试结果:

[Benchmark]
public bool HasFlag()
{
    var result = false;
    for (int i = 0; i < 100000; i++)
    {
        result = UserType.Employee.HasFlag(UserType.Driver);
    }
    return result;
}
[Benchmark]
public bool BitOperator()
{
    var result = false;
    for (int i = 0; i < 100000; i++)
    {
        result = (UserType.Employee & UserType.Driver) == UserType.Driver;
    }
    return result;
}

我们添加 [SimpleJob(RuntimeMoniker.Net48)][SimpleJob (RuntimeMoniker.NetCoreApp31)][SimpleJob(RuntimeMoniker.Net60)] 属性添加到 HasFlagBenchmarker 类,以查看不同版本 .NET Framework / .NET Core

方法作业运行时平均错误StdDevMedian
HasFlag.NET 6.0.NET 6.037.79 us3.781 us11.15 us30.30 us
BitOperator.NET 6.0.NET 6.038.17 us3.853 us11.36 us30.38 us
HasFlag.NET Core 3.1.NET Core 3.138.31 us3.939 us11.61 us30.37 us
BitOperator.NET Core 3.1.NET Core 3.138.07 us3.819 us11.26 us30.33 us
HasFlag.NET Framework 4.8.NET Framework 4.82,893.10 us342.563 美元1,010.06 美元2,318.93 us
BitOperator.NET Framework 4.8.NET Framework 4.838.04 us3.920 us11.56 us30.17 us

因此,在 .NET Framework 4.8 中,HasFlag 方法比 慢得多位操作符。 但是,.Net Core 3.1.Net 6.0 的性能有所提高。 所以在新版本中,我们可以同时使用这两种方式。

Define the Problem

Let’s define an enum that represents the types of users:

public enum UserType
{
    Customer = 1,
    Driver = 2,  
    Admin = 3,
}

We define the UserType enum that contains three values: Customer, Driver, and Admin.

But what if we need to represent a collection of values?

For example, at a delivery company, we know that both the Admin and the Driver are employees. So let’s add a new enumeration item Employee. Later on, we will show you how we can represent both the admin and the driver with it:

public enum UserType
{   
    Customer = 1,
    Driver = 2,  
    Admin = 3,
    Employee = 4
}

Define and Declare a Flags Attribute

A Flags is an attribute that allows us to represent an enum as a collection of values ​​rather than a single value. So, let’s see how we can implement the Flags attribute on enumeration:

[Flags]
public enum UserType
{   
    Customer = 1,
    Driver = 2,  
    Admin = 4,
}

We add the Flags attribute and number the values with powers of 2. Without both, this won’t work.

Now going back to our previous problem, we can represent Employee using the | operator:

var employee = UserType.Driver | UserType.Admin;

Also, we can define it as a constant inside the enum to use it directly:

[Flags]
public enum UserType
{                
    Customer = 1,             
    Driver = 2,               
    Admin = 4,                
    Employee = Driver | Admin
}

Behind the Scenes

To understand the Flags attribute better, we must go back to the binary representation of the number. For example, we can represent 1 as binary 0b_0001 and 2 as 0b_0010:

[Flags]
public enum UserType
{
    Customer = 0b_0001,
    Driver = 0b_0010,
    Admin = 0b_0100,
    Employee = Driver | Admin, //0b_0110
}

We can see that each value is represented in an active bit. And this is where the idea of ​​numbering the values ​​with the power of 2 came from. We can also note that Employee contains two active bits, that is, it is a composite of two values Driver and Admin.

Operations on Flags Attribute

We can use the bitwise operators to work with Flags.

Initialize a Value

For the initialization, we should use the value 0 named None, which means the collection is empty:

[Flags]
public enum UserType
{
    None = 0,
    Customer = 1,
    Driver = 2,
    Admin = 4,
    Employee = Driver | Admin
}

Now, we can define a variable:

var flags = UserType.None;

Add a Value

We can add value by using | operator:

flags |= UserType.Driver;

Now, the flags variable equals Driver.

Remove a Value

We can remove value by use &, ~ operators:

flags &= ~UserType.Driver;

Now, flagsvariable equals None.

We can check if the value exists by using & operator:

Console.WriteLine((flags & UserType.Driver) == UserType.Driver);

The result is False.

Also, we can do this by using the HasFlag method:

Console.WriteLine(flags.HasFlag(UserType.Driver));

Also, the result will be False.

As we can see, both ways, using the & operator and the HasFlag method, give the same result, but which one should we use? To find out, we will test the performance on several frameworks.

Measure the Performance

First, we will create a Console App, and in the .csproj file we will replace the TargetFramwork tag with the TargetFramworks tag:

<TargetFrameworks>net48;netcoreapp3.1;net6.0</TargetFrameworks>
We use the TargetFramworks tag to support multiple frameworks: .NET Framework 4.8, .Net Core 3.1, and .Net 6.0.

Secondly, let’s introduce the BenchmarkDotNet library to get the benchmark results:

[Benchmark]
public bool HasFlag()
{
    var result = false;
    for (int i = 0; i < 100000; i++)
    {
        result = UserType.Employee.HasFlag(UserType.Driver);
    }
    return result;
}
[Benchmark]
public bool BitOperator()
{
    var result = false;
    for (int i = 0; i < 100000; i++)
    {
        result = (UserType.Employee & UserType.Driver) == UserType.Driver;
    }
    return result;
}

We add [SimpleJob(RuntimeMoniker.Net48)], [SimpleJob(RuntimeMoniker.NetCoreApp31)], and [SimpleJob(RuntimeMoniker.Net60)] attributes to the HasFlagBenchmarker class to see the performance differences between different versions of .NET Framework / .NET Core:

MethodJobRuntimeMeanErrorStdDevMedian
HasFlag.NET 6.0.NET 6.037.79 us3.781 us11.15 us30.30 us
BitOperator.NET 6.0.NET 6.038.17 us3.853 us11.36 us30.38 us
HasFlag.NET Core 3.1.NET Core 3.138.31 us3.939 us11.61 us30.37 us
BitOperator.NET Core 3.1.NET Core 3.138.07 us3.819 us11.26 us30.33 us
HasFlag.NET Framework 4.8.NET Framework 4.82,893.10 us342.563 us1,010.06 us2,318.93 us
BitOperator.NET Framework 4.8.NET Framework 4.838.04 us3.920 us11.56 us30.17 us

So, in .NET Framework 4.8 a HasFlag method was much slower than the BitOperator. But, the performance improves in .Net Core 3.1 and .Net 6.0. So in newer versions, we can use both ways.

挥剑断情 2024-07-12 02:19:32

@Nidonocu

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

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

@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)));
银河中√捞星星 2024-07-12 02:19:32

最近问过 关于类似的事情。

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

这允许您执行以下操作:

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

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

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)

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.

浊酒尽余欢 2024-07-12 02:19:32

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

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

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");
            }
        }
    }
}
黒涩兲箜 2024-07-12 02:19:32

当使用标志时,我经常声明额外的 None 和 All 项目。 这些有助于检查是否设置了所有标志或未设置标志。

[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

 
更新 2019-10:

从 C# 7.0 开始,您可以使用二进制文字,这可能更直观地阅读:

[Flags] 
enum SuitsFlags { 

    None =     0b0000,

    Spades =   0b0001, 
    Clubs =    0b0010, 
    Diamonds = 0b0100, 
    Hearts =   0b1000,

    All =      0b1111

}

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

}
失退 2024-07-12 02:19:32

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

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

我认为这种表示形式清楚地表明了标志在幕后如何工作。

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

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

不必在意 2024-07-12 02:19:32

您也可以这样做,

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

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

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

少女情怀诗 2024-07-12 02:19:32

结合答案 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,
}

使用 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

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.

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:

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文