Enums & Flags

10 questions found

How do you declare an enum in C#, and what underlying type do its members have by default?

Beginner
An enum is a distinct value type representing a named set of constant integral values; by default, its members are backed by 'int', starting at 0 and auto-incrementing, unless you assign explicit values.
public enum DayOfWeek {
  Sunday,    // 0
  Monday,    // 1
  Tuesday    // 2
}
DayOfWeek today = DayOfWeek.Monday;
Console.WriteLine((int)today); // 1
Real-world example Representing a fixed set of order statuses, days of the week, or log severity levels with meaningful names.

Common follow-ups: Can you specify a different underlying type for an enum, like byte or long?

Fundamentals

How do you convert between an enum value and its underlying numeric value, and between an enum and its string name?

Beginner
Cast directly with (int) to get the numeric value; use ToString() to get the member's name as a string, and Enum.Parse<T>() (or the safer Enum.TryParse<T>()) to convert a string back into the corresponding enum value.
DayOfWeek day = DayOfWeek.Monday;
int numericValue = (int)day;          // 1
string name = day.ToString();          // "Monday"
DayOfWeek parsed = Enum.Parse<DayOfWeek>("Tuesday"); // DayOfWeek.Tuesday
Real-world example Converting a user-selected string from a dropdown into the corresponding enum value for use in business logic.

Common follow-ups: What exception does Enum.Parse<T>() throw if the input string doesn't match any enum member, and how does TryParse avoid it?

String Handling & StringBuilder

How do you create a 'flags' enum that supports combining multiple values using bitwise operations?

Intermediate
Mark the enum with [Flags] and assign each member a distinct POWER-OF-TWO value, letting you safely combine multiple flags into a single value with the bitwise OR operator and check for a specific flag's presence with bitwise AND.
[Flags]
public enum Permissions {
  None = 0,
  Read = 1,
  Write = 2,
  Delete = 4,
  All = Read | Write | Delete
}
Permissions userPerms = Permissions.Read | Permissions.Write;
bool canWrite = (userPerms & Permissions.Write) == Permissions.Write; // true
Real-world example Modeling a user's combined set of permissions (read/write/delete) compactly as a single stored enum value.

Common follow-ups: What does the [Flags] attribute actually change about the enum's behavior beyond just documentation intent?

Attributes & Reflection

How does the [Flags] attribute change the output of ToString() on a combined flags enum value?

Intermediate
Without [Flags], calling ToString() on a value that doesn't exactly match any single named member just returns the raw numeric value as a string; WITH [Flags], the runtime recognizes the value as a combination and formats it as a comma-separated list of the individual flag names that make it up.
[Flags]
public enum Permissions { None = 0, Read = 1, Write = 2, Delete = 4 }
var combined = Permissions.Read | Permissions.Write;
Console.WriteLine(combined.ToString()); // "Read, Write" -- thanks to [Flags]

// Without [Flags], the same combined value would just print "3"
Real-world example Displaying a human-readable summary of a user's combined permissions in a log message or admin dashboard.

Common follow-ups: How would you use HasFlag() as an alternative, more readable way to check for a specific flag's presence?

String Handling & StringBuilder

What is the performance consideration when using Enum.HasFlag() versus a direct bitwise AND check?

Intermediate
Enum.HasFlag() involves BOXING the enum value (converting it to an object) internally in older .NET versions, making it measurably slower than a direct bitwise AND comparison in performance-sensitive, high-frequency code — modern .NET has optimized this significantly, but the direct bitwise check remains marginally faster and avoids any boxing concern entirely.
[Flags]
public enum Permissions { None = 0, Read = 1, Write = 2 }
var perms = Permissions.Read;

// Slightly slower historically (boxing), but more readable
bool hasRead1 = perms.HasFlag(Permissions.Read);

// Faster, no boxing, less readable
bool hasRead2 = (perms & Permissions.Read) == Permissions.Read;
Real-world example Choosing the direct bitwise check over HasFlag() specifically in a tight, performance-critical loop checking permissions millions of times.

Common follow-ups: At what call frequency does this performance difference actually become practically noticeable in a real application?

Memory & Garbage Collection

How would you write an extension method to safely add or remove a specific flag from a [Flags] enum value without manual bitwise operators scattered throughout your code?

Advanced
Define generic (or type-specific) extension methods that encapsulate the bitwise OR (for adding) and AND-with-complement (for removing) operations, giving calling code a clean, readable API instead of repeating raw bitwise expressions everywhere.
public static class PermissionsExtensions {
  public static Permissions AddFlag(this Permissions value, Permissions flag) => value | flag;
  public static Permissions RemoveFlag(this Permissions value, Permissions flag) => value & ~flag;
}

