VB.NET Ничего Datetime? считыватель данных

Простой вопрос со встроенным if: dim mydate as datetime?


'Версия 1 (РАБОТАЕТ!)

If dtReader.IsDBNull(dtReader.GetOrdinal("mydate")) Then
    mydate = Nothing
Else
    mydate = dtReader.GetDateTime(dtReader.GetOrdinal("mydate"))
End If

Значение = ничего


'Версия 2 (НЕ РАБОТАЕТ!)

mydate = If(dtReader.IsDBNull(dtReader.GetOrdinal("mydate")), Nothing, dtReader.GetDateTime(dtReader.GetOrdinal("mydate")))

Значение = #12:00:00#


Может кто-нибудь объяснить мне, почему версия 2 получает это значение?


person Testman    schedule 09.12.2015    source источник
comment
Это не имеет никакого смысла. Проверьте свою входную информацию (например, если вы пишете один код за другим, они будут анализировать разные строки). Встроенные или многострочные условия, безусловно, ведут себя одинаково; просто используйте их правильно.   -  person varocarbas    schedule 09.12.2015
comment
Не совсем понятно, вы правы, отредактировано, спасибо   -  person Testman    schedule 09.12.2015
comment
Оба они выглядят одинаково, за исключением того, что в одном из них используется троичное выражение if. Пожалуйста, можете ли вы предоставить достаточно кода, чтобы воспроизвести проблему? Вы всегда можете посмотреть на IL, чтобы увидеть разницу.   -  person Sam Makin    schedule 09.12.2015
comment
Итак, вы можете сказать мне, почему у меня такое же дерьмо с этим: Если (Правда, Ничего, Сейчас)   -  person Testman    schedule 09.12.2015
comment
Ответ @SamMakin доказывает, что между обоими типами условий для дат, допускающих значение NULL, существует действительно странная разница! Действительно странно. В любом случае предлагаемые условия слишком неясны (могут легко относиться к разным исходным данным).   -  person varocarbas    schedule 09.12.2015
comment
После некоторых тестов и обсуждения в ответе damien_the_unbeliever я понял проблему: при назначении типа DateTime во второй части тернарного оператора вы заставляете VB.NET думать, что это DateTime, а не DateTime?. Если вы используете DateTime? вы бы избежали этой проблемы (что именно вы должны делать, поскольку присвоение другому типу неправильно). Вся эта ситуация стала немного запутанной, потому что VB.NET (Option Strict On/Off) не распознает эту проблему. С другой стороны, вы всегда должны приводить/преобразовывать в правильный тип, так что...   -  person varocarbas    schedule 09.12.2015


Ответы (2)


Это сводится к тому, что компилятору необходимо выполнить анализ типов для If. Имейте в виду, что Nothing – это не то же самое, что значение NULL в C#, оно ближе до default(T):

Если переменная имеет тип значения, который не допускает значение NULL, присвоение ей Nothing устанавливает для нее значение по умолчанию для объявленного типа.

Теперь, когда компилятор анализирует If, он должен определить тип всего выражения. Вот на что он смотрит:

If(Boolean,<AnyType>,DateTime)

Теперь он должен решить, какой тип выражения, на основе типов второго и третьего параметров, и он должен выбрать один из имеющихся типов. Так что, вполне естественно, он выбирает DateTime. И Nothing, преобразованное в DateTime, совпадает с минимальным значением.

Чтобы изменить это, дайте выбор вывести тип как DateTime? вместо этого:

mydate = If(dtReader.IsDBNull(dtReader.GetOrdinal("mydate")), _
          CType(Nothing,DateTime?), _
          dtReader.GetDateTime(dtReader.GetOrdinal("mydate")))

Согласно Спецификации языка Visual Basic, раздел 11.22 (Условное Выражения):

Если предоставлены три операнда, все три выражения должны быть классифицированы как значения, а первый операнд должен быть логическим выражением. Если результат выражения истинен, то результатом оператора будет второе выражение, иначе результатом оператора будет третье выражение. Тип результата выражения является доминирующим между типами второго и третьего выражений. Если нет доминирующего типа, возникает ошибка времени компиляции.

(Мой акцент).

И обратите внимание, что нет условного текста о том, что «если это используется в операторе присваивания, вы также можете принять во внимание объявленный тип присваиваемой переменной».

