9

Possible Duplicate:
How do I check for nulls in an ‘==’ operator overload without infinite recursion?

I have an object that looks like this:

public class Tags
{
   int mask;

   public static bool operator !=(Tags x, Tags y)
   {
      return !(x == y);
   }

   public static bool operator ==(Tags x, Tags y)
   {
      return x.mask == y.mask;
   }
}

This works fine for comparing instances to each other, but I also want to be able to handle an expression such as:

if(tags1 == null)

Doing this causes an exception on the following line:

return x.mask == y.mask;

Since y is null.

I've tried changing this function to:

public static bool operator ==(Tags x, Tags y)
{
   if (x == null && y == null) return true;
   if (x == null || y == null) return false;

   return x.mask == y.mask;
}

However, this creates a stack overflow since the implementation uses its own overridden comparison operator.

What's the trick to getting the == operator to handle comparisons to null? Thanks!

Community
  • 1
  • 1
Mike Christensen
  • 88,082
  • 50
  • 208
  • 326
  • 1
    See this [question][1]. [1]: http://stackoverflow.com/questions/73713/how-do-i-check-for-nulls-in-an-operator-overload-without-infinite-recursion – Rohan West Jan 30 '12 at 04:14
  • @MichaelLiu: Exactly what do you think is a bug? – Ben Voigt Jan 30 '12 at 04:23
  • Alas, I misread the answers. No bug. I'll delete my comment. – Michael Liu Jan 30 '12 at 04:26
  • @KonradRudolph its more a duplicate of Rohan West's link.. – nawfal Dec 16 '12 at 08:10
  • @nawfal I don’t agree. The *narrow* question is answered in Rohan’s link but the wider question – how to implement equality checking semantically correctly – is answered extensively in the other question. – Konrad Rudolph Dec 16 '12 at 13:12
  • @KonradRudolph Yes but the question asked by OP is that narrow. It wasn't more about a general implementation, but something very specific. True he would get the correct info from both links, but Rohan's link is the exact duplicate. – nawfal Dec 16 '12 at 18:49
  • @nawfal Our job here isn’t to answer questions – it’s to *help*. By just answering the narrow question you are providing questionable help at best, misleading information at worst. I strongly disagree that we should aim for this. See also the [discussion on the XY problem](http://meta.stackexchange.com/q/66377/1968) on meta.SO. What you are suggesting is discouraged on SO. – Konrad Rudolph Dec 16 '12 at 19:33
  • @KonradRudolph my first comment wasn't to say that the link you posted wasn't appropriate, not at all. It gives the OP the idea/help/answer/knowledge. But when closing a question its more appropriate to close with the exact duplicate, and in that regard I said Rohan's link is more appropriate. If not, would you try to close the narrow question in Rohan's link as well with your wider question since the latter answers/helps its OP better? No I guess. Thats the point. My comment wasn't a dig at you, just saying more appropriate link for _subsequent SO procedure_ is Rohan's.. – nawfal Dec 16 '12 at 19:41

5 Answers5

12

According to guideline:

public static bool operator ==(Tags a, Tags b)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(a, b))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)a == null) || ((object)b == null))
    {
        return false;
    }

    // Return true if the fields match:
    return a.mask == b.mask;
}
Kirill Polishchuk
  • 54,804
  • 11
  • 122
  • 125
  • I think so far I like this one the best.. And it works perfectly. – Mike Christensen Jan 30 '12 at 04:29
  • 3
    +1 for digging up the official guideline. – Heinzi Jan 30 '12 at 08:08
  • 1
    Unfortunately, this is **not good code** because it means that this operation is either inconsistent with whatever `Equals` returns, or has a duplicate implementation in `Equals`. To avoid this, **always call `Equals`** in an implementation of `operator ==`, and think of implementing `GetHashCode` as well. Don’t get creative, always [use a correct template](http://stackoverflow.com/a/104309/1968) – Konrad Rudolph Jan 31 '12 at 10:18
  • @KonradRudolph: Option 3: Call `operator ==` from `Equals`? – recursive Feb 03 '12 at 16:54
  • @recursive no call `Equals` from `==` – nawfal Dec 16 '12 at 06:54
6

Instead of x == null, you could use (object)x == null or Object.ReferenceEquals(x, null):

public static bool operator ==(Tags x, Tags y)
{
    if ((object)x == null && (object)y == null) return true;
    if ((object)x == null || (object)y == null) return false;

    return x.mask == y.mask;
}

But you should also implement Equals and GetHashCode:

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

public bool Equals(Tags tags)
{
    return (object)tags != null && this.mask == tags.mask;
}

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

Now operator == can be written simply:

public static bool operator ==(Tags x, Tags y)
{
    return (object)x != null ? x.Equals(y) : (object)y == null;
}
Michael Liu
  • 52,147
  • 13
  • 117
  • 150
1

Kirill gave already a good answer, but i use some kind of improved schema for implementation of the operators including a correct implementation of Equals() and GetHashCode() in the following way:

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

public static bool operator ==(MyType first, MyType second)
{
    if (ReferenceEquals(first, null)
       || ReferenceEquals(second, null))
    {
        return ReferenceEquals(first, second);
    }

    return first.Equals(second);
}

public override bool Equals(object obj)
{
    return Equals(obj as MyType);
}

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

    if (ReferenceEquals(this, other))
    {
        return true;
    }

    // Check all simple properties
    if (GetHashCode() != other.GetHashCode()
        || Name != other.Name
        || Age != other.Age
        || Phone != other.Phone)
    {
        return false;
    }

    return true;
}

public override int GetHashCode()
{
    unchecked
    {
        var hash = 34591;

        if (Name != null)
        {
            hash = hash * 29863 + Name.GetHashCode();
        }

        if (Phone != null)
        {
            hash = hash * 29863 + Phone.GetHashCode();
        }

        hash = hash * 29863 + Age;

        return hash;
    }
 }
Oliver
  • 43,366
  • 8
  • 94
  • 151
0

See this question. Uses Object.ReferenceEquals.

Community
  • 1
  • 1
Rohan West
  • 9,262
  • 3
  • 37
  • 64
  • @Mike: If the first argument is null, it exits early. It may call `x.Equals(y)` with `y == null`, but `Equals` needs to handle that properly anyway. – Ben Voigt Jan 30 '12 at 04:25
-1

If you can think of not using operators but methods instead, cant you define your own extension method. Like this.

public static bool IsEqual(this Tags a, Tags b) 
{
    if(a == null && b == null)
       return true;
    if((a != null && b == null) || (a == null && b != null))
       return false;
    return b.mask == this.mask
}