Polymorphic enums in C#, generic incrementation

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

sbyte

Signed 8-bit integer

byte

Unsigned 8-bit integer

short

Signed 16-bit integer

ushort

Unsigned 16-bit integer

int

Signed 32-bit integer

uint

Unsigned 32-bit integer

long

Signed 64-bit integer

ulong

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 !

This entry was posted in Syntax Puzzles and tagged , , . Bookmark the permalink.

Comments are closed.