Background:
This is a follow-up question on this recent question I asked on how to return an array of Class
module properties directly from an Dictionary
item.
I have now tried my way to work with Property Let
and Property Get
to populate a Private Array
to populate the Dictionary
with. However, running some test I encountered an Error 438
.
Code:
Imagine TstClass
as a class module with the following code:
Private lst(0 To 2) As Variant
Public Property Let Add(ByVal i As Long, ByVal NewVal As Variant)
lst(i) = NewVal
End Property
Public Property Get Val(ByVal i As Long) As Variant
Val = lst(i)
End Property
Public Function GetArray() As Variant
GetArray = vals
End Function
Then this code to test in a Module:
Sub Test()
Dim x As Long, arr As Variant, lst As Class1
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
For x = 1 To 3
Set lst = New Class1
lst.Add(0) = x
lst.Add(1) = x
lst.Add(2) = x
dict.Add x, lst
Next x
For x = 4 To 3 Step -1
If dict.Exists(x) = False Then
Set lst = New Class1
lst.Add(0) = x
lst.Add(1) = x
lst.Add(2) = x
dict.Add x, lst
Else
Set lst = dict(x)
lst.Add(1) = lst.Val(1) + 2
lst.Add(2) = lst.Val(2) + 2
dict(x) = lst '< Error 438 on this line
End If
Next x
For Each key In dict.keys
arr = dict(keys).GetArray
Next key
End Sub
Problem:
The error 438 will occur on dict(keyx) = lst
and tells me that the object (a Dictionary) doesn't support this Property
or Method
. The problem seems dodgy to me as the lst
Object didn't seem to be a problem on dict.Add x, lst
. As a matter of fact, the method to change an Item
through its Key
like this seems to be a very common practice.
Question:
Whereas something like Dict.Add x, "Hello"
and then Dict(x) = "Hello World"
seems to work. The code errors out on using a Class
object in the second method. Does anybody know why and if so, how to deal with this problem?
Thank you, JvdV
The OP code has quite a few typos, undeclared variables and usable but not 'correct' property Let/Get. This is how I would write the code of the op. Having now been well trained by the code inspections of the fantastic RubberDuck addin I no longer use 'default' properties but ensure that I use the fully qualified name.
Class code
' Typically VBA uses the plural of the 'item name' when returning the array.
Module code
Since the variable
lst
refers to an object,Set
is required here.As Tim correctly points out,
Set
is required here, becauselst
is an object.What isn't clear, is why a missing
Set
keyword would cause run-time error 438, an error that's typically encountered with buggy late-bound code. The reason is, in a word, let-coercion: in the absence of aSet
keyword, an assignment is an implicitLet
(value) assignment.If
Class1
had a default member,dict(keyx) = lst
would be let-coercing thelst
object and storing the value returned by that default member in the dictionary.Since
Class1
has no default member (and the reference isn'tNothing
), error 438 is raised (would be error 91 iflst
wasNothing
), because VBA is looking to invoke the member with aVB_UserMemId=0
attribute, and cannot find it.The object isn't the dictionary here, but your
Class1
instance; the missing "property or method" is that object's default member, implicitly invoked through let-coercion. And since the member call is implicit, it's easily missed, and the error number/message is easily confusing.Rubberduck (free, open-source VBIDE add-in project that I manage) fires an inspection result for Value Required here, that explains exactly what's going on:
In other words, that's one of many places where the VBA compiler defers to run-time instead of warning of something fishy at compile-time. Static code analysis is therefore the only way to reliably detect such bugs before they show up at run-time.