Associating Strings with enums in C#
I have seen other great articles out lining the benefits of some pretty clever and useful helper classes for enums. Many of these methods almost exactly mirror methods I had written in my own EnumHelper class. (Isn't it crazy when you imagine how much code duplication there must be like this out there!)
One thing that I don't see emphasized much is trying to associated string values with enums. For example, what if you want to have a Drop Down list that you can choose from a list of values (which are backed by an enum)? Lets start with a test enum:
public enum States
{
California,
NewMexico,
NewYork,
SouthCarolina,
Tennessee,
Washington
}
So if you made a drop down list out of this enum, using the ToString() method, you would get a drop down that looks like this:
While most people will understand this, it should really be displayed like this:
"But enums can't have spaces in C#!" you say. Well, I like to use the System.ComponentModel.DescriptionAttribute to add a more friendly description to the enum values. The example enum can be rewritten like this:
public enum States
{
California,
[Description("New Mexico")]
NewMexico,
[Description("New York")]
NewYork,
[Description("South Carolina")]
SouthCarolina,
Tennessee,
Washington
}
Notice that I do not put descriptions on items where the ToString() version of that item displays just fine.
How Do We Get To the Description?
Good question! Well, using reflection of course! Here is what the code looks like:
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
This method first looks for the presence of a DescriptionAttribute and if it doesn't find one, it just returns the ToString() of the value passed in. So
GetEnumDescription(States.NewMexico);
returns the string "New Mexico".
A Free Bonus: How to Enumerate Enums
Ok, so now we know how to get the string value of an enum. But as a free bonus, I also have a helper method that allows you to enumerate all the values of a given enum. This will allow you to easily create a drop down list based on an enum. Here is the code for that method:
public static IEnumerable<T> EnumToList<T>()
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Array enumValArray = Enum.GetValues(enumType);
List<T> enumValList = new List<T>(enumValArray.Length);
foreach (int val in enumValArray)
{
enumValList.Add((T)Enum.Parse(enumType, val.ToString()));
}
return enumValList;
}
As you can see, the code for either of these methods isn't too complicated. But used in conjunction, they can be really useful. Here is an example of how we would create the drop down list pictured above based on our enum:
DropDownList stateDropDown = new DropDownList();
foreach (States state in EnumToList<States>())
{
stateDropDown.Items.Add(GetEnumDescription(state));
}
Pretty simple huh? I hope you find this as useful as I do.
One More Example
There is one more scenario that I often find myself needing to associate string values with enums - when dealing with legacy constant string based system. Lets say you have a library that has the following method:
public void ExecuteAction(int value, string actionType)
{
if (actionType == "DELETE")
Delete();
else if (actionType == "UPDATE")
Update();
}
(I tried to make this look as legacy as I could for a contrived example). What happens if somebody passes in "MyEvilAction" as a value for actionType? Well, whenever I see hard coded strings, that is a code smell that could possibly point to the use of enums instead. But sometimes you don't have control over legacy code and you have to deal with it. So you could make an enum which looks like this:
public enum ActionType
{
[Description("DELETE")]
Delete,
[Description("UPDATE")]
Update
}
(I know, I know, this is a very contrived example) Then you could call the ExecuteAction Method like this:
ExecuteAction(5, GetEnumDescription(ActionType.Delete));
This at least makes the code more readable and may also make it more consistent and secure.