tags:

views:

1648

answers:

12

I have the following enum

  public enum myEnum
     {
         ThisNameWorks, 
         This Name doesn't work
         Neither.does.this;
     }

Is it not possible to have enums with "friendly names" ?

Thanks

+1  A: 

They follow the same naming rules as variable names. Therefore they should not contain spaces.

Also what you are suggesting would be very bad practice anyway.

TimothyP
+12  A: 

Enum value names must follow the same naming rules as normal variables in C#, therefore only first name is correct.

RaYell
Not only variables, but identifiers in general.
Joey
+1  A: 

Enum names live under the same rules as normal variable names, i.e. no spaces or dots in the middle of the names... I still consider the first one to be rather friendly though...

Robban
+2  A: 
public enum myEnum
{
         ThisNameWorks, 
         This_Name_can_be_used_instead,

}
aJ
Possible, but should be avoided. Doesn't follow MS naming guidelines. I think it's just available for old C developers or something :p
TimothyP
+7  A: 

No, but you can use the DescriptionAttribute to accomplish what you're looking for.

Yuriy Faktorovich
+3  A: 

You can use Discription Attribute to get that friendly name. You can use code below :

/// <summary>
        /// Very good method to Override ToString on Enums
        /// Case : Suppose your enum value is EncryptionProviderType and you want 
        /// enumVar.Tostring() to retrun "Encryption Provider Type" then you should use this method.
        /// Prerequisite : All enum members should be applied with attribute [Description("String to be returned by Tostring()")]
        /// Example : 
        ///  enum ExampleEnum
        ///  {
        ///   [Description("One is one")]
        ///    ValueOne = 1,
        ///    [Description("Two is two")]
        ///    ValueTow = 2
        ///  }
        ///  
        ///  in your class
        ///  ExampleEnum enumVar = ExampleEnum.ValueOne ;
        ///  Console.WriteLine(ToStringEnums(enumVar));
        /// </summary>
        /// <param name="en"></param>
        /// <returns></returns>
        public static string ToStringEnums(Enum en)
        {
            Type type = en.GetType();

            MemberInfo[] memInfo = type.GetMember(en.ToString());
            if (memInfo != null && memInfo.Length > 0)
            {
                object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attrs != null && attrs.Length > 0)
                    return ((DescriptionAttribute)attrs[0]).Description;
            }
            return en.ToString();
        }
Mahin
A: 

I suppose that you want to show your enum values to user therefore you want them to have some friendly name . Here's my suggestion : Use an enum type pattern.Although you should make some effort implementing it but it really worth it.

public class MyEnum
{

public static readonly MyEnum Enum1=new MyEnum("This will work",1);
public static readonly MyEnum Enum2=new MyEnum("This.will.work.either",2);
public static readonly MyEnum[] All=new []{Enum1,Enum2};
private MyEnum(string name,int value)
{
Name=name;
Value=value;
}

public string Name{get;set;}
public int Value{get;set;}

public override string ToString()
{
return Name;

}
}
Beatles1692
+23  A: 

You could use the Description attribute, as Yuriy suggested. The following extension method makes it easy to get the description for a given value of the enum :

    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;
    }

You can use it like that :

public enum MyEnum
{
    [Description("Description for Foo")]
    Foo,
    [Description("Description for Bar")]
    Bar
}

MyEnum x = MyEnum.Foo;
string description = x.GetDescription();
Thomas Levesque
I'm planning to add support for this into UnconstrainedMelody tonight.
Jon Skeet
@Thomas, great idea, I edited to eliminate horiz scrolling...
Charles Bretana
Cool :) I'm very interested in your UnconstrainedMelody project, but I noticed something quite annoying : the compiler complains if I try to use the Enums extension methods on a non-enum type (as expected), but Intellisense doesn't take the Enum constraint into account, it shows the methods for all value types... probably a bug in VS2008
Thomas Levesque
@Charles : thanks for the edit !
Thomas Levesque
I just checked with VS2010, the Intellisense bug mentioned above seems to be fixed
Thomas Levesque
+6  A: 

