3

I saw the question posed here: Have I implemented Equals()/GetHashCode() correctly? but my c# is not as strong, and I am unfimiliar with IEquatable enough that I would like to see this in VB.NET if possible please.

My example code (The class will eventually use INotifyPropertyChanged when I get there):

Public Class Car
Implements ICloneable
Implements IEquatable(Of Car)

Public Property Make() As String
    Get
        Return m_Make
    End Get
    Set(ByVal value As String)
        m_Make = value
    End Set
End Property
Private m_Make As String

Public Property Model() As String
    Get
        Return m_Model
    End Get
    Set(ByVal value As String)
        m_Model = value
    End Set
End Property
Private m_Model As String

Public Function Clone() As Object Implements System.ICloneable.Clone
    Return New Car() With { _
     .Make = Me.Make, _
     .Model = Me.Model _
    }
End Function

Public Overloads Function Equals(ByVal other As Car) As Boolean Implements System.IEquatable(Of Car).Equals
    Return other.Make = Me.Make AndAlso other.Model = Me.Model
End Function 
End Class

Thanks,

Community
  • 1
  • 1
Omar Mir
  • 1,500
  • 1
  • 19
  • 39

2 Answers2

6

You really do need to implement Overrides for the object.Equals and object.GetHashCode implementations.

Basically, implementing IEquatable(of T).Equals by itself will only work so long as the caller KNOWS to call IEquatable(of T).Equals instead of regular object.Equals.

Consider if you have an ArrayList of Cars and check if the list Contains(myCar), where myCar's Make and Model are the same as a car in the ArrayList...but the one in the ArrayList isn't actually the exact same instance. Contains would return false.

Worse yet, if you had a Hashtable or Dictionary, which uses GetHashCode to determine where to store entries, equality would never work because two cars with the same Make and Model would return different values for GetHashCode()

Basically, it comes down to you adding the following implementations to car:

Public Overrides Overloads Function Equals(obj As Object) As Boolean
    Return TypeOf obj Is Car AndAlso Equals(DirectCast(obj, Car)) 
End Function 

Public Overrides Function GetHashCode() As Int32
    Dim hash As Int32 = 179 ' or something intelligent

    hash = hash * 27 + Make.GetHashCode()
    hash = hash * 27 + Model.GetHashCode()

    Return hash
End Function

So the question I have is: why implement IEquatable at all? Why not just override Equals and GetHashCode?

Jeff
  • 35,755
  • 15
  • 108
  • 220
  • Implementing IEquatable so that I can implement a repository pattern. How does hashcode solve the problem of two object having the same properties? Also why the "something intelligent" comment? Sorry if I'm coming off a bit dense. Equals is being used from the ChangeTracker: https://gist.github.com/2065076 – Omar Mir Mar 18 '12 at 08:30
  • There are many different ways to implement a hash code. The 179 and 27 are just numbers I picked. The important part is to notice that the hash code is based off the Make and Model just like your Equals override. – Jeff Mar 19 '12 at 02:45
  • I see your ChangeTracker...but what I'm trying to say is that you're already needing to override Object.Equals too...so there's no need to also implement IEquatable. – Jeff Mar 19 '12 at 02:46
  • Gotcha, after going through it myself I understand there is no reason to implement IEquatable. I will remove that and override equals as per your example. Thanks! I need to know when to implement IEquatable - but that's a separate question though I wouldn't mind an explanation. – Omar Mir Mar 19 '12 at 05:15
  • 1
    When to implement IEquatable: when you want to express that a Foo can equal a Bar (completely different types). Really, it's never necessary though... – Jeff Mar 19 '12 at 18:20
  • I noticed that 179 is a prime number. Is that deliberate? Can you explain your reasoning? If not, can you confirm once more that the factors you use are indeed 100% arbitrary and have no relation to one another? – Ross Brasseaux Oct 11 '16 at 19:19
  • Prime numbers are said to promote efficiency when a dictionary relies on hashcodes to build its tree. I have this handy helper function to generate a hashcode based on an array of hashcodes: `Dim FinalHash As Integer = 17 : For Each HashCode As Integer In HashCodes() : FinalHash = FinalHash * 31 + HashCode : Next` I did not keep a note of the source where I got the two prime numbers from, but I recall it had been substantially thought and researched. – Ama Mar 09 '20 at 21:34
1

Only implement IEquatable<T> for structs or sealed classes. Any legitimate implementation of IEquatable<T>.Equals(T) needs to have semantics compatible with the class's override of Object.GetHashCode(), which must in turn have semantics compatible with the class's override of Equals(Object). If a type is not sealed, the only way to ensure that derived types' implementation of IEquatable<T>.Equals(T) will be compatible with their override of Object.Equals(Object) will be to have the former method chain to the latter, effectively nullifying any advantage one might have obtained from implementing IEquatable<T> in the first place.

Implementing IEquatable<T> is often a big win for struct types (saves a boxing operation on every comparison), and a somewhat smaller win for other sealed types (saves a typecast on every comparison). Unless performance is critical, I'd probably skip it for most non-struct types, even if they're sealed.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • The implication doesn't apply backwards does it? I.e. If I override .Equals I am not required by convention to implement IEquatable or IEqualityComparer? – Omar Mir Mar 20 '12 at 02:15
  • @OmarMir: The requirement that `IEquatable.Equals(T)` match `Object.Equals(Object)` only applies to types which either implement `IEquatable` or derive from types that do. Types which do not implement `IEquatable` are exempt from the requirement. – supercat Apr 26 '13 at 21:22