When I wrote my Excel financial library I agonized over the decision of which numeric type to use to represent money. Logic would push me toward decimal, but common usage among financial library writers would push me toward double. I ended up picking double, but I regret having to make that choice in the first place.
Conceptually, I’d like my numeric functions to work for anything that supports the basic arithmetic operators (i.e. +, -, *). Unfortunately that is not possible in .NET at this point in time. In essence you have to write your code twice as below.
static double SumDouble(double a, double b) { return a + b; }
static decimal SumDecimal(decimal a, decimal b) { return a + b; }
Granted, this is not a good state of affairs. We often discussed how to make it work, but we couldn’t find a solution that was both fast to run and cheap for us to implement. More often than not we speculated about having the numeric types implement a specific INumeric interface and add a generic constraint to the C#/VB languages to make it work. Hence the title of this post.
With we implemented dynamic in C# 4.0 it occurred to me that you can fake your way into writing your code just once. For sure, this solution doesn’t have the same performance characteristics of ‘writing your code twice’, but at least it doesn’t duplicate your code.
This is how it looks like:
static dynamic Sum1(dynamic a, dynamic b) { return a + b; }
The call to the ‘+’ operator is resolved at runtime, by the C# binder, hence a performance penalty is incurred. The penalty is less than you might think, given that the DLR caches things under the cover so that no v-table lookup is performed the second time around. The whole thing is explained in more detail here. But still, it is not as fast as a normal ‘+’ operator over a primitive type. I’ll let you enjoy micro performance testing this one 🙂
A slight refinement is to make the code generic so that a caller doesn’t see a signature with dynamic types as arguments.
static dynamic Sum2<T1, T2>(T1 a, T2 b)
{
dynamic ad = a;
dynamic bd = b;
return ad + bd;
}
I could make the return type generic as well, but that would force the caller to be explicit about the types, making the calling code much less readable. The other good thing about this signature is that you get a different call site with each combination of type arguments and, since they are separate, the binding caches should stay small. With the former signature there is only one call site and the cache could pile up to the point where the DLR decides to discard it.
Here is how the calling code looks like right now:
Console.WriteLine(Sum2(2m, 4m));
Console.WriteLine(Sum2(2.0, 4.0));
Console.WriteLine(Sum2(new DateTime(2000,12,1), new TimeSpan(24,0,0)));
Yet another way to write this code is as follows:
public static T Sum3<T>(T a, T b)
{
dynamic ad = a;
dynamic bd = b;
return ad + bd;
}
This gets around the problem of showing a dynamic return value and give you some more compile time type checking. But it prevents summing not coercible types. The compiler doesn’t let you get there. The last line below wont’ compile:
Console.WriteLine(Sum3(2m, 4m));
Console.WriteLine(Sum3(2.0, 4.0));
//Console.WriteLine(Sum3(new DateTime(2000,12,1), new TimeSpan(24,0,0)));
Also notice that in VB you could have done this a long time ago 🙂
Function Sum(Of T1, T2)(ByVal a As T1, ByVal b As T2)
Dim aa As Object = a
Dim bb As Object = b
Return aa + bb
End Function
In summary, by using dynamic you can write your numeric code just once, but you pay a performance price. You are the only one who can decide if the price is worth paying in your particular application. As often, the profiler is your friend.
View comments on GitHub or email me