C# 重写==并使实例比较等于null是件坏事吗?

C# 重写==并使实例比较等于null是件坏事吗?,c#,null,operator-overloading,C#,Null,Operator Overloading,背景 假设我有以下课程: public class MyValue<T> { public T Value { get; set; } public static bool operator ==(MyValue<T> first, MyValue<T> second) { // if first and second are the same instance, they are equal i

背景
假设我有以下课程:

public class MyValue<T>
{
    public T Value { get; set; }    
    public static bool operator ==(MyValue<T> first, MyValue<T> second)
    {
        // if first and second are the same instance, they are equal
        if (object.Equals(first, second))
        {
            return true;
        }

        // for each of the objects, get a value indicating whether either
        // the object or its Value property is null
        bool firstIsNull = object.Equals(first, null) ? true : first.Value == null;
        bool secondIsNull = object.Equals(second, null) ? true : second.Value == null;

        // if both are null, they are equal
        if (firstIsNull && secondIsNull)
        {
            return true;
        }

        // Not both are null. If one is, they are not equal
        if (firstIsNull || secondIsNull)
        {
            return false;
        }

        // Neither first nor second is null; compare their values
        return first.Value.Equals(second.Value);
    }    
    // more code implementing !=, Equals, GetHashCode, implicit conversions and so on
}
…问题是:
重载==(and!=)操作符的想法是让
null
的对象实例与
null
进行比较(部分原因是我们认为这有意义,部分原因是对象模型中存在向后兼容性问题):

MyValue a=新的MyValue();
MyValue b=null;
(a==b);//这是真的

这当然看起来有点令人困惑,但鉴于
MyClass
只是一个包装器,在大多数代码中都是不可见的,这对您来说也有意义吗,还是我们会因为忽略的原因而后悔的非常糟糕的事情?

如果您覆盖==,然后通常最好重写。等于使用相同的逻辑。

如果重写==,那么通常最好重写。等于使用相同的逻辑。

在我看来,C中的
=
运算符已经够混乱的了。如果你像那样超载,你会增加混乱的可能性

Class c = somethingEqualsToNullButNotNull;
if (c == null) { // true
}

object co = c;
if (co == null) { // false. WTF!?
}

在我看来,C#中的
==
运算符已经足够令人困惑了。如果你像那样超载,你会增加混乱的可能性

Class c = somethingEqualsToNullButNotNull;
if (c == null) { // true
}

object co = c;
if (co == null) { // false. WTF!?
}

您说您已经覆盖了
Equals
,以提供相同的行为。这意味着你已经违反了合同,合同明确规定:

- x.Equals(a null reference (Nothing in Visual Basic)) returns false.
基本上:不,不要这样做。这将使该类的用户感到困惑

现在
Nullable
似乎违反了这一规则:

int? x = new int?(); // Or int? x = null;
object o = null;
Console.WriteLine(x.Equals(o)); // True

。。。但在这种情况下,
x
不是参考,它是合同描述的一部分。使
x
成为引用的唯一方法是将值框起来,此时
x
仍然为空。在您的情况下,
MyValue
是一个类,因此您可以有一个违反约定的非空引用。

您说您已经重写了
Equals
,以提供相同的行为。这意味着你已经违反了合同,合同明确规定:

- x.Equals(a null reference (Nothing in Visual Basic)) returns false.
基本上:不,不要这样做。这将使该类的用户感到困惑

现在
Nullable
似乎违反了这一规则:

int? x = new int?(); // Or int? x = null;
object o = null;
Console.WriteLine(x.Equals(o)); // True

。。。但在这种情况下,
x
不是参考,它是合同描述的一部分。使
x
成为引用的唯一方法是将值框起来,此时
x
仍然为空。在您的情况下,
MyValue
是一个类,因此您可以有一个违反合同的非空引用。

@ck:已经这样做了;我去掉了它以减少代码量(如示例中最后一条代码注释所示)。这一点很好。@ck:已经完成了;我去掉了它以减少代码量(如示例中最后一条代码注释所示)。好的观点。是的,这不仅仅是一点困惑。反对这种做法的有力论据。尽管名称不同,但它看起来像一个可变引用类型。微软建议避免在这些函数上重载相等运算符。是的,这让人很困惑。反对这种做法的有力论据。尽管名称不同,但它看起来像一个可变引用类型。Microsoft建议避免在这些上重载相等运算符。这也是反对该解决方案的一个非常有力的理由。我想我们只需要对我们的旧代码进行一些重构,让它与包装器配合得很好。我碰巧再次无意中来到这里,我想到了一些东西。虽然我完全同意你的回答,但我也意识到,
Nullable
实际上只起作用,因为它打破了
等于
的契约:
int?n=零;Console.WriteLine(n.Equals(null))
(打印
true
)(显然
n
不是空引用,因为成功调用了
Equals
)。还是我遗漏了一些基本的东西?这也是反对解决方案的一个非常有力的论点。我想我们只需要对我们的旧代码进行一些重构,让它与包装器配合得很好。我碰巧再次无意中来到这里,我想到了一些东西。虽然我完全同意你的回答,但我也意识到,
Nullable
实际上只起作用,因为它打破了
等于
的契约:
int?n=零;Console.WriteLine(n.Equals(null))
(打印
true
)(显然
n
不是空引用,因为成功调用了
Equals
)。还是我遗漏了一些基本的东西?我已经得到了非常好的理由来反对这个解决方案。然而,看到更多关于它的想法可能会很有趣(只是为社区收集)。我会把这个问题保留几个小时,然后选择一个被接受的答案(很可能是当时投票最多的答案)。我已经得到了非常好的理由反对这个解决方案。然而,看到更多关于它的想法可能会很有趣(只是为社区收集)。我将把这个问题保留几个小时,然后选择一个被接受的答案(很可能是当时投票最多的答案)。