31

I have a enum containing the following (for example):

  • UnitedKingdom,
  • UnitedStates,
  • France,
  • Portugal

In my code I use Country.UnitedKingdom but I want to have the value be UK if I assign it to a string for example.

Is this possible?

Glory Raj
  • 17,397
  • 27
  • 100
  • 203
neildeadman
  • 3,974
  • 13
  • 42
  • 55
  • possible duplicate of [C# String enums](http://stackoverflow.com/questions/424366/c-sharp-string-enums) – nawfal Jun 08 '13 at 23:32
  • Related answer on another post - [Can my enums have friendly names?](https://stackoverflow.com/a/1415460/465053) – RBT May 03 '21 at 11:15

11 Answers11

53

You can't assign an enum value to a string to start with. You'd have to call ToString(), which would convert Country.UnitedKingdom to "UnitedKingdom".

Two options suggest themselves:

  • Create a Dictionary<Country, string>
  • A switch statement
  • Decorate each value with an attribute, and load that with reflection

Comments about each of them...

Sample code for Dictionary<Country,string>

using System;
using System.Collections.Generic;

enum Country
{
    UnitedKingdom, 
    UnitedStates,
    France,
    Portugal
}

class Test
{
    static readonly Dictionary<Country, string> CountryNames =
        new Dictionary<Country, string>
    {
        { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
    };

    static string ConvertCountry(Country country) 
    {
        string name;
        return (CountryNames.TryGetValue(country, out name))
            ? name : country.ToString();
    }

    static void Main()
    {
        Console.WriteLine(ConvertCountry(Country.UnitedKingdom));
        Console.WriteLine(ConvertCountry(Country.UnitedStates));
        Console.WriteLine(ConvertCountry(Country.France));
    }
}

You might want to put the logic of ConvertCountry into an extension method. For example:

// Put this in a non-nested static class
public static string ToBriefName(this Country country) 
{
    string name;
    return (CountryNames.TryGetValue(country, out name))
        ? name : country.ToString();
}

Then you could write:

string x = Country.UnitedKingdom.ToBriefName();

As mentioned in the comments, the default dictionary comparer will involve boxing, which is non-ideal. For a one-off, I'd live with that until I found it was a bottleneck. If I were doing this for multiple enums, I'd write a reusable class.

Switch statement

I agree with yshuditelu's answer suggesting using a switch statement for relatively few cases. However, as each case is going to be a single statement, I'd personally change my coding style for this situation, to keep the code compact but readable:

public static string ToBriefName(this Country country) 
{
    switch (country)
    {
        case Country.UnitedKingdom:  return "UK";
        case Country.UnitedStates:   return "US";
        default:                     return country.ToString();
    }
}

You can add more cases to this without it getting too huge, and it's easy to cast your eyes across from enum value to the return value.

DescriptionAttribute

The point Rado made about the code for DescriptionAttribute being reusable is a good one, but in that case I'd recommend against using reflection every time you need to get a value. I'd probably write a generic static class to hold a lookup table (probably a Dictionary, possibly with a custom comparer as mentioned in the comments). Extension methods can't be defined in generic classes, so you'd probably end up with something like:

public static class EnumExtensions
{
    public static string ToDescription<T>(this T value) where T : struct
    {
        return DescriptionLookup<T>.GetDescription(value);
    }

    private static class DescriptionLookup<T> where T : struct
    {
        static readonly Dictionary<T, string> Descriptions;

        static DescriptionLookup()
        {
            // Initialize Descriptions here, and probably check
            // that T is an enum
        }

        internal static string GetDescription(T value)
        {
            string description;
            return Descriptions.TryGetValue(value, out description)
                ? description : value.ToString();
        }
    }
}
Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Ahem, could always change the names of the enum values to their string representatives, if you don't mind breaking some style rules! – Ed James Jun 17 '09 at 16:20
  • Unfortunately, Dictionary would perform horribly: http://www.codeproject.com/KB/cs/EnumComparer.aspx – Igal Tabachnik Jun 17 '09 at 16:21
  • @hmemcpy hmm, always thought it'd perform basically like a Dictionary, given that an enum is effectively a set of int defined as names, hence why you can do enum i = {j =0, b=7} or whatever – Ed James Jun 17 '09 at 16:24
  • 10
    I would only worry about this when I saw some actual evidence that it was causing a bottleneck. As ever, use the simplest code that works until you have a good reason to do anything else. To my mind, there's a big difference between "would perform horribly" and "wouldn't perform optimally". – Jon Skeet Jun 17 '09 at 16:26
  • 1
    @Ed Woodcock Integers implement IEquatable, and the EqualityComparer which is used by the Dictionary handles that special case. Ayende (author of Rhino Mocks) wrote about it: http://ayende.com/Blog/archive/2009/02/21/dictionaryltenumtgt-puzzler.aspx – Igal Tabachnik Jun 17 '09 at 16:29
  • @Jon: I would like to point out the risk that people run when they wait to see something that can be simply changed to perform better (and still read clean) up front but will require a pretty major rewrite after the app is in production. If I was going to shove millions of rows though this I would probably use a factory method wtih a switch. But it would work either way. (And Jon's way is easier to extend later) – Matthew Whited Jun 17 '09 at 16:58
  • Why would it require a big rewrite to fix the performance after the app is in production? It would just mean changing the implementation of the extension method. Anyway, I've edited the answer a bit since you wrote that comment - more comments welcome! – Jon Skeet Jun 17 '09 at 17:05
  • It would depend on how people write the code around it. Many people probably would not use an extension method to extract the string. Some (probably most) would just use the indexer of the dictionary to retrieve the string. And in cases like that it could be difficult to change the code later. (I'm not talking about the well formed and abstracted apps... I'm talking about the fun ones that we all try to avoid maintaining) – Matthew Whited Jun 17 '09 at 17:43
  • The dictionary would have to be publicly visible for that to be an issue though - it would be *very* simple to make it private to the class containing the extension method. – Jon Skeet Jun 17 '09 at 17:50
  • 1
    I'll second that Jon - downvotes without a comment explaining why don't help anyone out – Scott Ivey Jun 17 '09 at 19:01
23

I prefer to use the DescriptionAttribute on my enums. Then, you can use the following code to grab that description from the enum.

enum MyCountryEnum
{    
    [Description("UK")]
    UnitedKingdom = 0,    

    [Description("US")]
    UnitedStates = 1,    

    [Description("FR")]
    France = 2,    

    [Description("PO")]
    Portugal = 3
}

public static string GetDescription(this Enum value)
{
    var type = value.GetType();

    var fi = type.GetField(value.ToString());

    var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];

    return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}

public static SortedDictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
    // validate.
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("T must be an Enum type.");
    }

    var results = new SortedDictionary<string, T>();

    FieldInfo[] fieldInfos = typeof(T).GetFields();

    foreach (var fi in fieldInfos)
    {

        var value = (T)fi.GetValue(fi);
        var description = GetDescription((Enum)fi.GetValue(fi));

        if (!results.ContainsKey(description))
        {
            results.Add(description, value);
        }
    }
    return results;
}

And then to get my bound enum list, its simply a call to

GetBoundEnum<MyCountryEnum>()

To get a single enum's description, you'd just use the extension method like this

string whatever = MyCountryEnum.UnitedKingdom.GetDescription();
Tyson Williams
  • 1,630
  • 15
  • 35
Scott Ivey
  • 40,768
  • 21
  • 80
  • 118
  • 1
    I keep getting `System.ArgumentException: Field 'value__' defined on type 'MyClass.EnumHelperTest+MyCountryEnum' is not a field on the target object which is of type 'System.Reflection.RtFieldInfo'.` Initial research suggests it doesn't like that enum isn't an instance? – Alex Angas Apr 04 '11 at 05:37
  • @AlexAngas I fixed your error and provide a few other minor improvements to Scott's answer here http://stackoverflow.com/a/35053745/625919 – DharmaTurtle Jan 28 '16 at 05:53
  • 1
    @AlexAngas The solution to your problem is discussed [here‌](http://www.distribucon.com/blog/GettingMembersOfAnEnumViaReflection.aspx)​. In short, you need to add some binding flags to the `GetFields` call to exclude the non-static, private backing field with special name `value__` (cf. line 416 of [enumbuilder.cs in .Net 4.6.1](http://referencesource.microsoft.com/#mscorlib/system/reflection/emit/enumbuilder.cs,25620f7fb1432241)). For example, this line works: `FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);` – Tyson Williams Jan 30 '16 at 20:18
15

You could create an extension method public static string ToShortString(this Country country). In the method you could use either a static Dictionary as Jon suggests, or you could simply do a switch case.

Example:

public static class CountryExtensions
{
    public static string ToShortString( this Country target )
    {
        switch (target) {
            case Country.UnitedKingdom:
                return "UK";
            case Country.UnitedStates:
                return "US";
            case Country.France:
                return "FR";
            case Country.Portugal:
                return "PT";
            default:
                return "None";
        }
    }
}
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
Timothy Carter
  • 15,459
  • 7
  • 44
  • 62
  • 1
    With only 4 values, I vote for the switch statement. Refactor to dictionary if and when the switch statement becomes more cumbersome than the dictionary implementation. – tvanfosson Jun 17 '09 at 16:24
  • Yeah agree with only 4 switch case would be the right choice. And thanks for adding the code, I hadn't gotten around to it yet. – Timothy Carter Jun 17 '09 at 16:27
  • +1 but bear in mind there are a slightly larger number of countries that the 4 listed =] – Ed James Jun 17 '09 at 16:35
6

Pseudo code:

enum MyCountryEnum
{
    UnitedKingdom = 0,
    UnitedStates = 1,
    France = 2,
    Portugal = 3,
}

string[] shortCodes = new string[] {"UK", "US", "FR", "PO"};


MyCountryEnum enumValue = MyCountryEnum.UnitedKingdom;
string code = shortCodes[enumValue];
rein
  • 32,967
  • 23
  • 82
  • 106
  • 7
    The maintenance overhead of this is worrisome. Keeping two unconnected data structures synchronized is always a red flag. – Rex M Jun 17 '09 at 18:37
3

One other possibility that hasn't been mentioned is something like this:

public class Country
{
    public static readonly Country UnitedKingdom = new Country("UK");
    public static readonly Country UnitedStates = new Country("US");
    public static readonly Country France = new Country("FR");
    public static readonly Country Protugal = new Country("PT");

    private Country(string shortName)
    {
        ShortName = shortName;
    }

    public string ShortName { get; private set; }
}

From this point you could add more properties, but beware of how much you add to the class, and how many static members you add, as the memory bloat it adds could make it not worth it.

I don't think there are many cases where this strategy is the best approach, but it is an option to be aware of when attempting to add properties or attributes to something you want to be able to treat as essentially an enum.

Timothy Carter
  • 15,459
  • 7
  • 44
  • 62
  • 1
    If you are concered about memory usage of your static objects you could make them static readonly properties and use Weak References. http://msdn.microsoft.com/en-us/library/ms404247.aspx – Matthew Whited Jun 17 '09 at 17:01
  • @Matthew Whited: can you provide an example of how to use Weak References in this case? – rohancragg Nov 30 '09 at 10:53
3

I had to leave my work on this project for a while, and having come back to it, I had a moment of inspiration.

Rather than an enum, I created a new class like so:

public class Country
{
    public const string UnitedKingdom = "UK";
    public const string France = "F";
}

This way I can use Country.UnitedKingdom in my code and the value "UK" will be used.

I'm just posting this answer as an alternative solution.

Neil

neildeadman
  • 3,974
  • 13
  • 42
  • 55
2

Just use the DescriptionAttribute

No need to create a dictionary if you only need to get a String representation for your enum values. See this example

[EDIT] Oh ... forgot to mention that it is more reusable than dictionaries, since you only need one common util class to help with getting the description and then all you need to do is add the DescriptionAttribute next time you add an enum value or you create a new enum with the same requirements. In the dictionary/switch solution, it is harder to maintain and it gets messy once you have many enum types.

Rado
  • 8,634
  • 7
  • 31
  • 44
  • Indeed, that was the other alternative I mentioned. However, it's more code and it uses reflection (which I generally try to avoid unless I really need it). Of course there's nothing magically about DescriptionAttribute - you could create your own attribute and use that instead. – Jon Skeet Jun 17 '09 at 16:29
2

I tried to submit an edit to Scott Ivey's answer, but it was rejected, here's yet another answer. My relatively minor edits:

1) I fixed Alex's error of System.ArgumentException: Field 'value__' defined on type 'MyClass.EnumHelperTest+MyCountryEnum' is not a field on the target object which is of type 'System.Reflection.RtFieldInfo'. Got it from here.

