Summary#
A [Flags] enum is a set of switches: each member is one bit, and a value holds any combination of them. Build a
combination with |, test for a member with HasFlag, and read a combination back as the members it holds.
[Flags]
enum Access { None = 0, Read = 1, Write = 2, Delete = 4 }
Access rights = Access.Read | Access.Write;
rights.HasFlag(Access.Write) // true
$"{rights}" // "Read, Write"Signature#
[Flags]
enum <Name> { None = 0, <A> = 1, <B> = 2, <C> = 4 }
bool <value>.HasFlag(<Name> flag)
<Name> a | b <Name> a & b <Name> a ^ b <Name> ~aDescription#
Every member needs its value#
Each member of a [Flags] enum is a bit, so write its value: the powers of two, 1, 2, 4, 8, …, and 0 for a
member meaning "none". An enum normally counts on from the previous member, which here would give 0, 1, 2, 3 — and
3 is not a bit, it is 1 | 2. So a member without a value is a compile error that names the fix.
A member may also name a combination on purpose — ReadWrite = 3 — and a value that equals it reads as that name.
Combining and testing#
| adds members, & keeps only the members both sides hold, ^ toggles, and ~ inverts. The result of any of them
is still the enum, so it can be stored, passed and tested like a single member.
HasFlag(flag) is true when every bit of flag is set — the same as (value & flag) == flag. It works in a
function, in a component, and inside a query predicate:
var editors = Share.Where(s => s.Rights.HasFlag(Access.Write)).Count();How a combination reads#
.Name, .Label, string interpolation and + all read a combination as the members it holds, smallest value
first, joined with ", " — "Read, Delete". When a member names the whole combination (ReadWrite = 3), that name
is used. Bits that no member names make the value print as its number (9), the way C# does rather than naming the
part it recognises. .Description joins the descriptions of the members that have one.
The server and the browser word a value identically: both are held to one shared table of cases, whose names are checked against C#'s own output.
A constant combination#
A combination of members is a compile-time constant, so a class can declare one:
class Provider {
public const Access Pushdown = Access.Read | Access.Write;
}What it cannot be combined with#
| with | why it is refused |
|---|---|
[Type(string)] | a stored member code names one member, and a flags value may hold several. A flags enum is stored as its number. |
[Role] | a role is one member a person is granted; grant each role as its own row. |
Examples#
// Who may do what with a shared folder — several rights at once.
[Flags]
enum Access { None = 0, Read = 1, Write = 2, Delete = 4 }
entity Share {
[Required, MaxLength(200)] string Path;
Access Rights;
security { allow read, create, update when IsAuthenticated; }
}
void AllowWriting(Share s) {
s.Rights = s.Rights | Access.Write;
}
int Writable() => Share.Where(s => s.Rights.HasFlag(Access.Write)).Count();See also#
- enum — declaring an enum and how it is stored
- [Label], [Icon], [Tone] — what a human reads —
[Label], and what a screen shows for a member