I mentioned Friday that I've completely refactored the section of the Inner Drive Extensible Architecture™—the Idea™—that handles quantitative functions. (I've also decided to make some SDK documentation available.) The actual design of the IMeasurable classes—Length, Speed and the like—bears more discussion.
What I have, essentially, is a collection of .NET structs that implement IMeasurable, one for each kind of thing you want to measure. The old design had a single struct called Numeric that could represent any kind of measurement at all. The change means that the measurement type is now enforced by the compiler rather than the run-time engine, because instead of this:
Numeric meters = new Numeric(10d, new Meter());
Numeric pounds = new Numeric(10d, new Pound());
Numeric compilesFine = meters.Add(pounds);
> IncompatibleUnitException thrown
You get this:
Length meters = new Length(10d, new Meter());
Mass pounds = new Mass(10d, typeof(Pound));
Length wontCompile = pounds.Add(meters);
> Compiler error: no overload of Mass.Add takes Length as a parameter
One thought I had, though, was: why not make each of the units a measureable type to further constrain the design? So you would have this instead:
Pound pounds = new Pound(10);
Gram grams = new Gram(10, MetricExponent.Kilo);
Pound pounds = pounds + grams;
Simply: that would require either dozens of nearly-identical structs, or I would have had to use classes instead. Remember that a struct can't inherit from anything except System.ValueType. So each struct either has to contain all the relevant code (about 1200 lines of it for each of my IMeasurable classes, including documentation), or it has to compose most of its functionality from other, static objects—which is actually quite difficult in this context.
Structs are small very fast, and immutable, which makes them the best choice for small, fast, immutable things like measurements. I'd rather represent measurements with structs than objects, but I'd also like to avoid writing and unit-testing dozens of nearly-identical classes. So I compromised: representing each type of measurement with its own struct (so there are now only 8 of them), and allow the abstract Unit class to define how the individual units interact.
Check out the Quantitative SDK and let me know what you think.