Click here to Skip to main content
Click here to Skip to main content

Units of Measure Validator for C#

By , 2 Jul 2012
 

Introduction

In C#, the sum of 1 meter and 1 second will be 2. But in most cases, adding or assigning two quantities of different units is a mistake of the programmer, which causes an unexpected and undesired runtime behavior.

This validator checks at compile time for unit violations without adding a new syntax or changing the runtime behavior - only the used units has to be specified through comments or attributes.

Figure 1: Validation Sample

Considering the units in figure 1, planet.Speed is meter per second, resultingForce is Newton (kg * m/s^2) and planet.Mass is kilogram. Since the quotient of Newton and kilogram is an acceleration, this quantity cannot be assigned to a speed - a mistake of the programmer, but detected even before starting the whole application.

Usage

The validator is available as MSBuild task and can be included inside any cs-project, whereas a reference at runtime is not needed. The units can be declared within a single project through xml documentation, but for project wide consistence, it is recommend to use the Unit-attribute. In this case, a runtime reference is needed to HDUnitsOfMeasureLibrary.

Installation

First, download either the binaries of this library or compile the library by yourself. All these assemblies should be placed into a suitable folder. 

If you want to install the validator for all projects (not recommended, and you should know what you are doing), you can edit the Microsoft.CSharp.Targets file, otherwise you can install it for each project separately by editing its csproj-file. To do this, open the file with an editor and replace the following comment: 

<!--
<Target Name="BeforeBuild">
</Target>
-->

with this XML:

<UsingTask TaskName="HDLibrary.UnitsOfMeasure.Validator.UnitValidationTask"
    AssemblyFile="Path-To-Your-Installation-Folder\HDUnitsOfMeasureValidatorTask.dll" />
<Target Name="BeforeBuild" DependsOnTargets="ResolveProjectReferences;ResolveAssemblyReferences">
  <UnitValidationTask Files="@(Compile)" References="@(_ResolveAssemblyReferenceResolvedFiles)" />
</Target>

You should adapt "Path-To-Your-Installation-Folder" to the path of your folder in which you placed your copy of HDUnitsOfMeasureValidatorTask.dll.

That is it - the next time you will build your project, unit violations are displayed within Visual Studio!

First Steps 

By default, the unit of each variable or member is unknown. The behavior of unknown units is the same as without validation. Thus, to work with this validator, the physical elements (like speed, length or time) should be marked with their corresponding units.

Only operations on those marked elements are evaluated.

Declaring Units

For Local Variables

