Inheritance


Inheritance is the concept that a new class can be based on an existing class, inheriting its interface and functionality from the original class. In Chapter 3, you explored the relationship between a class and an object, where the class is essentially a template from which objects can be created.

While this is very powerful, it doesn’t provide all the capabilities you might like. In particular, in many cases a class only partially describes what you need for your object. You may have a class called Person, for instance, which has all the properties and methods that apply to all types of people, things such as first name, last name, and birth date. While useful, this class probably doesn’t have everything you need to describe a specific type of person, such as an employee or a customer. An employee would have a hire date and a salary, which are not included in Person, while a customer would have a credit rating, something neither the Person nor the Employee classes would need.

Without inheritance, you’d probably end up replicating the code from the Person class in both the Employee and Customer classes so that they’d have that same functionality as well as the ability to add new functionality of their own.

Inheritance makes it very easy to create classes for Employee, Customer, and so forth. You don’t have to re-create that code for an employee to be a person; it automatically inherits any properties, methods, and events from the original Person class.

You can think of it this way: When you create an Employee class, which inherits from a Person class, you are effectively merging these two classes. If you then create an object based on the Employee class, it not only has the interface (properties, methods, and events) and implementation from the Employee class, but also those from the Person class.

While an Employee object represents the merger between the Employee and Person classes, it is important to realize that the variables and code contained in each of those classes remain independent. Two perspectives are involved.

From the outside, the client code that interacts with the Employee object sees a single, unified object that represents the merger of the Employee and Person classes.

From the inside, the code in the Employee class and the code in the Person class aren’t totally intermixed. Variables and methods that are Private are only available within the class they were written. Variables and methods that are Public in one class can be called from the other class. Variables and methods that are declared as Friend are only available between classes if both classes are in the same Visual Basic project. As discussed later in the chapter, there is also a Protected scope that is designed to work with inheritance, but, again, this provides a controlled way for one class to interact with the variables and methods in the other class.

Visual Studio 2005 includes a Class Designer tool that enables you to easily create diagrams of your classes and their relationships. The Class Designer diagrams are a derivative of a standard notation called the Unified Modeling Language (UML) that is typically used to diagram the relationships between classes, objects, and other object-oriented concepts. The Class Designer diagrams more accurately and completely model .NET classes, so that is the notation used in this chapter. The relationship between the Person, Employee, and Customer classes is illustrated in Figure 4-1.

image from book
Figure 4-1

Each box in this diagram represents a class; in this case, you have Person, Employee, and Customer classes. The line from Employee back up to Person, terminating in a triangle, indicates that Employee is derived from, or inherits from, Person. The same is true for the Customer class.

Later in this chapter you will see in more detail when and how inheritance should be used in software design. The beginning part of this chapter covers the syntax and programming concepts necessary to implement inheritance. First, you’ll create a base Person class. Then, you’ll use that class to create both Employee and Customer classes that inherit behavior from Person.

Before getting into the implementation, however, it’s necessary to understand some basic terms associated with inheritance - and there are a lot of terms, partly because there are often several ways to say the same thing. The various terms are all used quite frequently and interchangeably.

Tip 

Though we attempt to use consistent terminology in this book, be aware that in other books and articles and online all these terms are used in various permutations.

Inheritance, for instance, is also sometimes referred to as generalization because the class from which you are inheriting your behavior is virtually always a more general form of your new class. A person is more general than an employee, for instance.

The inheritance relationship is also referred to as an is-a relationship. When you create a Customer class that inherits from a Person class, that customer is a person. The employee is a person as well. Thus, you have this is-a relationship. As shown later in the chapter, multiple interfaces can be used to implement something similar to the is-a relationship, the act-as relationship.

When you create a class using inheritance, it inherits behaviors and data from an existing class. That existing class is called the base class. It is also often referred to as a superclass or a parent class.

The class you create using inheritance is based on the parent class. It is called a subclass. Sometimes, it is also called a child class or a derived class. In fact, the process of inheriting from a base class by a subclass is often referred to as deriving. You are deriving a new class from the base class. The process is also often called subclassing.

Implementing Inheritance

When you set out to implement a class using inheritance, you must first start with an existing class from which you will derive your new subclass. This existing class, or base class, may be part of the .NET system class library framework, it may be part of some other application or .NET assembly, or you may create it as part of your existing application.

Once you have a base class, you can then implement one or more subclasses based on that base class. Each of your subclasses automatically has all of the methods, properties, and events of that base class - including the implementation behind each method, property, and event. Your subclass can add new methods, properties, and events of its own, extending the original interface with new functionality. In addition, a subclass can replace the methods and properties of the base class with its own new implementation - effectively overriding the original behavior and replacing it with new behaviors.

Essentially, inheritance is a way of merging functionality from an existing class into your new subclass. Inheritance also defines rules for how these methods, properties, and events can be merged, including control over how they can be changed or replaced, and how the subclass can add new methods, properties, and events of its own. This is what you’ll explore as you go forward - what these rules are and what syntax you use in Visual Basic to make it all work.

Creating a Base Class

Virtually any class you create can act as a base class from which other classes can be derived. In fact, unless you specifically indicate in the code that your class cannot be a base class, you can derive from it (you’ll come back to this later).

Create a new Windows Application project in Visual Basic. Then add a class to the project using the Project image from book Add Class menu option and name it Person.vb. Begin with the following code:

  Public Class Person End Class 

At this point, you technically have a base class, as it is possible to inherit from this class even though it doesn’t do or contain anything.

You can now add methods, properties, and events to this class as you normally would. All of those interface elements would be inherited by any class you might create based on Person. For instance, add the following code:

 Public Class Person   Private mName As String   Private mBirthDate As Date      Public Property Name() As String     Get       Return mName     End Get     Set(ByVal value As String)       mName = value     End Set   End Property   Public Property BirthDate() As Date     Get       Return mBirthDate     End Get     Set(ByVal value As Date)       mBirthDate = value     End Set   End Property End Class

This provides a simple method that can be used to illustrate how basic inheritance works. This class can be represented by the class diagram in Visual Studio, as shown in Figure 4-2.

image from book
Figure 4-2

In this representation of the class as it is presented from Visual Studio, the overall box represents the Person class. In the top section of this box, you have the name of the class and a specification that it is a class. The section below it contains a list of the instance variables, or fields, of the class, with their scope marked as Private (note the lock icon). The bottom section lists the properties exposed by the class, both marked as Public. If the class had methods or events, then they would be displayed in their own sections in the diagram.

Creating a Subclass

To implement inheritance, you need to add a new class to your project. Use the Project image from book Add Class menu option and add a new class named Employee.vb. Begin with the following code:

  Public Class Employee   Private mHireDate As Date   Private mSalary As Double   Public Property HireDate() As Date     Get       Return mHireDate     End Get     Set(ByVal value As Date)       mHireDate = value     End Set   End Property   Public Property Salary() As Double     Get       Return mSalary     End Get     Set(ByVal value As Double)       mSalary = value     End Set   End Property End Class 

This is a regular standalone class with no explicit inheritance. It can be represented by the following class diagram (see Figure 4-3).

image from book
Figure 4-3

Again, you can see the class name, its list of instance variables, and the properties it includes as part of its interface. It turns out that, behind the scenes, this class inherits some capabilities from System.Object. In fact, every class in the entire .NET platform ultimately inherits from System.Object either implicitly or explicitly. This is why all .NET objects have a basic set of common functionality, including most notably the GetType method. This is discussed in detail later in the chapter.

While having an Employee object with a hire date and salary is useful, it should also have Name and BirthDate properties, just as you implemented in the Person class. Without inheritance, you’d probably just copy and paste the code from Person directly into the new Employee class, but with inheritance you can directly reuse the code from the Person class. Let’s make the new class inherit from Person.

The Inherits Keyword

To make Employee a subclass of Person, add a single line of code:

 Public Class Employee   Inherits Person 

The Inherits keyword indicates that a class should derive from an existing class, inheriting the interface and behavior from that class. You can inherit from almost any class in your project, or from the .NET system class library or from other assemblies. It is also possible to prevent inheritance, as covered later in the chapter. When using the Inherits keyword to inherit from classes outside the current project, you need to either specify the namespace that contains that class or have an Imports statement at the top of the class to import that namespace for your use.

The diagram in Figure 4-4 illustrates the fact that the Employee class is now a subclass of Person.

image from book
Figure 4-4

The line running from Employee back up to Person ends in an open triangle, which is the symbol for inheritance when using the Class Designer in Visual Studio. It is this line that indicates that the Employee class includes all the functionality, as well as the interface, of Person.

This means that an object created based on the Employee class has not only the methods HireDate and S alary, but also Name and BirthDate. To test this, bring up the designer for Form1 (which is automatically part of your project, because you created a Windows Application project) and add the following TextBox controls along with a button to the form:

Open table as spreadsheet

Control Type

Name

Text Value

TextBox

txtName

<blank>

TextBox

txtBirthDate

<blank>

TextBox

txtHireDate

<blank>

TextBox

txtSalary

<blank>

button

btnOK

OK

You can also add some labels to make the form more readable. The Form Designer should now look something like Figure 4-5.

image from book
Figure 4-5

Double-click the button to bring up the code window, and enter the following code:

 Private Sub btnOK_Click(ByVal sender As System.Object, _                         ByVal e As System.EventArgs) Handles btnOK.Click   Dim emp As New Employee()   With emp     .Name = "Fred"     .BirthDate = #1/1/1960#     .HireDate = #1/1/1980#     .Salary = 30000     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtHireDate.Text = Format(.HireDate, "Short date")     txtSalary.Text = Format(.Salary, "$0.00")   End With End Sub 

Tip 

The best Visual Basic practice is to use the With keyword, but be aware that this might cause issues with portability and converting code to other languages.

Even though Employee doesn’t directly implement the Name or BirthDate methods, they are available for use through inheritance. When you run this application and click the button, your controls are populated with the values from the Employee object.

When the code in Form1 invokes the Name property on the Employee object, the code from the Person class is executed, as the Employee class has no such method built in. However, when the HireDate property is invoked on the Employee object, the code from the Employee class is executed, as it does have that method as part of its code.

From the form’s perspective, it doesn’t matter whether a method is implemented in the Employee class or the Person class; they are all simply methods of the Employee object. In addition, because the code in these classes is merged to create the Employee object, there is no performance difference between calling a method implemented by the Employee class or calling a method implemented by the Person class.

Overloading Methods

Although your Employee class automatically gains the Name and BirthDate methods through inheritance, it also has methods of its own - HireDate and Salary. This shows how you’ve extended the base Person interface by adding methods and properties to the Employee subclass.

You can add new properties, methods, and events to the Employee class, and they will be part of any object created based on Employee. This has no impact on the Person class whatsoever, only on the Employee class and Employee objects.

You can even extend the functionality of the base class by adding methods to the subclass that have the same name as methods or properties in the base class, as long as those methods or properties have different parameter lists. You are effectively overloading the existing methods from the base class. It is essentially the same thing as overloading regular methods, as discussed in Chapter 3.

For example, your Person class is currently providing your implementation for the Name property. Employees may have other names you also want to store, perhaps an informal name and a very formal name in addition to their regular name. One way to accommodate this requirement is to change the Person class itself to include an overloaded Name property that supports this new functionality. However, you’re really only trying to enhance the Employee class, not the more general Person class, so what you want is a way to add an overloaded method to the Employee class itself, even though you’re overloading a method from its base class.

You can overload a method from a base class by using the Overloads keyword. The concept is the same as described in Chapter 3, but in this case an extra keyword is involved. To overload the Name property, for instance, you can add a new property to the Employee class. First, though, let’s define an enumerated type using the Enum keyword. This Enum will list the different types of name you want to store. Add this Enum to the Employee.vb file, before the declaration of the class itself:

 Public Enum NameTypes   Informal = 1   Formal = 2 End Enum Public Class Employee

You can then add an overloaded Name property to the Employee class itself:

 Public Class Employee   Inherits Person   Private mHireDate As Date   Private mSalary As Double   Private mNames As New Generic.Dictionary(Of NameTypes, String)    Public Overloads Property Name(ByVal type As NameTypes) As String     Get       Return mNames(type)     End Get     Set(ByVal value As String)       If mNames.ContainsKey(type) Then         mNames.Item(type) = value       Else         mNames.Add(type, value)       End If     End Set   End Property 

This Name property is actually a property array, enabling you to store multiple values via the same property. In this case, you’re storing the values in a Generic.Dictionary(Of K, V) object, which is indexed by using the Enum value you just defined. Chapter 7 discusses generics in detail. For now, you can view this generic Dictionary just like any collection object that stores key/value data.

Tip 

If you omit the Overloads keyword here, your new implementation of the Name method will shadow the original implementation. Shadowing is very different from overloading, and is covered later in the chapter.

Though this method has the same name as the method in the base class, the fact that it accepts a different parameter list enables you to use overloading to implement it here. The original Name property, as implemented in the Person class, remains intact and valid, but now you’ve added a new variation with this second Name property, as shown by Figure 4-6.

image from book
Figure 4-6

The diagram clearly indicates that the Name method in the Person class and the Name method in the Employee class both exist. If you hover over each Name property, you’ll see a tooltip showing the method signatures, making it very clear that each one has a different signature.

You can now change Form1to make use of this new version of the Nameproperty. First, add a couple of new text box controls and associated labels. The text box controls should be named txtFormaland txt - Informal, and the form should now look like the one shown in Figure 4-7. Double-click the button on the form to bring up the code window and add code to work with the overloaded version of the Name property:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim emp As New Employee()   With emp     .Name = "Fred"     .Name (NameTypes.Formal) = "Mr. Frederick R. Jones, Sr."     .Name (NameTypes.Informal) = "Freddy"     .BirthDate = #1/1/1960#     .HireDate = #1/1/1980#     .Salary = 30000     txtName.Text = .Name     txtFormal.Text = .Name (NameTypes.Formal)     txtInformal.Text = .Name (NameTypes.Informal)     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtHireDate.Text = Format(.HireDate, "Short date")     txtSalary.Text = Format(.Salary, "$0.00")   End With End Sub

image from book
Figure 4-7

As you can see, the code still interacts with the original Name property as implemented in the Person class, but you are now also invoking the overloaded version of the property implemented in the Employee class.

Overriding Methods

So far, you’ve seen how to implement a base class and then use it to create a subclass. You also extended the interface by adding methods, and you explored how to use overloading to add methods that have the same name as methods in the base class, but with different parameters.

However, sometimes you may want not only to extend the original functionality, but also to actually change or entirely replace the functionality from the base class. Instead of leaving the existing functionality and just adding new methods or overloaded versions of those methods, you might want to entirely override the existing functionality with your own.

You can do exactly that. If the base class allows it, you can substitute your own implementation of a method in the base class - meaning that your new implementation will be used instead of the original.

The Overridable Keyword

By default, you can’t override the behavior of methods on a base class. The base class must be coded specifically to allow this to occur, by using the Overridable keyword. This is important, as you may not always want to allow a subclass to entirely change the behavior of the methods in your base class. However, if you do wish to allow the author of a subclass to replace your implementation, you can do so by adding the Overridable keyword to your method declaration.

Returning to the Employee example, you may not like the implementation of the BirthDate method as it stands in the Person class. Say, for instance, that you can’t employ anyone younger than 16 years of age, so any birth date value more recent than 16 years ago is invalid for an employee.

To implement this business rule, you need to change the way the BirthDate property is implemented. While you could make this change directly in the Person class, that would not be ideal. It is perfectly acceptable to have a person under age 16, just not an employee.

Open the code window for the Person class and change the BirthDate property to include the Overridable keyword:

 Public Overridable Property BirthDate() As Date   Get     Return mBirthDate   End Get   Set(ByVal value As Date)     mBirthDate = value   End Set End Property

This change allows any class that inherits from Person to entirely replace the implementation of the BirthDate property with a new implementation.

By adding the Overridable keyword to your method declaration, you are indicating that you want to allow any subclass to override the behavior provided by this method. This means that you are giving permission for a subclass to totally ignore your prior implementation, or to extend your implementation by doing other work before or after your implementation is run.

If the subclass doesn’t override this method, the method works just like a regular method and is automatically included as part of the subclass’s interface. Putting the Overridable keyword on a method simply allows a subclass to override the method if you choose to have it do so.

The Overrides Keyword

In a subclass, you override a method by implementing a method of the same name, and with the same parameter list as the base class, and then you use the Overrides keyword to indicate that you are overriding that method.

This is different from overloading, because when you overload a method you’re adding a new method with the same name but a different parameter list. When you override a method, you’re actually replacing the original method with a new implementation.

Without the Overrides keyword, you’ll receive a compilation error when you implement a method with the same name as one from the base class.

Open the code window for the Employee class and add a new BirthDate property:

 Public Class Employee   Inherits Person   Private mHireDate As Date   Private mSalary As Double   Private mBirthDate As Date   Private mNames As New Generic.Dictionary(Of NameTypes, String)   Public Overrides Property BirthDate() As Date     Get       Return mBirthDate     End Get     Set(ByVal value As Date)       If DateDiff(DateInterval.Year, Value, Now) >= 16 Then         mBirthDate = value       Else         Throw New ArgumentException( _           "An employee must be at least 16 years old.")       End If     End Set   End Property 

Because you’re implementing your own version of the property, you have to declare a variable to store that value within the Employee class. This is not ideal, and there are a couple of ways around it, including the MyBase keyword and the Protected scope.

Notice also that you’ve enhanced the functionality in the Set block, so it now raises an error if the new birth date value would cause the employee to be less than 16 years of age. With this code, you’ve now entirely replaced the original BirthDate implementation with a new one that enforces your business rule (see Figure 4-8).

image from book
Figure 4-8

The diagram now includes a BirthDate method in the Employee class. While perhaps not entirely intuitive, this is how the class diagram indicates that you’ve overridden the method. If you hover the mouse over the property in the Employee class, the tooltip will show the method signature, including the Overrides keyword.

If you now run your application and click the button on the form, everything should work as it did before because the birth date you’re supplying conforms to your new business rule. However, you can change the code in your form to use an invalid birth date:

 With emp   .Name = "Fred"   .Name(NameTypes.Formal) = "Mr. Frederick R. Jones, Sr."   .Name(NameTypes.Informal) = "Freddy"   .BirthDate = #1/1/2000# 

When you run the application (from within Visual Studio) and click the button, you’ll receive an error indicating that the birth date is invalid. This proves that you are now using the implementation of the

BirthDate method from the Employee class, rather than the one from the Person class. Change the date value in the form back to a valid value so that your application runs properly.

The MyBase Keyword

You’ve just seen how you can entirely replace the functionality of a method in the base class by overriding it in your subclass. However, this can be somewhat extreme; sometimes it would be preferable to override methods so that you extend the base functionality, rather than replace the functionality.

To do this, you need to override the method using the Overrides keyword as you just did, but within your new implementation you can still invoke the original implementation of the method. This enables you to add your own code before or after the original implementation is invoked - meaning you can extend the behavior while still leveraging the code in the base class.

To invoke methods directly from the base class, you can use the MyBase keyword. This keyword is available within any class, and it exposes all the methods of the base class for your use.

Tip 

Even a base class such as Person is an implicit subclass of System.Object, so it can use MyBase to interact with its base class as well.

This means that within the BirthDate implementation in Employee, you can invoke the BirthDate implementation in the base Person class. This is ideal, as it means that you can leverage any existing functionality provided by Person while still enforcing your Employee-specific business rules.

To take advantage of this, you can enhance the code in the Employee implementation of BirthDate. First, remove the declaration of mBirthDate from the Employee class. You won’t need this variable any longer because the Person implementation will keep track of the value on your behalf. Then, change the BirthDate implementation in the Employee class as follows:

 Public Overrides Property BirthDate() As Date   Get     Return MyBase.BirthDate   End Get   Set(ByVal value As Date)     If DateDiff(DateInterval.Year, Value, Now) >= 16 Then       MyBase.BirthDate = value     Else       Throw New ArgumentException( _         "An employee must be at least 16 years old.")       End If     End Set   End Property