var perms = Permissions.Read;
perms = perms.AddFlag(Permissions.Write);      // Read, Write
perms = perms.RemoveFlag(Permissions.Read);    // Write
Real-world example Providing a clean, discoverable API for manipulating a permissions or feature-flags enum throughout a large codebase.

Common follow-ups: Could this same pattern be made generic to work with ANY [Flags] enum type, not just Permissions specifically?

Extension Methods

How do you safely validate that an enum value received from external input (like deserialized JSON or a database) is actually a DEFINED member, given that enums don't enforce this by default?

Advanced
Since C# enums are just named integers, a value like (Permissions)999 compiles and runs fine even though it doesn't correspond to any defined member (or valid flag combination) — use Enum.IsDefined() for simple enums, or a manual bitwise check against a computed 'all valid flags' mask for [Flags] enums, to validate untrusted input before trusting it.
public enum Status { Active = 0, Inactive = 1 }
int rawValue = 99; // e.g., from untrusted deserialized JSON
var status = (Status)rawValue; // compiles and runs, but is INVALID
bool isValid = Enum.IsDefined(typeof(Status), status); // false -- correctly caught
Real-world example Validating a status or category field parsed from external API input before it's trusted and used in business logic.

Common follow-ups: Why doesn't Enum.IsDefined() work correctly out of the box for validating combined [Flags] enum values?

Exception Handling

How would you use pattern matching (switch expressions) with an enum, including exhaustiveness warnings from the compiler?

Advanced
A switch expression over an enum lets you handle each member concisely and return a value directly; the compiler emits a WARNING (not an error, unlike some other languages) if you don't handle every enum member and lack a discard pattern, helping catch forgotten cases when new members are added later.
public enum OrderStatus { Pending, Shipped, Delivered, Cancelled }

string Describe(OrderStatus status) => status switch {
  OrderStatus.Pending => "Waiting to ship",
  OrderStatus.Shipped => "On its way",
  OrderStatus.Delivered => "Arrived",
  OrderStatus.Cancelled => "Order cancelled",
  _ => throw new ArgumentOutOfRangeException(nameof(status)) // catches truly invalid/undefined values
};
Real-world example Mapping an OrderStatus enum to a user-facing display string, with the compiler helping catch forgotten cases as the enum evolves.

Common follow-ups: Why is it good practice to throw in the discard '_' case rather than silently returning a default value?

Records & Pattern Matching

How do enums interact with generic constraints, and why has it historically been awkward to write generic, reusable flag-manipulation code?

Advanced
Prior to more recent C# versions, you couldn't constrain a generic type parameter to 'must be an enum' cleanly enough to perform bitwise operations generically (the 'where T : struct, Enum' constraint exists, but bitwise operators aren't directly usable on a generic Enum-constrained parameter without casting through the underlying integral type) — this has led to workarounds using Convert.ToInt64() or reflection for fully generic flag utilities.
public static bool HasFlagGeneric<T>(T value, T flag) where T : struct, Enum {
  long valueAsLong = Convert.ToInt64(value);
  long flagAsLong = Convert.ToInt64(flag);
  return (valueAsLong & flagAsLong) == flagAsLong; // workaround: convert through long
}
Real-world example Writing a single, reusable flag-checking utility that works generically across MULTIPLE different [Flags] enum types in a codebase.

Common follow-ups: How do newer .NET generic math features (INumber<T> and related interfaces) begin to address this kind of generic numeric limitation?

Generics

How would you design an enum-based state machine using pattern matching to enforce valid state transitions at compile time or runtime?

Advanced
Represent states as an enum, and define an explicit transition-validation method (often using a switch expression matching CURRENT state and DESIRED next state as a tuple) that only allows specific, intentional transitions — throwing or returning false for any transition not explicitly permitted, preventing invalid state changes from silently succeeding.
public enum OrderState { Created, Paid, Shipped, Cancelled }

public bool CanTransition(OrderState from, OrderState to) => (from, to) switch {
  (OrderState.Created, OrderState.Paid) => true,
  (OrderState.Created, OrderState.Cancelled) => true,
  (OrderState.Paid, OrderState.Shipped) => true,
  (OrderState.Paid, OrderState.Cancelled) => true,
  _ => false // every other transition is explicitly disallowed
};
Real-world example Enforcing a valid order lifecycle (Created -> Paid -> Shipped) and rejecting invalid transitions like Shipped -> Created.

Common follow-ups: How would you extend this design to also carry data specific to each state, beyond what a plain enum alone can represent?

Records & Pattern Matching