Describe the bug
.GetHashCode() returns different hash for 2 units for which .Equals() return true
To Reproduce
LengthinCm=Length.FromCentimeters(100);LengthinM=Length.FromMeters(1);Assert.AreEqual(inCm,inM);Assert.AreNotEqual(inCm.GetHashCode(),inM.GetHashCode());
Expected behavior
The GetHashCode() should return the same value because the .Equals() returns true
Additional context
From the remarks:
Two objects that are equal return hash codes that are equal. However, the reverse is not true: equal hash codes do not imply object equality, because different (unequal) objects can have identical hash codes.
I've peeked in the sources and I think the issue is, that the value from which the unit is constructed is used when calculating the hashcode:
| /// <summary> |
| /// Returns the hash code for this instance. |
| /// </summary> |
| /// <returns>A hash code for the current Length.</returns> |
| publicoverrideintGetHashCode() |
| { |
| returnnew{Info.Name,Value,Unit}.GetHashCode(); |
| } |
Shouldn't this be normalized to the unit's base unit or something along those lines?
Reason I'm asking is because I'm doing IEquatable implementations on my objects and in the .Equals(...) method, I'm comparing Units to a max of 5 decimals while in the GetHashCode() method I'm simply using the GetHashCode() method from the unit.
During the PR someone pointed out that for the GetHashCode(), the unit should also be rounded using the same logic as in the .Equals(...) method:
/// <inheritdoc />publicboolEquals(ResultAnalysis1DInternalForcesother){if(ReferenceEquals(null,other)){returnfalse;}if(ReferenceEquals(this,other)){returntrue;}returnbase.Equals(other)&&Equals(Member,other.Member)&&Equals(MemberRib,other.MemberRib)&&Index==other.Index&&Section.UnitsNetEquals(other.Section)&&N.UnitsNetEquals(other.N)&&Vy.UnitsNetEquals(other.Vy)&&Vz.UnitsNetEquals(other.Vz)&&Mx.UnitsNetEquals(other.Mx)&&My.UnitsNetEquals(other.My)&&Mz.UnitsNetEquals(other.Mz);}/// <inheritdoc />publicoverrideboolEquals(objectobj){returnReferenceEquals(this,obj)||objisResultAnalysis1DInternalForcesother&&Equals(other);}/// <inheritdoc />publicoverrideintGetHashCode(){unchecked{inthashCode=base.GetHashCode();hashCode=(hashCode*397)^(Member!=null?Member.GetHashCode():0);hashCode=(hashCode*397)^(MemberRib!=null?MemberRib.GetHashCode():0);hashCode=(hashCode*397)^Index;hashCode=(hashCode*397)^Section.GetHashCode();hashCode=(hashCode*397)^N.GetHashCode();hashCode=(hashCode*397)^Vy.GetHashCode();hashCode=(hashCode*397)^Vz.GetHashCode();hashCode=(hashCode*397)^Mx.GetHashCode();hashCode=(hashCode*397)^Mz.GetHashCode();returnhashCode;}}publicstaticboolUnitsNetEquals<TUnit>(thisTUnitvalue,TUnitother){if(value==null&&other==null){returntrue;}if(value!=null&&other!=null&&valueisIQuantitythisUnit&&otherisIQuantityotherUnit){try{doublethisValue=thisUnit.Value;doubleotherValueInThisUnits=otherUnit.As(thisUnit.Unit);returnComparison.Equals(thisValue,otherValueInThisUnits,ComparingConstants.DoubleComparisionDelta,ComparisonType.Absolute);}catch(ArgumentExceptione){returnfalse;}}returnEqualityComparer<TUnit>.Default.Equals(value,other);}
Describe the bug
.GetHashCode()returns different hash for 2 units for which.Equals()return trueTo Reproduce
Expected behavior
The
GetHashCode()should return the same value because the .Equals() returns trueAdditional context
From the remarks:
I've peeked in the sources and I think the issue is, that the value from which the unit is constructed is used when calculating the hashcode:
UnitsNet/UnitsNet/GeneratedCode/Quantities/Length.g.cs
Lines 1025 to 1032 in 7f67ffb
Shouldn't this be normalized to the unit's base unit or something along those lines?
Reason I'm asking is because I'm doing
IEquatableimplementations on my objects and in the.Equals(...)method, I'm comparing Units to a max of 5 decimals while in theGetHashCode()method I'm simply using theGetHashCode()method from the unit.During the PR someone pointed out that for the
GetHashCode(), the unit should also be rounded using the same logic as in the.Equals(...)method: