One problem with C# generics is the lack of member constraints. In other words, this is impossible:
public static T Add<T>(T left, T right)
{
    return left + right;
}
People have come up with a large number of solutions, but they all have their own problems, ranging from messy looking syntax to less-than-optimal performance (particularly due to virtual function calls). The solution I propose is a somewhat hacky one (yes that's a word) which maintains both good performance and clean looking code: write the function in IL.

The code for the add method boils down to something like this:
ldarg.0
ldarg.1
add
ret
For those not familiar with IL, the above code adds the first to arguments of the method, then returns the result.

I found that this actually works with generic types. No funny errors, no nothing. Of course, I didn't actually compile any IL source code - I wrote a separate program to generate the assembly. The source code for it is this:
namespace Generator
{
    using System;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;

    public static class Program
    {
        public static void Main()
        {
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Operations"), AssemblyBuilderAccess.Save);
            var module = assemblyBuilder.DefineDynamicModule("Operations", "Operations.dll");
            var type = module.DefineType("Operator", TypeAttributes.Public | TypeAttributes.Sealed);

            type.DefineBinaryOperatorMethod("Add", OpCodes.Add);
            type.DefineBinaryOperatorMethod("Subtract", OpCodes.Sub);
            type.DefineBinaryOperatorMethod("Multiply", OpCodes.Mul);
            type.DefineBinaryOperatorMethod("Divide", OpCodes.Div);
            type.DefineBinaryOperatorMethod("Remainder", OpCodes.Rem);
            type.CreateType();

            assemblyBuilder.Save("Operations.dll");
        }

        private static MethodBuilder DefineBinaryOperatorMethod(this TypeBuilder type, string name, OpCode operation)
        {
            var method = type.DefineMethod(name, MethodAttributes.Public | MethodAttributes.Static);
            var genericParameter = method.DefineGenericParameters("T").First();
            genericParameter.SetBaseTypeConstraint(typeof(ValueType));
            
            method.SetReturnType(genericParameter);
            method.SetParameters(genericParameter, genericParameter);
            method.DefineParameter(1, ParameterAttributes.None, "left");
            method.DefineParameter(2, ParameterAttributes.None, "right");

            var ilGenerator = method.GetILGenerator();
            var continueLabel = ilGenerator.DefineLabel();

            ilGenerator.Emit(OpCodes.Ldtoken, genericParameter);
            ilGenerator.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);
            ilGenerator.EmitCall(OpCodes.Callvirt, typeof(Type).GetProperty("IsPrimitive").GetGetMethod(), null);
            ilGenerator.Emit(OpCodes.Brtrue_S, continueLabel);
            ilGenerator.Emit(OpCodes.Ldstr, "The specified type is not supported by this operation.");
            ilGenerator.Emit(OpCodes.Newobj, typeof(ArgumentException).GetConstructor(new[] { typeof(string) }));
            ilGenerator.Emit(OpCodes.Throw);
            ilGenerator.MarkLabel(continueLabel);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldarg_1);
            ilGenerator.Emit(operation);
            ilGenerator.Emit(OpCodes.Ret);

            return method;
        }
    }
}
In case your wondering what the ldtoken/call/callvirt/brtrue.s/ldstr/newobj/throw stuff is doing there, that just checks to make sure the type argument is a primitive type (yes this only works on primitive types), and throws an ArgumentException otherwise. Before adding that, .NET threw some pretty nasty exceptions when I tried it on some non-primitive types.

As a final note, the methods generated with indeed work on all primitive types. Even booleans. I was surprised to get a DivideByZeroException upon attempting this:
Operator.Divide(true, false);
Again, this only works on primitive types. Remember, string and decimal are not primitive types. Perhaps this could be extended to calling the op_* functions...

After a couple small performance tests, I found that this method was nearly as fast as doing simple addition (x + y). Then again, I know nothing about microbenchmarks. Try it for yourself :D

Enjoy
1

View comments

Moving...
Moving...
Automatic Casting in IL
Automatic Casting in IL
Spectral
Spectral
A Solution to Generic Arithmetic
A Solution to Generic Arithmetic
1
Extension Methods and Negative Numbers
Extension Methods and Negative Numbers
It's a sad, sad world...
It's a sad, sad world...
Reconstructing Position from Depth (XNA)
Reconstructing Position from Depth (XNA)
1
Polymorphic Static Functionality
Polymorphic Static Functionality
BindableProperty
BindableProperty
In an effort to emulate Reactive Programming, I have created this nifty little class. It's preliminary (created five minutes ago), but seems useful enough for now.
The Awesomeness of Code Contracts
The Awesomeness of Code Contracts
Turret Bot
Volume Rendering
Geometry Builder
Geometry Builder
Here's a small class for building vertex and index data. I used this sort of thing a while ago when I was just starting on my planet renderer. I quickly found out that it, while putting everything into one nice vertex buffer and index buffer (saving me draw calls), became way to expensive to use when I started using much larger amounts of data. Nonetheless, it's still useful for making smaller things, such as boxes.
Triangles
Triangles
OK, I lied. I've been looking at LOD again, except this time around, I'm using triangles, not square patches. I've hacked out a couple useful methods, namely: Closest Point on Triangle Distance from Point to Triangle
Reboot
Reboot
Blog Archive
Active Projects
Active Projects
Total Pageviews
Total Pageviews
10816
Loading