Blog Arolla

Polymorphic enums in C#, generic incrementation

Consultez ici l’article précédent

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 !

2 comments for “Polymorphic enums in C#, generic incrementation

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *