Description
NOTE: This is work in progress and early thoughts, but input is most welcome.
Motivation
UnitsNet
nuget is nearing the 1MB mark and this is probably a lot bigger than most consumers would expect, since most users only work with a small set of quantities and units.
Proposal 1 - Core nuget + quantity nugets + meta nugets
This tries to achieve a kind of extension model, where new quantities are added via nugets - potentially 3rd party quantities.
- Move all shared code into
UnitsNet.Core
(UnitSystem
,UnitConverter
,QuantityParser
,UnitFormatter
,QuantityValue
etc..) - Repurpose our current
UnitsNet
nuget to a meta package that brings in the most common units (think 80/20, this is what we want most people to use) - Add nugets for all quantities
UnitsNet.Quantities.Length
,UnitsNet.Quantities.Mass
etc. (around 80 of these now), which has theLength
struct type (Length.g.cs
andLength.extra.cs
files) and any related extension methods (NumberToLengthExtensions
), with a dependency onUnitsNet.Core
- Add metapackage nugets to bring in a set of quantities for an engineering/technical domain, such as
UnitsNet.Meta.ElectricalEngineering
,UnitsNet.Meta.AudioEngineering
,UnitsNet.Meta.PetroleumEngineering
,UnitsNet.Meta.ChemicalEngineering
Concerns:
- Discoverability is hurt by moving code out into separate packages, so you need to browse packages to see what is possible unless you add a metapackage that brings in all or the most common quantities
- A lot of nugets (80 for quantities plus a few meta packages to bring in groups of quantities)
- Extension model with static types is tricky. How can we provide operator overloads such as
Force = Pressure * Area
, when the user may not have added all three nuget packages? Should there be a nuget package for the Force+Pressure+Area combination? Can we move operator overloads as well as interfaces and enums for all units to the core nuget, while keeping the quantity implementations and the bulk of the quantity code in separate nugets? Need to think of something clever here.
Proposal 2 - Use extension methods and builder pattern to reduce member count per quantity
Instead of this:
Length l = Length.FromMeters(1);
Length l = Length.FromKilometers(1);
Length l = Length.FromMillimeters(1);
We could do something like this:
Length l = Length.From.Meters(1);
Length l = Length.From.Kilo.Meters(1);
Length l = Length.From.Milli.Meters(1);
The idea is to move all the prefix code (milli, kilo, mega etc) to a base type #371 for reuse across quantities. We can achieve this by the builder pattern described above using inheritance or extension methods.
A similar approach could be made with conversion properties:
double mm = myLength.Milli.Meters; // Milli effectively multiplies the Meters result by 1e3
Concerns:
- The syntax is awkward and not very readable
- Code size reduction is only significant for quantities with a lot of prefixes
- Prefixes is not always as simple as multiplying by
1e3
etc. We have 3 logarithmic quantitiesAmplitudeRatio
,PowerRatio
andLevel
where the arithmetic is entirely different.
Proposal 3 - Move number extension methods to separate nuget
I just realized we have a ton of number extension methods, and this part of the library is something I think many simply don't use and could make sense to make opt-in.
For every unit, we have an extension method like Length l = 1.cm();
and Mass m = 85.kg();
. To support the four number types (int
, long
, double
, decimal
) we currently care about, we have 4 overloads of this method. And to support nullable like int? nullable = 10; Length? l = nullable.cm();
, we double that count. This means, for our 600+ units today there is now 8 x 600 extension methods. I would be very interested to see the diff in size when removing this, but I think it can be pretty significant since it seems it is mainly our high method/property count that causes the big size and not so much the amount of code inside each of them.
Update 2018-01-20:
I just tried a Release build locally before and after removing all the number extensions code. The difference is a STAGGERING 939 vs 475 kB ! I think we have found our low hanging fruit to reduce code size.
Besides adding this as a separate nuget, we can also consider using the QuantityValue
type to implicitly cast from multiple number types per extension method, to avoid the method overload explosion.