BrandGhost
C# Enum Flags: Combining Values with Bitwise Operations

C# Enum Flags: Combining Values with Bitwise Operations

C# Enum Flags: Combining Values with Bitwise Operations

A standard enum lets one variable hold exactly one value. But sometimes a variable needs to represent a combination -- a user can have Read and Write access, not just one or the other. That is where C# enum flags come in.

The [Flags] attribute turns a regular enum into a bitfield. Instead of mutually exclusive choices, each member represents an independent bit that can be switched on or off. You combine members with bitwise OR, check individual members with bitwise AND, and get meaningful string output for free. This guide covers exactly how to set this up and where the pattern fits.

What Is a Flags Enum?

A flags enum is a regular C# enum decorated with the [Flags] attribute where each member is assigned a distinct power of two. Because powers of two have no overlapping bits, any combination of members produces a unique integer value:

None    = 0  = 0000
Read    = 1  = 0001
Write   = 2  = 0010
Execute = 4  = 0100

Combining Read | Write sets bits 0001 | 0010 = 0011 = 3. The combination is unique and fully reversible -- you can always extract which individual flags are set from the combined value.

Without [Flags], there is nothing technically preventing you from using bitwise operations on an enum, but ToString() will produce the numeric value instead of the flag names, and tooling that inspects the attribute will miss the intent.

Declaring a Flags Enum

[Flags]
public enum FileAccess
{
    None      = 0,
    Read      = 1,            // 0001
    Write     = 2,            // 0010
    Execute   = 4,            // 0100
    ReadWrite = Read | Write  // 0011 -- composite convenience value
}

The rules for a correct flags enum:

  1. Include a None = 0 member. This represents "no flags set" and is important for default initialization and clearing.
  2. Assign power-of-two values for each independent flag.
  3. Define composite convenience values (like ReadWrite) using the named members, not raw integers. This way the composition is always expressed in terms of the individual flags.

A common mistake is using 1, 2, 3, 4:

// Wrong -- 3 collides with 1|2 and cannot be distinguished
[Flags]
public enum Permissions
{
    Read    = 1,
    Write   = 2,
    Execute = 3,  // Should be 4!
    Delete  = 4   // Should be 8!
}

With Execute = 3, you can never tell whether a variable holds "Execute" or "Read and Write." Always use powers of two for independent flags.

Setting and Combining Flags

Combine flags using bitwise OR:

// Grant Read and Execute access
FileAccess access = FileAccess.Read | FileAccess.Execute;
Console.WriteLine(access);    // "Read, Execute" (because [Flags] is present)
Console.WriteLine((int)access); // 5 (0001 | 0100 = 0101)

// Add a flag to an existing value
access |= FileAccess.Write;
Console.WriteLine(access);    // "Read, Write, Execute"

// Remove a flag
access &= ~FileAccess.Write;
Console.WriteLine(access);    // "Read, Execute"

The |= operator adds a flag; &= ~flag removes it. The tilde ~ is bitwise NOT, which flips all bits of the flag so that ANDing it clears only that bit.

Checking Whether a Flag Is Set

Two approaches: bitwise AND or HasFlag():

FileAccess access = FileAccess.Read | FileAccess.Execute;

// Bitwise AND -- explicit, no allocation
bool canRead    = (access & FileAccess.Read)    != 0;  // true
bool canWrite   = (access & FileAccess.Write)   != 0;  // false
bool canExecute = (access & FileAccess.Execute) != 0;  // true

// HasFlag -- readable, slight overhead in old runtimes due to boxing
bool alsoCanRead = access.HasFlag(FileAccess.Read);    // true

HasFlag was added in .NET Framework 4. In modern .NET (5+), the JIT frequently inlines HasFlag, reducing its overhead significantly. That said, boxing can still occur in some edge cases -- for hot paths where every allocation matters, the bitwise AND form is the safest choice.

To check if all of a set of flags are present:

FileAccess required = FileAccess.Read | FileAccess.Write;
bool hasAll = (access & required) == required;

To check if any of a set of flags are present:

bool hasAny = (access & required) != 0;

Clearing and Toggling Flags

Practical bit manipulation patterns:

FileAccess access = FileAccess.Read | FileAccess.Write | FileAccess.Execute;

