Mark Needham

Thoughts on Software Development

C#: Java-ish enums

with 5 comments

We’ve been writing quite a bit of code on my current project trying to encapsulate user selected values from drop down menus where we then want to go and look up something in another system based on the value that they select.

Essentially we have the need for some of the things that a Java Enum would give us but which a C# one doesn’t!

Right now we have several classes similar to the following in our code base to achieve this:

public class CustomerType
{
    public static readonly CustomerType Good = new CustomerType("Good", "GoodKey");
    public static readonly CustomerType Bad = new CustomerType("Bad", "BadKey");
 
    private readonly string displayValue;
    private readonly string key;
    private static readonly CustomerType[] all = new[] { Good, Bad };
 
    private CustomerType(string displayValue, string key)
    {
        this.displayValue = displayValue;
        this.key = key;
    }
 
    public static CustomerType From(string customerType)
    {
        return all.First(c => c.displayValue == customerType);
    }
 
    public static string[] Values
    {
        get { return all.Select(c => c.DisplayValue).ToArray(); }
    }
 
    public string DisplayValue
    {
        get { return displayValue; }
    }
 
    public string Key
    {
        get { return key; }
    }
}

To get values to display in drop downs on the screen we call the following property in our controllers:

CustomerType.Values

And to map from user input to our type we do this:

CustomerType.From("Good")

Right now that will blow up if you trying to parse a value that doesn’t have an instance associated with it.

Christian came up with the idea of storing each of the public static fields in an array and then using ‘First’ inside the ‘From’ method to select the one that we want.

We previously had a somewhat over complicated dictionary with the display value as the key and type as the value and looked it up that way.

So far this does all that we need to do and the only annoying thing is that if we add a new instance then we need to manually add it to the ‘all’ array.

An alternative would be to do that using reflection which I think would work but it’s simple enough like this so we haven’t taken that approach yet.

Be Sociable, Share!

Written by Mark Needham

April 17th, 2010 at 10:33 am

Posted in .NET

Tagged with

  • Re: only annoying thing is that if we add a new instance then we need to manually add it to the ‘all’ array

    We use this approach on our current project too! In our case, we just add an entry into the ‘all’ collection right in the private constructor.

    This way of doing enum’s is great too because ReSharper would list the right entries when you use smart complete (Ctrl-Shift-space).

    One caution is that if this enum is used in a remote call [eg. customerService.AddCustomer(“John Doe”, CustomerType.Good)], you may get a new instance of CustomerType on the other end. It would look the same (have the same key and displayValue), but won’t equal CustomerType.Good. One solution is to treat CustomerType as a real value object and implement Equals and GetHashCode against the key and/or displayValue.

  • Hey Mark,
    Java-ish enums are quite helpful. I have had used them extensively.
    Check the following same code for CardinalPosition (North, East, South and West) in C#:

    You would observe couple of things:
    1) There is a “All” which corresponds to all values, as required in your drop down combo.
    2) Properties are directly available (without private attribute and public method). It’s public but readonly. If you have to change it to an abstracted value, you can change the implementation and make it with a private attribute and public method. You will not have to change the client code in C#.
    3) This will work in remote calls too.

    public class CardinalPosition : ICloneable
    {
    public static readonly CardinalPosition North = new CardinalPosition(0, “N”);
    public static readonly CardinalPosition East = new CardinalPosition(1, “E”);
    public static readonly CardinalPosition South = new CardinalPosition(2, “S”);
    public static readonly CardinalPosition West = new CardinalPosition(3, “W”);
    public static readonly CardinalPosition[] All = { North, East, South, West };

    public readonly byte Id;
    public readonly string Name;

    private CardinalPosition(byte id, string name)
    {
    this.Id = id;
    this.Name = name;
    }

    public object Clone()
    {
    CardinalPosition cardinalPosition = new CardinalPosition(Id, Name);
    return cardinalPosition;
    }

    public override int GetHashCode()
    {
    return this.Id;
    }

    public static CardinalPosition Find(string name)
    {
    foreach (CardinalPosition position in All)
    if (position.Name == name) return position;

    throw (new NotSupportedException(string.Format(“Cardinal Position {0} is not supported.”, name)));
    }

    public override bool Equals(object obj)
    {
    if(! (obj is CardinalPosition)) return false;
    else
    {
    return this.Id == (obj as CardinalPosition).Id;
    }
    }

    public static bool operator ==(CardinalPosition c1, CardinalPosition c2)
    {
    return c1.Equals(c2);
    }

    public static bool operator !=(CardinalPosition c1, CardinalPosition c2)
    {
    return !c1.Equals(c2);
    }
    }

  • A few typos. Would appreciate if you could correct them:

    Hey Mark,
    Java-ish enums are quite helpful. I have haD used them extensively.
    Check the following **delete start** same ***delete end** code for CardinalPosition (North, East, South and West) in C#:

  • Hey,

    I’ve sorted the first bit but I wasn’t quite sure what you meant by the second?

    Cheers,
    Mark

  • It should read “Check the following code for CardinalPosition”

    Thanks,
    Munjal