director, software developer, qualified tas / ict teacher.

Published: 7 months ago

Extending Enums to be more useful

For a client I’m currently working with, we needed to make use of enums to provide basic status settings, small properties that would ultimately be visible and changeable on an MVC front end. After entertaining the thought we could just use a table to hold what we’re after, it was decided that’s probably not a decent way to do it and I started looking into enums.

Enums are something I’ve always been told to tread carefully with, they’re not the easiest to extend or change if business requirements actually require it, and they’re not exactly user friendly if they’re more than one word mashed together.

I was holding out hope that the MVC 5.1 update with the enum view support (http://www.asp.net/mvc/overview/releases/mvc51-release-notes) would make things easier, but it’s much of a muchness really.

Using some creative searching, stringing together several solutions I was able to arrive at something fairly easy to implement that still allows you to keep the core essence of enums, while being able to supply context to a user when needed.

If you want to skip the breakdown you can see the demo at https://github.com/LucasMoffitt/EnumHelper.

Let’s say we wanted to take a small list of car types and prepare logic so that we redirect a user to a different page depending on their selection. Straight away it’s easy to define that in an enum.

public enum BasicCars
{
FourDoorSedan,
FourByFour,
SportsCar,
Convertible,
PickUp,
}

Only problem is, the design guys aren’t going to be happy if you want to go flashing “FourDourSedan” to a user by calling .ToString(). Of course, you could implement something to replace the text on the view, but that usually involves matching, checking and replacing the values. That starts you down a nice little trap where a potentially growing list would become difficult to maintain.
What if we made them all a bit more descriptive? For some reason a lot of people forget the magic that is Data Annotations, few tweaks here and there, and you now have descriptions you’d be happy to show anyone.

public enum Cars
{
[Description("Four Door Sedan")]
FourDoorSedan,
[Description("4 x 4 ")]
FourByFour,
[Description("Sports Car")]
SportsCar,
[Description("Convertible")]
Convertible,
[Description("Pick-Up Truck")]
PickUp,
}

Only problem now is getting that description out and making it useful. Here’s the fun part. Using the code below, you can now simply just call .Description() on any enum to output the contents of the description annotation.

public static string Description(this Enum value)
{
return GetCustomDescription(value);
}
private static string GetCustomDescription(object objEnum)
{
var fieldInfo = objEnum.GetType().GetField(objEnum.ToString());
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length > 0) ? attributes[0].Description : objEnum.ToString();
}

The code should be pretty self-explanatory, but why stop there? We’re now able to get the text for an item, but I get the feeling one by itself isn’t useful, why not generate a list of them?

public static IEnumerable GetList<T>() where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
throw new ArgumentException("This is for use with enums only!");
return Enum.GetValues(typeof(T)).Cast<T>().Select(x => GetCustomDescription(x));
}

This code, will take your enum and return an IEnumerable of the descriptions on each, perfect if you want to print a list of them.

var cars = EnumHelper.GetList<Cars>();

The one thing about enums though, is that even with that handy description, there will be times you want to change context while still being able to use the enum. Using our cars as an example, what if we wanted to then have the option to buy or sell those cars? For our example I’m building in an optional prefix, but there’s no reason you couldn’t have a suffix, or any other string manipulation of that description.

public static IEnumerable GetList<T>(string prefix) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
throw new ArgumentException("This is for use with enums only!");
return Enum.GetValues(typeof (T)).Cast<T>().Select(x => string.IsNullOrEmpty(prefix) ? GetCustomDescription(x) : string.Format("{0} {1}", prefix, GetCustomDescription(x)));
}

And can be used with:

var buyCars = EnumHelper.GetList<Cars>("Buy a");

While we’ve taken a little detour, it’s helped to show that the once simple enum can be used and abused somewhat easily. Going back to our original problem of showing it on a front end (and my disappointment with the 5.1 enum select list change) that we need them in a select list and easily usable in a view.

Using the same theory as above, all we now have to do is build a select list.

public static SelectList GetSelectList<T>(string prefix) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
throw new ArgumentException("This is for use with enums only!");
var items = Enum.GetValues(typeof(T)).Cast<T>().Select(x => new SelectListItem
{
Text = string.IsNullOrEmpty(prefix) ? GetCustomDescription(x) : string.Format("{0} {1}", prefix, GetCustomDescription(x)),
Value = Convert.ToInt32(x).ToString(CultureInfo.InvariantCulture)
});
return new SelectList(items, "Value", "Text");
}

Then when you want to use them in a dropdown on a view, you just use it like a normal select list.

@Html.Label("Car Type", new { @class = "col-md-2 control-label" })
@Html.DropDownListFor(m => m.SelectedCar, EnumExtension.GetSelectList<Cars>(), new { @class = "form-control" })

Magic.

One Comment.
  1. Stephan says:

    Add support for internationalization and you’ve got something useful :)

Have a Comment?

Some HTML is OK