Enum in C#

Enum in C# is a user-defined value type that represents a fixed set of named constants. Instead of using random numbers or strings in code, an enum lets you give meaningful names to related values. This makes programs easier to read, safer to maintain, and less dependent on magic numbers.

Enums are common in application status values, user roles, order states, error codes, menu choices, permission flags, device modes, and configuration options. Whenever a variable should accept only a small known set of values, an enum is often a strong choice.


What Is Enum in C#?

An enum is a type that contains named constants. Each name maps to an underlying integral value. By default, the first enum member has value 0, and each next member increases by one.

enum OrderStatus
{
    Pending,
    Processing,
    Shipped,
    Delivered,
    Cancelled
}

Here, Pending is 0, Processing is 1, Shipped is 2, and so on. In normal code, you use the names instead of remembering the numbers.

Why Enums Are Useful

Enums make code self-explanatory. A value like OrderStatus.Shipped is much clearer than 2. The compiler also helps because a variable of type OrderStatus is meant to represent only values from that enum.

An enum turns a group of related constants into a named type.

This becomes very useful when the same values are used in many places. If order status is represented by strings, one typo can break the logic. With enums, member names are checked by the compiler and supported by IntelliSense.

Basic Enum Example

OrderStatus status = OrderStatus.Processing;

if (status == OrderStatus.Processing)
{
    Console.WriteLine("Order is being prepared");
}

The variable status stores one value from the OrderStatus enum. The comparison reads naturally, and there is no need to remember which number means processing.

Assigning Explicit Values

You can assign explicit numeric values to enum members. This is useful when values must match a database, protocol, file format, API contract, or existing system.

enum HttpStatus
{
    Ok = 200,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404,
    ServerError = 500
}

Explicit values also make the enum safer against accidental reordering. If enum values are stored outside the program, never rely blindly on automatic numbering. Changing the order later can corrupt meaning.

Underlying Type of Enum

The default underlying type of an enum is int. C# also allows other integral types such as byte, short, long, and their unsigned versions. The underlying type controls storage size and the range of allowed numeric values.

enum DeviceMode : byte
{
    Off = 0,
    Standby = 1,
    Active = 2,
    Error = 3
}

Using byte can be useful in embedded systems, binary protocols, or large arrays where storage matters. In normal business applications, the default int is usually fine and keeps code simple.

Enum and Switch Statement

Enums work very well with switch because each member represents a clear branch. This is common when different statuses need different actions.

switch (status)
{
    case OrderStatus.Pending:
        Console.WriteLine("Order received");
        break;

    case OrderStatus.Shipped:
        Console.WriteLine("Order is on the way");
        break;

    case OrderStatus.Cancelled:
        Console.WriteLine("Order was cancelled");
        break;
}

A switch over an enum keeps branching logic readable. In modern C#, switch expressions can also be used when you want to return a value based on an enum member.

Converting Enum to Number and String

Because enums are backed by numbers, you can cast an enum value to its underlying numeric value. You can also convert it to a string to display the member name.

OrderStatus status = OrderStatus.Shipped;

int number = (int)status;
string name = status.ToString();

Console.WriteLine(number);
Console.WriteLine(name);

This is useful for logging, debugging, serialization, and display. For public APIs, be careful before exposing raw enum numbers. Names are easier to understand, but numbers are sometimes more stable for long-term storage when values are explicitly controlled.

Parsing String to Enum

C# can parse strings into enum values using Enum.Parse or Enum.TryParse. In real applications, TryParse is usually safer because invalid input does not throw an exception.

string input = "Delivered";

if (Enum.TryParse(input, out OrderStatus parsedStatus))
{
    Console.WriteLine(parsedStatus);
}

This is useful when values come from command-line arguments, configuration files, forms, or API requests. If input comes from users, always validate it before trusting it.

Flags Enum in C#

A flags enum represents a combination of values. It is used when multiple options can be enabled at the same time. Permission systems are a common example. To create a proper flags enum, values should usually be powers of two.

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

These values can be combined using the bitwise OR operator.

FilePermission permission = FilePermission.Read | FilePermission.Write;

