Exploring the .NET CoreFX Part 6: Use IEquatable for Higher-Performance Equals()

Exploring the .NET CoreFX Part 6: Use IEquatable for Higher-Performance Equals()

Page content

This is part 6 of my Exploring the .NET CoreFX Series.

Let’s say you are writing a custom IList which contains the following code:

public class MyList<T> : IList<T>
{
    private T[] array;

    ...

    public int IndexOf(T item)
    {
        for (int i = 0; i != array.Length; ++i) {
            if (this.array[i].Equals(item)) {
                return i;
            }
        }

        return -1;
    }
}

The above code uses T“s implementation of Object.Equals(), which is defined as:

public virtual bool Equals(Object obj);

If T is a value type, it will be automatically boxed by the compiler, which has a slight performance cost. However, if you knew that T implemented IEquatable, then you could avoid the boxing entirely. For example, this code would be slightly better performing than the above for value types:

public class MyList<T> : IList<T> where T : IEquatable<T>
{
    // Everything else is the same as above
}

However, it is inadvisable to require MyList to only contain objects which implement IEquatable.

You can get the best of both worlds by using EqualityComparer.Default to choose IEquatable.Equals() when available, and Object.Equals() otherwise, as follows:

public class MyList<T> : IList<T>
{
    private T[] array;

    ...

    public int IndexOf(T item)
    {
        return IndexOf(item, EqualityComparer<T>.Default);
    }

    public int IndexOf(T item, IEqualityComparer<T> equalityComparer)
    {
        for (int i = 0; i != array.Length; ++i) {
            if (equalityComparer.Equals(this.array[i], item)) {
                return i;
            }
        }

        return -1;
    }
}

Recommendations

  1. Implement IEquatable on value types for which you expect .Equals() to be called a lot, especially those you put into arrays, lists, or hash tables.