During the implementation of my polymorphic enum, I have chosen to allow several types for the underlying “ordinal” value, as it is also possible for standard C# enums…
From the MSDN documentation, the allowed underlying types for enum types are the following (although other value types such as char are eligible, their use is not recommended) :
Type |
Size |
---|---|
Signed 8-bit integer |
|
Unsigned 8-bit integer |
|
Signed 16-bit integer |
|
Unsigned 16-bit integer |
|
Signed 32-bit integer |
|
Unsigned 32-bit integer |
|
Signed 64-bit integer |
|
Unsigned 64-bit integer |
In order to enable the ordinal parameter to be optional in the registration of enum values, I had to find a way to compute an automatic value. The rule I wanted to apply was very simple : if there is no value already register, use zero, else use the maximum value already registered, plus one. But how do you write such code without checking the type in an ugly fashion ?
Generic constraints are somehow limited : I can force the ordinal type to be a value type by using a “where T : struct” constraints, but there is no such thing as a “IPrimitiveNumericValue” interface !
Here is where I got to, using generics and lambdas…
public static class Incrementer { private static Func<T, T> GetFunc<T>(Func<T, T> f) { return f; } [...]
I first define static class and a private static helper function that take a Func<T, T> and returns… the unmodified argument. This is just a hint for the compiler, because I’m going to use lambdas and as you might know, a lambda can either be converted to a delegate (code) or to an expression tree representing the lambda (data). Without any indication, the compiler can’t infer the expected type, so we have to help him…
Then, the class defines a static field and initialises it in its static constructor :
private static Dictionary<Type, object> incrementers; static Incrementer() { incrementers = new Dictionary<Type, object>(); incrementers.Add(typeof(sbyte), GetFunc<sbyte>(i => (sbyte)(i + 1))); incrementers.Add(typeof(byte), GetFunc<byte>(i => (byte)(i + 1))); incrementers.Add(typeof(short), GetFunc<short>(i => (short)(i + 1))); incrementers.Add(typeof(ushort), GetFunc<ushort>(i => (ushort)(i + 1))); incrementers.Add(typeof(int), GetFunc<int>(i => i + 1)); incrementers.Add(typeof(uint), GetFunc<uint>(i => i + 1)); incrementers.Add(typeof(long), GetFunc<long>(i => i + 1)); incrementers.Add(typeof(ulong), GetFunc<ulong>(i => i + 1)); }
At this point, all the required incrementing functions are expressed as lambdas and registered in a dictionary.
The only method left to write is the PlusOne method :
public static T PlusOne<T>(this T value) where T : struct { object incrementer; if (!incrementers.TryGetValue(typeof(T), out incrementer)) throw new NotSupportedException( "This type is not supported."); return ((Func<T, T>)incrementer).Invoke(value); }
Type checking and dictionary lookup. That’s it !