Description
Sometimes, when using enumerations, you need to express the value as something other than the string representation of the enumeration value itself.
Let's say that I have the following enumeration that I want to put into a list for selection by a user:
public enum MyEnum
{
LaptopComputer,
TabletScreen,
VRHeadSet
}
The list will contain the items as they are named in the enumeration, which ends up looking pretty silly, as these selections are obviously not plain English.
As a solution to this problem, you could loop over the enumeration and create a switch statement, adding your own custom text to the list for each item in the enumeration, like so:
foreach (var name in Enum.GetNames(typeof(MyEnum)))
{
MyEnum myEnum = (MyEnum)Enum.Parse(typeof(MyEnum), name);
switch (myEnum)
{
case MyEnum.LaptopComputer:
items.Add("Laptop Computer");
break;
case MyEnum.TabletScreen:
items.Add("Tablet Screen");
break;
case MyEnum.VRHeadSet:
items.Add("Virtual Reality Headset");
break;
default:
break;
}
}
Obviously that is not the best route to go, as you've not got your descriptions living in the loop that is populating your list, instead of living with
the enumeration where they belong. The solution to this issue is simple... use the [Description]
attribute that is available in the ComponentModel
namespace
in .NET to decorate your enumeration values.
public​ enum MyEnum
{
[Description("Laptop Computer")]
LaptopComputer,
[Description("Tablet Screen")]
TabletScreen,
[LocalizedDescriptionAttribute("VRHeadSet")]
VRHeadSet
}
Simple, right? Now we've got the descriptions where they belong- attached to the enumeration values themselves. (You'll notice the last enumeration value is a little different,
which I will explain later.) So, now that we've got these descriptions attached to the values of our enumeration... how do we get at them from our code that is adding items to a list?
Easy! We'll just create a method called GetDescription
that takes an enum value, and returns the description via reflection.
public static string GetDescription(MyEnum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
Now that we have a method to return the descriptions of the values, our loop can look something like this:
foreach (var name in Enum.GetNames(typeof(MyEnum)))
{
MyEnum myEnum = (MyEnum)Enum.Parse(typeof(MyEnum), name);
items.Add(EnumExtensions.GetDescription(myEnum));
}
That's much simpler than the first loop, right? Well, we can make it even more simple, and make the GetDescription
method into an extension method, which
you can then call right off of the end of a enumeration value. Making the method an extension method also means that it will apply to all of the enumerations
in the namespace.
Now, let me explain the different attribute decoration on the VRHeadSet enumeration value. You've probably noticed that it is decorated with the LocalizedDescriptionAttribute
,
as opposed to the DescriptionAttribute
. (This attribute inherits the previously described DescriptionAttribute
.) This allows us to localize the descriptions on each of the enum values
by overriding the string that is put into the underlying description attribute when it is created. The value that is put into the LocalizedDescriptionAttribute
on the enumeration
value is the name of the string resource in the application's resource file.
Code
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Getting enum descriptions via extension method:");
Console.WriteLine();
Console.WriteLine("{0} has description '{1}'", MyEnum.Value1, MyEnum.Value1.GetDescription());
Console.WriteLine("{0} has description '{1}'", MyEnum.Value2, MyEnum.Value2.GetDescription());
Console.WriteLine("{0} has description '{1}'", MyEnum.Other, MyEnum.Other.GetDescription());
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Getting enum descriptions via function call:");
Console.WriteLine();
Console.WriteLine("{0} has description '{1}'", MyEnum.Value1, EnumExtensions.GetDescription(MyEnum.Value1));
Console.WriteLine("{0} has description '{1}'", MyEnum.Value2, EnumExtensions.GetDescription(MyEnum.Value2));
Console.WriteLine("{0} has description '{1}'", MyEnum.Other, EnumExtensions.GetDescription(MyEnum.Other));
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
public enum MyEnum
{
[Description("This is value 1")]
Value1,
[Description("This is value 2")]
Value2,
[LocalizedDescriptionAttribute("OtherValueDescription")]
Other,
}
public static class EnumExtensions
{
public static string GetDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr = Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
}
}
public class LocalizedDescriptionAttribute : DescriptionAttribute
{
public LocalizedDescriptionAttribute(string resourceId)
: base(Resource.ResourceManager.GetString(resourceId))
{
}
}