Почему именно массив.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;
, Что будет работать в обоих случаях одинаково.