4

At this point I have the following code.

internal enum Genders
{
  Male,
  Female,
  NotSure,
  Other
}

I'm thinking about adding an extra functionality so that I can foreach all the values and return a String based on that. So, I'd go for the mapping as follows.

Male    -> "boy"
Female  -> "girl"
NotSure -> "oh-boy"
Other   -> "cow"

Should I refactor the enum into a class or is it recommended to just assign ToString values to the different enum states? I've googled it but didn't see any code examples for it, so I'm not sure if it's advisable to do that.

Perhaps it's better to create an auxiliary class and use a method like this?

private IEnumerable<String> GetGenders()
{
  yield return "boy";
  yield return "girl";
  yield return "oh-boy";
  yield return "cow";
}

Or am I just confusing myself and should stop immediately?

  • seems a matter of personal taste to me. depends mostly on the level of complexity expected from the enum/class. if its just string mapping I would personally keep it an enum, but, again, its a matter of personal preference. – Oren Jan 21 '13 at 09:11

4 Answers4

5

Consider adding an extension to the Enum instead.

You could for example, do like this:

Gender.Male.Description (which would return string "boy")

The extension looks like this:

    /// <summary>
    /// Defines an EnumExtensions type.
    /// </summary>
    public static class EnumExtensions
    {
        /// <summary>
        /// Gets the value from a DescriptionAttribute applied to the enum.
        /// </summary>
        /// <param name="value">Enum value.</param>
        /// <returns>DescriptionAttribute value, or null if no attribute is applied.</returns>
        public static string Description(this Enum value)
        {
            var type = value.GetType();
            var fieldInfo = type.GetField(value.ToString(CultureInfo.InvariantCulture));

            var descriptionAttributes = fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

            return descriptionAttributes.Length > 0
                       ? ((DescriptionAttribute)descriptionAttributes[0]).Description
                       : null;
        }

Your enum would look like this:

public enum Gender
{
    [Description("boy")]
    Male,

    [Description("girl")]
    Female,

    [Description("oh-boy")]
    NotSure,

    [Description("cow")]
    Other
}
Amadeus Hein
  • 706
  • 1
  • 4
  • 12
  • 1
    We've played around with that approach in the past, but we had to reject it because it makes it much harder to localize the strings. – Matthew Watson Jan 21 '13 at 09:16
  • I agree, but the question doesn't mention localization. I'm using this method in an all-english application and find it quite neat for this purpose. :) – Amadeus Hein Jan 21 '13 at 09:23
  • An approach I have used here is I have the `Description()` method go and look up a string from the resource table based on the fully qualified type name of the enumeration and its value (e.g. `MyNamespace.MyEnum.Three`) from the relevant language-specific resource file. In the case of no resource being available, we just return the enumeration key as a string. – tomfanning Jan 21 '13 at 09:28
  • @MatthewWatson Luckily, the localization won't be an issue. Those *String* instances are going to be used to query a DB, which will always be in English. –  Jan 21 '13 at 09:34
  • @tomfanning I kinda combined the two approaches and rolled my own `LocalizedDescriptionAttribute` class (derived from `DescriptionAttribute`) that takes both a resource key and a default description string, to be used if no resources file could be found. I like this approach because it provides a fallback string value, and also because it's localizable but also compatible with systems that might want to look for an attached `DescriptionAttribute`, which are relatively common. – Jeremy Todd Jan 21 '13 at 09:37
  • @AmadeusHein Is the class definition neccessary? Or is it sufficient to only declare the attributes on the enums? –  Jan 21 '13 at 22:09
  • @CRMconfusee Yes, if you want to be able to cleanly and neatly get the string by typing Gender.Male.Description. Another way is like Jeremy Todd is showing in this comment: myEnumMember.GetCustomAttributes(typeof(DescriptionAttribute), false); – Amadeus Hein Jan 22 '13 at 07:29
1

It's reasonable to add such functionality, but not necessarily in the way that you propose. I'd use a dictionary to map enum values to their translations, and add TWO methods:

  1. A method that looks up a single enum in the dictionary and returns the (possibly localized) string for it.
  2. Another method that returns all the values in the dictionary as an IEnumerable.

I think it's extremely likely that if you want a list of all the strings for an enum, you would also want to look up one particular enum. If you need both methods, you want to have a single implementation for the list of enum strings to ensure that the two methods can't get out of sync and that (of course) you Don't Repeat Yourself.

(We have to map to strings any of our enums that can be displayed to the user, because we can't use the built-in enum.ToString() since that is in "programmer language" and furthermore is not localized.)

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
1

Well, it depends on your personal preferences and the requirements of the application. One big advantage of enums is to define a limited set of numeric values that even the compiler and development environment are aware of, and replacing them with a collection of strings negates that. This might also be important if you want to store the enum value in a database field at some point.

If performance isn't a big issue, you could decorate your enum with DescriptionAttributes to associate them with string names, or any other metadata you want:

internal enum Genders
{
  [Description("boy")]
  Male,
  [Description("girl")]
  Female,
  [Description("oh-boy")]
  NotSure,
  [Description("cow")]
  Other
}

...and then retrieve the descriptions using reflection as needed. Not lightning fast, but I do this fairly often with labels that are going to be displayed in a WPF form, where a handful of GetCustomAttributes() calls are the least of my performance concerns. :)

If you want to get fancy, one advantage of this approach (as opposed to hard-coded strings) is that you can tie your descriptions to strings contained in the application resources (so you could, for example, have localized versions for different languages). I believe Microsoft takes this approach for some of their libraries (or am I thinking of something else?). And of course you can retrieve the name of the enum member as defined in your code simply by invoking ToString() on the enum value.

You can also enumerate over the members of an enum with the Enum.GetValues() method. So really, you can do the things you're looking for either way, it's just a matter of whether it's worth it to you to complicate things or keep them simple.

Jeremy Todd
  • 3,261
  • 1
  • 18
  • 17
  • Speed isn't an issue at all. I'm unsure what is meant by "*reflection*". Is it the code suggested by @AmadeusHein or do you mean something else? I'd prefer to solve it the *enum* way, since I haven't done that before and it's always nice to learn a new approach. –  Jan 21 '13 at 09:32
  • 1
    "Reflection" is the name of the set of methods provided by the Framework that let you retrieve information about a type at run-time. For example, suppose you're given an Object but you're not sure what type it is, and you want to get a list of all of its public properties. Using Reflection, you can do `PropertyInfo[] myProperties = myObject.GetType().GetProperties();` and get a list. Likewise, you can get a list of the `DescriptionAttributes` attached to an enum member with `myEnumMember.GetCustomAttributes(typeof(DescriptionAttribute), false);`. It's a bit slow, but very flexible. – Jeremy Todd Jan 21 '13 at 09:43
0

it depends on the level of complexity you need to implement. if it's just description in English you can use DescriptionAttributes as suggested in other  answers. If you need to support multiple languages, you have to use the localization resources( either in .resx files or in the database) - e.g. see http://www.codeproject.com/Articles/19953/Localizing-NET-Enums

However for more complicated scenarios using enum is not sufficient.

There are a few problems with enumerations : 

  • Behavior related to the enumeration gets scattered around the application
  • New enumeration values require shotgun surgery
  • Enumerations don’t follow the Open-Closed Principle

(From http://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/)

   

The blog post Enumeration classes described the public abstract class Enumeration and how to create derived classes from it.

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170