Converting Enumerations to User Readable Strings in .NET

Suppose you have the following scenario: You have a function that can return multiple return types codes via an enum value. For each of those enum values, you want to inform the user the result of the operation by some user friendly string.

A common implementation I see is some sort of switch statement that resolves enumerations to a string by way of a switch statement or hash table or something. And whenever that string is needed, call into said method.

And although this is viable, this creates a disconnect between the enum and it’s actual string literal definition. In addition, for each new enumeration value, that giant switch statement needs to be updated with a new string value. If there were only a clean way to map an enum to a string value automatically… and there is! With clever usage of attributes, reflection, and extension methods, one can do something like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace EnumString
{
[AttributeUsage(AttributeTargets.Field)]
class EnumStringAttribute : Attribute
{
string myValue;
public EnumStringAttribute(string value)
{
myValue = value;
}

public override string ToString()
{
return myValue.ToString();
}
}

static class ExtensionMethods
{
public static string ToUserString(this Enum enumeration)
{
var type = enumeration.GetType();
var field = type.GetField(enumeration.ToString());
var enumString = (from attribute in field.GetCustomAttributes(true) where attribute is EnumStringAttribute select attribute).FirstOrDefault();
if (enumString != null)
return enumString.ToString();
return enumeration.ToString();
}
}

enum AuthenticationResult
{
[EnumString("This username is not registered.")]
NotRegistered,
[EnumString("Incorrect password.")]
BadPassword,
[EnumString("Logging in...")]
Success,
}

class Program
{
static void Main(string[] args)
{
Console.WriteLine(AuthenticationResult.BadPassword.ToUserString());
Console.WriteLine(AuthenticationResult.NotRegistered.ToUserString());
Console.WriteLine(AuthenticationResult.Success.ToUserString());
}
}
}

Delaring string values can simply be done inline, and the usage is simple as well! Just call the new extension method to get your user friendly string!

(Of course, for localization, you may want to map the enum to a string resource rather than a hard coded string value; but a similar approach can be used.)

11 comments:

Igor said...

It so sucks that you can't overwrite ToString() (or any enum method), I had this "problem" to, and I decided to write a static method in a class to do convert the enum to string instead of adding the Extension Method.

Koush said...

Yeah, that's how I used to do it also! Extension methods and the attribute just make it syntactically pleasing. :)

Amber said...

duh

syntactially pleasing?

how about

Console.WriteLine(BadPassword);

This is the way any C++/Python programmer would accept:

std::cout << "Hola, password verification resulted in code " << AuthenticationResult.BadPassword << std::endl;

Stuff like that comes for free once you start using operator>>/<<. You'd still have to provide that implementation, much like in .NET

I can live with qualifying the enum id.

Koush said...

You just made a whole lot of no sense.

I know you can operator overload in C++, but you can't overload ToString of an enum. Hence the whole point of this.

Koush said...

Console.WriteLine(BadPassword) would not compile, so I have no idea what you are talking about...

MonkeyPushButton said...

If you're mapping to a localised resource, you could surely just as well use Enum.GetName and name your resources appropriately?

Igor said...

@MonkeyPushButton: well yes and no, in my case I had[1] to convert the GetName to lowercase, and that looks ugly!

[1] I had to do that because of a compatibility issue with the binary representation of the object (written in C).

Evgeniy said...

I have another solution without using attributes. This is the helper class which stores Dictionary(String, YourEnumeration) inside.
It requres neither extension methods (.NET 3.0+ only) nor metadata analysis. Look at the ee.codeplex.com

Koush said...

The solution you provided is one I described in my post, which I did not find ideal:
"A common implementation I see is some sort of switch statement that resolves enumerations to a string by way of a switch statement or hash table or something."

Johan Hernandez said...

doesn't System.ComponentModel.TypeConverter + String.Format solves this? :)

Anonymous said...

Instead of creating the custom attribute, I use the Description attribute to achieve the same results with the extension method.