繁体   English   中英

如何在没有无限递归的情况下检查“==”运算符重载中的空值?

[英]How do I check for nulls in an '==' operator overload without infinite recursion?

以下将导致 == 运算符重载方法的无限递归

    Foo foo1 = null;
    Foo foo2 = new Foo();
    Assert.IsFalse(foo1 == foo2);

    public static bool operator ==(Foo foo1, Foo foo2) {
        if (foo1 == null) return foo2 == null;
        return foo1.Equals(foo2);
    }

如何检查空值?

使用ReferenceEquals

Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);

public static bool operator ==(Foo foo1, Foo foo2) {
    if (object.ReferenceEquals(null, foo1))
        return object.ReferenceEquals(null, foo2);
    return foo1.Equals(foo2);
}

在重载方法中强制转换为对象:

public static bool operator ==(Foo foo1, Foo foo2) {
    if ((object) foo1 == null) return (object) foo2 == null;
    return foo1.Equals(foo2);
}

使用ReferenceEquals 来自MSDN 论坛

public static bool operator ==(Foo foo1, Foo foo2) {
    if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
    if (ReferenceEquals(foo2, null)) return false;
    return foo1.field1 == foo2.field2;
}

如果您使用的是 C# 7 或更高版本,则可以使用空常量模式匹配:

public static bool operator==(Foo foo1, Foo foo2)
{
    if (foo1 is null)
        return foo2 is null;
    return foo1.Equals(foo2);
}

这为您提供了比调用 object.ReferenceEquals(foo1, null) 的代码更简洁的代码

在这种情况下,实际上有一种更简单的检查null方法:

if (foo is null)

而已!

这个特性是在 C# 7 中引入的

尝试Object.ReferenceEquals(foo1, null)

无论如何,我不建议重载==运算符; 它应该用于比较引用,并使用Equals进行“语义”比较。

如果我覆盖了bool Equals(object obj)并且我希望运算符==Foo.Equals(object obj)返回相同的值,我通常会像这样实现!=运算符:

public static bool operator ==(Foo foo1, Foo foo2) {
  return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
  return !object.Equals(foo1, foo2);
}

运算符==将在为我完成所有空检查后最终调用foo1.Equals(foo2) ,我已覆盖它以进行实际检查,如果两者相等。

我的做法是

(object)item == null

我依赖于object自己的相等运算符,它不会出错。 或自定义扩展方法(和重载):

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null;
}

public static bool IsNull<T>(this T? obj) where T : struct
{
    return !obj.HasValue;
}

或者处理更多的情况,可能是:

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null || obj == DBNull.Value;
}

该约束可防止值类型出现IsNull 现在就像打电话一样甜蜜

object obj = new object();
Guid? guid = null; 
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error

这意味着我有一种一致/不易出错的方式来检查空值。 我还发现(object)item == nullObject.ReferenceEquals(item, null)非常非常非常快,但Object.ReferenceEquals(item, null)是它很重要(我目前正在研究一些我必须对所有内容进行微优化的事情! )。

要查看有关实施相等性检查的完整指南,请参阅什么是比较引用类型的两个实例的“最佳实践”?

静态Equals(Object, Object)方法指示两个对象objAobjB是否相等。 它还使您能够测试值为null对象是否相等。 它比较objAobjB的相等性,如下所示:

  • 它确定两个对象是否表示相同的对象引用。 如果是,则该方法返回true 此测试等效于调用ReferenceEquals方法。 此外,如果objAobjB都为null ,则该方法返回true
  • 它确定objAobjB是否为null 如果是,则返回false 如果两个对象不表示相同的对象引用,并且都不为null ,则调用objA.Equals(objB)并返回结果。 这意味着如果objA覆盖Object.Equals(Object)方法,则调用此覆盖。

.

public static bool operator ==(Foo objA, Foo objB) {
    return Object.Equals(objA, objB);
}

回复更多覆盖运算符如何与在此处重定向为重复项的null 进行比较

在这样做是为了支持值对象的情况下,我发现新的符号很方便,并希望确保只有一个地方进行比较。 还利用 Object.Equals(A, B) 简化了空检查。

这将重载 ==、!=、Equals 和 GetHashCode

    public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
    public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
    public override bool Equals(object other) => Equals(other as ValueObject );
    public bool Equals(ValueObject other) {
        return !(other is null) && 
               // Value comparisons
               _value == other._value;
    }
    public override int GetHashCode() => _value.GetHashCode();

对于更复杂的对象,在 Equals 中添加额外的比较和更丰富的 GetHashCode。

对于现代和精简的语法:

public static bool operator ==(Foo x, Foo y)
{
    return x is null ? y is null : x.Equals(y);
}

public static bool operator !=(Foo x, Foo y)
{
    return x is null ? !(y is null) : !x.Equals(y);
}

运算符 == 重载中的一个常见错误是使用(a == b)(a ==null)(b == null)来检查引用相等性。 这反而会导致调用重载的运算符 ==,从而导致infinite loop 使用ReferenceEquals或将类型强制转换为 Object,以避免循环。

看看这个

// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
    return true;
}

// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
    return false;
}

重载 Equals() 和运算符 == 的参考指南

您可以尝试使用对象属性并捕获产生的 NullReferenceException。 如果您尝试的属性是从 Object 继承或覆盖的,那么这适用于任何类。

public static bool operator ==(Foo foo1, Foo foo2)
{
    //  check if the left parameter is null
    bool LeftNull = false;
    try { Type temp = a_left.GetType(); }
    catch { LeftNull = true; }

    //  check if the right parameter is null
    bool RightNull = false;
    try { Type temp = a_right.GetType(); }
    catch { RightNull = true; }

    //  null checking results
    if (LeftNull && RightNull) return true;
    else if (LeftNull || RightNull) return false;
    else return foo1.field1 == foo2.field2;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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