Description
NOTE: This is work in progress and early thoughts, but input is most welcome.
An interface or abstract type implemented by all quantity types, such as Length
and Mass
.
Motivation
- Represent generic quantities in code, where we don't need to know if it is
Length
orMass
but want to communicate it is a quantity - we need to useobject
today - Get textual representation of quantity with
someQuantity.ToString()
=>"5 kg"
, this works today except we need to treat it asobject
and we don't have access to overloads for things like culture and significant digits after radix - Enumerate units for this quantity as this currently requires reflection, a usecase is to dynamically show GUI for converting a passed in quantity (1 meter) to any of the other length units
- Present generic quantities visually in a 2D graph by showing unit selector for the quantities to change unit and getting the values for each quantity in the selected unit, without hardcoding support for specific quantities
Discussion points
- Should we change quantities from struct to class? As discussed in the double/decimal issue, with class we might benefit from supporting both double and decimal internal representations without doubling the code. With an interface, it will quickly be boxed anyway unless using generic methods instead
- Should the new members be added to quantity base type or be added to the
UnitSystem
type. One thing to consider is what culture to use for things like abbreviations and number formatting, whereUnitSystem
instances are created explicitly with a given culture (default uses CurrentUICulture in Windows).
Proposal
- Add interface
IQuantity
since structs can't inherit base types, but users should be wary that casting to interface means boxing the value with those performance implications - ToString() should reflect the original value and unit instead of using the base unit, which may be very different than the units the user is working with (addressed in Preserve value and unit #389)
- Add getter property for
QuantityType
since quantities already expose this value - Add getter properties for original value and unit to present or filter on these values separately
- Add getter property for unit abbreviations as current you must figure out to use
UnitSystem.Default.GetAllAbbreviations()
- Add getter properties for singular and plural names of quantity for both
"ForcePerLength"
and"Force per length"
forms, to make it easier to textually describe the quantity type. Currently you use reflection to obtainQuantityType
enum value and callToString()
on it to get"ForcePerLength"
. - Add methods to obtain singular and plural names of a given unit to get `"Newton
- Add getter property for the quantity description in JSON so this can be displayed next to the quantity selector GUI
- Add getter property for enumerating all units for this
QuantityType
as this currently requires reflection (see sample app https://github.com/angularsen/UnitsNet/pull/380/files#diff-6f7804b386e2c6cd78fb5a331e9c9a58R18)
-UnitConverter.ConvertBy methods should not use reflection as this is unnecessary brittle and can be solved by generating code to check if from/to unit types are LengthUnit and passing that toLength.From()
andLength.As()
methods - Reduce code size by moving shared code into base type (or helper class), we currently have ~80 quantities. We already do use helper classes for heavy things like
Parse()
andToString()
, so not sure how much more to gain here.
Case studies
Converter app with hard coded quantities and using strings for conversions
https://github.com/angularsen/UnitsNet#example-creating-a-unit-converter-app
- Since quantities are hard coded in the app, we can obtain the list of units like this:
if (selectedQuantity == "Length") { SetConversionUnits(Length.Units.Select(x => x.ToString())) }
else if (selectedQuantity == "Mass") {} /* and so on */
- Alternative is to use reflection to obtain list of units, but this is brittle
- Use strings for quantities and units in the GUI lists and use
UnitConverter
to convert between two units of a quantity like this:
double centimeters = UnitConverter.ConvertByName(5, "Length", "Meter", "Centimeter"); // 500
Converter app with generic quantities #353
The main difference to the example above is that we want to obtain the list of units for any selected quantity, without knowing the quantity type.
We either have the quantity string ("Length"
or "Mass"
), or its equivalent QuantityType.Length
and QuantityType.Mass
enum values, but there is currently no intuitive way of getting abbreviations from this.
Challenge: Present the plural name of any unit
For listing units in the GUI. We can get "meter" and "newton per meter"
from LengthUnit.Meter
and ForcePerLengthUnit.NewtonPerMeter
by calling ToString()
and splitting the pascal case into lower case words, but it would only be singular and difficult to reliably present in plural form. The plural form is defined in JSON, so we can easily generate that.
Challenge: Present abbreviation of any unit
This currently requires you to know the quantity or unit type, in order to look it up statically via string x = Length.GetAbbreviation(LengthUnit unit)
or dynamically via string x = UnitSystem.Default.GetDefaultAbbreviation(Type unitType, int unitValue)
This dotnetfiddle shows how to get all length abbreviations, given that we added LengthUnit
values to the list and can resolve abbreviations based on that type:
object[] lengthUnits = Length.Units.Skip(1).Take(10).Cast<object>().ToArray(); // Assume we add these to the GUI list and read those back as `object`s at some point
Console.WriteLine("Length unit abbreviations: " + string.Join(", ", lengthUnits.Select(unit => UnitSystem.Default.GetDefaultAbbreviation(unit.GetType(), (int)unit))));
// Output:
// Length unit abbreviations: cm, dm, pica, pt, fathom, ft, in, km, m, µin
https://dotnetfiddle.net/9f5TiF
A base type could have the method `GetAbbreviation(object unit)
Discussion on converting from struct
to class
to support decimal
#285
This is related, because we are here discussing whether to convert from struct
to class
in order to get the benefit of supporting both double
and decimal
in the same library without doubling the code size.
Fixes #354
Related issues
- PR: WPF MVVM Sample App WPF MVVM Sample App #353
- Why don't we have a base class for quantities? Why don't we have a base class for quantities? #308
- Absolute Values of Quantities discuss Absolute Values of Quantities #325 (decided to add this through base type)
- Getting abbreviation list of a quantity Getting abbreviation list of a quantity #354 (without resorting to reflection)