Search notes:

VBA: classes

Classes require a class module whose name determines the name of the class it contains.

Constructor, destructor

Defining a class with a constructor and a destructor.
The name of the constructor is class_initialize, the name of the destructor is class_terminate, irrespective of the actual name of a class.
It is not possible to use parameters (arguments) in a constructor!
option explicit

private sub class_initialize() ' No parameters possible!
    msgBox "Constructor of class was called"
end sub

private sub class_terminate()
    msgBox "Destructor of class was called"
end sub
Github repository about-VBA, path: /language/classes/constructor-destructor/tq84.bas
Note again: classes (such as the above) need to be inserted into a VBA project as class module, not as a »standard« module.
Initializing the class (calls the constructor and the destructor):
option explicit

sub main()

    dim obj   as  tq84

    msgBox "Before constructor"

    set obj = new tq84

    msgBox "After constructor"

end sub
Github repository about-VBA, path: /language/classes/constructor-destructor/main.bas
The combination of class_initialize and class_terminate is particularly useful to implement the resource acquisition is initialization pattern in VBA.

Member variables

Defining a class with three member variables of which two are public and one is private.
option explicit

'
' Declare three member variables for the class.
'
' Note: member variables are not declared with dim, rather
' one of the keywords public or private is used.
'
' If they're public, they can be accessed from »outside« the
' class. If private, they can only be accessed within the class.
'

public  foo as double
public  bar as string
private baz as long

private sub class_initialize()
  '
  ' Within a class method, members of the class can
  ' optionally be referred to with »me«. In other
  ' programming languages, that would be self or this.
  '
    me.foo = 1.23456
       baz = 42
end sub

private sub class_terminate()
    msgBox "foo = " & foo & ", bar = " & bar & ", baz = " & baz
end sub
Github repository about-VBA, path: /language/classes/member-variables/tq84.bas
Using the class
option explicit

sub main()

    dim obj as new tq84

    obj.bar = "hello world"

  '
  ' The member baz is declared private, it
  ' cannot be assigned outside of the class:
  '
  ' obj.baz = 99

end sub
Github repository about-VBA, path: /language/classes/member-variables/main.bas

Member functions

Here's a class with a member function and a member sub:
option explicit

private value as long

public sub store_value(value_ as long)

  ' Apparently, the »me« keyword cannot be used
  ' in member subs and member functions …

    value = value_
end sub

public function retrieve_value as long
    retrieve_value = value
end function
Github repository about-VBA, path: /language/classes/member-functions/tq84.cls
After instantiating the class with the new operator, it can be used to store and retrieve a value:
option explicit

sub main()

    dim obj as new tq84

    obj.store_value(42)

    msgBox "Stored value is " & obj.retrieve_value

end sub
Github repository about-VBA, path: /language/classes/member-functions/main.bas

property let/set

After defining property let and property set functions, the person's name and age can be set with the familiar VBA constructs obj.member = var and var = obj.member.
Here, we're definining a class for a person with such property set and let functions. As usual with simple examples, a person has a name and an age.
'
'      Beware the »Definitions of property procedures for the same property are inconsistent« error.
'
option explicit

private nm as string
private ag as long


property let name (name_ as string)

  '
  ' Apparently, in property let/set functions,
  ' the »me« keyword is not allowed …
  '

    nm = name_
end property

property let age (age_   as long  )
    ag = age_
end property

property get name() as string
    name = nm
end property

property get age() as long
    age = ag
end property

property get text() as string
    text = "My name is " & nm & " and I am " & age & " years old."
end property
Github repository about-VBA, path: /language/classes/let-set/person.bas
The corresponding sub that uses the class:
option explicit

sub main()

   dim p1 as new person
   dim p2 as new person

   p1.name = "Fred" : p1.age = 71
   p2.name = "Jane" : p2.age = 68

   msgBox p1.text & chr(13) & p2.text

end sub
Github repository about-VBA, path: /language/classes/let-set/main.bas
TODO: Compare with property set method.

Copying and the new operator

In order to create a new instance of a class, the keyword new must be used. This is demonstrated in the following example.
First, a new instance of the tq84 class is cretead with new tq84 and its reference assigned to the obj_1 variable.
Then, the obj_1 is assigned to obj_2. Since there is no invocation of new, no new instance is created. Thus, the constructor class_initialize is not called and both obj_1.m and obj_2.m point to the same string.
In order for the destructor class_terminate to be called, both references to the class need to be set to nothing.
option explicit

sub main()

    dim obj_1 as tq84
    dim obj_2 as tq84

    debug.print "set obj_1 = new tq84"
    set obj_1 = new tq84

  '
  ' Setting obj_1's member m to foo:
  '
    obj_1.m = "foo"

  '
  ' Setting obj_2 to obj_1 does not create a new
  ' object (the operator new is not involved)
  ' obj_2 is just an alias to obj_1.
  '
    set obj_2 = obj_1

  '
  ' Changing obj_1's member m to bar.
  '
    obj_1.m = "bar"


  '
  ' Because obj_2 is an alias to obj_1,
  ' obj_2.m is equal to obj_1.m. The
  ' following line prints
  '    obj_2.m = bar
  '
    debug.print "obj_2.m = " & obj_2.m

  '
  ' Setting obj_1 to nothing does not yet
  ' invoke the destructor class_terminate.
  ' This is because obj_2 still points
  ' to the same object
  '
    set obj_1 = nothing

  '
  ' However, setting the other reference
  ' to the object to nothing will cause the
  ' class's destructor to be called.
  '
    set obj_2 = nothing

    debug.print "exiting main"

end sub

sub a(ad as long)
    debug.print "ad = " & ad
end sub
Github repository about-VBA, path: /language/classes/copying/main.bas
The class:
option explicit

public m as string

private sub class_initialize() ' No parameters possible!
    debug.print "class_initialize"
end sub

private sub class_terminate()
    debug.print "class_terminate"
end sub
Github repository about-VBA, path: /language/classes/copying/tq84.cls

Default member

If the declaration of the a class's property member method is immediately followed by an attribute NAME.vb_UserMemId = 0 statement (NAME being the name of the property), then this property becomes the default property for the class and will be invoked when an instance of the class is used without a member.
Note: in order for that to work, the class needs to be imported, not copy-pasted.
'
' vim: ft=basic
'

option explicit

private m_name as string
private m_age  as long

property let name(name_ as string)  ' {
    attribute name.vb_UserMemId = 0
    m_name = name_
end property ' }

property get name() as string ' {
    name = m_name
end property ' }

property let age(age_ as long) ' {
    m_age = age_
end property ' }

property get age() as long ' {
    age = m_age
end property ' }
Github repository about-VBA, path: /language/classes/default-member/person.cls
option explicit

sub main() ' {

    dim p as new person

    p.name = "Fred"
    p.age  =    42

    debug.print p.name & "'s age is " & p.age

  '
  ' Use power of default property: name needs
  ' not be specified.
  '
    debug.print p & "'s age is " & p.age

  '
  ' Change name, again using default property
  '
    p = "Charly"
    debug.print p & "'s age is " & p.age

end sub ' }
Github repository about-VBA, path: /language/classes/default-member/main.bas

See also

reference counting
Interfaces can be used to guarantee that a class implements a given set of functions and subs (somewhat similar to a pure abstract class in C++)
callByName(),
typeName()
Datatype object.
VBA language

Index