← Open in the full interactive course (progress tracking, search & more)

Enums give names to numbers — making your code clearer, safer, and easier to maintain.

Imagine you're building an order system. An order can be in one of several states: Pending, Shipped, Delivered, or Cancelled. You could represent these as integers: 0, 1, 2, 3. But that's error‑prone — what if you accidentally use 5? What if you forget which number means what? Enums solve this by giving each state a meaningful name, while still being efficient integers under the hood.

In C#, an enum (short for enumeration) is a value type that defines a set of named constants. It's one of the most practical tools for writing self‑documenting and type‑safe code. In this lesson, you'll learn everything from the basics of defining enums to advanced scenarios like flags, underlying types, and parsing.

What Is It?

The Simple Explanation

An enum is a way to give friendly names to a fixed set of numeric values. Instead of using magic numbers like 1, 2, 3 in your code, you use names like Pending, Shipped, Delivered. It makes your code readable and reduces the chance of mistakes.

The Technical Definition

In C#, an enum is a value type defined with the enum keyword. It inherits from System.Enum (which itself inherits from ValueType). Each enum member has an associated integral value (by default int, but you can specify other underlying types). Enums are strongly typed, meaning you cannot accidentally assign an integer to an enum variable without an explicit cast.

Key characteristics:

Concept Example
Basic enum enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }
Enum with explicit values enum Status { Pending = 1, Shipped = 2, Delivered = 4, Cancelled = 8 }
Flags enum [Flags] enum Permissions { None = 0, Read = 1, Write = 2, Execute = 4 }
Underlying type enum Color : byte { Red, Green, Blue }

Why Does It Exist?

The Problem

In many programs, you need to represent a fixed set of options — like days of the week, order statuses, user roles, or permissions. Using plain integers or strings for these leads to several problems:

The Solution

Enums give you the best of both worlds:

Enums are a fundamental tool in .NET for writing self‑documenting, robust code that is easy to maintain.

Big Picture

Enums are used throughout .NET — from DayOfWeek to HttpStatusCode. Here's how they fit into your application:

ENUMS IN AN APPLICATION
Domain Model
OrderStatus, UserRole, PaymentMethod — enums define the vocabulary of your business logic.
Business Logic
switch statements, if checks, and methods that operate based on enum values.
Persistence & Serialization
Store enums as integers or strings in databases, JSON, or XML. Use Enum.ToString() and Enum.Parse() for conversion.
UI & User Interaction
Populate dropdowns, radio buttons, and display friendly names using Enum.GetNames() or custom attributes.

How It Works

Step 1 — Defining an enum

public enum OrderStatus
{
    Pending = 0,
    Shipped = 1,
    Delivered = 2,
    Cancelled = 3
}

The compiler assigns each member a constant integer value. If you don't specify values, they start at 0 and increment.

Step 2 — Using an enum variable

OrderStatus current = OrderStatus.Shipped;
Console.WriteLine(current);                 // "Shipped"
Console.WriteLine((int)current);            // 1

The variable holds the underlying integer, but the compiler ensures you can only assign valid enum values (unless you cast).

Step 3 — Switching on an enum

string GetStatusMessage(OrderStatus status)
{
    return status switch
    {
        OrderStatus.Pending => "Your order is being processed.",
        OrderStatus.Shipped => "Your order is on the way!",
        OrderStatus.Delivered => "Your order has been delivered.",
        OrderStatus.Cancelled => "Your order was cancelled.",
        _ => "Unknown status"
    };
}

Enums work perfectly with switch expressions and statements, making your logic clear.

Step 4 — Parsing from strings or numbers

// From string (case‑insensitive by default)
if (Enum.TryParse("shipped", true, out var parsed))
{
    Console.WriteLine(parsed); // Shipped
}

// From integer
OrderStatus status = (OrderStatus)2; // Delivered

Use Enum.TryParse for safe conversion from user input or external data.

Step 5 — Flags (bitwise combinations)

[Flags]
public enum FileAccess
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4
}

var access = FileAccess.Read | FileAccess.Write; // Combine
Console.WriteLine(access); // "Read, Write"
Console.WriteLine((int)access); // 3

bool canRead = access.HasFlag(FileAccess.Read); // true

With [Flags], the enum values are powers of two, and you can combine them using bitwise OR. The HasFlag method checks if a specific flag is set.