if (permission.HasFlag(FilePermission.Read))
{
    Console.WriteLine("Read allowed");
}

The [Flags] attribute improves formatting and makes the enum intention clear. Without powers of two, combinations can overlap and produce confusing results.

Enum Validation

A surprising detail is that C# can store numeric values that are not declared in the enum. This can happen through casting, deserialization, or external data. Because of this, validation may be required when enum values come from outside the program.

OrderStatus status = (OrderStatus)99;

if (!Enum.IsDefined(typeof(OrderStatus), status))
{
    Console.WriteLine("Invalid order status");
}

Enum.IsDefined is helpful for normal enums, but flags enums need different validation because combinations may be valid even when the exact combined value is not directly listed as a member.

Enum vs Constants

FeatureEnumConstants
PurposeFixed set of related valuesIndividual fixed values
Type safetyBetterDepends on usage
Best exampleOrder status, role, modeTax rate, max retry count
GroupingBuilt into the typeManual grouping through classes

If the values are choices from one category, use an enum. If the value is an independent fixed number or string, use a constant. This keeps the model closer to the real meaning of the data.

Default Value of Enum

The default value of an enum variable is the value whose underlying number is 0. This is important because fields, array elements, and default-initialized variables can automatically become zero. If your enum does not define a meaningful zero member, the object can still contain that value internally.

enum PaymentStatus
{
    Unknown = 0,
    Pending = 1,
    Paid = 2,
    Failed = 3
}

Adding Unknown = 0 or None = 0 is often a good practice when a no-value state is possible. It makes default initialization explicit instead of accidental.

Enum with Databases

When enums are stored in a database, stability becomes important. If you store enum numbers, changing the numeric values later changes the meaning of old records. If you store enum names, renaming a member can break old data or API compatibility. Both approaches can work, but the rule must be intentional.

For internal systems, storing explicit numbers is common because it is compact and stable when values are managed carefully. For APIs and human-readable configuration, strings can be easier to debug. In both cases, avoid reusing old enum values for a new meaning because historical data may become misleading.

Enum in JSON and APIs

Enums often appear in JSON APIs. Depending on the serializer settings, an enum may be serialized as a number or as a string. Strings are easier for humans to understand, while numbers can be smaller and stable when explicitly assigned.

OrderStatus status = OrderStatus.Delivered;

// Possible API output:
// "Delivered"
// or
// 3

For public APIs, document the expected enum format clearly. If clients depend on the API, changing from numbers to strings or renaming enum values can become a breaking change.

Versioning Enum Values

Enums look simple, but they need careful versioning when they cross application boundaries. Adding a new enum member may break old clients if they do not know how to handle it. Removing or renaming a member is even more dangerous because existing data, logs, reports, and integrations may still contain the old value.

A safer approach is to add new members without changing old numeric values, keep old members when backward compatibility matters, and make unknown handling part of the code. This is why many production enums include an Unknown value and a default branch in switch logic.

Enums are best when the list of choices is controlled by the developer and changes rarely. If the values are managed by users, administrators, or external data, a database table or configuration model is usually more flexible. Choosing between enum and data-driven design early prevents painful migrations later.

In short, an enum should describe a small vocabulary that belongs to the source code. It should not become a replacement for lookup tables, permissions databases, or business data that must change without redeploying the application.

This keeps the enum reliable, readable, and stable across future code changes.

Stable enum design matters in production.

Common Mistakes with Enum in C#

  • Using automatic numbering for values stored in a database.
  • Adding new enum members in the middle and changing old numeric meanings.
  • Using enum when new values must be added dynamically by users.
  • Forgetting powers of two in flags enums.
  • Trusting parsed or cast enum values without validation.

Best Practices for Enum in C#

Name enum types clearly, use singular names such as OrderStatus, assign explicit values when the enum crosses system boundaries, and use None = 0 when a no-value state is meaningful. Keep enum members stable if they are stored or shared externally.

Use enums for stable choices, not for data that changes frequently. If admins or users need to create new values at runtime, a database table is usually better than an enum because enum members are compiled into the application.