This book hasn't emphasized the fact, but you've been working with classes since the very beginning. The very first program you created in Lesson 1 included several classes, such as the program's main form and some behind-the-scenes classes that help get the program running. Since then, you've used all kinds of control classes — the MessageBox
class, the Array
class, collection classes, the Debug
class, and more. You can even treat primitive data types such as Integer
and String
as classes under some circumstances.
In this lesson you learn how to create your own classes. You learn how to define a class and give it properties.
A class defines a type of object. It defines the properties, methods, and events provided by its type of object. After you define a class, you can make as many instances of that class as you like.
For example, the Button
class defines the properties and behaviors of a button user interface element. You can create any number of instances of Button
s and place them on your forms.
You can think of a class as a blueprint for making objects. When you create an instance of the class, you use the blueprint to make an object that has the properties and behaviors defined by the class. You can also think of a class as a cookie cutter. Once you've created the cookie cutter, you can make any number of cookies that all have the same shape.
Classes are very similar to the structures described in Lesson 17, and many of the techniques you learned there apply here as well. For example, you can give a class fields that the instance can use to perform calculations.
There are several important differences between structures and classes, but one of the most important is that structures are value types, whereas classes are reference types. Perhaps the most confusing consequence of this is that when you assign structure variable A
equal to structure variable B, A
becomes a copy of B
. In contrast, if you assign class variable C
equal to class variable D
, then variable C
now points to the same object that variable D
does.
For a more detailed discussion of some of these differences, see the section "Structures versus Classes" in Lesson 17.
The rest of this lesson focuses on classes and doesn't really talk about structures.
Note that the same techniques apply to both structures and classes: structures have the same benefits as classes described in the following section. Just because I'm describing those benefits or techniques here doesn't mean I'm implying that classes are better because they have these advantages and structures don't.
The biggest benefit of classes is encapsulation. A well-designed class hides its internal workings from the rest of the program so the program can use the class without knowing how the class works.
For example, suppose you build a Turtle
class to represent a turtle crawling across the screen, drawing lines as it moves. The class would need properties such as X, Y
, and Direction
to define the Turtle
's location and direction. It might also provide methods such as Turn
to make it change direction, and Move
to make it move.
The Turtle
class needs to know how to draw the Turtle
's path as it moves, but the main program doesn't need to know how it works. It doesn't need to know about Graphics
objects, Pens
, or the trigonometric functions the Turtle
uses to figure out where to go. The main program only needs to know how to set the Turtle
's properties and call its methods.
You can download the Turtle example program from the book's web site (at www.wrox.com
or www.vb-helper.com/24hourvb.html
) as part of the Lesson23 folder and follow along in its code as you read through this lesson.
Some other benefits of classes (and structures for that matter) include the following:
Grouping data and code — The code that makes a Turtle
move is contained in the same object as the data that determines the Turtle
's position and direction.
Code reuse — You only need to write the code for the Turtle
class once and then all instances of the class get to use it. You get even more code reuse through inheritance, which is described in Lesson 25.
Polymorphism — Polymorphism means you can treat an object as if it were another class as long as it inherits from that class. For example, a Student
is a type of Person
so you should be able to treat a Student
object as if it were either a Student
or a Person
. Lesson 25 describes this further.
Now that you know a bit about what classes are for, it's time to learn how to build one.
Making a class in Visual Basic is simple. Open the Project menu and select Add Class. Give the class a meaningful name and click Add. The following code shows a newly created Employee
class:
Public Class Employee End Class
At this point, the class can't do anything. You can write code to create an instance of the class but it will just sit there. To make the class useful, you need to add properties, methods, and events.
Properties are values associated with a class. For example, an Employee
class might define FirstName, LastName
, and EmployeeId
properties.
Methods are actions that an object can perform. For example, an Employee
class might provide a CalculateBonus
method that calculates the employee's end-of-year bonus based on performance during the year. (Methods are simply subroutines and functions provided by the class.)
Events are raised by the class to tell the rest of the program that something interesting happened, sort of like raising a flag to draw attention to something. For example, the Employee
class might raise a TooManyHours
event if the program tried to assign an employee more than 40 hours of work in a week.
Properties, methods, and events allow a program to control and interact with objects. The following sections explain how you can add properties to a class. Lesson 24 explains how to give a class methods and events.
If you give a class a public variable, other pieces of code can get and set that variable's values. This kind of variable is called a field. A field is similar to a property but it has one big disadvantage: it provides unrestricted access to its value. That means other parts of the program could dump any old garbage into the field without the class being able to stop them.
In contrast, a class implements a property by using property get and property set methods that can include code to protect the class from garbage values. Because the program uses those methods to access the value, they are also called accessor methods. You'll learn more about this as you learn how to build properties.
The following sections describe the two most common approaches for implementing properties: auto-implemented properties and backing fields.
The easiest way to make a property is to use an auto-implemented property. The syntax for an auto-implemented property is as follows:
accessibility
PropertyName
AsdataType
Here accessibility
determines what code can use the property. It can be Public, Private
, and so forth. The dataType
determines the property's data type and Name
determines its name.
The following code creates a simple property named FirstName
of type String
:
Public Property FirstName As String
Creating an auto-implemented property is almost as easy as making a field but it's a tiny bit more work, so why should you bother? Making an auto-implemented property sets up the code so it will be easy to upgrade the property to use more complicated code later. For example, suppose you decide that the Turtle
class's Direction
property should only allow angles between 0 and 259 degrees. In that case you can convert the auto-implemented property into a property built using a backing field, as described in the next section. If you initially made Direction
a simple field, then adding validation code would be more difficult.
When you make an auto-implemented property, Visual Basic automatically generates property get and set procedures behind the scenes. You can use those procedures without needing to know the details about how they work (another example of encapsulation).
When you make a property that is not auto-implemented, you need to write the property get and set procedures yourself.
The following shows the basic syntax used to define a property that is not auto-implemented:
accessibility
PropertyName
AsdataType
Get ...getCode...
End Get Set(ByVal value AsdataType)
...setCode...
End Set End Property
Here accessibility
, dataType
, and Name
are the same as before. The getCode
and setCode
are the pieces of code that get and set the property's value.
One common way to implement this kind of property is with a backing field. A backing field is a field that stores data to represent the property. The getCode
and setCode
use the field to get and set the property's value.
The following Visual Basic code shows a version of the Direction
property stored in the backing field named _Direction
:
' The Turtle's direction in degrees. Private _Direction As Integer = 0 ' Backing field.
Public Property Direction As Integer Get Return _Direction End Get Set(ByVal value As Integer) _Direction = value End Set End Property
The code starts by defining the field _Direction
to hold the property's value. The field is private so only the code inside the class can see it.
Different programmers use different naming conventions for the backing field. Some use the same name as the property with an underscore in front as shown here. Others add m_
to the name to indicate that the variable has module-level scope. Others add Value
to the end of the property's name as in DirectionValue
. As is usually the case with naming conventions, it doesn't matter too much what you do as long as you're consistent.
The property's Get
procedure simply returns the value of _Direction
.
The property's Set
procedure saves a new value in the backing field _Direction
. The new value that the calling code is trying to assign to the property is stored in a parameter named value
.
The preceding code simply copies values in and out of the backing field, so why bother doing this instead of using a public field? There are several reasons.
First, a property hides its details from the outside world, increasing the class's encapsulation. As far as the outside world is concerned, a description of the Direction
property tells you what is stored (the direction) but not how it is stored (as an integer value in degrees).
This example stores the direction in degrees, but suppose you decided that the class would work better if you stored the direction in radians? If Direction
is a field, then any code that uses it would now break because it is using degrees. However, if you use property procedures, they can translate between degrees and radians as needed so the code outside the class doesn't need to know that anything has changed.
The following code shows a new version of the Direction
property that stores its value in radians. As far as the code outside the class is concerned, nothing has changed.
' Store Direction in radians but get and set in degrees. Private _Direction As Double = 0 ' Backing field. Public Property Direction As Integer Get Return CInt(_Direction * 180 / Math.PI) End Get Set(ByVal value As Integer) _Direction = value * Math.PI / 180 End Set End Property
Second, you can also add validation code to property procedures. For example, suppose the Direction
property represents an angle in degrees and you only want to allow values between 0 and 359. The following code asserts that the new value is within that range. The program can continue correctly if the value is outside of this range so the code uses Debug.Assert
instead of throwing an exception.
' The Turtle's direction in degrees. Private _Direction As Integer = 0 ' Backing field. Public Property Direction As Integer Get Return _Direction End Get Set(ByVal value As Integer) Debug.Assert((value >= 0) AndAlso (value <= 359), "Direction should be between 0 and 359 degrees") _Direction = value End Set End Property
Finally, property procedures give you a place to set breakpoints if something is going wrong. For example, if you know that some part of your program is setting a Turtle
's Direction
to 45 when it should be setting it to 60 but you don't know where, you could set a breakpoint in the property Set
procedure to see where the change is taking place.
In this Try It, you create a simple Person
class with FirstName, LastName, Street, City, State
, and Zip
properties, some having simple validations. You also build a simple test application, as shown in Figure 23-1.
You can download the code and resources for this Try It from the book's web page at www.wrox.com
or www.vb-helper.com/24hourvb.html
. You can find them in the Lesson23 folder in the download.
In this lesson:
Build the program shown in Figure 23-1.
Create a Person
class.
Make auto-implemented properties for Street, City, State
, and Zip
.
For this application, assume that setting FirstName
or LastName
to a blank string is an error and add validation code to their property procedures.
Don't allow FirstName
or LastName
to be set to Nothing
, which is different from setting them equal to blank strings. If you don't check for these, the other code in the property procedures will throw an error anyway when it tries to manipulate the Nothing
value, but if you check yourself, then you can provide a more meaningful exception.
Build the program shown in Figure 23-1.
This is reasonably straightforward.
Create a Person
class.
Use the Project menu's Add Class item. Name the class Person
.
Make auto-implemented properties for Street, City, State
, and Zip
.
You can use code similar to the following:
' Auto-implemented properties. Public Property Street As String Public Property City As String Public Property State As String Public Property Zip As String
For this application, assume that setting FirstName
or LastName
to a blank string is an error and add validation code to their property procedures.
The following code shows how you might implement the FirstName
property. The code for the LastName
property is similar.
' FirstName property. Private _FirstName As String = "" Public Property FirstName As String Get Return _FirstName End Get Set(ByVal value As String) If (value Is Nothing) Then Throw New ArgumentOutOfRangeException("FirstName", "Person.FirstName cannot be Nothing.") End If If (value.Length = 0) Then Throw New ArgumentOutOfRangeException("FirstName", "Person.FirstName cannot be blank.") End If ' Validations passed. Save the new value. _FirstName = value End Set End Property
Please select Lesson 23 on the DVD to view the video that accompanies this lesson.
Copy the program you built for this lesson's Try It or download the version available on the book's web site. Convert the auto-implemented Zip
property into one that uses property get and set procedures. Notice that you don't need to change the main program.
Make the property set procedure verify that the new value has the format #####. Hint: The following Like
statement returns True
if value has the correct format:
value Like "#####"
Copy the program you built for Exercise 1 or download the version available on the book's web site. Modify the Zip
property get procedure to allow either the format ##### or #####-####. Add a new PhoneNumber
property that allows the formats ###-#### or ###-###-####.
Write a program similar to the one shown in Figure 23-2 to manipulate complex numbers. When you enter the complex numbers' real and imaginary parts in the textboxes and click Calculate, the program should display the sum, difference, and product of the two complex numbers.
Make a ComplexNumber
class with properties Real
and Imaginary
to hold a number's real and imaginary parts. Give the class AddTo, MultiplyBy
, and SubtractFrom
functions that combine the current ComplexNumber
with another taken as a parameter and return the result as a new ComplexNumber
.
Hints: Recall from school these equations for calculating with complex numbers:
(A + Bi
) + (C + Di
) = (A + C) + (B + D)i
(A + Bi
) - (C + Di
) = (A - C) + (B - D)i
(A + Bi
) * (C + Di
) = (A * C - B * D) + (A * D + B * C)i
For more review of complex numbers, see en.wikipedia.org/wiki/Complex_numbers or mathworld.wolfram.com/ComplexNumber.html
.
You can find solutions to this lesson's exercises in the Lesson23 folder inside the download available on the book's web site at www.wrox.com
or www.vb-helper.com/24hourvb.html
.