views:

72

answers:

2

I have a variation on a Quantity (Fowler) class that is designed to facilitate conversion between units. The type is declared as:

public class QuantityConvertibleUnits<TFactory> 
    where TFactory : ConvertableUnitFactory, new() { ... }

In order to do math operations between dissimilar units, I convert the right hand side of the operation to the equivalent Quantity of whatever unit the left hand side is in, and do the math on the amount (which is a double) before creating a new Quantity. Inside the generic Quantity class, I have the following:

    protected static TQuantity _Add<TQuantity>(TQuantity lhs, TQuantity rhs)
       where TQuantity : QuantityConvertibleUnits<TFactory>, new()
    {
        var toUnit = lhs.ConvertableUnit;
        var equivalentRhs = _Convert<TQuantity>(rhs.Quantity, toUnit);
        var newAmount = lhs.Quantity.Amount + equivalentRhs.Quantity.Amount;
        return _Convert<TQuantity>(new Quantity(newAmount, toUnit.Unit), toUnit);
    }

    protected static TQuantity _Subtract<TQuantity>(TQuantity lhs, TQuantity rhs)
       where TQuantity : QuantityConvertibleUnits<TFactory>, new()
    {
        var toUnit = lhs.ConvertableUnit;
        var equivalentRhs = _Convert<TQuantity>(rhs.Quantity, toUnit);
        var newAmount = lhs.Quantity.Amount - equivalentRhs.Quantity.Amount;
        return _Convert<TQuantity>(new Quantity(newAmount, toUnit.Unit), toUnit);
    }

    ... same for multiply and also divide

I need to get the typing right for a concrete Quantity, so an example of an add op looks like:

        public static ImperialLengthQuantity operator +(ImperialLengthQuantity lhs, ImperialLengthQuantity rhs) { return _Add(lhs, rhs); }

The question is those verbose methods in the Quantity class. The only change between the code is the math operator (+, -, *, etc.) so it seems that there should be a way to refactor them into a common method, but I am just not seeing it.

How can I refactor that code?

Cheers,
Berryl

A: 

There is no direct generic operator support in C#. You can fake it in C# 4.0 on .NET 4 using dynamic - i.e.

TQuantity newAmount = (dynamic)lhs.Quantity.Amount -
                      (dynamic)equivalentRhs.Quantity.Amount;

Or you can do it in .NET 3.5 and above using MiscUtil which provides an Operator class with utility methods (which supports custom and inbuilt operators):

TQuantity newAmount = Operator.Add(lhs.Quantity.Amount,
                                   equivalentRhs.Quantity.Amount);

Alternatively; provide an ICalculator<T> interface and write implementations per-type.

Marc Gravell
+4  A: 

You could write an

 protected static TQuantity _Operator<TQuantity>(TQuantity lhs, TQuantity rhs,
                                       Func<double, double, double> op) 
       where TQuantity : QuantityConvertibleUnits<TFactory>, new()
 {  
        var toUnit = lhs.ConvertableUnit;  
        var equivalentRhs = _Convert<TQuantity>(rhs.Quantity, toUnit);  
        var newAmount = op(lhs.Quantity.Amount,equivalentRhs.Quantity.Amount);  
        return _Convert<TQuantity>(new Quantity(newAmount, toUnit.Unit), toUnit);  
  }  

and call this function in your _Add, etc. using (d1, d2) => d1 + d2 as an additional parameter.

Jens
Sweet solution. Thanks!
Berryl