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

Beginning Visual Basic 2005 (2006)

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

Chapter 10

How It Works

The new constructor that you added to SportsCar runs after the existing one in Car. .NET knows that it’s supposed to run the code in the constructor of the base class before running the new constructor in the class that inherits from it, so in effect it runs this code first:

‘Constructor Public Sub New()

‘Set the default values Color = “White”

_speed = 0 _numberOfDoors = 5

End Sub

And then it runs this code:

‘Constructor Public Sub New()

‘Change the default values Color = “Green” NumberOfDoors = 2

End Sub

To summarize what happens:

1.

2.

3.

4.

5.

6.

7.

The constructor on the base class Car is called.

Color is set to White.

_speed is set to 0.

_numberOfDoors is set to 5.

The constructor on the new class SportsCar is called.

Color is set to Green.

NumberOfDoors is set to 2.

Because you defined _numberOfDoors as a private member in Car, you cannot directly access it from inherited classes, just as you wouldn’t be able to access it directly from a consumer of the class. Instead, you rely on being able to set an appropriate value through the NumberOfDoors property.

Polymorphism: Scary Word, Simple Concept

Another very common word mentioned when talking about object-oriented programming is polymorphism. This is, perhaps the scariest term, but one of the easiest to understand! In fact, you have already done it in the previous example.

Look again at the code for DisplayCarDetails:

‘DisplayCarDetails - procedure that displays a car’s details Sub DisplayCarDetails(ByVal theCar As Car)

‘Display the details of the car

336

Building Objects

Console.WriteLine(“Color: “ & theCar.Color)

Console.WriteLine(“Number of doors: “ & theCar.NumberOfDoors)

Console.WriteLine(“Current speed: “ & theCar.Speed)

End Sub

The first line says that the parameter you want to accept is a Car object. But when you call the object, you’re actually passing it a SportsCar object.

Look at how you create the object and call DisplayCarDetails:

‘Create a new sportscar object Dim objCar As New SportsCar

‘Display the details of the car DisplayCarDetails(objCar)

How can it be that if the function takes a Car object, you’re allowed to pass it as a SportsCar object?

Well, polymorphism (which comes from the Greek for “many forms”) means that an object can be treated as if it were a different kind of object, provided common sense prevails. In this case, you can treat a SportsCar object like a Car object because SportsCar inherits from Car. This act of inheritance dictates that what a SportsCar object can do must include everything that a Car object can do; therefore, you can treat the two objects in the same way. If you need to call a method on Car, SportsCar must also implement the method.

This does not hold true the other way round. Your DisplaySportsCarDetails function, defined like this:

Sub DisplaySportsCarDetails(ByVal theCar As SportsCar)

cannot accept a Car object. Car is not guaranteed to be able to do everything a SportsCar can do, because the extra methods and properties you add to SportsCar won’t exist on Car. SportsCar is a more specific type of Car.

So, to summarize, when people talk about polymorphism, this is the action they are referring to — the principle that an object can behave as if it were another object without the developer having to go through too many hoops to make it happen.

Overriding More Methods

Although you’ve overridden Car’s constructor, for completeness you should look at how to override a normal method.

To override a method you need to have the method in the base Car class. Because Accelerate shouldn’t change depending on whether you have a sports car or a normal car, and IsMoving was added for ease of use — and hence doesn’t really count in this instance as it isn’t a behavior of the object — you need to add a new method called CalculateAccelerationRate. Assume that on a normal car this is a constant, and on a sports car you change it so that it takes the power-to-weight ratio into consideration. In the following Try It Out, you add another method to override.

337

Chapter 10

Try It Out

Adding and Overriding Another Method

1.Add this method to the Car class:

‘CalculateAccelerationRate - assume a constant for a normal car Public Function CalculateAccelerationRate() As Double

‘If we assume a normal car goes from 0-60 in 14 seconds,

‘that’s an average rate of 4.2 mph/s Return 4.2

End Function

2.Now to test the method, change the DisplayCarDetails procedure in Module1 to read like this:

‘DisplayCarDetails - procedure that displays a car’s details Sub DisplayCarDetails(ByVal theCar As Car)

‘Display the details of the car Console.WriteLine(“Color: “ & theCar.Color)

