Click here to Skip to main content
Click here to Skip to main content

Descriptive Enumerations

By , 8 Mar 2006
Rate this:
Please Sign up or sign in to vote.

Introduction

Those of us who write ASP.NET for living often come across the problem of wanting to use enums in our pages. For example, you may have a page that allows someone to register for an airplane seat. Then, you may have some code that looks like:

public enum SeatType
{
    Window=1,
    Aisle=2
}

public class Registration
{
    ...
    SeatType wantsSeat;
    ...
}

Back in your ASP.NET page, you may loop over all the enum values, using something like:

    foreach (string s in Enum.GetNames(typeof(SeatType)))
    {
        string name = s;
        SeatType t = (SeatType) Enum.Parse(typeof(SeatType),s);
        int val = (int) t;
        
        //do something with name and val like 
        //add them to an <option> tag
    }

This code is a bit ugly, but it's worth keeping due to the fact that we can have methods like:

    public SeatType GetPassengerSeatType(Passenger p) {
        return p.Seat;
    }

The real issue arises when we add a new seat type to our enum that isn't a valid identifier. Note that Enum.GetNames simply returns the names of the enumerated constants. C# doesn't allow us to have something like:

public enum SeatType
{
    Window=1,
    Aisle=2,
    Anything Except Seat Near Bathroom
}

Since "Anything Except Seat Near Bathroom" isn't a valid identifier, I've seen several people make use of attributes to do something like this:

    public enum SeatType
    {    
        [Description("Window")] Window=1,
        [Description("Aisle")] Aisle=2,
        [Description("Anything Except Seat" + 
            " Near Bathroom")] AnythingExceptSeatNearBathroom
    }

    public static string GetEnumDescription(Enum value)
    {
        FieldInfo fi= value.GetType().GetField(value.ToString()); 
        DescriptionAttribute[] attributes = 
          (DescriptionAttribute[])fi.GetCustomAttributes(
          typeof(DescriptionAttribute), false);
        return (attributes.Length>0) ? 
                attributes[0].Description : 
                value.ToString();
    }

This code works, but it's not really good object oriented design: we're asking another class to retrieve information about our enum type. Clearly, what we would like to do is have a Description as a property of each member of our enum, rather than as an attribute of each member.

With .NET 2.0 and generics, we are able to have a very clean solution to the problem.

Using the code

I created a simple generic abstract class called DescriptiveEnum that will allow you to have...descriptive enums!

Before explaining how the DescriptiveEnum class works that makes this possible, I'll show you how to use it to solve the example we mentioned above. Here is the new SeatType "enum".

public class SeatType : DescriptiveEnum<SeatType,int>
{
    public static readonly SeatType Window = 
           new SeatType("Window Seat",1);
    public static readonly SeatType Aisle = 
           new SeatType("Aisle Seat", 2);
    public static readonly SeatType 
           AnythingExceptSeatNearBathroom = 
           new SeatType("Anything Except Seat Near Bathroom", 3);

    private SeatType(string desc, int code) : base(desc,code)
    {
        
    }
}

Only three things are necessary here:

  1. You need to inherit from DescriptiveEnum and pass the name of your inheriting class as the first generic type. The second generic type can be any value type that you want to use to store your enum values. By default, real .NET enums use integers to store constant enum values. So, I am doing the same thing in the example above. But again, you can store doubles, etc.
  2. Define a private constructor for your type that takes a string and the same type that you specified for the generic enum value. Since I specified int, I need to specify int in the constructor. You can't actually get this wrong; if you try to have a constructor that takes a double when you specified an int in your generic type, the compiler will throw an error at you. The important point to realize here is that the constructor should be private. Other classes should not be creating new instances of your enum type.
  3. Define public static readonly types of your class, and instantiate them by calling your private constructor, passing in a unique code for each enum. In the example above, I specified 1,2,3. You can make these anything you want as long as they are unique. If they are not unique, the class will throw an exception at run time.

So after doing the above, we have a data type that looks and feels just like a built-in enum, but with our descriptions:

SeatType c = SeatType.Aisle;
string desc = SeatType.AnythingExceptSeatNearBathroom.Description;

Additionally, our base class defines some other useful methods:

SeatType c;
int seatkind = GetSeatTypeFromDatabaseSomeWhere();

c = SeatType.GetEnumFromCode(seatkind);

SeatType[] allSeatTypes = SeatType.GetEnumMembers();

Finally, with generics, the base class is able to define public static explicit operator conversions to and from the enum constant you define (int, in our example above). So, this lets you use casting exactly like you do with enums. So you don't even have to call:

SeatType.GetEnumFromCode(seatkind);

like we did above. You can actually just do:

SeatType c = (SeatType) GetSeatTypeFromDatabaseSomeWhere();

Likewise, you can call:

int x = (int) SeatType.Aisle

if you want. Although, generally, I would call the SeatType.Aisle.Code member.

And that's all there is to it. Generics is a beautiful thing.

History

  • 1.0 - original release.

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

Tim Clark
Web Developer
United States United States
Tim Clark graduated in 2005 with an MS in Computer Science. He is currently employed as a .NET developer.

Comments and Discussions

 
QuestionWhere is DescriptiveEnumInvalidCodeException Pinmemberokcode23-Jul-06 23:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 8 Mar 2006
Article Copyright 2006 by Tim Clark
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid