22

How do I deal with null fields in GetHashCode function?

Module Module1
  Sub Main()
    Dim c As New Contact
    Dim hash = c.GetHashCode
  End Sub

  Public Class Contact : Implements IEquatable(Of Contact)
    Public Name As String
    Public Address As String

    Public Overloads Function Equals(ByVal other As Contact) As Boolean _
        Implements System.IEquatable(Of Contact).Equals
      Return Name = other.Name AndAlso Address = other.Address
    End Function

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
      If ReferenceEquals(Me, obj) Then Return True

      If TypeOf obj Is Contact Then
        Return Equals(DirectCast(obj, Contact))
      Else
        Return False
      End If
    End Function

    Public Overrides Function GetHashCode() As Integer
      Return Name.GetHashCode Xor Address.GetHashCode
    End Function
  End Class
End Module
davmos
  • 9,324
  • 4
  • 40
  • 43
Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632
  • XORing is not a good way of combining hash codes. For a more robust approach, see http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode – Jeff Yates Jul 28 '11 at 13:42

2 Answers2

38

Typically, you check for null and use 0 for that "part" of the hash code if the field is null:

return (Name == null ? 0 : Name.GetHashCode()) ^ 
  (Address == null ? 0 : Address.GetHashCode());

(pardon the C#-ism, not sure of the null check equivalent in VB)

davmos
  • 9,324
  • 4
  • 40
  • 43
itowlson
  • 73,686
  • 17
  • 161
  • 157
  • np about the 'csism'. you just clarified that the hash code for null is 0. – Shimmy Weitzhandler Mar 15 '10 at 02:56
  • btw, if the distinguishing field is an int, can I return the int itself instead of it's hashcode? would that be a bad idea? i.e. return ContactId ^ (Name == null ? 0 : Name.GetHashCode) (it's an int)? – Shimmy Weitzhandler Mar 15 '10 at 02:59
  • 2
    The only requirement for hash codes is that equal objects return equal hash codes. Since equal ints are equal, returning the int as its own hash code is fine. Indeed, this is exactly what Int32.GetHashCode appears to do...! – itowlson Mar 15 '10 at 03:02
  • 3
    XORing is not a good way of combining hash codes. For a more robust approach, see http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode – Jeff Yates Jul 28 '11 at 13:43
  • seems Nullable already returns 0 when HasValue is false: https://msdn.microsoft.com/en-us/library/axk9ks7d(v=vs.110).aspx – Leon van der Walt Apr 01 '15 at 14:29
  • @Shimmy Note that in many (all?) versions of the .NET Framework, the override of `GetHashCode()` that exists in `System.Int32` simply returns the integer itself. In that case `ContactId.GetHashCode()` is the same as just `ContactId` when the type of `ContactId` is `int`. of course you never know if the implementation will change in future versions of .NET. – Jeppe Stig Nielsen Jan 30 '17 at 22:21
15

As Jeff Yates suggested, the override in the answer would give the same hash for (name = null, address = "foo") as (name = "foo", address = null). These need to be different. As suggested in link, something similar to the following would be better.

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        hash = hash * 23 + (Name == null ? 0 : Name.GetHashCode());
        hash = hash * 23 + (Address == null ? 0 : Address.GetHashCode());
    }
    return hash;
}

What is the best algorithm for an overridden System.Object.GetHashCode?

Community
  • 1
  • 1
IllNate
  • 176
  • 1
  • 4
  • 5
    `(Name == null ? 0 : Name.GetHashCode())` can be condensed to `(Foo?.GetHashCode() ?? 0)` in C# 7 – Adam May 22 '19 at 15:21