Skip to content

Providing a default implementation for IQuantity<TUnitType>.QuantityInfo #1614

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

lipchev
Copy link
Collaborator

@lipchev lipchev commented Aug 19, 2025

  • providing a default implementation for IQuantity<TUnitType>.QuantityInfo
  • removing the explicit implementations for IQuantity and IQuantity<TUnit> (net8+)

…yInfo

- removing the explicit implementations for IQuantity and IQuantity<TUnit> (net8+)
@lipchev
Copy link
Collaborator Author

lipchev commented Aug 19, 2025

@angularsen Default interface implementations and structs don't usually play very well together, but I think we should be ok here. Here are some notes:

  • implementing a static function (on the interface) is always fine (no boxing)
  • implementing an instance member causes the struct to be boxed as it's interface definition (I think the term was interface dispatch or something of the sort)
  • if we are already boxed (i.e. using IQuantity or IQuantity<TUnit> directly) there are no additional allocations
  • the penalty for doing the same with classes is generally negligible (pretty much the same as using an IReadOnlyList vs List)

Here's a typical bad-usage example:

public Enum GetUnitEnum<TQuantity>(TQuantity quantity) where TQuantity : IQuantity
{
   return quantity.Unit;
} 

Its probably obvious to everyone that there is going to be boxing when converting the unit to Enum but what's not obvious is that since the concrete TQuantity doesn't have an explicit implementation of the IQuantity.Unit, it's the default interface implementation that is invoked, however that implementation is on the IQuantity type, therefore we are forced to convert our TQuantity to IQuantity which adds another allocation.

This is obviously not how you would typically access the untyped unit- normally you would only do so if you are forced to use an IQuantity (instead of TQuantity).

On the other hand, if you are one that cares about the extra allocations, but for some reason can't have access to the TUnit, then you'd be better off working with the UnitKey.

Same goes for the QuantityInfo<TUnit> - if you have some constraint where TQuantity: IQuantity<TUnit> then accessing the non-explicit property would cost you, where as the equivalent constraint where TQuantity: IQuantity<TQuantity, TUnit> would not.

It looks tricky, I know- but I'm pretty sure I've checked all our usages, and there aren't any, that I could find, that still require the use of these properties. The sole usages (here or in the 🐲 PR) are the interface extensions in the QuantityExtensions class, but they are fine as they are already using the concrete IQuantity / IQuantity<TUnit> definition, not a generic one.

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.

1 participant