I really like some features of C# (lambdas and delegates, out parameters, C#'s variant on generics).
and I really dislike some other features (such as properties or explicit initializations that circumvent the constructor).
But some parts of the language are just plain disappointing. The enum is the feature at the front of that parade. I'll explain the problem
shortly and show how it can be fixed with a little code. Along the way, I'll also look at a reasonable application of the reflection APIs.
The Problem With C# enums
The difficulties with the C# enum stem primarily from its history.
The C# enum is pretty-much identical to the C++ enum,
which was, in turn, based on standard C.
C used used a simple integer to represent an enum element, and so does C#.
The problem with this integer implementation is that it's too easy to spoof. Any integer can, ultimately
be an enum, even integers that aren't legal values. Consider the following code (which
compiles just fine resharper doesn't even complain about it):
class foo</a>
{
public enum Suite { Hearts, Clubs, Diamonds, Spades };
public void dealer()
{ f( (Suite) 10 );
}
public void f( Suite theSuite )
{
switch (theSuite)
{
case Suite.Hearts: /*...*/ break;
case Suite.Clubs: /*...*/ break;
case Suite.Diamonds: /*...*/ break;
case Suite.Spades: /*...*/ break;
}
}
}
The f() method fails miserably because 10 doesn't represent any of the actual values
assigned to the enum elements, so none of the cases will run.
Were an enum implemented correctly by the language, the foregoing code wouldn't
compile at all. It just shouldn't be possible for theSuite to not represent a legal Suite.
The other problem with C#'s enum mechanism is that it isn't a real class, as it is in
Java. A C# enum can't have methods, for example,
or implement interfaces. As we've seen in my enum-related articles over the past few months,
those facilities are pretty useful. For example, in Java, you can write an enum that verifies
that all its members exist in a database table that defines the same enum at the database level (and
vice versa). Because the code verifies itself, you never need to worry about the code and
database getting out of synch with each other. However, you need to define methods in the enum definition
to pull this feat off.
Fortunately, we can fix all this.
Back before Java had enums, programmers implemented them using classes. (In fact, Java enums
are still effectively implemented that way under the covers.)
A Java enum for Suite is implemented by the compiler more or less like this:
class Suite extends Enum
{
public static final Suite HEARTS = new Suite();
public static final Suite CLUBS = new Suite();
public static final Suite DIAMONDS = new Suite();
public static final Suite SPADES = new Suite();
private Suite()
}
Of course, you can do exactly the same thing in C# (with minor tweaks to the syntax and naming conventions):
public class Suite : EnumBase<Suite> // I'll explain this superclass shortly
{
public static readonly Suite Hearts = new Suite();
public static readonly Suite Clubs = new Suite();
public static readonly Suite Diamonds = new Suite();
public static readonly Suite Spades = new Suite();
private Suite() { }
}
The private constructor assures that the only four instances of Suite
that can possibly exist are those that are allocated within the class itself (Suite.HEARTS, Suit.CLUBS, etc.). That is, the sort of spoofing I discussed earlier simply isn't possible. If you pass a Suite reference to a method, that reference cannot possibly
refer to anything other than a legal Suite element (or null, of course).
You can also compare using == without difficulty:
void f( Suite someSuite )
{
if( someSuite == Suite.HEARTS )
//...
}
A roll-your-own enum can also implement interfaces, define methods (and constructors which are
used to create the individual enum elements), and so forth.
You may want to review my other enum-related articles to see why that's such a good thing.
The only real (and unsolvable) issue is that you can't switch on a Suite. However,
you can accomplish the same thing with a series of nested if/else statements.
For what it's worth, the presence of a switch is considered a "bad smell"
in OO systems it's usually an indication that you're not using interfaces as much as you should.
The nullability of a class-style enum is also a mixed blessing, by the way. Because it is a
value object, a C# enum can't be null.
Consequently, a method that takes a standard
enum parameter is assured that there will be something there (even
if that something has a nonsensical value). A class-style enum can be
null, however, so it's best to put a
if(myEnumArgument == null)
throw new ArgumentNullException("myEnumArgument");
or equivalent at the top of every method that takes a class-style-enum argument.
An if statement is probably a better choice than a Debug.Assert(...) here.
Using the EnumBase Class to Make an enum
The only problem with the class-based-enum approach is the absence of the useful features that are provided
by the language. For example, you can't iterate across the enum elements, or get a string that holds the enum
name. Most of that work is done in Java and C#'s Enum superclass, but
the C# Enum superclass isn't much use to us if we're implementing the enum pattern from
scratch, as it depends on compiler-generated magic.
This problem is solvable by writing our own EnumBase<T> superclass.
Let's start by looking at how the superclass used. (The listings archive contains all the code I'm about
to discuss.)
A few unit tests demonstrate the facilities supported by the superclass. First, I define a test enum:
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Tools; // the EnumBase<T> is defined in this namespace
namespace EnumTest
{
public class TestEnum : EnumBase<TestEnum>
{
public static readonly TestEnum FIRST = new TestEnum();
public static readonly TestEnum SECOND = new TestEnum();
private TestEnum() { }
}
The main thing to notice is the EnumBase<TestEnum> superclass. Admittedly, it is odd to
pass the current class name into the generic argument of a superclass, but it's perfectly
legal. I'll explain how to use that class information in a moment, but for now, look at this
parameter as a convenient way to pass a subclass name to the base class.



