Skip to content

Extending the functionality of BaseUnits & UnitSystem #651

Open
@lipchev

Description

@lipchev

Following the many lengthy exchanges concerning the BaseUnits (default) and the UnitSystem in #646 here is a summary of limitations that I think should be addressed at some point.
First let me start by stating the general use cases (the way that I see them- feel free to add/correct me here):

  1. Domain separation
  • Stock Screen works with quantities in kg | liters (UnitSystem)
  • Recipe Screen works with quantities in mg | ml
  • Persistence- use a well defined UnitSystem for storing values- forget about Quantity.To/FromXX- As(..), new (..) instead
  • Reporting- I want my report to use this and that measure unit (ideally UnitSystem would be serializable)
  1. Presentation customization
  • default unit, default abbreviation- ideally it is also possible to associate those with a range- [1e, 1e3] -> [Gram], [1e3,1e6] -> [Kilogram]
  • list of common units- what do you put in the combo-box

Currently, the default (abbreviation for) BaseType for a UnitSystem is determined by taking the first matching unit, that satisfies the equality comparison with the UnitSystem.BaseUnits. The following two operations depend on the success of the previous lookup:

  • Constructing quantities using the (double, UnitSystem) constructor
  • Converting using the As(UnitSystem) operator

This approach has the following limitations:

  1. Possible run-time exceptions due to some combination of quantity types in a UnitSystem not having a matching base type definition- it makes sense to throw an exception for custom UnitSystems- but at least constructing/converting using SI should be safe
  2. BaseUnits cannot be specified for prefixed units: [Kilo]Gram is not matched for SI as the BaseUnits apply to the Gram only
  3. Ambiguous BaseUnits definitions: using the "dimensions ignored" conversion may sometimes lead to ambiguous conversions such as with the Liter -> CubicMeter derivatives. Consider the following mapping:
  • 1 l = 1 dm3 -> Decimeter
  • 1 dl = 1 ? (0.1 dm3 | 100 cm3)
  • 1 ml = 1 cm3 -> Centimeter
  1. Overlapping BaseUnits- having the find-first approach makes it easy to both override/have your default unit definition overridden by mistake
  2. Mapping BaseUnits definitions for UnitSystems in run-time: there is currently no way to define missing or override existing BaseUnits for a UnitSystem

I think the last point is the key to solving most of the limitations on the list. My proposition would basically consist of storing a dictionary representing the default unit for a given quantity type inside the UnitSystem:

private readonly Dictionary<QuantityType, UnitInfo> _defaultUnits; // could be Lazy<..>

This dictionary could be constructed eagerly or lazily (as is currently the case)- using the existing convention (to start with):

        public UnitSystem(BaseUnits baseUnits)
        {
            ...
            // default implementation (does not fix the prefixed entities matching issue)
            _defaultUnits = Quantity.Infos.ToDictionary(x => x.QuantityType,
                                                        i => i.GetUnitInfosFor(baseUnits).FirstOrDefault());     
        }

The Constructors/As(UnitSystem) methods would no longer have to go about finding the "first matching base type" every time- instead they would get it directly from the UnitSystem:

    public double As(UnitSystem unitSystem)
    {
        .. 
        var defaultUnitInfo = unitSystem.GetDefaultUnitInfo(QuantityType);  // casting omitted for simplicity
        if(defaultUnitInfo == null)
            throw new ArgumentException("No default unit was defined for the given UnitSystem.", nameof(unitSystem));
        return As(firstUnitInfo.Value);
    }

There would of course be helper methods for the user to register his preferred defaults for a given quantity type like:

        public void RegisterDefaultUnit(QuantityType quantityType, UnitInfo unitInfo)
        {
            _defaultUnits[quantityType] = unitInfo;
        }

Some tweaking of the JSON schema/parsing would probably be required for solving the issue with the base types for the prefixed quantities.
// TODO discuss the usefulness of the points presented in the Presentation Customization section and/or see if any (/other) customizations might be of interest for a given UnitSystem

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementpinnedIssues that should not be auto-closed due to inactivity.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions