C# 如何为通过值进行比较的类编写一个好的GetHashCode()实现?
假设我们有这样一门课:C# 如何为通过值进行比较的类编写一个好的GetHashCode()实现?,c#,hash,gethashcode,consistent-hashing,C#,Hash,Gethashcode,Consistent Hashing,假设我们有这样一门课: class MyClass { public string SomeValue { get; set; } // ... } 现在,假设两个MyClass实例的SomeValue属性相等时,它们是相等的。因此,我覆盖了对象.Equals()和对象.GetHashCode()方法来表示它Object.GetHashCode()返回SomeValue.GetHashCode(),但同时我需要遵循以下规则: 如果一个对象的两个实例相等,则它们应返回相同的哈希
class MyClass
{
public string SomeValue { get; set; }
// ...
}
现在,假设两个MyClass
实例的SomeValue
属性相等时,它们是相等的。因此,我覆盖了对象.Equals()
和对象.GetHashCode()
方法来表示它Object.GetHashCode()
返回SomeValue.GetHashCode()
,但同时我需要遵循以下规则:
SomeValue
可以更改,我们之前得到的哈希代码可能会变得无效
我只能考虑使类不可变,但我想知道其他人在这种情况下会做什么
在这种情况下你会怎么做?拥有这样一个类是否代表了设计决策中更微妙的问题?总合同规定,如果a.equals(B)为真,那么它们的哈希代码必须相同。如果A中的某个值发生更改,导致A.equals(B)不再为true,则A.GetHashCode()可以返回与以前不同的值。可变对象无法缓存GetHashCode(),必须在每次调用该方法时计算它 本文提供了有关GetHashCode和可变性的详细指南:
总合同规定,如果A.equals(B)为真,则它们的哈希代码必须相同。如果A中的某个值发生更改,导致A.equals(B)不再为true,则A.GetHashCode()可以返回与以前不同的值。可变对象无法缓存GetHashCode(),必须在每次调用该方法时计算它 本文提供了有关GetHashCode和可变性的详细指南:
您需要在可变性和GetHashCode之间进行选择,GetHashCode为'equal'对象返回相同的值。通常,当您认为您想要为可变对象实现“相等”时,您最终决定您有“相等的阴影”,并且实际上并不意味着Object.Equals相等 在任何类型的数据结构中,将可变对象作为“键”对我来说都是一个巨大的危险信号。例如:
MyObj a = new MyObj("alpha");
MyObj b = new MyObj("beta");
HashSet<MyObj> objs = new HashSet<MyObj>();
objs.Add(a);
objs.Add(b);
// objs.Count == 2
b.SomeValue = "alpha";
// objs.Distinct().Count() == 1, objs.Count == 2
MyObj a=新的MyObj(“α”);
MyObj b=新的MyObj(“β”);
HashSet objs=新HashSet();
添加(a)项;
添加(b)项;
//objs.Count==2
b、 SomeValue=“alpha”;
//objs.Distinct().Count()==1,objs.Count==2
我们严重违反了HashSet的合同。这是一个明显的例子,有一些微妙的例子。您需要在易变性和为“equal”对象返回相同值的GetHashCode之间进行选择。通常,当您认为您想要为可变对象实现“相等”时,您最终决定您有“相等的阴影”,并且实际上并不意味着Object.Equals相等 在任何类型的数据结构中,将可变对象作为“键”对我来说都是一个巨大的危险信号。例如:
MyObj a = new MyObj("alpha");
MyObj b = new MyObj("beta");
HashSet<MyObj> objs = new HashSet<MyObj>();
objs.Add(a);
objs.Add(b);
// objs.Count == 2
b.SomeValue = "alpha";
// objs.Distinct().Count() == 1, objs.Count == 2
MyObj a=新的MyObj(“α”);
MyObj b=新的MyObj(“β”);
HashSet objs=新HashSet();
添加(a)项;
添加(b)项;
//objs.Count==2
b、 SomeValue=“alpha”;
//objs.Distinct().Count()==1,objs.Count==2
我们严重违反了HashSet的合同。这是一个明显的例子,有一些微妙的例子。如果您的GetHashCode()
依赖于某个可变值,那么每当值发生变化时,您就必须更改哈希值。否则你就违反了平等法
如果您将对象放入哈希集
或作为字典
中的键,则在有人请求哈希时,哈希就不应更改。在这些情况下,您必须确保哈希代码只要存储在这样的容器中就不会被更改。这可以手动确保,只需在编程时处理此问题,也可以为对象提供一些Freeze()
方法。如果调用此函数,则任何后续设置属性的尝试都会导致某种异常(然后还应提供一些Defrost()
方法)。此外,您还将Freeze()
方法的调用放入GetHashCode()
实现中,因此您可以非常确定没有人会错误地更改冻结的对象
还有最后一个提示:如果您需要更改这样一个容器中的对象,只需删除它,更改它(不要忘记解冻它),然后重新添加它。如果您的GetHashCode()
依赖于某个可变值,那么每当值发生更改时,您必须更改哈希值。否则你就违反了平等法
如果您将对象放入哈希集
或作为字典
中的键,则在有人请求哈希时,哈希就不应更改。在这些情况下,您必须确保哈希代码只要存储在这样的容器中就不会被更改。这可以手动确保,只需在编程时处理此问题,也可以为对象提供一些Freeze()
方法。如果调用此函数,则任何后续设置属性的尝试都会导致某种异常(然后还应提供一些Defrost()
方法)。此外,您还将Freeze()
方法的调用放入GetHashCode()
实现中,因此您可以非常确定没有人会错误地更改冻结的对象
还有最后一个提示:如果你需要改变这样一个容器中的一个对象,只需移除它,改变它(别忘了解冻),然后再次添加它。显然。。。但是如果我们在哈希表中放入一个,然后更改
SomeValue
属性,我们就会遇到麻烦,因为哈希代码现在不同了。我想知道解决这个问题的方法是什么。我能想到的一个方法是使用不变性。是的,这是对可变性的限制。例如,在Java中,Set的文档中说:“注意:如果对象是可变的,必须非常小心