Wednesday, 28 November 2007

Creating string valued C# enums

Background

I've recently been writing an email subscription application for one of our e-commerce sites and re-used a pattern that I developed a couple of years ago. I thought it might be worth sharing to see if anyone else is doing anything similar...


The Problem

I want a C# enum with a string or character value. In otherwords, I want to do something like:



enum Status : string
{
Active = "A",
PendingActivation = "P",
Deleted = "D"
...
}

In my application I want to use the nice friendly enum constants e.g. "Status.Active", but in my database I'm optimising and only want to store a single character to represent each state. I also want a centralised point where I convert to/from the value so that if I add or change states I only have one place to change the code. Finally, I need to be able to compare instances of the enum for equality, e.g. If (Subscription.Status == Status.PendingActivation).


I guess some of the database folks out there will say that I should have a new database table "STATUS" and have records in it for each possible status and then use the STATUS record's ID elsewhere in the database. May'be, but to be honest sometimes that is total overkill. Sometimes, I just want a simple flag column that happens to have more than two states (e.g. true or false) and so I cannot use a boolean.


Unfortunately, string enum values are not supported in C#, so I decided to develop a "pseudo enum" class that worked like an enum but with string values...


Base Class

Firstly, since I'm going to need more than one of these types I created a base class to do most of the work:



public abstract class EnumObject
{
private string _caption;
private string _value;

protected EnumObject()
{
}

protected EnumObject(string caption, string theValue)
: base()
{
_caption = caption;
_value = theValue;
}

public string Caption
{
get { return _caption; }
set { _caption = value; }
}

public string Value
{
get { return _value; }
set { _value = value; }
}

public static bool operator ==(EnumObject a, EnumObject b)
{
if ((object)a == null && (object)b == null)
{
return true;
}
else if ((object)a == null || (object)b == null)
{
return false;
}

return a.Value == b.Value;
}

public static bool operator !=(EnumObject a, EnumObject b)
{
return !(a == b);
}

public override bool Equals(object b)
{
if (b is EnumObject)
{
return (this == (EnumObject)b);
}
else
{
return false;
}
}

public override int GetHashCode()
{
return base.GetHashCode();
}
}

Note: if you don't need to support XML serialization you can remove the parameterless constructor and the SET operations from the Value and Caption properties.


This base class gives us a basic Caption + Value pair and the ability to compare instances based on their Value.


An Instance

Now to define a pseudo-enum we need to define a new class that inherits from EnumObject. The example below defines a new "enum" called SubscriptionStatus with two constants - "PendingActivation" and "Active". In C# if strings were supported as enum values it might look like this...



enum SubscriptionStatus : string
{
Active = "A",
PendingActivation = "P"
}

You will notice in the code below, for each enum constant we define a static property that returns an instance of our new type prepopulated with the appropriate Caption and Value. Since this is a fully fledged class we can also add other useful behaviour like a static Create() method that returns an instance of the enum based on a Value. This is useful when we need to read a Value from the database and turn it into an instance of the enum class. It achieves my objective of centralising the translation code.



public class SubscriptionStatus : EnumObject
{
public SubscriptionStatus()
: base()
{
}

private SubscriptionStatus(string caption, string theValue)
: base(caption, theValue)
{
}

public static SubscriptionStatus PendingActivation
{
get { return new SubscriptionStatus("PendingActivation", "P"); }
}

public static SubscriptionStatus Active
{
get { return new SubscriptionStatus("Active", "A"); }
}

// Add more enum constants here...

public static SubscriptionStatus Create(string code)
{
switch (code.ToUpper())
{
case "P":
return PendingActivation;

case "A":
return Active;

default:
throw new ArgumentException("Invalid status code:" + code);
}
}
}

Finally, when we want to write the Value to the database we can simple pass the .Value property of the enum instance. For example, mySubscription.Status.Value.


So thats my enum object pattern, let me know what you think or if you have any improvements.

Labels: , ,


Comments: Post a Comment





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]