In Lesson 26 you learned how to build constructors, special methods that execute when an object is created. In this lesson you learn about other special methods you can give a class. In particular, you learn how to overload and override class methods.
Lesson 26 mentioned that you can give a class any number of constructors as long as they have different parameter lists. For example, it's common to give a class a parameterless constructor that takes no parameters, and one or more other constructors that take parameters.
Making multiple methods with the same name but different parameter lists is called overloading. Visual Basic uses the parameter list to determine which version to use when you invoke the method.
For example, suppose you're building a course assignment application and you have built Student, Course
, and Instructor
classes. You could give the Student
class two versions of the Enroll
method, one that takes as a parameter the name of the class in which the student should be enrolled and a second that takes a Course
object as a parameter.
You could give the Instructor
class similar versions of the Teach
method to make the instructor teach a class by name or Course
object.
Finally, you could give the Course
class different Report
methods that:
Display a report in a dialog if there are no parameters
Append a report to the end of a file if the method receives a FileStream
as a parameter
Save the report into a new file if the method receives a String
(the filename) as a parameter
Making overloaded methods is so easy that there's little else to say. Simply write each version as you normally would write a method. Just remember to give each version of the method a different parameter list.
When one class inherits from another you can add new properties, methods, and events to the new class to give it new features that were not provided by the parent class.
Once in a while it's also useful to replace a method provided by the parent class with a new version. This is called overriding the parent's method.
Before you can override a method, you should mark the method in the parent class with the Overridable
keyword so it allows itself to be overridden. Next, add the keyword Overrides
to the derived class's version of the method to indicate that it overrides the parent class's version.
Overridable methods are also sometimes called virtual methods, particularly in other programming languages such as C++ and C#.
For example, suppose the Person
class defines the usual assortment of properties: FirstName, LastName, Street, City
, and so on. Suppose it also provides the following GetAddress
function that returns the Person
's name and address formatted for printing on an envelope:
' Return the Person's address. Public Overridable Function GetAddress() As String Return FirstName & " " & LastName & vbCrLf & Street & vbCrLf & City & " " & State & " " & Zip End Function
Now suppose you derive the Employee
class from Person
. An Employee
's address looks just like a Person
's except it also includes MailStop
.
The following code shows how the Employee
class can override the GetAddress
function to return an Employee
-style address:
' Return the Employee's address. Public Overrides Function GetAddress() As String Return MyBase.GetAddress() & vbCrLf & MailStop End Function
Notice how the function calls the base class's version of GetAddress
to reuse that version of the function and avoid duplicated work.
IntelliSense can help you build overridden methods. For example, when you type "Public Overrides" and a space in the Employee
class, IntelliSense lists the overridable methods that you might be trying to override. If you select one, IntelliSense fills in a default implementation for the new method. The following code shows the code IntelliSense generated for the GetAddress
function:
Public Overrides Function GetAddress() As String Return MyBase.GetAddress() End Function
The most miraculous thing about overriding a method is that the object uses the method even if you invoke it from the base class. For example, suppose you have a Person
variable pointing to an Employee
object. Remember that an Employee
is a kind of Person
, so a Person
variable can refer to an Employee
, as in the following code:
Dim bob As New Employee() With { .FirstName = "Bob", .LastName = "Boastful", .Street = "22 Breach Blvd", .City = "Bugsville", .State = "CO", .Zip = "82789", .MailStop = "B-52" } Dim bobAsPerson As Person = bob
Now if the code calls bobAsPerson.GetAddress()
, the result is the Employee
version of GetAddress
. The address includes Bob's MailStop
even though a Person
object doesn't have a MailStop
.
You can think of the Overridable
keyword as making a slot in the base class for the method. When you override the method, the derived class fills this slot with a new version of the method. Now, even if you call the method from the base class, it uses whatever is in the slot.
Overriding a class's ToString
function is particularly useful. All classes inherit a ToString
method from System.Object
, the ultimate ancestor of all other classes, but the default implementation of ToString
isn't always useful. By default, for classes that you defined such as Person
and Employee, ToString
returns the class's name.
For example, in the ListPeople example program, which is shown in Figure 27-1 and available as part of this lesson's code download, the Employee
class's ToString
function returns ListPeople.Employee
. Though this correctly reports the object's class, it might be nice if it returned something that contained information about the object's properties. In this example, it would be better if it returned the Employee
object's first and last names.
The following code shows how you can override the ToString
function to return an Employee
's first and last name:
' Return first and last name. Public Overrides Function ToString() As String Return FirstName & " " & LastName End Function
This makes more sense. Now your program can use an Employee
object's ToString
method to learn about the object.
Overriding ToString
also has a nice side benefit for Windows Forms development. Certain controls and parts of Visual Studio use an object's ToString
method to decide what to display. For example, the ListBox
and ComboBox
controls display lists of items. If those items are not simple strings, the controls use the items' ToString
methods to generate output.
If the list is full of Employee
objects and you've overridden the Employee
class's ToString
method, then a ListBox
or ComboBox
can display the employees' names.
The ListPeople example program demonstrates method overriding. When it starts, the program uses the following code to fill its ListBox
with two Student
objects and two Employee
objects. Both of these classes inherit from Person
.
' Make some people. Private Sub Form1_Load() Handles MyBase.Load lstPeople.Items.Add(New Student("Ann", "Archer", "101 Ash Ave", "Debugger", "NV", "72837")) lstPeople.Items.Add(New Student("Bob", "Best", "222 Beach Blvd", "Debugger", "NV", "72837")) lstPeople.Items.Add(New Employee("Cat", "Carter", "300 Cedar Ct", "Debugger", "NV", "72837", "MS-1")) lstPeople.Items.Add(New Employee("Dan", "Dental", "404 Date Dr", "Debugger", "NV", "72837", "MS-2")) End Sub
The Employee
class overrides its ToString
method, so you can see the Employees
' names in Figure 27-1 instead of their class names. The Student
class does not override its ToString
method, so Figure 27-1 shows class names for the Student
objects.
If you double-click a person in the list, the program executes the following code:
' Display the selected Person's address. Private Sub lstPeople_DoubleClick() Handles lstPeople.DoubleClick Dim personClicked As Person = DirectCast(lstPeople.SelectedItem, Person) MessageBox.Show(personClicked.GetAddress()) End Sub
This code converts the ListBox
's selected item into a Person
object. The item is actually either a Student
or an Employee
but both of those inherit from Person
(they are kinds of Person
), so the program can treat them as Persons
.
The program calls the object's GetAddress
function and displays the result. If the object is actually a Student
, the result is a basic name and address. If the object is actually an Employee
, the result is a name and address plus mailstop.
In addition to ListBoxes
and ComboBoxes
, some parts of Visual Studio use an object's ToString
method, too. For example, if you stop an executing program and hover the mouse over an object in the debugger, a tooltip appears that displays the results of the object's ToString
function.
In this Try It, you build a simple drawing application. You build a Shape
class to represent a drawn shape and then derive the ShapeRectangle
and ShapeEllipse
classes from that one.
You give the Shape
class an overridable Draw
method that takes as a parameter a Graphics
object and draws the shape on the object. The Shape
class's version of Draw
simply draws a rectangle with a big X in it.
You make the ShapeRectangle
and ShapeEllipse
classes override Draw
to produce their own shapes.
Finally, you add overloaded versions of Draw
that take pen and brush objects as parameters to use when drawing.
You can download the code and resources for this Try It from the book's web page at www.wrox.com
or www.vb-helper.com/24hourvb.html
. You can find the programs in the Lesson27 folder.
In this lesson:
Start a new program and create the Shape
class.
Give the class a property named Bounds
of type Rectangle
. (Rectangle
is a structure with properties X, Y, Width
, and Height
.)
Make a constructor that takes x, y, width
, and height
parameters and uses them to initialize the Bounds
property.
Make an overridable Draw
method that takes three parameters of type Graphics, Pen
, and Brush
. Make this method draw a box with an X in it on the Graphics
object using the Pen
and Brush
.
Make a second Draw
method that takes only a Graphics
object as a parameter. Make it invoke the other Draw
method, passing it the pen and brush Pens.Red
and Brushes.Transparent
.
Derive the ShapeRectangle
class from the Shape
class.
Make a constructor that takes x, y, width
, and height
parameters and invokes the base class's constructor.
Override the Shape
class's version of the Draw
method that takes four parameters so it draws a rectangle.
Override the second Draw
method so it calls the first, passing it the pen and brush Pens.Black
and Brushes.Transparent
.
Repeat the previous steps for the ShapeEllipse
class.
In the main form, create a List(Of Shape)
.
In the form's Load
event handler, create some ShapeRectangles
and ShapeEllipses
and add them to the list.
Give the form a CheckBox
labeled "Colored." In its CheckedChanged
event handler, invalidate the form to force a redraw.
In the form's Paint
event handler, redraw the form by looping through the Shapes
in the list. If the CheckBox
is checked, invoke the Shape
objects' Draw
methods, passing them the pen and brush Pens.Blue
and Brushes.LightBlue
. If the CheckBox
is not checked, invoke the Draw
methods with no parameters.
If gr
is the Graphics
object, then you can use these methods to draw:
gr.Clear(Me.BackColor)
— Clears the object with the form's background color
gr.FillRectangle(
brush
, rect
)
— Fills a rectangle defined by rect
with brush
gr.DrawRectangle(
— Outlines a rectangle defined by pen
, rect
)rect
with pen
gr.FillEllipse(
— Fills an ellipse defined by brush
, rect
)rect
with brush
gr.DrawEllipse(
— Outlines an ellipse defined by pen
, rect
)rect
with pen
In the Paint
event handler, draw on the Graphics
object provided by the event handler's e.Graphics
parameter.
Start a new program and create the Shape
class.
Give the class a property named Bounds
of type Rectangle
. (Rectangle
is a structure with properties X, Y, Width
, and Height
.)
Use code similar to the following:
Public Property Bounds As Rectangle
Make a constructor that takes x, y, width
, and height
parameters and uses them to initialize the Bounds
property.
Use code similar to the following:
Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) Bounds = New Rectangle(x, y, width, height) End Sub
Make an overridable Draw
method that takes three parameters of type Graphics, Pen
, and Brush
. Make this method draw a box with an X in it on the Graphics
object using the Pen
and Brush
.
Use code similar to the following:
' Draw a box with an X in it using a pen and brush. Public Overridable Sub Draw(ByVal gr As Graphics, ByVal thePen As Pen, ByVal theBrush As Brush) gr.FillRectangle(theBrush, Bounds) gr.DrawRectangle(thePen, Bounds) gr.DrawLine(thePen, Bounds.Left, Bounds.Top, Bounds.Right, Bounds.Bottom) gr.DrawLine(thePen, Bounds.Right, Bounds.Top, Bounds.Left, Bounds.Bottom) End Sub
Make a second Draw
method that takes only a Graphics
object as a parameter. Make it invoke the other Draw
method, passing it the pen and brush Pens.Red
and Brushes.Transparent
.
Use code similar to the following:
' Draw a box with an X in it. Public Overridable Sub Draw(ByVal gr As Graphics) Draw(gr, Pens.Red, Brushes.Transparent) End Sub
Derive the ShapeRectangle
class from the Shape
class.
Make a constructor that takes x, y, width
, and height
parameters and invokes the base class's constructor.
Use code similar to the following:
Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) MyBase.New(x, y, width, height) End Sub
Override the Shape
class's version of the Draw
method that takes four parameters so it draws a rectangle.
Use code similar to the following:
Public Overrides Sub Draw(ByVal gr As Graphics, ByVal thePen As Pen, ByVal theBrush As Brush) gr.FillRectangle(theBrush, Bounds) gr.DrawRectangle(thePen, Bounds) End Sub
Override the second Draw
method so it calls the first, passing it the pen and brush Pens.Black
and Brushes.Transparent
.
Use code similar to the following:
Public Overrides Sub Draw(ByVal gr As Graphics) Draw(gr, Pens.Black, Brushes.Transparent) End Sub
Repeat the previous steps for the ShapeEllipse
class.
The following code shows the ShapeEllipse
class's code:
Public Class ShapeEllipse Inherits Shape Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer) MyBase.new(x, y, width, height) End Sub Public Overrides Sub Draw(ByVal gr As Graphics, ByVal thePen As Pen, ByVal theBrush As Brush) gr.FillEllipse(theBrush, Bounds) gr.DrawEllipse(thePen, Bounds) End Sub Public Overrides Sub Draw(ByVal gr As Graphics) Draw(gr, Pens.Black, Brushes.Transparent) End Sub End Class
In the main form, create a List(Of Shape)
.
Use code similar to the following:
' Our list of shapes to draw. Private Shapes As New List(Of Shape)()
In the form's Load
event handler, create some ShapeRectangles
and ShapeEllipses
and add them to the list.
You can use code similar to the following, or you can create any ShapeEllipse
and ShapeRectangle
objects that you like:
' Make some shapes. Private Sub Form1_Load() Handles MyBase.Load Shapes.Add(New ShapeEllipse(50, 50, 200, 200)) Shapes.Add(New ShapeEllipse(100, 80, 40, 60)) Shapes.Add(New ShapeEllipse(100, 80, 40, 60)) Shapes.Add(New ShapeEllipse(120, 100, 20, 30)) Shapes.Add(New ShapeEllipse(160, 80, 40, 60)) Shapes.Add(New ShapeEllipse(180, 100, 20, 30)) Shapes.Add(New ShapeEllipse(135, 130, 30, 50)) Shapes.Add(New ShapeRectangle(120, 190, 60, 5)) Shapes.Add(New ShapeRectangle(75, 25, 150, 50))
Shapes.Add(New ShapeRectangle(50, 75, 200, 10)) End Sub
Give the form a CheckBox
. In its CheckedChanged
event handler, invalidate the form to force a redraw.
The following code shows this event handler:
' Force a redraw. Private Sub chkColored_CheckedChanged() _ Handles chkColored.CheckedChanged Me.Invalidate() End Sub
In the form's Paint
event handler, redraw the form by looping through the Shapes
in the list. If the CheckBox
is checked, invoke the Shape
objects' Draw
methods, passing them the pen and brush Pens.Blue
and Brushes.LightBlue
. If the CheckBox
is not checked, invoke the Draw
methods with no parameters.
Use code similar to the following:
' Redraw the objects. Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint e.Graphics.Clear(Me.BackColor) ' Loop through all of the shapes. For Each theShape As Shape In Shapes If (chkColored.Checked) Then ' Draw with a pen and brush. theShape.Draw(e.Graphics, Pens.Blue, Brushes.LightBlue) Else ' Draw with no pen or brush. theShape.Draw(e.Graphics) End If Next theShape End Sub
Figure 27-2 shows the result for the objects you created.
Please select Lesson 27 on the DVD to view the video that accompanies this lesson.
Copy the complex number program you built in Lesson 26, Exercise 2 (or download Lesson 26's version from the book's web site). Override the class's ToString
method so it returns the number in a form similar to "2 + 3i." Overload the ComplexNumber
class's constructor and AddTo, MultiplyBy
, and SubtractFrom
functions so you can pass them a single Double
parameter representing a real number with no imaginary part. Modify the form so you can test the new methods.
Copy the bank account program you built in Lesson 26, Exercise 4 (or download Lesson 26's version from the book's web site). Derive a new OverdraftAccount
class from the Account
class. Give it a constructor that simply invokes the base class's constructor. Override the Debit
method to allow the account to have a negative balance and charge a $50 fee if any debit leaves the account with a negative balance. Change the main program so the Account
variable is still declared to be of type Account
but initialize it as an OverdraftAccount
. Hints: Don't forget to make the BankAccount
class's version of Debit
overridable; and to allow OverdraftAccount
code to access the _Balance
backing field, change that field from Private
to Protected
.
Copy Lesson 24's Turtle program. The Turtle
class has a Move
method that moves the turtle a specified distance in the object's current direction. Overload this method by making a second version that takes as parameters the x and y coordinates where the turtle should move. Be sure to raise the OutOfBounds
event if the point is not on the canvas. Hint: Can you reuse code somehow between the two Move
methods?
You can find solutions to this lesson's exercises in the Lesson27 folder inside the download available on the book's web site at www.wrox.com
or www.vb-helper.com/24hourvb.html
.