Неправильное предупреждение компилятора при сравнении struct с null

Рассмотрим следующий код:

DateTime t = DateTime.Today;

bool isGreater = t > null;

С Visual Studio 2010 (С# 4,.NET 4.0) я получаю следующее предупреждение:

warning CS0458: Результат выражения всегда "null" типа "bool?"

Это неверно; результат всегда false (типа bool):

Теперь структура DateTime перегружает оператор > (больше). Любая недействительная структура (например, DateTime) неявно конвертируется в соответствующий тип Nullable<>. Вышеприведенное выражение в точности эквивалентно

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

который также генерирует одно и то же неправильное предупреждение. Здесь оператор > - оператор снят. Это работает, возвращая false, если HasValue любого из двух операндов false. В противном случае оператор, поднятый, перейдет к разворачиванию двух операндов к базовой структуре, а затем вызовет перегрузку >, определенную этой структурой (но это не обязательно в этом случае, когда один операнд не имеет значения HasValue).

Можете ли вы воспроизвести эту ошибку, и эта ошибка известна? Я что-то не понял?

Это то же самое для всех типов структур (не простых типов типа int, а не типов перечислений), которые перегружают соответствующий оператор.

(Теперь, если мы используем == вместо >, все должно быть полностью схожим (потому что DateTime также перегружает оператор ==), но это не похоже. Если я скажу

DateTime t = DateTime.Today;

bool isEqual = t == null;

Я получаю предупреждение no ☹ Иногда вы видите, что люди случайно проверяют переменную или параметр для null, не понимая, что тип их переменной является структурой (которая перегружает == и которая не является простой тип типа int). Было бы лучше, если бы они получили предупреждение.)


Обновление: С помощью компилятора С# 6.0 (на основе Roslyn) Visual Studio 2015 неверное сообщение с isGreater выше изменяется на CS0464 с правильным и полезным предупреждающим сообщением. Кроме того, отсутствие предупреждения с isEqual выше фиксировано в VS2015 компиляторе, но только если вы скомпилируете с /features:strict.

Ответ 1

Вы правы: это ошибка в Visual Studio. Стандарт С# 4.0 (§ 7.3.7 Операторы с поднятием) имеет следующее:

Для реляционных операторов

<  >  <=  >=

[...] Поднятый оператор выдает значение false, если один или оба операнда равны нулю....

И в самом деле, в MonoDevelop вы получите следующее предупреждение:

Результат сравнения типа System.DateTime с null всегда false.

Ответ 2

Я обнаружил эту ошибку независимо при реализации поднятого поведения оператора в Roslyn, и я исправил ее в Roslyn, прежде чем ушел.

Извините, что я не видел этого, когда вы разместили его еще в октябре. Спасибо, что отправили его в Connect! И многие извинения за ошибку; это долговременная ошибка в семантическом анализе оператора.

Кстати, я буду обсуждать, как Roslyn оптимизирует снятые выражения на http://ericlippert.com в конце этого месяца (декабрь 2012), так что если этот вопрос вас интересует, проверьте:

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

Ответ 3

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, поэтому предупреждение.