What's the difference between IEquatable and just overriding Object.Equals()?

C#.NetEqualsEqualityIequatable

C# Problem Overview


I want my Food class to be able to test whenever it is equal to another instance of Food. I will later use it against a List, and I want to use its List.Contains() method. Should I implement IEquatable<Food> or just override Object.Equals()? From MSDN:

> This method determines equality by > using the default equality comparer, > as defined by the object's > implementation of the > IEquatable.Equals method for T > (the type of values in the list).

So my next question is: which functions/classes of the .NET framework make use of Object.Equals()? Should I use it in the first place?

C# Solutions


Solution 1 - C#

The main reason is performance. When generics were introduced in .NET 2.0 they were able to add a bunch of neat classes such as List<T>, Dictionary<K,V>, HashSet<T>, etc. These structures make heavy use of GetHashCode and Equals. But for value types this required boxing. IEquatable<T> lets a structure implement a strongly typed Equals method so no boxing is required. Thus much better performance when using value types with generic collections.

Reference types don't benefit as much but the IEquatable<T> implementation does let you avoid a cast from System.Object which can make a difference if it's called frequently.

As noted on Jared Parson's blog though, you must still implement the standard Object.Equals and Object.GetHashcode overrides.

Solution 2 - C#

According to the MSDN:

> If you implement IEquatable<T>, you > should also override the base class > implementations of > Object.Equals(Object) and GetHashCode > so that their behavior is consistent > with that of the IEquatable<T>.Equals > method. If you do override > Object.Equals(Object), your overridden > implementation is also called in calls > to the static Equals(System.Object, > System.Object) method on your class. > This ensures that all invocations of > the Equals method return consistent > results.

So it seems that there's no real functional difference between the two except that either could be called depending on how the class is used. From a performance standpoint, it's better to use the generic version because there's no boxing/unboxing penalty associated with it.

From a logical standpoint, it's also better to implement the interface. Overriding the object doesn't really tell anyone that your class is actually equatable. The override may just be a do nothing class or a shallow implementation. Using the interface explicitly says, "Hey, this thing is valid for equality checking!" It's just better design.

Solution 3 - C#

Extending what Josh said with a practical example. +1 to Josh - I was about to write the same in my answer.

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}

This way, I have re-usable Equals() method that works out of the box for all my derived classes.

Solution 4 - C#

If we call object.Equals, it forces to expensive boxing on value types. This is undesirable in performance-sensitive scenarios. The solution is to use IEquatable<T>.

public interface IEquatable<T>
{
  bool Equals (T other);
}

The idea behind IEquatable<T> is that it gives the same result as object.Equals but more quickly. The constrain where T : IEquatable<T> must be used with generic types like below.

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

otherwise, it binds to slower object.Equals().

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questiondevoured elysiumView Question on Stackoverflow
Solution 1 - C#JoshView Answer on Stackoverflow
Solution 2 - C#CodexArcanumView Answer on Stackoverflow
Solution 3 - C#this. __curious_geekView Answer on Stackoverflow
Solution 4 - C#Seyedraouf ModarresiView Answer on Stackoverflow