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
.
An Array knows nothing about <T> so it can't implement or use the IEquatable interface. An Array instead holds objects that are not generic. It will call Equals to compare one object to another as all objects have an Equals method which you are free to override.
Therefore, if .indexOf () returns a number greater than or equal to zero (or in comparator terms: >= 0 ), then we know that the element we're looking for exists in the array. Note: This example is meant to be friendly and familiar even to new developers, but Using .indexOf () to identify "collisions" within an array is something of a blunt tool.
Because generic object collections use the IEquatable<T> interface when testing for equality in such methods as Contains, IndexOf, LastIndexOf, and Remove. An Array knows nothing about <T> so it can't implement or use the IEquatable interface.
This is because the string 'c' is in the third position, and arrays (like just about everything else in programming) use a zero-based index - meaning that 'a' is in position 0, 'b' is in position 1, 'c' is in position 2, and so on. However, if what you're looking for is not found in the array, .indexOf () will return -1.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With