Nothing surprises me in VB.NET

Posted on Tue 27 March 2012 in Coding

I currently work in a VB.NET project with a customer, and what amazes me is that I over and over again get the feeling that the language is designed to be easy to work with, but at the same time there are so many aspects of it that behave in a, to me, completely unexpected and non-intuitive way. I’ve thought many times that I should write something about these different “aha moments” where something has baffled me, but that would easily turn into pointless ranting. Not so long ago, however, I stumbled across something that I do think is relevant to bring to the surface. So here it comes! :-)

Remember that my perspective here is that of a Java/C# programmer. If you’re a VB.NET programmer by heart, I assume you will spot my incorrect assumption pretty quickly.

Some background: In Java and C#, you can use the conditional ? : operator for evaluating short conditional expressions. VB.NET has the IIf function which behaves similarly, except it eagerly evaluates both the true expression and the false expression (it’s a function after all), which sometimes (most of the time?) is undesirable. However, VB 9 introduced the If operator, which properly only evaluates the expression that corresponds to the conditional value.

Now, I observed the aforementioned surprising behavior after writing a function with return type DateTime? (i.e., a nullable DateTime) in which I conditionally returned either a DateTime value or Nothing using the If operator. When I tried to save the resulting value in a database in the Nothing case I got back an exception saying that the date was way out of range! The following code snippet illustrates what I did (except the conditional value wasn’t a boolean literal, of course):

Module Module1
    Sub Main()
        Console.WriteLine(SomeDate())
    End Sub

    Function SomeDate() As DateTime?
        Return If(False, DateTime.Now, Nothing)
    End Function
End Module

What does the code above print? The answer: 0001-01-01 00:00:00, which corresponds to DateTime.MinValue. Huh? Let’s look at the corresponding code in C#:

internal class Program {
    private static void Main(string[] args) {
        Console.WriteLine(SomeDate());
    }

    private static DateTime? SomeDate() {
        return false ? DateTime.Now : (DateTime?)null;
    }
}

Note that the C# compiler doesn’t allow us to just write null as the false expression; an explicit cast to DateTime?is required. The reason is that it cannot implicitly convert between null and DateTime (and this is a clue to the final answer). The C# code produces an empty output, just as expected.

So what happened here? When googling for differences in how C# and VB.NET initialize value types, I found this StackOverflow thread, which lead me to read more about the Nothing keyword. I learned that for a non-nullable value type, Nothing evaluates to the default value of the type, which happens to be DateTime.MinValue for DateTime. So that’s what happened! But why? The answer is in the type inference taking place for the If operator; the compiler realizes that the resulting type can be non-nullable DateTime, since Nothing in that context will evaluate to the default DateTime value. Next, the result is assigned to a nullable DateTime, which is perfectly fine. In C#, as noted above and in the MSDN link, null does not behave as Nothing and a compile-time error would occur (which is why the cast is required).

So, the root cause was an incorrect assumption that Nothing behaves as null, and the subsequent use of Nothing in the If operator.

One solution is to use an explicit cast, which brings us closer to the C# version of the code:

Function SomeDate() As DateTime?
    Return If(False, DateTime.Now, DirectCast(Nothing, DateTime?))
End Function

IMHO this is a bit clumsy, and it makes the otherwise elegant conditional expression read a lot less clearly. Thus, I ended up not using the If operator at all:

Function SomeDate() As DateTime?
    If False Then Return DateTime.Now
    Return Nothing
End Function