// Remove Write
access &= ~FileAccess.Write;
Console.WriteLine(access);  // "Read, Execute"

// Clear all flags
access = FileAccess.None;

// Toggle a flag (flip it regardless of current state)
access ^= FileAccess.Read;
Console.WriteLine(access);  // "Read" (was off, now on)
access ^= FileAccess.Read;
Console.WriteLine(access);  // "None" (was on, now off)

Toggling with XOR (^=) is less common but useful when you want to flip without checking the current state -- for example, implementing an "enable/disable" button in a UI.

ToString() and Parsing

With [Flags], ToString() produces a comma-separated list of the active flag names:

FileAccess access = FileAccess.Read | FileAccess.Execute;

Console.WriteLine(access.ToString());        // "Read, Execute"
Console.WriteLine(access.ToString("G"));     // "Read, Execute" (general)
Console.WriteLine(access.ToString("D"));     // "5" (decimal)
Console.WriteLine(access.ToString("F"));     // "Read, Execute" (flags, same as G for [Flags] enums)
Console.WriteLine(access.ToString("X"));     // "00000005" (hex)

Without [Flags], access.ToString() produces "5" instead of "Read, Execute".

Parsing works the same way as regular enums:

// Parse a flags value from a string
if (Enum.TryParse<FileAccess>("Read, Execute", out FileAccess parsed))
{
    Console.WriteLine(parsed);  // "Read, Execute"
    Console.WriteLine((int)parsed);  // 5
}

// Parse a combined name defined as composite
if (Enum.TryParse<FileAccess>("ReadWrite", out FileAccess rw))
{
    Console.WriteLine(rw.HasFlag(FileAccess.Read));   // true
    Console.WriteLine(rw.HasFlag(FileAccess.Write));  // true
}

Using Long for Large Flags Enums

The default int underlying type provides 32 bits; however, the highest bit (1 << 31) produces a negative value due to signed integer representation. Practical usage is limited to 31 independent flags. For more, use long:

[Flags]
public enum LargePermissions : long
{
    None         = 0L,
    Permission1  = 1L << 0,   // bit 0
    Permission2  = 1L << 1,   // bit 1
    // ...
    Permission63 = 1L << 62   // bit 62 (0-indexed, sign bit excluded for safety)
}

The 1L << n syntax is clear and explicit. Writing 1 << 31 produces -2147483648 because signed overflow wraps the high bit into a negative value; use 1L << 31 for long-backed enums to avoid this.

In practice, an enum with more than 20-30 flags often indicates a design problem. Consider whether the flags can be grouped into smaller, focused enums.

When to Use Flags Enum vs Regular Enum

Scenario Flags Enum Regular Enum
Variable holds exactly one value No Yes
Variable holds zero or more values Yes No
Example: DayOfWeek No -- one day at a time Yes
Example: WorkingDays Yes -- Mon+Tue+Wed No
Example: HttpMethod No Yes
Example: FileAccess Yes No

A quick test: can your variable logically be "both X and Y at the same time"? If yes, flags. If no, regular.

For a full comparison of when to use enum versus other constructs, see When to Use Enum vs Constants in C# and the C# Enum Complete Guide.

Serializing Flags Enums

System.Text.Json with JsonStringEnumConverter serializes flags enums as comma-separated names:

using System.Text.Json;
using System.Text.Json.Serialization;

var options = new JsonSerializerOptions
{
    Converters = { new JsonStringEnumConverter() }
};

FileAccess access = FileAccess.Read | FileAccess.Execute;

string json = JsonSerializer.Serialize(access, options);
Console.WriteLine(json);  // "Read, Execute"

FileAccess deserialized = JsonSerializer.Deserialize<FileAccess>(json, options)!;
Console.WriteLine(deserialized.HasFlag(FileAccess.Read));    // true
Console.WriteLine(deserialized.HasFlag(FileAccess.Execute)); // true

When designing APIs, string enum serialization is preferred over integer serialization. It makes the payload self-documenting and prevents client code from depending on internal numeric values.

Real-World Example: User Permissions

A common real-world use for flags enum is a permission system:

[Flags]
public enum UserPermission
{
    None         = 0,
    ViewContent  = 1 << 0,   // 1
    CreatePost   = 1 << 1,   // 2
    EditPost     = 1 << 2,   // 4
    DeletePost   = 1 << 3,   // 8
    ManageUsers  = 1 << 4,   // 16
    AdminAccess  = 1 << 5,   // 32

