2

假设我们有这样一个类:

class MyClass
{
    public string SomeValue { get; set; }

    // ...
}

现在,假设两个MyClass实例在它们的SomeValue属性相等时是相等的。因此,我覆盖了Object.Equals()Object.GetHashCode()表示它的方法。Object.GetHashCode()返回SomeValue.GetHashCode()但同时我需要遵循以下规则:

  1. 如果一个对象的两个实例相等,它们应该返回相同的哈希码。
  2. 哈希码不应在整个运行时更改。

但显然,SomeValue可以改变,我们之前得到的哈希码可能会失效。

我只能考虑使该类不可变,但我想知道其他人在这种情况下会做什么。

在这种情况下你会怎么做?拥有这样一个类是否代表了设计决策中的一个更微妙的问题?

4

3 回答 3

2

通用合约规定,如果 A.equals(B) 为真,那么它们的哈希码必须相同。如果 A 中的 SomeValue 以 A.equals(B) 不再为真的方式发生变化,则 A.GetHashCode() 可以返回与以前不同的值。可变对象不能缓存GetHashCode(),每次调用方法都必须计算。

这篇文章有关于 GetHashCode 和可变性的详细指南:

http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

于 2012-12-07T01:10:27.107 回答
2

如果您GetHashCode()依赖于某个可变值,则必须在值更改时更改哈希值。否则你违反平等法则。

HashSet如果您将对象放入 a或作为 a 中的键,则需要一旦有人要求散列就永远不应该更改的部分Dictionary。在这些情况下,您必须确保哈希码不会被更改,只要它存储在这样的容器中。这可以通过在编程时简单地处理这个问题来手动确保,或者您可以为Freeze()您的对象提供一些方法。如果这被调用,任何后续尝试设置属性都会导致某种异常(你也应该提供一些Defrost()方法)。此外,您将Freeze()方法的调用放入您的GetHashCode()实现中,因此您可以非常确定没有人会错误地更改冻结的对象。

最后一个提示:如果您需要更改此类容器中的对象,只需将其移除、更改(不要忘记解冻)并重新添加。

于 2012-12-07T16:04:16.017 回答
1

您需要在可变性和 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

我们严重违反了 的合同HashSet<T>。这是一个明显的例子,也有微妙的例子。

于 2012-12-07T01:33:03.687 回答