Here's the bottom line first. You can write code that assigns the value Nothing to value types. For example:
Dim myNullableBoolean As Boolean?
myNullableBoolean = Nothing
But to really understand what is happening here, it's necessary to go back to basics. The first "basic" is "value" types versus "reference" types. The reason this is necessary is because value types (before .NET 2.0) could not be given this kind of value for reasons that went right to the core of the way Windows works. It's the only way to understand what changed.
There are two ways to store information in program memory. Both start with something called the stack - the computer memory in your program that is managed directly by .NET. For example, stack memory is reclaimed immediately after being released and .NET always knows where information is stored in the stack.
Value Types - Stored Directly on the Stack
The first way to store information is to save the actual value itself in the stack. Types that are stored this way are called value types and include most of the fundamental types you use. Here's the complete list of nullable value types:
- Byte
- SByte
- Short
- Integer
- Long
- UShort
- UInteger
- ULong
- Single
- Double
- Boolean
- Char
- Date
So, if you code:
Dim myInt as Integer = 1
... the Integer value 1 is stored directly in the stack.
Reference Types - The Address of the Value is Stored on the Stack
The second way to store information is to save the address of the information on the stack instead. This address is called a reference. (In other languages, this address is called a pointer, but that term isn't used in VB.) The value is then saved at the address in a different part of memory called the heap. You can't save value types in the heap directly. But most other objects are saved as reference types. In particular, a Class is saved in the heap. So, if you code:
Public Class myReferenceType
Public myRefValue As Integer
End Class
Then every instance of myReferenceType is stored in the heap. Note that I didn't say, myReferenceType itself was stored in the heap. It doesn't exist until you actually create an instance of it.
Dim x As New MyReferenceType
x.myRefValue = 1
One thing to keep in mind that a String is not a value type. Strings are actually arrays of characters and are reference types. Therefore, this code displays, "Y is Nothing" in the Output window.
Dim Y As String
If Y Is Nothing Then Console.WriteLine("Y is Nothing")
One type that is always stored in the heap is the Object type. For this reason, when a value type is saved as an Object, the information is saved in the heap. This process is called boxing and unboxing.
Dim X As Integer = 1
'X is boxed in Object Y
Dim Y As Object = X
'Y is unboxed back into X
X = CInt(X)
But suppose you code something like this:
Dim Y As Object
Console.WriteLine(Y.ToString)
This results in the dreaded, "Object reference not set to an instance of an object." error. That means there is nothing in the heap memory. This brings us back to our original topic. If you code:
Dim Y As Object
If Y Is Nothing Then
Console.WriteLine("Y is Nothing")
Else
Console.WriteLine(Y.ToString)
End If
... you get "Y is Nothing" in the Output window. That means that no instance of the Object has been created yet, just a place for the address on the stack if and when one is created.
(Another minor myth is that setting an Object to Nothing will destroy it. No way. The heap is managed by .NET "garbage collection" and space on the heap is reclaimed only through that mechanism. Setting an Object to Nothing only makes it available to be reclaimed through garbage collection.)
This brings us back to the original topic. Before .NET 2.0, it was impossible to create a value type that was Nothing because, since the actual information stored in a value type was on the stack, it always existed. This code:
Dim X As Integer
Console.WriteLine(X)
... will display the value 0 in the Output window since that's the default value of an Integer when nothing has been assigned to it yet. This causes problems in some situations because sometimes, you really do want "nothing" to be assigned to a value type. The most common case is a database application where an uninitialized field really is "nothing" - not a default value. (This is represented in VB.NET using a special class with just one member: DBNull. More about that later.) Another example might be attributes in HTML statements that your program is building. An attribute can have a value or be missing entirely. Normally, your program will need to know the difference. Before nullable types, you would have to declare a different variable to handle the "missing entirely" case.
In VB.NET, these "nullable types" are used to require the use of "generics". The initial example ...
Dim myNullableBoolean As Boolean?
... could have been coded ...
Dim myNullableBoolean As Nullable(Of Boolean)
These types are not the same types as the value types that we have had all along. These are new types that didn't exist before. The standard Boolean type, for example, still isn't nullable. But you can create a type that is also 'Boolean' except that it is nullable. In other words, a nullable Boolean type, in addition to the two values True and False, can also be Nothing.
On the next page, we get into specific nullable types syntax introduced VB.NET 2008 and later.