2) Added a return so you can actually copy/paste it and it'll work.

3) Changed the SortedDictionary to Dictionary because SortedDictionary always sorts by the key, in this case the string Description. There's no reason to alphabetize the Description. In fact, sorting it destroys the original order of the enum. Dictionary doesn't preserve the enum order either, but at least it doesn't imply order like SortedDictionary does.

enum MyCountryEnum
{    
    [Description("UK")]
    UnitedKingdom = 0,    

    [Description("US")]
    UnitedStates = 1,    

    [Description("FR")]
    France = 2,    

    [Description("PO")]
    Portugal = 3
}

public static string GetDescription(this Enum value)
{
    var type = value.GetType();

    var fi = type.GetField(value.ToString());

    var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];

    return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}

public static Dictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
    // validate.
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("T must be an Enum type.");
    }

    var results = new Dictionary<string, T>();

    FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);

    foreach (var fi in fieldInfos)
    {

        var value = (T)fi.GetValue(fi);
        var description = GetDescription((Enum)fi.GetValue(fi));

        if (!results.ContainsKey(description))
        {
            results.Add(description, value);
        }
    }
    return results;
}
DharmaTurtle
  • 6,858
  • 6
  • 38
  • 52
1

Whenever I see an enum I feel that the code should be refactored. Why not make a Country class and add methods to do some of the obstacles you are trying to get around. Assigning values to an enum is an even bigger code smell.

