Почему именно массив.IndexOf не проверяет IEquatable, как это делает список?
Рассмотрим Этот код:
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;
}
Результаты:
0
-1
Почему результаты различаются между List<T> и Array? Я предполагаю, что это сделано специально, но почему?
Глядя на код List<T>.IndexOf заставляет меня задуматься еще больше, так как он портируется на Array.IndexOf.
3 ответа:
Реализация
IndexOfв классе array вызывает метод:
public static int IndexOf(Array array, object value, int startIndex, int count)Как вы видите, он использует
objectв качестве параметра value. В этом методе есть код:object obj = objArray[index]; if (obj != null && obj.Equals(value)) return index;Classs работает с объектами, поэтому он вызывает метод
public virtual bool Equals(object obj), а не универсальный.В
ListклассеIndexOfиспользуется универсальная реализация:public static int IndexOf<T>(T[] array, T value, int startIndex, int count)Таким образом, он использует универсальный компаратор качества:
EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);UPD : я написал небольшой пост об этой проблеме: http://blog.rogatnev.net/2017/07/14/IndexOf-with-IEquatable.html
Поскольку универсальные коллекции объектов используют интерфейс
IEquatable<T>при тестировании на равенство в таких методах, какContains,IndexOf,LastIndexOf, иRemove.Массив ничего не знает о
<T>, поэтому он не может реализовать или использовать IEquatable интерфейс.Массив вместо этого содержит объекты, которые не являются универсальными. Он вызовет
EqualsДля сравнения одного объекта с другим, поскольку все объекты имеют методEquals, который вы можете переопределить.
List<T>может использовать интерфейсIEquatable<T>, так что один работает, как ожидалось.Массив использует метод
EqualsизObject, и вы не переопределяете его, а просто реализуетеIEquatable.Попробуйте определить
EqualsКак:public override bool Equals(Object other) => other != null && (other as Item).Id == Id;, Что будет работать в обоих случаях одинаково.