简体   繁体   中英

C# Getting Enum values

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?

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

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();

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

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

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.

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

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.

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

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

var codes = new Dictionary<Country, string>() 
        { { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
        { Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM