2009-03-04 2 views
6

Если я переопределяю Equals и GetHashCode, как мне решить, какие поля сравнивать? И что произойдет, если у меня есть два объекта с двумя полями каждый, но Equals проверяет только одно поле?Переопределение равных(), но не проверка всех полей - что произойдет?

Другими словами, скажем, у меня есть этот класс:

class EqualsTestClass 
{ 
    public string MyDescription { get; set; } 
    public int MyId { get; set; } 

    public override bool Equals(object obj) 
    { 
     EqualsTestClass eq = obj as EqualsTestClass; 
     if(eq == null) { 
      return false; 
     } else { 
      return MyId.Equals(eq.MyId); 
     } 
    } 

    public override int GetHashCode() 
    { 
     int hashcode = 23; 
     return (hashcode * 17) + MyId.GetHashCode(); 
    } 
} 

Я считаю, два объекта равны, если они имеют один и тот же MyId. Поэтому, если Id равен, но описание отличается, они все равно считаются равными.

Мне просто интересно, что такое подводные камни такого подхода? Конечно, конструкция, как это будет вести себя, как и ожидалось:

 List<EqualsTestClass> test = new List<EqualsTestClass>(); 

     EqualsTestClass eq1 = new EqualsTestClass(); 
     eq1.MyId = 1; 
     eq1.MyDescription = "Des1"; 

     EqualsTestClass eq2 = new EqualsTestClass(); 
     eq2.MyId = 1; 
     eq2.MyDescription = "Des2"; 

     test.Add(eq1); 
     if (!test.Contains(eq2)) 
     { 
      // Will not be executed, as test.Contains is true 
      test.Add(eq2); 
     } 

Как eq2 это значение, равное EQ1, он не будет добавлен. Но это код, который я контролирую, но мне интересно, есть ли код в структуре, который может вызвать непредвиденные проблемы?

Итак, должен ли я всегда добавлять все общедоступные поля в свое сравнение Equals() или какие рекомендации избегать неприятного сюрприза из-за плохой Framework-Mojo, которая была совершенно неожиданной?

ответ

11

Причина для переопределения Equals() заключается в том, что вы определяете, что означает, что для двух экземпляров равны. В некоторых случаях это означает, что все поля должны быть равными, но это не обязательно. Вам решать.

Для получения дополнительной информации см. documentation и post.

+0

договорились, как ОП, я overidden Equals(), чтобы просто проверить первичный идентификатор, и это он, его полностью программисты называют. –

1

Я не думаю, что вам нужно беспокоиться о Framework в этом случае. Если вы, как конструктор классов, считаете два экземпляра этого класса равными, если они имеют один и тот же MyId, тогда вам нужно только проверить MyId в своих методах переопределения Equals() и GetHashCode().

1

Вам нужно всего лишь проверить поля, которые необходимы для соответствия, если все, что нужно сопоставить, это идентификатор, а затем идите с этим.

1

Вопрос: Если я переопределяю Equals и GetHashCode, как мне решить, какие поля я сравниваю?

Это зависит от того, что вы пытаетесь выполнить. Если вы пытаетесь выяснить, одинаковы ли объекты, вы должны сравнить их все. Если у вас есть «ключ», и вы только хотите знать, являются ли они одним и тем же «объектом», даже если другие данные разные, просто проверьте значения «ключ».

И что произойдет, если у меня есть два объекта с двумя полями каждый, но Equals проверяет только одно поле?

Тогда у вас будет метод равенства, который просто проверяет, является ли «ключ» одинаковым и потенциально может иметь несколько «равных» объектов с внутренними отклонениями.

1

Другие заявили, что это совершенно верно и ожидается, и именно так предполагается, что Equals будет работать. Таким образом, нет проблем с ним, как с классом.

Я был бы очень осторожен в этом как API. Простите меня, если это не то, что было предназначено: в этом случае это всего лишь примечание осторожности для других.

Потенциальная проблема заключается в том, что пользователи API, естественно, ожидают, что равные объекты будут «быть одинаковыми». Это не является частью договора о равенстве, но оно является частью смысла здравого смысла этого слова. Класс выглядит немного как двоичный кортеж, но он не один, поэтому это должно быть по разумным причинам.

Примером такой разумной причины является то, что одно поле является «видимой деталью реализации», например, максимальным коэффициентом нагрузки на контейнере на основе хэш-таблицы. Примером опасной (хотя и заманчивой) причины является «потому что я добавил описание в дальнейшем и не хотел менять метод Equals, если он что-то сломал».

Таким образом, вполне допустимо сделать что-то немного интуитивно понятное, особенно если вы четко подтверждаете, что поведение может быть удивительным. Такие методы Equals должны поддерживаться, потому что их запрет будет сумасшедшим в тех случаях, когда поле, очевидно, не имеет значения. Но должно быть понятно, почему имеет смысл создавать две пары описания ID с одинаковым идентификатором и разными описаниями, но не имеет смысла добавлять их как в контейнер (например, HashSet), который использует Equals/HashCode для предотвращения дублирования записей.

0

Ваш пример проверяет, содержит ли список (EqualsTestClass) объект того же типа с теми же значениями свойств. Другой способ выполнить эту задачу без переопределения равных (и традиционного понимания равных) - использовать пользовательский сопоставитель. Это будет выглядеть примерно так (в VB):

Public Class EqualsTestComparer 
Implements IEqualityComparer(Of EqualsTestClass) 
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals 
    If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode 
    Return obj.ToString.ToLower.GetHashCode 
End Function  
End Class 

Тогда в вашей обычной вы просто использовать собственный компаратор:

If Not test.Contains(eq2, New EqualsTestComparer) Then 
    //Do Stuff 
End if 
Смежные вопросы