Как реализовать IEquatable <T>, когда изменяемые поля являются частью равенства - проблема с GetHashCode - PullRequest
1 голос
/ 13 апреля 2010

Я использую Entity Framework в своем приложении.

Я реализовал с частичным классом сущности интерфейс IEquatable<T>:

Partial Class Address : Implements IEquatable(Of Address) 'Other part generated
  Public Overloads Function Equals(ByVal other As Address) As Boolean _
      Implements System.IEquatable(Of Address).Equals
    If ReferenceEquals(Me, other) Then Return True
    Return AddressId = other.AddressId
  End Function

  Public Overrides Function Equals(ByVal obj As Object) As Boolean
    If obj Is Nothing Then Return MyBase.Equals(obj)
    If TypeOf obj Is Address Then 
      Return Equals(DirectCast(obj, Address)) 
  Else
    Return False
  End Function

  Public Overrides Function GetHashCode() As Integer
    Return AddressId.GetHashCode
  End Function
End Class

Теперь в моем коде я использую это следующим образом:

Sub Main()
  Using e As New CompleteKitchenEntities
    Dim job = e.Job.FirstOrDefault
    Dim address As New Address()

    job.Addresses.Add(address)
    Dim contains1 = job.Addresses.Contains(address) 'True
    e.SaveChanges()
    Dim contains2 = job.Addresses.Contains(address) 'False

    'The problem is that I can't remove it:
    Dim removed = job.Addresses.Remoeve(address) 'False

  End Using
End Sub

Обратите внимание (я проверил в визуализаторе отладчика), что класс EntityCollection хранит свои сущности в HashSet, поэтому он имеет отношение к функции GetHashCode, я хочу, чтобы она зависела от идентификатора, поэтому сущности сравниваются по своим идентификаторам.

Проблема в том, что когда я нажимаю сохранить, идентификатор меняется с 0 до значения в дБ. Таким образом, вопрос в том, как получить уравновешенный объект, должным образом хешированный.

Пожалуйста, помогите мне найти, что не так в функции GetHashCode (по идентификатору) и что я могу изменить, чтобы она работала.

Большое спасибо.

Ответы [ 2 ]

1 голос
/ 27 августа 2012

Незапечатанные классы не должны реализовывать IEquatable<T>, потому что единственный способ гарантировать, что производный класс, который переопределяет Object.Equals() и Object.GetHashCode(), реализует IEquatable<BaseType> способом, совместимым с Object.GetHashCode(), - это интерфейс реализация для вызова виртуального Object.Equals(Object) метода. Поскольку единственная цель IEquatable<T> состоит в том, чтобы избежать затрат на вызов Object.Equals(Object), а безопасная реализация IEquatable<T> в незапечатанном классе не может избежать его вызова, такая реализация не будет иметь смысла.

Кроме того, я настоятельно рекомендую избегать переопределения Object.Equals (или реализации IEquatable<T>) для любого изменяемого типа класса. Для структур, изменяемых или нет, полезно переопределить Object.Equals и Object.GetHashCode и реализовать IEquatable<theirOwnType>, поскольку поля структуры хранятся как, например, ключ Dictionary будет неизменным, даже если тип структуры предоставляет изменяемые открытые поля.

1 голос
/ 13 апреля 2010

Вы использовали изменяемое поле (AddressId) в качестве части хеша - это, к сожалению, обречено. Под чем я подразумеваю: когда вы добавляете это, AddressId равно 0? -1? не имеет значения, что именно точно , но это не окончательный идентификатор - и он сохраняется с этим ключом / хэшем. При сохранении идентификатор фактический (столбец IDENTITY из базы данных) обновляется в объекте.

Проще говоря, вы не можете надежно хешировать это значение, если оно может измениться, когда оно является частью словаря. Одним из возможных обходных путей было бы рассмотрение единицы работы, то есть вставка - это единица работы. Значение: если данные и т. Д. Живут только так долго, то это не проблема, так как вы никогда не будете пытаться получить доступ к данным после их сохранения. Впоследствии (в другом контексте) загрузка данных также должна быть в порядке, так как идентификатор не меняется в течение жизни.

В качестве альтернативы: отбросьте этот хэш / равенство.

...