Classes for the Masses

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.

Why Classes?

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


As it turns out, in Microsoft Visual Studio .NET, where most, if not everything, is an object, you can use this form. But we can, even now, provide ourselves with some of these built in “smarts” by wrapping commonly used string functions into a class module. An added bonus is that once you’ve created an object from a class, whenever you type the object variable name followed by a period in a code module, IntelliSense kicks in and provides a list of all of the class’s properties and methods. So you may not remember all of a certain object’s methods, but you don’t have to. Also, you can see the properties and methods in the Object Browser window after selecting the class from the Classes list.

 

 

 


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.

The cStringEx Class

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?”

The Working Class

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).

A Word on Initialization

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.

The cStringEx Class

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