Store Nothing in Nullable Double wtih if statement - vb.net

I just stumbled upon something really weird with nullable variables:
'Normal case
Dim myNumber As Double? = Nothing
Dim hasValue As Boolean = myNumber.HasValue 'False as expected
'Weird case
Dim myNumber2 As Double? = If(True, Nothing, 42)
Dim hasValue2 As Boolean = myNumber2.HasValue 'True ?! (myNumber2 == 0)
Why does the if store a 0 instead of Nothing in my nullable Double ?

The If(True, Nothing, 42) is what is throwing you off. For the IF statement, both results need to be the same type. Since Nothing is not a type, VB automatically looks at the second result and casts the Nothing to a double which results in 0.0.

Related

How to affect to a boolean value the result of a boolean? expression

I would like to code in VB the equivalent of this in C#:
bool? a = whatever;
bool b= (a==true);
VB compiler does not accept this:
Dim a As Boolean?
Dim b As Boolean = (a = True)
I suppose in this context, it interprets (a = True) as an affectation while I want it to be interpreted as an expression.
(a == True) is apparently a syntax error.
You can use the GetValueOrDefault-method:
Dim a As Boolean?
Dim b As Boolean = a.GetValueOrDefault()
You could also use CBool
Dim a As Boolean?
Dim b As Boolean = CBool(a = True)
You need to be careful with the differences between 0, Nothing, and vbNull.
0 is a default value for a Boolean.
vbNull is a reserved Null value which should translate as 1.
Nothing will throw an exception in almost all circumstances.
Dim a As Boolean? = Nothing
Dim b As Boolean? = vbNull
Dim c As Boolean = vbNull
Dim d As Boolean
Print(a = True) 'will throw an Exception
Print(b = True) 'will return True (as vbNull = Int(1))
Print(c = True) 'will return True as the ? is unnecessary on a Boolean as vbNull = Int(1)
Print(d = True) 'will return False as the default value of a Boolean is 0
Print(a.GetValueOrDefault) 'will return False as this handles the Nothing case.
When working with unassigned values you should always check for Nothing first (or just follow good practice and set the value before you use it).
Dim a As Boolean?
Dim b As Boolean = IIf(IsNothing(a), False, a)
This will return False if a is Nothing, otherwise return A.
Only after testing for Nothing can you test for vbNull, as Nothing returns an error on all values. The code below will return False if Nothing or vbNull, or a otherwise.
Dim a As Boolean?
Dim b As Boolean = IIf(IsNothing(a), False, IIf(a = vbNull, False, a))
Note: you cannot use the code below as the test a = vbNull will be against Nothing which will throw an Exception.
Or(IsNothing(a), a = vbNull)
I would also avoid using GetValueOrDefault in any real application, as when you start using more complicated data types the default won't be so simple and you can get unexpected results. It's far better IMHO to test for IsNothing (or Object = Nothing, Object Is Nothing) than to rely on quirks of a datatype.
Best practice would be to ensure a has a value, which you can do using
Dim a As Boolean? = New Boolean()
Dim b As Boolean = a
The reason I say this is the best practice is because it translates to all Classes, not just Booleans. Noted that this is overkill for Booleans.
Hope this helps.

VB.NET 2015 Nullable Integer Not Working with If() operator?

Why is this result 0 (zero) and NOT Nothing?
Dim foobar As Integer? = If(False, 1, Nothing)
From Microsoft Documentation:
If a variable is of a value type, the behavior of Nothing depends on
whether the variable is of a nullable data type. To represent a
nullable value type, add a ? modifier to the type name. Assigning
Nothing to a nullable variable sets the value to null. For more
information and examples, see Nullable Value Types.
Further research yields even more interesting results:
Dim foo As Integer? = If(False, 1, Nothing) '0
Dim bar As String = If(False, 1, Nothing) '"0"
Dim bar2 As String = If(False, "1", Nothing) 'Nothing
Dim bar3 As Integer? = If(False, "1", Nothing) 'Nothing
Dim bar4 As Integer? = If(False, CStr(1), Nothing) '0
Does the following suggest that the result type is predicated on the type second value?
Dim bar3 As Integer? = If(False, "1", Nothing) 'Nothing
EDITED with additional finding and desired result, but why should BOTH arguments need to be evaluated and not just the short-circuited version of If()?
Dim foo As Integer? = IIf(False, 1, Nothing) 'Nothing
A fix for If operator,
Dim foo As Integer? = If(False, 1, New Nullable(Of Integer))
and one other
Dim foo As Integer?
If True Then
foo = 1
End If
In addition to dbasnett's answer, you can do:
Dim foo As Integer? = If(False, 1, CType(Nothing, Integer?))

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)
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)

Mystery of the If() function in VB.net

I have this code:
dstCell.CELL_VALUE_INT = If(srcCell.CELL_VALUE_FLOAT IsNot Nothing,
Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT)),
Nothing)
when srcCell.CELL_VALUE_FLOAT is Nothing it mysteriously evaluates to the True part!
Funny part is that a normal If statement correctly evaluates to the False part:
If (srcCell.CELL_VALUE_FLOAT IsNot Nothing) Then
dstCell.CELL_VALUE_INT = Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT))
Else
dstCell.CELL_VALUE_INT = Nothing
End If
Any ideas?
Thank u!
EDIT:
CELL_VALUE_FLOAT is a Nullable(Of Double) and CELL_VALUE_INT is a Nullable(of Integer)
In Quickwatch the condition evaluates correclty to False, but when running the If() function evaluates to the True part.
when srcCell.CELL_VALUE_FLOAT is Nothing it mysteriously evaluates to the True part!
Nope, it does not. It just evalues the false part (Nothing) as 0, thus setting CELL_VALUE_INT to 0.
Let me elaborate: The expression
Dim i As Integer? = If(False, 1, Nothing)
fills i with 0. (Test it, if you don't believe me.)
Why does this happen? Nothing in VB.NET is not the same as null in C#. If used with a value type, Nothing means "the default value of that type". If infers Integer (not Integer?) as the common type for 1 and Nothing, and, thus, evaluates Nothing as default(Integer) = 0.
You can fix this as follows:
Dim i As Integer? = If(False, 1, DirectCast(Nothing, Integer?))
which, in your example, would mean
dstCell.CELL_VALUE_INT = If(srcCell.CELL_VALUE_FLOAT IsNot Nothing,
Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT)),
DirectCast(Nothing, Integer?))
This should yield the correct value now.
Since this is quite surprising behaviour, I have filed a Microsoft Connect suggestion some time ago to add a compiler warning.
Nothing in VB.NET is not fully equal to null in C#
It is more like default(T) where T is a Type.
' VB:
dim x as DateTime = DateTime.MinValue
If x Is Nothing then
Console.WriteLine("True")
End if
' C#
var x = DateTime.MinValue
if (x == default(DateTime))
Console.WriteLine("True");
if (x == null) ' throw a compile time error
And
dim x as Double = nothing ' x will be 0 (default for Double)
the build in inline if expects both return values to be the same type. So what you a really doing is:
dstCell.CELL_VALUE_INT = If(srcCell.CELL_VALUE_FLOAT IsNot Nothing,
Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT)),
Convert.ToDouble(Nothing))
since the false part gets internally converted to double
and dstCell.CELL_VALUE_INT will be 0 instead of nothing.
Try this one:
dstCell.CELL_VALUE_INT = If(srcCell.CELL_VALUE_FLOAT IsNot Nothing,
Ctype(Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT)), Integer?),
Nothing)
Assuming that your value is a Single(Float):
The default value of Single is 0 not Nothing.
You can use a Nullable(Of T) if you want to check for null values.
Dim srcCell.CELL_VALUE_FLOAT As Nullable(Of Single)
srcCell_CELL.VALUE_FLOAT = Nothing
Dim dstCell.CELL_VALUE_INT As Nullable(Of Int32) = _
(If(srcCell.CELL_VALUE_FLOAT.HasValue,
CInt(Math.Round(CDbl(srcCell.CELL_VALUE_FLOAT))),
Nothing))

VB.NET: How do I use coalesce with db column values and nullable types? Or is there a better solution?

I'm trying to do something similar to what's described here, but with nullable types.
http://www.csharp-station.com/Tutorials/Lesson23.aspx
int availableUnits = unitsInStock ?? 0;
In VB, it would be this:
Dim availableUnits As Int32 = If(unitsInStock, 0)
However I'm working with db columns, which could be DbNull, and nullable types, which can be Nothing (which is different to DbNull). If a column is DbNull, I want to return Nothing, otherwise return the value. For eg:
Dim availableUnits As Int32? = If(myDataReader("UnitsInStock").Value, Nothing)
The error I'm getting is "Specified cast is not valid" but I'm not sure why. I've tried this as well:
Dim availableUnits As Int32? = If(isDbNull(myDataReader("UnitsInStock").Value), myDataReader("UnitsInStock").Value, Nothing)
Which is messy and just results in the same error. The only thing that works is this:
Dim availableUnits As Int32?
If isDbNull(myDataReader("UnitsInStock").Value) Then
availableUnits = myDataReader("UnitsInStock").Value
Else
availableUnits = Nothing
End If
Which is just silly. Is there a better way of getting nullable db values into nullable variables that I'm not aware of?
If using DataSets instead of DataReaders is an option for you, the DataRow.Field method knows how to handle Nullable variables properly:
Dim availableUnits = myDataRow.Field(Of Int32?)("UnitsInStock")
It is silly, but you have to cast the Nothing when you set it:
Dim availableUnits As Int32? = If(myDataReader.IsDBNull("UnitsInStock"), _
CType(Nothing, Int32?), _
myDataReader.GetInt32("UnitsInStock"))
As I mention in what turns out to be a very similar question you are best served by not using Nothing here, but rather New Int32?:
Dim availableUnits = If(myDataReader.IsDBNull("UnitsInStock"), _
New Int32?, _
myDataReader.GetInt32("UnitsInStock"))