You can declare a unit for a local variable by adding an one-line comment which encloses the unit in square brackets. Between the comment token (//) and the opening bracket, no characters are allowed. After the closing bracket, an explaining text can be added, separated by a whitespace. 

Examples:

int length; //[m] the length in meter
double time; //[s]

For Properties and Fields 

The units of properties and fields can be declared through a xml documentation: 

/// <summary>
/// The length in [m]
/// </summary>
private double length;

/// <summary>
/// The time in [s]
/// </summary>
private double Time {get; set;} 

The unit can be specified anywhere inside the summary-tag and has to be enclosed in square brackets. 

It is of disadvantage, that the unit description is only available for references within this project, because references from other projects currently have no access to the xml documentation and assume the unit of this member as "unknown". 

For this case, you can use the UnitAttribute. Here it is of disadvantage, that you have to add an assembly reference to HDUnitsOfMeasureLibrary.dll, and this dependency still exists at runtime. Nevertheless, using the Unit-attribute is the recommended way:

[Unit("m")]
private double length;

[Unit("s")]
private double Time {get; set;}

For Methods, Constructors and Parameters 

Methods and constructors are parameterized members, which have several input units and zero or one output unit (for constructors, the output unit, if given, determines the unit of the created object). 

If you want to declare the unit within the xml documentation, you can do it the following way: 

/// <param name="time">time ([s])</param>
/// <param name="number">number in [1] - number is a dimensionless quantity</param>
/// <param name="unknown">can be any unit, no unit is specified</param>
/// <returns>the length, [m]</returns>
public int GetLength(int time, int number, double unknown) {...} 

If no unit is specified for any of the parameters, its unit is unknown. 

Unknown Units in contrast to Dimensionless Units 

If no unit is specified, the unit is unknown. Unknown units are equal to dimensionless units, but assigns to unknown quantities are not validated:

double number = 1; //[1] dimensionless unit
double value = 1; //unknown unit
double length = 1.Meter(); //[m] a length in meters

number = value; //is ok
value = number; //is ok
value = length; //is ok, since no validation is done
number = length; //is not ok, since number is "1" and length is "m"

The Vicious Circle of Units 

Since the unit of a hard coded number is dimensionless, you cannot assign it to an element with a different unit than dimensionless. To break this circle, you can insert the comment "/*ignore unit*/" (no whitespaces) exactly after the "=" in the assignment - such assignments are not validated. 

With the predefined constants in the Units class or the extension methods on int and double, you can easily convert any unknown or dimensionless unit into any target unit without using "ignore unit".

int time = 5; //[m] -> not ok, since 5 is unknown ("1") and time is "m"
time =/*ignore unit*/5; -> is ok, since this assignment will not be validated
time = 5 * Units.Meter; //is ok, since Units.Meter is 1 meter
time = 5.Meter(); //is ok
time = 5.AsUnit("m"); //is ok
time = 5.AsUnit("km").ConvertFromTo("km", "m"); //is ok

Validation

The following assignments to elements with a known unit are validated:
=, *=, /=, +=, -=

Arguments in method invocations are validated, if the parameters require certain units.

A validation is successful, if the inferred unit from the expression is equal to the unit of the target element. Two units are equal if they have the same coherent unit (see here, chapter "What are coherent Units?") and the same conversion factor to it. So, "km" is not equal to "m", but "MN" (Mega newton) is equal to "Gg * m / s^2" (Giga gram * meter / square-second).

Dynamic Unit Descriptions

Usually, each unit description is static. If "a" is declared as meter, "a" will be always meter.

But this is not true for class members or methods: If a vector is declared as meter, the length of the vector is meter too. If a vector is declared as Newton, the length of the vector will be Newton.

This genericity can be accomplished by dynamic unit descriptions. Those descriptions can be resolved into concrete units by involving the used context (the target object and the arguments). Such a dynamic unit can be specified with the DynamicUnitAttribute.

There are three kinds of dynamic expressions: The "@" is the only one which can be used with properties and fields. If you declare the length of a vector as "@", the unit of the target object will be inserted. So if the vector is declared as X, its length is X too. You can even build some more complex expressions like "m/@" or "@^2" - depending on the context it will be "m/m" or "m^2" (if the target object is declared as "m"). 

With curly brackets, the unit of the enclosed parameter can be referenced. Square brackets will insert the value of the argument passed to the referenced parameter. If the referenced parameter is a string and has the attribute UnitDescriptionAttribute, the validator will check this argument to be a valid unit string, if the argument is a constant expression. This attribute can define an underlaying unit constraint for this string. If the underlaying unit constraint is "m", valid arguments are "km" or "cm". This constraint can be dynamic, too.

A parameter can be referenced by its zero based index or its name. 

As example, here the signature of the ConvertFromTo method:

[return: DynamicUnit("[targetUnitString]")]
public static double ConvertFromTo([DynamicUnit("[sourceUnitString]")] this double value,
	[UnitString] string sourceUnitString, [UnitString(UnderlayingUnitConstraint = "[sourceUnitString]")] string targetUnitString)

The result unit is the value of the targetUnitString argument. targetUnitString has to be a valid unit string, which has to be the same underlying unit as the sourceUnitString argument.

If value is kilometer, sourceUnitString has to be "km", otherwise the validator logs an error. If sourceUnitString is "km", targetUnitString has to be some kind of length too - e.g. "cm" or "µm".

External Unit Definition and Description 

If you want to introduce a new unit, you can use the assembly attribute UnitDefinitionAttribute

[assembly: UnitDefinition("N", "Newton", "kg * m/s^2")]

This unit is then available for this project and all projects, which reference this project.

If you want to describe the units of an foreign element, you can use the attribute UnitDescriptionAttribute:

[assembly: UnitDescription("System.Windows.Vector.Length", ResultUnit = "@")]

Beside the result unit, you can define the parameter units for parameterized members too. In this case, you can even specify the overloading for this description (e.g. "double,double").

An example:

[assembly: UnitDescription("System.Math.Min", ResultUnit = "{0}", ParameterUnits = ",{0}")]

This description forces the second argument to have the same unit as the first. At the same time it specifies the unit of the result. The first argument can be any unit.

Thus, the resulting unit of Math.Min(1.Meter(), 2.Meter()) will be meter. 

Implementation, a Short Overview

The core libraries of this validator are NRefactory and my HDUnitsOfMeasureLibrary.

The process of validation is separated into different steps, some of them are parallelized.

First, the code is parsed into an abstract syntax tree. Parallel to this, the referenced assemblies are loaded (if not cached) using Mono.Cecil. Then both are converted into a single type-system. This type-system is traversed to find references to the same element and to extract the units from the attributes and xml documentation. After this step, each element has an unit description. An unit description can resolve possible dynamic units into concrete units by analyzing the passed target object and arguments. 

In the next step, the whole abstract syntax tree is traversed again (but now parallelized) to validate assignments and method calls by using the extracted units and the inferred units of expressions. 

Future 

This library is still under development - there are still some bugs and limitations.

Currently, one big issue is the performance: Despite parallelization, it is still quite slow - but there are enough points to optimize.

History 

  • 1.0 Initial version 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Henning Dieterichs
Student
Germany Germany
Member
I am a 18 years old German student, who just finished his diploma from German secondary school qualifying for university admission.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionGread idea, but I can't get it to work properly.memberMember 959045511 Nov '12 - 5:52 
Hi Henning,
 
I've been playing around with your lib for the last hour or so. The problem I have is with introducing new kinds of units.
 
Suppose I've got these declarations somewhere in my code:
[assembly: UnitDefinition("N", "Newton", "kg * m/s^2")]
[assembly: UnitDefinition("A", "Ampere")]
[assembly: UnitDefinition("W", "Watt", "kg * m^2 / s^3")]
[assembly: UnitDefinition("rad", "Radian")]
[assembly: UnitDefinition("kW", "Kilowatt", "1000 W")]
 
Now when I try something like this:
public class Foobar
{
  [Unit("rad/s")]
  private double _myRPM;
}
 
I get an error saying that the unit "rad/s" could not be parsed. The same ist true with trying to declare new units that derive from previously added units - e.g. deriving Watts from N*m/s, or declaring Kilowatts.
 
Using the new units natively (just Newtons or Ampere) works.
 
I'm using the binary you posted. I did look around the source for some time but could not make much of it in the time I had.
 
Great concept, anyway. Looking forward to using it when I can. Smile | :)
 
Regards
Andi
GeneralRe: Gread idea, but I can't get it to work properly.memberHenning Dieterichs11 Nov '12 - 10:57 
Hello!
I can reproduce it, and it seems to be a strange bug, since "rad" can be parsed.
However, there are too much things I don't like any more, that I am thinking of starting from scratch...
Introducing the "UnitDefinition" attribute (and all the other assembly attributes) was a big mistake, a xml resource seems by far to be a better way.
 
Does anybody have any suggestions or feature requests for a possible future version?
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

QuestionAssign unit definition to items of listmemberThecentury3 Sep '12 - 22:52 
How can I assign unit definition to items of generic List?
BugNull Reference Exception on buildmemberThecentury3 Sep '12 - 22:37 
Added the following lines into SplittedUnitDescriptionProvider.GetUnitDescription() to prevent NRE.
 
IProperty property = member as IProperty;
if ( property != null )
{
    if ( property.CanSet )
    {
        method = property.Setter;
    }
    else if ( property.CanGet )
    {
        method = property.Getter;
    }
}</pre>

GeneralMy vote of 5memberJean Paul V.A13 Jul '12 - 13:48 
Superb Article!
GeneralMy vote of 5memberShlomiO11 Jul '12 - 9:22 
Wow!!
A very inspiring article.
GeneralMy vote of 4memberDerndorfer11 Jul '12 - 1:09 
The article is excellent, but the code-example contains a severe error concerning units of measure.
 
The code (line 38) is wrong. The correct code would be:
 
