VB_VarUserMemId
(for module-scope variables) and VB_UserMemId
(for procedures) attributes are used in VBA mostly for two things.
A List
class that would encapsulate a Collection
would want to have an Item
property, so the client code can do this:
For i = 1 To myList.Count 'VBA Collection Objects are 1-based
Debug.Print myList.Item(i)
Next
But with a VB_UserMemId
attribute set to 0 on the Item
property, the client code can do this:
For i = 1 To myList.Count 'VBA Collection Objects are 1-based
Debug.Print myList(i)
Next
Only one member can legally have VB_UserMemId = 0
in any given class. For properties, specify the attribute in the Get
accessor:
Option Explicit
Private internal As New Collection
Public Property Get Count() As Long
Count = internal.Count
End Property
Public Property Get Item(ByVal index As Long) As Variant
Attribute Item.VB_Description = "Gets or sets the element at the specified index."
Attribute Item.VB_UserMemId = 0
'Gets the element at the specified index.
Item = internal(index)
End Property
Public Property Let Item(ByVal index As Long, ByVal value As Variant)
'Sets the element at the specified index.
With internal
If index = .Count + 1 Then
.Add item:=value
ElseIf index = .Count Then
.Remove index
.Add item:=value
ElseIf index < .Count Then
.Remove index
.Add item:=value, before:=index
End If
End With
End Property
For Each
loop constructWith the magic value -4
, the VB_UserMemId
attribute tells VBA that this member yields an enumerator - which allows the client code to do this:
Dim item As Variant
For Each item In myList
Debug.Print item
Next
The easiest way to implement this method is by calling the hidden [_NewEnum]
property getter on an internal/encapsulated Collection
; the identifier needs to be enclosed in square brackets because of the leading underscore that makes it an illegal VBA identifier:
Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_Description = "Gets an enumerator that iterates through the List."
Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40" 'would hide the member in VB6. not supported in VBA.
'Gets an enumerator that iterates through the List.
Set NewEnum = internal.[_NewEnum]
End Property