我编写了一个包装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)
那么我该如何解决呢?有什么方法可以从重载的操作符体内调用基本==
操作符?还是不使用==
来执行空检查的其他方法?
答案 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;
}