Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual Basic 2005 Express Edition - From Novice To Professional (2006)

.pdf
Скачиваний:
386
Добавлен:
17.08.2013
Размер:
21.25 Mб
Скачать

130

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

That single tiny change sets up the OffRoader class to inherit from the Car class. So, now the OffRoader can do everything the Car can. Drop back into the Module1.vb source and add some code to the Main() function to show this in action:

Module Module1

Sub Main()

Dim myJeep As New OffRoader() myJeep.Drive()

myJeep.Brake()

Console.ReadLine()

End Sub

End Module

Run the program now and you’ll see that even though you haven’t added any real code to the OffRoader class, it will work just as if it were a Car class (in that you can call the Drive() and Brake() methods). See Figure 5-3.

Figure 5-3. Inheritance lets you instantly “inherit” functionality into a new class (called the subclass).

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

131

Of course at this point it would be easy to add functionality to the OffRoader class, perhaps a DriveOffRoad() method, but that doesn’t really show you much.

Something else that inheritance lets you do is treat one type of object as another. For example, even though OffRoader may have additional methods and functionality over a car, it is still a Car. Therefore, OffRoader objects can be treated as Car objects in your application.

Go ahead and make a change to the Main() function to show this:

Module Module1

Sub Main()

Dim myJeep As New OffRoader()

Dim aCar As Car aCar = myJeep

aCar.Drive()

aCar.Brake()

Console.ReadLine()

End Sub

End Module

If you run the application again at this point, you’ll find it works exactly the same as it did before; the OffRoader object is now being treated as a standard Car.

Virtual Methods

Inheritance on its own provides you with a means to make one class inherit the functionality of another. Usually, after doing that, you’ll add more functionality to specialize the subclass. However, you can also override the functionality in the base class, effectively replacing the way that even that inherited functionality works. Why on earth would you want to do this? Why not just change the base class?

132

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

Well, that’s certainly an option. But, if you have a bunch of code already relying on the functionality in the base class, perhaps also some other classes inheriting from it, the last thing you want to do is to take the risk that changing the base class would introduce unwanted behavior in the rest of the application. It’s much safer in those specialized cases to create a subclass that overrides the functionality you need to change. Another example of why you’d want this of course is when you want to change the way a class works, but you don’t have access to the source code of the original class. This is always the case when you want to change the way that a class in the .NET Framework works, unless of course you work for Microsoft.

The solution is virtual methods. Making a method virtual involves nothing more than putting the word Overridable in front of its name. After that’s done, subclasses can override the virtual method with the Overrides keyword. Let’s extend that car example to see it in action.

Try It Out: Virtual Methods

Load up the example you were working on earlier, and change the Car class’s Drive() method as shown:

Public Class Car

Public Sub New()

End Sub

Public Overridable Sub Drive()

Console.WriteLine("We're now whizzing down the road")

End Sub

Public Sub Brake()

Console.WriteLine("The car comes to a screeching stop")

End Sub

End Class

By adding Overridable to the method definition in the base class, you’ve told Visual Basic that this method can be overridden in a subclass. The only subclass you have is the OffRoader class, so bring up its source. You’ll add a Drive() method to this class that overrides the virtual method in the base class.

In the class type, beneath the constructor, type in Public Overrides and then press the spacebar. IntelliSense pops up, asking you which method you want to override, as you can see in Figure 5-4.

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

133

Figure 5-4. Typing Public Overrides followed by a space causes IntelliSense to pop up, asking you what you want to override.

Double-click Drive() from the list (or use the arrow keys and then press Enter on the selected one), and VB Express will fill in the rest of the method signature and put in some code for you:

Public Overrides Sub Drive()

MyBase.Drive()

End Sub

The new method includes a call to MyBase.Drive(). This means that unless you remove that line and put in some code of your own, the OffRoader.Drive() method will automatically use the Drive() method from the base Car class.

Remove that line and change the method:

Public Overrides Sub Drive()

Console.WriteLine("You zoom down a rocky ravine!")

End Sub

