2011-11-15 27 views
3

我有一个这样的实体类(有很多东西失踪):Contains如何返回false,但GetHashCode()返回相同的数字,并且Equals返回true?

class Parent 
{ 
    private readonly Iesi.Collections.Generic.ISet<Child> children = 
     new Iesi.Collections.Generic.HashedSet<Child>(); 

    public virtual void AddChild(Child child) 
    { 
     if (!this.children.Contains(child)) 
     { 
      this.children.Add(child); 
      child.Parent = this; 
     } 
    } 

    public virtual void RemoveChild(Child child) 
    { 
     if (this.children.Contains(child)) 
     { 
      child.Parent = null; 
      this.children.Remove(child); 
     } 
    } 
} 

然而,当我试图删除一个孩子,if语句评估为false。于是,我把对if声明断点,评估某些表达式:

this.children.Contains(child) => false 
this.children.ToList()[0].Equals(child) => true 
this.children.ToList()[0].GetHashCode() => 1095838920 
child.GetHashCode() => 1095838920 

我的理解是,如果GetHashCode返回相同的值,它会检查Equals。为什么Contains返回false?


我的两个ParentChild实体从一个共同的Entity基类,这是通用实体基类的非通用版本从3.0 NHibernate的食谱第25页继承。这里是我的基类:

public class Entity : IEntity 
{ 
    public virtual Guid Id { get; private set; } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as Entity); 
    } 

    private static bool isTransient(Entity obj) 
    { 
     return obj != null && 
      Equals(obj.Id, Guid.Empty); 
    } 

    private Type getUnproxiedType() 
    { 
     return GetType(); 
    } 

    public virtual bool Equals(Entity other) 
    { 
     if (other == null) 
      return false; 

     if (ReferenceEquals(this, other)) 
      return true; 

     if (!isTransient(this) && 
      !isTransient(other) && 
      Equals(Id, other.Id)) 
     { 
      var otherType = other.getUnproxiedType(); 
      var thisType = getUnproxiedType(); 
      return thisType.IsAssignableFrom(otherType) || 
       otherType.IsAssignableFrom(thisType); 
     } 

     return false; 
    } 

    public override int GetHashCode() 
    { 
     if (Equals(Id, Guid.Empty)) 
      return base.GetHashCode(); 

     return Id.GetHashCode(); 
    } 

} 

经过进一步调查,我觉得这样的事情正在发生:

  1. 调用parent.AddChild(child)
  2. 保存到数据库中,这引起child.Id中产生
  3. 调用parent.RemoveChild(child)

......并且如下面所讨论的那样,这改变了GetHashCode()

这是在我的程序中的错误的结果 - 我本来是要重装步骤2和步骤之间parent 3.

不过,我认为有更多的东西基本上是错误的。

+0

出于好奇,你能不能用'私有Iesi.Collections.Generic.HashedSet 儿童进行测试=新Iesi.Collections.Generic.HashedSet ();' –

回答

3

为了使这个工作,我不得不改变我的Entity类'GetHashCode方法来懒散评估哈希码,但一旦计算,缓存结果,决不让它改变。这是我的新实现的GetHashCode

private int? requestedHashCode; 

    public override int GetHashCode() 
    { 
     if (!requestedHashCode.HasValue) 
     { 
      requestedHashCode = isTransient(this) 
       ? base.GetHashCode() 
       : this.Id.GetHashCode(); 
     } 
     return requestedHashCode.Value; 
    } 

为了更好地实施基础机构类,见AbstractEntity

3

我想不出任何其他的方式,这可能发生 - Iesi.Collections.Generic.HashedSet必须包含自己的Contains行为不同于我们的预期。

+0

寻找到它与ReSharper的,它只是围绕着一个包装标准'字典'和'Contains'调用'ContainsKey'。 –

+0

查看此[发布](http://stackoverflow.com/questions/2197678/dictionary-containskey-not-working-as-expected)关于Dictionary.ContainsKey – sq33G

2

如果Equals(Child)的实现方式与Equals(object)的重写不同,则可能发生这种情况。这将取决于Child的样子。

它可以发生的原因是Henk提到 - 是Parent散列码计算的一部分和相等,例如?如果是,则将Parent设置为null可能会将子项的哈希码更改为HashSet中记录的哈希码以外的哈希码。如果Parent不是等号/散列计算的一部分,虽然将可变类型放入散列集合或将其用作散列表中的关键字仍然有点奇怪。

+0

好点。两者都来自通用的'Entity'基类(从所有NHibernate文献推荐)。我已经发布了上面的基类。 –

3

Child是否覆盖object.Equals(object)并执行IEquatable<Child>?有可能收集所做的相等与您在代码示例的第二行调用的Equals方法不同。

相关问题