#c# #.net #generics
#c# #.net #общие
Вопрос:
У меня есть следующий класс
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
return Id.Equals(other.Id);
}
}
Если я попытаюсь создать уникальный список элементов списка следующим образом
Guid a = Guid.NewGuid();
List<Product> listA = new List<Product>();
listA.Add(new Product(){Id = a});
List<Product> listB = new List<Product>();
listB.Add(new Product()
{
Id = a
});
Debug.Assert(listA.Union(listB).Count()==1);
возвращаются два элемента, это происходит до тех пор, пока я не переопределю метод object.Equals, как только я это сделаю, и мой код выглядит следующим образом
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Id.Equals(Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (Product)) return false;
return Equals((Product) obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
теперь вызывается мой метод IEquatable Equals, но только если я переопределяю базовый метод, более того, если я ставлю точку останова для метода object equals, он никогда не вызывается.
Почему это так?
—-ОБНОВИТЬ
Так и с классом продукта
class Product : IEquatable<Product>
{
public Guid Id
{
get;
set;
}
public bool Equals(Product other)
{
return Id.Equals(other.Id);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Если GetHashCode удален, IEquatable реализация Equals никогда не выполняется Я понимаю, что вы обычно должны реализовывать Equals и GetHashCode вместе, не так ли?
Комментарии:
1.
GetHashCode
Похоже, добавление этой реализации в ваш исходный класс дает вам желаемое поведение.
Ответ №1:
Проблема в исходной реализации, в которой вы не переопределяете GetHashcode
метод. Under the hood Union
использует Set<T>
структуру стилей для удаления дубликатов. Эта структура помещает объекты в сегменты на основе значения, возвращаемого из GetHashCode
. Если хэш-код не совпадает между равными объектами (что он должен делать), то их потенциально можно поместить в разные сегменты и никогда не сравнивать с Equals
В общем, если вы реализуете IEquatable<T>
, вы всегда должны
- Переопределение
Object.Equals
- Переопределение
Object.GetHashCode
Невыполнение обоих приведет вас к подобным ситуациям.
Обратите внимание, что ваша реализация может быть немного упрощена
class Product : IEquatable<Product>
{
public Guid Id { get; set; }
public bool Equals(Product other)
{
if (ReferenceEquals(null, other)) {
return false;
}
return other.Id == this.Id;
}
public override bool Equals(object obj)
{
return Equals(obj as Product);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Комментарии:
1. Это просто реализация метода equals в resharper по умолчанию. Если это так (и я уверен, что вы правы, кстати), почему точка останова не попадает? Это ошибка пользователя? Также здесь BOL msdn.microsoft.com/en-us/library/ms224763.aspx предполагает, что он должен искать IEquatable?
2. Я использую стандартный метод объединения LINQ, приведенный выше код взят из тестового жгута, чтобы подтвердить то, что я вижу
3. @KevHunter я запустил этот конкретный пример в отладчике и подтвердил, что
override bool Equals
он никогда не вызывается в образце. Оно всегда будет проходить черезIEquatable<T>
версию. Вы запускали это локально?4. Добавление метода GetHashCode, предложенного Aakash, дает мне ожидаемое поведение, есть идеи, почему это?
5. @KevHunter о, я не понял, что в вашем исходном impl вы не переопределяли
GetHashcode
. Да, это определенно предварительное требование.Equals
Метод даже не будет вызван, если хэш-коды не совпадают.
Ответ №2:
Документация для Enumerable.Объединение говорит:
Средство сравнения равенства по умолчанию, Default, используется для сравнения значений типов, которые реализуют универсальный интерфейс IEqualityComparer(Of T). Чтобы сравнить пользовательский тип данных, вам необходимо реализовать этот интерфейс и предоставить свои собственные методы GetHashCode и Equals для этого типа.
Вы реализуете IEquatable
. Вам необходимо реализовать IEqualityComparer
.