问题 将struct与null进行比较时,编译器警告错误


请考虑以下代码:

DateTime t = DateTime.Today;

bool isGreater = t > null;

使用Visual Studio 2010(C#4,.NET 4.0),我收到以下警告:

警告CS0458:表达式的结果始终为'bool'类型的'null'

这是不正确的;结果总是如此 false (类型 bool):

现在,结构DateTime重载了 > (大于)运算符。任何不可为空的结构(如DateTime)都可以隐式转换为相应的结构 Nullable<> 类型。上面的表达式完全相同

bool isGreater = (DateTime?)t > (DateTime?)null;

这也产生了同样的错误警告。在这里 > 运营商是 取消 运营商。这通过返回false if来工作 HasValue 它的任何两个操作数都是 false。否则,提升的运算符将继续将两个操作数展开到底层结构,然后调用重载 > 由该结构定义(但在这种情况下,这不是必需的,其中一个操作数不是 HasValue)。

你能重现这个bug吗?这个bug是众所周知的吗?我误解了什么吗?

对于所有结构类型(不是简单类型)都是一样的 int,而不是枚举类型),使有问题的运算符超载。

(现在,如果我们使用 == 代替 >,一切都应该完全相似(因为DateTime也会超载 == 运营商)。但它并不相似。如果我说

DateTime t = DateTime.Today;

bool isEqual = t == null;

我明白了 没有 警告有时您会看到人们意外地检查变量或参数是否为null,而没有意识到他们的变量类型是一个结构(超载 == 这不是一个简单的类型 int)。如果他们得到警告会更好。)


更新: 使用C#6.0编译器(基于 罗斯林)Visual Studio 2015,错误的消息 isGreater 以上内容已更改为CS0464,并带有正确且有用的警告消息。另外,缺乏警告 isEqual 上面是在VS2015的编译器中修复的,但只有在编译时才有 /features:strict


10140
2017-10-24 09:10


起源

[anything] > null 对我来说没有任何意义(对我来说,至少)。尽管如此,有趣的问题,我认为它应该警告 bool 总是最终成为 false。 - Alex
可能会有所帮助: blogs.msdn.com/b/abhinaba/archive/2005/12/11/501544.aspx 和 blogs.msdn.com/b/abhinaba/archive/2005/12/14/503533.aspx - Habib
有趣的是, DateTime.CompareTo(object)  特别 回报 1 当你比较 null,无论传入的物体类型如何。 - Rawling
同样的警告出现在VS 2012中.net 4.5 :) - Arman McHitarian


答案:


你是对的: 这是Visual Studio中的一个错误。 C#4.0标准(§7.3.7解除运营商)有这样的说法:

对于关系运算符

<  >  <=  >=

[...] 提升的运算符产生值 false 如果一个或两个操作数为空。 ...

事实上,在MonoDevelop中,您会收到以下警告:

比较类型的结果 System.DateTime 同 null 总是 false


5
2017-10-24 09:39



但是,在这 > case,结果为false,不为null。结果的类型是 bool不是 bool?。 - Rawling
@Rawling是吗?这听起来不对,应该是 bool? 因为操作员被抬起。 - Konrad Rudolph
Visual Studio似乎这么认为,但是 DateTime.Today > null 给我 false 类型 bool显然,提问者得到了相同的信息。我同意这很奇怪。 - Rawling
您: 这通常适用于所有运营商,而不仅仅是添加。 不可以。请阅读C#语言规范的“提升操作员”部分。解除了运营商 == 和 > 返回 bool 就像原来的运营商一样。那不同于 + 运营商。解除了 + 运算符(一元和二元)返回 DateTime? (可空)因为未提升的形式返回 DateTime (不可为空的)。 - Jeppe Stig Nielsen
由于这个答案更好,我会接受它。编辑后,这是一个很好的答案。如果这是一个已知的bug,没有人提供参考,所以我提交了它。看到 Connect的这个反馈项目。 - Jeppe Stig Nielsen


我在Roslyn中实现提升的操作员行为时独立地发现了这个错误,并且在我离开之前将其修复在Roslyn中。

对不起,我在10月份发布时没有看到这个。感谢您将其提交给Connect!许多道歉的错误;这是操作员语义分析中的一个长期错误。

顺便说一下,我将讨论Roslyn如何优化提升的表达式 http://ericlippert.com 本月晚些时候(2012年12月),如果此主题感兴趣,请查看:

http://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/


9
2017-12-19 00:26



没有必要为没有看到Stack Overflow上的每个线程而道歉。我希望我记得检查你即将发布的关于提升运营商的帖子。 - Jeppe Stig Nielsen


DateTime t = DateTime.Today;

bool isGreater = (DateTime?)t > (DateTime?)null;

在这种情况下,警告的内容是 t > null。这永远不会成真。因为它无法评估。

在这种情况下:

bool isGreater = (DateTime?)t > (DateTime?)null;

我们正在评估 (DateTime?)t > (DateTime?)null;

或者基本上是在最好的情况下 t > null;和之前一样。 DateTime.Now永远不会大于Undefined,因此警告。


1
2017-10-24 09:19



我同意它与警告有关,但你是否已阅读警告 信息?这是警告的文本​​是令人不安的错误,而不是发出警告的事实。 (也许在可空类型开发期间的某个时候,他们计划有更多的比较运算符(>, ==,等等)表现不同,但后来改变了主意?) - Jeppe Stig Nielsen