Skip to content

QuantityValue implemented as a fractional number 🐲 #1544

Open
lipchev wants to merge 86 commits into
angularsen:masterfrom
lipchev:fractional-quantity-value
Open

QuantityValue implemented as a fractional number 🐲 #1544
lipchev wants to merge 86 commits into
angularsen:masterfrom
lipchev:fractional-quantity-value

Conversation

@lipchev

@lipchev lipchev commented Apr 13, 2025

Copy link
Copy Markdown
Collaborator
  • QuantityValue implemented as a fractional number
  • IQuantity interfaces optimized (some methods refactored as extensions)
  • UnitInfo: introduced two new properties: ConversionFromBase and ConversionToBase which are used instead of the switch(Unit) conversion
  • UnitsNetSetup: introduced helper methods for adding external quantities, or re-configuring one or more of the existing ones
  • UntAbbreviationsCache: introduced additional factory methods (using a configuration delegate)
  • UnitParser: introduced additional factory methods (using a configuration delegate)
  • UnitConverter: re-implemented (multiple versions)
  • Inverse relationship mapping implemented as a type of implicit conversion
  • updated the JsonNet converters
  • introducing the SystemTextJson project
  • added a new UnitsNetConfiguration project to the Samples, showcasing the new configuration options
  • many more tests and benchmarks (perhaps too many)

- IQuantity interfaces optimized (some methods refactored as extensions)
- QuantityInfo/UnitInfo hierachy re-implemented (same properties, different constructors)
- QuantityInfoLookup is now public
- UntAbbreviationsCache, UnitParser, QuantityParser optimized
- UnitConverter: re-implemented (multiple versions)
- removed the IConvertible interface
- updated the JsonNet converters
- introducing the SystemTextJson project
- added a new UnitsNetConfiguration to the Samples project showcasing the new configuration options
- many more tests and benchmarks (perhaps too many)
@lipchev

lipchev commented Apr 13, 2025

Copy link
Copy Markdown
Collaborator Author

@angularsen Clearly, I don't expect this to get merged in the Gitty up! fashion, but at least we have the whole picture, with sources that I can reference.

If you want, send me an e-mail, we could do a quick walk-through / discussion.

lipchev added 2 commits April 18, 2025 00:27
…lection constructors with IEnumerable

- `UnitAbbreviationsCacheInitializationBenchmarks`: replaced some obsolete usages
@angularsen

Copy link
Copy Markdown
Owner

100k lines removed, 100k lines added 🙈

image

@lipchev

lipchev commented Apr 18, 2025

Copy link
Copy Markdown
Collaborator Author

100k lines removed, 100k lines added 🙈

I tried to create this PR twice before (many months ago), while the changes to the unit definitions were still not merged- and the web interface was giving me an error when trying to browse the files changed.. Something like "Too many files to display" 😄

@angularsen

Copy link
Copy Markdown
Owner

Ok, I'm not going to get through a review of this many files anytime soon.
Maybe we should have a screen sharing session and go through it together. I may have some time this weekend, what about you? What timezone are you in?

On the surface though, it seems like this could be split up into chunks. I know it's tedious and extra work, but it will be way faster to review. Do you see any chunks of changes to easily split off into separate PRs?

@lipchev

lipchev commented Apr 18, 2025

Copy link
Copy Markdown
Collaborator Author

Ok, I'm not going to get through a review of this many files anytime soon. Maybe we should have a screen sharing session and go through it together. I may have some time this weekend, what about you? What timezone are you in?

Sofia (GMT+3), but time zones are not relevant to my sleep schedule - so basically any time you want.

On the surface though, it seems like this could be split up into chunks. I know it's tedious and extra work, but it will be way faster to review. Do you see any chunks of changes to easily split off into separate PRs?

