A Step Too Far

straight-jacket

Occasionally, I have a bit of a compulsive behavior. When presented with a challenge, I usually won’t give up until I have a working answer… and sometimes that answer get’s a little crazy. Here’s one of my more recent journeys down that path.

I was asked, “Hey Kevin. I am a method that accepts a type parameter, ‘T’. Is there a type constraint I can add to T so that I may sum them?”

As an example, consider this code: 

public string FormatNumber<T>(T t1, T t2) where T:IFormattable
{
    T sum = t1 + t2;
    return String.Format("{0:N2}", sum);
}

This sample is not very useful by itself, and it just an example.


The problem is, t1 and t2 cannot be added because the compiler cannot guarantee that t1 and t2 can even be summed. Unfortunately, generics do not have a way to add a constraint for operators yet.


The eventual solution was that the code needed some rethinking. The need for generics and adding them was legitimate, but not worth going over. Either way, I started wondering how this could be possible without doing something like this:

public T Add<T>(T t1, T t2)
{
    if (t1 is int)
        return (T) (object)(((int) ((object) t1)) + ((int) ((object) t2)));
    if (t1 is long)
        return (T) (object)(((long) ((object) t1)) + ((long) ((object) t2)));
    if (t1 is float)
            return (T) (object)(((float) ((object) t1)) + ((float) ((object) t2)));
    throw new NotImplementedException("Can't do addition.");
}

That just seems unattractive to me. So I starting thinking… and thinking… and it spiraled down from there. The solution that I came up with is very unattractive, slow, and nuts.


My solution? Dynamically generate an assembly using Reflection.Emit. It works, but not very elegant. Here it is:

public static T Add<T>(T t1, T t2)
{
    if (!typeof(T).IsPrimitive)
    {
        throw new Exception("Type is not primitive.");
    }
    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
    TypeBuilder typeProxy = moduleBuilder.DefineType("AdditionType", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Serializable);
    MethodBuilder methodBuilder = typeProxy.DefineMethod("SumGenerics", MethodAttributes.Static, typeof(T), new[] { typeof(T), typeof(T) });
    ILGenerator generator = methodBuilder.GetILGenerator();
    generator.Emit(OpCodes.Ldarg_S, 0);
    generator.Emit(OpCodes.Ldarg_S, 1);
    generator.Emit(OpCodes.Add);
    generator.Emit(OpCodes.Ret);
    Type adder = typeProxy.CreateType();
    MethodInfo mi = adder.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)[0];
    return (T)mi.Invoke(null, BindingFlags.Default, null, new object[] {t1, t2}, null);
}

Not to mention, that you have to tell your AppDomain how to resolve the type, and that involves a little magic with the OnAssemblyResolve of the AppDomain:

private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
    return args.Name == assemblyBuilder.FullName ? assemblyBuilder : null;
}

Sometimes, it just feels good to write crazy code and get it out of your system, before you really do start writing code…