我应该如何为HashSet重写Equals和GetHashCode?

时间:2013-12-16 13:57:14

标签: c# hash equals hashset

让我说我上课了:

    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<IdenNode>,当它的id.Names为Equal时,它的两个元素是相同的(Equal)。

所以,我要覆盖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或其他?

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

3 个答案:

答案 0 :(得分:7)

如果id(或id.Name)为null,则返回0完全正常。Nullable<T>(如int?)为“null”值返回0。

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

的定义来实现的

答案 1 :(得分:4)

小心 nulls !你有很多这些。照顾 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();
}

答案 2 :(得分:2)

  

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

归零是好的。请注意,在名称上定义值相等是一个坏主意;我知道美国至少有三个Eric Lippert,而且他们不是我。有数百万,可能是数十亿的名字发生碰撞的人。

  

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

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

  

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

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