5,696,576 members and growing! (17,759 online)
Email Password   helpLost your password?
Languages » C# » General     Intermediate

Enhancing C# Enums

By Cassio Mosqueira

How to make a class look like an enum with added functionality
C#, Windows, .NET 2.0, .NET, Visual Studio, Dev

Posted: 9 Oct 2007
Updated: 11 Oct 2007
Views: 22,576
Bookmarked: 26 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
37 votes for this Article.
Popularity: 5.38 Rating: 3.43 out of 5
6 votes, 16.2%
1
6 votes, 16.2%
2
2 votes, 5.4%
3
3 votes, 8.1%
4
20 votes, 54.1%
5

Introduction

An enum in C# is a value type that is used to represent a set of fixed distinct values so that it can be used later in program logic. A very straightforward example of a classic enum declaration is given below:

public enum Rating
{
    Awful = 1,
    Bad = 2,
    Medium = 3,
    Good = 4,
    Awesome = 5
}

The enum above describes a set of possible values that a variable of type Rating can assume. This way, we can use it in our code to implement any kind of logic in a type-safe way. For instance, if we have a Rating variable called userRating, we could use it like this:

if(userRating == Rating.Awesome)
{
    //Do something

}

If we try to cast an int that was not defined in the enum list, I would expect the runtime to throw an exception. However, the funny thing is that the line of code bellow will create a variable rating with the value 6.

Rating rating = (Rating)6; //does not throw an exception

The way I see it, it is not a good thing, since our enum could be used improperly and we would only be able to validate it in another class. Besides that, sometimes it's useful to add some functionality to enums. The problem is that they have some limitations. Since it's not a class, it cannot be extended and its defined values are limited to a small set of primitive types (byte, sbyte, short, ushort, int, uint, long, ulong). For instance, sometimes it is very convenient to get the list of enum values and display it in a combobox. Although there are some reflection techniques that let you define and retrieve descriptions for the enum items, you are always limited to a key and a value.

Solution

To overcome these limitations, we can use a regular class to represent an enum while keeping the same basic characteristics. Here's the code of the base class that we will use to implement the enhanced enums.

using System.Collections.ObjectModel;
public abstract class EnumBaseType<T> where T : EnumBaseType<T>
{
    protected static List<T> enumValues = new List<T>();

    public readonly int Key;
    public readonly string Value;

    public EnumBaseType(int key, string value)
    {
        Key = key;
        Value = value;
        enumValues.Add((T)this);
    }

    protected static ReadOnlyCollection<T> GetBaseValues()
    {
        return enumValues.AsReadOnly();
    }

    protected static T GetBaseByKey(int key)
    {
        foreach (T t in enumValues)
        {
            if(t.Key == key) return t;
        }
        return null;
    }
    
    public override string ToString()
    {
    return Value;
    }
}

Note that in this class we define a key and a value field. This corresponds to the classical enum implementation, but now we are not limited to a key and a value. If required, we can extend this class and add new fields to the structure. Here is the actual implementation of our enhanced Rating enum:

public class Rating : EnumBaseType<Rating>
{
    public static readonly Rating Awful = new Rating(1, "Awful");
    public static readonly Rating Bad = new Rating(2, "Bad");
    public static readonly Rating Medium = new Rating(3, "Medium");
    public static readonly Rating Good = new Rating(4, "Good");
    public static readonly Rating Awesome = new Rating(5, "Awesome");

    public Rating(int key, string value) : base(key, value)
    {
    }

public static ReadOnlyCollection<Rating> GetValues()
    {
        return GetBaseValues();
    }

    public static Rating GetByKey(int key)
    {
        return GetBaseByKey(key);
    }
}

Now it can be used as a regular enum in an if statement and can also iterate through the values defined in our class. Notice that, because they are static, we still need to explicitly implement the methods GetValues() and GetByKey() if we want to use them. Otherwise, the enumValues collection would be empty.

foreach (Rating rating in Rating.GetValues())
{
    Console.Out.WriteLine("Key:{0} Value:{1}", rating.Key, rating.Value);

    if (rating == Rating.Awesome)
    {
        //Do something

    }
}

Now let's pretend we are using our Rating enum to rate movies on a website and we want to associate a "Must See" field to the rating, so that Good and Awesome movies display the "Must See" icon besides it. To do that, we have to add a new field to our Rating class. So now the Rating implementation is going to look like this:

public class Rating : EnumBaseType<Rating>
{
    public static readonly Rating Awful = new Rating(1, "Awful", false);
    public static readonly Rating Bad = new Rating(2, "Bad", false);
    public static readonly Rating Medium = new Rating(3, "Medium", false);
    public static readonly Rating Good = new Rating(4, "Good", true);
    public static readonly Rating Awesome = new Rating(5, "Awesome", true);

    public readonly bool MustSee;

    public Rating(int key, string value, bool mustSee) : base(key, value)
    {
        this.MustSee = mustSee;
    }

    public static ReadOnlyCollection<Rating> GetValues()
    {
        return GetBaseValues();
    }

    public static Rating GetByKey(int key)
    {
        return GetBaseByKey(key);
    }
}

Although we added new information to the Rating class, the clients using this enhanced enum will not be affected by this change. In addition, they will have the new MustSee field available.

foreach (Rating rating in Rating.GetValues())
{
    if (rating.MustSee)
    {
        Console.Out.WriteLine("This is a Must See movie!");
    }
}

Drawback

The only drawback of this approach is that the enhanced enum cannot be used in a switch statement, since its values are not constants. However, I still think it's worth using it when you need advanced functionality associated with your enums.

Conclusion

In this article, we saw how to use classes to simulate the behavior of enums while leveraging OOP techniques to enhance their capabilities. I hope, in the future, that the C# team includes some syntax sugar to allow the use of advanced enums without the need to write custom code and without the drawback concerning switch statements.

History

  • 9 October, 2007 -- Original version posted
  • 11 October, 2007 -- First update

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Cassio Mosqueira


I've been developing .NET enterprise applications since 2002.

I am originally from Rio de Janeiro and I am currently working as a developer for Sales Resource Group in Oakville, Ontario.

I'm vegetarian and I like surfing and listening to progressive metal bands like Dream Theater and Pain of Salvation.

Check out my blog!

Occupation: Web Developer
Location: Brazil Brazil

Other popular C# articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 27 (Total in Forum: 27) (Refresh)FirstPrevNext
GeneralExtend some more...memberdancerjohn4:08 16 Oct '07  
GeneralRe: Extend some more...memberCassio Alves5:20 16 Oct '07  
GeneralSystem.EnummemberRdunzl16:17 14 Oct '07  
GeneralRe: System.EnummemberCassio Alves16:36 14 Oct '07  
GeneralRe: System.Enum [modified]memberRdunzl20:45 14 Oct '07  
GeneralRe: System.EnummemberCassio Alves4:22 15 Oct '07  
GeneralRe: System.EnummemberRdunzl21:12 15 Oct '07  
GeneralEnum staticsmemberThomas Lykke Petersen20:31 11 Oct '07  
GeneralGood idea, but ...memberJakub Mller23:04 10 Oct '07  
GeneralGood Jobmembermerlin9814:49 10 Oct '07  
GeneralRe: Good JobmemberCassio Alves5:55 10 Oct '07  
Generalbase methods can be publishedmember_SAM_21:38 9 Oct '07  
GeneralRe: base methods can be publishedmemberCassio Alves5:50 10 Oct '07  
GeneralRe: base methods can be publishedmember_SAM_6:23 10 Oct '07