views:

166

answers:

5

Hello, when I try to overload operator == and != in C#, and override Equal as recommended, I found I have no way to distinguish a normal object and null. For example, I defined a class Complex.

public static bool operator ==(Complex lhs, Complex rhs)
{
    return lhs.Equals(rhs);
}

public static bool operator !=(Complex lhs, Complex rhs)
{
    return !lhs.Equals(rhs);
}

public override bool Equals(object obj)
{
    if (obj is Complex)
    {
        return (((Complex)obj).Real == this.Real &&
                   ((Complex)obj).Imaginary == this.Imaginary);
    }
    else
    {
        return false;
    }
}

But when I want to use

if (temp == null)

When temp is really null, some exception happens. And I can't use == to determine whether the lhs is null, which will cause infinite loop.

What should I do in this situation.

One way I can think of is to us some thing like Class.Equal(object, object) (if it exists) to bypass the == when I do the check.

What is the normal way to solve the problem?

Thank you.

+10  A: 

You can use the following at the top of your Equals override:

if (Object.ReferenceEquals(obj, null))
    return false;

The exception you are getting is probably a StackOverflowException because your == operator will cause infinite recursion.

EDIT:

If Complex is a struct you should not have any problems with NullReferenceExceptions. If Complex is a class you can change your implementation of the == and != operator overloads to avoid the exception (Laurent Etiemble already pointed this out in his answer):

public static bool operator ==(Complex lhs, Complex rhs)
{
    return Equals(lhs, rhs);
}

public static bool operator !=(Complex lhs, Complex rhs)
{
    return !Equals(lhs, rhs);
} 
Jakob Christensen
Actually it's Object reference not set to an instance of an object.It should work, thanks. :)
LLS
The exception still exists, since null.Equals() is attempted to be called.
LLS
I'd consider making Complex a struct instead of a class.
Jakob Christensen
That's true. It just an example. And thanks anyway. (Actually Microsoft has already implemented Complex, so there is no need to create one in real life)
LLS
+3  A: 

You should consider using the static Equals method in the operator overloads (which will cal the instance Equals method):

public static bool operator ==(Complex lhs, Complex rhs)
{
    return Equals(lhs, rhs);
}

public static bool operator !=(Complex lhs, Complex rhs)
{
    return !Equals(lhs, rhs);
}

Note: You may also check for null in the Equals method.

You can also read the Object.Equals Topic on MSDN, which is a great source of samples.

Laurent Etiemble
Thanks, it works, but I'm not sure why.And, why can Object be omitted in Object.Equals()?And I didn't find why this can prevent the null problem in MSDN. (Does it check null first?)
LLS
You don't need the Object class prefix because Complex inherit from Object; Complex see the Object's static methods.I don't know the internals of the static Equals method, but it likely check for null before calling the instance Equals method.
Laurent Etiemble
If lhs is not null the static Equals calls the instance method Equals on lhs. If lhs is null Equals compares the references of lhs and rhs.
Jakob Christensen
Thank you. It seems to be an elegant solution.
LLS
A: 

There is a better approach then using operators is and cast:

Complex c = obj as Complex;
return (c != null) && (c.Real == this.Real) && (c.Imaginary == this.Imaginary);

Here is a quick test concerning Equals operator override and comparing with null:

class Complex
{
    public override bool Equals(object obj)
    {
        if (obj is Complex)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Debugging doesn't step into operator's body:

var b = (new Complex() == new Complex());
abatishchev
Thanks, but it seems that c != null will cause infinite loop.
LLS
@LLS: Really? Interesting. Not sure that comparing with `null` will use `object` comparing operator. But maybe..
abatishchev
@LLS: See my updated post
abatishchev
@abatishchev: I haven't tested it yet, but it was a caution in the text book. And I did encounter this problem when I tried to recursively call ==.
LLS
+3  A: 
public static bool operator ==(Complex lhs, Complex rhs)
{
    if (Object.ReferenceEquals(lhs, null))
    {
        return Object.ReferenceEquals(rhs, null);
    }

    return lhs.Equals(rhs);
}

public static bool operator !=(Complex lhs, Complex rhs)
{
    return !(lhs == rhs);
}

Poor man's unit test

Action<Complex, Complex> tester = (left, right) =>
{
    Console.WriteLine(left == right);
    Console.WriteLine(left != right);
    Console.WriteLine(left == null);
    Console.WriteLine(left != null);
    Console.WriteLine("---");
};

tester(new Complex(), new Complex());
tester(null, new Complex());
tester(null, null);
tester(new Complex(), null);
Anthony Pegram
Thanks a lot. It's easy to understand.
LLS
A: 

I think you shoud test for null in the == operator implementation. Otherwise, when lhs is null, you'd call Complex(null).Equals (I don't know for C#, but in Java this would be a Nullpointer Exception)

To test for null, I suggest something like:

if (null == lhs && null == rhs) return true
else if (null == lhs) return false
else return lhs.Equals(rhs);

So Object.Equals will be called for all == comparisons above.

nang
You get a recursive call to the == operator.
JonB
Ok, interesting. Just being curious, how comes that operator ==(Complex, Complex) is called, when I test for null == lhs? How does the compiler decide that "null" is of type Complex?
nang