Tuesday, March 1, 2011

Units of Measure in C#: Introduction

F# provides the feature Units of Measure for sufficiently handling measurable entities of different units and quantities. Unfortunately this functionality is not accessible from the other .NET languages.

I have searched the internet for a similar solution in C#, but the approaches I have found so far have been relatively heavy-weight and not easily extendable. In particular, the measure type is generally implemented as a class with several members.

I would prefer a more light-weight solution where the measure type is implemented as a struct, preferably with a single member representing the measured amount in some reference unit for each specific quantity. At the same time I would like to have quantity type safety during compile-time, and it should the library should be easily extendable with new quantities and units without affecting the implementation of the measure type.

Based on these requirements, I have now implemented a compact C# Units of Measure library. I have implemented the library using the .NET 3.5 profile, and I have verified that it is possible to build under Mono (2.8 or later) as well. As far as I can tell, there is nothing preventing use of this library from other .NET languages either.

To facilitate feedback from other developers with similar requests, I have made the library available as open source (under the Eclipse Public License). The library is denoted csunits and is available on Github. A quick introduction to the API and instructions for extending the library are also provided on this web site.

I plan to continuously extend the library with new quantities and units, as well as add more functionality to it. In particular, I plan to implement a measure array type for efficient numerical handling of large number of measures in the same unit.

Any feedback on the library is more than welcome. Issues are preferably reported on the Github project web site, but comments to this blog post are perfectly OK as well :-) And ideas on how to further extend the library are of course very much appreciated.

11 comments:

  1. Hey, I had a look at your library and I think it looks very good.

    It is a shame that c# does not have the f# ability to include unit handling in the compiler.

    We will probably try to use it in an upcoming project, but we will have to extend the units and quantities to facilitate our domain.

    I hate reading licenses (and have trouble understanding them), but am I right when I am allowed to extend the library with more units and quantities by altering the code, and use the library in a commercial product?

    Note: I think there is a bug in QuantityDimesion line 37, the Length dimension should be 0, not 10.

    Cheers
    Julius

    ReplyDelete
  2. Hi Julius,

    and many thanks for pointing out the error in QuantityDimension.cs. Length dimension should indeed be zero for electric current :-) I will fix this right away.

    Please feel free to make any changes and extensions you like (as long as you do not remove the copyright notices), and if you would like to use the library in a commercial product, you are more than welcome.

    I would of course appreciate it if you would forward the extensions you make; maybe it could save me some work. Also, if you use the library in a commercial product it would be great if you notify me when it is available.

    Best regards,
    Anders

    ReplyDelete
  3. Hi Anders,

    Just wanted to let you know that we have done some extensions to the library to make it more fluent;

    Added extension methods for AmountType so you can write;

    75.0.kg()
    compared to
    new StandardMeasure< Mass>(75.0).

    Also added Times and Divide to Measure class in the form of
    public Measure< Q2> Times< Q1, Q2>(Measure< Q1> factor, IUnit< Q2> iUnit)...

    This enable you to write;

    var vol = 30.0.cm().Times(0.02.m2(), Volume.Liter);

    compared to
    Measure height = new Measure (30.0, Length.CentiMeter);
    StandardMeasure< Area> area = (StandardMeasure< Area>)0.02;

    StandardMeasure< Volume> stdVol;
    ArithmeticOperations.Times(height, area, out stdVol);

    var vol = stdVol[Volume.Liter]

    -Julius

    ReplyDelete
  4. Hi again Julius,

    and how very interesting that you implemented extension methods on AmountType. I have seen a similar suggestion on StackOverflow, http://stackoverflow.com/questions/348853/units-of-measure-in-c-almost/2949803#2949803, is that where you got the idea from as well?

    Also interesting that you have added Times and Divide methods. I made a similar implementation earlier, but ended up having concerns about overloaded methods, and decided to implement Times and Divide in a separate static class. But it is good to see the compactness you have been able to achieve.

    Thanks for sharing!
    Anders

    ReplyDelete
  5. Hi Anders,

    Yes, that is where I got the extension method idea from.

    I do see your concern about overload in the Measure class since you are publishing this as a library. We do not have this concern since we are a small business with more control over the usage. You could restrict the inheritance of the Measure class by making it sealed, but I guess that is not a preferred solution.

    I will keep you posted on changes we do!

    -Julius

    ReplyDelete
  6. Interesting! I've done similar library in C#:

    Here's how the API looks like:

    Duration hr = Measure.Duration(24);
    Area Area = Measure.Area(1000);
    Weight kg = Measure.Weight(10);

    Basic arithmetic:

    Length length1 = Measure.Length(10);
    Length length2 = Measure.Length(100);
    Length length3 = Measure.Length(1);
    //Do some operation
    Length newLength = length1 + length2 + length3;
    //Check result in meters
    Assert.Equal(1011, newLength.In());

    ReplyDelete
  7. oops.. looks like the generics tags were scraped out by blogger.

    Duration hr = Measure.Duration<Hours>(24);
    Area Area = Measure.Area<SquareMeters>(1000);
    Weight kg = Measure.Weight<Kilograms>(10);

    Length length1 = Measure.Length<Meters>(10);
    Length length2 = Measure.Length<Centimeters>(100);
    Length length3 = Measure.Length<Kilometers>(1);
    //Do some operation
    Length newLength = length1 + length2 + length3;
    //Check result in meters
    Assert.Equal(1011, newLength.In<Meters>());

    ReplyDelete
  8. Dear Darnell,

    many thanks for prodviding the examples from your Units of Measurement API. It is the library that is described in this blog post, right? http://splotter.wordpress.com/2009/09/03/csharp-unit-of-measurement/
    Have you been doing any more work on your library lately?

    When starting the work on csunits, I initially Googled for alternatives, and at that time I came across your solution as well. Yet some other approaches were described on Stackoverflow, as also discussed with Julius above.

    In my opinion, all available solutions were too heavy-weight, and therefore I designed my own library. Please try it out, and if you find issues, do not hesitate to report them.

    Best regards,
    Anders

    ReplyDelete
  9. That's stunning I didn't know about all this until the moment I saw this blog post of yours over here. I also wanted to ask you something that is connected with your blog. Do you own any valuable data about how to defend your entries from being used without your awareness about it?

    ReplyDelete
  10. Dear Young,

    I am not sure I understand your question? Could you please elaborate?

    Best regards,
    Anders

    ReplyDelete