【问题标题】:Warning: "... overrides Object.Equals(object o) but does not override Object.GetHashCode()"警告:“...覆盖 Object.Equals(object o) 但不覆盖 Object.GetHashCode()”
【发布时间】:2011-06-24 15:27:12
【问题描述】:

我覆盖了我班级的 Equals() 来比较 Guid 类型的 ID 值。

然后 Visual Studio 发出警告:

... 覆盖 Object.Equals(object o) 但 不覆盖 Object.GetHashCode()

然后我也像这样覆盖它的 GetHashCode():

public partial class SomeClass
{
    public override bool Equals(Object obj)
    {
        //Check for null and compare run-time types.
        if (obj == null || this.GetType() != obj.GetType()) return false;

        return this.Id == ((SomeClass)obj).Id;
    }

    public override int GetHashCode()
    {
        return this.Id.GetHashCode();
    }
}

它似乎工作。 我做对了吗?记住 Id 的类型是 Guid。 我的类是实体框架对象有关系吗?

【问题讨论】:

    标签: c# .net entity-framework


    【解决方案1】:

    正如其他人所说,在 Equals 中使用反射似乎很狡猾。撇开这些不谈,让我们专注于 GetHashCode。

    您不得违反 GetHashCode 的主要规则是如果两个对象相等,则它们必须具有相同的哈希码。或者,一种等效的说法是如果两个对象具有不同的哈希码,那么它们一定是不相等的。你的实现在那里看起来不错。

    您可以随意违反相反的规定。也就是说,如果两个对象具有相同的哈希码,那么它们被允许相等或不相等,只要你认为合适。

    我假设“Id”是一个不可变的属性。如果“Id”可以在对象的生命周期内发生变化,那么在将对象放入哈希表时可能会遇到问题。考虑确保在计算相等性和哈希码时仅使用不可变属性。

    您的实现看起来不错,但您提出这个问题的事实表明您可能没有牢牢掌握构建 GetHashCode 实现的所有微妙因素。一个很好的起点是我关于这个主题的文章:

    http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

    【讨论】:

    • 我已经阅读了您的文章,并同意您的观点,但我发现自己的DataContract 只有可变属性。 public string Day { get; set; } 例如。似乎 .NET 的自动序列化要求属性是可变的。我的Equals 方法应该根据这些可变属性确定两个对象是否相等。不存在不可变的属性。因此,我无法生成安全的GetHashCode 方法。看来我的选择是不覆盖Equals,或者永远不要将我的DataContract 放入哈希表中......
    • 我在你的帖子中读到过,但似乎很难确保合同没有被破坏。
    • 我真的很想看到更多关于在 Equals 中使用反射的“狡猾”的讨论,特别是考虑到它在 the official documentation of the Object.Equals Method 的示例中使用的事实(参见上面的“矩​​形”类页面,例如)。
    • @T.E.D.:这取决于你所说的“很好”。它符合合法哈希码的最低标准。相等的对象具有相等的哈希码,因为所有对象都具有相等的哈希码。当对象发生变异时,哈希码不会发生变异,因为哈希码是一个常量。当对象在集合中时,哈希码永远不会改变,因为哈希码永远不会改变。但是,它在平衡哈希表的哈希码的目的上却大大失败了。一切都会被分类到同一个桶中,所以你不妨使用一个列表!
    • @T.E.D.:你的问题是“如果我不调用 GetHashcode,我如何实现它有关系吗?”好吧,不,我猜不是。但是,如果一个类型实现了相等性,那么假设它可以用在 LINQ 查询的连接子句中肯定是合理的,现在您已经有了一个哈希表来处理。大多数类型的作者无法选择是否将实例放入哈希表中,因此没有太多要求为这些作者提供指导。
    【解决方案2】:

    在我看来是正确的。每当我做这样的事情时,我通常也会实现IEquatable,这样相同编译时类型的变量之间的比较会更有效率。

    public partial class SomeClass : IEquatable<SomeClass>
    {
         public override bool Equals(Object obj)
         {
             return Equals(obj as SomeClass);
         }
         public bool Equals(SomeClass obj)
         {
             if (obj == null) 
                 return false;
             return Id == obj.Id;
         }
         public override int GetHashCode()
         {
             return Id.GetHashCode();
         }
    } 
    

    此结构还允许将具有相同 Id 的更多派生对象与派生程度较低的对象进行比较。如果这不是所需的行为,那么您还必须像在问题中那样比较类型。

    if (obj.GetType() != typeof(SomeClass)) return false;
    

    【讨论】:

      【解决方案3】:

      由于您不是在处理密封类,因此我建议您不要像 this.GetType() != obj.GetType() 这样检查类是否相等。 SomeClass 的任何子类也应该能够参与 Equals,因此您可能希望改用它:

      if (obj as SomeClass == null) return false;
      

      【讨论】:

      • 子类实例何时会等于基类实例?子类将具有额外的属性,而基类在进行比较时不会知道它们。
      • Equals 目前实现只关心Id,所有子类都会有。
      • 我承认你的逻辑,但正如我在对 Eric 的评论中指出的那样,OP 的 Equals 代码直接来自 official MSDN documentation 的示例。 (参见 Point 类示例)。
      【解决方案4】:

      传统上,Equals 的实现方式是两个对象只有在各方面完全相同时才会“相等”。例如,如果您有两个对象代表数据库中的同一对象,但其中一个对象的Name 属性与另一个对象不同,则这些对象不被视为“相等”,应避免产生相同的“哈希码”如果可能的话。

      最好选择“不相等”而不是冒险调用两个不相等的对象。这就是对象的默认实现使用对象本身的内存位置的原因:除非它们是完全相同的对象,否则不会将两个对象视为“相等”。所以我想说,除非你想同时编写 GetHashCodeEquals 以检查它们所有属性的相等性,否则你最好不要覆盖任何一种方法。

      如果您有一个数据结构(如HashSet),您特别希望根据 ID 值确定相等性,您可以为该数据结构提供特定的IEqualityComparer 实现。

      【讨论】:

        【解决方案5】:

        你的第一个问题得到了很好的答案:

        我做对了吗?

        我会回答你的第二个问题

        我的类是实体框架对象有关系吗?

        是的,这很重要。实体框架在内部大量使用HashSet。例如,动态代理使用HashSet 表示集合导航属性,EntityObjects 使用EntityCollection,后者又在内部使用HashSet

        【讨论】:

        • +1:因此,除非您了解 Entity Framework 期望它们的行为方式,否则不要乱用哈希码。
        • 我想知道 OP 实现是否需要根据您的回答进行修改。您说这很重要,以及为什么重要,但这对 GetHashCode() 的实施者来说并不意味着什么。请您提供更多信息吗?
        • @Andrew:信息在其他答案中。 OP 的实现不需要任何改变。
        猜你喜欢
        • 1970-01-01
        • 2016-12-05
        • 2019-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-09
        相关资源
        最近更新 更多