Why the downvotes? I think it is pretty widely accepted that using a polymorphic approach is better than using an enum. There is zero reason to use an enum when you can use the ValueObject design instead.

Here is a good blog post on the topic: http://devpinoy.org/blogs/cruizer/archive/2007/09/12/enums-are-evil.aspx

Bryan Rowe
  • 9,185
  • 4
  • 26
  • 34
  • I would agree that in almost all cases when someone is using an Enum, they should probably do a double take at their code to see if an Object might be a better option. – Dan Jun 17 '09 at 17:59
  • This is by far the cleanest way to do things. I'm surprised you didn't get more upvotes. – Kramii Oct 08 '10 at 09:40
0
var codes = new Dictionary<Country, string>() 
        { { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
        { Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);
BlackTigerX
  • 6,006
  • 7
  • 38
  • 48
0

The following solution works (compiles and runs). I see two issues:

  1. You would have to make sure the enums are in sync. (An automated test can do that for you.)

  2. You would be relying in the fact that enums are not type safe in .NET.

    enum Country
    {
        UnitedKingdom = 0,
        UnitedStates = 1,
        France = 2,
        Portugal = 3
    }
    
    enum CountryCode
    {
        UK = 0,
        US = 1,
        FR = 2,
        PT = 3
    }
    
    void Main()
    {
        string countryCode = ((CountryCode)Country.UnitedKingdom).ToString();
        Console.WriteLine(countryCode);
        countryCode = ((CountryCode)Country.Portugal).ToString();
        Console.WriteLine(countryCode);
    }
    
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Klinger
  • 4,900
  • 1
  • 30
  • 35
  • 1
    While this would work. You will probably want to be explicit on the values instead of letting the compiler add them for you. – Matthew Whited Jun 17 '09 at 17:02