Click here to Skip to main content
14,930,183 members
Articles / Programming Languages / C#
Technical Blog
Posted 17 Dec 2011

Tagged as

Stats

12.2K views
7 bookmarked

A Different Type of Enum

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
17 Dec 2011MIT4 min read
A reusable code base for enums that we can expand to whichever way we want

C# enums have been bugging me for quite a long time.

Don't get me wrong, I use enums wherever I can, but there are some things that the syntax is just too annoying, not to mention the performance penalty of having to use reflection for certain operations like parsing from strings, or getting all available values.

Searching the web, I found some patterns that addressed the usability and interoperability of Enums and other types, but they usually required to write too much code per Enum because they rely on static code patterns for each enum type.

As you all probably know, static classes/code can't use inheritance well. You still have access to protected static parent members, but you can't use polymorphism to override the base methods. What's more is that polymorphism isn't enough here because inheritance isn’t type-safe enough for the job.

Pondering how to address this issue, I think I've found a good mix of writing as little code as possible on one hand, and gaining the type-safe, readable, interchangeable features similar to enum, and in the same stroke also gaining usability of a real object type with a way to define customized operators, explicit and implicit conversion methods, value mapping, and anything else you wish to add in functionality.

The hero of the day <dramatic drum rolls>: C# Generics.

C# generics are really versatile, and here they provide the conduit for code reuse, while keeping the enum types unique.

Let's dive into the code and I'll explain my solution as best as I can.

Please note that the patterns themselves are not of my design, I only came up with the way to write it more reusable using generics.

First, we'll start with an interface declaration (later, I'll explain why):

C#
public interface IEnumBase<E, T> where E: IEnumBase<E, T>{
    T Value { get; }
    string Name { get; }
}

The trick here is including the IEnumBase derived class (enum-type from now on) in the generic template (as type E), causing the entire hierarchy to be unique to the specific enum-type.

Now the base code implementation, starting with the class definition and the constructor and interface members implementation:

C#
public abstract class EnumBase<E, T> : IEnumBase<EnumBase<E, T>, T> where E: EnumBase<E, T>
{
#region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }
    protected EnumBase(string Name, T EnumValue)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }
    public override string ToString() { return Name; }
#endregion

Pretty basic, though you should note that the class is abstract and the constructor is protected forcing instantiation only via a child-class. Also the mapping.Add call will be explained further down.

Now comes the 'good stuff', what the generic class is all about – its static methods and members.

C#
#region Static tools
static private readonly Dictionary<string, EnumBase<E, T>> mapping;static EnumBase()
{
    mapping = new Dictionary<string, EnumBase<E, T>>();
}

One feature of native C# enums available via reflection is parsing and enumerating, a dictionary makes this easy and efficient (as I mentioned before, I take no credit for the ideas, I just use them in my own way).

Whenever a new object is constructed, it's added to the mapping Dictionary, thus populating it automagically.

Now the Parse method:

C#
protected static E Parse(string name){
    EnumBase<E, T> result;
    if (mapping.TryGetValue(name, out result))
    {
        return (E)result;
    }
    throw new InvalidCastException();
}

Hold on! Why is that method protected? This means I'll need to write code for the child class! Why!?

Don't get angry, I already told you static code is problematic to reuse. The reason for defining the Parse method protected is to ensure the inheriting class' static members are initialized when it's called, and we can only do that by forcing the derived class to call from its own static method.

By writing all the code, all the derived class needs to do is just forward the call to the parent class' method, a single short line of code.

Next the enumeration member (using a bit of LINQ magic):

C#
protected static IEnumerable<E> All
{
    get { return mapping.Values.AsEnumerable().Cast<E>(); }
}

Like with the Parse method, this static property is also protected.

So, now that we have an EnumBase, how do we define a type-safe, versatile enum-type?

Here is a simple example:

C#
public sealed class YesNoEnum : EnumBase<YesNoEnum, int>
{
    public static readonly YesNoEnum Yes = new YesNoEnum(0, "Yes");
    public static readonly YesNoEnum No = new YesNoEnum(1, "No");
    private YesNoEnum(int value, String name) : base(name, value) { }
    public new static IEnumerable<YesNoEnum> All
    {
        get { return EnumBase<YesNoEnum, int>.All; }
    }
    public static explicit operator YesNoEnum(string str)
    {
        return Parse(str);
    }
    public static implicit operator string(YesNoEnum e)
    {
        return e.Name;
    }
    public static implicit operator int(YesNoEnum e)
    { return e.Value; }
}

As you can see, YesNoEnum, although a bit more complicated in the declaration phase, can now be used easily in your code, where YesNoEnum.Yes and YesNoEnum.No are unique type-safe entities member of the YesNoEnum class, interchangeably as a string or int, and we can enumerate through its values.

I still have one thing left to explain, why I based EnumBase<E, T> on an interface IEnumBase<E, T>.

The reason was I wanted an additional implementation where the Value and Name properties point to the same string value. I could have declared it using a string value but this special case does not require a Dictionary, a List will suffice, and there is no need for a pair of string values in memory, one is enough.

I will not go into the details of this special case implementation, but you can examine it in the complete code available via the link below.

That’s all folks. Now that we have a reusable code base for enums, we can expand it in whichever way we want. Do you have any ideas as to what other features would be helpful to add to this base class?

The complete code link can be found at http://pastebin.com/kwD3yjer.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Life Lockszmith
Help desk / Support
Israel Israel
I'm a 35+ family and tech support guy from Israel

Comments and Discussions

 
QuestionNice code, but is not it already solved by MS? Pin
ForgaSw30-May-12 17:16
MemberForgaSw30-May-12 17:16 
GeneralGood work Pin
sudhansu_k12319-Dec-11 20:28
Membersudhansu_k12319-Dec-11 20:28 

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

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