【问题标题】:How to implement IEquatable<T> when mutable fields are part of the equality - Problem with GetHashCode当可变字段是相等的一部分时如何实现 IEquatable<T> - GetHashCode 的问题
【发布时间】:2010-04-13 03:45:54
【问题描述】:

我在我的应用程序中使用实体框架。

我用实体的部分类实现了IEquatable&lt;T&gt;接口:

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 函数有关,我希望它依赖于 ID,因此实体通过它们的 ID 进行比较。

问题是当我点击保存时,ID 从 0 变为它的 db 值。 所以问题是我怎样才能有一个平等的对象,被正确地散列。

请帮助我找出 GetHashCode 函数中的问题(按 ID)以及我可以更改哪些内容以使其正常工作。

非常感谢。

【问题讨论】:

    标签: equals hashset mutable gethashcode iequatable


    【解决方案1】:

    您已使用可变字段 (AddressId) 作为哈希的一部分 - 不幸的是,这注定要失败。我的意思是:当你添加它时,AddressId 是 0? -1? 究竟是什么并不重要,但它不是最终的 id - 它与这个密钥/哈希一起存储。保存时,实际 id(数据库中的IDENTITY 列)会更新到对象中。

    很简单,你不能可靠地对这个值进行哈希处理,如果它在字典的一部分时可以改变的话。一种可能的解决方法是考虑工作单元,即插入是工作单元。含义:如果数据等的寿命与此一样长,那么这不是问题,因为您将永远不会在保存后尝试访问数据。随后(在不同的上下文中)加载数据也应该没问题,因为 id 在生命周期内不会改变。

    或者:删除此哈希/等式。

    【讨论】:

    • 我重新开始了,抱歉。无论如何,我的目标是平等,我希望我可以跳过 GetHashCode 实现部分。是的,初始值为 0。无论如何我使用 EF,所以所有对象都使用 ID 初始化为 0,然后属性被一个一个地单独设置,而不是由初始化程序,这意味着 ID 字段在实体已经发生变化时发生变化散列,也许你会知道如何解决它,并在这个可变对象上享受正确的散列和相等性。
    • 我认为这个问题的标题应该是“如何在可变字段上应用相等性”
    • @Shimmy - 在散列中?你没有。在某些情况下,您可以在第一次获取哈希时记住它,但如果不破坏Equals,您就不能在这里这样做。
    • 类 EntityCollection 使用内部 HashSet 所以当你向集合中添加一个新实体时,它的 ID 为 0,直到你调用 SaveChanges 重新加载 ID,然后当我想检查集合是否包含新添加的实体时,它找不到它。任何解决方法?我不明白什么是确定的答案:是否有可能在散列值的可变字段中具有平等可用性?有什么选择吗?我认为针对 GetHashCode 的 Equality 设计模式很糟糕,它们旨在提供 2 个功能,而实际上它们相互限制
    • 我试图删除覆盖的 GetHashCode,现在一切正常,因为在 HashTables 或集合中,我不能依赖可变字段,所以它总是返回常规的 GetHashCode,这不适用于数组、列表、集合等,其中 Equals 函数用于在调用 contains 等时确定相等性。
    【解决方案2】:

    非密封类不应实现IEquatable&lt;T&gt;,因为确保覆盖Object.Equals()Object.GetHashCode() 的派生类将以与Object.GetHashCode() 一致的方式实现IEquatable&lt;BaseType&gt; 的唯一方法是用于接口实现调用虚拟Object.Equals(Object) 方法。由于IEquatable&lt;T&gt; 的唯一目的是避免调用Object.Equals(Object) 的开销,并且IEquatable&lt;T&gt; 在未密封类上的安全实现无法避免调用它,因此这样的实现将毫无用处。

    另外,我强烈建议不要对任何可变类类型覆盖 Object.Equals(或实现 IEquatable&lt;T&gt;)。对于结构体,无论是否可变,覆盖Object.EqualsObject.GetHashCode,并实现IEquatable&lt;theirOwnType&gt;,因为结构体的字段存储为例如即使结构类型公开了可变的公共字段,Dictionary 键也将是不可变的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 2012-10-23
      • 1970-01-01
      • 2019-01-01
      • 1970-01-01
      • 2014-09-16
      • 2011-03-18
      相关资源
      最近更新 更多