Иллюстрированный самоучитель по VB.NET

Полиморфизм на практике


Наследование часто помогает избавиться от громоздких конструкций Select Case и If-Then-Else, чтобы вся черновая работа выполнялась компилятором и механизмом полиморфизма. Например, цикл из следующего фрагмента работает как с экземплярами класса Employee, так и с экземплярами Programmer:

Sub Maln()

Dim tom As New Employee("Tom". 50000)

Dim sally As New Programmer("Sally", 150000)

Dim ourEmployees(l) As Employee ourEmpl.oyees(0)=tom

ourEmployees(l)= Sally

Dim anEmployee As Employee

For Each anEmployee In ourEmployees

anEmployee.RaiseSalary(0.1D)

Console.WriteLine(anEmployee.TheName & "salary now is " & _

anEmployee.Salary()) Next

Console. ReadLine()

End Sub



Результат выполнения этого примера показан на рис. 5.2. Мы видим, что в каждом случае вызывается правильный метод RaiseSalary, несмотря на то что в массиве типа Employee хранятся как объекты Employee, так и объекты Programmers.

Рис. 5.2. Использование полиморфизма в программе

Иногда говорят, что в VB .NET по умолчанию методы являются виртуальными. Термин «виртуальный» означает, что при вызове метода компилятор использует истинный тип объекта вместо типа контейнера или ссылки на объект.

В только что рассмотренном примере под виртуальностью следует понимать, что, хотя все ссылки относятся к типу Empl oyee (поскольку объекты хранятся в массиве Employee), компилятор проверяет истинный тип объекта sally (это тип Programmer) для вызова правильного метода Rai seSal агу, обеспечивающего большую прибавку.

Виртуальные методы довольно часто используются в ситуациях, когда в контейнере базового типа хранятся объекты как базового, так и производного типа. Впрочем, наш упрощенный подход к вызову виртуальных методов сопряжен с некоторыми опасностями. Модификация класса Programmer и включение в него уникальных членов нарушают нормальную работу полиморфизма. В следующем примере класс Programmer дополняется двумя новыми членами (полем и свойством), выделенными жирным шрифтом:



Public Class Programmer

Inherits Employee

Private m_gadget As String

Public Sub New(ByVal theName As String.

ByVal curSalary As Decimal)

MyBase.New(theName. curSalary)

End Sub

Public Overloads Overrides Sub RaiseSalary(ByVal Percent As Decimal)

MyBase.RaiseSalary(1.2D * Percent, "special")

End Sub

Public Property ComputerGadget() As String Get

Return m_Gadget End Get SetCByVal Value As String)

m_Badget = Val ue

End Set

End Property

End Class

В процедуру Sub Main добавляются новые строчки, выделенные жирным шрифтом:

Sub Main()

Dim tom As New Employee("Tom". 50000)

Dim sally As New Programmed"Sally". 150000)

sally.ComputerGadget = "Ipaq"

Dim ourEmployees.d) As Employee

ourEmployees(0)= tom

ourEmployees(l)= sally

Dim anEmployee As Employee

For Each anEmployee In ourEmployees

anEmployee.RaiseSalary(0.1D)

Console.WriteLine(anEmployee.TheName & "salary now is "

& anEmployee.Salary()) Next

Console.WriteLine(ourEmployeesd).TheName & "gadget is an "_

& ourEnployees(l).Gadget) Console. ReadLine()

End Sub

При попытке откомпилировать новый вариант программы будет выдано сообщение об ошибке:

C:\book to comp\chapter 5\VirtualProblems\VirtualProblems\Modulel.vb(17): The name 'Gadget'is not a member of 'VirtualProblems.Employee1.

Хотя объект sally, хранящийся в элементе массива ourEmployees(l), относится к типу Programmer, компилятор этого не знает и потому не может найти свойство ComputerGadget. Более того, при включенном режиме Option Strict (а отключать его не рекомендуется) для использования уникальных членов класса Programmer вам придется производить явное преобразование элементов массива к типу Programmer:

Console.WriteLine(ourEmployees(l).TheName & "gadget is an " & _

CType(ourEmployeesd), Programmer).ComputerGadget)

Преобразование объекта, хранящегося в объектной переменной базового типа, в объект производного класса называется понижающим преобразованием (down-casting); обратное преобразование называется повышающим (upcasting).


Понижающее преобразование весьма широко распространено, однако использовать его не рекомендуется, поскольку при этом часто приходится проверять фактический тип объектной переменной в конструкциях следующего вида: If TypeOf ourEmployees(l)Is Programmer Then

Else If TypeOf ourEmployees(l)Is Employee Then

End If

Перед вами те самые конструкции, для борьбы с которыми нам понадобился полиморфизм! (Повышающее преобразование всегда обходится без проблем, поскольку основополагающее правило наследования гласит, что объекты производных классов всегда могут использоваться вместо объектов базовых классов.)

Хороший стиль программирования требует, чтобы при использовании специальных средств класса Programmer объекты хранились в контейнере, предназначенном только для типа Programmer. В этом случае вам не придется проверять возможность преобразования командой If-TypeOf.




Содержание раздела