Finally, just to tidy up the code a little, change the Main() subroutine (in Module1.vb) to look like this:

Module Module1

Sub Main()

Dim aCar As Car = New OffRoader() aCar.Drive()

aCar.Brake()

Console.ReadLine()

End Sub

End Module

134

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

Once again you have a Car object variable that’s getting an OffRoader object stored in it. Run the program now and you’ll find that, even though you are dealing with a variable that references Car objects, it’s the OffRoader object’s Drive() method that gets called at runtime (see Figure 5-5). Handy, isn’t it.

Figure 5-5. Even though you are using a Car object variable, you call the actual object’s Drive() method—OffRoader.Drive()—at runtime, thanks to virtual methods and overriding.

Virtual methods are particularly handy when writing functions. As you’ve seen, by putting a specific instance of a class into an object variable that references the base class, you can treat all subclasses as a general base class. This makes it easy to write methods that take base class parameters (that is, a Car object as a parameter) and then just work, even if someone (including you) passes in an instance of a specific subclass.

Abstract Methods and Classes

Virtual methods are great when you think there is a chance that someone might want to override your code in a subclass. There are occasions, though, when you not only want someone to override your methods, but also want to prevent someone from instantiating a base class on its own. That’s where abstract methods and classes come into play. To use the car analogy once again, you can’t walk into a car dealership and say “I want a car” and expect instant satisfaction. Car is a generic base class that really means very little. Most people actually want a specific instance of a subclass of Car.

Abstract methods have no code in them. When you declare a method as MustOverride, you are effectively saying, “This method must be implemented by a subclass.”

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

135

You declare an abstract class with the MustInherit keyword, and you declare an abstract method with the MustOverride keyword. For this reason, a lot of VB programmers call these things MustInherit classes and MustOverride methods. The rest of the world, though, calls them abstract, so if you plan on speaking to a Java, C#, or C++ programmer in your future, it’s a good idea to learn the terminology they use.

When you think about it, a class with a method that can’t actually do anything until it is subclassed and overwritten, is itself somewhat useless. Therefore, if you declare an abstract (MustOverride) method in a class, you also need to declare the class as MustInherit. Here’s an example of what that looks like:

Public MustInherit Class GameGraphic

Public MustOverride Sub Draw()

End Class

This tiny class could represent the very simplest foundation for a graphic in a game. Until it’s subclassed into something more specific (for example, PlayerGraphic or

AlienInvaderGraphic), it’s useless. By using MustInherit and MustOverride, you force whoever subclasses GameGraphic to implement the Draw() method. If the subclass doesn’t actually include a Draw() method, the compiler will throw an error. Also, just as when using virtual methods, there is nothing stopping you from putting methods with code into the base abstract class. This would let you provide a base class with functionality to do common tasks, but still require a subclass to implement a key function before the class can be used.

Take a look at an example before you move on.

NOTINHERITABLE CLASSES

Before you leave the world of special classes, there is one more kind that you should know about but that doesn’t really warrant an example: NotInheritable classes.

Just as you can create an abstract class by placing the keyword MustInherit in front of the class definition, you can create a NotInheritable class by using the word NotInheritable in the same way that you would MustOverride.

NotInheritable classes are the polar opposite of abstract ones. Whereas an abstract class cannot be turned into an object and must be subclassed, NotInheritable classes cannot be subclassed and must

be turned into objects. Use NotInheritable classes when you absolutely never want someone to subclass. A good example of this in the .NET Framework itself is the System.String class. System.String is marked as NotInheritable because the developers of the Framework did not want people overriding functionality in the String data type. This makes sense because System.String is used so much that it would be silly to override it, and potentially break the way it works and all code that relies on it.

136

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

Try It Out: Declaring and Using Abstract Classes

Start up a new console application and as before add a Car.vb class to the project. When the code editor appears, type in this for the Car class:

Public MustInherit Class Car

Public MustOverride Sub Describe()

End Class

You won’t add all the other functionality here, so consider this a car in a brochure. All cars in brochures have just one function, and that is to describe themselves.

Now, let’s start to add another class, called OffRoader. Just type in the highlighted lines directly underneath your Car class (sure, you could add another class file, but for this example it’s handy to keep everything in one place).

Public MustInherit Class Car

Public MustOverride Sub Describe()

End Class

Public Class OffRoader

Inherits Car

As soon as you hit the Enter key on the end of the Inherits line, VB Express automatically sees what you are trying to do and lends a helping hand, adding code to your class so that it looks like this:

Public Class OffRoader

Inherits Car

Public Overrides Sub Describe()

End Sub

End Class

You are subclassing a MustInherit class with a MustOverride method, so Visual Basic intelligently shows you the work you need to do. Add the highlighted code to this new class, and then add a third class, Sedan, underneath, like this:

Public MustInherit Class Car

Public MustOverride Sub Describe()

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

137

End Class

Public Class OffRoader

Inherits Car

Public Overrides Sub Describe()

Console.WriteLine("Big, with big wheels and a big engine")

End Sub

End Class

Public Class Sedan

Inherits Car

Public Overrides Sub Describe()

Console.WriteLine("Just your average kid taxi")

End Sub

End Class

Once again, as soon as you type in Inherits Car for the Sedan class, VB jumps in and adds a stub of the methods that you need to code.

Back in the Module1.vb source, add a new method and some code to Main() to bring the program to life:

Module Module1

Sub Main()

Dim jeep As New OffRoader()

Dim ford As New Sedan()

DescribeCar(jeep)

DescribeCar(ford)

Console.ReadLine()

End Sub

Public Sub DescribeCar(ByVal theCar As Car) theCar.Describe()

End Sub

End Module

138

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

First, you’ve added a new method called DescribeCar(). This takes a Car object as a parameter and then calls its Describe() method.

DescribeCar() takes a Car object as a parameter, but Car is an abstract class and can’t actually be turned into an object. So, what’s happening here? Well, you can still use abstract classes as parameters. What you’re saying is that the parameter passed to the method will be a subclass of Car, and as such can do anything Car can do. Because DescribeCar() is an abstract method, you know that any subclass of Car must provide a full implementation of that method, as the classes OffRoader and Sedan do.

Run the program now and you’ll see output like that in Figure 5-6.

Figure 5-6. OffRoader and Sedan can be passed to DescribeCar() because they both inherit from Car and implement all the abstract methods as required.

Beware Shadows

There’s a special keyword in Visual Basic 2005 that I’m not a big fan of: Shadows. Shadows looks, on the surface anyway, to do exactly the same thing as Overrides does, but beneath the surface it does strange and sinister things.

C H A P T E R 5 M O R E - A D V A N C E D O B J E C T O R I E N T A T I O N

139

The idea behind Shadows is that the designers of Visual Basic 2005 wanted to give you a way to “hide” functionality. You can use Shadows to not only replace a base class’s method, but also to remove it completely, and even replace instance variables within the class. For example, take a look at this code:

Public Class Car

Public Sub Describe()

Console.WriteLine("I am a generic car")

End Sub

End Class

Public Class OffRoader

Inherits Car

Private Shadows Sub Describe()

Console.WriteLine("I am an offroader")

End Sub

End Class

It looks innocent enough, doesn’t it. We have a base Car class, and an OffRoader class that inherits from it. Now, look at the Describe() sub in the OffRoader class. Notice anything strange?

Private Shadows Sub Describe()

Describe() in the Car class is Public. If you have a Car object, you can call Describe() on it. Describe() in the OffRoader class, on the other hand, is Private. What this means is that if you have an OffRoader object, you can’t call Describe() on it, because it’s now Private. Even worse, if you treat an OffRoader object as a Car, you can call Describe() on it—like this:

Public myOffRoader As Car = New OffRoader() myOffRoader.Describe()

Confusing, isn’t it? Here we have an OffRoader object that used Shadows to hide the Describe() method, but Describe() can still be called if you treat the object as an instance of Car–in that case, it’s the Car.Describe() method that gets called.