-
Notifications
You must be signed in to change notification settings - Fork 3
Overview
UnitsOfMeasurement project provides you with the source components (C# source files and T4 text templates) for creating and managing unit of measurement types that you may need in your C# application.
Basically what is left for you to do - after the project is created from the template - is to specify required units in a text file (_definitions.txt) and generate corresponding unit types according to T4 text templates (_unit.t4, _scale.t4, _generator.tt). Target unit types may be created either in a single .cs file (_generator.cs) or in multiple .cs files (one for each unit: Meter.cs, Second.cs, ...), depending on boolean variable (__one_cs_file) specified in the template. The process is shown on the below diagram:

Sample definitions
Assume you have prepared the following definitions:
///////////////////////////////////////////////////////
//
// Length
//
unit Meter "m" = <Length>;
unit Kilometer "km" = Meter / 1000;
unit Inch "in" = 100 * Meter / 2.54;
unit /* international */ Foot "ft" = Inch / 12;
unit /* international */ Yard "yd" = Foot / 3;
unit /* international */ Mile "mil" = Yard / 1760;
///////////////////////////////////////////////////////
//
// Time
//
unit Second "s" = <Time>;
unit Minute "min" = Second / 60;
unit Hour "h" = Minute / 60;
///////////////////////////////////////////////////////
//
// Velocity
//
unit Meter_Sec "m/s" = Meter / Second;
unit Kilometer_Hour "km/h" = Kilometer / Hour | Meter_Sec * (1/1000) / ((1/60)/60);
///////////////////////////////////////////////////////
//
// Currency
// Note: the rates (conversion factors) below are
// to be updated on application startup.
//
unit<decimal> EUR "EUR" = <Money>; // Euro
unit<decimal> USD "USD" = 1.3433 * EUR; // US Dollar
unit<decimal> GBP "GBP" = 0.79055 * EUR; // British Pound
unit<decimal> PLN "PLN" = 4.1437 * EUR; // Polish Zloty
The text specifies required units (Meter, Meter_Sec etc.), unit symbols ("m", "m/s"), underlying value type (double as a default, decimal, float) as well as conversion (e.g. Hour = Minute / 60) and arithmetic (e.g. Meter_Sec = Meter / Second) relationships between units.
Next you run template transformation ("Run Custom Tool" on _generator.tt). It takes the above definitions as input and generates the following units (as C# partial structs):
- length units: Meter, Kilometer, Inch, Foot, Yard, Mile
- time units: Second, Minute, Hour
- velocity units: Meter_Sec, Kilometer_Hour.
- currency units: EUR, USD, GBP, PLN.
That's all. Now you can compile the project and make use of generated units in your application.
Usage
You can use generated units directly, referring to their names explicitly as in the below excerpt from ProjectileRange demo application (with a little more elaborate set of units):
void CalculateProjectileRange(Degree degrees, out Second tmax, out Meter xmax, out Meter ymax)
{
Meter_Sec2 g = (Meter_Sec2)9.80665; // the gravitational acceleration
Meter_Sec v = (Meter_Sec)715.0; // the velocity at which the projectile is launched (AK-47)
Meter h = (Meter)0.0; // the initial height of the projectile
Radian angle = (Radian)degrees; // the angle at which the projectile is launched
// the time it takes for the projectile to finish its trajectory:
tmax = (v * Sin(angle) + Sqrt(v * Sin(angle) * v * Sin(angle) + 2.0 * g * h)) / g;
// the max vertical distance traveled by the projectile
ymax = h;
for (Second t = (Second)1.0; t < tmax; t++)
{
Meter y = h + v * Sin(angle) * t - g * t * t / 2.0;
if (y > ymax) ymax = y;
}
// the total horizontal distance traveled by the projectile
xmax = v * Cos(angle) * tmax;
}You can also refer to units via their proxies (Unit<T> and Scale<T> proxies gathered in a static Catalog class) and use them as variables or collections when you cannot refer to any explicit unit(s):
// unit of symbol "in" (Inch proxy):
Unit<double> inch = Catalog.Unit<double>("in");
// Units of Meter family:
IEnumerable<Unit<double>> meterFamily = Catalog.Units<double>(Meter.Family);
// Get length from an input string:
string input = "10 m";
var parser = new QuantityParser<double>(meterFamily/*units allowed in the input*/);
IQuantity<double> length;
if (!parser.TryParse(input, out length))
{
Console.WriteLine("Invalid number or unit other than any the following: \"{0}\".",
string.Join("\", \"", parser.Units.SelectMany(u => u.Symbol))
);
}
else
{
// Convert received length to units from the Meter family:
foreach (var unit in parser.Units)
{
IQuantity<double> output = unit.From(length);
Console.WriteLine("{0,-40} {1} -> {2}", unit, input, output);
}
// Finally give the result in inches:
Inch inches1 = Inch.From(length); // conversion via Inch unit (explicitly)
IQuantity<double> inches2 = inch.From(length); // conversion via Inch proxy
} See Demo Applications and Demo Unit Tests for more examples.
Customizing
You can modify _unit.t4 and/or _scale.t4 templates to fit structure and functionality of generated units to your needs. You can further extend the functionality by writing your own extensions to the generated partial structs.
See also
- Demo Definitions for more extensive unit definitions and the corresponding demo units.
- Definition Syntax for details on definition syntax,
- Definitions Explained on its semantics,
- Project components to understand Units of Measurement project structure.