覆盖==并且实例比较等于null是一件坏事吗?

时间:2009-10-15 16:11:39

标签: 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
        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
}

此类将用作对象模型中属性的类型。对象可能如下所示:

public class SomeClass
{

    public SomeClass()
    {
        SomeValue = new MyValue<string>();
    }
    public MyValue<string> SomeValue { get; set; }
}

这个想法是该类只是表示其Value属性的值。它有一些被剥离的功能,因为它与问题无关(一些验证和东西)。由于该类本身没有任何价值,因此我们希望它在使用时尽可能少地侵入。隐式转换允许这样的代码:

SomeClass instance = new SomeClass();
instance.SomeValue = "my string";

......问题:
重载==(和!=)运算符的想法是让Valuenull的对象的实例与null进行比较(部分原因是我们认为它有意义) ,部分原因是对象模型中的向后兼容性问题):

MyValue<string> a = new MyValue<string>();
MyValue<string> b = null;
bool areTheyEqual = (a == b); // this will be true

这当然看起来有点令人困惑,但鉴于MyClass只是一个包装器,在大多数代码中它将是相当不可见的,它对你来说是否有意义,或者它是否真的很糟糕我们会因为我们忽略的原因而后悔吗?

3 个答案:

答案 0 :(得分:10)

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

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

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

答案 1 :(得分:6)

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

- x.Equals(a null reference (Nothing in Visual Basic)) returns false.

基本上:不,不要这样做。这会让班上的用户感到困惑。

现在Nullable<T> 出现以破坏此规则:

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

...但在这种情况下,x不是引用,它是合同说明的一部分。使x成为引用的唯一方法是将值设置为空,此时x为空。在您的情况下,MyValue,因此您可以拥有违反合同的非空引用。

答案 2 :(得分:0)

如果覆盖==,那么通常最好覆盖.Equals以使用相同的逻辑。