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:
- Include a
None = 0member. This represents "no flags set" and is important for default initialization and clearing. - Assign power-of-two values for each independent flag.
- 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).