Simple Example

public enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

Season current = Season.Summer;
Console.WriteLine($"It's {current}!"); // It's Summer!

// Switch
string weather = current switch
{
    Season.Spring => "Mild",
    Season.Summer => "Hot",
    Season.Autumn => "Cool",
    Season.Winter => "Cold",
    _ => "Unknown"
};
Console.WriteLine(weather); // Hot

// Loop through all values
foreach (Season s in Enum.GetValues(typeof(Season)))
{
    Console.WriteLine($"{s} = {(int)s}");
}
// Spring = 0, Summer = 1, Autumn = 2, Winter = 3

Code → Meaning → Result

Real-World Example

Imagine an e‑commerce system where each order has a status, and we also need to track a user's permissions using bit flags.

// Simple enum for order status
public enum OrderStatus
{
    Pending,
    Processing,
    Shipped,
    Delivered,
    Cancelled
}

// Flags enum for user permissions
[Flags]
public enum Permission
{
    None = 0,
    ViewOrders = 1 << 0,  // 1
    EditOrders = 1 << 1,  // 2
    DeleteOrders = 1 << 2, // 4
    ManageUsers = 1 << 3,  // 8
    Admin = ViewOrders | EditOrders | DeleteOrders | ManageUsers
}

public class OrderService
{
    public string GetOrderStatusMessage(OrderStatus status)
    {
        return status switch
        {
            OrderStatus.Pending => "We're preparing your order.",
            OrderStatus.Processing => "Your order is being processed.",
            OrderStatus.Shipped => "Your order has shipped!",
            OrderStatus.Delivered => "Your order was delivered.",
            OrderStatus.Cancelled => "Your order was cancelled.",
            _ => "Unknown status"
        };
    }

    public bool CanUserEditOrder(Permission perms) => perms.HasFlag(Permission.EditOrders);
}

// Usage
var service = new OrderService();
var orderStatus = OrderStatus.Shipped;
Console.WriteLine(service.GetOrderStatusMessage(orderStatus)); // "Your order has shipped!"

var userPerms = Permission.ViewOrders | Permission.EditOrders;
Console.WriteLine($"Can edit: {service.CanUserEditOrder(userPerms)}"); // True
Console.WriteLine($"Has Admin: {userPerms.HasFlag(Permission.Admin)}"); // False

Why this is realistic:

Analogy

Enum = A dropdown menu