If you have the following enum:

public enum MyEnum {
    First,
    Second,
    Third
}

You can declare Extension Methods for MyEnum (like you can for any other type). I just whipped this up:

namespace Extension {
    public static class ExtensionMethods {
        public static string EnumValue(this MyEnum e) {
            switch (e) {
                case MyEnum.First:
                    return "First Friendly Value";
                case MyEnum.Second:
                    return "Second Friendly Value";
                case MyEnum.Third:
                    return "Third Friendly Value";
            }
            return "Horrible Failure!!";
        }
    }
}

With this Extension Method, the following is now legal:

Console.WriteLine(MyEnum.First.EnumValue());

Hope this helps!!

Pwninstein
Excellent answer!
p.campbell
Keep in mind that the switch statement is effectively a linear search with complexity O(n) -- the worst case requires comparison with very value in the enum.
Roy Tinker
+4  A: 

One problem with this trick is that description attribute cannot be localized. I do like a technique by Sacha Barber where he creates his own version of Description attribute which would pick up values from the corresponding resource manager.

http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx

Although the article is around a problem that's generally faced by WPF developers when binding to enums, you can jump directly to the part where he creates the LocalizableDescriptionAttribute.

Trainee4Life
+1  A: 

Some great solutions have already been posted. When I encountered this problem, I wanted to go both ways: convert an enum into a description, and convert a string matching a description into an enum.

I have two variants, slow and fast. Both convert from enum to string and string to enum. My problem is that I have enums like this, where some elements need attributes and some don't. I don't want to put attributes on elements that don't need them. I have about a hundred of these total currently:

public enum POS
{   
    CC, //  Coordinating conjunction
    CD, //  Cardinal Number
    DT, //  Determiner
    EX, //  Existential there
    FW, //  Foreign Word
    IN, //  Preposision or subordinating conjunction
    JJ, //  Adjective
    [System.ComponentModel.Description("WP$")]
    WPDollar, //$   Possessive wh-pronoun
    WRB, //     Wh-adverb
    [System.ComponentModel.Description("#")]
    Hash,
    [System.ComponentModel.Description("$")]
    Dollar,
    [System.ComponentModel.Description("''")]
    DoubleTick,
    [System.ComponentModel.Description("(")]
    LeftParenth,
    [System.ComponentModel.Description(")")]
    RightParenth,
    [System.ComponentModel.Description(",")]
    Comma,
    [System.ComponentModel.Description(".")]
    Period,
    [System.ComponentModel.Description(":")]
    Colon,
    [System.ComponentModel.Description("``")]
    DoubleBackTick,
    };

The first method for dealing with this is slow, and is based on suggestions I saw here and around the net. It's slow because we are reflecting for every conversion:

using System;
using System.Collections.Generic;
namespace CustomExtensions
{

/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
public static class EnumExtensions
{
    /// <summary>
    /// Gets the description string, if available. Otherwise returns the name of the enum field
    /// LthWrapper.POS.Dollar.GetString() yields "$", an impossible control character for enums
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string GetStringSlow(this Enum value)
    {
        Type type = value.GetType();
        string name = Enum.GetName(type, value);
        if (name != null)
        {
            System.Reflection.FieldInfo field = type.GetField(name);
            if (field != null)
            {
                System.ComponentModel.DescriptionAttribute attr =
                       Attribute.GetCustomAttribute(field,
                         typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
                if (attr != null)
                {
                    //return the description if we have it
                    name = attr.Description; 
                }
            }
        }
        return name;
    }

    /// <summary>
    /// Converts a string to an enum field using the string first; if that fails, tries to find a description
    /// attribute that matches. 
    /// "$".ToEnum<LthWrapper.POS>() yields POS.Dollar
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T ToEnumSlow<T>(this string value) //, T defaultValue)
    {
        T theEnum = default(T);

        Type enumType = typeof(T);

        //check and see if the value is a non attribute value
        try
        {
            theEnum = (T)Enum.Parse(enumType, value);
        }
        catch (System.ArgumentException e)
        {
            bool found = false;
            foreach (T enumValue in Enum.GetValues(enumType))
            {
                System.Reflection.FieldInfo field = enumType.GetField(enumValue.ToString());

                System.ComponentModel.DescriptionAttribute attr =
                           Attribute.GetCustomAttribute(field,
                             typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;

                if (attr != null && attr.Description.Equals(value))
                {
                    theEnum = enumValue;
                    found = true;
                    break;

                }
            }
            if( !found )
                throw new ArgumentException("Cannot convert " + value + " to " + enumType.ToString());
        }

        return theEnum;
    }
}
}

The problem with this is that you're doing reflection every time. I haven't measured the performance hit from doing so, but it seems alarming. Worse we are computing these expensive conversions repeatedly, without caching them.

Instead we can use a static constructor to populate some dictionaries with this conversion information, then just look up this information when needed. Apparently static classes (required for extension methods) can have constructors and fields :)

using System;
using System.Collections.Generic;
namespace CustomExtensions
{

/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
/// I'm not sure this is a good idea. While it makes that section of the code much much nicer to maintain, it 
/// also incurs a performance hit via reflection. To circumvent this, I've added a dictionary so all the lookup can be done once at 
/// load time. It requires that all enums involved in this extension are in this assembly.
/// </summary>
public static class EnumExtensions
{
    //To avoid collisions, every Enum type has its own hash table
    private static readonly Dictionary<Type, Dictionary<object,string>> enumToStringDictionary = new Dictionary<Type,Dictionary<object,string>>();
    private static readonly Dictionary<Type, Dictionary<string, object>> stringToEnumDictionary = new Dictionary<Type, Dictionary<string, object>>();

    static EnumExtensions()
    {
        //let's collect the enums we care about
        List<Type> enumTypeList = new List<Type>();

        //probe this assembly for all enums
        System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
        Type[] exportedTypes = assembly.GetExportedTypes();

        foreach (Type type in exportedTypes)
        {
            if (type.IsEnum)
                enumTypeList.Add(type);
        }

        //for each enum in our list, populate the appropriate dictionaries
        foreach (Type type in enumTypeList)
        {
            //add dictionaries for this type
            EnumExtensions.enumToStringDictionary.Add(type, new Dictionary<object,string>() );
            EnumExtensions.stringToEnumDictionary.Add(type, new Dictionary<string,object>() );

            Array values = Enum.GetValues(type);

            //its ok to manipulate 'value' as object, since when we convert we're given the type to cast to
            foreach (object value in values)
            {
                System.Reflection.FieldInfo fieldInfo = type.GetField(value.ToString());

                //check for an attribute 
                System.ComponentModel.DescriptionAttribute attribute =
                       Attribute.GetCustomAttribute(fieldInfo,
                         typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;

                //populate our dictionaries
                if (attribute != null)
                {
                    EnumExtensions.enumToStringDictionary[type].Add(value, attribute.Description);
                    EnumExtensions.stringToEnumDictionary[type].Add(attribute.Description, value);
                }
                else
                {
                    EnumExtensions.enumToStringDictionary[type].Add(value, value.ToString());
                    EnumExtensions.stringToEnumDictionary[type].Add(value.ToString(), value);
                }
            }
        }
    }

    public static string GetString(this Enum value)
    {
        Type type = value.GetType();
        string aString = EnumExtensions.enumToStringDictionary[type][value];
        return aString; 
    }

    public static T ToEnum<T>(this string value)
    {
        Type type = typeof(T);
        T theEnum = (T)EnumExtensions.stringToEnumDictionary[type][value];
        return theEnum;
    }
 }
}

Look how tight the conversion methods are now. The only flaw I can think of is that this requires all the converted enums to be in the current assembly. Also, I only bother with exported enums, but you could change that if you wish.

This is how to call the methods

 string x = LthWrapper.POS.Dollar.GetString();
 LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();