Enum With String Values In C#

Hello again,
 

I am currently making a Virtual Earth asp.net ajax server control and came to the point where I had to replicate the enums in my classes, but the issue with them is that the enums do not use integer values but string ones. In C# you cannot have an enum that has string values :(. So the solution I came up with (I am sure it has been done before) was just to make a new custom attribute called StringValueAttribute and use this to give values in my enum a string based value.

Note: All code here was written using .NET 3.5 and I am using 3.5 specific features like automatic properties and exension methods, this could be rewritten to suit 2.0 quite easily.


First I created the new custom attribute class, the source is below:

    /// <summary>
    /// This attribute is used to represent a string value
    /// for a value in an enum.
    /// </summary>
    public class StringValueAttribute : Attribute {

        #region Properties

        /// <summary>
        /// Holds the stringvalue for a value in an enum.
        /// </summary>
        public string StringValue { get; protected set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor used to init a StringValue Attribute
        /// </summary>
        /// <param name="value"></param>
        public StringValueAttribute(string value) {
            this.StringValue = value;
        }

        #endregion

    }

 
Then I created a new Extension Method which I will use to get the string value for an enums value:

        /// <summary>
        /// Will get the string value for a given enums value, this will
        /// only work if you assign the StringValue attribute to
        /// the items in your enum.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetStringValue(this Enum value) {
            // Get the type
            Type type = value.GetType();

            // Get fieldinfo for this type
            FieldInfo fieldInfo = type.GetField(value.ToString());

            // Get the stringvalue attributes
            StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
                typeof(StringValueAttribute), false) as StringValueAttribute[];

            // Return the first if there was a match.
            return attribs.Length > 0 ? attribs[0].StringValue : null;
        }
 

So now create your enum and add the StringValue attributes to your values:

 public enum Test : int {
        [StringValue("a")]
        Foo = 1,
        [StringValue("b")]
        Something = 2       
 }


Now you are ready to go, to get the string value for a value in the enum you can do so like this now:

Test t = Test.Foo;
string val = t.GetStringValue();

- or even -

string val = Test.Foo.GetStringValue();



All this is very easy to implement and I like the ease of use you get from the extension method [I really like them :)] and it gave me the power I needed to do what I needed. I haven't tested the performance hit that the reflection may give and might do so sometime soon.


Thanks
Stefan

