Skip to content

Extending the functionality of BaseUnits & UnitSystem #651

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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