resultingForce = G *(planetMass * otherPlante) / Math.Pow (distance, 2.0);
 
The gravitational constant G has the units [m�/(kg.s�].
 
Then the dimensional equation is correct.
GeneralRe: My vote of 4 [modified]memberHenning Dieterichs11 Jul '12 - 1:26 
Thank you for your comment!
 
But you haven't tried the attached demo, have you?
If the demo is correct, how the validator, which checks for mistakes, would be demonstrated?
-> This mistake is intended.
 
Quote:
Error 4 "kg * m / s^2" expected, but got "m^3". C:\Users\Henning Dieterichs\Desktop\Validator_Source\Validator\ValidatorDemo\ConsoleApplicationValidatorDemo\PlanetSystem.cs 38 39 ConsoleApplicationValidatorDemo


 
-- edit: See my next comment.
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...


modified 11 Jul '12 - 12:50.

GeneralRe: My vote of 4memberDerndorfer11 Jul '12 - 1:34 
Your assumption is correct: I did not use the demo.
 
If this mistake was intended, then it should be explained in the text below (as you did some code-lines later).
GeneralRe: My vote of 4memberHenning Dieterichs11 Jul '12 - 2:12 
Hi,
 
I am stupid... (and currently due to my hard day very exhausted Smile | :) )
- Please ignore my last comment.
 
You are in right, "this" mistake was not intended; I am in right too, there is actually no mistake (the error I've quoted has a different cause).
In the screenshot, the line is not complete - the missing part you mentioned is on the right, but, due to the size of the screenshot, got lost (as well as the semicolon). In the next update, I will shorten the width of the screenshot a bit more, to make it more clearer that the line continues.
 

The line in the demo application actually says the following:
resultingForce += G * (planet.Mass * otherPlanet.Mass) * distance / Math.Pow(distance.Length, 3);
My validator has claimed, because in the demo application is another intended mistake: Mass is specified as "m" in the comments and as "kg" in the unit-attribute to demonstrate the capabilities of this validator. On the one hand, the validator assumes, that Mass is "m", but on the other hand he claims that two different units are specified for the same element.
 
Greetings,
Henning
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

GeneralMy vote of 5memberGalatei9 Jul '12 - 9:07 
This is five as hell. Great job.
GeneralMy vote of 5memberpita20123 Jul '12 - 8:48 
excellent, thank you for bringing the concept of units from f# to c# in a unique way
QuestionValidation vs New TypesmvpPaulo Zemek3 Jul '12 - 8:04 
I would like to see a comparison between this technique and the creation of new types.
 
For example, you start saying that the sum of 1 second and 1 meter is 2.
But, if we look at the .Net itself a second is usually represented by a TimeSpan. So that's not exactly true. The sum of two ints is an int. The sum of two doubles is a double. The sum of a DateTime and a TimeStamp is a DateTime with the adjusted time.
 
So, explicit type can be used when dealing with distances. TimeSpan is already a "time" distance. We may also create a Distance type. So, a Distance of 1m + a TimeStamp of 1 second equals compile time error.
 
I am pretty sure that it works great this way. So, have you any comments on this? Or a reason to consider better to use a validator instead?
GeneralRe: Validation vs New TypesmemberHenning Dieterichs3 Jul '12 - 12:29 
There are a few points I am missing in my own article, too, but after finishing the hard coding work, I was to exhausted to write such a comprehensive article Wink | ;) (I couldn't wait to publish it)
 
In the next update, I am going to discuss some design decisions I made in terms of the implementation of this validator.
 

The very big issue of units of measure libraries using generics like this one on stackoverflow[^], is the following:
Quantity<N> force = 1.Meter();
Quantity<s> time = 1.Second();
Quantity<Nps> specificSpeed = force / time; //Newton per seconds
You would need a lot (=> infinite) of overloadings to handle all possible operations: N/s, N/N, N/m, s/N, m/N, and the same for *, and there would be still some limitations - what about N*N*N*N*N*N*N*N (the nth product has to define the multiplication result with the (n-1)th product)?
C# generics are not flexible enough.
Even for very simple applications I am not recommending using generics for units, since at some time you may want to multiply or divide two units - which would render the generic approach totally useless.
 
A good question, by the way.
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

GeneralRe: Validation vs New TypesmvpPaulo Zemek3 Jul '12 - 12:34 
But that's a generic solution.
I mean really restrictive ones.
 
Create a type to mean Newton per Second. In that type you create all the needed operators (if it is multiply-able by time, which is the result? The fact is, you will need to implement such operator).
I can see the advantage that it is really validated at compile time. I can't say about performance considering the .Net inlinings and optimizations, but it is surely a lot of work.
So, if you can talk about that, it will be great.
 
What I mean is (using DateTime, TimeSpan again):
DateTime + DateTime, if I remember well, is not valid.
DateTime + TimeSpan = DateTime.
TimeSpan + TimeSpan = TimeSpan.
So, DateTime + TimeSpan + TimeSpan + TimeSpan is still a DateTime.
 

----
 
Edit: Personally, I would love to have an "easy type generator".
Maybe it is something like what you shown with the Quantity generic class, but it would be a meta-model, not a real generic.
 
For example, it should be simple to say: Distance(double) and it will really create a struct named Distance.
Then, you could put things like Distance+Distance=Distance (and it should be capable to using the sum between the doubles that really compose it).
Distance+double = Distance (so you are telling that it should support addition by double).
Distance + SomeType = SomeOtherType (and it will implement it the same way... if the type is a real type will try to use it, if it is a type declared the same way as Distance, will use its inner content to do the operation).
GeneralRe: Validation vs New TypesmemberHenning Dieterichs4 Jul '12 - 1:29 
In the moment, I've not much time, so my response will be short:
 
There are simply too much operations (think of all specific quantities, i.e. quantities per kilogram). And additions and subtractions are not the problem, since these operations do not change the unit (in most cases), in contrast to multiplications and divisions, which change always the unit or dimension.
 
And if you introduce a new unit and if someone other find a relationship between this new unit and an other new unit, he wont be able to put them into one quantity. E.g. Newton/Dollar - the more force you want, the more dollars you need - wont be possible without modifying dollar or Newton to make them compatible.
 
And the concept of class-generation raises more problems than it solves:
E.g. one problem:
If I have a new unit in one project (e.g. the hall-constant) and want to use this unit in an other project, the generated class has to be shared, otherwise there are not equal.
 

But I will think on it, nevertheless.
 

----
You could even abdicate the concept of generics by using a class generator with T4-Templates - its nearly the same question.
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

GeneralRe: Validation vs New TypesmemberHenning Dieterichs5 Jul '12 - 11:31 
After (virtually) trying out some concepts, here my final answer:
 
You could actually use the .Net type system to validate the correct use of units instead of this validator.
There would be a Meter class, a Kilogram class, a Newton class and others, like TimeSpan and DateTime.
Each coherent unit has its own class (specific units too, e.g. SievertPerKilogram etc.).
 
Only coherent units and derived units based on coherent units are allowed (m, s, but neither km nor hour), to limit the number of units and combinations. Conversions from feet to meters should be separated into a conversion class (this could cause rounding problems: adding 1 MOhm to 4 MOhm would require a conversion to Ohm and then back to MOhm).
 
I would guess, there are still about 200 commonly used coherent and derived units (assuming there are 7 SI base units and the most important combinations are possible).
Since most of these 200 classes have to know each other ([1]), it would be a maintaining disaster.
If you want to introduce a new unit, you have to change all underlaying units or units which could be derived from this unit, otherwise multiplications and divisions won't work (consider the associative property of doubles, (a*b)*c = a*(b*c)). If you can abdicate the operator overriding of "*" and "/", you could move these unit associations into their own classes to decrease the dependencies, but this horrifies the readability of your expressions to validate (UnitAssociations.Divide(Meters, Seconds) is ugly to read).
 
To maintain all the 200 classes, you would need a type generator.
But all these 200 classes have to be defined in one single assembly, as there are always circular references (m/s -> mps => mps * s => m, dependencies from m or s to mps and from mps or s to m). So, you cannot add new units in custom assemblies.
Each developer would have its own unit-library.
 
A problem of type generators is, that generating twice the same unit in different assemblies will result in incompatible units, so all different unit-libraries are incompatible.
 
And what if you want to use vectors? You would need to define for each unit class a vector unit class (2D and 3D). And what happens, if you want to multiply them?
 
This problem cannot be solved by generics (like Vector2D), because a dynamic generic result is not possible [2].
 

So I highly recommend not trying to write such a library.
 

[1]:
Sievert / Kilogram = Sievert.Div(Sievert, Kilogram) = SievertPerKilogram
SievertPerKilogram * Kilogram = SievertPerKilogram.Mul(SievertPerKilogram, Kilogram)
Kilogram * SievertPerKilogram = SievertPerKilogram.Mul(Kilogram, SievertPerKilogram)
= Sievert
 
Dependencies:
Sievert -> Kilogram, SieverPerKilogram
SievertPerKilogram -> Kilogram, Sievert
=> Total 4 dependencies for 3 units...
 
[2]:
Generic overriding of "*" or "/" is not possible...
 
public static Vector operator *(Vector v1, Vector v2)
{
TResult = v1.Unit * v2.Unit; //Not possible
//v1.Unit and v2.Unit are incompatible and unknown at compile time, a multiplication is not possible
return new Vector(v1.UnderlayingVector * v2.UnderlayingVector);
}
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

GeneralRe: Validation vs New TypesmvpPaulo Zemek5 Jul '12 - 11:55 
So... if you can put that in the article, I think it will be a great addition.
GeneralRe: Validation vs New Types [modified]memberQwertie10 Jul '12 - 10:24 
I agree completely that using the regular type system for units is a bad idea. In fact, it's a *terrible* idea.
 
It's not just because you have to create lots of types. In C++ someone made a template class to represent the basic SI units, so for example Unit<float,1,0,0,0,0,0,0> might represent metres, Unit<float,0,1,0,0,0,0,0> might represent seconds, and Unit<float,1,-1,0,0,0,0,0> would represent metres per second. Using typedef you could have aliases like Unit<float>::metre and Unit<float>::second.
 
Even though this solution only needs one single generic type, it is extremely unpleasant to use. Why?
 
1. Very cumbersome. You have to type things like Unit<float>::second all over the place.
2. Incompatible with standard libraries. Even if Unit<float>::second converts to float implicitly, if you call Abs((Unit<float>::second) -3), the output is 3, not (Unit<float>::second)3 like you want.
3. Unit inference is impossible.
 
To make matters worse, .NET may do a poor job of optimizing generics. Metres<float> will be slower than a plan float in some (not all) cases.
 
Unit inference makes a unit checker much easier to use. For example, if I write this function:
 
public int Abs(int x) { return x < 0 ? -x : x; }
public double Square(double x) { return x*x; }
 
A unit inference engine can determine automatically that Abs returns the same unit as its argument, and Square returns the square of its argument. No annotations are needed whatsoever. The output units can be inferred, and the functions can accept ANY input units.
 
Or consider this:
 
double cachedSpeed = double.NaN;
double calculateSpeed([Unit("m/s^2")] double accel, [Unit("s")] double time)
{
   if (double.IsNaN(cachedSpeed))
      cachedSpeed = accel * time;
   return cachedSpeed;
}
 
Notice that there is no unit on cachedSpeed. However, a unit inference engine can detect that cachedSpeed has a unit of m/s and that the calculateSpeed() method also has the same units.
 
Henning's article does not give the impression that this unit checker supports this kind of inference. However, it could support inference in the future, and user code would not require any changes (unless the unit checker finds some new bugs Big Grin | :-D ).

-- modified 10 Jul '12 - 16:32.
GeneralRe: Validation vs New TypesmemberHenning Dieterichs10 Jul '12 - 11:15 
Thank you for your arguments!
 
Quote:
A unit inference engine can determine automatically that Abs returns the same unit as its argument

In principle, my engine is capable of inferring such unit information. But what happens, if the validator does not have access to the source of this method (e.g. because the method is inside a different assembly)?
 
That's why my validator supports dynamic units:
[return:DynamicUnit("{x}")]
public int Abs([AnyUnit] int x) { return x < 0 ? -x : x; }
If you try to return "x*x", my validator claims, because the result is expected to have the same unit of "x".
 
Because you cannot add unit attributes to Math.Abs since it is closed source, you can do it externally by using the UnitDescription-attribute. In the next release, this is done by xml-resources inside the assembly.
 

Your last idea sounds very interesting:
Quote:
and user code would not require any changes

This could be possible, but it would not be a simple "unit" validator any more.
But how to tell the validator that two quantities have the same unit?
double length1 = 4;
double speed = length1 / time;
double lendth2 = speed * length1; //how the validator will know that length2 is meter (surely by inferring the unit, but you cannot validate something if you have nothing to validate against)?
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

GeneralRe: Validation vs New TypesmemberQwertie10 Jul '12 - 19:55 
Er, length2 is not metres! Given this:
 
double length1 = 4;                        // line 10
double speed = length1 / time;         // line 11
double lendth2 = speed * length1;   // line 12
 
An inference engine can detect and store the relationships between the units of the variables. If you then write:
 
length1 += 4.AsUnit("m");               // line 13
time = 10.AsUnit("s");                     // line 14
 
The engine can now detect that length1 is m, speed is m/s, length2 is m^2/s. Then the engine can give an error if you write
 
double total = length1+length2;
// Error message could be: unit mismatch in length1+length2.
// length1 is m (derived from line 13)
// length2 is m^2/s (derived from line 12, 11, 14, 13)
 
I wrote a unit inference engine for boo, but I don't remember how it works. I only remember that it is not as difficult as it first seemed Smile | :) However, my inference engine was not fully completed (end of university! EDIT: actually I think I stopped working on it because the author of boo completely ignored my work and refused to allow any changes to the boo grammar in support of unit checking) ... and it did not have any "dynamic units" like yours. I could send you the source code if you like; also I wrote a final report and slides that you may be interested in reading.
 
"But what happens, if the validator does not have access to the source of this method (e.g. because the method is inside a different assembly)?"
 
It's slightly unfortunate that your engine processes C#. If it parsed CIL instead, it could scan mscorlib.dll and learn all the unit relationships of the methods in it. Theoretically it could analyze Math.Abs:
 
.method public hidebysig static int32 Abs (int32 'value') cil managed
{
     IL_0000: ldarg.0
     IL_0001: ldc.i4.0
     IL_0002: blt.s IL_0006
     IL_0004: ldarg.0
     IL_0005: ret
     IL_0006: ldarg.0
     IL_0007: call int32 System.Math::AbsHelper(int32)
     IL_000c: ret
}
 
and learn that the return value has the same units as the argument. Not easy, but possible.
GeneralRe: Validation vs New Types [modified]memberQwertie11 Jul '12 - 4:14 
Henning Dieterichs wrote:
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...
 
Youe English is good, but:
 
"But what happens, if the validator" - no comma allowed.
"If you try to return "x*x", my validator claims, ..." is meaningless. "claims" is transitive, it must claim something.
 
From the article:
 
"That is it" - no one says "That is it". Use "That is all", "That's all", or "That's it".
"A validation is successful, if the inferred unit" - no comma allowed.
"But this is not true for class members or methods: If a vector is declared as meter, the length of the vector is meter too. If a vector is declared as Newton, the length of the vector will be Newton." - Correct, but when I read this I was very confused at first, because I am also a C++ programmer and vector<T> is like List<T> in C++. Also, if your unit checker does not assume the units of X = Y = Vector, the article should say "the length of the vector should be in meters" and "the length of the vector should be in Newtons", instead of "will be Newton".

modified 11 Jul '12 - 10:49.

GeneralRe: Validation vs New TypesmemberHenning Dieterichs11 Jul '12 - 11:48 
Thank you very much for your corrections! I will include them in the next update.
The use of comma seems to be my biggest problem at the moment...
If you find spelling- or grammer-mistakes, please let me know, so that I can correct them (at least for me) - english is not my first language...

QuestionnicememberCIDev3 Jul '12 - 7:31 
A nicely written article with some interesting concepts.
Just because the code works, it doesn't mean that it is good code.

GeneralMy vote of 5memberashved3 Jul '12 - 4:13 
Really nice

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 2 Jul 2012
Article Copyright 2012 by Henning Dieterichs
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid