Units in C # - almost

Inspired by Units in F # and despite the statement ( here ) that you could I do this in C #, I had an idea the other day that I played with.

namespace UnitsOfMeasure { public interface IUnit { } public static class Length { public interface ILength : IUnit { } public class m : ILength { } public class mm : ILength { } public class ft : ILength { } } public class Mass { public interface IMass : IUnit { } public class kg : IMass { } public class g : IMass { } public class lb : IMass { } } public class UnitDouble<T> where T : IUnit { public readonly double Value; public UnitDouble(double value) { Value = value; } public static UnitDouble<T> operator +(UnitDouble<T> first, UnitDouble<T> second) { return new UnitDouble<T>(first.Value + second.Value); } //TODO: minus operator/equality } } 

Usage example:

 var a = new UnitDouble<Length.m>(3.1); var b = new UnitDouble<Length.m>(4.9); var d = new UnitDouble<Mass.kg>(3.4); Console.WriteLine((a + b).Value); //Console.WriteLine((a + c).Value); <-- Compiler says no 

The next step is trying to implement transformations (fragment):

 public interface IUnit { double toBase { get; } } public static class Length { public interface ILength : IUnit { } public class m : ILength { public double toBase { get { return 1.0;} } } public class mm : ILength { public double toBase { get { return 1000.0; } } } public class ft : ILength { public double toBase { get { return 0.3048; } } } public static UnitDouble<R> Convert<T, R>(UnitDouble<T> input) where T : ILength, new() where R : ILength, new() { double mult = (new T() as IUnit).toBase; double div = (new R() as IUnit).toBase; return new UnitDouble<R>(input.Value * mult / div); } } 

(I would like to avoid instantiating objects using static, but as we all know, you cannot declare a static method in an interface ) Then you can do the following:

 var e = Length.Convert<Length.mm, Length.m>(c); var f = Length.Convert<Length.mm, Mass.kg>(d); <-- but not this 

Obviously there is a gaping hole in this compared to F # units (I will let you figure it out).

Oh, the question is, what do you think of this? Is it worth using? Has anyone else done better?

UPDATE for people interested in this subject area, here is a link to a document from 1997 discussing another type of solution (not specifically for C #)

+52
units-of-measurement c # f #
Dec 08 '08 at 7:32
source share
12 answers

You do not need dimensional analysis. For example (from the answer you referenced) in F # you can do this:

 let g = 9.8<m/s^2> 

and it will generate a new acceleration unit derived from meters and seconds (you can actually do the same in C ++ using templates).

In C #, you can perform dimensional analysis at runtime, but it adds extra overhead and prevents you from checking compile time. As far as I know, there is no way to do full compile time units in C #.

Whether it is worth doing, of course, depends on the application, but for many scientific applications this is definitely a good idea. I do not know of any existing libraries for .NET, but they probably exist.

If you're curious about how to do this at runtime, the idea is that each value has a scalar value and integers representing the power of each base block.

 class Unit { double scalar; int kg; int m; int s; // ... for each basic unit public Unit(double scalar, int kg, int m, int s) { this.scalar = scalar; this.kg = kg; this.m = m; this.s = s; ... } // For addition/subtraction, exponents must match public static Unit operator +(Unit first, Unit second) { if (UnitsAreCompatible(first, second)) { return new Unit( first.scalar + second.scalar, first.kg, first.m, first.s, ... ); } else { throw new Exception("Units must match for addition"); } } // For multiplication/division, add/subtract the exponents public static Unit operator *(Unit first, Unit second) { return new Unit( first.scalar * second.scalar, first.kg + second.kg, first.m + second.m, first.s + second.s, ... ); } public static bool UnitsAreCompatible(Unit first, Unit second) { return first.kg == second.kg && first.m == second.m && first.s == second.s ...; } } 

If you do not allow the user to change the value of units (a good idea anyway), you can add subclasses for common units:

 class Speed : Unit { public Speed(double x) : base(x, 0, 1, -1, ...); // m/s => m^1 * s^-1 { } } class Acceleration : Unit { public Acceleration(double x) : base(x, 0, 1, -2, ...); // m/s^2 => m^1 * s^-2 { } } 

You can also define more specific operators on derived types to avoid checking for compatible units on generic types.

+39
Dec 08 '08 at 8:03
source share

You can add extension methods for numeric types to generate measures. This will be a bit like DSL:

 var mass = 1.Kilogram(); var length = (1.2).Kilometres(); 

This is not quite a .NET convention, and perhaps not the most open function, so perhaps you would add them to the target namespace for those they like, and also suggest more traditional construction methods.

+15
Jun 01. '10 at 12:43 on
source share

Using separate classes for different units of the same measure (e.g. cm, mm and ft for length) seems strange. Based on the .NET Framework DateTime and TimeSpan classes, I would expect something like this:

 Length length = Length.FromMillimeters(n1); decimal lengthInFeet = length.Feet; Length length2 = length.AddFeet(n2); Length length3 = length + Length.FromMeters(n3); 
+14
Dec 08 '08 at 7:46
source share

Now such a C # library exists: http://www.codeproject.com/Articles/413750/Units-of-Measure-Validator-for-Csharp

It has almost the same functions as for checking the compilation time of the F # block, but for C #. The core is the MSBuild task, which analyzes the code and looks for validation.

Device information is stored in comments and attributes.

+9
Jul 03 '12 at 15:05
source share

I recently released Units.NET on GitHub and on NuGet .

It gives you all the common units and conversions. It is lightweight, tested and supports PCL.

Conversion Examples:

 Length meter = Length.FromMeters(1); double cm = meter.Centimeters; // 100 double yards = meter.Yards; // 1.09361 double feet = meter.Feet; // 3.28084 double inches = meter.Inches; // 39.3701 
+8
Jul 21 '13 at 22:55
source share

Thanks for the idea. I implemented units in C # in a variety of ways that always seem like a trick. Now I can try again using the ideas described above. My goal is to identify new units based on existing ones, for example

 Unit lbf = 4.44822162*N; Unit fps = feet/sec; Unit hp = 550*lbf*fps 

and for the program to determine the correct sizes, scaling, and symbol to use. In the end, I need to create a basic system of algebras that can transform things like (m/s)*(m*s)=m^2 and try to express the result based on existing units.

It is also necessary to be able to serialize blocks so that new blocks do not need to be encoded, but simply declared in an XML file as follows:

 <DefinedUnits> <DirectUnits> <!-- Base Units --> <DirectUnit Symbol="kg" Scale="1" Dims="(1,0,0,0,0)" /> <DirectUnit Symbol="m" Scale="1" Dims="(0,1,0,0,0)" /> <DirectUnit Symbol="s" Scale="1" Dims="(0,0,1,0,0)" /> ... <!-- Derived Units --> <DirectUnit Symbol="N" Scale="1" Dims="(1,1,-2,0,0)" /> <DirectUnit Symbol="R" Scale="1.8" Dims="(0,0,0,0,1)" /> ... </DirectUnits> <IndirectUnits> <!-- Composite Units --> <IndirectUnit Symbol="m/s" Scale="1" Lhs="m" Op="Divide" Rhs="s"/> <IndirectUnit Symbol="km/h" Scale="1" Lhs="km" Op="Divide" Rhs="hr"/> ... <IndirectUnit Symbol="hp" Scale="550.0" Lhs="lbf" Op="Multiply" Rhs="fps"/> </IndirectUnits> </DefinedUnits> 
+3
Nov 16 '10 at 20:13
source share

Here is my concern for creating units in C # / VB. Please correct me if you think I'm wrong. Most of the implementations I read about seem to be related to creating a structure that combines a value (int or double) with a unit. Then you try to identify the main functions (+ - * / etc.) for these structures that take into account unit conversions and consistency.

I find this idea very attractive, but every time I give up some huge step for a project, it seems. It is like an all-or-nothing deal. You probably would not just change a few numbers to units; the thing is that all the data inside the project is appropriately marked with a unit to avoid any ambiguity. This means saying goodbye to ordinary doubles and ints, each variable is now defined as a β€œunit” or β€œlength” or β€œmeters”, etc. Do people really do this on a large scale? Therefore, even if you have a large array, each element must be marked with a block. Obviously, this will have both dimensions and consequences.

Despite all the tricks in trying to push the block logic in the background, some bulky notation seems inevitable with C #. F # does some backstage magic that better reduces the irritation coefficient of the device logic.

Also, how well can we get the compiler to process the unit like a regular double, when we want it, without using CType or ".Value" or any additional notation? For example, with null values, does the code know how to handle double? just like a double (of course, if your double? is zero, then you get an error message).

+3
Feb 09 2018-11-21T00:
source share

there is jscience: http://jscience.org/ , but groovy dsl for units: http://groovy.dzone.com/news/domain-specific-language-unit- . iirc, C # has a closure, so you should be able to attach something.

+1
Dec 08 '08 at 7:55
source share

Why not use CodeDom to generate all possible unit permutations automatically? I know this is not the best, but I will definitely work!

0
Dec 08 '08 at 8:31
source share

See Boo Ometa (which will be available for Boo 1.0): Boo Ometa and Extensible Analysis

0
Jan 28 '09 at 22:07
source share

I really enjoyed reading this question and its answers.

I have a pet project that I've been messing with over the years, and recently started rewriting it and releasing it in open source at http://ngenericdimensions.codeplex.com

This is somewhat similar to many of the ideas expressed in the question and answers on this page.

Basically, it is about creating common dimensions with a unit of measure and your own data type as placeholders of a typical type.

For example:

 Dim myLength1 as New Length(of Miles, Int16)(123) 

Using some additional extension methods, for example:

 Dim myLength2 = 123.miles 

and

 Dim myLength3 = myLength1 + myLength2 Dim myArea1 = myLength1 * myLength2 

This will not compile:

 Dim myValue = 123.miles + 234.kilograms 

New blocks can be expanded in your own libraries.

These data types are structures that contain only one internal member variable, which makes them easy.

In principle, operator overloads are limited to "dimensionality" structures, so each unit does not need operator overloads.

Of course, the big flaw is the longer generics syntax declaration, which requires 3 types of data. So if this is a problem for you, then this is not your library.

The main goal was to decorate the interface with modules while checking compile time.

There is a lot to do in the library, but I would like to publish it if that was what someone was looking for.

0
Sep 27 '11 at 4:05
source share

you can use QuantitySystem instead of implementing it yourself. It is based on F # and greatly improves block management in F #. This is the best implementation I've found so far, and it can be used in C # projects.

http://quantitysystem.codeplex.com

0
Mar 30 '14 at 20:21
source share



All Articles