1

I am trying to implement an immutable Point class where two Point instances are considered equal if they have the same Coordinates. I am using Jon Skeet's implementation of a Coordinate value type.

For comparing equality of Points I have also inherited EqualityComparer<Point> and IEquatable<Point> and I have a unit test as below:

Point.cs:

public class Point : EqualityCompararer<Point>, IEquatable<Point>
{
    public Coordinate Coordinate { get; private set; }

    // EqualityCompararer<Point>, IEquatable<Point> methods and other methods
}

PointTests.cs:

[Fact]
public void PointReferencesToSamePortalAreNotEqual()
{
    var point1 = new Point(22.0, 24.0);
    var point2 = new Point(22.0, 24.0);

    // Value equality should return true
    Assert.Equal(point1, point2);

    // Reference equality should return false
    Assert.False(point1 == point2);
}

Now I am really confused by the 3 interface/abstract methods that I must implement. These are:

  • IEquatable<Point>.Equals(Point other)
  • EqualityComparer<Point>.Equals(Point x, Point y)
  • EqualityComparer<Point>.GetHashCode(Point obj)

And since I have overriden IEquatable<Point>.Equals, according to MSDN I must also implement:

  • Object.Equals(object obj)
  • Object.GetHashCode(object obj)

Now I am really confused about all the Equals and GetHashCode methods that are required to satisfy my unit test (Reference equality should return false and value equality should return true for point1 and point2).

Can anyone explain a bit further about Equals and GetHashCode?

Community
  • 1
  • 1
rexcfnghk
  • 14,435
  • 1
  • 30
  • 57
  • Easy way to explain the relationship: if `a.Equals(b) == true` then `a.GetHashCode() == b.GetHashCode()`, if `a.Equals(b) == false` then `a.GetHashCode() == b.GetHashCode() || a.GetHashCode() != b.GetHashCode()` – Scott Chamberlain Nov 07 '13 at 07:02
  • This I understand, but my question is why the interfaces / abstract classes? How should I implement them to keep them in line? – rexcfnghk Nov 07 '13 at 07:03
  • 1
    You don't need to implement `IEqualityComparer`, that is only for external classes to be passed in as a special case comparer. A good example: the [various string comparers](http://msdn.microsoft.com/en-us/library/system.stringcomparer%28v=vs.110%29.aspx) for things like ignoring case. `IEquatable` is so you don't need to box your struct inside a `object` to do the comparisons, if you are using a class instead of a struct you don't really need IEquatable either. – Scott Chamberlain Nov 07 '13 at 07:06
  • @ScottChamberlain: I implemented `EqualityComparer` because I used collections in my other classes consuming `Point` (e.g. when I call `IEnuermable.Distinct()` it will use my comparer instead of the default one) – rexcfnghk Nov 07 '13 at 07:13
  • What you do is make the default behave the way you want. EqualityComparer is only when you want non default behavior for comparisons. – Scott Chamberlain Nov 07 '13 at 07:18

2 Answers2

1

Because Coordinate already implments GetHashCode() and Equals(Coordinate) for you it is actually quite easy, just use the underlying implmentation

public class Point : IEquatable<Point>
{
    public Coordinate Coordinate { get; private set; }

    public override int GetHashCode()
    {
        return Coordinate.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as Point);
    }

    public bool Equals(Point point)
    {
        if(point == null)
            return false;

        return this.Coordinate.Equals(point.Coordinate);
    }
}

the IEquatable<Point> is unnecessary as all it does is save you a extra cast. It is mainly for struct type classes to prevent the boxing of the struct in to the object passed in to bool Equals(object).

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • However when I have an `IEnumerable` and call `Distinct` on it, it returns duplicated elements where the `Coordinate`s are equal. Do I need to use `EqualityComparer` in this case? – rexcfnghk Nov 07 '13 at 08:06
  • Never mind. Figured out myself. You are right. Accepted as answer. – rexcfnghk Nov 07 '13 at 12:07
0

Equals: Used to check if two objects are equal. There are several checks for equality (by value, by reference), and you really want to have a look at the link to see how they work, and the pitfalls when you don't know who is overriding them how.

GetHashCode:
A hash code is a numeric value that is used to insert and identify an object in a hash-based collection such as the Dictionary class, the Hashtable class, or a type derived from the DictionaryBase class. The GetHashCode method provides this hash code for algorithms that need quick checks of object equality.

Let's assume you're having two huge objects with heaps of objects inside, and that comparing them might take a very long time. And then you have a collection of those objects, and you need to compare them all. As the definitions say, GetHashCode will return a simple number you can compare if you don't want to compare the two objects. (and assuming you implemented them correctly, two different objects will not have the same hashcode, while objects who are supposed to be "equal" will).

And if you want Jon Skeet's opinion on something similar, look here.

Community
  • 1
  • 1
Noctis
  • 11,507
  • 3
  • 43
  • 82