为什么TimeSpan和Guid Structs可以比较为null?

时间:2009-08-04 06:36:18

标签: c# .net .net-2.0 null struct

我注意到一些.NET结构可以与null进行比较。 例如:

  TimeSpan y = new TimeSpan();
        if (y == null)
            return;

编译得很好(与Guid结构相同) 现在我知道stucts是值类型,并且上面的代码不应该编译,除非有一个带有对象的operator ==的重载。但是,据我所知,没有 我用反射器和MSDN上的文档查看了这个类 他们两个确实实现了以下接口:

IComparable, IComparable<T>, IEquatable<T>

但是,尝试使用相同的接口似乎没有帮助:

struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
    public int CompareTo(Object obj) {
        return 0;
    }
    public int CompareTo (XX other){
        return 0;
    }
    public bool Equals (XX other){
        return false;
    }
    public override bool Equals(object value){
        return false;
    }
    public static int Compare(XX t1, XX t2){
        return 0;
    }
}

我正在使用:.NET 2.0 Visual Studio 2005。

有谁知道这是什么原因? 我只是想更好地理解。这不是问题,因为我知道我不应该将结构与null进行比较。

5 个答案:

答案 0 :(得分:13)

这是==运营商。

TimeSpan类有一个等于运算符的重载:

public static bool operator ==(DateTime d1, DateTime d2)
{
     return (t1._ticks == t2._ticks);
}

这本身不能与null进行比较,但 ......

随着可空类型的到来,每个结构都可以隐式转换为可以为空的类型,所以当你看到像

这样的东西时
TimeSpan y = new TimeSpan();
if (y == null)
    return;

看不到发生了这种情况:

TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
    return;

Null获取隐式转换(隐式赋值?),但并非所有System.Object个对象都执行:

TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
    return;

好的,但是相等运算符不会采用可为空的参数,是吗?

嗯,msdn在这里有所帮助,说明:

  

预定义的一元和二元   运营商和任何用户定义的   存在于值类型的运算符   也可以由可空类型使用。   这些运算符产生空值   如果[任何]操作数为空;除此以外,   运算符使用包含的值   计算结果。

因此,您可以有效地为每个运营商免费提供可空的实现,并且具有固定的定义行为。上面提到的“包含值”是非可空运算符返回的实际值。

答案 1 :(得分:7)

本案例适用于C#语言规范第7.9.6节中的泛型。

  

即使T可以表示值类型,也允许使用x == null结构,并且当T是值类型时,结果简单地定义为false。

我在规范中挖了一下,找不到更一般的规则。 Jon的answer表示这是一个可以为空的促销问题。

这条规则(或类似的变体)似乎确实在这里应用。如果仔细观察反射输出,你会发现比较不存在。 C#编译器显然正在优化这种比较并将其替换为false。

例如,如果您键入以下内容

var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);

然后反编译你会看到以下

var x = new TimeSpan();
var y = false;
Console.WriteLine(x);

答案 2 :(得分:7)

当包含可空类型时,有效地引入了这个问题。存在从TimeSpanTimeSpan?的隐式转换,并且TimeSpan?与该类型的空值之间存在比较。

编译器会针对某些类型发出警告,这使得它更清楚它正在尝试做什么:

int x = 10;
if (x == null)
{
    Console.WriteLine();
}

发出此警告:

Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
       since a value of type 'int' is never equal to 'null' of type 'int?'

我相信Marc Gravell和我一起制定了警告的情况......这是一种耻辱,但这并不一致。

答案 3 :(得分:4)

另请参阅:C# 3 (.NET 3.5) version of csc fails to report CS0162 for unrechable code (struct/null)

从C#3编译器开始,这意味着它有时甚至不会警告你这个; -p

由于Guid / TimeSpan等提供==,因此它们会陷入此陷阱,而不会向您发出警告。

答案 4 :(得分:3)

我找到了它:)

以下是警告:

int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
//             type 'int' is never equal to 'null' of type 'int?'

编译器无法发出正确的警告,表明您键入的null已转换为TimeSpan?类型进行比较。

编辑:规范中的相关部分是§13.7.1,声明null可以隐式转换为任何可空类型,并且(非常难以阅读)部分§13.7.2声明值类型{ {1}}可以隐式转换为T

我最初的写作:

无论发生什么事都是C#规范中的内容,因为就像JaredPar所说它只编译为T?

请注意,这不会编译:

false