-
Notifications
You must be signed in to change notification settings - Fork 382
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
✨ Add back IEquatable with strict equality #1100
Conversation
@dschuermans I implemented strict equality, if you want to look over it. Based on decision in #1017 (comment). |
0591e4a
to
72c4124
Compare
}} | ||
|
||
/// <inheritdoc /> | ||
/// <summary>Returns true if either <see cref=""Value"" /> or <see cref=""Unit"" /> are not exactly equal for both quantities.</summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it just me, or is this confusing wording?
Shouldn't it be:
Returns true if Value and Unit are exactly equal for both quantities?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is for !=
not equals, but yes I agree it was clumsy wording.
I propose these:
==
Returns true if both <see cref=""Value"" /> and <see cref=""Unit"" /> are exactly equal for both quantities.
!=
Returns true if either <see cref=""Value"" /> or <see cref=""Unit"" /> are not exactly equal for both quantities.
Nice, But if I understand correctly, the case I made in the mentioned issue still won't work right? (1m equals 100cm and thus should yield the same hashcode) |
No, Or did I misunderstand? |
Erm, for me personally that sounds wrong if UnitsNet will now report that Imagine that you're filling out forms to get a building permit and you provide your plan which has values in cm whereas the city's system works in m and your request for a permit gets denied because the clerk tells you "well, we only allow this to be a max of But in any case, if i recall my initial question was that if it was possible to globally configure the default comparison's number of decimals, so that I wouldn't have to use the overload everywhere in code. We "fixed" this by using an extension method which allows us to configure the number of decimals in a single spot So for me and my project, things could've stayed the way they were, as I created work-arounds for it. |
Codecov ReportBase: 86% // Head: 87% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #1100 +/- ##
========================================
Coverage 86% 87%
========================================
Files 321 321
Lines 48165 49803 +1638
========================================
+ Hits 41699 43337 +1638
Misses 6466 6466
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
I hear your pain @dschuermans 😅 This discussion has literally gone for years, on and off. I think the reasoning is best outlined in this comment #1017 (comment) I simply don't see a trivial solution here that makes everyone happy. As summarized in the comment:
Static config of equalityI guess we could allow for a static configuration of a default Length.FromMeters(1).Equals(Length.FromCentimeters(100), 1e-5, ComparisonType.Absolute) // 1m +/- 0.00001
Length.FromMeters(1).Equals(Length.FromCentimeters(100), 1e-3, ComparisonType.Relative) // 1m +/- 0.1% At least then you are making an informed choice on the rounding. If you see a better solution or have convincing arguments for option 2 or 3, then I'm all ears. |
Well, one solution I see here (not sure if it's technically feasible) is to have 2 separate packages of UnitsNet:
On the other hand, Length unit1 = Length.FromMeters(1, Equality.Sane);
Length unit2 = Length.FromCentimeters(100, Equality.Sane);
unit1.Equals(Unit2); // returns true
Length unit3 = Length.FromMeters(1, Equality.Strict);
Length unit4 = Length.FromCentimeters(100, Equality.Strict);
unit3.Equals(Unit4); // Returns false; Store the equality setting on the unit itself, it could then be used inside the public bool Equals({_quantity.Name} other)
{{
if(equalitySetting != other.equalitySetting){
throw new NotSupportedException("You cannot compare strict units with sane units");
}
switch(equalitySetting)
{
case Equality.Strict:
return new {{ Value, Unit }}.Equals(new {{ other.Value, other.Unit }});
case Equality.Sane:
{
double thisValue = this.Value;
double otherValueInThisUnits = other.As(this.Unit);
return Comparison.Equals(thisValue, otherValueInThisUnits, 1e-05, ComparisonType.Absolute);
}
}
}}
public override int GetHashCode()
{
switch(equalitySetting)
{
case Equality.Strict:
{
return new { Info.Name, Value, Unit }.GetHashCode();
}
case Equality.Sane:
{
var valueOfUnitAsBaseUnitRounded = Math.Round(this.As(this.QuantityInfo.BaseUnitInfo.Value), 5, MidpointRounding.AwayFromZero);
return (new
{
this.QuantityInfo.Name,
valueOfUnitAsBaseUnitRounded,
this.QuantityInfo.BaseUnitInfo.Value
}).GetHashCode();
}
}
} |
Thank you for the comments and suggestions.
I don't like this option. It will confuse people what package to choose, and it complicates transient dependencies on UnitsNet, where libraries A and B take in two different UnitsNet nugets. It also feels overkill to solve a rather small problem.
This is the challenge. How do we produce the same hash code for units that are approximately equal, for very small or very large values and when comparing very small units to very large units?
Not sure I favor this approach. Consumers may run into runtime exceptions if quantities happen to be constructed with different equality settings, such as getting quantities computed by a library out of your control. I am not a fan of static config either, but if we had to choose between ctor parameter and static config, I would in this case prefer the latter. I'm still not convinced we should offer the static config though, since we would still have to solve the challenge about the hashcode mentioned above. |
Some other options not mentioned:
However, none of these really address the problem that many will expect 100 cm to equal 1m out of the box. |
The current discussion seems to have met a dead end, I propose to move ahead with the strict equality change and rather discuss any ideas for global configuration in a separate issue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've suggested fixes for the comments on the Equals overload.
As for the question of the precision on the custom tests- I have no suggestions :)
Sorry for the late reply, I have been missing out on email notifications for some time and also been very busy. Will review soon. |
6d328da
to
ca52de5
Compare
ca52de5
to
8fe07c8
Compare
Fixes #1017
IEquatable
.Value
andUnit
.GetHashCode()
left unchanged, which includes quantity name in addition to value and unit, so thatLengthUnit.Meter = 1
andMassUnit.Gram = 1
are still considered different in collections ofIQuantity
.Reverts commit f3c7e25.
"🔥 Remove IEquatable and equality operators/methods"