Run your application and you’ll see that it works just fine even though the Employee class no longer contains any code to actually keep track of the birth date value. You’ve effectively merged the BirthDate implementation from Person right into your enhanced implementation in Employee, creating a hybrid version of the property.

The MyBase keyword is covered in more detail later in the chapter. Here, you can see how it enables you to enhance or extend the functionality of the base class by adding your own code in the subclass but still invoking the base class method when appropriate.

Virtual Methods

The BirthDate method is an example of a virtual method. Virtual methods are those that can be overridden and replaced by subclasses.

Virtual methods are more complex to understand than regular nonvirtual methods. With a nonvirtual method, only one implementation matches any given method signature, so there’s no ambiguity about which specific method implementation will be invoked. With virtual methods, however, there may be several implementations of the same method, with the same method signature, so you need to understand the rules that govern which specific implementation of that method will be called.

When working with virtual methods, keep in mind that the datatype of the object is used to determine the implementation of the method to call, rather than the type of the variable that refers to the object.

If you look at the code in your form, you can see that you’re declaring an object variable of type Employee, and you are then creating an Employee object that you can reference via that object:

 Dim emp As New Employee()

It is not surprising, then, that you are able to invoke any of the methods that are implemented as part of the Employee class, and through inheritance, any of the methods implemented as part of the Person class:

 With emp   .Name = "Fred"   .Name(NameTypes.Formal) = "Mr. Frederick R. Jones, Sr."   .Name(NameTypes.Informal) = "Freddy"   .BirthDate = #1/1/1960#   .HireDate = #1/1/1980#   .Salary = 30000

When you call the BirthDate property, you know that you’re invoking the implementation contained in the Employee class, which makes sense because you know that you’re using a variable of type Employee to refer to an object of type Employee.

However, because your methods are virtual methods, you can experiment with some much more interesting scenarios. For instance, suppose that you change the code in your form to interact directly with an object of type Person instead of one of type Employee:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As New Person()    With person     .Name = "Fred"     .BirthDate = #1/1/1960#      txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")   End With  End Sub 

You can no longer call the methods implemented by the Employee class, because they don’t exist as part of aPerson object, but only as part of an Employee object. However, you can see that both the Name and BirthDate properties continue to function as you’d expect. When you run the application now, it will work just fine. You can even change the birth date value to something that would be invalid for Employee:

  .BirthDate = #1/1/2000# 

The application will now accept it and work just fine, because the BirthDate method you’re invoking is the original version from the Person class.

These are the two simple scenarios, when you have a variable and object of type Employee or a variable and object of type Person. However, because Employee is derived from Person, you can do something a bit more interesting. You can use a variable of type Person to hold a reference to an Employee object. For example, you can change the code in Form1 as follows:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Person   person = New Employee()   With person     .Name = "Fred"     .BirthDate = #1/1/2000#     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")   End With End Sub

What you’re doing now is declaring your variable to be of type Person, but the object itself is an instance of the Employee class. You’ve done something a bit complex here, as the datatype of the variable is not the same as the datatype of the object itself. Remember that a variable of a base class type can always hold a reference to an object of any subclass.

Important 

This is the reason why a variable of type System.Object can hold a reference to literally anything in the .NET Framework, because all classes are ultimately derived from System.Object.

This technique is very useful when creating generic routines. It makes use of an object-oriented concept called polymorphism, which is discussed more thoroughly later in this chapter. This technique enables you to create a more general routine that populates your form for any object of type Person. Add the following code to the form:

  Private Sub DisplayPerson(ByVal thePerson As Person)   With thePerson     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")   End With End Sub 

Now you can change the code behind the button to make use of this generic routine:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Person   person = New Employee()   With person     .Name = "Fred"     .BirthDate = #1/1/2000#   End With   DisplayPerson(person) End Sub

The benefit here is that you can pass a Person object or an Employee object to DisplayPerson and the routine will work the same either way.

When you run the application now, things get interesting. You’ll get an error when you attempt to set the BirthDate property because it breaks your 16-year-old business rule, which is implemented in the Employee class. How can this be when your person variable is of type Person?

This clearly demonstrates the concept of a virtual method. It is the datatype of the object, in this case Employee, that is important. The datatype of the variable is not the deciding factor when choosing which implementation of an overridden method is invoked.

The following table shows which method is actually invoked based on the variable and object datatypes when working with virtual methods:

Open table as spreadsheet

Variable Type

Object Type

Method Invoked

Base

Base

Base

Base

Subclass

Subclass

Subclass

Subclass

Subclass

Virtual methods are very powerful and useful when you implement polymorphism using inheritance. A base class datatype can hold a reference to any subclass object, but it is the type of that specific object which determines the implementation of the method. Therefore, you can write generic routines that operate on many types of object as long as they derive from the same base class. You’ll learn how to make use of polymorphism and virtual methods in more detail later in this chapter.

Overriding Overloaded Methods

Earlier, you wrote code in your Employee class to overload the Name method in the base Person class. This enabled you to keep the original Name functionality but also extend it by adding another Name method that accepted a different parameter list.

You’ve also overridden the BirthDate method. The implementation in the Employee class replaced the implementation in the Person class. Overriding is a related, but different concept from overloading. It is also possible to both overload and override a method at the same time.

In the earlier overloading example, you added a new Name property to the Employee class, while retaining the functionality present in the base Person class. You may decide that you not only want to have your second overloaded implementation of the Name method, but also want to replace the existing one by overriding the existing method provided by the Person class.

In particular, you may want to do this so that you can store the Name value in the Hashtable object along with your Formal and Informal names. Before you can override the Name method, you need to add the Overridable keyword to the base implementation in the Person class:

 Public Overridable Property Name() As String   Get     Return mName   End Get   Set(ByVal value As String)     mName = value   End Set End Property

With that done, the Name method can now be overridden by any derived classes. In the Employee class, you can now override the Name method, replacing the functionality provided by the Person class. First, add a Normal option to the Enum that controls the types of Name value you can store:

 Public Enum NameTypes   Informal = 1   Formal = 2   Normal = 3 End Enum

Now you can add code to the Employee class to implement a new Name property. This is in addition to the existing Name property already implemented in the Employee class:

  Public Overloads Overrides Property Name() As String   Get     Return Name(NameTypes.Normal)   End Get   Set(ByVal value As String)     Name(NameTypes.Normal) = value   End Set End Property 

Notice that you’re using both the Overrides keyword, to indicate that you’re overriding the Name method from the base class, and the Overloads keyword, to indicate that you’re overloading this method in the subclass.

This new Name property merely delegates the call to the existing version of the Name property that handles the parameter-based names. To complete the linkage between this implementation of the Name property and the parameter-based version, you need to make one more change to that original overloaded version:

 Public Overloads Property Name(ByVal type As NameTypes) As String   Get     Return mNames(Type)   End Get   Set(ByVal value As String)     If mNames.ContainsKey(type) Then       mNames.Item(type) = value     Else       mNames.Add(type, value)     End If     If type = NameTypes.Normal Then       MyBase.Name = value     End If   End Set End Property

This way, if the client code sets the Name property by providing the Normal index, you are still updating the name in the base class as well as in the Dictionary object maintained by the Employee class.

Shadowing

Overloading allows you to add new versions of the existing methods as long as their parameter lists are different. Overriding allows your subclass to entirely replace the implementation of a base class method with a new method that has the same method signature. As you’ve just seen, you can even combine these concepts not only to replace the implementation of a method from the base class, but also to simultaneously overload that method with other implementations that have different method signatures.

However, anytime you override a method using the Overrides keyword, you are subject to the rules governing virtual methods - meaning that the base class must give you permission to override the method. If the base class doesn’t use the Overridable keyword, then you can’t override the method. Sometimes you may need to override a method that is not marked as Overridable, and shadowing enables you to do just that.

The Shadows keyword can also be used to entirely change the nature of a method or other interface element from the base class, although that is something which should be done with great care, as it can seriously reduce the maintainability of your code. Normally, when you create an Employee object, you expect that it can not only act as an Employee, but also as a Person because Employee is a subclass of Person. However, with the Shadows keyword, you can radically alter the behavior of an Employee class so that it doesn’t act like a Person. This sort of radical deviation from what is normally expected invites bugs and makes code hard to understand and maintain.

Shadowing methods is very dangerous and should be used as a last resort. It is primarily useful in cases where you have a preexisting component such as a Windows Forms control that was not designed for inheritance. If you absolutely must inherit from such a component, you may need to use shadowing to “override” methods or properties. There are serious limits and dangers, but it may be your only option.

You’ll explore this in more detail later. First, let’s see how Shadows can be used to override nonvirtual methods.

Overriding Nonvirtual Methods

Earlier in the chapter, we discussed virtual methods and how they are automatically created in Visual Basic when the Overrides keyword is employed. You can also implement nonvirtual methods in Visual Basic. Nonvirtual methods are methods that cannot be overridden and replaced by subclasses, so most methods you implement are nonvirtual.

Important 

If you don’t use the Overridable keyword when declaring a method, then it is nonvirtual.

In the typical case, nonvirtual methods are easy to understand. They can’t be overridden and replaced, so you know that there’s only one method by that name, with that method signature. Therefore, when you invoke it, there is no ambiguity about which specific implementation will be called. The reverse is true with virtual methods, where there may be more than one method of the same name, and with the same method signature, and you need to understand the rules governing which implementation will be invoked.

Of course, you knew it couldn’t be that simple, and it turns out that you can override nonvirtual methods by using the Shadows keyword. In fact, you can use the Shadows keyword to override methods regardless of whether or not they have the Overridable keyword in the declaration.

Important 

The Shadows keyword enables you to replace methods on the base class that the base class designer didn’t intend to be replaced.

Obviously, this can be very dangerous. The designer of a base class must be careful when marking a method as Overridable, ensuring that the base class continues to operate properly even when that method is replaced by another code in a subclass. Designers of base classes typically just assume that if they don’t mark a method as Overridable it will be called and not overridden. Thus, overriding a nonvirtual method by using the Shadows keyword can have unexpected and potentially dangerous side effects, as you are doing something that the base class designer assumed would never happen.

If that isn’t enough complexity, it turns out that shadowed methods follow different rules from virtual methods when they are invoked. That is, they don’t act like regular overridden methods; instead, they follow a different set of rules to determine which specific implementation of the method will be invoked. In particular, when you call a nonvirtual method, the datatype of the variable refers to the object that indicates which implementation of the method is called, not the datatype of the object, as with virtual methods.

To override a nonvirtual method, you can use the Shadows keyword instead of the Overrides keyword. To see how this works, add a new property to the base Person class:

  Public ReadOnly Property Age() As Integer   Get     Return CInt(DateDiff(DateInterval.Year, Now, BirthDate))   End Get End Property 

Here you’ve added a new method, called Age, to the base class, and thus automatically to the subclass. This code has a bug, introduced on purpose for illustration. The DateDiff parameters are in the wrong order, so you’ll get negative age values from this routine. We introduced a bug because sometimes you will find bugs in base classes that you didn’t write (and which you can’t fix because you don’t have the source code).

The following example walks you through the use of the Shadows keyword to address a bug in your base class, acting under the assumption that for some reason you can’t actually fix the code in the Person class.

Notice that you’re not using the Overridable keyword on this method, so any subclass is prevented from overriding the method by using the Overrides keyword. The obvious intent and expectation of this code is that all subclasses will use this implementation and not override it with their own.

However, the base class cannot prevent a subclass from shadowing a method, so it doesn’t matter whether you use Overridable or not; either way works fine for shadowing.

Before you shadow the method, let’s see how it works as a regular nonvirtual method. First, you need to change your form to use this new value. Add a text box named txtAge and a related label to the form. Next, change the code behind the button to use the Age property. You’ll include the code to display the data on the form right here to keep things simple and clear:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Employee = New Employee()   With person     .Name = "Fred"     .BirthDate = #1/1/1960#     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)   End With End Sub

Don’t forget to change the birth date value to something valid for an Employee.

At this point, you can run the application and the age field should appear in your display as expected, though with a negative value due to the bug we introduced. There’s no magic or complexity here. This is basic programming with objects, and basic use of inheritance as discussed at the beginning of this chapter.

Of course, you don’t want a bug in your code, but you don’t have access to the Person class, and the Person class doesn’t allow you to override the Age method, so what are you to do? The answer lies in the Shadows keyword, which allows you to override the method anyway.

Let’s shadow the Age method within the Employee class, overriding and replacing the implementation in the Person class even though it is not marked as Overridable. Add the following code to the Employee class:

  Public Shadows ReadOnly Property Age() As Integer   Get     Return CInt(DateDiff(DateInterval.Year, BirthDate, Now))   End Get End Property 

In many ways, this looks very similar to what you’ve seen with the Overrides keyword, in that you’re implementing a method in your subclass with the same name and parameter list as a method in the base class. In this case, however, you’ll see some different behavior when you interact with the object in different ways.

Technically, the Shadows keyword is not required here. Shadowing is the default behavior when a subclass implements a method that matches the name and method signature of a method in the base class. However, if you omit the Shadows keyword, the compiler will give you a warning indicating that the method is being shadowed, so it is always better to include the keyword, both to avoid the warning and to make it perfectly clear that you knew what you were doing when you chose to shadow the method.

Remember that your code in the form is currently declaring a variable of type Employee and is creating an instance of an Employee object:

 Dim person As Employee = New Employee()

This is a simple case, and, surprisingly, when you run the application now you’ll see that the value of the age field is correct, indicating that you just ran the implementation of the Age property from the Employee class. At this point, you’re seeing the same behavior that you saw when overriding with the Overrides keyword.

Let’s take a look at the other simple case, where you’re working with a variable and object that are both of datatype Person. Change the code in Form1 as follows:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Person = New Person()   With person     .Name = "Fred"     .BirthDate = #1/1/1960#     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)   End With End Sub

Now you have a variable of type Person and an object of that same type. You would expect that the implementation in the Person class would be invoked in this case, and that is exactly what happens; the age field displays the original negative value, indicating that you’re invoking the buggy implementation of the method directly from the Person class. Again, this is exactly the behavior you’d expect from a method overridden via the Overrides keyword.

This next example is where things get truly interesting. Change the code in Form1 as follows:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Person = New Employee()   With person     .Name = "Fred"     .BirthDate = #1/1/1960#     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)   End With End Sub

Now you are declaring the variable to be of type Person, but you are creating an object that is of datatype Employee. You did this earlier in the chapter when exploring the Overrides keyword as well, and in that case you discovered that the version of the method that was invoked was based on the datatype of the object. The BirthDate implementation in the Employee class was invoked.

If you run the application now, you will find that the rules are different when the Shadows keyword is used. In this case, the implementation in the Person class is invoked, giving you the buggy negative value. When the implementation in the Employee class is ignored, you get the exact opposite behavior of what you got with Overrides.

The following table summarizes which method implementation is invoked based on the variable and object datatypes when using shadowing:

Open table as spreadsheet

Variable Type

Object Type

Method Invoked

Base

Base

Base

Base

Subclass

Base

Subclass

Subclass

Subclass

In most cases, the behavior you will want for your methods is accomplished by the Overrides keyword and virtual methods. However, in cases where the base class designer doesn’t allow you to override a method and you want to do it anyway, the Shadows keyword provides you with the needed functionality.

Shadowing Arbitrary Elements

The Shadows keyword can be used not only to override nonvirtual methods, but also to totally replace and change the nature of a base class interface element. When you override a method, you are providing a replacement implementation of that method with the same name and method signature. Using the Shadows keyword, you can do more extreme things, such as change a method into an instance variable or change a Property into a Function.

However, this can be very dangerous, as any code written to use your objects will naturally assume that you implement all the same interface elements and behaviors as your base class, because that is the nature of inheritance. Any documentation or knowledge of the original interface is effectively invalidated because the original implementation is arbitrarily replaced.

Important 

By totally changing the nature of an interface element, you can cause a great deal of confusion for programmers who might interact with your class in the future.

To see how you can replace an interface element from the base class, let’s entirely change the nature of the Age property. In fact, let’s change it from a read-only property to a read-write property. You could get even more extreme - change it to a Function or a Sub.

Remove the Age property from the Employee class and add the following code:

  Public Shadows Property Age() As Integer   Get     Return CInt(DateDiff(DateInterval.Year, BirthDate, Now))   End Get   Set(ByVal value As Integer)     BirthDate = DateAdd(DateInterval.Year, -value, Now)   End Set End Property 

With this change, the very nature of the Age method has changed. It is no longer a simple read-only property; now it is a read-write property that includes code to calculate an approximate birth date based on the age value supplied.

As it stands, your application will continue to run just fine because you’re only using the read-only functionality of the property in your form. You can change the form to make use of the new read-write functionality:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Person = New Employee()   With person     .Name = "Fred"     .BirthDate = #1/1/1960#     .Age = 20     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)   End With End Sub

However, this will leave you with a syntax error. The variable you’re working with, person, is of datatype Person, and that datatype does not provide a writeable version of the Age property. In order to use your enhanced functionality, you must use a variable and object of type Employee:

  Dim person As Employee = New Employee() 

If you now run the application and click the button, you’ll see that the Age is displayed as 20, and the birth date is now a value calculated based on that age value, indicating that you are now running the shadowed version of the Age method as implemented in the Employee class.

As if that weren’t odd enough, you can do some even stranger and more dangerous things. You can change Age into a variable, and you can even change its scope. For instance, you can comment out the Age property code in the Employee class and replace it with the following code:

  Private Shadows Age As String 

At this point, you’ve changed everything. Age is now a String instead of an Integer. It is a variable instead of a Property or Function. It has Private scope instead of Public scope. Your Employee object is now totally incompatible with the Person datatype, something that shouldn’t occur normally when using inheritance.

This means that the code you wrote in Form1 will no longer work. The Age property is no longer accessible and can no longer be used, so your project will no longer compile. This directly illustrates the danger in shadowing a base class element such that its very nature or scope is changed by the subclass.

Because this change prevents your application from compiling, remove the line in the Employee class that shadows Age as a String variable, and uncomment the shadowed Property routine:

 Public Shadows Property Age() As Integer   Get     Return CInt(DateDiff(DateInterval.Year, BirthDate, Now))   End Get   Set(ByVal value As Integer)     BirthDate = DateAdd(DateInterval.Year, -value, Now)   End Set End Property

This restores your application to a working state, and you can move on.

Levels of Inheritance

So far, you’ve created a single base class and a single subclass, thus demonstrating that you can implement inheritance that is a single level deep. However, you can create inheritance relationships that are several levels deep. These are sometimes referred to as chains of inheritance.

Tip 

In reality, you’ve been creating a two-level inheritance hierarchy so far, because you know that your base class actually derived from System.Object, but for most purposes it is easiest to simply ignore that fact and treat only your classes as part of the inheritance hierarchy.

Multiple Inheritance

Don’t confuse multilevel inheritance with multiple inheritance, which is an entirely different concept that is not supported by either Visual Basic or the .NET platform itself. The idea behind multiple inheritance is that you can have a single subclass that inherits from two base classes at the same time.

For instance, you may have an application that has a class for Customer and another class for Vendor. It is quite possible that some customers are also vendors, so you might want to combine the functionality of these two classes into a CustomerVendor class. This new class would be a combination of both Customer and Vendor, so it would be nice to inherit from both of them at once.

While this is a useful concept, multiple inheritance is complex and somewhat dangerous. Numerous problems are associated with multiple inheritance, but the most obvious is the possibility of collisions of properties or methods from the base classes. Suppose that both Customer and Vendor have a Name property. CustomerVendor would need two Name properties, one for each base class. Yet it only makes sense to have one Name property on CustomerVendor, so to which base class does it link, and how will the system operate if it doesn’t link to the other one?

These are complex issues with no easy answers. Within the object-oriented community there is continual debate as to whether the advantages of code reuse outweigh the complexity that comes along for the ride.

Multiple inheritance is not supported by the .NET Framework, so it is likewise not supported by Visual Basic. However, you can use multiple interfaces to achieve an effect similar to multiple inheritance, a topic discussed later in the chapter when we talk about implementing multiple interfaces.

Multilevel Inheritance

You’ve seen how a subclass derives from a base class with your Person and Employee classes. However, nothing prevents the Employee subclass from being the base class for yet another class, a subsubclass so to speak. This is not at all uncommon. In the working example, you may find that you have different kinds of employees, some who work in the office and others who travel.

To accommodate this, you may want to have OfficeEmployee and TravelingEmployee classes. Of course, these are both examples of an employee and should share the functionality already present in the Employee class. The Employee class already reuses the functionality from the Person class. Figure 4-9 illustrates how these classes are interrelated.

image from book
Figure 4-9

The Employee is a subclass of Person, and your two new classes are both subclasses of Employee. While both OfficeEmployee and TravelingEmployee are employees, and thus also people, they are each unique. An OfficeEmployee almost certainly has a cube or office number, while a TravelingEmployee will keep track of the number of miles traveled.

Add a new class to your project and name it OfficeEmployee. To make this class inherit from your existing Employee class, add the following code to the class:

 Public Class OfficeEmployee   Inherits Employee End Class

With this change, the new class now has Name, BirthDate, Age, HireDate, and Salary methods. Notice that methods from both Employee and Person are inherited. A subclass always gains all the methods, properties, and events of its base class.

You can now extend the interface and behavior of OfficeEmployee by adding a property to indicate which cube or office number the employee occupies:

 Public Class OfficeEmployee   Inherits Employee   Private mOffice As String   Public Property OfficeNumber() As String     Get       Return mOffice     End Get     Set(ByVal value As String)       mOffice = value     End Set   End Property End Class 

To see how this works, let’s enhance your form to display this value. Add a new TextBox control named txtOffice and an associated label so that your form looks like the one shown in Figure 4-10.

image from book
Figure 4-10

Now change the code behind the button to make use of the new property:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As OfficeEmployee = New OfficeEmployee()   With person     .Name = "Fred"     .BirthDate = #1/1/1960#     .Age = 20     .OfficeNumber = "A42"     txtName.Text = .Name     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)     txtOffice.Text = .OfficeNumber   End With End Sub

You’ve changed the routine to declare and create an object of type OfficeEmployee - thus enabling you to make use of the new property, as well as all existing properties and methods from Employee and Person, as they’ve been “merged” into the OfficeEmployee class via inheritance. If you now run the application, the name, birth date, age, and office values are displayed in the form.

Inheritance like this can go many levels deep, with each level extending and changing the behaviors of the previous levels. In fact, there is no specific technical limit to the number of levels of inheritance you can implement in Visual Basic, although very deep inheritance chains are typically not recommended and are often viewed as a design flaw, something discussed in more detail later in this chapter.

Interacting with the Base Class, Your Class, and Your Object

You’ve already seen how you can use the MyBase keyword to call methods on the base class from within a subclass. The MyBase keyword is one of three special keywords that enable you to interact with important object and class representations:

  • Me

  • MyBase

  • MyClass

The Me Keyword

The Me keyword provides you with a reference to your current object instance. Typically, you don’t need to use the Me keyword, as anytime you want to invoke a method within your current object you can just call that method directly.

To see clearly how this works, let’s add a new method to the Person class that returns the data of the Person class in the form of a String. This is interesting in and of itself, as the base System.Object class defines the ToString method for this exact purpose. Remember that all classes in the .NET Framework ultimately derive from System.Object, even if you don’t explicitly indicate it with an Inherits statement. This means that you can simply override the ToString() method from the Object class within your Person class by adding the following code:

  Public Overrides Function ToString() As String   Return Name End Function 

This implementation returns the person’s Name property as a result when ToString() is called.

Tip 

By default, ToString() returns the class name of the class. Up to now, if you had called the ToString() method on a Person object, you would have gotten a result of InheritanceAndInterfaces.Person.

Notice that the ToString() method is calling another method within your same class - in this case, the Name method.

You could also write this routine using the Me keyword:

 Public Overrides Function ToString() As String   Return Me.Name End Function

However, this is redundant because Me is the default for all method calls in a class. These two implementations are identical, so typically the Me keyword is simply omitted to avoid the extra typing.

To see how the ToString() method now works, you can change your code in Form1 to use this value instead of the Name property:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click     Dim objPerson As OfficeEmployee = New OfficeEmployee()     With objPerson       .Name = "Fred"       .BirthDate = #1/1/1960#       .Age = 20       .OfficeNumber = "A42"       txtName.Text = .ToString()       txtBirthDate.Text = Format(.BirthDate, "Short date")       txtAge.Text = CStr(.Age)    txtOffice.Text = .OfficeNumber   End With End Sub

When you run the application, you’ll see that the person’s name is displayed appropriately, which makes sense, as the ToString() method is simply returning the result from the Name property.

Earlier, we discussed virtual methods and how they work. Because either calling a method directly or calling it using the Me keyword invokes the method on the current object, the method calls conform to the same rules as an external method call. In other words, your ToString() method may not actually end up calling the Name method in the Person class if that method was overridden by a class farther down the inheritance chain, such as the Employee or OfficeEmployee classes.

For example, you could override the Name property in your OfficeEmployee class such that it always returns the informal version of the person’s name, rather than the regular name. You can override the Name property by adding this method to the OfficeEmployee class:

  Public Overloads Overrides Property Name() As String   Get     Return MyBase.Name(NameTypes.Informal)   End Get   Set(ByVal value As String)     MyBase.Name = value   End Set End Property 

This new version of the Name method relies on the base class to actually store the value, but instead of returning the regular name on request, now you are always returning the informal name:

 Return MyBase.Name(NameTypes.Informal)

Before you can test this, you need to enhance the code in your form to actually provide a value for the informal name. Make the following change to the code:

 Private Sub btnOK_Click(ByVal sender As System.Object,     ByVal e As System.EventArgs) Handles btnOK.Click   Dim objPerson As OfficeEmployee = New OfficeEmployee()   With objPerson     .Name = "Fred"     .Name(NameTypes.Informal) = "Freddy"     .BirthDate = #1/1/1960#     .Age = 20     .OfficeNumber = "A42"     txtName.Text = .ToString()     txtBirthDate.Text = Format(.BirthDate, "Short date")     txtAge.Text = CStr(.Age)     txtOffice.Text = .OfficeNumber   End With End Sub

When you run the application, you’ll find that the name field displays the informal name. Even though the ToString() method is implemented in the Person class, it is invoking the implementation of Name from the OfficeEmployee class. This is because method calls within a class follow the same rules for calling virtual methods as code outside a class, such as your code in the form. You’ll see this behavior with or without the Me keyword, as the default behavior for method calls is to implicitly call them via the current object.

While methods called from within a class follow the same rules for virtual methods, this is not the case for shadowed methods. Here, you’ll find that the rules for calling a shadowed method from within your class are different from those outside your class.

To see how this works, make the Name property in OfficeEmployee a shadowed method instead of an overridden method:

 Public Shadows Property Name() As String   Get     Return MyBase.Name(NameTypes.Informal)   End Get   Set(ByVal value As String)     MyBase.Name = value   End Set End Property

Before you can run your application, you must adjust some code in the form. Because you’ve shadowed the Name property in OfficeEmployee, the version of Name from Employee that acts as a property array is now invalid.

Important 

Shadowing a method replaces all implementations from higher in the inheritance chain, regardless of their method signature.

To make your application operate, you need to change the variable declaration and object creation to declare a variable of type Employee so that you can access the property array while still creating an instance of OfficeEmployee: Dim person As Employee = New OfficeEmployee()

Because your variable is now of type Employee, you also need to comment out the lines that refer to the OfficeNumber property, as it is no longer available:

 With person   .Name = "Fred"   .Name(NameTypes.Informal) = "Freddy"   .BirthDate = #1/1/1960#   .Age = 20   '.OfficeNumber = "A42"   txtName.Text = .ToString()   txtBirthDate.Text = Format(.BirthDate, "Short date")   txtAge.Text = CStr(.Age)   'txtOffice.Text = .OfficeNumber End With

When you run the application now, it displays the name Fred, rather than Freddy, meaning it is not calling the Name method from OfficeEmployee; instead, it is calling the implementation provided by the Employee class. Remember that the code to make this call still resides in the Person class, but it now ignores the shadowed version of the Name method.

Shadowed implementations in subclasses are ignored when calling the method from within a class higher in the inheritance chain.

You’ll get this same behavior with or without the Me keyword. The Me keyword, or calling methods directly, follows the same rules for overridden methods as any other method call. For shadowed methods, however, any shadowed implementations in subclasses are ignored and the method is called from the current level in the inheritance chain.

Why, then, does the Me keyword exist? Primarily to enable you to pass a reference to the current object as a parameter to other objects or methods. As shown when you look at the MyBase and MyClass keywords, things can get very confusing, and there may be value in using the Me keyword when working with MyBase and MyClass to ensure that it is always clear which particular implementation of a method you intended to invoke.

The MyBase Keyword

While the Me keyword allows you to call methods on the current object instance, at times you might want to explicitly call into methods in your parent class. Earlier, you saw an example of this when you called back into the base class from an overridden method in the subclass.

The MyBase keyword references only the immediate parent class, and it works like an object reference. This means that you can call methods on MyBase, knowing that they are being called just as if you had a reference to an object of your parent class’s datatype.

Important 

There is no way to directly navigate up the inheritance chain beyond the immediate parent. This means that you can’t directly access the implementation of a method in a base class if you are in a sub-subclass. Such behavior isn’t a good idea anyway, which is why it isn’t allowed.

The MyBase keyword can be used to invoke or use any Public, Friend, or Protected element from the parent class. This includes all elements directly on the base class, and any elements the base class inherited from other classes higher in the inheritance chain.

You’ve already used MyBase to call back into the base Person class as you implemented the overridden Name property in the Employee class.

Important 

Any code within a subclass can call any method on the base class by using the MyBase keyword.

You can also use MyBase to call back into the base class implementation even if you’ve shadowed a method. Though it wasn’t noted at the time, you’ve already done this in your shadowed implementation of the Name property in the OfficeEmployee class. The highlighted lines indicate where you’re calling into the base class from within a shadowed method:

 Public Shadows Property Name() As String   Get     Return MyBase.Name(NameTypes.Informal)   End Get   Set(ByVal value As String)     MyBase.Name = value   End Set End Property

The MyBase keyword enables you to merge the functionality of the base class into your subclass code as you deem fit.

The MyClass Keyword

As you’ve seen, when you use the Me keyword or call a method directly, your method call follows the rules for calling both virtual and nonvirtual methods. In other words, as you discovered earlier with the Name property, a call to Name from your code in the Person class actually invoked the overridden version of Name located in the OfficeEmployee class.

While this behavior is often useful, sometimes you’ll want to ensure that you really are running the specific implementation from your class; even if a subclass overrode your method, you still want to ensure you’re calling the version of the method that is directly in your class.

Maybe you decide that your ToString() implementation in Person should always call the Name implementation that you write in the Person class, totally ignoring any overridden versions of Name in any subclasses.

This is where the MyClass keyword shines. This keyword is much like MyBase, in that it provides you with access to methods as though it were an object reference - in this case, a reference to an instance of the class that contains the code you’re writing when using the MyClass keyword. This is true even when the instantiated object is an instance of a class derived from your class.

You’ve seen that a call to ToString() from within Person actually invokes the implementation in Employee or OfficeEmployee if your object is an instance of either of those types. Let’s restore the Name property in OfficeEmployee so that it is an overridden method, rather than a shadowed method, to see how this works:

 Public Overloads Overrides Property Name() As String   Get     Return MyBase.Name(NameTypes.Informal)   End Get   Set(ByVal value As String)     MyBase.Name = value   End Set End Property

With this change, and based on your earlier testing, you know that the ToString() implementation in Person will automatically call this overridden version of the Name property, as the call to the Name method follows the normal rules for virtual methods. In fact, if you run the application now, the Name field on the form displays Freddy, the informal name of the person.

You can force the use of the implementation in the current class through the use of MyClass. Change the ToString() method in Person as follows:

 Public Overrides Function ToString() As String   Return MyClass.Name End Function

You are now calling the Name method, but you’re doing it using the MyClass keyword. When you run the application and click the button, the name field in the form displays Fred rather than Freddy, proving that the implementation from Person was invoked even though the datatype of the object itself is OfficeEmployee.

The ToString() method is invoked from Person, since neither Employee nor OfficeEmployee provide an overridden implementation. Then, because you’re using the MyClass keyword, the Name method is invoked directly from Person, explicitly defeating the default behavior you’d normally expect.

Constructors

As discussed in Chapter 3, you can provide a special constructor method, named New, on a class and it will be the first code run when an object is instantiated. You can also receive parameters via the constructor method, allowing the code that creates your object to pass data into the object during the creation process.

Constructor methods are affected by inheritance differently than regular methods. A normal Public method, such as BirthDate on your Person class, is automatically inherited by any subclass. From there you can overload, override, or shadow that method, as discussed already.

Simple Constructors

Constructors don’t quite follow the same rules. To explore the differences, let’s implement a simple constructor method in the Person class:

  Public Sub New()   Debug.WriteLine("Person constructor") End Sub 

If you now run the application, you’ll see the text displayed in the output window in the IDE. This occurs even though the code in your form is creating an object of type OfficeEmployee:

 Dim person As Employee = New OfficeEmployee()

As you might expect, the New method from your base Person class is invoked as part of the construction process of the OfficeEmployee object, simple inheritance at work. However, interesting things occur if you implement a New method in the OfficeEmployee class itself:

  Public Sub New()   Debug.WriteLine("OfficeEmployee constructor") End Sub 

Notice that you are not using the Overrides keyword, nor did you mark the method in Person as Overridable. These keywords have no use in this context and, in fact, will cause syntax errors if you attempt to use them on constructor methods.

When you run the application now, you’d probably expect that only the implementation of New in OfficeEmployee would be invoked. Certainly, that is what would occur with a normal overridden method. Of course, New isn’t overridden, so when you run the application, both implementations are run. Both strings are output to the output window in the IDE.

Note that the implementation in the Person class ran first, followed by the implementation in the OfficeEmployeeclass. This occurs because, as an object is created, all the constructors for the classes in the inheritance chain are invoked, starting with the base class and working out through all the subclasses one by one. In fact, if you implement a New method in the Employeeclass, you can see that it too is invoked:

  Public Sub New()   Debug.WriteLine("Employee constructor") End Sub 

When the application is run and the button clicked, you’ll see all three strings in the output window. All three constructor methods were invoked, starting with the Person class and working down to the OfficeEmployee class.

Constructors in More Depth

The rules governing constructors without parameters are pretty straightforward, but things get a bit more interesting if you start requiring parameters on your constructors.

To understand what is going on, you need to consider how even your simple constructors are being invoked. While you see them as being invoked from the base class down through all subclasses to your final subclass, what is really happening is a bit different.

In particular, it is the subclass New method that is invoked first. However, Visual Basic is automatically inserting a line of code into your routine at compile time. For instance, in your OfficeEmployee class you have a constructor:

 Public Sub New()   Debug.WriteLine("OfficeEmployee constructor") End Sub

Behind the scenes, Visual Basic inserts what is effectively a call to the constructor of your parent class on your behalf. You could do this manually by using the MyBase keyword with the following change:

 Public Sub New()   MyBase.New()   Debug.WriteLine("OfficeEmployee constructor") End Sub

This call must be the first line in your constructor. If you put any other code before this line, you’ll get a syntax error indicating that your code is invalid. Because the call is always required, and because it always must be the first line in any constructor, Visual Basic simply inserts it for you automatically.

It is also worth noting that if you don’t explicitly provide a constructor on a class by implementing a New method, Visual Basic creates one for you behind the scenes. The automatically created method simply has one line of code:

 MyBase.New()

All classes have constructor methods, either created explicitly by you as you write a New method or created implicitly by Visual Basic as the class is compiled.

Tip 

A constructor method is sometimes called a ctor, short for constructor. This term is often used by tools such as ILDASM or .NET Reflector.

By always calling MyBase.New() as the first line in every constructor, you are guaranteed that it is the implementation of New in your top-level base class that actually runs first. Every subclass invokes the parent class implementation all the way up the inheritance chain until only the base class remains. Then its code runs, followed by each individual subclass, as you’ve already seen.

Constructors with Parameters

This works great when your constructors don’t require parameters, but if your constructor does require a parameter, then it becomes impossible for Visual Basic to automatically make that call on your behalf. After all, how would Visual Basic know what values you want to pass as parameters?

To see how this works, let’s change the New method in the Person class to require a name parameter. You can use that parameter to initialize the object’s Name property:

 Public Sub New(ByVal name As String)   Me.Name = name   Debug.WriteLine("Person constructor") End Sub

Now your constructor requires a String parameter and uses it to initialize the Name property.

You are using the Me keyword to make your code easier to read. Interestingly enough, the compiler will actually understand and correctly compile the following code:

 Name = name

However, that is not at all clear to a developer reading the code. By prefixing the property name with the Me keyword, you’ve made it clear that you’re invoking a property on the object and providing it with the parameter value.

At this point, your application won’t compile because there is an error in the New method of the Employee class. In particular, Visual Basic’s attempt to automatically invoke the constructor on the Person class fails because it has no idea what data value to pass for this new name parameter. There are three ways you can address this error:

  • Make the name parameter Optional.

  • Overload the New method with another implementation that requires no parameter.

  • Manually provide the Name parameter value from within the Employee class.

If you make the Name parameter Optional, you’re indicating that the New method can be called with or without a parameter. This means that one viable option is to call the method with no parameters, so Visual Basic’s default of calling it with no parameters works just fine.

If you overload the New method, you can implement a second New method that doesn’t accept any parameters, again allowing Visual Basic’s default behavior to work as you’ve seen. Keep in mind that this solution only invokes the overloaded version of New with no parameter; the version that requires a parameter would not be invoked.

The final way you can fix the error is by simply providing a parameter value yourself from within the New method of the Employee class. To do this, change the Employee class:

 Public Sub New()   MyBase.New("George")   Debug.WriteLine("Employee constructor") End Sub

By explicitly calling the New method of the parent class, you are able to provide it with the required parameter value. At this point, your application will compile, but it won’t run.

Constructors, Overloading, and Variable Initialization

What isn’t clear from this code is that you’ve now introduced a very insidious bug. The constructor in the Person class is using the Name property to set the value:

 Public Sub New(ByVal name As String)   Me.Name = name   Debug.WriteLine("Person constructor") End Sub

However, the Name property is overridden by the Employee class, so it is that implementation that will be run. Unfortunately, that implementation makes use of a Dictionary object, which isn’t available yet! It turns out that any member variables declared in a class with the New statement, such as the Dictionary object in Employee won’t be initialized until after the constructor for that class has completed:

 Private mNames As New Generic.Dictionary(Of NameTypes, String)

Because you are still in the constructor for Person, there’s no way the constructor for Employee can be complete. To resolve this, you need to change the Employee class a bit so that it doesn’t rely on the Dictionary being created in this manner. Instead, you’ll add code to create it when needed.

First, change the declaration of the variable in the Employee class:

  Private mNames As Generic.Dictionary(Of NameTypes, String) 

Then, update the Name property so that it creates the Hashtable object if needed:

 Public Overloads Property Name(ByVal type As NameTypes) As String   Get     If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameTypes, String)     Return mNames(type)   End Get   Set(ByVal value As String)     If mNames Is Nothing Then mNames = New Generic.Dictionary(Of NameTypes, String)     If mNames.ContainsKey(type) Then       mNames.Item(type) = value     Else       mNames.Add(type, value)     End If     If type = NameTypes.Normal Then       MyBase.Name = value     End If   End Set End Property

This ensures that a Dictionary object is created in the Employee class code even though its constructor hasn’t yet completed.

More Constructors with Parameters

Obviously, you probably don’t want to hard-code a value in a constructor as you did in the Employee class, so you may choose instead to change this constructor to also accept a name parameter. Change the Employee class constructor as shown:

 Public Sub New(ByVal name As String)   MyBase.New(name)   Debug.WriteLine("Employee constructor") End Sub

Of course, this just pushed the issue deeper, and now you’ll find that the OfficeEmployee class has a compile error in its New method. Again, you can fix the problem by having that method accept a parameter so that it can provide it up the chain as required. Make the following change to OfficeEmployee:

 Public Sub New(ByVal name As String)   MyBase.New(name)   Debug.WriteLine("OfficeEmployee constructor") End Sub

Finally, the code in the form is no longer valid. You’re attempting to create an instance of OfficeEmployee without passing a parameter value. Let’s update that code and then you can run the application:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click      Dim person As Employee = New OfficeEmployee("Mary")      With person       '.Name = "Fred" 

You’re passing a name value to the constructor of OfficeEmployee. In addition, you’ve commented out the line of code that sets the Name property directly - meaning that the value passed in the constructor will be displayed in the form.

The Protected Scope

You’ve seen how a subclass automatically gains all the Public methods and properties that compose the interface of the base class. This is also true of Friend methods and properties; they are inherited as well and are available only to other code in the same project as the subclass.

Private methods and properties are not exposed as part of the interface of the subclass, meaning that the code in the subclass cannot call those methods, nor can any code using your objects. These methods are only available to the code within the base class itself. This can get confusing, as the implementations contained in the Private methods are inherited and are used by any code in the base class; it is just that they aren’t available to be called by any other code, including code in the subclass.

There are times when you want to create methods in your base class that can be called by a subclass as well as the base class but not by code outside of those classes. Basically, you want a hybrid between Public and Private - methods that are private to the classes in the inheritance chain but are usable by any subclasses that might be created within the chain. This functionality is provided by the Protected scope.

Protected methods are very similar to Private methods in that they are not available to any code that calls your objects. Instead, these methods are available to code within the base class and to code within any subclass. The following table lists all the available scope options:

Open table as spreadsheet

Scope

Description

Private

Available only to code within your class

Protected

Available only to classes that inherit from your class

Friend

Available only to code within your project/component

Protected Friend

Available to classes that inherit from your class (in any project) and to code within your project/component. This is a combination of Protected and Friend.

Public

Available to code outside your class

The Protected scope can be applied to Sub, Function, and Property methods. To see how the Protected scope works, let’s add an Identity field to the Person class:

 Public Class Person   Private mName As String   Private mBirthDate As String   Private mID As String   Protected Property Identity() As String     Get       Return mID     End Get     Set(ByVal value As String)       mID = value     End Set   End Property 

This data field represents some arbitrary identification number or value assigned to a person. This might be a social security number, an employee number, or whatever is appropriate.

The interesting thing about this value is that it is not currently accessible outside your inheritance chain. For instance, if you try to use it from your code in the form, you’ll discover that there is no Identity property on your Person, Employee, or OfficeEmployee objects.

However, there is an Identity property now available inside your inheritance chain. The Identity property is available to the code in the Person class just like any other method. Interestingly, even though Identity is not available to the code in your form, it is available to the code in the Employee and OfficeEmployee classes, because they are both subclasses of Person. Employee is directly a subclass, and OfficeEmployee is indirectly a subclass of Person because it is a subclass of Employee.

Thus, you can enhance your Employee class to implement an EmployeeNumber property by using the Identity property. To do this, add the following code to the Employee class:

  Public Property EmployeeNumber() As Integer   Get     Return CInt(Identity)   End Get   Set(ByVal value As Integer)     Identity = CStr(value)   End Set End Property 

This new property exposes a numeric identity value for the employee, but it uses the internal Identity property to manage that value. You can override and shadow Protected elements just as you do with elements of any other scope.

Protected Variables

Up to this point, we’ve focused on methods and properties and how they interact through inheritance. Inheritance, and, in particular, the Protected scope, also has an impact on instance variables and how you work with them.

Though it is not recommended, you can declare variables in a class using Public scope. This makes the variable directly available to code both within and outside of your class, allowing any code that interacts with your objects to directly read or alter the value of that variable.

Variables can also have Friend scope, which likewise allows any code in your class or anywhere within your project to read or alter the value directly. This is also generally not recommended because it breaks encapsulation.

Important 

Rather than declare variables with Public or Friend scope, it is better to expose the value using a Property method so that you can apply any of your business rules to control how the value is altered as appropriate.

Of course, you know that variables can be of Private scope, and this is typically the case. This makes the variables accessible only to the code within your class, and it is the most restrictive scope.

As with methods, however, you can also use the Protected scope when declaring variables. This makes the variable accessible to the code in your class and to the code in any class that derives from your class - all the way down the hierarchy chain.

Sometimes this is useful, because it enables you to provide and accept data to and from subclasses, but to act on that data from code in the base class. At the same time, exposing variables to subclasses is typically not ideal, and you should use Property methods with Protected scope for this instead, as they allow your base class to enforce any business rules that are appropriate for the value, rather than just hope that the author of the subclass only provides good values.

Events and Inheritance

So far, we’ve discussed methods, properties, and variables in terms of inheritance - how they can be added, overridden, extended, and shadowed. In Visual Basic, events are also part of the interface of an object, and they are affected by inheritance as well.

Inheriting Events

Chapter 3 discusses how to declare, raise, and receive events from objects. You can add such an event to the Person class by declaring it at the top of the class:

 Public Class Person   Private mName As String   Private mBirthDate As String   Private mID As String   Public Event NameChanged(ByVal newName As String) 

Then, you can raise this event within the class anytime the person’s name is changed:

 Public Overridable Property Name() As String   Get     Return mName   End Get   Set(ByVal value As String)     mName = value     RaiseEvent NameChanged(mName)   End Set End Property

At this point, you can receive and handle this event within your form anytime you’re working with a Person object. The nice thing about this is that your events are inherited automatically by subclasses - meaning that your Employee and OfficeEmployee objects will also raise this event. Thus, you can change the code in your form to handle the event, even though you’re working with an object of type OfficeEmployee.

First, you can add a method to handle the event to Form1:

  Private Sub OnNameChanged(ByVal newName As String)   MsgBox("New name: " & newName) End Sub 

Note that you’re not using the Handles clause here. In this case, for simplicity, you use the AddHandler method to dynamically link the event to this method. However, you could have also chosen to use the WithEvents and Handles keywords, as described in Chapter 3 - either way works.

With the handler built, you can use the AddHandler method to link this method to the event on the object:

 Private Sub btnOK_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnOK.Click   Dim person As Employee = New OfficeEmployee("Mary")   AddHandler person.NameChanged, AddressOf OnNameChanged   With pPerson     .Name = "Fred" 

Also note that you’re uncommenting the line that changes the Name property. With this change, you know that the event should fire when the name is changed.

When you run the application now, you’ll see a message box, indicating that the name has changed and proving that the NameChanged event really is exposed and available even though your object is of type OfficeEmployee, rather than Person.

Raising Events from Subclasses

One caveat you need to keep in mind is that while a subclass exposes the events of its base class, the code in the subclass cannot raise the event. In other words, you cannot use the RaiseEvent method in Employee or OfficeEmployee to raise the NameChanged event. Only code directly in the Person class can raise the event.

To see this in action, let’s add another event to the Person class, an event that can indicate the change of other arbitrary data values:

 Public Class Person   Private mName As String   Private mBirthDate As String   Private mID As String   Public Event NameChanged(ByVal newName As String)   Public Event DataChanged(ByVal field As String, ByVal newValue As Object) 

You can then raise this event when the BirthDate is changed:

 Public Overridable Property BirthDate() As Date   Get     Return mBirthDate   End Get   Set(ByVal value As Date)     mBirthDate = value     RaiseEvent DataChanged("BirthDate", value)   End Set End Property

It would also be nice to raise this event from the Employee class when the Salary value is changed. Unfortunately, you can’t use the RaiseEvent method to raise the event from a base class, so the following code won’t work (don’t enter this code):

 Public Property Salary() As Double   Get     Return mSalary   End Get   Set(ByVal value As Double)     mSalary = value     RaiseEvent DataChanged("Salary", value)   End Set End Property

Fortunately, there is a relatively easy way to get around this limitation. You can simply implement a Protected method in your base class that allows any derived class to raise the method. In the Person class, you can add such a method:

  Protected Sub OnDataChanged(ByVal field As String, _     ByVal newValue As Object)   RaiseEvent DataChanged(field, newValue) End Sub 

You can use this method from within the Employee class to indicate that Salary has changed:

 Public Property Salary() As Double   Get     Return mSalary   End Get   Set(ByVal value As Double)     mSalary = value     OnDataChanged("Salary", value)   End Set End Property

Notice that the code in Employee is not raising the event, it is simply calling a Protected method in Person. It is the code in the Person class that actually raises the event, meaning everything will work as desired.

You can enhance the code in Form1 to receive the event. First, create a method to handle the event:

  Private Sub OnDataChanged(ByVal field As String, ByVal newValue As Object)   MsgBox("New " & field & ": " & CStr(newValue)) End Sub 

Then, link this handler to the event using the AddHandler method:

 Private Sub btnOK_Click(ByVal sender As System.Object,_     ByVal e As System.EventArgs)Handles btnOK.Click   Dim person As Employee =New OfficeEmployee("Mary ")   AddHandler person.NameChanged,AddressOf OnNameChanged   AddHandler person.DataChanged,AddressOf OnDataChanged  

