06 March 2009

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,

public static 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> {
    public static 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();

    protected struct Value {
        public T V;

        public Value(T v) { V = v; }

        public static Value operator +(Value a, Value b) { return new Value(Policy.Add(a.V, b.V)); }
        public static Value operator -(Value a, Value b) { return new Value(Policy.Subtract(a.V, b.V)); }
        public static Value operator *(Value a, Value b) { return new Value(Policy.Multiply(a.V, b.V)); }
        public static Value operator /(Value a, Value b) { return new Value(Policy.Divide(a.V, b.V)); }
        public static implicit operator Value(T v) { return new Value(v); }
        public static implicit operator Value(int v) { return new Value(Policy.FromInt(v)); }
        public static implicit operator T(Value v) { return v.V; }
    }

    protected static Value L(int value) { return new 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



blog comments powered by Disqus