malat
malat

Reputation: 12505

Computing hashcode of combination of value type and array

It seems I cannot understand the documentation of Hashcode struct:

I tried a naive:

public struct S : IEquatable<S>
{
    public int I { get; set;  }
    public string[] A { get; set;  }

    public override bool Equals(object? obj) => obj is S s && Equals(s);

    public bool Equals(S other) => I == other.I && StructuralComparisons.StructuralEqualityComparer.Equals(A, other.A);

    // public override int GetHashCode() => I.GetHashCode() ^ StructuralComparisons.StructuralEqualityComparer.GetHashCode(A);
    public override int GetHashCode() => HashCode.Combine(I, A);
}

Which lead to:

S s1 = new S() { I = 42, A = new string[] { "hello" } };
S s2 = new S() { I = 42, A = new string[] { "hello" } };
Assert.Equal(s1.GetHashCode(), s2.GetHashCode());

Giving an error:

Xunit.Sdk.EqualException: 'Assert.Equal() Failure
Expected: 840823323
Actual:   -1160370390'

What is the right way to use HashCode.Combine (I did not like my earlier XOR solution) ?

Upvotes: 0

Views: 1131

Answers (1)

D Stanley
D Stanley

Reputation: 152566

You're using HashCode.Combine correctly. The problem is that you're using StructuralEqualityComparer for the equality comparison but not for the hash code generation, so the two strings arrays are "equal" but do not generate the same hash code by default. Use

public override int GetHashCode() => HashCode.Combine(I, 
       StructuralComparisons.StructuralEqualityComparer.GetHashCode(A));

This is necessary because Combine will call GetHashCode on each instance, which for lists will be different regardless of the values in the list. So you need to specify how you want to generate the hash code.

Upvotes: 2

Related Questions