Неправильное предупреждение компилятора при сравнении 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, поэтому предупреждение.