Osy#betaRun it on Osyrin: osyrin.com

Reference / Enum

[Flags] — a value that holds several members

[Flags] enum <Name> { None = 0, <A> = 1, <B> = 2, <C> = 4 }

[Flags] makes an enum's members bits, so one value can hold several of them at once — Access.Read | Access.Write. Combine members with | & ^ ~, test one with HasFlag, and a combined value reads as the members it holds ("Read, Write"), exactly as C# prints it. Every member needs an explicit power-of-two value.

stable1 example compiled by CIenumflagsbitwise

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> ~a

Description#

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#

withwhy 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#

Related

enum

A fixed set of named values, used as a member type. Stored as a number by default, or as the member's own name with…

[Label], [Icon], [Tone] — what a human reads

An enum member stores a compact value but shows a human-readable label. [Label("…")] gives a member its label…

Enums

A fixed set of named values, used as a member type. By default an enum stores as a compact number; add [Type(string)]…