3

I have a class named Point that overloading "==" and "!=" operators to compare two Point object. How can I compare my Point object with "null", this a problem because when I call == or != operator with null a problem inside Equals method. Please open a console application and see what I want to say.How can I fix it.

public class Point
    {
        public int X { get; set; }
        public int Y { get; set; }


        public static bool operator == (Point p1,Point p2)
        {
            return p1.Equals(p2);
        }

        public static bool operator != (Point p1, Point p2)
        {
            return !p1.Equals(p2);
        }


        public override bool Equals(object obj)
        {
            Point other = obj as Point;

            //problem is here calling != operator and this operator calling  this method again
            if (other != null)
            {
                if (this.X == other.X && this.Y == other.Y)
                {
                    return true;
                }
                return false;
            }

            else
            {
                throw new Exception("Parameter is not a point");
            }
        }
    }

class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point { X = 9, Y = 7 };
            Point p2 = new Point { X = 5, Y = 1 };

            p1.X = p2.X;
            p1.Y = p2.Y;

            bool b1=p1==p2;

            Console.ReadKey();
        }
    }
erhan urun
  • 115
  • 9
  • Check for `null` in the operators and don't throw. Comparing a value against null is definitely *not* an exceptional case. – Panagiotis Kanavos Nov 06 '14 at 09:07
  • What error are you getting? Because in my eyes your code will cause an infinite loop? – Nunners Nov 06 '14 at 09:07
  • thanks the problem is solved by Lasse V. Karlsen – erhan urun Nov 06 '14 at 09:25
  • Pardon the shameless plug but since this question is so damned difficult I’ll just refer to my [existing answer](http://stackoverflow.com/a/104309/1968) which shows the One Right Way™ of implementing equality operators in .NET, and I’ll add that you should also strongly consider implementing the [`IEquatable` interface](http://msdn.microsoft.com/en-us/library/ms131187(v=vs.110).aspx) in your class: that’s what it’s there for, and people rudely ignore it. :-( Oh, and when overriding `Equals`, you **must** override `GetHashCode` as well. Otherwise chaos ensues. – Konrad Rudolph Nov 06 '14 at 10:04

4 Answers4

5

Use ReferenceEquals to check for null:

if (ReferenceEquals(other, null))

Having said that, Equals should generally not throw an exception if it encounters an unknown object type, it should just return false, thus here is the method I would write:

public override bool Equals(object obj)
{
    Point other = obj as Point;

    if (ReferenceEquals(other, null))
        return false;

    return (this.X == other.X && this.Y == other.Y);
}

An additional problem is that your operators will throw an exception if you compare null against something, as you can't invoke the instance method on a null reference.

Thus, here is the full class I would've written:

public class Point
{
    public int X { get; set; }
    public int Y { get; set; }


    public static bool operator == (Point p1,Point p2)
    {
        if (ReferenceEquals(p1, p2)) return true;
        if (ReferenceEquals(p1, null)) return false;
        return p1.Equals(p2);
    }

    public static bool operator != (Point p1, Point p2)
    {
        return !(p1 == p2);
    }

    public bool Equals(Point other)
    {
        if (ReferenceEquals(other, null))
            return false;

        return (this.X == other.X && this.Y == other.Y);
    }

    public override bool Equals(object obj)
    {
        Point other = obj as Point;
        if (ReferenceEquals(other, null))
            return false;
        return Equals(other);
    }
}
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
1

You should not throw an exception at:

        if (other != null)
        {
            ...
        }
        else
        {
            throw new Exception("Parameter is not a point");
        }

You can read http://msdn.microsoft.com/en-US/library/336aedhh%28v=vs.85%29.aspx to see how Equals() is properly implemented.

Thomas Lielacher
  • 1,037
  • 9
  • 20
  • 1
    It may be a bad idea to use other!= null, since you will use the overlaod for != . Better user ReferenceEquals – ShayD Nov 06 '14 at 09:12
  • There is no real advantage in using `ReferenceEquals(ref, null)` over `ref == null`. IMHO the second one is easier to read. Anyway, my intention was to give an answer, why the aksers `==` operator fails. Nevertheless you should stick to the guidelines from Microsoft to ensure `Equals` works as expected. – Thomas Lielacher Nov 06 '14 at 09:31
1

Handling equality in C# can be tricky and it's easy to fall into pit traps like you've encountered here.

I always follow the same pattern when dealing with equality, be it in value types or in reference types (except the null checking obviously): implement a static private helper method that takes care of all possibilities, and let all equality checks invoke this method.

So, in your case, I'd do the following:

UPDATE: Fixed a typo, equals should be private, not public.

 private static bool equals(Point p1, Point p2)
 {
     if (object.ReferenceEquals(p1, p2))
         return true;

     if (object.ReferenceEquals(p1, null) || object.ReferenceEquals(p2, null))
         return false;

     return p1.X == p2.X && p1.Y == p2.Y;
 }

Ok, so what are we doing here?

  1. We first check to see if both objects have equal references. If thats the case, then they have to be equal as they are the same object. Note that this takes care of the null == null case.

  2. Then we check to see if any of the two arguments is equal to null. If they are, then we know that p1 and p2 are not equal.

    To see if the arguments are equal to null we use the satic method bool object.ReferenceEquals(,) to avoid the issue you are having which is calling again your equality implementation.

  3. Last, we now know we have two non null Point arguments so we go ahead and implement the specific equality logic of two Point objects.

With this simple method we have dealt with all possible equality checks in our class. Now we simply have to invoke this method from all possible equality methods and operators:

public static bool operator ==(Point p1, Point p2)
{
    return Point.equals(p1, p2);
}

public static bool operator !=(Point p1, Point p2)
{
    return !Point.equals(p1, p2);
}

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

Additionaly, you should implement the IEquatable<Point> interface which is pretty straightforward. This is specially relevant when dealing with value types as you avoid boxing conversions when performing equality checks:

public bool Equals(Point p)
{
    return Point.equals(this, p);
}

And last but not least, you are overriding the Equals method and the == and != operators so you should also override int GetHashCode() in a way consistent with your equality implementation: if p1 == p2 then p1.GetHashCode() must equal p1.GetHashCode() (Note, that this does not mean that if p1 != p2, p1.GetHashCode() must not equal p2.GetHashCode()):

public override int GetHashCode()
{
    return this.X ^ this.Y;
}

Hope this small guideline helps.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Your solution is very good thanks. – erhan urun Nov 06 '14 at 09:33
  • The problem with this approach is that it implements `Equals(Object)` in terms of `Equals(Object, Object)` (well, actually, confusingly, `equals`), which is overridden. But the `Object` class in .NET is designed to handle this exactly the other way round: that is, the pre-defined static [`Object.Equals(Object, Object)`](http://msdn.microsoft.com/en-us/library/w4hkze5k%28v=vs.110%29.aspx) method is implemented in terms of `Object.Equals(Object)`, and works perfectly well when the latter is overridden. So that’s the route implementors should take. That way, they override one fewer method. – Konrad Rudolph Nov 06 '14 at 10:10
  • @KonradRudolph And where have I done something different? My static `equals(Point, Point)` is a *private implementation detail* of my class; I fail to see where the issue with `object.Equals(object, object)` is. Maybe I'm misunderstanding you. – InBetween Nov 06 '14 at 10:17
  • @KonradRudolph Ooops, now I see what you are saying. Thats a typo! `equals` should be private. Thanks for pointing it out. – InBetween Nov 06 '14 at 10:19
  • @InBetween Actually, I was mainly confused by `equals` vs `Equals`. However, your code still has one minor flaw, a performance bug: your `equals` tests whether the first argument is `null`. However, `Point.Equals(Object)` has already done that. implicitly. The test is thus redundant but your code still needs it for different code paths. This isn’t ideal. It’s arguably acceptable of course but I would personally avoid such redundancy in core functionality: equality comparison is potentially used a lot, in hot code paths. – Konrad Rudolph Nov 06 '14 at 10:21
  • @KonradRudolph True, that is a fair point, but unless I have good reasons that indicate that the few nanoseconds I waste performing in some equality checks an unecessary `null` check is not allowing my code meet my performance goals I prefer the simplicity of this approach where you have all the logic in one place and not sprinkled across several methods. – InBetween Nov 06 '14 at 10:25
  • @KonradRudolph I forgot to point out that I use this pattern mainly with `IComarable` where the amount of public entry points is rather big (around 10 if I remember correctly). In this case, having all the logic focused in one single method is handy unless you run into specific performance issues (to be honest, I have yet to find a case where this code is the performance bottleneck). – InBetween Nov 06 '14 at 10:44
  • @InBetween Of course you shouldn’t repeat the logic, I agree. The correct way to implement this with minimal code duplication (and `IEquatable`, which is the equality comparison equivalent of `IComparable`) is highlighted in an old answer I wrote: http://stackoverflow.com/a/104309/1968 – Konrad Rudolph Nov 06 '14 at 11:22
0

Maybe something like this:

public override bool Equals(object obj)
{
    if (obj != null && obj is Point)
    {
       Point other = (Point)obj;
       if (this.X == other.X && this.Y == other.Y)
       {
           return true;
       }
       return false;
    }
    return false;
}
Renatas M.
  • 11,694
  • 1
  • 43
  • 62