Jim DeMarco
Much has been made of classes in various publications I’ve read and seminars I’ve attended. With all of this attention being paid to them I became curious, tried my hand at them, and saw the light. The only problem I had was in finding examples that clearly and simply explained what classes were and how to use them. All the examples I found were based on real life objects, like creating a Car class that had a Color property and an Accelerate method. These are fine to see what a class is but I was searching for examples of coding problems solved by classes, to give me an idea of how to use them. This article deals with some useful day–to-day coding issues and hopefully provides a meaningful explanation of how to create and use class modules.
As a developer who’s been actively promoting object-oriented programming (OOP) practices, I’m often asked why bother. There are many reasons why it makes sense to use OOP whenever possible. One reason is because it makes reusing code simple. How many times have you written the following to see if a value exists within a string:
Dim sSomeString as String
Dim iPos as Integer
sSomeString = “This is a string”
iPos = InStr(sSomeString, “is”)
If iPos > 0 Then
‘do something here
End If
Or even something simple like returning the length of a string:
Dim sSomeString as String
Dim iLen as Integer
iLen = Len(sSomeString)
If iLen > 0 Then
‘do something here
End If
You’ve probably written these many times (I know I have). Chances are you keep them in a code library and copy and paste them or just type them in for the millionth plus time whenever you need them. Wouldn’t it be nice if a string variable had a little built-in knowledge of itself instead? What if we could rewrite the above like this:
Dim
sSomeString as New cStringEx
sSomeString.Value = "This is a string"
If sSomeString.Contains("is") Then 'returns True
'do something here
End If
If sSomeString.Length > 0 'returns 16
'do something here
End If

