阿罗哈
这是一个覆盖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);
我猜它内部使用的哈希值是在插入item时生成的,如果是真的,那就是
可以理解,hashset.Contains(oghc)不起作用。
我还猜测它会通过其哈希代码查找项目,如果找到匹配项,则它只会检查谓词,这可能就是为什么第一个测试失败的原因(再次,我只是在这里猜测)。
但是为什么最后一次测试失败了,我只是将该对象从哈希集中删除了?我是否缺少某些东西,这是从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<T>
代码,您会看到Add(value)
执行以下操作:
IEqualityComparer<T>.GetHashCode()
获取值的哈希码。对于默认比较器,这可以归结为GetHashCode()
。 当您调用
Remove(value)
时,它再次执行步骤1.和2.,以查找引用所在的位置。然后,它调用 IEqualityComparer<T>.Equals()
以确保它确实找到了正确的值。但是,由于您更改了GetHashCode()
返回的内容,因此它将计算出不同的存储桶/插槽位置,这是无效的。因此,它找不到对象。因此,请注意
Equals()
在这里并没有真正发挥作用,因为如果哈希码发生更改,它甚至都不会到达正确的存储区/插槽位置。关于c# - HashSet <T> .RemoveWhere()和GetHashCode(),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11848558/