Think of an enum as a dropdown list with a fixed set of options. You can only pick one of the predefined choices (or a combination if it's a flags enum). This prevents you from typing a random value that doesn't exist.

Underlying integer = The hidden ID

Each option in the dropdown has a hidden numeric ID (like 0, 1, 2). You see the friendly name, but the computer uses the number for efficiency.

Flags enum = Checkboxes

A flags enum is like a set of checkboxes — you can select multiple options (e.g., Read + Write), and each option is a bit in a binary number.

Under the Hood

What happens inside the .NET runtime when you define and use an enum?

ENUM INTERNALS
1. COMPILE-TIME

The compiler replaces enum member names with their constant integer values. The enum type is emitted as a struct that inherits from System.Enum.

2. RUNTIME REPRESENTATION
Memory: [OrderStatus value] stores an integer (4 bytes by default)

Enum variables are just integers with compile‑time type information. No extra overhead.

3. ENUM METHODS
4. FLAGS UNDER THE HOOD
[Flags] does NOT change the enum's behavior — it's a hint for ToString() and tools.

Without [Flags], ToString() on combined values would just show a number. With [Flags], it displays comma‑separated names.

Common Confusion

1. "Enum values are guaranteed to be unique"

They are unique by name, but you can explicitly assign duplicate values. The compiler allows it, but it's a bad practice because it breaks uniqueness.

enum Duplicates { A = 1, B = 1 } // Both A and B equal 1 — ambiguous.

2. "Enum is a reference type"

No, enums are value types (structs). They are stored inline and have no heap allocation unless boxed.

3. "Flags attribute makes enums behave differently"

The [Flags] attribute does not change the enum's underlying behavior. It only provides a hint for ToString() and other tools (like debuggers, serializers) to treat the enum as a set of flags. You can still use bitwise operations without it.

4. "Enum values must start at 0"

No, you can start at any integer. But it's common to start at 0 so that the default value (0) can represent a sensible default, like None or Unknown.

Common Mistakes

Mistake 1 — Assuming enum variable always holds a defined value

Wrong: You can assign any integer to an enum variable via casting, even if it's not defined.

OrderStatus status = (OrderStatus)99; // Compiles, but status is invalid.
Console.WriteLine(status); // "99" — ToString() returns the number.

Correct: Validate input using Enum.IsDefined before using it.

if (Enum.IsDefined(typeof(OrderStatus), status)) { /* safe */ }

Mistake 2 — Forgetting [Flags] for bitwise enums

Wrong: Defining a flags enum without [Flags] makes ToString() unhelpful and can confuse tools.

Correct: Always apply [Flags] when you intend to combine values.

Mistake 3 — Using Enum.Parse without TryParse for user input

Wrong: Enum.Parse throws an exception if the string is invalid.

Correct: Use Enum.TryParse for safe parsing, especially from user input.

Mistake 4 — Using enums for open‑ended sets

Wrong: If the set of values can change frequently (e.g., user‑defined categories), an enum is not appropriate because it's fixed at compile time.

Correct: Use a database table or a dictionary for dynamic sets. Enums are for known, stable sets.

When Should I Use It?

Use an enum when:

Avoid enums when:

Mental Model

Enum = a set of named integer constants.
Value = the underlying integer.
Name = the friendly label.
Flags = combine multiple values with bitwise OR.

Remember:
· Enums are value types — they live on the stack.
· They are statically defined — you can't add new members at runtime.
· Use [Flags] for bitmask semantics.
· Always validate enum values from external sources.

Key Takeaway


Check Your Understanding

You've seen how enums work, when to use them, and how to avoid common pitfalls. Test your knowledge with these practical scenarios.

1. What is the default underlying type of a C# enum?

Show answer

Correct: B

Why B is correct: The default underlying type for an enum in C# is int. You can override this by specifying a different integer type.

Why A, C, D are incorrect: They are not the default; they require explicit declaration.

Reinforcement: Enums are integer-based; int is the default.

2. Which method safely converts a string to an enum value without throwing an exception?

Show answer

Correct: B

Why B is correct: Enum.TryParse returns a boolean indicating success and outputs the parsed value via an out parameter. It does not throw if parsing fails.

Why A is incorrect: Enum.Parse throws an exception if the string is not a valid enum value.

Why C is incorrect: There is no Convert.ToEnum method.

Why D is incorrect: Casting from string to enum is not valid; you cannot cast a string to an enum.

Reinforcement: Use Enum.TryParse for safe conversion, especially from user input.

3. What does the [Flags] attribute do to an enum?

Show answer

Correct: A

Why A is correct: The [Flags] attribute indicates that the enum can be treated as a bit field (i.e., a set of flags). It changes the behavior of ToString() to produce a comma‑separated list of flag names when the value is a combination of flags. It does not change the underlying type or behavior otherwise.

Why B is incorrect: [Flags] has nothing to do with thread safety.

Why C is incorrect: The underlying type remains the same unless you explicitly change it.

Why D is incorrect: [Flags] does not affect switch usage.

Reinforcement: [Flags] is a hint for formatting and tooling; it enables nice ToString() output for combined values.

4. Given the enum enum Color { Red, Green, Blue }, what is the integer value of Color.Green?

Show answer

Correct: B

Why B is correct: By default, enum members are assigned consecutive integer values starting from 0. So Red=0, Green=1, Blue=2.

Why A, C, D are incorrect: They represent the values of Red, Blue (2), and an invalid value.

Reinforcement: Enum values start at 0 unless explicitly specified.

5. Which of the following is a valid way to combine two flags enum values?

Show answer

Correct: B

Why B is correct: Bitwise OR (|) is the correct way to combine flag values. It sets the bits from both values.

Why A is incorrect: Addition (+) can work if the flags are mutually exclusive (no overlapping bits), but it's not the intended bitwise operation and can cause carry‑over errors if bits overlap.

Why C is incorrect: Bitwise AND (&) gives the intersection, not the union.

Why D is incorrect: XOR (^) toggles bits, which is not the standard combination operation.

Reinforcement: Use bitwise OR (|) to combine flags.

You now have a solid understanding of enums — from naming constants to combining flags!


dotnetmadeeasy.com — Learn C# and .NET, the right way.