34 Comments

  • You can use this attribute for your StringValueAttribute:
    [AttributeUsage(AttributeTargets.Field)]

    If you have that, you can only use the attribute on the fields in an enum. :-)

  • Thanks for that mate, could be handy to stop people using it for purposes it is not made to do :P and I am sure it will happen.


    Thanks
    Stefan

  • Hi Stefan,

    Interesting approach to this. I also faced the same problem a while ago and posted my solution called SpecializedEnum to CodePlex recently. It provides fairly thorough emulation of enum's behaviors with any arbitrary object type.

    James.

  • I like this idea. I do have one question though. How do you do the reverse? Using your example, if you load the field from a database, and the field contains "b", how do you set the object property (which is of type Test) to the proper enum value? In other words, how would you implement the extension method SetValueByString? I ask because I've tried to do this and have failed so far :(

  • How do you set the string value? Ie you read the value "b" from the database and want to set the enum to that value.

  • Unfortunately you can't use this where a constant value is expected, such as switch statements. So it kind of breaks the contract an Enum would normally have with its consumers.

  • Tarek,

    I did not take this from anywhere? I just looked at that article and it is honestly the first time I have seen this.

    I can see why you think I took this it is the same solution to the problem. But it is possible that 2 people out of 6,602,224,175 in the world could came up with the same idea, and implement it?

    Fair enough if I did but I can assure you this was an idea that I had to solve a problem I had. I did not go and look for anything as I wanted to solve this issue myself and come up with a solution. If you think otherwise then you are open to your oppinion.

    Cheers
    Stefan

  • Stefan, I'm sorry for what I thought.
    actually when i was browsing Google results for the c# string enum i got ur link as well codeproject one.

    Thanks for the solution it saved me so much time. and thank u for sharing it with us it is really a genius idea.

    Tarek Jajeh

  • Tarek,

    I can see where you were coming from. They look like the same solution. I am amazed myself. It is just something I thought of and did, and it turns out it had already been done, shoulda googled and saved myself the trouble :P but its good to solve things yourself sometimes.


    Cheers
    Stefan

  • Why not using ToString/Parse methods of standard C# enum?

  • How would you propose this working to solve this issue?

  • Valid points, but it is called StringValue which is pretty specific to what it does, and if you read the post I state the following:

    "I am currently making a Virtual Earth asp.net ajax server control and came to the point where I had to replicate the enums in my classes, but the issue with them is that the enums do not use integer values but string ones. "

    The reason for this is that the Virtual Earth library at the time of creating this has string based values for it's enumerations I needed a way to make nice enumerations that could also contain the string based value, this did this for me.


    Thanks
    Stefan

  • what "using" statments are required for this code to work? I can't seem to compile it (all I have is one line at the top "using System;")

  • using System.Reflection will be needed as well. I would suggest you try Resharper (http://www.jetbrains.com/resharper/) a tool like this will make life easy as it will help you add the required using statements for example, saving headache and time :)


    Cheers
    Stefan

  • I had a similar situation where I needed to loop though a string array of property names in my object, in this case they were DateTime properties and did it the following way. I used this as a helper method to build my insert sql and not include those properties with a null date. The important part is inside the loop

    private string[] getNullableDateColumnsAndValues()
    {
    string columns = "AGENT_EFF_DT,AGENT_TERM_DT,AGENT_1ST_APPT_DT,BANK_DT,SELECT_START_DT,SELECT_END_DT,MUTUAL_DT,REGCD_DT,JUMBOCD_DT,TRANS_DT";
    string[] tmpArray = columns.Split(',');
    string buildColumnValues = string.Empty;
    string buildColumns = string.Empty;
    string[] returnArray = new string[2];

    Type type = this.GetType();

    foreach (string tmpStr in tmpArray)
    {
    DateTime dtProperty = (DateTime)type.InvokeMember(tmpStr, BindingFlags.GetProperty, null, this, null);

    if (dtProperty.Year != 1)
    {
    buildColumnValues += "'" + dtProperty.ToString("d") + "',";
    buildColumns += tmpStr + ",";
    }
    }

    //remove end comma
    if (!buildColumns.Equals(string.Empty))
    {
    buildColumnValues = buildColumnValues.Remove(buildColumnValues.Length - 1);
    buildColumns = buildColumns.Remove(buildColumns.Length - 1);
    }

    returnArray[0] = buildColumnValues;
    returnArray[1] = buildColumns;
    return returnArray;
    }

  • Something similar that I've used - a little more code but no reflection performance hit:

    enum BookingStatus {
    Attended = 0,
    Partial = 1,
    NoShow = 2,
    TurnedAway = 3,
    Failed = 4,
    OnlineTurnedAway = 5
    }
    static Dictionary BookingStatusNames = new Dictionary(){
    { BookingStatus.Attended , "Attended" },
    { BookingStatus.Partial , "Partial Attended" },
    { BookingStatus.NoShow , "No Show" },
    { BookingStatus.TurnedAway , "Turned Away Late" },
    { BookingStatus.Failed , "Failed Test" },
    { BookingStatus.OnlineTurnedAway ,"Online - Not Complete" }
    };

  • Thanks for this very interesting and useful. I made one ammendment to your code which is support for multiple values being set on a enum (i.e. when using the [Flags] Attribute).

    This allows for multi-value / flag enums to be used.

  • For my use, it was easier to do the following (described in my URL):
    using System;
    using System.Collections.Generic;

    namespace testConsole
    {
    class Program
    {
    static void Main(string[] args)
    {
    string[] stringArr = new string[] { "1", "2", "Text with spaces" };
    string text1;
    string text2;
    string text3;
    IEnumerator stringEnum = Program.makeStringEnum(stringArr);
    stringEnum.MoveNext();
    text1 = stringEnum.Current; stringEnum.MoveNext();
    text2 = stringEnum.Current; stringEnum.MoveNext();
    text3 = stringEnum.Current; stringEnum.MoveNext();
    }

    static IEnumerator makeStringEnum(string[] arr)
    {
    foreach (string str in arr)
    {
    yield return str;
    }
    }
    }
    }

  • Why do you inherit from "int"? It works fine without. Thanks and great implementation.

  • Why don't you just add a Description attribute (using System.ComponentModel) and a write a utility method to read? Very simple.

    Write your enum as follows:

    public enum AppStatus
    {
    [Description("Nothing in here")]
    None = 0,
    [Description("Out of scope")]
    OutOfScope,
    [Description("Oops something went wrong")]
    SomethingWentWrong,
    [Description("We have no idea")]
    NoIdea,
    [Description("Working normally")]
    WorkingAsNormal,
    }

    Then a simple util method to read them using reflection.

    public static String GetEnumDescription(Enum e)
    {
    FieldInfo fieldInfo = e.GetType().GetField(e.ToString());

    DescriptionAttribute[] enumAttributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

    if (enumAttributes.Length > 0)
    {
    return enumAttributes[0].Description;
    }
    return e.ToString();
    }



    AppStatus myAppStatus = AppStatus.WorkingAsNormal;

    string myAppStatusDescription = GetEnumDescription(myAppStatus);

  • Enum.GetName( typeof(Patology), enumPatology)

  • Thanks, this is perfect for me

  • Hi,
    it's exactly what I've looking for :)
    How to do it reverse?
    When I receive string :
    public MyEnum Choos
    {
    get
    {
    string Value = CallCallback();
    return ?????? HOW TO PARSE IT
    }
    }


    enum MyEnum{
    [StringValue("ok: none")]
    None = 0,
    [StringValue("You're switching to partial block!")]
    Partial = 1,
    [StringValue("The system is hidden. Leave it !")]
    Hidden = 2,
    [StringValue("smth4")]
    Closed = 3,
    [StringValue("smth 6")]
    Failed = 4,
    [StringValue("smth 5")]
    isOpen = 5
    }

  • Easy, simple, clean. Thanks for posting this.

  • Thanks for share this one.

    Its awesome.

    Once again thanks

  • any performance issue here? when i use the GetstringValue

  • Thanks alot! it helped me alot.
    please keep posting like these kind of stuff.

  • So did anyone ever come up with a way to set the enum value from the string value returned from the database?

  • Ian, for reverse approach, I will try with the following.
    In my requirements, I need that just for one enum, that why is not an extension approach, but it may help.

    public static Enumerations.Pages GetPageByUrl(string pageUrl)
    {
    pageUrl = pageUrl.Substring(pageUrl.LastIndexOf('/') + 1);

    foreach (Enumerations.Pages item in Enum.GetValues(typeof(Enumerations.Pages)))
    {
    FieldInfo fieldInfo = typeof(Enumerations.Pages).GetField(item.ToString());
    StringEnumValueAttribute[] attribs = fieldInfo.GetCustomAttributes(
    typeof(StringEnumValueAttribute),
    false) as StringEnumValueAttribute[];
    if (attribs.Length > 0 && attribs[0].Value.Contains(pageUrl))
    return item;
    }

    return Enumerations.Pages._NotAvailable;
    }

    HTH.
    Milton Rodríguez

  • Stefan,

    can I show the selected string attribute for the selected description based on the value similar to:

    Enum.GetName(typeof(MyEnum), myId) would return the selected description for that id.

    cheers

    Davy

  • Hi Its a very good article and exactly the same which I was looking for. Thanks a lot.

  • Hey guys, how do I reversed the code above?

  • Anyone run into to a compiler error: "Extension methods must be defined in a non-generic static class" on public class StringValueAttribute : Attribute section ?? (I'm using .net 3.5)

  • Here's how you'll go the other way around (string to enum value):

    public static object GetEnumStringValue(this string value,
    Type enumType, bool ignoreCase)
    {
    object result = null;
    string enumStringValue = null;
    if (!tipoEnum.IsEnum)
    throw new ArgumentException
    ("enumType should be a valid enum");

    foreach (FieldInfo fieldInfo in enumType.GetFields())
    {
    var attribs =
    fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false)
    as StringValueAttribute[];
    //Get the StringValueAttribute for each enum member
    if (attribs.Length > 0)
    enumStringValue = attribs[0].StringValue;

    if (string.Compare(enumStringValue, value, ignoreCase) == 0)
    result = Enum.Parse(enumType, fieldInfo.Name);
    }
    return result;
    }

Comments have been disabled for this content.