Console.WriteLine(“Number of doors: “ & theCar.NumberOfDoors) Console.WriteLine(“Current speed: “ & theCar.Speed)

Console.WriteLine(“Acceleration rate: “ & _

theCar.CalculateAccelerationRate)

End Sub

3.Run the project and you’ll get an output similar to Figure 10-11.

Figure 10-11

You’ve built a method on Car as normal. This method always returns a value of 4.2 mph/s for the acceleration rate.

Of course, our acceleration calculation algorithm is pure fantasy — no car is going to accelerate at the same rate irrespective of the gear, environment, current speed, and so on.

4.To override the method, you just have to provide a new implementation in SportsCar. However, there’s one thing you need to do first. To override a method you have to mark it as Overridable. To do this, open the Car class again and add the Overridable keyword to the method:

Public Overridable Function CalculateAccelerationRate() As Double

338

Building Objects

5.Now, you can create a method with the same name in the SportsCar class. In order to override the method in the base class, you must add the Overrides keyword before the method type (Function or Procedure):

‘CalculateAccelerationRate - take the power/weight into consideration Public Overrides Function CalculateAccelerationRate() As Double

‘You’ll assume the same 4.2 value, but you’ll multiply it

‘by the power/weight ratio

Return 4.2 * GetPowerToWeightRatio() End Function

You didn’t add the Overrides keyword when you overrode the constructor; you didn’t need to! Visual Basic 2005 handled this for you.

6.Now if you run the project, you get an adjusted acceleration rate as shown in Figure 10-12.

Figure 10-12

How It Works

Overriding the method lets you create your own implementation of an existing method on the object. Again, coming back to this concept of encapsulation, the object consumer doesn’t have to know that anything is different about the object — they just call the method in the same way as they would for a normal Car object. This time, however, they get a different result rather than the constant value they always got on the normal Car object.

When you override a method, it’s quite different from overriding a constructor. When you override a constructor, the original constructor still gets called first. When you override a method, the original method gets called only if you specifically call it from inside the new overriding method using MyBase.Method Name. For example, you could invoke MyBase.CalculateAccelerationRate from SportsCar to return a value of 4.2.

Inheriting from the Object Class

The final thing to look at, with respect to inheritance, is that if you create a class without using the Inherits clause, the class will automatically inherit from a class called Object. This object provides you with a few methods that you can guarantee will be supported by every object you ever have. Most of these methods are beyond the scope of this book. However, the two most useful methods at this level are:

339

Chapter 10

ToString: This method returns a string representation of the object. You can override this to provide a helpful string value for any object; for example, you might want a person object to return that person’s name. If you do not override it, it will return the name of the class.

GetType: This method returns a Type object that represents the data type of the object.

Remember, you do not have to inherit explicitly from Object. This happens automatically.

Objects and Structures

You created a structure in Chapter 5. Like a class, a structure provides a way to group several pieces of information together that all refer to one thing. A structure can even have methods and properties as well as member variables, just as a class can. Here are some of the differences between structures and classes.

In terms of semantics, structures are known as value types and classes are known as reference types. That is, a variable representing a structure means the actual chunk of computer memory that stores the contents of the structure itself, whereas a variable representing a class instance is actually, as you have seen, a “hook” on which the object hangs.

This explains the difference in instantiation — you don’t need to use the New keyword to instantiate a structure before you use it, because it is a value type, just like an integer. You do have to use the New keyword with a form or other complex object because it is a class instance — a reference type.

You have seen that two different object variable “hooks” can be used to hang up the same object. If you set a property in the object using one of the hooks, its value will be as you set it if you get it using the other hook.

Dim

objMyCar As New Car

‘objMyCar.Color is “White”

Set

objThisCar =

objMyCar

‘same object, different hooks

objThisCar.Color

= “Beige”

‘now objMyCar.Color is also “Beige”

Two different structure variables, on the other hand, always refer to different groups of pieces of information.

Dim structMyCustomer As Customer, structThisCustomer As Customer structMyCustomer.FirstName = “Victor”

structThisCustomer = structMyCustomer ‘different structures structThisCustomer.FirstName = “Victoria” ‘structMyCustomer.FirstName is still “Victor”

Also, you cannot inherit from a structure — another important consideration when choosing whether to use a class or a structure.

