包含方法如何返回 false,但 GetHashCode() 返回相同的数字,而 Equals 返回 true?

4

我有一个实体类,像这样(省略了很多内容):

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,该基类是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();
    }

}

经过进一步调查,我感觉类似以下情况正在发生:
  1. 调用 parent.AddChild(child)
  2. 保存到数据库,导致生成了 child.Id
  3. 调用 parent.RemoveChild(child)
...正如下面讨论的那样,这会改变 GetHashCode()
这是我的程序中的一个错误 - 我应该在步骤2和3之间重新加载 parent
尽管如此,我认为还有更根本性的问题。

出于好奇,你能否测试一下这个代码:private Iesi.Collections.Generic.HashedSet<Child> children = new Iesi.Collections.Generic.HashedSet<Child>(); - L.B
4个回答

3

我想不出其他的方式,这种情况只能是发生了这种情况 - Iesi.Collections.Generic.HashedSet 必须包含其自己的 Contains 方法,而该方法的行为与我们预期的不同。


通过ReSharper查看,它只是一个标准的Dictionary<T, object>的包装器,并且Contains调用了ContainsKey - Scott Whitlock
请参考这篇关于 Dictionary.ContainsKey 的帖子。 - sq33G

3

Child是否同时重写了object.Equals(object)并实现了IEquatable<Child>接口?集合所做的相等性判断可能与您在代码示例的第二行调用的Equals方法不同。


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

2
这可能是由于Equals(Child)Equals(object)的覆盖实现不同所致。这将取决于Child的具体情况。
同时,正如Henk提到的影响,也可能由于Parent是否在哈希码和相等性的计算中起作用。如果是这样,将Parent设置为null可能会改变子项的哈希码,使其与HashSet中记录的哈希码不同。如果Parent不是相等性/哈希计算的一部分,则没有问题,尽管将可变类型放入哈希集合或将其用作哈希表中的键仍然有点奇怪。

好的地方。两者都派生自一个共同的“Entity”基类(来自所有NHibernate文献的推荐)。我已经在上面发布了基类。 - Scott Whitlock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接