Finally, make sure you are changing and displaying the Salary property:

 With person   .Name = "Fred"   .Name(NameTypes.Informal) = "Freddy"   .BirthDate = #1/1/1960#   .Age = 20   .Salary = 30000   txtName.Text = .ToString()   txtBirthDate.Text = Format(.BirthDate, "Short date")   txtAge.Text = CStr(.Age)   txtSalary.Text = Format(.Salary, "0.00") End With

When you run the application and click the button now, you’ll get message boxes displaying the changes to the Name property, the BirthDate property (twice, once for the BirthDate property and once for the Age property, which changes the birth date), and the Salary property.

Shared Methods

Chapter 3 explored shared methods and how they work: providing a set of methods that can be invoked directly from the class, rather than requiring that you create an actual object.

Shared methods are inherited just like instance methods and so are automatically available as methods on subclasses, just as they are on the base class. If you implement a shared method in BaseClass, you can call that method using any class derived from BaseClass.

Like regular methods, shared methods can be overloaded and shadowed. They cannot, however, be overridden. If you attempt to use the Overridable keyword when declaring a Shared method, you will get a syntax error. For instance, you can implement a method in your Person class to compare two Person objects:

  Public Shared Function Compare(ByVal person1 As Person, _     ByVal person2 As Person) As Boolean   Return (person1.Name = person2.Name) End Function 

To test this method, let’s add another button to the form, name it btnCompare, and set its Text value to Compare. Double-click the button to bring up the code window and enter the following:

 Private Sub btnCompare_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnCompare.Click   Dim emp1 As New Employee("Fred")   Dim emp2 As New Employee("Mary")   MsgBox(Employee.Compare(emp1, emp2)) End Sub

This code simply creates two Employee objects and compares them. Note, though, that the code uses the Employee class to invoke the Compare() method, displaying the result in a message box. This establishes that the Compare() method implemented in the Person class is inherited by the Employee class, as you’d expect.

Overloading Shared Methods

Shared methods can be overloaded using the Overloads keyword in the same manner as you overload an instance method. This means that your subclass can add new implementations of the shared method as long as the parameter list differs from the original implementation.

For example, you can add a new implementation of the Compare method to Employee:

  Public Overloads Shared Function Compare(ByVal employee1 As Employee, _     ByVal employee2 As Employee) As Boolean   Return (employee1.EmployeeNumber = employee2.EmployeeNumber) End Function 

This new implementation compares two Employee objects, rather than two Person objects, and in fact compares them by employee number, rather than name.

You can enhance the code behind btnCompare in the form to set the EmployeeNumber properties:

 Private Sub btnCompare_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnCompare.Click   Dim emp1 As New Employee("Fred")   Dim emp2 As New Employee("Mary")   emp1.EmployeeNumber = 1   emp2.EmployeeNumber = 1   MsgBox(Employee.Compare(emp1, emp2)) End Sub

While it might make little sense for these two objects to have the same EmployeeNumber value, it will prove a point. When you run the application now, even though the Name values of the objects are different, your Compare() routine will return True, proving that you’re invoking the overloaded version of the method that expects two Employee objects as parameters.

The overloaded implementation is available on the Employee class or any classes derived from Employee, such as OfficeEmployee. The overloaded implementation is not available if called directly from Person, as that class only contains the original implementation.

Shadowing Shared Methods

Shared methods can also be shadowed by a subclass. This allows you to do some very interesting things, including converting a shared method into an instance method or vice versa. You can even leave the method as shared but change the entire way it works and is declared. In short, just as with instance methods, you can use the Shadows keyword to entirely replace and change a shared method in a subclass.

To see how this works, use the Shadows keyword to change the nature of the Compare() method in OfficeEmployee:

  Public Shared Shadows Function Compare(ByVal person1 As Person, _     ByVal person2 As Person) As Boolean        Return (person1.Age = person2.Age)    End Function 

Notice that this method has the same signature as the original Compare() method you implemented in the Person class, but instead of comparing by name, here you’re comparing by age. With a normal method you could have done this by overriding, but Shared methods can’t be overridden, so the only thing you can do is shadow it.

Of course, the shadowed implementation is only available via the OfficeEmployee class. Neither the Person nor Employee classes, which are higher up the inheritance chain, are aware that this shadowed version of the method exists.

To use this from your Form1 code, you can change the code for btnCompare as follows:

 Private Sub btnCompare_Click(ByVal sender As System.Object, _     ByVal e As System.EventArgs) Handles btnCompare.Click   Dim emp1 As New Employee("Fred")   Dim emp2 As New Employee("Mary")   emp1.Age = 20   emp2.Age = 25   MsgBox(OfficeEmployee.Compare(emp1, emp2)) End Sub

Instead of setting the EmployeeNumber values, you’re now setting the Age values on your objects. More important, notice that you’re now calling the Compare() method via the OfficeEmployee class, rather than via Employee or Person. This causes the invocation of the new version of the method, and the ages of the objects are compared.

Shared Events

As discussed in Chapter 3, you can create shared events, events that can be raised by shared or instance methods in a class, whereas regular events can only be raised from within instance methods.

When you inherit from a class that defines a shared event, your new subclass automatically gains that event just as it does with regular events, as discussed earlier in this chapter.

As with instance events, a shared event cannot be raised by code within the subclass; it can only be raised using the RaiseEvent keyword from code in the class where the event is declared. If you want to be able to raise the event from methods in your subclass, you need to implement a Protected method on the base class that actually makes the call to RaiseEvent.

This is no different from what was discussed earlier in the chapter, other than that with a shared event you can use a method with Protected scope that is marked as shared to raise the event, rather than use an instance method.

Creating an Abstract Base Class

So far, you’ve seen how to inherit from a class, how to overload and override methods, and how virtual methods work. In all of the examples so far, the parent classes have been useful in their own right and could be instantiated and do some meaningful work. Sometimes, however, you want to create a class such that it can only be used as a base class for inheritance.

MustInherit Keyword

The current Person class is being used as a base class, but it can also be instantiated directly to create an object of type Person. Likewise, the Employee class is also being used as a base class for the OfficeEmployee class you created that derives from it.

If you want to make a class act only as a base class, you can use the MustInherit keyword, thereby preventing anyone from creating objects based directly on the class and requiring them instead to create a subclass and then create objects based on that subclass.

This can be very useful when you are creating object models of real-world concepts and entities. You’ll look at ways to leverage this capability later in this chapter. You can change Person to use the MustInherit keyword:

  Public MustInherit Class Person 

This has no effect on the code within Person or any of the classes that inherit from it, but it does mean that no code can instantiate objects directly from the Person class; instead, you can only create objects based on Employee or OfficeEmployee.

This doesn’t prevent you from declaring variables of type Person, it merely prevents you from creating an object by using New Person(). You can also continue to make use of Shared methods from the Person class without any difficulty.

MustOverride Keyword

Another option you have is to create a method (Sub, Function, or Property) that must be overridden by a subclass. You might want to do this when you are creating a base class that provides some behaviors but relies on subclasses to also provide other behaviors in order to function properly. This is accomplished by using the MustOverride keyword on a method declaration.

If a class contains any methods marked with MustOverride, the class itself must also be declared with the MustInherit keyword or you’ll get a syntax error:

  Public MustInherit Class Person 

This makes sense. If you’re requiring that a method be overridden in a subclass, it only stands to reason that your class can’t be directly instantiated; it must be subclassed to be useful.

Let’s see how this works by adding a LifeExpectancy method in Person that has no implementation and must be overridden by a subclass:

  Public MustOverride Function LifeExpectancy() As Integer 

Notice that there is no End Function or any other code associated with the method. When using MustOverride, you cannot provide any implementation for the method in your class. Such a method is called an abstract method or pure virtual function, as it only defines the interface, and no implementation. Methods declared in this manner must be overridden in any subclass that inherits from your base class.

If you don’t override one of these methods, you’ll generate a syntax error in the subclass, and it won’t compile. You need to alter the Employee class to provide an implementation for this method:

  Public Overrides Function LifeExpectancy() As Integer   Return 90 End Function 

Your application will compile and run at this point because you are now overriding the LifeExpectancy() method in Employee, so the required condition is met.

Abstract Base Classes

You can combine these two concepts, using both MustInherit and MustOverride, to create something called an abstract base class, sometimes referred to as a virtual class. This is a class that provides no implementation, only the interface definitions from which a subclass can be created, as shown in the following example:

  Public MustInherit Class AbstractBaseClass   Public MustOverride Sub DoSomething()   Public MustOverride Sub DoOtherStuff() End Class 

This technique can be very useful when creating frameworks or the high-level conceptual elements of a system. Any class that inherits AbstractBaseClass must implement both DoSomething() and DoOtherStuff() or a syntax error will result.

In some ways, an abstract base class is comparable to defining an interface using the Interface keyword. The Interface keyword is discussed in detail later in this chapter. You could define the same interface shown in this example with the following code:

  Public Interface IAbstractBaseClass   Sub DoSomething()   Sub DoOtherStuff() End Interface 

Any class that implements the IAbstractBaseClass interface must implement both DoSomething and DoOtherStuff or a syntax error will result, and in that regard this technique is similar to an abstract base class.

Preventing Inheritance

If you want to prevent a class from being used as a base class, you can use the NotInheritable keyword. For instance, you can change your OfficeEmployee as follows:

  Public NotInheritable Class OfficeEmployee 

At this point, it is no longer possible to inherit from this class to create a new class. Your OfficeEmployee class is now sealed, meaning it cannot be used as a base from which to create other classes.

If you attempt to inherit from OfficeEmployee, you’ll get a compile error indicating that it cannot be used as a base class. This has no effect on Person or Employee; you can continue to derive other classes from them.

Typically, you want to design your classes so that they can be subclassed, because that provides the greatest long-term flexibility in the overall design. Sometimes, however, you want to ensure that your class cannot be used as a base class, and the NotInheritable keyword addresses that issue.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net