person Damien_The_Unbeliever    schedule 09.12.2015
comment
Но возникает вопрос: почему, когда вы делаете mydate = Nothing, он получает Nothing, а не логическое минимальное значение даты? Почему поведение тернарного оператора отличается? На мой взгляд, это ошибка (хотя и не слишком актуальная): тернарный оператор всегда должен давать тот же результат, что и условие (обычное присваивание). - person varocarbas; 09.12.2015
comment
@varocarbas - это не ошибка. Сначала вам нужно определиться с типом выражения If, а это определяется исключительно типами второго и третьего параметров. Он не учитывает (и, как правило, не может) также учитывать тот факт, что его результат впоследствии будет присвоен определенной переменной. - person Damien_The_Unbeliever; 09.12.2015
comment
Вы пытаетесь оправдать поведение, приведя во внимание свои знания о конкретной реализации, но на мой взгляд это здесь неуместно. Как программист, я ожидаю, что тернарные операторы будут давать точно такие же результаты, как и другие условия. Когда я преобразовываю любой набор if... else в тернарный оператор, я хочу получить точно такой же результат; любое другое поведение было бы ошибкой для меня (= вопреки моим разумным ожиданиям). Тот факт, что ошибка является непреднамеренной (т. е. ошибкой при сборке компилятора) или преднамеренной (т. е. ошибкой в ​​логике), на мой взгляд, не имеет значения. - person varocarbas; 09.12.2015
comment
@varocarbas - нет, примите во внимание это утверждение - Dim abc = If(a=b,c,d). Компилятор должен иметь возможность анализировать оператор If и определять тип его результата еще до того, как он узнает тип abc. Оператор If можно использовать в любой позиции, где выражение допустимо и должно быть доступно для независимого анализа. Его нельзя строго ограничивать использованием только в правой части задания. - person Damien_The_Unbeliever; 09.12.2015
comment
Протестируйте этот код: Dim d As DateTime? = New DateTime? d = If(True, Nothing, Now) Dim d2 As DateTime? = New DateTime? If True Then d2 = Nothing Else d2 = Now End If If d = d2 Then 'Never reached End If он выдает: d = минимальная дата и d2 = ничего, а d отличается от d2. Для меня это ошибка. Я хочу, чтобы оба условия давали абсолютно одинаковые результаты или, в худшем случае, давали результаты, которые можно было бы считать равными. Это мое понимание того, как должны работать тернарные операторы (и как я хочу, чтобы они работали). - person varocarbas; 09.12.2015
comment
@varocarbas - язык работает так, как указано. То, что оно не соответствует вашим ожиданиям, ни здесь, ни там. Я пытался объяснить, почему язык разработан таким образом и почему, следовательно, он не соответствует вашим ожиданиям. Я не знаю, как еще сказать, что оценка типов в операторе If и затем анализ присваивания обязательно развязаны. - person Damien_The_Unbeliever; 09.12.2015
comment
Это эквивалентно - If(True, DirectCast(Nothing, Date?), Now) - person Sam Makin; 09.12.2015
comment
@SamMakin - да, DirectCast тоже можно использовать. Вы даже можете разыграть Now, а не Nothing. Ключевая часть — предоставить компилятору выбор DateTime?. - person Damien_The_Unbeliever; 09.12.2015
comment
На самом деле, я провел несколько тестов с C# и понял проблему. Если вы сделаете: d = If(True, Nothing, CType(Now, DateTime?)) он будет вести себя точно так, как ожидалось. Когда вы присваиваете Now напрямую, вы заставляете тернарный оператор думать, что он имеет дело с DateTime, а не с DateTime?. - person varocarbas; 09.12.2015
comment
Возможно, это не совсем ошибка, но два отрицательных момента для VB.NET здесь (иногда он выигрывает, иногда проигрывает): во-первых, неправильное распознавание этой ситуации (Option Strict On/Off здесь не имеет значения); во-вторых, заставляя меня использовать CType (DirectCast не работает в данном конкретном случае? Почему?). - person varocarbas; 09.12.2015
comment
@varocarbas - очень сложно написать эквивалент C # с явно очевидными типами, потому что, как я уже сказал, Nothing сродни default(T) C #, и вам нужно указать тип там. - person Damien_The_Unbeliever; 09.12.2015
comment
Моя точка зрения заключалась в том, что C# не позволял мне делать такие вещи (жаловался на то, что Now не является DateTime?), и именно поэтому я понял проблему. Как было сказано, поведение нормальное (т. е. странность возникает из-за отсутствия преобразования/приведения к нужному типу), но VB.NET должен предупредить об этой проблеме (т. е. не разрешать такое поведение с Option Strict Off, что C# правильно делает ). - person varocarbas; 09.12.2015

Переключите Опцию Строго ВКЛ! Идут неявные преобразования.

Пример см. в этом ответе.

Обновление: если тип, который вы устанавливаете, не допускает значение NULL, эти два оператора if полностью совпадают. Если они допускают значение null (DateTime не является значением по умолчанию), то два оператора if дают разные результаты. Пример:

Тест 1:

Код:

Dim d As DateTime?

d = If(True, Nothing, Now)

Результат:

DateTime? dateTime = new DateTime?(DateTime.MinValue);

Тест 2:

Код:

Dim d As DateTime?
If True Then
    d = Nothing
Else
    d = Now
End If

Результат:

DateTime? dateTime = null;
person Sam Makin    schedule 09.12.2015
comment
Вы правы - с параметром Option Strict On он не предупреждает. Однако причина все же в конверсии. Он возвращает новую дату и время вместо ничего - person Sam Makin; 09.12.2015
comment
Это, конечно, странно, и ваш ответ достоин (тоже вопрос). В любом случае проблема Strict On/Off здесь не имеет никакого значения. - person varocarbas; 09.12.2015