Search code examples
c#.netcollectionsindexofiequatable

Why is Array.IndexOf not checking for IEquatable like List<T> does?


Consider this code:

public static void Main()
{
    var item = new Item { Id = 1 };

    IList list = new List<Item> { item };
    IList array = new[] { item };

    var newItem = new Item { Id = 1 };

    var lIndex = list.IndexOf(newItem);
    var aIndex = array.IndexOf(newItem);

    Console.WriteLine(lIndex);
    Console.WriteLine(aIndex);
}

public class Item : IEquatable<Item>
{
    public int Id { get; set; }

    public bool Equals(Item other) => other != null && other.Id == Id;

}

Results:

0
-1

Why are the results different between List<T> and Array? I guess this is by design, but why?

Looking at the code of List<T>.IndexOf makes me wonder even more, since it's porting to Array.IndexOf.


Solution

  • Implementation of IndexOf in array class calls method:

    public static int IndexOf(Array array, object value, int startIndex, int count)

    As you see, it uses object as value parameter. In this method there is code:

    object obj = objArray[index];
    if (obj != null && obj.Equals(value))
        return index;
    

    Classs works with objects, so it calls public virtual bool Equals(object obj) method, not generic one.

    In List class IndexOf uses generic implementation:

    public static int IndexOf<T>(T[] array, T value, int startIndex, int count)
    

    So, it uses generic quality comparer:

    EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);
    

    UPD: I wrote a little post about this problem: http://blog.rogatnev.net/2017/07/14/IndexOf-with-IEquatable.html