This will be an evolving post, hopefully. (If no-one comments on it, it probably
won’t change unless I come up with better ideas myself.) Since working on a Java
project last year, I’ve been increasingly fed up with C#’s enums. They’re really
not very object oriented: they’re not type-safe (you can cast from one enum to
another via a cast to their common underlying type), they don’t allow any
behaviour to be specified, etc. They’re just named constant integral values.
Until I played with Java
1.5’s enum support, that wouldn’t have struck me as being
a problem, but (at least in some cases) enums can give you so much more. This post
is a feature proposal for C# 4.0. (I suspect the lid for this kind of thing is closed
on 3.0.)
What’s the basic idea?
Instead of being a fixed set of integral values, imagine if enums were a fixed set of
objects. They could have behaviour (and state of sorts), just like other objects – the
only difference would be that there’d only ever be one instance for each value. When
I say they could have “state of sorts” I mean that two values of an enum could differ
in just what they represented. For instance, imagine an enumeration of coins – I’ll use
US coins for convenience for most readers. Each value in the enum would have a name
(Cent, Nickel, Dime, Quarter, Dollar) and a monetary value in cents (1, 5, 10, 25, 100
respectively). Each might have a colour property too, or the metal they’re made of. That’s
the kind of state I mean. In fact, there’s nothing in the proposal below to say that the
state within an enum has to stay the same. I’d recommend that it did stay the same,
but maybe someone’s got an interesting use case where mutable enums would be useful.
(Actually, it’s not terribly hard to think of an example where the underlying state mutates
even if it doesn’t appear to from a public property point of view – some properties could
be lazily initialised if they might take time to compute.)
As well as properties, the enum type could have methods, just as other types do. For instance,
an enumeration of available encryption types may have methods to encrypt data. (I’m giving
fairly general examples here – in my experience, the actual enums you might use tend to be
very domain-specific, and as such don’t make good examples. I’m also trying to steer well
clear of risking giving away any intellectual property owned by my employer.)
Now, consider the possibilities available when you bring polymorphism into the picture. Not
every implementation of a method has to be the same. Some enum values may be instances of
a type derived from the top-most one. This would be limited at compile-time to make enums
fixed – you couldn’t derive from an enum type in the normal way, so you’d
always know that if you had a reference to an instance of the enum type, you’d got one of
the values specified by the enum.
What’s the syntax?
I propose a syntax which for the simplest of cases looks very much like normal enumerations,
just with class enum
instead of enum
:
public class enum Example
{
FirstValue,
SecondValue;
}
|
Note the semi-colon at the end of the list of values. This could perhaps be optional,
but when using the power of “class enums” (as I’ll call them for now) in a non-trivial way,
you’d need them anyway to tell the compiler you’d reached the end of the list of values,
and other type members were on the way. The next step is to introduce a constructor and
a property:
public class enum Coins
{
Cent(1),
Nickel(5),
Dime(10),
Quarter(25),
Dollar(100);
readonly int valueInCents;
Coins(int valueInCents)
{
this.valueInCents = valueInCents;
}
public int ValueInCents
{
get { return valueInCents; }
}
}
|
Now, there would actually be some significant compiler magic going on at this point. Each of
the generated constructor calls would actually have an extra parameter – the name of the value. That
parameter would be present in every generated constructor, and if no constructors were declared, a protected
constructor would be generated which took just that parameter. Each constructor would implicitly
call the base constructor which would stash the name away and make it available through a Name
property (and no doubt through calls to ToString()
too). What’s the base class in this case?
System.ClassEnum
or some such. This could be an ordinary type as far as the CLR is concerned,
although language compilers would be as well to prevent direct derivation from it. This leaves room for
some compilers to allow types which aren’t really enums to derive from it, but does have the advantage
of not requiring a CLR change. Whenever you use someone else’s code you’re always taking a certain amount on
trust anyway, so arguably it’s not an awful risk. More about the services of System.ClassEnum
later…
The next piece of functionality is to have some members with overridden behaviour. The canonical example
of this is simple arithmetic operations – addition, subtraction, division and multiplication. The enumeration
contains a value for each operation, and has an Eval
method to perform the operation. Here’s
what it would look like in C#:
public class enum ArithmeticOperation
{
Addition
{
public override int Eval(int x, int y)
{
return x+y;
}
},
Subtraction
{
public override int Eval(int x, int y)
{
return x-y;
}
},
Multiplication
{
public override int Eval(int x, int y)
{
return x*y;
}
},
Division
{
public override int Eval(int x, int y)
{
return x/y;
}
};
public abstract int Eval(int x, int y);
}
|
Sometimes, you may wish to save a bit of space and specify the implementation of
a method as a delegate – especially if the method would otherwise be abstract (i.e.
there was no “common” implementation which most values would use). Here, C#’s
anonymous method syntax helps:
public class enum ArithmeticOperation
{
delegate int Int32Operation (int x, int y);
Addition (delegate (int x, int y) { return x+y; }),
Subtraction (delegate (int x, int y) { return x-y; }),
Multiplication (delegate (int x, int y) { return x*y; }),
Division (delegate (int x, int y) { return x/y; });
Int32Operation op;
ArithmeticOperation (Int32Operation op)
{
this.op = op;
}
public int Eval (int x, int y)
{
return op(x, y);
}
}
|
That’s still a bit clumsy, of course – let’s try with lambda function syntax instead:
public class enum ArithmeticOperation
{
Addition ( (x,y) => x+y),
Subtraction ( (x,y) => x-y),
Multiplication ( (x,y) => x*y),
Division ( (x,y) => x/y);
Func<int, int> op;
ArithmeticOperation (Func<int,int> op)
{
this.op = op;
}
public int Eval (int x, int y)
{
return op(x, y);
}
}
|
Now we’re really cooking! Of course, some of the time you’ll be able to provide a single implementation for most values,
which only some values will want to override. Something like:
public class enum InputType
{
Integer,
String,
Date
{
public override string Format(object o)
{
return ((DateTime)o).ToString("yyyyMMdd");
}
};
public virtual string Format(object o)
{
return o.ToString();
}
}
|
So far, this is all quite similar to Java’s enums in appearance. Java’s enums also come with an ordinal
(the position of declaration within the enum) automatically, but in my experience this is as much of a
pain as it is a blessing. In particular, as that ordinal can’t be specified in the source code, if you
have other code relying on specific values (e.g. to pass data across a web-service) you have to leave
bogus values in the list in order to keep the ordinals of the later values the same. Java also only
allows you to specify the constructors in “top-most” enum. This can occasionally be a nuisance. Let’s extend
the enum above to include DateTime
and Time
– both of which need the same kind of
“special” formatting. In Java, you’d have to override Format
in three different places, like
this:
public class enum InputType
{
Integer,
String,
DateTime
{
public override string Format(object o)
{
return ((System.DateTime)o).ToString("yyyyMMdd HH:mm:ss");
}
},
Date
{
public override string Format(object o)
{
return ((System.DateTime)o).ToString("yyyyMMdd");
}
},
Time
{
public override string Format(object o)
{
return ((System.DateTime)o).ToString("HH:mm:ss");
}
};
public virtual string Format(object o)
{
return o.ToString();
}
}
|
I would propose that enum values could reuse each other’s implementations, possibly
parameterising them via constructors. The above could be rewritten as:
public class enum InputType
{
Integer,
String,
DateTime("yyyyMMdd HH:mm:ss")
{
string formatSpecifier;
protected DateTime(string formatSpecifier)
{
this.formatSpecifier = formatSpecifier;
}
public override string Format(object o)
{
return ((System.DateTime)o).ToString(formatSpecifier);
}
},
Date : DateTime("yyyyMMdd"),
Time : DateTime("HH:mm:ss");
public virtual string Format(object o)
{
return o.ToString();
}
}
|
If Date
wanted to further specialise the class (e.g. if another method needed overriding),
it could add implementation there too. Note that the DateTime
constructor does not explicitly
call any constructor. In this case, an implicit call to the InputType
constructor which took
only the name parameter would be made. Explicit calls to base class constructors could be included in the normal
way – the extra parameter would be entirely hidden from the source code. Only protected constructors could
be called by derived types in the normal way.
Switch
Switch statements would appear in exactly the same way they do now. (Possibly without the qualification (e.g.
case Time:
instead of case InputType.Time:
. The code is more readable without the
qualification, and is unambiguous, but it would be inconsistent with the current handling of switch cases
for “normal” enums.) The implementation would work a lot like strings – either using the equivalent of
a sequence of if
statements or building a map behind the scenes. This is where keeping something
like Java’s ordinals would speed things up, but then I would at least want something like an attribute to be able to
specify the value in source code to avoid the problems described in Java earlier. Note that only reference
equality needs to be checked in any of these cases, as only one instance of any value would be created.
Static field initializers and static constructors
Java has restrictions on where you can use static fields in enums, because the static field initializers
are executed after the enum values have been created. Static fields are useful in a surprising number
of circumstances, mostly getting at an enum value dynamically by something other than name. The rules
for this would need to be considered carefully – sometimes it’s useful to have code which will be run
after the enums have all been set up; other times you want it beforehand (so you can use the static fields
during initialization, e.g. to add a value to a map).
Other features
Like Java, there could be an EnumSet<T>
type which would be the equivalent of using
FlagsAttribute
on normal enums. Indeed, the compiler could even generate operator overloads
to make this look nicer.
In Java, some enums with many values overriding many methods can end up being pretty large. In
C#, of course, we can use partial types to split the whole enum definition over several
files. (Some may object to this, others not – it would be available if you wanted it.)
Open questions
- The potential abuse problem mentioned earlier
- Serialization/deserialization would need to know to use the previously set up values
- Should identifying values (like Java ordinals) be present, if only for switch performance?
I suspect there are other things I haven’t thought of, but with any luck this will be food for thought.