重写GetHash()和Equals()

时间:2016-12-15 14:24:23

标签: c#

我无法覆盖GetHashCode()方法和Equals()方法。

public class Coordinate
{ 
   int x; 
   int y;

    public Coordinate(int p,int q)    
    { 
      this.x = p ;   
      this.y = q;
    }
 }

假设我创建了两个具有相同x和y坐标的坐标点对象。

我希望我的程序能够理解它们是平等的。

Coordinate Point 1 = new Coordinate(0,0);

Coordinate Point 2 = new Coordinate(0,0);

默认情况下,他们按预期提供不同的GetHashCode()。 我希望它们通过覆盖它来提供相同的哈希代码,然后使用该哈希代码作为Key从Dictionary生成值。在搜索之后,我知道我还必须覆盖Equals()

5 个答案:

答案 0 :(得分:0)

您必须覆盖Equals(),因为如果两个对象具有相同的哈希码,则并不意味着它们被视为相等。哈希码只是作为一个“索引”来加速搜索。

每次使用new时,都会创建一个实例,它与另一个实例不是同一个实例。这就是ReferenceEquals()检查 - 想象两瓶相同的苏打水 - 它们是相同的,但它们不是相同的

Equals()旨在检查(开发人员)是否要将两个实例视为相等,即使它们不是同一个实例。

答案 1 :(得分:0)

你可以用这种方式实现一些东西:

public override bool Equal(Object o) {
  if (object.ReferenceEquals(o, this))
    return true;

  Coordinate other = o as Coordinate;

  else if (null == other) 
    return false; 

  return x == other.x && y == other.y;
}

public override int GetHashCode() {
  return x.GetHashCode() ^ y.GetHashCode();
}

其中Equals返回true当且仅当实例相等而GetHashCode()执行快速估算(如果实例具有不同的哈希码,则实例不相等但是,相反的情况并非如此)并且尽可能确保散列的均匀分布(因此在Dictionary和类似的结构中,每个键的值大致相等)

https://msdn.microsoft.com/en-us/library/336aedhh(v=vs.100).aspx

https://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=vs.110).aspx

答案 2 :(得分:0)

我会覆盖这样的命名方法。对于GetHashCode方法,我从this question中选择了其中一个选项,但如果您愿意,可以选择其他选项。

我还将类更改为不可变。您应该只使用不可变属性/字段来计算哈希码。

public class Coordinate { 
    public Coordinate(int p, int q) {
        x = p;
        y = q;
    }

    private readonly int x; 
    private readonly int y;

    public int X { get { return x; } }
    public int Y { get { return y; } }

    public override int GetHashCode() {
        unchecked // Overflow is fine, just wrap
        {
            int hash = (int) 2166136261;
            // Suitable nullity checks etc, of course :)
            hash = (hash * 16777619) ^ x.GetHashCode();
            hash = (hash * 16777619) ^ y.GetHashCode();
            return hash;
        }       
    }

    public override bool Equals(object obj) {
        if (obj == null)
            return false;

        var otherCoordinate = obj as Coordinate;
        if (otherCoordinate == null)
            return false;

        return
            this.X == otherCoordinate.X &&
            this.Y == otherCoordinate.Y;        
    }
 }

答案 3 :(得分:0)

当您拥有不可变对象时,通常会发生您尝试做的事情,无论如何,如果您不想使用struct,您可以这样做:

    public class Coord : IEquatable<Coord>
    {
        public Coord(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public int X { get; }
        public int Y { get; }
        public override int GetHashCode()
        {
            object.Equals("a", "b");
            // Just pick numbers that are prime between them
            int hash = 17;
            hash = hash * 23 + this.X.GetHashCode();
            hash = hash * 23 + this.Y.GetHashCode();
            return hash;
        }

        public override bool Equals(object obj)
        {
            var casted = obj as Coord;
            if (object.ReferenceEquals(this, casted))
            {
                return true;
            }
            return this.Equals(casted);
        }

        public static bool operator !=(Coord first, Coord second)
        {
            return !(first == second);
        }

        public static bool operator ==(Coord first, Coord second)
        {
            if (object.ReferenceEquals(second, null))
            {
                if (object.ReferenceEquals(first, null))
                {
                    return true;
                }
                return false;
            }
            return first.Equals(second);
        }

        public bool Equals(Coord other)
        {
            if (object.ReferenceEquals(other, null))
            {
                return false;
            }
            return object.ReferenceEquals(this, other) || (this.X.Equals(other.X) && this.Y.Equals(other.Y));
        }
    }

注意。如果您使用自定义相等,那么真的应该使您的类不可变,因为如果您使用基于哈希的集合,它可能会破坏您的代码。

我认为,当您想要像您一样进行自定义相等检查时,执行所有这些重载被认为是一种好习惯。特别是当object.GetHashCode()为两个对象返回相同的值时,Dictionary和其他基于散列的集合使用使用object.Equals的默认相等运算符。

Object.ReferenceEquals(Ob,Ob)确定引用相等,a.k.a如果两个引用指向相同的分配值,则两个引用相等,确保它是完全相同的对象。

Object.Equals(Ob)object类中的虚拟方法,默认情况下它会像Object.ReferenceEquals(Ob,Ob)

那样比较引用

Object.Equals(Ob,Ob)调用Ob.Equals(Ob),所以是的,只是在IIRC之前检查null的静态速记。

答案 4 :(得分:0)

这是一个简单的方法。

首先,将类的ToString()方法覆盖为:

public override string ToString()
{
    return string.Format("[{0}, {1}]", this.x, this.y);
}

现在您可以轻松覆盖GetHashCode()Equals(),如下所示:

public override int GetHashCode()
{
    return this.ToString().GetHashCode();
}

public override bool Equals(object obj)
{
    return obj.ToString() == this.ToString();
}

现在,如果你试试这个:

Coordinate p1 = new Coordinate(5, 0);
Coordinate p2 = new Coordinate(5, 0);

Console.WriteLine(p1.Equals(p2));

你会得到: