Flexible Enumerations

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!

Originally posted by Rupert Benbrook on 4 February 2009 here.