Working with Enums

Since I’m always getting questions about handling enums, I decided to blog about it.

Back to the basics

First let me cover some ground for those who are not familiar at all with enums. If you are familiar with them already, just skip this section.

Say we have some code somewhere that looks like the following:

Console.WriteLine(SayHello((0)));  // prints out "Hello, Administrator!"
Console.WriteLine(SayHello((1)));  // prints out "Hello, Developer!"
Console.WriteLine(SayHello((2)));  // prints out "Hello, Secretary!" 

There’s no question about those parameters being passed to the SayHello: they’re magic numbers that should be avoided like the plague.  🙂

 The SayHello method looks like so:

private static string SayHello(int role)
{
    string roleName = string.Empty;

    switch (role)
    {
        case 0:
            roleName = "Administrator";
            break;
        case 1:
            roleName = "Developer";
            break;
        case 2:
            roleName = "Secretary";
            break;
    }

    return String.Format("Hello, {0}!", roleName);
}

Enter the Enums

Instead of passing magic numbers to the SayHello method, we could use an enum instead. The enum could be defined like so:

public enum Roles
{
    Administrator,
    Developer,
    Secretary,
}

The SayHello should then be changed to:

private static string SayHello(Roles role)
{
    string roleName = string.Empty;

    switch (role)
    {
        case Roles.Administrator:
            roleName = "Administrator";
            break;
        case Roles.Developer:
            roleName = "Developer";
            break;
        case Roles.Secretary:
            roleName = "Secretary";
            break;
    }

    return String.Format("Hello, {0}!", roleName);
}

And then could be called like so:

Console.WriteLine(SayHello((Roles.Administrator))); 
Console.WriteLine(SayHello((Roles.Developer))); 
Console.WriteLine(SayHello((Roles.Secretary))); 

It sure sounds more readable, right? No more need to either decipher magical numbers or pollute the code with in-line comments describing what the magical numbers mean.

The Enum’s underlying value

By default, the underlying value of an enum is an integer. Each member of the enum gets a value assigned to it automatically, starting with zero. One could change the way the values get assigned by just assigning them directly. For instance, our Roles enum could be changed so that the first item gets assigned the value of 4, and then all members get assigned incrementally from there:

public enum Roles
{
    Administrator = 4,
    Developer,
    Secretary,
}

Alternatively, each element could have a different value assigned to it:

public enum Roles
{
    Administrator = 3,
    Developer = 1,
    Secretary = 2,
}

That could be helpful because the enum could be mapping to values that get stored to and retrieved from a database, so that underlying value may be very important. The cool think is that nothing has to be changed on the places where the enum is used.

Little gotcha: don’t ever trust values being received as enums!

Because our Roles enum uses ints for its underlying value, the following code is valid:

Console.WriteLine(SayHello((Roles)1));

Since the number 1 can be cast to the Roles enum, the compiler is okay with that code. During runtime, things also work fine, because 1 maps to the Developer option within the enum. This is great, because the integer value could be coming from a database, so we could map database integers from a column to values of an enum.

However, what about the following code:

Console.WriteLine(SayHello((Roles)99));

Hmm, currently, there’s no element in the enum that maps to 99, which means that the switch statement in our SayHello method wouldn’t know how to handle such value. So this is something to keep in mind: never trust what you’re getting as an enum!

One option to address this issue is by always providing some default whenever an unknown value is passed as an enum. For example:

switch (role)
{
    case Roles.Administrator:
        roleName = "Administrator";
        break;
    case Roles.Developer:
        roleName = "Developer";
        break;
    case Roles.Secretary:
        roleName = "Secretary";
        break;
    default :
        roleName = "Unknown";
        break;
}

That works as long as the requirements for the method say it’s fine to have a default value. However, there are cases where unknown values should not be allowed. The way to accomplish is by checking whether the given value is actually defined in the enum, like so:

private static string SayHello(Roles role)
{
    if (!Enum.IsDefined(typeof(Roles), role))
    {
        throw new ArgumentException("Given role is not valid.", "role");
    }
    ...
} 

So remember: either handle bad values by having a default value, or just reject the input by checking whether the given value is defined in the enum or not. 

Use the ToString method on an enum

It’s pretty easy to use the string representation of an enums value; all we have to do is to call the ToString method on it! For instance, our SayHello method can be rewritten in the following way:

private static string SayHello(Roles role)
{
    if (!Enum.IsDefined(typeof(Roles), role))
    {
        throw new ArgumentException("Given role is not valid.", "role");
    }

    string roleName = role.ToString();

    return String.Format("Hello, {0}!", roleName);
}

Getting all strings out of an enum

Another common thing to do is list out all the elements of an enum as its string representation:

foreach (string element in Enum.GetNames(typeof(Roles)))
{
    Console.WriteLine(element);
}

The GetNames method on the Enum class returns a string array containing the string representation of each item defined on the given enum type.

Using enums to populate lists

It’s also very useful to use enums to populate lists (such as DropDown controls). The code below lists out all the elements of an enum, printing out both the underlying value as well as the string representation (those values can be used as the ValueMember and DisplayMember on a DropDown, respectively):

Type enumType = typeof(Roles);
foreach (int elementValue in Enum.GetValues(enumType))
{
    Console.WriteLine(String.Format("Value Member = {0} / Display Member = {1}",
        elementValue, Enum.GetName(enumType, elementValue)));
}

GetValues returns an array of integers (that is, if the underlying value of the enum is integer; the default), whereas the GetName method takes in the integer value and returns the string representation of the element. The code prints out the following:

enum[1]

Notice that both GetValues and GetName takes in a Type object, therefore, it uses Reflection to figure out what elements and values are declared on the specific enum type. It is a good idea to create a variable to store the Type object and reuse the same variable for both methods, as opposed to creating the Type objects every time.

Parsing a string into an enum

There are cases where what we have is the string that maps to an element on a enum, and we need to create an actual instance of the enum based on that string. The string can be parsed into an enum like so:

string foo = "Developer";
Roles role = (Roles)Enum.Parse(typeof(Roles), foo);

Note that the Parse method returns an object, so we always need to cast that object to the specific enum type that we expect.

Also keep in mind that the Parse method will throw an ArgumentException in case the string cannot be parsed into an enum element, so make sure to always check it and act accordingly:

string foo = "Teacher";
if (Enum.IsDefined(typeof(Roles), foo))
{
    Roles role = (Roles)Enum.Parse(typeof(Roles), foo);
}

Final Thoughts

Enums are certainly not the answer to everything, but they’re certainly helpful, and whenever they are the best choice for whatever it is you’re implementing, it’s good to know how to make the best use of them.

Also, make sure to check out this other post, "Fun with Extension Methods and Enums".

Advertisements
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: