Search code examples
vb.netnullableshort-circuiting

Set Nullable property default value to Nothing not working as desired


I have a property which type is Nullable of Integer an default value Nothing as shown below:

Property TestId As Integer? = Nothing

the following code evaluates the property TestId to Nothing (as wanted)

Dim test As RadTreeNode = rtvDefinitionCreate.FindNodeByValue(DefinitionHeaderEnum.Test)
If test Is Nothing Then
    definition.TestId = Nothing
Else
    definition.TestId = test.Nodes(0).Value
End If

but the code below evaluates as 0 (default value for Integer, even when is Integer? with default value Nothing)

Dim test As RadTreeNode = rtvDefinitionCreate.FindNodeByValue(DefinitionHeaderEnum.Test)
definition.TestId = If(IsNothing(test), Nothing, test.Nodes(0).Value)

What is wrong with the above code? Any Help??

(later in the code when call the property, the property has 0)


Solution

  • It' because you are compiling you code with Option Strict Off.

    If you would compile your code with Option Strict On, the compiler would give you an error, telling you that it can't convert from String to Integer?, avoiding such suprises at runtime.


    This is a weirdness when using properties/ternary operator/option strict off in VB.NET.

    Consider the following code:

    Class Test
        Property NullableProperty As Integer? = Nothing
        Public NullableField As Integer? = Nothing
    End Class
    
    Sub Main()
        ' Setting the Property directly will lest the ternary operator evaluate to zero
        Dim b = New Test() With {.NullableProperty = If(True, Nothing, "123")}
        b.NullableProperty = If(True, Nothing, "123")
    
        ' Setting the Property with reflection or setting a local variable 
        ' or a public field lets the ternary operator evaluate to Nothing
        Dim localNullable As Integer? = If(True, Nothing, "123")
        Dim implicitLocal = If(True, Nothing, "123")
        b.NullableField = If(True, Nothing, "123")
        b.GetType().GetMethod("set_NullableProperty").Invoke(b, New Object() {If(True, Nothing, "123")})
        b.GetType().GetProperty("NullableProperty").SetValue(b, If(True, Nothing, "123"), Nothing)
    End Sub
    

    Another difference to consider:

    Dim localNullable As Integer? = If(True, Nothing, "123") 
    

    will evaluate to Nothing but

    Dim localNullable As Integer? = If(SomeNonConstantCondition, Nothing, "123") 
    

    will evaluate to 0


    You can create an extension method to do the nasty work for you.

    <Extension()>
    Function TakeAs(Of T, R)(obj As T, selector As Func(Of T, R)) As R
        If obj Is Nothing Then
            Return Nothing
        End If
        Return selector(obj)
    End Function
    

    and call it like

    definition.TestId = test.TakeAs(Of Int32?)(Function(o) o.Nodes(0).Value)