【问题标题】:How should I override Equals and GetHashCode for HashSet?我应该如何覆盖 HashSet 的 Equals 和 GetHashCode?
【发布时间】:2014-01-03 22:44:26
【问题描述】:

假设我有课:

    public class Ident
    {
        public String Name { get; set; }
        public String SName { get; set; }
    }

还有一个:

    class IdenNode
    {
        public Ident id { get; set; }
        public List<IdenNode> Nodes { get; set; }

        public IdenNode()
        {
            Nodes = new List<IdenNode>();
        }
    }

我想使用 HashSet&lt;IdenNode&gt; 时要记住,当且仅当它们的 id.Names 相等时,它的两个元素是相同的(相等)。

所以,我将覆盖 EqualsGetHashCode,如下所示:

        public override bool Equals(object obj)
        {
            IdenNode otherNode = obj as IdenNode;

            return otherNode != null && 
                   otherNode.id != null && 
                   id.Name == otherNode.id.Name;
        }

        public override int GetHashCode()
        {
            if (id != null)
                return id.Name.GetHashCode();
            else
                // what should I write here?
        }

我觉得对吗?如果是,我应该在GetHashCode 中放置什么?

更新

能否告诉我可以Equals 方法中使用==!= 吗?或者可能是ReferenceEquals 或其他?

另外,我应该覆盖运算符==!= 吗?

【问题讨论】:

  • 字符串用作标识符、区分大小写等是一件很糟糕的事情。您不能使用 int 或 GUID 之类的东西(是的,它是一个字符串,但它保证对您的系统来说是唯一的)
  • @Maess 提出了一个很好的观点。如果两个人同名同姓怎么办?
  • @Maess 一个 GUID 不是字符串! :)
  • @Moo-Juice 你是对的,我在考虑数据库,有时它是一个字符串。
  • 你不应该覆盖任何东西。使用相等比较器创建 HashSet(保持问题本地化)。如果你坚持,至少用private set;替换那些set;

标签: c# hash equals hashset


【解决方案1】:

如果id(或id.Name)为空,则返回0 是完全可以的。Nullable&lt;T&gt;(如int?)为“空”值返回0。

请记住,从GetHashCode() 返回相同值的两个对象并不意味着相等 - 它仅意味着两个对象可能相等。然而,翻转是两个“相等”的对象必须返回相同的哈希码。您对EqualsGetHashCode 的定义似乎满足了这两个原则

【讨论】:

  • 明白你关于返回 0 的意思。你能告诉我是否也应该覆盖运算符“==”和“!=”吗?
  • @mehow 我应该澄清“可空类型”是指Nullable&lt;T&gt;Reference 类型不返回 anything,因为您不能在空引用变量上调用 GetHashCode
  • @DStanley 完全有效的答案我只是在想一个可空类型的ToString() 方法。我的坏 -> +1 给你
【解决方案2】:

当心空值!你有很多。注意StackOverflow:在Equals 方法中尝试不使用 == 和!=。通常,我们在 null 的情况下返回 0 作为哈希码,例如:

public override bool Equals(object obj) {
  // Often we should compare an instance with itself, 
  // so let's have a special case for it (optimization)
  if (Object.ReferenceEquals(obj, this)) 
    return true;

  IdenNode other = obj as IdenNode;

  // otherNode != null line in your code can cause StackOverflow:
  // "!=" calls "Equals" which in turn calls "!=" etc...
  if (Object.ReferenceEquals(null, other))
    return false;

  // Id can be null
  if (Object.ReferenceEquals(id, other.id))
    return true;
  else if (Object.ReferenceEquals(id, null) || Object.ReferenceEquals(other.id, null))
    return false;

  // Let's be exact when comparing strings:
  // i.e. should we use current locale or not etc
  return String.Equals(id.Name, other.id.Name, StringComparison.Ordinal);
}

public override int GetHashCode() {
  // It's typical to return 0 in case of null
  if (Object.ReferenceEquals(null, id))
    return 0;
  else if (Object.ReferenceEquals(null, id.Name)) // <- Name can be null as well!
    return 0;

  return id.Name.GetHashCode();
}

【讨论】:

  • 明智的防御机制+1。
  • 非常感谢,这看起来就是我想要的。你能告诉我是否应该在我的情况下覆盖“==”和“!=”?
  • @user2706838:你不需要覆盖它们,但是,恕我直言,你最好这样做
【解决方案3】:

如果是,我应该在 GetHashCode 中放置什么?

返回零很好。请注意,在名称上定义值相等是一个坏主意;我知道美国至少还有另外三个Eric Lipperts,他们不是我。确实有数百万甚至数十亿人发生了名字冲突。

能否告诉我在 Equals 方法中使用“==”和“!=”可以吗?或者可能是 ReferenceEquals 或其他?

我的建议是:在混合引用和值相等时,要非常清楚。如果您打算引用相等,请说出来。

另外,我应该覆盖运算符“==”和“!=”吗?

是的。 Equals 表示一件事而 == 表示另一件事令人困惑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    • 1970-01-01
    相关资源
    最近更新 更多