8128
8128

Reputation: 999

Odd interaction of "If" and Nullable(Of Integer)

I'm using Nullable(Of Integer) and have just been stung by Nothing being cast to 0. That's excatly what I don't want when using Nullable(Of Integer).

func1 below doesn't behave as I'd expect. I can get it to do my will by ammending it (see func2). But I don't see why this should be necessary (and I think I might find it hard to remember to do it).

Why isn't func1 doing what I want? I think I've encountered this before, and I'd rather not see it again.

Function func1(parameter As Integer) As Nullable(Of Integer)
    Return If(parameter > 10, parameter, Nothing)
End Function

Function func2(parameter As Integer) As Nullable(Of Integer)
    Return If(parameter > 10, parameter, DirectCast(Nothing, Integer?))
End Function

Sub Main
    ' Should be True
    System.Console.WriteLine(func1(11).HasValue)
    System.Console.WriteLine(func2(11).HasValue)
    System.Console.WriteLine()

    ' Should be False
    System.Console.WriteLine(func1(9).HasValue)
    System.Console.WriteLine(func2(9).HasValue)     
End Sub

The results I get (running this in LINQPad) are:

True
True

True
False

Upvotes: 0

Views: 65

Answers (3)

dbasnett
dbasnett

Reputation: 11773

Here is func1 rewritten. Note that there is no need for casting.

Function func1(parameter As Integer) As Nullable(Of Integer)
    Dim rv As New Nullable(Of Integer)
    If parameter > 10 Then
        rv = parameter
    End If
    Return rv
End Function

The If operator, If(foo,foo=true,foo=false), should be used sparingly because it is slower than the standard If construct.

edit: The statement about the If operator is incorrect.

Thanks to Chris.

Upvotes: 0

IronAces
IronAces

Reputation: 1883

To explain what is happening here, I'll start by removing the shorthand part of your code which may help with my explanation.

Integers in VB.net cannot be assigned using Null or DBNull. IT can be assigned using Nullable-of-T as you have. However, as soon as you make the object Nullable-ish, it can be evaluated to be 0.

Consider the following

dim x as Integer = nothing 'evaluates to x=0

So when your function runs, you use DirectCast() to return a nullable-ish Integer, which then evaluates to be not nullable by func2

Function func1(parameter As Integer) As Nullable(Of Integer)
    Return If(parameter > 10, parameter, Nothing)
End Function

Function func2(parameter As Integer) As Nullable(Of Integer)
    Return If(parameter > 10, parameter, DirectCast(Nothing, Nullable(of Integer)))
End Function

Sub Main()
    ' Should be True
    System.Console.WriteLine(func1(11).HasValue)
    System.Console.WriteLine(func2(11).HasValue)
    System.Console.WriteLine()

    ' Should be False
    System.Console.WriteLine(func1(9).HasValue)
    System.Console.WriteLine(func2(9).HasValue)
    Console.ReadLine()
End Sub

Upvotes: 1

Fabio
Fabio

Reputation: 32445

Facts important in your case:

  • Inline If method expected that both "true" and "false" expressions must return same type.
  • Nothing is default value of type.
    For Integer it is 0.
    For reference type it is null

In first method inline If method expects that "False" expression must return an Integer, because compiler cannot decide return type based on Nothing, it will use type produced by "True" expression. So Nothing will produce default value of Integer type which is 0.

In second method both parameters have explicitly declared returned types, where Integer can be implicitly converted to the Nullable, so compiler will return Nullable as result of If method.

The key role in your problem is inline If method. Which uses Nothing as default value of Integer.

If you rewrite it as normal If .. Else then everything works without DirectCast

Private Function GetNullableInteger(parameter As Integer) As Integer?
   If parameter > 10 Then
        Return parameter
   Else
        Return Nothing
   End If
End Function

Upvotes: 2

Related Questions