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
)。还是我遗漏了一些基本的东西?我已经得到了非常好的理由来反对这个解决方案。然而,看到更多关于它的想法可能会很有趣(只是为社区收集)。我会把这个问题保留几个小时,然后选择一个被接受的答案(很可能是当时投票最多的答案)。我已经得到了非常好的理由反对这个解决方案。然而,看到更多关于它的想法可能会很有趣(只是为社区收集)。我将把这个问题保留几个小时,然后选择一个被接受的答案(很可能是当时投票最多的答案)。