Flexible Enumerations 7

Sometimes enumerations in .NET just don’t cut it. In the end they’re just a numeric value to which a piece of string metadata is attached to some of the values. Consider the following enumerations:

public enum OfficeLocationNames
{
    London,
    Edinburgh,
    Redmond
}

public enum OfficeLocationCodes
{
    LON,
    EDI,
    RED
}

What are these enumerations actually trying to say? I see these and think that there are three office locations, London, Edinburgh and Redmond, and each has a name and a three letter code, i.e. one type of thing, an office location, with two pieces of descriptive information about that thing, name and code. But using enumerations has forced us to define two things, when it’s really one. So what solutions are there?

Here’s one solution I like. I’m sure there are more, so feel free to suggest variations or completely different patterns in the comments. First off I define an interface, IOfficeLocation, to specify what makes up an office location:

public interface IOfficeLocation
{
    string Code { get; }
    string Name { get; }
    double Latitude { get; }
    double Longitude { get; }
}

I’ve taken the liberty of adding two more pieces of descriptive information to the office location too, latitude and longitude, just to prove the point. Note that this is immutable, i.e. you can’t change the values once they’ve been created.

Next, I define the OfficeLocations static class that will be our container for the three possible office locations. Inside this class I define the OfficeLocation class, which is a concrete implementation of the IOfficeLocation interface. Again, this class is immutable, and importantly it’s a private class to the OfficeLocations static class. This ensures that only the OfficeLocations class can construct instances of the OfficeLocation class. Finally I define the three office locations as static read-only fields of the OfficeLocations class, all using the IOfficeLocation interface, but constructed from the private OfficeLocation class.

public static class OfficeLocations
{
    private class OfficeLocation : IOfficeLocation
    {
        private readonly string code;

        public OfficeLocation(string code, string name, double latitude, double longitude)
        {
            this.code = code;
            this.name = name;
            this.latitude = latitude;
            this.longitude = longitude;
        }

        private readonly string name;

        private readonly double latitude;

        private readonly double longitude;

        public string Code
        {
            get { return code; }
        }

        public string Name
        {
            get { return name; }
        }

        public double Latitude
        {
            get { return latitude; }
        }

        public double Longitude
        {
            get { return longitude; }
        }
    }

    public static readonly IOfficeLocation London = new OfficeLocation("LON", "London", 0d, 0d);
    public static readonly IOfficeLocation Edinburgh = new OfficeLocation("EDI", "Edinburgh", 0d, 0d);
    public static readonly IOfficeLocation Redmond = new OfficeLocation("MAN", "Manchester", 0d, 0d);
}

Using the OfficeLocations class is very similar to an enumeration, and because there is only ever one instance of London, Edinburgh and Redmond I can test for equality without needing to override the Equals method in the OfficeLocation class. I also like the fact that I cannot do more than or less than comparisons against office locations. It doesn’t make sense, to see if London is more than Edinburgh, does it? However, if it did I could implement IComparable on OfficeLocation and then I could make those comparisons.

public bool IsInUnitedKingdom(IOfficeLocation location)
{
    return (location == OfficeLocations.London) || (location == OfficeLocations.Edinburgh);
}

So, as usual, comments are always welcome. Are there any variations on this theme? Or perhaps issues with this implementation? Surely not!

7 thoughts on “Flexible Enumerations

  1. Reply Mark Feb 4, 2009 23:14

    I have actually used something similar except with a non-sealed base class and no interface, to implement "inheritable enums". I have a business scenerio where a set of enumerations need to be segregated by domain but at the same time I have to support compatibility with some legacy systems where everything is all mixed together where the base classes are containers for any number of more specific enumerations from multiple domain areas. Cut down example just to show the general idea.

    public class BaseEnum

    {

    protected BaseEnum( int scope )

    public int Scope { get; }

    }

    public class BusinessBase

    {

    public ICollection<BaseEnum> Enums { get; }

    }

    public class DomainEnum : BaseEnum

    {

    public DomainEnum(string name, …) : base(1 /*scope assigned*/) {}

    public string Name {get; }

    public readonly static DomainEnum EnumValue1 = new DomainEnum("Value1");

    }

    public class OtherDomainEnum : BaseEnum

    {

    public OtherDomainEnum(string name, …) : base(2 /*scope assigned*/) {}

    public string Name {get; }

    public readonly static OtherDomainEnum EnumValue1 = new OtherDomainEnum("Value1");

    }

    I have set classes and a wrapper that works to manage the scope values to insure that duplicates result in compile errors. I also have lookup dictionaries built using reflection so I can have call slike .Parse(…) and .LookupByXXXX() methods to resolve an enum by name, or other values in the extended enum.

    This is been VERY helpful. Some of them are even code generated from legacy database systems where the "enum" has a LOT of values, but as you mention it only occurs once in the system.

  2. Reply Mark Feb 4, 2009 23:57

    Hmm. the example was cut off. Sorry must be size limited.

  3. Reply Rupert Benbrook Feb 5, 2009 06:31

    Nope, not size limited just a problem with the comment styles. I’ll fix them then take a look at your example.

  4. Reply Rupert Benbrook Feb 5, 2009 21:27

    I’ve not quite got the comment styles right, but they’ll do for now and I can now see your example. I’m not sure I quite follow it to be honest. What purpose does the scope value have? And why would you force every enumerable type to have one? Surely if you want to separate two business domains then you would do so by separating their namespaces and assemblies. I’m also concerned when you say that the enumeration comes from the database. If it comes from the database then it’s not really an enumeration, which by definition have a fixed set of values.

  5. Reply Simon Ince Feb 9, 2009 08:47

    Nice post – interesting variation on something I do too. Myself and Josh had a brief blog conversation about a related subject too;

    blogs.msdn.com/…/strongly-typed-

    http://www.thejoyofcode.com/…/Avoiding_Primit

    Simon

  6. Reply Josh Feb 11, 2009 07:44

    That wasn’t a conversation. I blogged about it and then you both ripped me off! Thieves! Thieves everywhere!

    :)

  7. Reply Rupert Benbrook Feb 11, 2009 08:43

    Hey boys, stop fighting, there are plenty of enumerations for all of us! In fact I quite like that there are variations on the same pattern with different features that emphasised differently by each of us. It just goes to prove there isn’t a single answer and that the context is often important too.

Leave a Reply