The Framework Classes

Although we discussed the .NET Framework in general in Chapter 2, take a look now at some aspects of the .NET Framework’s construction that can help you when building objects. In particular, you want to take a look at namespaces and how you can create your own namespaces for use within your objects.

340

Building Objects

Namespaces

The .NET Framework is actually a vast collection of classes. There are around 3,500 classes in the .NET Framework all told, so how are you as a developer supposed to find the ones that you want?

The .NET Framework is divided into a broad set of namespaces that group similar classes together. This limits the number of classes that you have to hunt through if you’re looking for a specific piece of functionality.

These namespaces are also hierarchical in nature, meaning that a namespace can contain other namespaces that further group classes together. Each class must belong to exactly one namespace — it can’t belong to multiple namespaces.

Most of the .NET Framework classes are lumped together in a namespace called System, or namespaces that are also contained within System. For example:

System.Data contains classes related to accessing data stored in a database.

System.Xml contains classes used to read and write XML documents.

System.Windows.Forms contains classes for drawing windows on the screen.

System.Net contains classes for communicating over a network.

The fact that namespaces exist means that all of the objects you’ve been using actually have longer names than the one’s used in your software code. Until this point, you’ve been using a shorthand notation to refer to classes.

In fact, earlier when we said that everything has to be derived from Object, we were stretching it a bit. Because Object is contained within the System namespace, its full name is System.Object. Likewise, Console is actually shorthand for System.Console, meaning that this line:

Console.ReadLine()

is actually the same as this line:

System.Console.ReadLine()

This can get a little silly, especially when you end up with object names like System.Web.Services. Description.ServiceDescription!

.NET automatically creates a shorthand version of all the classes within System, so you don’t have to type System all the time. Later, you’ll see how you can add shorthand references to other namespaces.

There is also the My namespace, which you’ve already seen in use in some of the earlier chapters. This namespace provides access to the most common classes that you’re most likely to need in your everyday programming tasks.

Like the System namespace, the My namespace contains a collection of other classes, which in turn contain classes of their own. At the top level, there is the My.Application class, which provides a wealth of information related to the currently executing application such as the application’s assembly name, the

341

Chapter 10

current path to the application’s executable file, and so on. There is also the My.Computer class, which provides detailed information about the computer the application is executing on, such as the amount of free space on the hard drive and the amount of available memory.

The My.Forms class provides access to the various forms in the application and allows you to manipulate those forms easily; for example, you can show, hide, and close them. There is also the My.Resources class, which provides quick and easy access to an application’s resource files if it contains them. You can place localized text strings and images in a resource file and use the My.Resources class to gain access to these resources for use in your application.

The My.Settings class provides access to an application’s configuration file if it has one and allows you to quickly read the settings needed by your application such as startup settings or database connection information. It also allows you to create, persist, and save user settings for your application. Finally, there is the My.User class, which provides a wealth of information related to the current user of your application, such as login name and the domain name that the user is logged into.

Every class must be in exactly one namespace, but what about the classes we’ve made so far? Well, this project has a default namespace, and your new classes are placed into this namespace. In the next Try It Out, you discover a current namespace.

Try It Out

Finding the Name of the Current Namespace

1.To see the namespace that you’re using, right-click the Objects project in the Solution Explorer and select Properties.

2.The Root Namespace entry in the Objects Property Pages window gives the name of the namespace that will be used for new classes, as shown in Figure 10-13.

Figure 10-13

What this means is that your classes will have the text Objects prefixed to them, like this:

The Car class is actually called Objects.Car.

The SportsCar class is actually called Objects.SportsCar.

As you may have guessed, .NET automatically creates a shorthand version of your classes too, so you can refer to SportsCar instead of having to type Objects.SportsCar.

342

Building Objects

The motivation behind using namespaces is to make life easier for developers using your classes. Imagine that you give this project to another developer for use and they have already built their own class called Car. How do they tell the difference between their class and your class?

Well, yours will actually be called Objects.Car, whereas theirs will have a name like MyOwnProject.Car or YaddaYadda.Car. Namespaces remove the ambiguity of class names. (Of course, we didn’t choose a very good namespace, because it doesn’t really describe the classes that the namespace contains — we just chosen a namespace that illustrated the purpose of the chapter.)

