After reading Luca Bolognese's
blog post regarding using
C# 4.0's dynamic keyword to simulate INumeric I immediately thought that must be a way to express
this with generics instead. Generics, however, do not allow you to abstract over operators (which is what
is is needed to really do INumeric
) but you can simulate it using adapters as described in
this post.
Say I want to write a general average routine that is generic over what type I am using to store the data.
That is, I want one routine that works equally for double
as well as decimal
.
What I want to be able to write is something like,
publicstatic T Average(params T[] values) { T total = 0;foreach (var v in values) total = total + v;return total / values.Length; }
where the T
is any type that supports adding and dividing. I can't get this code to
compile exactly but we can get surprisingly close! My final version looks like,
class Math<T, A> : PolicyUser<T, A> where A : struct, IOperatorPolicy<T> {publicstatic T Average(params T[] values) {var total = L(0);foreach (var v in values) total = total + v;return total / values.Length; } }
which is much closer to the original than I thought originally could get!
I started by cloning the operator policy and added a FromInt()
I will explain below,
interface IOperatorPolicy<T> { T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); T Divide(T a, T b); T FromInt(int value); }
Then I cloned the double
policy struct and then added a decimal
mainly by cut/paste/search/replace
struct DoubleOperatorPolicy : IOperatorPolicy<double> {double IOperatorPolicy<double>.Add(double a, double b) { return a + b; }double IOperatorPolicy<double>.Subtract(double a, double b) { return a - b; }double IOperatorPolicy<double>.Multiply(double a, double b) { return a * b; }double IOperatorPolicy<double>.Divide(double a, double b) { return a / b; }double IOperatorPolicy<double>.FromInt(int value) { return value; } }struct DecimalOperatorPolicy : IOperatorPolicy<decimal> {decimal IOperatorPolicy<decimal>.Add(decimal a, decimal b) { return a + b; }decimal IOperatorPolicy<decimal>.Subtract(decimal a, decimal b) { return a - b; }decimal IOperatorPolicy<decimal>.Multiply(decimal a, decimal b) { return a * b; }decimal IOperatorPolicy<decimal>.Divide(decimal a, decimal b) { return a / b; }decimal IOperatorPolicy<decimal>.FromInt(int value) { return value; } }
The cleaver bit is to define a base type that contains a struct
that defines the operators
as overloads. The overloads use a policy struct
to implement that overloaded operators. This struct
also defines implicit conversion operators to hide some (but not all) of the messiness introduced by using this
wrapper type. The PolicyUser
class looks like,
class PolicyUser<T, A> where A: struct, IOperatorPolicy<T> {static A Policy = new A();protectedstruct Value {public T V;public Value(T v) { V = v; }publicstatic Value operator +(Value a, Value b) { returnnew Value(Policy.Add(a.V, b.V)); }publicstatic Value operator -(Value a, Value b) { returnnew Value(Policy.Subtract(a.V, b.V)); }publicstatic Value operator *(Value a, Value b) { returnnew Value(Policy.Multiply(a.V, b.V)); }publicstatic Value operator /(Value a, Value b) { returnnew Value(Policy.Divide(a.V, b.V)); }publicstaticimplicitoperator Value(T v) { returnnew Value(v); }publicstaticimplicitoperator Value(int v) { returnnew Value(Policy.FromInt(v)); }publicstaticimplicitoperator T(Value v) { return v.V; } }protectedstatic Value L(int value) { returnnew Value(Policy.FromInt(value)); } }
What the Value
struct does is allows you to use the type Value
in place of the type T
whenever you want to
use operators. When operators are used you only need to ensure one sub-expression is promoted to Value
and the implict operator will
take care of the rest.
One remaining oddness is using literals. Implicit operators have there limits which makes using literal a bit strange. This was also odd in the Policy post as well and I used the same trick to make it a little less cumbersome. I introduced
a static method L()
that takes an integer and converts it to Value
allowing you to use integer literals by calling FromInt()
I introduced earlier. You can
extend to this to allow other types by repeating the pattern for, say double
, allowing you to use double
literals as well. I didn't
because I didn't need it for my example (but probably will if I try to implement something more complicated.
To bind the actual data type to provide the data type and the policy. To make this simpler I created two create implementations of Math
.
class DoubleMath : Math<double, DoubleOperatorPolicy> { }class DecimalMath : Math<decimal, DecimalOperatorPolicy> { }
Calling the average method looks like,
var resultDouble = DoubleMath.Average(1, 2, 3, 4, 5);var resultDecimal = DecimalMath.Average(1, 2, 3, 4, 5);
It should be straight forward to see how this could be adapted to a Financial
class, as Luca was trying to build, and how the routines could
be made independent of the data type used in the calculations.
There you have it. Policies can be used to simulate INumeric
without having to resort to using dynamic
.
Edit: Mar 9, 2009: Fixed HTML formatting error