A variable holds a single value. It may be a simple value such as an Integer or String, or a reference that points to a more complex entity. Two kinds of more complex entities are classes and structures.
Classes and structures are both container types. They group several related data values into a convenient package that you can manipulate as a group.
For example, an EmployeeInfo structure might contain fields that hold information about an employee (such as first name, last name, employee ID, office number, extension, and so on). If you make an EmployeeInfo structure and fill it with the data for a particular employee, you can then move the structure around as a single unit instead of passing around a bunch of separate variables holding the first name, last name, and the rest.
This chapter explains how to declare classes and structures, and how to create instances of them (instantiate them). It also explains the differences between classes and structures and provides some advice about which to use under different circumstances.
A class packages data and related behavior. For example, a WorkOrder class might store data describing a customer’s work order in its properties. It could contain methods (subroutines and functions) for manipulating the work order. It might provide methods for scheduling the work, modifying the order’s requirements, and setting the order’s priority.
Here is the syntax for declaring a class:
[attribute_list] [Partial] [accessibility] [Shadows] [inheritance] _ Class name[(Of type_list)] [Inherits parent_class] [Implements interface] statements End Class
The only things that all class declarations must include are the Class clause and the End Class statement. Everything else is optional. The following code describes a valid (albeit not very interesting) class:
Class EmptyClass End Class
The following sections describe the pieces of the general declaration in detail.
The optional attribute_list is a comma-separated list of attributes that apply to the class. An attribute further refines the definition of a class to give more information to the compiler and the runtime system.
Attributes are rather specialized. They address issues that arise when you perform very specific programming tasks. For example, if your application must use drag-and-drop support to copy instances of the class from one application to another, you must mark the class as serializable, as shown in the following code:
<Serializable()> _ Class Employee Public FirstName As String Public LastName As String End Class
Some attributes are particular to specific kinds of classes. For example, the DefaultEvent attribute gives the form designer extra information about component classes. If you double-click on a component on a form, the code designer opens to the component’s default event.
Because attributes are so specialized, they are not described in more detail here. For more information, see the sections in the online help that are related to the tasks you need to perform.
For more information on attributes, see the “Attributes” section of the Visual Basic Language Reference or go to http://msdn.microsoft.com/library/en-us/vbls7/html/vblrfVBSpec4_10.asp. For a list of attributes defined by Visual Basic, search the online help for “Attribute Hierarchy” or go to http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemattributeclasshierarchy.asp.
The Partial keyword tells Visual Basic that the current declaration defines only part of the class. The following code shows the Employee class broken into two pieces:
Partial Public Class Employee Public FirstName As String Public LastName As String ... End Class ... other code, possibly unrelated to the Employee class ... Partial Public Class Employee Public Email As String ... End Class
The program could contain any number of other pieces of the Employee class, possibly in different code modules. At compile time, Visual Basic finds these pieces and combines them to define the class.
One of the primary benefits of classes is that they hold the code and data associated with the class together in a nice package. Scattering the pieces of a class in this way makes the package less self-contained and may lead to confusion. To prevent confusion, you should avoid splitting a class unless you have a good reason to (for example, to allow different developers to work on different pieces of the class at the same time).
At least one of the pieces of the class must be declared with the Partial keyword, but in the other pieces it is optional. Explicitly providing the keyword in all of the class’s partial definitions emphasizes the fact that the class is broken into pieces and may minimize confusion.
A class’s accessibility clause can take one of the following values: Public, Protected, Friend, Protected Friend, or Private.
Public indicates that the class should be available to all code inside or outside of the class’s module. This enables the most access to the class. Any code can create and manipulate instances of the class.
You can only use the Protected keyword if the class you are declaring is contained inside another class. For example, the following code defines an Employee class that contains a protected EmployeeAddress class:
Public Class Employee Public FirstName As String Public LastName As String Protected Address As EmployeeAddress Protected Class EmployeeAddress Public Street As String Public City As String Public State As String Public Zip As String End Class ... other code ... End Class
Because the EmployeeAddress class is declared with the Protected keyword, it is only visible within the enclosing Employee class and any derived classes. For example, if the Manager class inherits from the Employee class, code within the Manager class can access the Address variable.
The Friend keyword indicates that the class should be available to all code inside or outside of the class’s module within the same project. The difference between this and Public is that Public allows code outside of the project to access the class. This is generally only an issue for code libraries (.dll files) and control libraries. For example, suppose that you build a code library containing dozens of routines and then you write a program that uses the library. If the library declares a class with the Public keyword, the code in the library and the code in the main program can use the class. On the other hand, if the library declares a class with the Friend keyword, only the code in the library can access the class, not the code in the main program.
Protected Friend is the union of the Protected and Friend keywords. A class declared Protected Friend is accessible only to code within the enclosing class or a derived class and only within the same project.
A class declared Private is accessible only to code in the enclosing module, class, or structure. If the EmployeeAddress class were declared Private, only code within the Employee class could use that class.
If you do not specify an accessibility level, it defaults to Friend.
The Shadows keyword indicates that the class hides the definition of some other entity in the enclosing class’s base class.
The following code shows an Employee class that declares a public class OfficeInfo and defines an instance of that class named Office. The derived class Manager inherits from Employee. It declares a new version of the OfficeInfo class with the Shadows keyword. It defines an instance of this class named ManagerOffice.
Public Class Employee Public Class OfficeInfo Public OfficeNumber As String Public Extension As String End Class Public FirstName As String Public LastName As String Public Office As New OfficeInfo End Class Public Class Manager Inherits Employee Public Shadows Class OfficeInfo Public OfficeNumber As String Public Extension As String Public SecretaryOfficeNumber As String Public SecretaryExtension As String End Class Public ManagerOffice As New OfficeInfo End Class
The following code uses the Employee and Manager classes. It creates instances of the two classes and sets their Office.Extension properties. Both of those values are part of the Employee class’s version of the OfficeInfo class. Next, the code sets the Manager object’s ManagerOffice.SecretaryExtension value.
Dim emp As New Employee Dim mgr As New Manager emp.Office.Extension = "1111" mgr.Office.Extension = "2222" mgr.ManagerOffice.SecretaryExtension = "3333"
Note that the Manager class contains two different objects of type OfficeInfo. Its Office property is the Employee class’s flavor of OfficeInfo class. Its ManagerOffice value is the Manager class’s version of OfficeInfo.
The presence of these different classes with the same name can be confusing. Usually, you are better off not using the Shadows keyword in the declarations and giving the classes different names. In this case, you could call the Manager class’s included class ManagerOfficeInfo.
It is more common to use the Shadows keyword to allow the derived class to replace a method in the parent class.
A class’s inheritance clause can take the value MustInherit or NotInheritable.
MustInherit prohibits the program from creating instances of the class. The program should create an instance of a derived class instead. This kind of class is sometimes called an abstract class.
By using MustInherit, you can make a parent class that defines some of the behavior that should be implemented by derived classes without implementing the functionality itself. The parent class is not intended to be used itself, just to help define the children.
For example, the following code defines the Vehicle class with the MustInherit keyword. This class defines features that are common to all vehicles. It defines a NumWheels variable and a Drive subroutine declared with the MustOverride keyword. The real world doesn’t contain generic vehicles, however. Instead, it contains cars, trucks, and other specific kinds of vehicles. The code defines a Car class that inherits from the Vehicle class. When you enter the Inherits statement and press Enter, Visual Basic automatically adds the empty Drive subroutine required by the Vehicle class.
Public MustInherit Class Vehicle Public NumWheels As Integer Public MustOverride Sub Drive() End Class Public Class Car Inherits Vehicle Public Overrides Sub Drive() End Sub End Class
The following code uses these classes. It declares a Vehicle and a Car variable. The first assignment statement causes an error because it tries to make a new Vehicle object. This is not allowed, because Vehicle is declared with the MustInherit keyword. The program sets variable a_car to a new Car variable and then sets variable a_vehicle to a_car. This works because a Car is a type of Vehicle, so the a_vehicle variable can refer to a Car object. This lets the program handle different kinds of vehicles (Cars, Trucks, BigRigs) using the generic Vehicle type if that is convenient. In its last line, the code assigns a_vehicle directly to a new Car object.
Dim a_vehicle As Vehicle Dim a_car As Car a_vehicle = New vehicle ' This causes an error because Vehicle is MustInherit. a_car = New Car ' This works. a_vehicle = a_car ' This works. a_vehicle = New Car ' This works.
The NotInheritable keyword does the opposite of the MustInherit keyword. MustInherit says that a class must be inherited to be instantiated. NotInheritable says no class can inherit from this one.
You can use NotInheritable to stop other developers from making new versions of the classes you have built. This isn’t really necessary if you design a well-defined object model before you start programming and if everyone obeys it. NotInheritable can prevent unnecessary proliferation of classes if developers don’t pay attention, however. For example, declaring the Car class NotInheritable would prevent overeager developers from deriving FrontWheelDriveCar, RedCar, and Subaru classes from the Car class.
The Of type_list clause makes the class generic. It allows the program to create instances of the class that work with specific data types. For example, the following code defines a generic Tree class. The class includes a public variable named RootObject that has the data type given in the class’s Of data_type clause.
Public Class Tree(Of data_type) Public RootObject As data_type ... End Class
When you read this declaration, you should think “Tree of something,” where something is defined later when you make an instance of the class.
The following code fragment declares the variable my_tree to be a “Tree of Employee.” It then instantiates my_tree and sets its RootObject variable to an Employee object.
Dim my_tree As Tree(Of Employee) my_tree = New Tree(Of Employee) my_tree.RootObject = New Employee ...
Chapter 19 discusses generic classes further.
The Inherits statement indicates that the class (the child class) is derived from another class (the parent class). The child class automatically inherits the parent’s properties, methods, and events.
The following code defines an Employee class that contains LastName, FirstName, OfficeNumber, and Extension variables. It then derives the Manager class from the Employee class. Because it inherits from the Employee class, the Manager class automatically has LastName, FirstName, OfficeNumber, and Extension variables. It also adds new SecretaryOfficeNumber and SecretaryExtension variables. These are available to instances of the Manager class but not to the Employee class.
Public Class Employee Public FirstName As String Public LastName As String Public OfficeNumber As String Public Extension As String End Class Public Class Manager Inherits Employee Public SecretaryOfficeNumber As String Public SecretaryExtension As String End Class
The following code makes a new Employee object and sets its Extension property to 1000. It then creates a Manager object and sets its Extension and SecretaryExtension values.
Dim emp As New Employee emp.Extension = "1000" Dim mgr As New Manager mgr.Extension = "2000" mgr.SecretaryExtension = "2001"
If a class inherits from another class, the Inherits statement must be the first statement after the Class statement that is not blank or a comment. Also note that a class can inherit from at most one parent class, so a class definition can include at most one Inherits statement.
For more information on inheritance, see the Chapter 15.
The Implements keyword indicates that a class will implement an interface. An interface defines behaviors that the implementing class must provide, but it does not provide any implementation for the behaviors.
For example, the following code defines the IDomicile interface. By convention, the names of interfaces should begin with the capital letter I. This interface defines the SquareFeet, NumBedrooms, and NumBathrooms properties, and the Clean subroutine.
The interface also defines the read-only property FileSystemNeeded. Usually a read-only property calculates its return from other property values. For example, FileSystemNeeded might return True if a house has more than a certain number of square feet. You can define write-only properties similarly, although they are much less common than read-only properties.
The House class implements the IDomicile interface. When you type the Implements statement and press Enter, Visual Basic automatically generates empty routines to provide the features defined by the interface.
Public Interface IDomicile Property SquareFeet() As Integer Property NumBedrooms() As Integer Property NumBathrooms() As Integer ReadOnly Property NeedsFireSystem() As Boolean Sub Clean() End Interface Public Class House Implements IDomicile Public Sub Clean() Implements IDomicile.Clean End Sub Public Property NumBathrooms() As Integer Implements IDomicile.NumBathrooms Get End Get Set(ByVal Value As Integer) End Set End Property Public Property NumBedrooms() As Integer Implements IDomicile.NumBedrooms Get End Get Set(ByVal Value As Integer) End Set End Property Public Property SquareFeet() As Integer Implements IDomicile.SquareFeet Get End Get Set(ByVal Value As Integer) End Set End Property Public ReadOnly Property NeedsFireSystem() As Boolean _ Implements IDomicile.NeedsFireSystem Get End Get End Property End Class
An interface defines behaviors but does not supply them. When you derive a class from a parent class, the derived class inherits all the code that the parent class uses to implement its features. When you implement an interface, the behavior is defined, but not supplied for you. That makes interfaces more difficult to use than inheritance, so inheritance is generally preferred whenever it is possible.
One case where the inheritance of Visual Basic is insufficient is when you need to implement multiple inheritance. In multiple inheritance, one child class can inherit from more than one parent class. For example, you might define a Domicile class and a Boat class, and then make the HouseBoat class inherit from both. You can do this in some languages but not in Visual Basic. However, you can make a class implement more than one interface.
The following code defines the IBoat interface. It then defines the HouseBoat class and uses two Implements statements to indicate that it implements both the IDomicile and IBoat interfaces.
Public Interface IBoat Property HasMotor() As Boolean Property HorsePower() As Integer Property Capacity() As Integer Sub Sail() End Interface Public Class HouseBoat Implements IDomicile Implements IBoat Public Sub Clean() Implements IDomicile.Clean End Sub Public ReadOnly Property NeedsFireSystem() As Boolean Implements IDomicile.NeedsFireSystem Get End Get End Property Public Property NumBathrooms() As Integer Implements IDomicile.NumBathrooms Get End Get Set(ByVal Value As Integer) End Set End Property Public Property NumBedrooms() As Integer Implements IDomicile.NumBedrooms Get End Get Set(ByVal Value As Integer) End Set End Property Public Property SquareFeet() As Integer Implements IDomicile.SquareFeet Get End Get Set(ByVal Value As Integer) End Set End Property Public Property Capacity() As Integer Implements IBoat.Capacity Get End Get Set(ByVal Value As Integer) End Set End Property Public Property HasMotor() As Boolean Implements IBoat.HasMotor Get End Get Set(ByVal Value As Boolean) End Set End Property Public Property HorsePower() As Integer Implements IBoat.HorsePower Get End Get Set(ByVal Value As Integer) End Set End Property Public Sub Sail() Implements IBoat.Sail End Sub End Class
Using an interface in place of inheritance is sometimes called interface inheritance. The class doesn’t inherit a parent class’s code, but it does inherit the definition of the features that it must provide.
Note that a class can inherit from one class and also implement one or more interfaces. To save coding, you could make one of the parent interfaces into a class. For example, if the IDomicile interface defines more behaviors than the IBoat interface, and if those behaviors are generic enough to provide help for derived classes, you could turn IDomicile into a Domicile class that implemented those features. Then the House and HouseBoat classes could inherit the Domicile class’s features, and HouseBoat would implement IBoat.
If a class declaration uses any Implements statements, they must come after any Inherits statement and before any other statements (other than comments).