Background:
This is a follow-up question on this recent question I asked on how to return an array of Class
module prop
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
Option Explicit
Private Type Properties
List As Variant
End Type
Private p As Properties
Public Sub Class_Initialize()
ReDim p.List(2)
End Sub
Public Property Let Item(ByVal i As Long, ByVal NewVal As Variant)
p.List(i) = NewVal
End Property
Public Property Get Item(ByVal i As Long) As Variant
Item = p.List(i)
End Property
' Typically VBA uses the plural of the 'item name' when returning the array.
Public Function Items() As Variant
Items = p.List
End Function
Module code
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.Item(0) = x
lst.Item(1) = x
lst.Item(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.Item(0) = x
lst.Item(1) = x
lst.Item(2) = x
dict.Add x, lst
Else
With dict.Item(x)
.Item(1) = lst.Item(1) + 2
.Item(2) = lst.Item(2) + 2
End With
End If
Next x
Dim myKey As Variant
For Each myKey In dict.Keys
arr = dict.Item(myKey).GetArray
Next Key
End Sub
Set dict(keyx) = lst
Since the variable lst
refers to an object, Set
is required here.
As Tim correctly points out, Set
is required here, because lst
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 a Set
keyword, an assignment is an implicit Let
(value) assignment.
If Class1
had a default member, dict(keyx) = lst
would be let-coercing the lst
object and storing the value returned by that default member in the dictionary.
Since Class1
has no default member (and the reference isn't Nothing
), error 438 is raised (would be error 91 if lst
was Nothing
), because VBA is looking to invoke the member with a VB_UserMemId=0
attribute, and cannot find it.
The error 438 will occur on
dict(keyx) = lst
and tells me that the object (aDictionary
) doesn't support this Property or Method.
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 a context that requires a value type, the expression 'lst' of object type 'VBAProject.Class1' is used that does not have a suitable default member.
Object used where a value is required
The VBA compiler does not raise an error if an object is used in a place that requires a value type and the object's declared type does not have a suitable default member. Under almost all circumstances, this leads to a run-time error 91 'Object or With block variable not set' or 438 'Object doesn't support this property or method' depending on whether the object has the value 'Nothing' or not, which is harder to detect and indicates a bug.
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.