VBA Attributes VB_[Var]UserMemId


Example

VB_VarUserMemId (for module-scope variables) and VB_UserMemId (for procedures) attributes are used in VBA mostly for two things.

Specifying the default member of a class

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

Making a class iteratable with a For Each loop construct

With 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