As part of my university seminar, I found myself writing this little enum:

 

using System.ComponentModel;

namespace OpenUni.Domain.Modules
{
    public enum ModuleTypes
    {
        [Description("ר")]
        Standard,

        [Description("מ")]
        Advanced,

        [Description("מס")]
        AdvancedSeminar,

        [Description("תש")]
        Masters
    }
}

 

If you can't read Hebrew then for the sake of this post, the following is applicable:

 

using System.ComponentModel;

namespace OpenUni.Domain.Modules
{
    public enum ModuleTypes
    {
        [Description("A standard module")]
        Standard,

        [Description("An advanced module")]
        Advanced,

        [Description("An advanced seminar")]
        AdvancedSeminar,

        [Description("Module in a Masters course")]
        Masters
    }
}

 

You want the description of a given value, or to parse a given description string (say from the DB) to get the value it represents.

Many code bases I've seen contains an EnumHelper class or a variation of one, which allow just the same. Many of these use un-cached reflection to achieve that. As in my scenario this mapping will happen many (I mean *many*) times, I thought about making it a bit more agile.

 

Here's what I got:

 

using System; using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Reflection; namespace NHibernate.Type

{ /// <summary> /// Allow access to enum values with /// <see cref="DescriptionAttribute">DescriptionAttribute</see> /// set on them /// </summary> public static class DescribedEnumHandlers { private static readonly IDictionary handlers = new ListDictionary(); /// <summary> /// Initialises enum types to be used with the <see cref="DescribedEnumHandlers"></see> /// </summary> /// <param name="assemblies">The assemblies to grab described enums from</param> public static void Initialise(params Assembly[] assemblies) { Initialise((IEnumerable<Assembly>)assemblies); } /// <summary> /// Initialises enum types to be used with the <see cref="DescribedEnumHandlers"></see> /// </summary> /// <param name="assemblies">The assemblies to grab described enums from</param> public static void Initialise(IEnumerable<Assembly> assemblies) { var titledEnums = from assembly in assemblies select assembly into a from type in a.GetTypes() where type.IsEnum && (from f in type.GetFields() where f.GetCustomAttributes(typeof (DescriptionAttribute), false).Length == 1 select f ).Count() > 0 orderby type.FullName select type; foreach (var type in titledEnums) { handlers.Add(type, new DescribedEnumHandler(type)); } } /// <summary> /// Extract the description for a given enum value /// </summary> /// <param name="value">An enum value</param> /// <returns>It's description, or it's name if there's no registered description for the given value</returns> public static string EnumToDescription(object value) { var handler = handlers[value.GetType()] as DescribedEnumHandler; return handler != null ? handler.GetDescriptionFrom((Enum)value) : value.ToString(); } /// <summary> /// Gets the enum value for a given description or value /// </summary> /// <typeparam name="T">The enum type</typeparam> /// <param name="stringValue">The enum value or description</param> /// <returns>An enum value matching the given string value, as description (using <see cref="DescriptionAttribute">DescriptionAttribute</see>) or as value</returns> public static Enum ToEnumValue<T>(this string stringValue) where T :struct { var type = typeof (T); var handler = handlers[type] as DescribedEnumHandler; return handler != null ? handler.GetValueFrom(stringValue) : (Enum)Enum.Parse(type, stringValue, false); } /// <summary> /// Used to cache enum values descriptions mapper /// </summary> private class DescribedEnumHandler { private readonly IDictionary<Enum, string> toDescription = new Dictionary<Enum, string>(); private readonly IDictionary<string, Enum> fromDescription = new Dictionary<string, Enum>(); public DescribedEnumHandler(Type type) { var enumEntrys = from f in type.GetFields() let attributes = f.GetCustomAttributes(typeof(DescriptionAttribute), false) where attributes.Length == 1 let attribute = (DescriptionAttribute)attributes[0] select new { Value = (Enum)Enum.Parse(type, f.Name), attribute.Description }; foreach (var enumEntry in enumEntrys) { toDescription[enumEntry.Value] = enumEntry.Description; fromDescription[enumEntry.Description] = enumEntry.Value; } } public string GetDescriptionFrom(Enum value) { return toDescription[value]; } public Enum GetValueFrom(string title) { return fromDescription[title]; } } } }

 

Usage:

DescribedEnumHandlers.Initialise(typeof(ModuleTypes).Assembly);

Console.WriteLine(DescribedEnumHandlers.EnumToDescription(ModuleTypes.Standard));
Console.WriteLine("ר".ToEnumValue<ModuleTypes>());

 

Next time I'll show you how I made it play nicely with NHibernate

 

 

* code is licensed as BSD if you wish to use it