Skip to content

GetHashCode returns different hash for Equal units #1017

Description

@dschuermans

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);}

Metadata

Metadata

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions