I understood from this answer that C# static field initializers "are executed... prior to the first use of a static field of that class," but that still produces results I didn't expect, at least with generic types.
Coming from the Java world, I was missing my rich enums, and I thought with C#'s more serious generics that I ought to be able to replicate them with a minimum of boilerplate. Here (stripped of some details, like comparability) is what I came up with:
public class AbstractEnum<T> where T : AbstractEnum<T>
{
static readonly IDictionary<String, T> nameRegistry = new Dictionary<String, T>();
readonly String name;
protected AbstractEnum (String name)
{
this.name = name;
nameRegistry[name] = (T) this;
}
public String Name {
get {
return name;
}
}
public static T ValueOf(String name) {
return nameRegistry[name];
}
public static IEnumerable<T> Values {
get {
return nameRegistry.Values;
}
}
}
And some example subclasses:
public class SomeEnum : AbstractEnum<SomeEnum> {
public static readonly SomeEnum V1 = new SomeEnum("V1");
public static readonly SomeEnum V2 = new SomeEnum("V2");
SomeEnum(String name) : base(name) {
}
}
public class OtherEnum : AbstractEnum<OtherEnum> {
public static readonly OtherEnum V1 = new OtherEnum("V1");
public static readonly OtherEnum V2 = new OtherEnum("V2");
OtherEnum(String name) : base(name) {
}
}
This looks good and more or less does the trick... except that, following the letter of the spec, the actual instances (SomeEnum.V1
, OtherEnum.V1
etc.) don't get initialized unless at least one of them is referred to explicitly. Static fields/methods in the base class don't count. So, for instance, the following:
Console.WriteLine("Count: {0}", SomeEnum.Values.Count());
foreach (SomeEnum e in SomeEnum.Values) {
Console.WriteLine(e.Name);
}
writes Count: 0
, but if I add the following line --
Console.WriteLine("SomeEnum.V1: " + SomeEnum.V1.Name);
-- even after the above, I get:
Count: 2
V1
V2
(Note, by the way, that initializing the instances in a static constructor makes no difference.)
Now, I can fix this by marking nameRegistry
as protected
and pushing Values
and ValueOf
down into the subclasses, but I was hoping to keep all the complexity in the superclass and keep the boilerplate to a minimum. Can anyone whose C#-fu is superior to mine come up with a trick for making the subclass instances "self-executing"?
Note: FWIW, this is in Mono, on Mac OS. YM in MS .NET, on Windows, MV.
ETA: For monoglot C# developers (or even polyglot developers whose experience is limited to languages starting with 'C') wondering WTF I'm trying to do: this. C# enums take care of the type safety issue, but they're still missing everything else.