简体   繁体   中英

How to perform a null check in an equality operator overload

I've written a simple class that wraps an XElement. I want to pass through equality operations to the wrapped instance. So I wrote this:

public class XmlWrapper
{
    protected readonly XElement _element;

    public XmlWrapper(XElement element)
    {
        _element = element;
    }

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

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

This seems straightforward, but actually it throws an exception for a very simple null check:

    XmlWrapper wrapper = new XmlWrapper(someElement);
    XmlWrapper nullWrapper = null;

    if (wrapper != nullWrapper) Console.WriteLine("Wrapper is not null"); //This line throws

It's because the equality operator is receiving null for one of its arguments and therefore can't retrieve the wrapped XElement. So the obvious thought is to add a null check, like this:

static public bool operator == (XmlWrapper lhs, XmlWrapper rhs)
{
    if (lhs == null && rhs == null) return true;
    if (lhs == null || rhs == null) return false;
    return lhs._element.Equals(rhs._element);
}

However, this results infinite recursion, because the == operator calls the == operator again.

If this were any other kind of method, I'd just call the base, but that doesn't work with an operator, eg you can't write

if (lhs base.== rhs)

So how do I get around this? Is there some way to call the base == operator from within the overloaded operator body? Or some other way to perform the null check without using == ?

Here is the code on DotNetFiddle .

public static bool operator == (XmlWrapper lhs, XmlWrapper rhs)
{
    if (Object.ReferenceEquals(lhs, null) && Object.ReferenceEquals(rhs, null))
    {
       return true;
    }

    if (Object.ReferenceEquals(lhs, null) || Object.ReferenceEquals(rhs, null))
    {
       return false;
    }

    return lhs._element.Equals(rhs._element);
}

This should provide the desired behavior.

Also note that if overriding equality you need to override GetHashCode

public class XmlWrapper : IEquatable<XmlWrapper> {
    protected readonly XElement _element;

    public XmlWrapper(XElement element) {
        _element = element ?? throw new ArgumentNullException(nameof(element));
    }

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

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

    public override string ToString() {
        return _element != null ? _element.ToString() : this.GetType().FullName;
    }

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

    public override bool Equals(object obj) {
        return obj is XmlWrapper other && Equals(other);
    }

    public bool Equals(XmlWrapper other) {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return _element.Equals(other._element);
    }
}

It is worth noting this implementation is specific to reference types. Since XmlWrapper is a reference type.

You can shorten the code down to one line using the ternary operator:

static public bool operator == (XmlWrapper lhs, XmlWrapper rhs)
{
    return lhs is null ? rhs is null : !(rhs is null) && lhs._element == rhs._element;
}

Which says, "if lhs is null, return true if rhs is null or false if rhs is not null. Otherwise (if lhs is not null), return true if rhs is not null and their elements are equals, else return false."

You can apply Elvis operator and null-coalescing operator as well to make it working.

    static public bool operator == (XmlWrapper lhs, XmlWrapper rhs)
    {
        if (lhs?._element == null && rhs?._element == null) return true;
        return lhs?._element?.Equals(rhs?._element) ?? false;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM