如何在相等运算符重载中执行空检查

时间:2019-08-30 21:31:58

标签: c# .net operator-overloading

我编写了一个包装XElement的简单类。我想将相等操作传递给包装的实例。所以我写了这个:

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);
    }
}

这似乎很简单,但实际上它为非常简单的null检查抛出了异常:

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

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

这是因为相等运算符为其参数之一接收到null,因此无法检索包装的XElement。因此,显而易见的想法是添加一个空检查,如下所示:

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);
}

但是,这会导致无限递归,因为==运算符再次调用了==运算符。

如果这是任何其他类型的方法,我只会调用基数,但这不适用于运算符,例如你不会写

if (lhs base.== rhs)

那么我该如何解决呢?有什么方法可以从重载的操作符体内调用基本==操作符?还是不使用==来执行空检查的其他方法?

这里是code on DotNetFiddle

4 个答案:

答案 0 :(得分:4)

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);
}

答案 1 :(得分:2)

这应该提供所需的行为。

还要注意,如果要覆盖相等性,则需要覆盖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);
    }
}

值得注意的是,此实现特定于引用类型。由于XmlWrapper是引用类型。

答案 2 :(得分:1)

您可以使用三元运算符将代码缩短到一行:

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

说:“如果lhs为null,则如果rhs为null则返回true;如果rhs不为null,则返回false。否则(如果lhs不为null),如果rhs不为null并且它们的元素相等,则返回true。错误。”

答案 3 :(得分:0)

您还可以应用Elvis运算符和null-coalescing运算符以使其正常工作。

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