We will look at a simple class module I put together to create useful string functions like Contains and Length. In the previous example I declared a variable of type cStringEx. cStringEx is a custom object created from the cStringEx class we are going to make. Class modules are code modules whose functions cannot be run from a standard code module or form code module unless you create an instance of them (instantiate an object from them), as in Dim sSomeString as New cStringEx in the previous example. Once you create the instance of the class object you have access to all of its public properties and methods (also referred to as its interface) such as the cStringEx Contains and Length methods.
To put it simply, by creating a class module containing functions that perform related tasks you’ve created a reusable and portable tool for yourself or anyone else who wants the same functionality. This process is known as encapsulation. Encapsulation hides the machinations of a process from a user. You do not need to know how the cStringEx Contains method determines whether a string argument can be found within the Value property. The class does the work for you. It’s the end result you’re interested in, not how you got there.
For example, a while back I was looking for code to read and write values to the Windows Registry. All the code I found was in standard modules and made calls to cryptic Win32 APIs that I wanted to avoid. After trying out a few, I found one that was written well enough that I could add it to my application and read and write to the registry with relative ease. Yes it made all those API calls for me and I did not need to know a thing about them. All I had to do was create an object from the class and call the class’s ReadValue and WriteValue methods without worrying about API calls or how the class was working.
Class modules can contain properties, sub routines, and functions. These can be public (users of the class will see and have access to them) or private (functions that do behind the scenes work that users do not need to see). We’ll start by adding a class module to our database and saving it as cStringEx. In Access 97 choose Insert | Class Module from the Database Window or a code module menu. In Access 2000 do the same from the VBA IDE (Visual Basic Editor). Open the module, and add a private module-level variable to hold the string value we’ll use in all of our class functions:
Private m_sTheString As String
Since we’ve declared this variable as private we’ll need a way to get to it and set/return its value. To do this we’ll add a public property to the class module. Properties come in three flavors:
· Property Get will return the value of the private variable to us
· Property Let allows us to assign a value to the private variable.
· Property Set is similar to Property Let except it is used to set a reference to an object (we will not be using Property Set in this example).
Add the following Property to the class module:
Public Property Get Value() As String
Value = m_sTheString
End Property
Public Property Let Value (ByVal sNewString As String)
m_sTheString = sNewString
End Property
Notice that
Property Let assigns the value of its passed argument (sNewString) to the
module level private variable m_sTheString, and that the Property Get returns the
value of this same variable. Also note that the Property Get return value, the
Property Let argument and the private variable they refer to, must all be of
the same data
type, in our example, 'String'.
Let’s test this out by typing the following statements in the Debug Window, pressing Enter after each line (press Ctrl + G if you can’t find the Debug window):
Set oMyString = New cStringEx
oMyString.Value= “my string”
Debug.Print oMyString.Value
If all goes well, the Debug statement will print the value “my string”.
Let’s take a quick look at what we’ve done here. The line
“Set oMyString = New cStringEx”
instantiates or creates a new object from the class. Note that the type of variable is the same as the name of the class module itself. The next line
“oMyString.Value = “my string”
calls Property Let and stores the value “my string” in the private module-level variable, m_sTheString.
I’m sure that you’ve noticed that the Property Let in our class module requires an argument that we are not providing. By virtue of the fact that we are assigning a value to a class property the argument is passed for us during the assignment. The last line calls the property Get to return the string currently contained in the class’s private module level variable.
The cStringEx class object is going to extend (or enhance) VBA’s standard String data type. Now that we know how to create our string object let’s put it to work. The inspiration for this class came from a book on Java that I’ve had my nose in for a while. In Java (almost) everything is stored in classes, including Strings. A Java string is smart enough to know its own length or if it contains a sub-string. I thought, “Why not provide this functionality to strings in my VBA applications?”
One common thing we do with strings is determine their length. Let’s begin by creating a method in our class that returns the length of the string value contained in our Value property.
Public Function Length() As Long
Dim tmp As Long
tmp = Len(Me.Value)
Length = tmp
End Function
Notice that within the Length function we are passing the class’s Value Property Get as an argument to the Len function. This is because within the Property Get we will check to see if the property has been initialized before allowing access to it. (See A Word on Initialization later in this article. Our sample property is not making this check; see the complete code for details). I could have passed the private variable m_sTheString directly if I did not want to check the value within the Property. Notice that I used the Me keyword to signify that we’re referring to this class’s Value property (the Me keyword is optional). Let’s go back to the debug window and see the result of this:
Set oMyString = New cStringEx
oMyString.Value = “This is a string”
Debug.Print oMyString.Length
Again, if all goes well, the Debug.Print should return 16. Let’s add one more method to the class and then I’ll let you take a look at the complete class module. Acommon action we perform on strings is to check for the existence of a sub-string (string within a string). For this we’d normally use the InStr function along with a variable to hold the result of InStr(). We would then take action based on the return value. Let’s wrap up this functionality in the Contains method that will return a Boolean value.
Public Function Contains(What As String, _
Optional IgnoreCase As Boolean = True) _
As Boolean
'What - input - string to search for
'IgnoreCase = input - if true, find match regardless of case
'Function returns True if Me.Value contains What
Dim tmp As Integer
Dim blnResult As Boolean
Dim intIgnoreCase As Integer
intIgnoreCase = Abs(IgnoreCase)
tmp = InStr(1, Value, What, intIgnoreCase)
blnResult = (tmp > 0)
Contains = blnResult
End Function
Once again we’ll go the Debug window and test our work:
Set oMyString = New cStringEx
oMyString.Value = “This is a string”
Debug.Print oMyString.Contains(“str”)
Debug.Print oMyString.Contains(“abc”)
Debug.Print oMyString.Contains(“thi”)
Debug.Print oMyString.Contains(“thi”, False)
The result of the first Debug.Print should be true, the second false, third true, and the last false (because we turned off IgnoreCase).
Every class module has two built in events, Initialize and Terminate. Although not required, it is a good idea to initialize (or provide default values) to your class variables. In the cStringEx class I initialize the m_sTheString variable to a nonsense value, but you can use any value (or non-value) that makes sense to you. Then I check for this value in the Value Property Get and prevent access to the property if the value has not been set by the programmer. The Terminate event (which I’m not using here) gives you a chance to clean up before destroying your object.
Take some time now to look at the complete class code. As you’ll see this is a very simple class that performs common string manipulation chores. Think about some of the things that you’d like your strings to know about themselves. Things like StartsWith or EndsWith methods that tell you if a string starts or ends with a sub-string. Or how about strings that can change their own case? Just look at the ToUpper, ToLower, or ToProper methods. Need more functionality? Add whatever methods you like to the class module. You can customize it anyway you like!
By simply declaring an object of the class type you will give yourself access to these functions. With the exception of the Reverse method these functions will work in all versions of Access from A97 and up. The Reverse method uses the StrReverse function that is not available to A97 (although you could certainly modify the function to make it work).
Once you’ve walked through this simple example (most real working classes will contain many private variables and their associated properties and methods) why not try your hand at creating one from scratch? How about a “smart” numeric class that knows whether or not it is an even number, knows its own square root, or is self limiting via MinVal and MaxVal methods.
Have fun!
'----------------------------------------------------------------------
' Module : cStringEx
' DateTime : 12/17/01 10:04
' Author : Jim DeMarco
' Purpose : Wraps common string functions à la VS.NET or Java String _ class
'----------------------------------------------------------------------
Private m_sTheString As String
'
Public Property Get Value() As String
If m_sTheString = "NOG" Then Exit Property
Value = m_sTheString
End Property
Public Property Let Value(ByVal sNewString As String)
m_sTheString = sNewString
End Property
Public Function Length() As Long
Dim tmp As Long
tmp = Len(Value)
Length = tmp
End Function
Public Function Contains(What As String, _
Optional IgnoreCase As Boolean = True) As Boolean
'What - input - string to search for
'IgnoreCase = input - if true find match regardless of case
'returns True if Value contains What
Dim tmp As Integer
Dim blnResult As Boolean
Dim intIgnoreCase As Integer
intIgnoreCase = Abs(IgnoreCase)
tmp = InStr(1, Value, What, intIgnoreCase)
blnResult = (tmp > 0)
Contains = blnResult
End Function
Public Function StartsWith(What As String) As Boolean
Dim tmp As String
Dim iLen As Long
Dim result As Boolean
If Len(What) & "" > 0 Then
iLen = Len(What)
tmp = Left$(Value, iLen)
result = (tmp = What)
End If
StartsWith = result
End Function
Public Function EndsWith(What As String) As Boolean
Dim tmp As String
Dim iLen As Long
Dim iStartPos As Long
Dim result As Boolean
If Len(What) & "" > 0 Then
iLen = Len(What)
iStartPos = Len(Value) - iLen
tmp = Mid$(Value, iStartPos + 1)
result = (tmp = What)
End If
EndsWith = result
End Function
Public Function CharAt(Where As Integer) As String
Dim tmp As String
If Where > 0 Then
tmp = Mid$(Value, Where, 1)
End If
CharAt = tmp
End Function
Public Function ToUpper() As String
Dim tmp As String
tmp = UCase(Value)
ToUpper = tmp
End Function
Public Function ToLower() As String
Dim tmp As String
tmp = LCase(Value)
ToLower = tmp
End Function
Public Function ToProper() As String
Dim tmp As String
tmp = StrConv(Value, vbProperCase)
ToProper = tmp
End Function
Public Function Append(What As String) As String
Dim tmp As String
If (Len(What) & "") > 0 Then
tmp = Value & What
End If
Append = tmp
End Function
Public Function Prepend(What As String) As String
Dim tmp As String
If (Len(What) & "") > 0 Then
tmp = What & Value
End If
Prepend = tmp
End Function
Public Function Reverse() As String
Dim tmp As String
tmp = StrReverse(Value)
Reverse = tmp
End Function
Private Sub Class_Initialize()
m_sTheString = "NOG"
End Sub
Updated code is available at http://www14.brinkster.com/jdthree/accessd/cStringEx.txt
Please contact the author at Jdemarco@hshhp.org to ask questions or report errors.
Jim
DeMarco ©2002
May be distributed as long as the copyright remains.
Jim DeMarco
Bio