About Me

Wednesday, August 10, 2011

Convert enum to a custom string

Enums are really usefull.
I'm using them in each application.
The primary benefit of using enum is that it makes your code more readable.
If you have variables that should has only strict number of values, then enums is really what you should use.
For example, if you have class that describing a person, its always better to have a property containing Gender.Male or Gender.Female then using just a string  "Male" or number "1" meaning that this is male and "0" for female.
Its not a problem even if you have dozen values, like age kinds:

public enum Age
{
  Unknown,
  Infant,
  Child,
  Adolescent,
  Adult,
  MatureAdult,
  OlderAdult,
  AgedAdult,
  Neonate
 }

If you are using enum you will be always sure that the code will not compile if you make a typo and type "Chaild" instead of "Child".
Its also much easy to add new states to the list when you need it.
In other words you are keep control all possible values that your variable may has. At the same time, the value  of the variable is looking friendly and well readable.

One of the problem with this approach is to show custom text that should be associated with the Enum value.

For instance, you want to build optional list that contains all possible values of the Age enum:
As you may see, the list looks ugly because the text is not readable at all. Also, you may want to describe each state and specify which age range is for each option so the user will be easy to select proper value :

To build such select list you should create a relation between the enum value and custom text value:

ASP.NET MVC Controller code:
public ActionResult Index()
        {
         Array values = Enum.GetValues(typeof(Age));
   List<KeyValuePair<string,string>> enumValues = new List<KeyValuePair<string, string>>();
         
   foreach (int value in values)
   {
    string text = string.Empty;
          switch ((Age)value)
          {
           case Age.Unknown:
            text = "Unknown";
            break;
           case Age.Infant:
            text = "Infant (>28 days<1 year)";
            break;
           case Age.Child:
      text = "Child (1-12 years)";
            break;
           case Age.Adolescent:
      text = "Adolescent (13-17 years)";
            break;
           case Age.Adult:
      text = "Adult (18-64 years)";
            break;
           case Age.MatureAdult:
      text = "Mature Adult (65-74 years)";
            break;
           case Age.OlderAdult:
      text = "Older Adult (75-84 years)";
            break;
           case Age.AgedAdult:
      text = "Aged Adult (>85 years)";
            break;
           case Age.Neonate:
      text = "Neonate (0-28 days)";
            break;
           default:
            throw new ArgumentOutOfRangeException();
          }
          
    enumValues.Add(new KeyValuePair<string, string>(text,value.ToString()));
         }
         
   ViewData["Ages"] = enumValues;
            
   return View();
        }

ASP.NET MVC View code:
<body>
    <% foreach (KeyValuePair<string,string> enumValue in (IEnumerable<KeyValuePair<string, string>>) ViewData["Ages"])
    { %>
      <%= Html.RadioButton(enumValue.Key, enumValue.Value)%>  
      <%= Html.Label(enumValue.Key, enumValue.Key)%> <br />
    <%
    } 
    %>
</body>

As you may see to build such relation, you should reserve some place in your code to have all this friendly strings and remember where this place is locating.
If you going to use it again, you should either copy/paste the same text or move it to the some class.
Both ways are not good because you and other developers of your project should always remember where is the place where the text can be changed.
Its great that there is a solution that allows to have custom text in the same place where you have Enum definition. You should use an attribute System.ComponentModel.DescriptionAttribute 
The attribute allows to specify any text as description for each enum element:
public enum Age
 {
  Unknown,
[Description("Infant (>28 days<1 year)")]
  Infant,
[Description("Child (1-12 years)")]
  Child,

  [Description("Adolescent (13-17 years)")]
  Adolescent,

  [Description("Adult (18-64 years)")]
  Adult,

  [Description("Mature Adult (65-74 years)")]
  MatureAdult,

  [Description("Older Adult (75-84 years)")]
  OlderAdult,

  [Description("Aged Adult (>85 years)")]
  AgedAdult,

  [Description("Neonate (0-28 days)")]
  Neonate
 }

When Enum is defined like above, its easy to get those custom text values with simple extender that's using reflection:

public static class EnumExtender
 {
  public static string GetDescription(this Enum en)
  {
   return GetDescription(en.GetType(), en.ToString());
  }

  public static string GetDescription(Type enumType, Enum en)
  {
   return GetDescription(enumType, en.ToString());
  }

  public static string GetDescription(Type enumType, string name)
  {
   MemberInfo[] members = enumType.GetMember(name);
   if (members.Length > 0)
   {
    MemberInfo mi = members[0];
    object[] attrs = mi.GetCustomAttributes(typeof(DescriptionAttribute), false);

    if (attrs.Length == 1 && attrs[0] is DescriptionAttribute)
    {
     DescriptionAttribute attr = (DescriptionAttribute)attrs[0];
     return attr.Description;
    }
   }
   return name;
  }

 }

In this way the code that builds custom text looks much better :
public ActionResult Index()
        {
         Array values = Enum.GetValues(typeof(Age));
   List<KeyValuePair<string,string>> enumValues = new List<KeyValuePair<string, string>>();
         
   foreach (int value in values)
   {
    string customText = ((Age)value).GetDescription();
    string enumValue = value.ToString();
    KeyValuePair<string, string> keyValuePair = new KeyValuePair<string, string>(customText, enumValue);

    enumValues.Add(keyValuePair);
   }

         ViewData["Ages"] = enumValues;
            
   return View();
        }

No comments:

Post a Comment