В VB.NET следующий код:
Dim myBool As Boolean = False
Dim myDate As Date? = If(myBool, Date.Today, Nothing)
Console.WriteLine(myDate)
Производит:
1/1/0001 12:00:00 AM
Однако, если я напишу это как:
Dim myBool As Boolean = False
Dim myDate As Date?
If myBool Then
myDate = Date.Today
Else
myDate = Nothing
End If
Console.WriteLine(myDate)
Затем он, как и ожидалось, выведет значение null/nothing.
Что здесь происходит, и как я могу использовать однострочный код для назначения даты или значения null для DateTime с нулевым значением в VB.NET без того, чтобы по умолчанию он фактически не был установлен на 01/01/0001 12:00:00 AM
?
Выражение If(myBool, Date.Today, Nothing)
должно стоять само по себе и оцениваться без какого-либо влияния со стороны левой части задания; он не знает, что ему будет назначено значение DateTime, допускающее значение NULL.
Это неудачно для VB.Net, но при рассмотрении типов Nullable мы должны помнить, что они были созданы с учетом концепции null
в C#, которая отличается от Nothing
в VB.Net. В частности, Nothing
на самом деле ближе к выражению default(T)
в C# (или теперь просто default
).
Поэтому часто бывает полезно рассмотреть адаптацию кода C#. В этом случае фактический прямой перевод исходной строки кода на C# выглядит так, где null
никогда не используется:
DateTime? myDate = myBool ? DateTime.Today : default;
Вы обнаружите, что это соответствует поведению VB.Net, которое вы уже наблюдали. Итак, мы видим, что окончательным результатом выражения является стандартное (не допускающее значение NULL) значение DateTime
, где Nothing
интерпретируется как значение по умолчанию для этого типа (DateTime.MinValue
)... которое выглядит как 1/1/0001 12:00:00 AM
при записи на консоль в вашей системе.
Стоит отметить, что выражение Nothing
также должно стоять отдельно в версии кода If/Else, опять же без учета левой части присваивания.
myDate = Nothing
На этот раз все работает так, как ожидалось, потому что теперь default
в переводе C# рассматривается как ссылка, а не значение, поэтому результатом является истинное null
вместо ненулевого значения по умолчанию для типа значения.
Но кроме того, этот блок Else
не нужен, и если его полностью опустить, вы получите тот же результат.
Вместо этого, чтобы добиться желаемого результата, вы можете сделать следующее:
If(myBool, Date.Today, CType(Nothing, DateTime?))
Или это:
If(myBool, Date.Today, New Nullable(Of DateTime))
В качестве альтернативы я мог бы использовать дополнительную строку:
Dim myBool As Boolean = False
Dim myDate As Date?
If myBool Then myDate = Date.Today
Console.WriteLine(myDate)
Еще один шаг к полному пониманию всего этого — это If()
, поскольку условное выражение If(myBool, Date.Today, Nothing)
не является вызовом функции. У него есть некоторая функциональная семантика, но If()
здесь на самом деле является оператором .
Это позволяет компилятору применить к нему специальную обработку, поэтому Nothing
, используемый в качестве второго аргумента, может быть оценен с учетом типа выражения Date.Today
. В противном случае каждый аргумент функции должен был бы полностью разрешаться независимо — без учета других — перед входом в функцию, и Nothing
всё равно была бы нетипизированной (или Object
) null
ссылкой.
Я верю, что это сработает.
Dim myBool As Boolean = False
Dim myDate As Date? = If(myBool, Date.Today, New Date?)
Debug.WriteLine(myDate)
Я считаю, что проблема связана с двойственной природой ключевого слова VB Nothing и путаницей, которую оно может вызвать при присвоении значения NULL типу, допускающему значение NULL.
Представляет значение по умолчанию для любого типа данных. Для ссылочных типов значением по умолчанию является нулевая ссылка. Для типов значений значение по умолчанию зависит от того, допускает ли тип значения значение NULL.
Date?
— это сокращение от Nullable(of DateTime)
.
Когда вы пишете:
Dim myDate As Date? = If(myBool, Date.Today, Nothing)
Если вы считаете, что Nothing
означает значение null, компилятор должен определить тип, возвращаемый ключевым словом If(...)
, на основе предоставленных аргументов. Date.Today
, очевидно, является типом DateTime
, а Nothing
можно интерпретировать как значение по умолчанию для типа DateTime
, поэтому он выбирает DateTime
в качестве возвращаемого типа, поскольку его можно неявно преобразовать в Nullable(of DateTime)
.
Если вы попытаетесь написать эквивалент C# как:
DateTime? myDate = myBool ? DateTime.Today : null;
Компилятор C# пометит его с ошибкой «Тип условного выражения не может быть определен, поскольку нет неявного преобразования между DateTime и <null>
». Имея это в виду, можно сказать, что это «ошибка» в компиляторе VB, которую следует устранить.
Как указывают другие ответы, решение состоит в том, чтобы код был однозначным в отношении того, что должно быть возвращено предложением If(...)
, и заменить Nothing
значением Nullable(Of DateTime)`, например:
Dim myDate As Date? = If(myBool, Date.Today, New Date?())
Кстати, эквивалент C# использует default
вместо null
, например DateTime? myDate = myBool ? DateTime.Today : default;
, что мы видим, потому что он также дублирует наблюдаемое поведение VB.Net.
Спасибо за такое подробное объяснение.
Nothing
на самом деле бытьdefault
так неинтуитивно! Проклятие VB.NET...