A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://stackoverflow.com/questions/8447/what-does-the-flags-enum-attribute-mean-in-c below:

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

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:

Method Job Runtime Mean Error StdDev Median HasFlag .NET 6.0 .NET 6.0 37.79 us 3.781 us 11.15 us 30.30 us BitOperator .NET 6.0 .NET 6.0 38.17 us 3.853 us 11.36 us 30.38 us HasFlag .NET Core 3.1 .NET Core 3.1 38.31 us 3.939 us 11.61 us 30.37 us BitOperator .NET Core 3.1 .NET Core 3.1 38.07 us 3.819 us 11.26 us 30.33 us HasFlag .NET Framework 4.8 .NET Framework 4.8 2,893.10 us 342.563 us 1,010.06 us 2,318.93 us BitOperator .NET Framework 4.8 .NET Framework 4.8 38.04 us 3.920 us 11.56 us 30.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.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4