    // Convenience composites
    Contributor  = ViewContent | CreatePost,
    Editor       = Contributor | EditPost | DeletePost,
    Admin        = Editor | ManageUsers | AdminAccess
}

public class User
{
    public UserPermission Permissions { get; set; }

    public bool Can(UserPermission permission)
        => Permissions.HasFlag(permission);
}

// Usage
var user = new User { Permissions = UserPermission.Contributor };

Console.WriteLine(user.Can(UserPermission.ViewContent));  // true
Console.WriteLine(user.Can(UserPermission.ManageUsers));  // false

// Grant extra permission
user.Permissions |= UserPermission.EditPost;
Console.WriteLine(user.Can(UserPermission.EditPost));     // true

// Revoke permission
user.Permissions &= ~UserPermission.EditPost;
Console.WriteLine(user.Can(UserPermission.EditPost));     // false

This is a clean, efficient permission model. Storage requires a single integer column; comparison is a bitwise operation; the composite convenience values (Editor, Admin) keep the code readable. For more context on how this integrates with How to Use Enum in C#, the foundational usage patterns apply directly.

Common Mistakes With Flags Enum

Not using powers of two. Values like 1, 2, 3, 4 produce ambiguous combinations. Use 1, 2, 4, 8 or the 1 << n syntax.

Forgetting None = 0. Without it, no way to represent "no flags," and default(MyFlags) is an unnamed 0. This causes problems in switch statements and HasFlag checks.

Using == 0 instead of checking the None member. Semantically, access == FileAccess.None is clearer than (int)access == 0.

Using [Flags] on an enum with non-power-of-two values. The attribute is documentation and formatting guidance, not enforcement. C# will not stop you -- but ToString() will produce confusing output, and combining values will be undefined.

For deeper context on common enum pitfalls across both regular and flags usage, see You're Using Enums Wrong and Enums in CSharp -- A Simple Guide to Expressive Code.

Frequently Asked Questions

What is the [Flags] attribute in C#?

[Flags] marks an enum as a bitfield where members can be combined using bitwise OR. It changes how ToString() renders combined values -- showing flag names instead of the raw integer -- and signals design intent to readers.

How do I check if a flag is set in C#?

Use HasFlag(): myEnum.HasFlag(FlagValue). Or use bitwise AND: (myEnum & FlagValue) != 0. Both return true if the flag bit is set. In .NET 5+, HasFlag is inlined and has no performance overhead over the bitwise form.

How do I add and remove flags from a C# enum?

Add with bitwise OR: access |= FileAccess.Write;. Remove with bitwise AND and NOT: access &= ~FileAccess.Write;. Toggle with XOR: access ^= FileAccess.Write;.

Why must enum flags be powers of two?

Powers of two have no overlapping bits, so any combination of flags produces a unique integer. Non-power-of-two values overlap with combinations of smaller values, making it impossible to determine which individual flags are set from the combined result.

Can I use a long for a C# flags enum?

Yes. Add : long after the enum name and use 1L << n syntax for flag values. This supports up to 63 independent flags. Use long only when you genuinely need more than 31 flags.

How do I serialize a flags enum to JSON in C#?

Add JsonStringEnumConverter to your JsonSerializerOptions. The serializer renders combined flags as a comma-separated string of names, which is more readable than the raw integer.

When should I use [Flags] vs a regular enum?

Use [Flags] when a variable must logically hold more than one value at a time (permissions, weekday selection, feature toggles). Use a regular enum when the variable holds exactly one exclusive choice (status, severity, direction).

Remove Control Flag Refactoring - How to Simplify Logic

Simplify your code with the remove control flag refactoring technique in C#. See the benefits of removing control flags in this guide to applying the technique.

C# Enum: Complete Guide to Enumerations in .NET

Master C# enum with this complete guide. Learn to declare enums, use flags, convert to string, apply switch pattern matching, and follow .NET best practices.

How to Use Enum in C#: Declaration, Values, and Best Practices

Learn how to use enum in C# from scratch. Covers declaring enums, assigning values, casting, comparing, iterating, and the best practices every C# developer should follow.

An error has occurred. This application may no longer respond until reloaded. Reload