Yes, I do have some ideas:

  1. UnitAbbreviationsCache and the UnitParser should be more or less free of changes once UnitAbbreviationsCache.CreateEmpty should use the default QuantityInfoLookup #1548 is merged
  2. I plan to remove the IConvertible interface tonight (lots of red points there)
  3. The QuantityParser has just a few minor changes which I was going to try to push as well (other than that it's mostly just double changing to QuantityValue)
  4. QuantityFormatter - there was an issue that I created earlier that should (mostly) solve the differences
  5. Refactoring the QuantityInfo can theoretically be done without the ConversionExpressions (which would open the way for the changes to the IQuantity interface and some of the extension methods).
  6. UnitParser: introduce two new method: GetUnitFromAbbreviation and TryGetUnitFromAbbreviation returning a UnitInfo (which could be used for constructing an instance of the quantity)
  7. Introduce the changes to the IQuantity interface and some of the extension methods
  8. Move the exceptions in their own folder and replace the usages of the NotImplementedException with the appropriate UnitNotFoundException
  9. Update the UnitTestBaseClassGenerator - I've refactored the Parse/TryParse tests (completing the test coverage) - having a look at the diff on the MassTestsBase.g.cs, it looks like these account for about half of all diffs in the PR 😄
  10. Make the QuantityInfoLookup public: apart from the extra constructors, there doesn't appear to be any other differences- and I don't see any reason to keep it internal
  11. Introduce the CodeGen/Helpers/ExpressionEvaluationHelpers.cs + CodeGen/Helpers/ExpressionAnalyzer and replace the unit conversion expressions such as (_value / 1e3) * 1e-2d) with the simplified expression (unless we actually plan to use the the rest of this PR- this would probably be an overkill).
  12. The JsonQuantityConverter stuff from SystemText could theoretically come with it's double versions first (but we do need to have a discussion about it)

Hopefully by the time we get to 5) you'd be up to speed (and fed up with PRs) and we can turn back to reviewing / working on the rest of it as a whole 😄

@angularsen

Copy link
Copy Markdown
Owner

Ok, sounds good. Just send PRs my way and I'll try to get to them. I have a little bit of extra time this weekend.

@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions

Copy link
Copy Markdown
Contributor

This PR was automatically closed due to inactivity.

@github-actions github-actions Bot closed this Jun 28, 2025
@github-actions

Copy link
Copy Markdown
Contributor

This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

This PR was automatically closed due to inactivity.

@github-actions github-actions Bot closed this Jun 6, 2026
@lipchev lipchev reopened this Jun 22, 2026
@lipchev

lipchev commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

@angularsen I've reopened this:

  1. I've sync the changes from upstream (see 7d1f055) - I kept the obsolete usages, as to avoid the performance overhead of the dictionary lookup. I'm not too sure about the extension methods, and the usages of the default configuration - we can make the IQuantity.QuantityInfo property internal, or we could force the converter to do the dictionary lookup. However we should note that if we were to completely remove the IQuantity.QuantityInfo property from the #NET build, then using the GetQuantityInfo extension would fail for HowMuch, unless it is registered in the default configuration.
  2. I've made the QuantityValue -> double operator implicit. I tested the package with my own projects and frankly there were significantly more line changes that I had to make in order to get everything to compile, compared to the actual migration of IQuantity.Value to QuantityValue 😆 I don't think this would be the case for most people, but there were a lot of Assert.Equals that failed for me too...
  3. Finally, I removed the EmitDefaultValue = false from the quantity unit and value

angularsen added a commit that referenced this pull request Jul 22, 2026
Extracted from #1544 because deterministic code generation is
independent of the QuantityValue feature and improves the existing
generator on its own. Moving it out reduces the size and conflict
surface of the original PR.

Changes:
- Deserialize relation definitions without relying on SortedSet's
current-culture comparer.
- Normalize relations with an ordinal, case-insensitive immutable sorted
set before parsing and rewriting the file.

Tests:
- No test cases changed; verified by running the full code generator and
confirming it produced no unrelated generated-file changes.
angularsen added a commit that referenced this pull request Jul 22, 2026
Extracted from #1544 because format-string parsing is unrelated to
QuantityValue and is a standalone correctness improvement for the
existing formatter. Moving it out keeps the feature PR focused and lets
this behavior ship independently.

