patternModerate
Nullable<T> Implementation for VB6/VBA
Viewed 0 times
vb6nullableforvbaimplementation
Problem
Because I was spoiled with C# and the .NET framework, whenever I have to work with VB6 I feel like something's missing in the language. A little while ago I implemented a
Today I would have liked to be able to declare a
Now before I go any further I have to mention that I have used "procedure attributes" in the
The
```
Private Function ValidateItemType(val As Variant) As Boolean
Dim result As Boolean
If Not IsObject(val) Then
If this.TItem = vbNullString Then this.TItem = TypeName(val)
result = IsType
List for VB6 (here), and before that I implemented String.Format() and a number of string-helper functions (here). Don't go looking for a StringFormat method in the VB6 language specs, that method is the one I've written.Today I would have liked to be able to declare a
Nullable in VB6, so I implemented a class that allowed me to do that. I named this class Nullable and it goes like this:Private Type tNullable
Value As Variant
IsNull As Boolean
TItem As String
End Type
Private this As tNullable
Option Explicit
Private Sub Class_Initialize()
this.IsNull = True
End SubNow before I go any further I have to mention that I have used "procedure attributes" in the
Value property, making it the type's default member:Public Property Get Value() As Variant
'default member
Value = this.Value
End Property
Public Property Let Value(val As Variant) 'damn case-insensitivity...
'default member
If ValidateItemType(val) Then
this.Value = val
this.IsNull = False
End If
End Property
Public Property Set Value(val As Variant)
'used for assigning Nothing.
'Must be explicitly specified (e.g. Set MyNullable.Value = Nothing; Set MyNullable = Nothing will not call this setter)
Dim emptyValue As Variant
If val Is Nothing Then
this.IsNull = True
this.Value = emptyValue
Else
Err.Raise vbObjectError + 911, "Nullable", "Invalid argument."
End If
End PropertyThe
ValidateItemType private method determines whether the type of a value is "ok" to be assigned as the instance's Value:```
Private Function ValidateItemType(val As Variant) As Boolean
Dim result As Boolean
If Not IsObject(val) Then
If this.TItem = vbNullString Then this.TItem = TypeName(val)
result = IsType
Solution
I think the itself class might be mis-named, because it is really 'Empty-able' not Nullable or 'Nothing-able'.
You have to keep in mind that Empty, Null, and Nothing are very different concepts in VB6. Setting and object to Nothing is basically just syntactic sugar for releasing the pointer to the Object. This is the same as asking for ObjPtr() to return Null for that instance (although there is no way to test this in VB6 - see the code and explanation below).
Null is actually better to conceptualize in VB6 as a type rather than an uninitialized variable, as the code below demonstrates:
This brings me to the explanation of why your class should really be referred to as 'Empty-able'. A Variant is best thought of as an object with 2 properties - a type and a pointer. If it is uninitialized, it basically has a pointer to Nothing and a type of Empty. But is isn't Null, because the Variant itself still exists with its default "properties".
However if I do Set n = Nothing, not only Debug.Print n Is Nothing
will print False, the instance gets reset to a Nullable and
...the setter (Public Property Set Value) does not get called
This is because of VB6's obnoxious default behavior when you use a reference to an object that was set to nothing. It "helpfully" creates a new object for you as can be verified by the code below - before the second call to ObjPtr(temp), it implicitly runs
VB6 treats setting an Object equal to Nothing as a special case, so it never calls the Property Set. What is it basically doing is:
EDIT:
Excellent explanation of how Variants work under the hood here.
You have to keep in mind that Empty, Null, and Nothing are very different concepts in VB6. Setting and object to Nothing is basically just syntactic sugar for releasing the pointer to the Object. This is the same as asking for ObjPtr() to return Null for that instance (although there is no way to test this in VB6 - see the code and explanation below).
Null is actually better to conceptualize in VB6 as a type rather than an uninitialized variable, as the code below demonstrates:
Dim temp As Variant
'This will return "True"
Debug.Print (temp = Empty)
'This will return "False"
Debug.Print (IsNull(temp))
temp = Null
'This will return "True"
Debug.Print (IsNull(temp))
'This will return "Null"
Debug.Print (TypeName(temp))This brings me to the explanation of why your class should really be referred to as 'Empty-able'. A Variant is best thought of as an object with 2 properties - a type and a pointer. If it is uninitialized, it basically has a pointer to Nothing and a type of Empty. But is isn't Null, because the Variant itself still exists with its default "properties".
However if I do Set n = Nothing, not only Debug.Print n Is Nothing
will print False, the instance gets reset to a Nullable and
...the setter (Public Property Set Value) does not get called
This is because of VB6's obnoxious default behavior when you use a reference to an object that was set to nothing. It "helpfully" creates a new object for you as can be verified by the code below - before the second call to ObjPtr(temp), it implicitly runs
Set temp = New Test. You should be able to verify this with a Debug.Print in Class_Initialize().Private Sub Testing()
Dim temp As New Test
Debug.Print (ObjPtr(temp))
Set temp = Nothing
'The code below instantiates a new Test object, because it is used after being released.
Debug.Print (ObjPtr(temp))
End SubVB6 treats setting an Object equal to Nothing as a special case, so it never calls the Property Set. What is it basically doing is:
AddressOf(n) = AddressOf(Nothing).EDIT:
Excellent explanation of how Variants work under the hood here.
Code Snippets
Dim temp As Variant
'This will return "True"
Debug.Print (temp = Empty)
'This will return "False"
Debug.Print (IsNull(temp))
temp = Null
'This will return "True"
Debug.Print (IsNull(temp))
'This will return "Null"
Debug.Print (TypeName(temp))Private Sub Testing()
Dim temp As New Test
Debug.Print (ObjPtr(temp))
Set temp = Nothing
'The code below instantiates a new Test object, because it is used after being released.
Debug.Print (ObjPtr(temp))
End SubContext
StackExchange Code Review Q#41601, answer score: 10
Revisions (0)
No revisions yet.