String representation of an Enum – Dev

The best answers to the question “String representation of an Enum” in the category Dev.

QUESTION:

I have the following enumeration:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

The problem however is that I need the word “FORMS” when I ask for AuthenticationMethod.FORMS and not the id 1.

I have found the following solution for this problem (link):

First I need to create a custom attribute called “StringValue”:

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

Then I can add this attribute to my enumerator:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

And of course I need something to retrieve that StringValue:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

Good now I’ve got the tools to get a string value for an enumerator.
I can then use it like this:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

Okay now all of these work like a charm but I find it a whole lot of work. I was wondering if there is a better solution for this.

I also tried something with a dictionary and static properties but that wasn’t better either.

ANSWER:

Use method

Enum.GetName(Type MyEnumType,  object enumvariable)  

as in (Assume Shipper is a defined Enum)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

There are a bunch of other static methods on the Enum class worth investigating too…

ANSWER:

Try type-safe-enum pattern.

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Update
Explicit (or implicit) type conversion can be done by

  • adding static field with mapping

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • n.b. In order that the initialisation of the the “enum member” fields doesn’t throw a NullReferenceException when calling the instance constructor, be sure to put the Dictionary field before the “enum member” fields in your class. This is because static field initialisers are called in declaration order, and before the static constructor, creating the weird and necessary but confusing situation that the instance constructor can be called before all static fields have been initialised, and before the static constructor is called.
  • filling this mapping in instance constructor

    instance[name] = this;
    
  • and adding user-defined type conversion operator

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }
    

ANSWER:

Unfortunately reflection to get attributes on enums is quite slow:

See this question: Anyone know a quick way to get to custom attributes on an enum value?

The .ToString() is quite slow on enums too.

You can write extension methods for enums though:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

This isn’t great, but will be quick and not require the reflection for attributes or field name.


C#6 Update

If you can use C#6 then the new nameof operator works for enums, so nameof(MyEnum.WINDOWSAUTHENTICATION) will be converted to "WINDOWSAUTHENTICATION" at compile time, making it the quickest way to get enum names.

Note that this will convert the explicit enum to an inlined constant, so it doesn’t work for enums that you have in a variable. So:

nameof(AuthenticationMethod.FORMS) == "FORMS"

But…

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

ANSWER:

You can reference the name rather than the value by using ToString()

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

The documentation is here:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

…and if you name your enums in Pascal Case (as I do – such as ThisIsMyEnumValue = 1 etc.) then you could use a very simple regex to print the friendly form:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

which can easily be called from any string:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Outputs:

Convert My Crazy Pascal Case Sentence To Friendly Case

That saves running all the way around the houses creating custom attributes and attaching them to your enums or using lookup tables to marry an enum value with a friendly string and best of all it’s self managing and can be used on any Pascal Case string which is infinitely more reusable. Of course, it doesn’t allow you to have a different friendly name than your enum which your solution does provide.

I do like your original solution though for more complex scenarios though. You could take your solution one step further and make your GetStringValue an extension method of your enum and then you wouldn’t need to reference it like StringEnum.GetStringValue…

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

You could then access it easily straight from your enum instance:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());