Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use UnitConverter to do conversion functions and allow basic extensibility #1023

Merged
merged 23 commits into from
Feb 8, 2022

Conversation

tmilnthorp
Copy link
Collaborator

Here's a very-rough initial idea for supporting #865.

Rather than generating functions that are not extensible, we can reuse our existing UnitConverter class and be able to use it for conversions. Users could add/remove custom functions at-will.

Need to formalize this more as we should really use QuantityInfo for the parameters.


var inBaseUnits = ToUnit(BaseUnit);

if(!unitConverter.TryGetConversionFunction<Angle>(inBaseUnits.Unit, unit, out var conversionFunction))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it.

As a very minor performance improvement, can we call the innermost method overload? I think it has to go through 2 additional TryGetConversionFunction methods to get to the most specific one.

Copy link
Owner

@angularsen angularsen Jan 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the minor detail of failing tests, I think this looks good?

So in order to extend this, we can simply call Acceleration.DefaultConversionFunctions.SetXXX() on startup?

Will there be a racing condition with the initialization of the global static UnitConverter.Default?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to formalize this more as we should really use QuantityInfo for the parameters.

What did you mean by this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it.

As a very minor performance improvement, can we call the innermost method overload? I think it has to go through 2 additional TryGetConversionFunction methods to get to the most specific one.

Done!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the minor detail of failing tests, I think this looks good?

So in order to extend this, we can simply call Acceleration.DefaultConversionFunctions.SetXXX() on startup?

Will there be a racing condition with the initialization of the global static UnitConverter.Default?

You certainly could. I do worry a bit though about that causing unintended consequences, especially since GetValueAs uses the DefaultConversionFunctions. This would affect:

  • operator +
  • operator -
  • operator <=
  • operator >=
  • operator <
  • operator >
  • CompareTo
  • Equals
  • As

That said, if you write a custom conversion function, arguably it should end up being used for * for example.

@tmilnthorp tmilnthorp changed the title Unitconversion Use UnitConverter to do conversion functions and allow basic extensibility Jan 31, 2022
@tmilnthorp tmilnthorp marked this pull request as ready for review February 3, 2022 15:48
@codecov
Copy link

codecov bot commented Feb 3, 2022

Codecov Report

Merging #1023 (c5be852) into master (8d2eeb6) will decrease coverage by 2.3%.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff            @@
##           master   #1023     +/-   ##
========================================
- Coverage    82.7%   80.4%   -2.2%     
========================================
  Files         309     309             
  Lines       47598   46126   -1472     
========================================
- Hits        39317   37081   -2236     
- Misses       8281    9045    +764     
Impacted Files Coverage Δ
...nitsNet/GeneratedCode/Quantities/Acceleration.g.cs 81.3% <ø> (-2.2%) ⬇️
...et/GeneratedCode/Quantities/AmountOfSubstance.g.cs 81.7% <ø> (-2.3%) ⬇️
...tsNet/GeneratedCode/Quantities/AmplitudeRatio.g.cs 76.6% <ø> (-1.6%) ⬇️
UnitsNet/GeneratedCode/Quantities/Angle.g.cs 81.5% <ø> (-2.3%) ⬇️
...tsNet/GeneratedCode/Quantities/ApparentEnergy.g.cs 76.0% <ø> (-1.5%) ⬇️
...itsNet/GeneratedCode/Quantities/ApparentPower.g.cs 76.6% <ø> (-1.6%) ⬇️
UnitsNet/GeneratedCode/Quantities/Area.g.cs 83.2% <ø> (-2.2%) ⬇️
UnitsNet/GeneratedCode/Quantities/AreaDensity.g.cs 72.2% <ø> (-2.9%) ⬇️
.../GeneratedCode/Quantities/AreaMomentOfInertia.g.cs 78.2% <ø> (-1.8%) ⬇️
UnitsNet/GeneratedCode/Quantities/BitRate.g.cs 84.3% <ø> (-2.4%) ⬇️
... and 216 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8d2eeb6...c5be852. Read the comment docs.

@tmilnthorp
Copy link
Collaborator Author

Hmmm let me add some tests. Kinda shocked we don't have any tests for to base conversion in ToUnit!

[Fact]
public void ToUnit()
[Theory]
[MemberData(nameof(UnitTypes))]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I parameterized a few tests to make them nicer

@tmilnthorp
Copy link
Collaborator Author

I think this is ready. I wonder why the new CodeCov report isn't coming through?

Copy link
Owner

@angularsen angularsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly nitpicks, which means the PR is good.

@angularsen
Copy link
Owner

angularsen commented Feb 4, 2022

I have no idea how the codecov is triggered. It almost seems random.

@angularsen
Copy link
Owner

It seems codecov failed to process the last uploaded coverage reports. Can't see any error codes or any details on what though.

https://codecov.io/github/angularsen/UnitsNet/commit/b20c987ade7301039ed68e63b16137101e966ac3

image

@angularsen angularsen merged commit e5aba5b into angularsen:master Feb 8, 2022
@angularsen
Copy link
Owner

@lipchev
Copy link
Collaborator

lipchev commented Feb 14, 2022

Hey guys, I've just updated the release note- indicating that this is a breaking change for implementors of the IQuantity interface.

@lipchev
Copy link
Collaborator

lipchev commented Feb 14, 2022

And by the way- I haven't looked into this closely but - couldn't this be made into an extension method? We could still have the GetValueAs function call it by default, while people extending the main interface can choose to completely ignore it for their 'custom quantities'.
I wasn't really affected (only had 2-3 spots needed the extra implementation) - but one would have to copy-paste the full body for each custom type which is not ideal.
@angularsen @tmilnthorp

@tmilnthorp
Copy link
Collaborator Author

Hey guys, I've just updated the release note- indicating that this is a breaking change for implementors of the IQuantity interface.

Oops. Got a bit carried away there 😄

In retrospect, I think I should just remove the UnitConverter overloads. How the conversion / extensibility is implemented is just an implementation detail. Interfaces should be as small as possible to provide the required functionality and no more.

Would you guys prefer removing them now (also a breaking change, although arguably for fewer consumers between minor versions), or making them obsolete for v5 removal?

@lipchev
Copy link
Collaborator

lipchev commented Feb 15, 2022

I think the impact of doing it now would be smaller (probably just me and you).

@tmilnthorp
Copy link
Collaborator Author

I think the impact of doing it now would be smaller (probably just me and you).

I would tend to agree. I made #1047 to remove this overload. I will think about your other proposal in another PR, just wanted to get that break out (if @angularsen agrees this is the best action).

@angularsen
Copy link
Owner

I agree to remove for now to avoid breaking change in v4.

I was thinking if we could maybe make use of default interface methods, but this seems limited to netstandard2.1 / netcore3 and I believe we should still support nestandard2.0.

@tmilnthorp
Copy link
Collaborator Author

I agree to remove for now to avoid breaking change in v4.

I was thinking if we could maybe make use of default interface methods, but this seems limited to netstandard2.1 / netcore3 and I believe we should still support nestandard2.0.

I had thought of that also, but I wouldn't be able to provide a default implementation anyways, since DefaultConverter lives on the individual quantity implementations, not interface definition.

angularsen pushed a commit that referenced this pull request Aug 4, 2022
Co-authored-by: Andreas Gullberg Larsen <[email protected]>

Fixes #1069 

Per @lipchev's comments in #1023, I decided to add the local conversion method back (as private ```TryToUnit```). This will allow better performance for auto-generated conversions before falling back to the extensibility points.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants