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

问题描述:

我覆盖了我的类的Equals()以比较类型为Guid的ID值。警告:“...覆盖Object.Equals(对象o),但不覆盖Object.GetHashCode()”

然后Visual Studio中警告说:

...重写Object.Equals(对象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类型的。 我的课是一个实体框架对象吗?

既然你不是在处理一个封闭的类,我建议不要检查类的平等,就像这样。所有子类的SomeClass应该能够参加Equals也,所以你可能要改为使用:

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

什么时候子类实例会等于基类实例?该子类将具有其他属性,并且基类在执行比较时不会了解它们。 –

+3

'Equals'目前仅用于处理所有子类将拥有的'Id'。 – recursive

+0

我承认你的逻辑,但正如我在对Eric的评论中指出的,OP的Equals代码直接来自[官方MSDN文档]示例(http://msdn.microsoft.com/en-us/库/ bsc2ak47(v = vs.110)的.aspx)。 (请参阅Point类示例)。 – kmote

传统Equals以这样的方式,两个对象只会是“平等”,如果他们实施在各方面都完全一样。例如,如果您有两个对象表示数据库中的同一对象,但其中一个对象的属性不同于另一对象,则这些对象不会被视为“等于”,并且应该避免在可能的情况下生成相同的“哈希码” 。

在“不等于”一边比错误地调用两个不相等的对象更好。这就是为什么对象的默认实现使用对象本身的内存位置:没有两个对象永远不会被视为“相等”,除非它们是完全相同的对象。所以我会说,除非你想写GetHashCodeEquals这样一种方式来检查它们所有属性的相等性,你最好不要覆盖任何一种方法。

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

它看起来对我很正确。每当我做这样的事情时,我通常也执行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; 

正如其他人所说的那样,在Equals中使用Reflection似乎不太合适。除此之外,让我们专注于GetHashCode。

GetHashCode的主要规则,你不能违反如果两个对象相等,那么它们必须具有相同的散列码。或者,如果两个对象具有不同的哈希代码,则它们的等同方式是,那么它们必须不相等。你的实现在那里看起来不错。

您可以自由违反反面意见。也就是说,如果两个对象具有相同的散列码,那么它们被允许相等或不相等,正如您认为合适的那样。

我假设“Id”是一个不变的属性。如果“Id”可以在对象的生命周期内改变,那么当把对象放在哈希表中时可能会遇到问题。考虑确保只有不可变属性用于计算相等和散列码。

你的实现看起来不错,但是你问这个问题的事实表明你可能没有牢牢掌握构建GetHashCode实现的所有细微因素。一个良好的开始是我的主题文章:

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

+0

我已阅读您的文章,并同意您的观点,但我发现自己处于一种只有可变属性的“DataContract”的情况。 '公共字符串日{get;组; ''例如。似乎.NET的自动序列化需要属性是可变的。我的'Equals'方法应根据这些可变属性确定两个对象是否相等。不存在不可变属性。因此,我无法生成安全的'GetHashCode'方法。看起来我的选择是不重写'Equals',或者永远不要将我的'DataContract'放到一个哈希表中...... – crush

+0

@crush:你有第三个选项,它是*当它处于哈希表*。 –

+1

我在你的文章中读到过,但似乎很难确保合同不被破坏。 – crush

你得外观极好回答您的第一个问题:

我有没有做正确吗?

我会回答你的第二个问题

会有问题,我的班是一个实体框架的对象?

是的,它很重要。实体框架在内部使用HashSet。例如,动态代理使用HashSet来代表集合导航属性,并且EntityObject使用EntityCollection,其在内部依次使用HashSet

+1

+1:因此,除非您了解实体框架如何期待它们的行为,否则不要混淆哈希代码。 – StriplingWarrior

+0

我想知道OP实现是否需要根据您的答案进行修改..您说它很重要,为什么它很重要,但不是这意味着实现GetHashCode()的实现者。你能提供更多的信息吗? – Andrew

+0

@Andrew:该信息是在其他答案。 OP的实施不需要任何改变。 –