Chapter 23. Defining Classes and Their Properties

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.

WHAT IS A CLASS?

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 Buttons 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

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.

CLASS BENEFITS

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.

Note

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.

MAKING A CLASS

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.

PROPERTIES

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.

AUTO-IMPLEMENTED PROPERTIES

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 Property Name As dataType

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.

Backing Fields

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 Property Name As dataType
    Get
        ...getCode...
    End Get
    Set(ByVal value As dataType)
        ...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.

Note

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.

TRY IT

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.

Figure 23-1

Figure 23.1. Figure 23-1

Note

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.

Lesson Requirements

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.

Hints

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

Step-By-Step

  • Build the program shown in Figure 23-1.

    1. This is reasonably straightforward.

  • Create a Person class.

    1. Use the Project menu's Add Class item. Name the class Person.

  • Make auto-implemented properties for Street, City, State, and Zip.

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

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

Note

Please select Lesson 23 on the DVD to view the video that accompanies this lesson.

EXERCISES

  1. 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 "#####"
  2. 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 ###-###-####.

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

    Figure 23-2

    Figure 23.2. Figure 23-2

    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.

    Note

    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.

Figure 23-2
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset