警告:"...覆盖Object.Equals(对象o)但不覆盖Object.GetHashCode()"

Zac*_*son 25 .net c# entity-framework

我重写了我班级的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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它似乎工作.我做得对吗?记住Id是Guid类型.我的类是实体框架对象是否重要?

Eri*_*ert 33

正如其他人所说的那样,在平等中使用反思似乎很狡猾.抛开这一点,让我们专注于GetHashCode.

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

你可以自由地违反反过来.也就是说,如果两个对象具有相同的哈希码,则允许它们相等或不相等,如您所见.

我假设"Id"是一个不可变的属性.如果"Id"可以在对象的生命周期内更改,那么将对象放入哈希表时可能会出现问题.考虑确保在计算相等性和哈希码时仅使用不可变属性.

您的实现看起来不错,但您提出问题的事实表明您可能无法掌握构建GetHashCode实现的所有细微因素.一个好的开始是我关于这个主题的文章:

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

  • @TED:你的问题是“*如果我不调用 GetHashcode,我如何实现它重要吗?*”好吧,不,我想不是。但是,如果一个类型实现了相等,那么可以合理地假设它可以用于 LINQ 查询的联接子句中,现在您有一个哈希表需要处理。大多数类型的作者无法选择是否将实例放入哈希表中,因此没有太多的调用来为这些作者提供指导。 (3认同)
  • 我已阅读您的文章,并同意您的观点,但我发现自己处于一种只有可变属性的“DataContract”的情况。`公共字符串日{获取;放; }` 例如。.NET 的自动序列化似乎要求属性是可变的。我的“Equals”方法应该根据这些可变属性确定两个对象是否相等。不存在不可变的属性。因此,我无法生成安全的“GetHashCode”方法。看来我的选择是不覆盖“Equals”,或者永远不要将“DataContract”放入哈希表中...... (2认同)
  • 我在你的帖子中读到了这一点,但似乎很难确保合同没有被打破. (2认同)
  • 我真的希望看到更多关于在Equals中使用Reflection的"狡猾"的讨论,特别是考虑到它在[Object.Equals方法的官方文档](http://msdn.microsoft. com/en-us/library/bsc2ak47(v = vs.110).aspx)(例如,参见该页面上的"Rectangle"类). (2认同)
  • @kmote:这是一个问答网站,而不是讨论网站。如果您可以以具有“特定答案”的“特定问题”的形式表达您的担忧,那么将问题作为问题发布,看看人们会说什么。 (2认同)
  • @TED:这取决于你所说的“很好”是什么意思。它满足合法哈希码的最低标准。相同的对象具有相同的哈希码,因为所有对象都具有相同的哈希码。当对象发生变化时,哈希码不会发生变化,因为哈希码是常量。当对象位于集合中时,哈希码永远不会改变,因为哈希码永远不会改变。然而,它在哈希码的“目的”上戏剧性地失败了,即“平衡哈希表”。所有东西都会被分类到同一个桶中,所以你不妨使用一个列表! (2认同)

Jef*_*dge 8

它对我来说是正确的.每当我做这样的事情时,我通常也会实现,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();
     }
} 
Run Code Online (Sandbox Code Playgroud)

此结构还允许具有相同Id的更多派生对象进行比较,使其等于较少派生的对象.如果这不是所需的行为,那么您还必须像在问题中那样比较类型.

if (obj.GetType() != typeof(SomeClass)) return false;
Run Code Online (Sandbox Code Playgroud)


rec*_*ive 6

既然你没有处理密封类,我建议不要像这样检查类的相等性this.GetType() != obj.GetType().任何子类都SomeClass应该能够参与Equals,所以你可能想要使用它:

if (obj as SomeClass == null) return false;
Run Code Online (Sandbox Code Playgroud)

  • 目前实现`Equals`只关心`Id`,所有子类都有. (3认同)

Str*_*ior 5

传统Equals上以这样的方式实现,即如果两个对象在各方面完全相同,则它们将只是"等于".例如,如果有两个对象表示数据库中的同一对象,但是其中一个对象具有与另一个不同的Name属性,则对象不会被视为"等于",并且应尽可能避免生成相同的"Hashcode".

最好是在"不平等"方面犯错,而不是冒险调用两个不相等的对象.这就是对象的默认实现使用对象本身的内存位置的原因:除非它们是完全相同的对象,否则不会将两个对象视为"相等".所以我要说除非你想要写两个GetHashCode并且Equals以这样的方式检查它们所有属性的相等性,否则最好不要覆盖任何一种方法.

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


Lad*_*nka 5

你得到了第一个问题的优秀答案:

我做得对吗?

我会回答你的第二个问题

我的类是实体框架对象是否重要?

是的,这很重要.实体框架HashSet在内部使用了很多.例如,动态代理HashSet用于表示集合导航属性和EntityObject使用EntityCollection,而后者又在HashSet内部使用.