为什么Contains返回false,而GetHashCode()返回相同的数字,而Equals返回true ?
本文关键字:返回 数字 Equals true false Contains GetHashCode 为什么 | 更新日期: 2023-09-27 18:14:37
我有一个这样的实体类(有很多东西丢失):
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?
我的Parent
和Child
实体都继承自一个通用的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();
}
}
经过进一步的调查,我觉得发生了这样的事情:
- 呼叫
parent.AddChild(child)
- 保存到数据库,导致
child.Id
生成 - 呼叫
parent.RemoveChild(child)
…正如下面所讨论的,这正在改变GetHashCode()
。
这是我的程序中的一个错误的结果-我应该在步骤2和3之间重新加载parent
。
我想不出这可能发生的任何其他方式- Iesi.Collections.Generic.HashedSet
必须包含自己的Contains
,其行为与我们期望的不同。
Child
是否都覆盖object.Equals(object)
并实现IEquatable<Child>
?这是可能的,相等的集合正在做的是不一样的Equals
方法你调用的第二行代码示例。
要使其工作,我必须更改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。
如果Equals(Child)
的实现与Equals(object)
的覆盖不同,则可能发生这种情况。这真的取决于Child
的样子。
它可以也可以发生由于Henk提到的影响- Parent
是计算哈希码和等式的一部分,例如?如果是这样,将Parent
设置为null
可能会改变子节点的哈希码,而不是记录在HashSet
中的哈希码。如果Parent
不是相等/哈希计算的一部分,这不是问题,尽管将可变类型放入哈希集或将其用作哈希表中的键仍然有些奇怪。