The easiest way to define properties for a class is by adding public variables to the class module. For example, you could very easily create an Account class by declaring two public variables in a class module named Account:
Public Balance As Double
Public Name As String
This is pretty easy. It's just as easy to create private data for a class; simply declare a variable Private, and it will be accessible only from code within the class module:
Private mstrMothersMaidenName As String
Private mintWithdrawalsMonthToDate As Integer
The ability to protect part of an object's data, while exposing the rest as properties, is called data hiding. This is one aspect of the object-oriented principle of encapsulation, as explained in "Classes: Putting User-Defined Types and Procedures Together."
Data hiding means that you can make changes in the implementation of a class — for example, increasing the Account class's private variable mintWithdrawalsMonthToDate
from an Integer to a Long — without affecting existing code that uses the Account object.
Data hiding also allows you to define properties that are read-only. For example, you could use a Property Get procedure to return the value of the private variable containing the number of withdrawals in a month, while only incrementing the variable from within the Account object's code. Which brings us to property procedures.
Data hiding wouldn't be much use if the only way you could create properties was by declaring public variables. How much good would it do you to give the Account class a Type property, if any code that had a reference to an Account object could blithely set the account type to any value at all?
Property procedures allow you to execute code when a property value is set or retrieved. For example, you might want to implement the Type property of the Account object as a pair of Property procedures:
Public Enum AccountTypes
atSavings = 1
atChecking
atLineOfCredit
End Enum
' Private data storage for the Type property.
Private mbytType As AccountTypes
Public Property Get Type() As AccountTypes
Type = mbytType
End Property
Public Property Let Type(ByVal NewType As AccountTypes)
Select Case NewType
Case atChecking, atSavings, atLineOfCredit
' No need to do anything if NewType is valid.
Case Else
Err.Raise Number:=vbObjectError + 32112, _
Description:="Invalid account type"
End Select
If mbytType > NewType Then
Err.Raise Number:=vbObjectError + 32113, _
Description:="Cannot downgrade account type"
Else
mbytType = NewType
End If
End Property
Now suppose you have a variable named acct
that contains a reference to an Account object. When the code x = acct.Type
is executed, the Property Get procedure is invoked to return the value stored in the class module's private data member mbytType
.
When the code acct.Type = atChecking
is executed, the Property Let is invoked. If the Account object is brand new, mbytType
will be zero, and any valid account type can be assigned. If the current account type is atSavings, the account will be upgraded.
However, if the current account type is atLineOfCredit, the Property Let will raise an error, preventing the downgrade. Likewise, if the code acct.Type = 0
is executed, the Select statement in the Property Let will detect the invalid account type and raise an error.
In short, property procedures allow an object to protect and validate its own data.
For More Information Are public variables good for anything, then? "Property Procedures vs. Public Variables" outlines the appropriate uses of both.
The capabilities of property procedures are explored further in "Putting Property Procedures to Work for You."