December 29th | 2009
Dealing with Nullable Types and IIf in VB.NET
Here’s a tip that might bite some C# developers getting their feet wet with VB.NET. In fact, I got bit by this one recently, and it didn’t help that it was knee deep in COM. Let’s examine this harmless looking C# statement:
int? someSpoon; string helloValue = someSpoon.HasValue ? "The spoon is " + someSpoon.Value : "There is no spoon";
This is a pretty typical use of the ternary operator. If someSpoon has a value, then the helloValue is “The spoon is <value>”, if it doesn’t have a value, it is “There is no spoon”. This is a safe use because someSpoon.Value will not be evaluated unless the condition is true.
VB.NET does not have a built-in operator similar to this, however it does have a function called IIf. Its syntax is this:
Function IIf(ByVal expression As Boolean, _ ByVal truePart As Object, _ ByVal falsePart As Object)
We’d be tempted to write something like this with our VB.NET IIf:
Dim someSpoon As Integer? IIf(someSpoon.HasValue, _ "The spoon is " & someSpoon.Value, _ "There is no spoon")
But wait! We just introduced a bug. Take a minute to figure it out.
Got it? OK let’s see if we agree. The problem is that as IIf is just a plain function, someSpoon.Value will always be evaluated. If you call .Value on a nullable type without a value, you’ll get an exception. This is because the Value must be resolved before it is passed into IIf. C# doesn’t exhibit this behavior because the true part is completely ignored.
So, what’s the fix?
You might be tempted to switch to an If/Else statement but I chose to write my own IIf using lambdas. It’s called SIIf—S is for safe—and it doesn’t exhibit this behavior. Instead of passing in the Value, we pass in a function that will be invoked to get the Value if it’s needed.
Here’s the source for it:
Public Function SIIf(Of T)(ByVal expression As Boolean, _ ByVal trueExpression As Func(Of T), _ ByVal falseExpression As Func(Of T)) If expression Then Return trueExpression() End If Return falseExpression() End Function
I also added generics—another annoyance to VB.NET developers who use Option Explicit. Here’s how it would be used:
Dim someSpoon As Integer? = Nothing Dim result As String = SIIf(Of String)(someSpoon.HasValue, _ Function() "The spoon is " & someSpoon.Value, _ Function() "There is no spoon")
All we really have to do is remember to put Function() before the 2nd and 3rd parameters. This prevents our exception from happening because Value is only ever invoked if the expression is true by the lambda.
Update: As some of you have already pointed out, this functionality is built into the .NET Framework 3.5 using a three argument If statement, which I overlooked. I would recommend using this over my own solution if possible.