.netvb.net.net-4.8

Assigning nullable DateTime with inline IfElse sets variable to 01/01/0001 12:00:00 AM


In VB.NET the following code:

Dim myBool As Boolean = False
Dim myDate As Date? = If(myBool, Date.Today, Nothing)
Console.WriteLine(myDate)

Produces: 1/1/0001 12:00:00 AM

However, if I write this out as:

Dim myBool As Boolean = False
Dim myDate As Date?

If myBool Then
    myDate = Date.Today
Else
    myDate = Nothing
End If
Console.WriteLine(myDate)

Then it will, as expected, print null/nothing.

What's going on here, and how can I have a one-liner to assign a date or null to a nullable DateTime in VB.NET without it in fact defaulting to 01/01/0001 12:00:00 AM?


Solution

  • The If(myBool, Date.Today, Nothing) expression must stand on its own, and is evaluated without any influence at all from the left-hand side of the assignment; it doesn't know it will be assigned to a Nullable DateTime.

    It's unfortunate for VB.Net, but when considering Nullable types we must remember these were built thinking first about C#'s concept of null, which is not the same as Nothing in VB.Net. Specifically, Nothing is actually closer to C#'s default(T) expression (or now just default).

    It's therefore often useful to consider the C# adaption of the code. In this case, the actual direct C# translation of the original line of code looks like this, where null is never used:

    DateTime? myDate = myBool ? DateTime.Today : default;
    

    You'll find this matches the VB.Net behavior you've already observed. So we see the final result of the expression is a standard (not nullable) DateTime value, where Nothing is interpreted as the default value for this type (DateTime.MinValue)... which looks like 1/1/0001 12:00:00 AM when written to the Console on your system.


    It's worth noting the Nothing expression must also stand on it's own in the If/Else version of the code, again without any regard for the left-hand side of the assignment.

    myDate = Nothing
    

    It works as expected this time because now the default in the C# translation is seen as a reference instead of a value, so the result is a true null instead of a value-type's non-null default.

    But also, this Else block is not needed, and if completely omitted you'd have the same result.


    To create the desired result you can instead do this:

    If(myBool, Date.Today, CType(Nothing, DateTime?))
    

    Or this:

    If(myBool, Date.Today, New Nullable(Of DateTime))
    

    Alternatively, I might use an extra line:

    Dim myBool As Boolean = False
    Dim myDate As Date?
    If myBool Then myDate = Date.Today
    Console.WriteLine(myDate)
    

    One final wrinkle to fully understanding all of this is the If() for the If(myBool, Date.Today, Nothing) conditional expression is not a function call. It does have some function semantics, but the If() here is actually an operator.

    This allows the compiler to give it special treatment, so the Nothing used as the second argument can be evaluated to take the type of the Date.Today expression into account. Otherwise, each function argument would have to fully resolve independently — without regard for the others — before entering the function, and the Nothing would still be an un-typed (or Object) null reference.