The Imports Statement

Now you know you don’t need to prefix your classes with Car or System because .NET automatically creates a shorthand version, but how do you do this yourself? The answer is the Imports statement!

If you go back to Chapter 9, you might remember this code from the top of the Debug form:

Imports System.Collections.Generic

Public Class Debug

You may recall this code as well:

‘Using the List<T> class

Private objCustomerList As New List(Of Customer)

You used the Imports statement to import the System.Collections.Generic namespace into your project. You needed to do this to gain access to the List<T> class. The full name of this class is System.Collections.Generic.List(Of T), but because you had added a namespace import declaration, you could just write List(Of Customer) instead, substituting the Customer class in place of the T parameter.

All Imports statements must be written right at the top of the code file you want to use them in, before any other code including the Class declaration.

The only drawback happens if you import two namespaces that have an identically named class or child namespace and Visual Basic 2005 cannot tell what it is you are after (like Car.Car and MyOwnProject. Car). If this happens, you will be informed by Visual Basic 2005 that the name is ambiguous — in which case the quickest and easiest thing to do is to specify the full name that you’re after.

Creating Your Own Namespace

Namespaces are defined by wrapping the Class . . . End Class definition in a Namespace . . . End Namespace definition. By default, classes created in Visual Basic 2005 are automatically assigned to a root namespace. Visual Studio 2005 automatically names this root namespace based on the project name. In the next Try It Out, you learn to create a namespace.

Try It Out

Creating a Namespace

1.Using the Solution Explorer, right-click the project and select Properties. The Root Namespace field tells you the name. In this case, the root namespace name is Objects.

343

Chapter 10

2.It’s often recommended that you build your namespaces such that the full names of the classes you develop are prefixed with the name of your company. So, if my company was called MyCodeWidgets, ideally I would want my Car class called MyCodeWidgets.Car. To do this, change the Root Namespace field from Objects to MyCodeWidgets (see Figure 10-14). Then click the Save button on the toolbar to have this change applied to your project.

Figure 10-14

3.Visual Studio 2005’s Object Browser is a useful tool that allows you to see what classes you have available in your project. You can find it by selecting View Object Browser from the menu bar. When the Object Browser is displayed, the first item is usually the project. You can navigate down into it to find your Car class (see Figure 10-15).

Figure 10-15

4.Note that you can also see the methods, properties, and member variables listed for the class. Pertinent to this discussion, however, is the namespace. This is immediately above the class and is indicated by the icon containing the open and closed brace symbols ({}).

That’s fine, but imagine now that you have two projects both containing a class called Car. You need to use namespaces to separate the Car class in one project from the Car class in another. Open the Code Editor for Car and add Namespace CarPerformance before the class definition and End Namespace after it. (I’ve omitted the code for brevity.)

344

Building Objects

Namespace CarPerformance

Public Class Car

. . .

End Class End Namespace

5.Now open the Object Browser again and you’ll see a screen like the one in Figure 10-16.

Figure 10-16

6.Since you have added the CarPerformance namespace to the Car class, any code that references the Car class will need to import that namespace in order to be able to access the shorthand methods of the Car class. Add this Imports statement to the very top of the SportsCar class and Module1 module.

Imports MyCodeWidgets.CarPerformance

How It Works

What you’ve done is put Car inside a namespace called CarPerformance. Because this namespace is contained within MyCodeWidgets, the full name of the class becomes MyCodeWidgets.Car Performance.Car. If you put the classes of the other (imaginary) project into CarDiagnostics, it would be called MyCodeWidgets.CarDiagnostics.Car. Notice how Module1 still appears directly inside MyCodeWidgets. That’s because you haven’t wrapped the definition for Module1 in a namespace as you did with Car. Running your project at this point will produce the same results as before.

Inheritance in the .NET Framework

Inheritance is quite an advanced object-oriented topic. However, it’s really important to include this here because the .NET Framework makes heavy use of inheritance.

One thing to understand about inheritance in .NET is that no class can inherit directly from more than one class. As everything must inherit from System.Object, if a class does not specifically state that it inherits from another class, it inherits directly from System.Object. The upshot of this is that everything must inherit directly from exactly one class (everything, that is, except System.Object itself).

345