Почему мне разрешено сравнивать тип, не допускающий значения NULL, с типом NULL?

Возможный дубликат:
C # согласен с сравнение типов значений с null

Если я попытаюсь назначить null типу, не допускающему значения NULL, в C #:

System.DateTime time = null;

Я получу ошибку времени компиляции:

ошибка CS0037: не удается преобразовать значение NULL в System.DateTime, поскольку это тип значения, не допускающий значения NULL.

что имеет смысл. Но если сравнить этот же тип с null:

System.DateTime time = obtainFromSomewhere();
if( time == null ) {
    //whatever;
}

ошибки времени компиляции нет. Для меня это не имеет смысла - если я не могу назначить null, то почему это вообще должно быть null?

Почему мне разрешено сравнивать тип, не допускающий значения NULL, с null?


person sharptooth    schedule 19.04.2011    source источник
comment
Потому что само заявление действительно. Вы всегда можете проверить объект на нуль.   -  person halfdan    schedule 19.04.2011
comment
Разве это не допустимо из-за разрешенного неявного преобразования типов, не допускающих значения NULL, в NULL?   -  person username    schedule 19.04.2011
comment
См. Раздел Сравнение структур с нулевым значением и C # нормально сравнивает типы значений с null, где Эрик Липперт уже дал отличный ответ (пока ни один из ответов здесь не кажется правильным ).   -  person Dirk Vollmar    schedule 19.04.2011
comment
@ 0xA3: Ответ Брайана, по сути, правильный, но да, вы правы в том, что это дубликат. @sharptooth: если вы сравниваете встроенный тип, такой как int, с null, компилятор выдает предупреждение о том, что сравнение всегда ложно, но не делает этого для поднятых определяемых пользователем операторов равенства; это недостаток компилятора. Прошу прощения за отсутствие предупреждения; нам действительно стоит добавить сюда.   -  person Eric Lippert    schedule 19.04.2011


Ответы (5)


Причина, по которой это работает для DateTime, заключается в том, что DateTime определяет собственный оператор ==. Благодаря этому он получает расширенную версию оператора, которая может использоваться с DateTime?. Поскольку и DateTime, и null могут быть неявно преобразованы в DateTime?, сравнение компилируется, но во время выполнения всегда будет иметь значение false.

Спасибо Мэтту Эллену за указание на тот факт, что мой первоначальный ответ не охватывал пример в вопросе.

person Brian Rasmussen    schedule 19.04.2011
comment
но в примере с Sharptooth нет параметра типа, поэтому он не должен компилироваться. Ошибка времени компиляции должна произойти, если оба операнда не являются ссылочными типами или null. - person Matt Ellen; 19.04.2011
comment
Фактически, прямо над вашей цитатой это: предопределенные операторы равенства ссылочных типов не позволяют сравнивать операнды типов значений. Следовательно, если тип структуры не объявляет свои собственные операторы равенства, невозможно сравнивать значения этого типа структуры. - person Matt Ellen; 19.04.2011

Это связано с боксом.

DateTime можно поместить как object, и таким образом он станет ссылкой, которую можно сравнить с null (хотя всегда будет false).

Однако объект (null) нельзя распаковать обратно в DateTime, поэтому его нельзя назначить DateTime.

Пример: вы могли бы сделать

object now = DateTime.Now;
bool isNull = now == null

РЕДАКТИРОВАТЬ: Как заметил Брайан Расмуссен, я ошибался с теорией бокса. Бокс будет происходить только в том случае, если он явно приведен к объекту, как в моем примере или в (object)DateTime.Now == null.

person František Žiačik    schedule 19.04.2011
comment
Он должен использовать DataTime? вместо DateTime! это обычная практика - person Farzin Zaker; 19.04.2011
comment
@Farzin Zaker: здесь задается вопрос: почему мне разрешено сравнивать тип, не допускающий значения NULL, с типом NULL? not Что я должен использовать. - person František Žiačik; 19.04.2011
comment
Бокса нет. Проверьте сгенерированный IL. - person Brian Rasmussen; 19.04.2011
comment
@ Брайан Расмуссен: ты прав. - person František Žiačik; 19.04.2011
comment
Это не из-за бокса. В разделе 7.6.10 спецификации C # 4.0 указано, что предопределенные операторы равенства ссылочных типов никогда не вызывают операции упаковки для их операндов. - person Eric Lippert; 19.04.2011

Начиная с .NET 2.0, существует неявное преобразование в тип, допускающий значение NULL (см., Что сказал Эрик Липперт здесь).

Компилятор выдает следующее предупреждение, указывающее на то, что происходит преобразование:

C:\>c:\windows\Microsoft.NET\Framework\v2.0.50727\csc test.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.4927
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727

test.cs(16,12): warning CS0464: Comparing with null of type 'int?' always produces 'false'

В .NET 1.1, в котором не было типов, допускающих значение NULL, ваш образец кода не был бы законным:

C:\>c:\windows\Microsoft.NET\Framework\v1.1.4322\csc test.cs
Microsoft (R) Visual C# .NET Compiler version 7.10.3052.4
for Microsoft (R) .NET Framework version 1.1.4322
Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.

test.cs(12,13): error CS0019: Operator '==' cannot be applied to operands of type 'System.DateTime' and ''
person Dirk Vollmar    schedule 19.04.2011

это должен быть тип, допускающий значение NULL:

System.DateTime? time = null;

ваш код должен выглядеть так:

System.DateTime? time = obtainFromSomewhere();
if( time.HasValue ) {
    //use time.Value;
}

но помните, что ваша obtainFromSomewhere функция должна возвращать тип DateTime?.

person Farzin Zaker    schedule 19.04.2011
comment
Эм .. Разве это не должно быть if( time.HasValue )? - person sharptooth; 19.04.2011
comment
Да, поэтому и спрашиваю. Если null был получен, как мы можем использовать .Value? - person sharptooth; 19.04.2011
comment
Если время не имеет ценности, используйте ценность времени ... ??? - person Jaymz; 19.04.2011
comment
нет, вы должны проверить свойство HasValue, если оно истинно, а затем использовать свойство Value. - person Farzin Zaker; 19.04.2011

На мой взгляд, это допустимо, потому что NULL не является реальным значением ни одного типа.

Хорошо, что разрешен некоторый код, подобный приведенному ниже:

System.DateTime time = obtainFromSomewhere(); // allways a date
System.DateTime? otherTime = obtainFromSomewhereElse(); // null if nothing planned

if (time == otherTime)
{
    // match
    ...
}

Что бы мы делали без null?

person Larry    schedule 19.04.2011