Changes:
- Parse significant-digit, abbreviation-index, currency, and percent
suffixes with invariant culture.
- Apply the same parsing rules to the span-based and .NET
Framework-compatible code paths.

Tests:
- Add Format_CustomSpecifierSuffix_ParsesUsingInvariantCulture for
abbreviation and significant-digit formats.
angularsen added a commit that referenced this pull request Jul 22, 2026
Extracted from #1544 because representing a unit system for only the
dimensions an application uses is useful independently of QuantityValue.
Moving it out gives the API change focused review and reduces the
original PR's scope.

Changes:
- Allow UnitSystem to be constructed from any BaseUnits value defining
at least one dimension.
- Continue rejecting null and fully undefined base-unit sets.
- Clarify the constructor documentation and exception message.

Tests:
- Change the seven previously rejected one-dimension-missing cases to
assert successful construction.
- Add a single-dimension UnitSystem case.
- Add a dedicated assertion that BaseUnits.Undefined remains invalid.
angularsen added a commit that referenced this pull request Jul 23, 2026
Extracted from #1544 because composing an isolated quantity catalog is
useful for custom quantities without depending on QuantityValue. Moving
this API out gives the catalog design focused review and reduces the
feature PR's size and initialization surface.

Changes:
- Add QuantitiesSelector for appending external quantity definitions to
a base catalog.
- Add configured factory overloads for QuantityInfoLookup,
UnitAbbreviationsCache, and UnitParser.
- Allow parser factories to configure abbreviations before parser
construction.
- Add UnitsNetSetup.Create and a selection builder for isolated,
consistently wired setups.
- Keep UnitsNetSetup.Default behavior unchanged.

Tests:
- Add QuantitiesSelector coverage for base, chained additions, ordering,
and null input.
- Add cache factory coverage for default and custom base catalogs.
- Add parser factory coverage for external quantities and custom
abbreviations.
- Add isolated setup coverage for component wiring, catalog selection,
additions, and duplicate base selection.
angularsen added a commit that referenced this pull request Jul 23, 2026
Extracted from #1544 (#1544) because choosing the global quantity catalog is an independent setup capability, not a requirement of fractional QuantityValue. Keeping it separate makes the singleton initialization and compatibility implications explicit and allows this commit to be deferred without blocking the isolated factory APIs.

Changes:
- Lazily create UnitsNetSetup.Default from the existing setup builder.
- Add ConfigureDefaults for selecting the global catalog before first use.
- Synchronize configuration and creation so concurrent first access cannot observe a partially configured builder.
- Reject configuration after the singleton has been created.

Tests:
- Add a dedicated test assembly so global singleton state is isolated from the existing suite.
- Verify configured catalog selection, default cache/parser wiring, excluded units, and rejection of reconfiguration.
- Run the regression on net10.0 and net48 (1 test per target).
angularsen added a commit that referenced this pull request Jul 23, 2026
Extracted from #1544 because choosing the global quantity catalog is an
independent setup capability, not a requirement of fractional
QuantityValue. Keeping it separate makes the singleton initialization
and compatibility implications explicit and allows this commit to be
deferred without blocking the isolated factory APIs.

Changes:
- Lazily create UnitsNetSetup.Default from the existing setup builder.
- Add ConfigureDefaults for selecting the global catalog before first
use.
- Synchronize configuration and creation so concurrent first access
cannot observe a partially configured builder.
- Reject configuration after the singleton has been created.

Tests:
- Add a dedicated test assembly so global singleton state is isolated
from the existing suite.
- Verify configured catalog selection, default cache/parser wiring,
excluded units, and rejection of reconfiguration.
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97%. Comparing base (9cd9c6d) to head (f7053dc).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##           master   #1544      +/-   ##
=========================================
  Coverage      96%     97%              
=========================================
  Files         457     237     -220     
  Lines       30163   18371   -11792     
=========================================
- Hits        29098   17880   -11218     
+ Misses       1065     491     -574     
Flag Coverage Δ
net48 97% <ø> (+1%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@angularsen

Copy link
Copy Markdown
Owner

@lipchev I took the liberty of splitting out some more changes and syncing this PR branch again.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants