我注意到一些.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进行比较。
答案 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)
当包含可空类型时,有效地引入了这个问题。存在从TimeSpan
到TimeSpan?
的隐式转换,并且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