[英]HashSet<T>.RemoveWhere() and GetHashCode()
阿罗哈,
这是一个覆盖GetHashCode的简单类:
class OverridesGetHashCode
{
public string Text { get; set; }
public override int GetHashCode()
{
return (Text != null ? Text.GetHashCode() : 0);
}
// overriding Equals() doesn't change anything, so I'll leave it out for brevity
}
当我创建该类的实例时,将其添加到HashSet然后更改其Text属性,如下所示:
var hashset = new HashSet<OverridesGetHashCode>();
var oghc = new OverridesGetHashCode { Text = "1" };
hashset.Add(oghc);
oghc.Text = "2";
那么这不起作用:
var removedCount = hashset.RemoveWhere(c => ReferenceEquals(c, oghc));
// fails, nothing is removed
Assert.IsTrue(removedCount == 1);
这两个都没有:
// this line works, i.e. it does find a single item matching the predicate
var existing = hashset.Single(c => ReferenceEquals(c, oghc));
// but this fails; nothing is removed again
var removed = hashset.Remove(existing);
Assert.IsTrue(removed);
我猜它内部使用的哈希是在插入项时生成的,如果这是真的,那么hashset.Contains(oghc)不起作用是可以理解的。 我也猜测它通过哈希码查找项目,如果找到匹配,那么它只检查谓词,这可能是第一次测试失败的原因(再次,我只是在这里猜测)。 但是为什么最后一次测试失败了,我只是从hashset中得到了那个对象? 我错过了什么,这是从HashSet中删除某些内容的错误方法吗?
感谢您抽出时间来阅读。
更新:为避免混淆,这里是Equals():
protected bool Equals(OverridesGetHashCode other)
{
return string.Equals(Text, other.Text);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((OverridesGetHashCode) obj);
}
通过在HashSet
中使用该对象时更改对象的哈希码是违反HashSet
的契约。
无法移除对象不是问题所在。 您不能首先更改哈希码。
让我引用MSDN :
只要没有对对象状态的修改来确定对象的Equals方法的返回值,对象的GetHashCode方法必须始终返回相同的哈希代码。 请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。
他们讲的故事略有不同,但实质是一样的。 他们说,哈希码永远不会改变。 实际上,只要确保没有人再使用旧的哈希码,您就可以对其进行更改。 并不是说这是好的做法,但它确实有效。
这里有很好的答案,只是想添加这个。 如果查看反编译的HashSet<T>
代码,您将看到Add(value)
执行以下操作:
IEqualityComparer<T>.GetHashCode()
来获取值的哈希码。 对于默认的比较器,这归结为GetHashCode()
。 当您调用Remove(value)
它会执行步骤1.和2.再次查找引用所在的位置。 然后它调用IEqualityComparer<T>.Equals()
以确保它确实找到了正确的值。 但是,由于您已更改GetHashCode()
返回的内容,因此它会计算不同的存储桶/插槽位置,这是无效的。 因此,它找不到对象。
所以,请注意, Equals()
并没有真正发挥作用,因为如果哈希码发生变化,它甚至永远不会到达正确的桶/槽位置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.