C# Enum: Definition, Examples, Best Practices, and Pitfalls

Editor’s Note: This post originally appeared on Submain.

We’ve discussed arrays and lists in C#. This time, we’ll take the same journey with a funky little type called the enum. C# enums are very useful constructs, but they have some quirky behavior that can bite you if you’re not careful.

Let’s now see what makes enums tick, how to use them effectively, and what to be careful of when using them.

C# Enum: Under the Hood

Conceptually, enums are certainly simpler than arrays and lists. Enums allow a developer to create “a distinct value type with a set of named constants,” according to Microsoft docs. In normal human language, enums let you create “categories” of things and place them into their own type. An example will help to drive the point home.

Let’s say you have a payroll system which keeps track of employee types. These types include values like “contractor,” “salaried,” and “hourly.” This is a great place for an enum type. We’ll see why they’re so useful in a minute, but first, let’s see how to declare one.

public enum EmployeeType {
  Salaried,
  Contractor,
  Hourly
}

Pretty simple, right? The compiler assigns an integer to each category when the code is compiled. In the above example, “Salaried” will be assigned zero, “Contractor” will be assigned the number one, and so on. This makes storage of enums very simple. In fact, they’re value types, meaning that they’re stored on stack memory, not heap memory.

We’ve seen what enums look like, but what’s the big deal? The benefit comes in the form of code readability and reliability. Continuing with the payroll system example, if you have payroll numbers assigned to each type of employee, you no longer need to compare an employee type to a number explicitly, like this:

if(employee.employeeType == 2) {
  // some code...
}

Developers refer to these integers as “magic numbers.” When another developer comes along to update this code or read it, they aren’t going to understand what “2” means. If you need to change the employee type, how do you know what number to place in the if statement?

If you see code like this, enums are a great way to refactor it.

I think you’d agree that using the enum is much clearer to the reader.

if(employee.employeeType == EmployeeType.Hourly) {
  // some code here...
}

Best Practice #1: Use enums to eliminate “magic numbers” from your code. Make your code explicit and easy to read.

Pitfall #1: Using “magic numbers” leads to hard-to-understand code and business logic errors. Enums will eliminate these issues.

The C# Enum Shell Game

The number behind enums is usually not important. However, there are situations where C# enums become a bit of a shell game. If you’re not careful, those hidden numbers can get you into trouble. There are three ways that the relationship between the C# enums and the numbers behind them can get you into trouble: not using zero, casting an int to an enum, and changing enums your application depends on.

No One Likes a Zero

As I mentioned above, C# enum values begin at zero by default. This is fine in most cases. However, you do have the option to set your own integer values for each enum category. In the payroll example, you might interface with a legacy system that already has employee type codes defined. In this case, you might have to make sure the enum values match up with the downstream application’s numbering system. You can do that when you declare the enum.

public enum EmployeeType {
  Salaried = 14,
  Contractor = 52,
  Hourly = 15
}

However, there’s a pitfall to this method. The compiler assigns a default value to any variables that are declared but not initialized. A numeric type like enum will default to zero if not initialized. Therefore, you can have code that does this:

class Program
{
    static void Main(string[] args)
    {
        var emp = new Employee();
        Console.WriteLine(emp.employeeType);
        Console.ReadLine();
    }
}

public enum EmployeeType
{
    Salaried = 1,
    Contractor,
    Hourly
}

    class Employee
    {
        public EmployeeType employeeType { get; set; }
    }

What will the Console.WriteLine call type out? It writes out zero. But zero means nothing in our enum. This can lead to logic problems within your code. For example, switch statements will be thrown off if you don’t initialize the employeeType property with a constructor.

Instead of letting zero hang out there, make it a default value. For instance, you could have an “Unknown” category set to zero. That way, a developer has to explicitly set it to another valid value.

Best Practice #2: Don’t assign different numbers to categories unless you really need to in order for the system to function.

Pitfall #2: Assign a value to zero to avoid business logic errors in switch statements.

Finding Your Castaways

The C# enum’s relationship to the integer type can lead to some strange behavior and code. You can’t implicitly cast an integer value to an enum. In other words, if a method requires you to pass in an EmployeeType when calling it, you can’t pass a “1” into the method to signify a Salaried employee. This type safety is actually a major advantage of using enums over “magic numbers” as we’ve mentioned before.

Unfortunately, you can explicitly cast an integer to an enum. The following code compiles.

EmployeeType empType = (EmployeeType) 500;

This is quite a strange phenomenon, leading to an undefined value for this variable. Please don’t ever do this. Use enums as they were meant to be used.

Best Practice #3: Don’t cast integer values into enums. Just don’t.

Enumerating Your Dependencies

Another tricky behavior you may notice when using C# enums is how they’re constructed. The integer values that represent each category within the enum are assigned at compile time. Let’s take our payroll example.

public enum EmployeeType {
  Salaried = 1,
  Contractor,
  Hourly
}

The compiler assigns numbers to enum categories at compile time. So pay attention to enums your code depends on. You have to recompile any code that uses an enum that changes these numbers. Otherwise, your code will still think the old values exist.

Best Practice #4: If you need to update an enum, add more values to the “end” of it so that a new number is added without affecting the existing ones.

Pitfall #3: Try not to change the order or number of the categories in your enums. This can break code that depends on your enums unless the dependent code is recompiled.

C# Enums Are Your Friend

Don’t be scared by some of these weird behaviors of C# enums. All in all, enums are efficient, easy to use, and a great way to make your code simpler to read and maintain. Go get rid of those “magic numbers” and use a little magic called enums instead.

Leave a Reply