为什么 Contains 返回 false,但 GetHashCode() 返回相同的数字,而 Equals 返回 true?

Sco*_*ock 5 c# nhibernate contains equals gethashcode

我有一个像这样的实体类(缺少很多东西):

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试删除子项时,该if语句的计算结果为false。因此,我在if语句上放置了一个断点,并计算了某些表达式:

this.children.Contains(child) => false
this.children.ToList()[0].Equals(child) => true
this.children.ToList()[0].GetHashCode() => 1095838920
child.GetHashCode() => 1095838920
Run Code Online (Sandbox Code Playgroud)

我的理解是,如果GetHashCode返回相同的值,则会检查Equals. 为什么Contains返回错误?


myParentChild实体都继承自一个公共Entity基类,该基类是 NHibernate 3.0 Cookbook 第 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();
    }

}
Run Code Online (Sandbox Code Playgroud)

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

  1. 呼唤parent.AddChild(child)
  2. 保存到数据库,导致child.Id生成
  3. 呼唤parent.RemoveChild(child)

...正如下面所讨论的,这种情况正在发生变化GetHashCode()

这是我的程序中的一个错误造成的 - 我应该parent在步骤 2 和 3 之间重新加载。

不过,我认为还有一些更根本的错误。

Sco*_*ock 4

为了让它工作,我必须改变我的Entity类的GetHashCode方法来延迟计算哈希码,但一旦计算出来,缓存结果并且永远不会让它改变。这是我的新实现GetHashCode

    private int? requestedHashCode;

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

有关基实体类的更好实现,请参阅AbstractEntity