ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3281

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

m A m o u n t W i t h d r a w n + = W i t h d r a w ( 1 . 5 ) ;

 

 

 

 

r e t u r n

m A m o u n t W i t h d r a w n ;

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

В этом случае возникает та же проблема, что и в следующем фрагменте:

 

v o i d

f n ( )

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

f n () ;

// В ы з о в ф у н к ц и е й с а м о й с е б я

 

 

}

 

 

 

 

 

 

 

 

 

Вызов f п () из

f п ()

приводит к рекурсивному

вызову функцией самой себя. Анапа

гично,

такой

вызов

W i t h d r a w ( ) , как показано в

фрагменте выше, приводит к вызов;

функцией самой себя, пока программа в конечном счете не завершится аварийно.

Требуется

указать С#,

что в методе S a v i n g s A c c o u n t . W i t h d r a w ( )

следуетщ

звать

метод

B a n k A c c o u n t . W i t h d r a w ( ) .

Один

и з

вариантов

решения

поставлена!

задачи состоит в преобразовании указателя

t h i s

в

указатель на

объект ВапкАссоия

перед выполнением вызова:

 

 

 

 

 

/ / W i t h d r a w - э т а в е р с и я о б р а щ а е т с я к с о к р ы т о м у м е т о д у

/ / б а з о в о г о к л а с с а п о с р е д с т в о м я в н о г о п р е о б р а з о в а н и я t h i s n e w p u b l i c d e c i m a l W i t h d r a w ( d e c i m a l m W i t h d r a w a l )

{

/ / П р е о б р а з о в а н и е у к а з а т е л я t h i s в о б ъ е к т к л а с с а

/ / B a n k A c c o u n t

 

 

 

B a n k A c c o u n t b a =

( B a n k A c c o u n t ) t h i s ;

 

/ / В ы з о в W i t h d r a w ( )

с и с п о л ь з о в а н и е м о б ъ е к т а B a n k A c c o u n t

d e c i m a l m A m o u n t W i t h d r a w n = b a . W i t h d r a w ( m W i t h d r a w a l ) ;

m A m o u n t W i t h d r a w n + = b a . W i t h d r a w ( 1 . 5 ) ;

 

r e t u r n m A m o u n t W i t h d r a w n ;

 

}

 

 

 

Данное решение вполне работоспособно: вызов

ba . W i t h d r a w () вызывает мети

класса B a n k A c c o u n t . Однако

в будущем изменение

программы может привести кл

кому изменению иерархии

классов, что S a v i n g s A c c o u n t не будет непосредственны)

потомком B a n k A c c o u n t . Подобная модификация приведет к неверной работе функцщ найти причину которой будет нелегко.

Необходим способ пояснить С#, что требуется вызвать функцию W i t h d r a w () из клас­ са, являющегося непосредственным предшественником т е к у щ е г о — причем без явной именования этого класса. Для этой цели в С# служит ключевое слово b a s e .

Это то же ключевое слово b a s e , которое конструктор использует для переда! аргумента конструктору базового класса.

Ключевое слово С# b a s e в показанном далее фрагменте кода представляет собой то же, что и t h i s , но приведение к базовому классу выполняется независимо от того, кап именно класс является таковым .

/ / W i t h d r a w - м о ж н о с н и м а т ь л ю б у ю с у м м у в п р е д е л а х б а л а н с а ; / / в о з в р а щ а е т с н я т у ю с о с ч е т а с у м м у

n e w p u b l i c d e c i m a l W i t h d r a w ( d e c i m a l m W i t h d r a w a l )

{

/ / С н я т и е д о п о л н и т е л ь н о й с у м м ы 1 . 5 0 b a s e . W i t h d r a w ( 1 . 5 M ) ;

290

Часть IV. Объектно-ориентированное программирова


// Снятие со с ч е т а с

о с т а в ш е й с я с у м м о й

return base . Withdraw (mWithdrawal) ;

}

 

Вызов b a s e . W i t h d r a w ()

приводит к вызову метода B a n k A c c o u n t . W i t h d r a w ( ) ;

и самым проблема, связанная с рекурсией, снимается. Кроме того, данное решение работает и при изменении иерархии наследования.

Можно перегрузить метод базового класса методом в подклассе. Это и замечательно, одновременно очень опасно.

Проведем мысленный эксперимент: когда должно приниматься решение о том, какой из м е т о д о в — B a n k A c c o u n t . W i t h d r a w () или S a v i n g s A c c o u n t . W i t h d r a w () — будет вызван: во время компиляции или во время выполнения программы?

Для того чтобы понять, в чем здесь отличие, будет немного изменена рас­ сматривавшаяся ранее программа H i d i n g W i t h d r a w a l (здесь приведена только та часть, в которую внесены изменения).

// H i d i n g W i t h d r a w a l P o l y m o r p h i c a l l y - с о к р ы т и е м е т о д а

// W i t h d r a w () б а з о в о г о к л а с с а м е т о д о м с т е м же и м е н е м в

// п о д к л а с с е

 

 

 

 

 

 

public

c l a s s P r o g r a m

 

 

 

 

(

 

 

 

 

 

 

 

p u b l i c

s t a t i c

v o i d M a k e A W i t h d r a w a l ( B a n k A c c o u n t

b a ,

 

 

 

 

 

 

d e c i m a l m A m o u n t )

{

 

 

 

 

 

 

 

b a . W i t h d r a w ( m A m o u n t ) ;

 

 

 

}

 

 

 

 

 

 

 

p u b l i c

s t a t i c

v o i d M a i n ( s t r i n g [ ]

a r g s )

 

{

 

 

 

 

 

 

 

B a n k A c c o u n t

b a ;

 

 

 

 

 

S a v i n g s A c c o u n t

s a ;

 

 

 

 

b a = n e w B a n k A c c o u n t ( 2 0 0 M ) ;

 

 

M a k e A W i t h d r a w a l ( b a ,

1 0 0 M ) ;

 

 

 

s a = n e w S a v i n g s A c c o u n t ( 2 0 0 M ,

1 2 ) ;

 

M a k e A W i t h d r a w a l ( s a ,

1 0 0 M ) ;

 

 

 

/ / В ы в о д и м с о с т о я н и я с ч е т о в

 

 

C o n s o l e . W r i t e L i n e ( " Б а л а н с

B a n k A c c o u n t р а в е н

{ 0 : C } " ,

 

 

 

 

b a . B a l a n c e ) ;

 

 

C o n s o l e . W r i t e L i n e ( " Б а л а н с

S a v i n g s A c c o u n t р а в е н { 0 : C } " ,

 

 

 

 

s a . B a l a n c e ) ;

 

 

/ / О ж и д а е м п о д т в е р ж д е н и я п о л ь з о в а т е л я

 

C o n s o l e . W r i t e L i n e ( " Н а ж м и т е < E n t e r > д л я " +

 

 

 

 

 

" з а в е р ш е н и я п р о г р а м м ы . . . " ) ;

 

C o n s o l e . R e a d ( ) ;

 

 

 

 

 

Вывод этой демонстрационной программы на экран может вас удивить (а может и не удивить — в зависимости от того, чего именно вы ожидали):

Глава 13. Полиморфизм

291


Б а л а н с B a n k A c c o u n t р а в е н

$ 1 0 0 . 0 0

 

 

 

 

 

 

Б а л а н с S a v i n g s A c c o u n t

р а в е н

$ 1 0 0 . 0 0

 

 

 

 

 

Н а ж м и т е < E n t e r > д л я з а в е р ш е н и я п р о г р а м м ы . . .

 

 

 

 

В этот раз, вместо снятия со

счета

в

функции

M a i n ( ) , программа передает объект

счета функции M a k e A W i t h d r a w a l ( ) .

 

 

 

 

 

 

 

 

Первый вопрос очевиден: как функция

M a k e A W i t h d r a w a l ()

может

принимать

объект

S a v i n g s A c c o u n t ,

 

если

она ожидает в качестве аргумента объект BankAc

c o u n t ? Ответ не

менее

ясен:

потому

что

S a v i n g s A c c o u n t

Я В Л Я Е Т С Я

BankAc

c o u n t

(см. главу 12, "Наследование") .

 

 

 

 

 

 

 

 

Второй вопрос

не

так

очевиден. Когда функции M a k e A W i t h d r a w a l

()

передает!

объект

B a n k A c c o u n t ,

она

вызывает B a n k A c c o u n t . W i t h d r a w ()

— это

понятно. Не

когда передается объект типа S a v i n g s A c c o u n t ,

вызывается тот

же метод. Должен ли

в этом

случае вызываться метод W i t h d r a w ()

подкласса?

 

 

 

 

С

одной стороны,

поскольку

объект

ba

принадлежит типу

B a n k A c c o u n t , вызов

b a . W i t h d r a w ()

должен вызывать метод B a n k A c c o u n t . W i t h d r a w ( ) .

С другой сто

роны,

хотя объект ba и объявлен

как B a n k A c c o u n t , фактически

он

представляет собой

объект

S a v i n g s A c c o u n t ,

так

что

 

должен

быть вызван

метод

S a v i n g s A c

c o u n t

. W i t h d r a w ( ) . Оба

аргумента достаточно

логичны .

 

 

 

 

В данном случае С# принимает как более весомый первый аргумент. Это более безопасный выбор — работать с объявленным типом — поскольку он устраняет все недора зумения. Объект объявлен как B a n k A c c o u n t , и так тому и быть.

Что неверно в стратегии использования объявленного типа

В ряде случаев вам не требуется работа с объявленным типом. На самом деле необходимо, чтобы вызов базировался на реальном типе, т.е. на типе времени исполнения, а не на объявленном типе. Например, вам нужно, чтобы выполнялись действия со счетом типа S a v i n g s A c c o u n t , который хранится в переменной типа B a n k A c c o u n t . Такая

возможность принятия

решения во время выполнения программы называется полимор­

физмом, или поздним связыванием (late binding). Стратегия использования

объявленного

типа называется ранним

связыванием (early binding), в противоположность

позднему.

Термин полиморфизм происходит из греческого языка: поли означает много,

аморф — форма, или действие.

По л и м о р ф и з м и позднее связывание вообще-то не одно и то же. Однако их отличие весьма тонкое. Полиморфизм означает возможность принятия решения о том, какой ме­

тод должен быть вызван в процессе выполнения программы . Позднее связывание — спо­ соб реализации полиморфизма языком программирования .

П о л и м о р ф и з м является ключевой составляющей частью объектно-ориентированного программирования . Он настолько важен, что языки, его не поддерживающие, не имеют права называться объектно-ориентированными.

Я з ы к и программирования, поддерживающие классы, но не поддерживающие полиморфизм, называются объектно-основанными языками (object-based lan­ guages). Примером такого языка может служить язык Ada.

292

Часть IV. Объектно-ориентированное программирование


Без полиморфизма в наследовании мало толку. Позвольте привести наглядный при­ мер, иллюстрирующий данный тезис. Предположим, вы написали м о щ н у ю программу, использующую класс... ну, скажем, S t u d e n t . После нескольких месяцев проектирова­ ния, кодирования и тестирования вы наконец-то вынесли ее на суд восхищенных пользо­ вателей (начинающих даже поговаривать, что совершенно напрасно не существует Но - бевской премии в области программирования) .

Проходит время, и ваш шеф требует, чтобы вы добавили в программу возможность рабо­ ты с аспирантами, которые, конечно, похожи на студентов, но все же немного отличаются от них (сами аспиранты считают, что они отличаются во всем). Предположим, что формулы для вычисления оплаты за обучение для студентов и аспирантов совершенно различны. Вашему боссу это безразлично, но в программе имеется масса вызовов функции C a l c T u i t i o n ( ) , яредаазначенной для таких расчетов. Вот пример одного из таких вызовов:

v o i d S o m e F u n c t i o n ( S t u d e n t s )

{

// . . . К а к и е - т о д е й с т в и я . . .

s . C a l c T u i t i o n ( ) ; II... продолжение . . .

}

Если бы С# не поддерживал позднее связывание, то вам бы пришлось редактировать функцию S o m e F u n c t i o n ( ) , чтобы проверять в ней, является ли переданный объект s пе­

ременной типа S t u d e n t или

G r a d u a t e S t u d e n t . Программа должна была

бы вызывать

Student. C a l c T u i t i o n ()

в случае, когда переменная s принадлежала бы

классу S t u -

|dent, и G r a d u a t e S t u d e n t . C a l c T u i t i o n () в случае класса G r a d u a t e S t u d e n t . Это было бы не так страшно, если бы не две вещи.

Это только одна функция. А теперь представьте, что C a l c T u i t i o n () вызывает­

ся в сотнях мест...

Предположим, что C a l c T u i t i o n () — не единственное отличие между двумя классами. Ш а н с ы , что вы найдете все места в программе, требующие изменений, резко снижаются ...

При поддержке полиморфизма вы просто позволяете С# самостоятельно решить, ка­ кой метод должен быть вызван.

Использование is для полиморфного доступа к скрытому методу

Каким образом сделать программу полиморфной? Один из подходов для решения

этой задачи в

С# состоит в использовании ключевого слова i s (о котором

рассказыва­

юсь в

главе

12, "Наследование") . Выражение ba

is

S a v i n g s A c c o u n t

возвращает

иачение

t r u e

или f a l s e в зависимости от класса

объекта во время выполнения

про­

граммы. Объявленный тип может быть B a n k A c c o u n t ,

но с какого типа объектом

при­

ходится иметь дело в реальности? В приведенном далее фрагменте исходного текста is

используется для

обращения

к функции W i t h d r a w () класса

S a v i n g s A c c o u n t .

public c l a s s

P r o g r a m

 

 

p u b l i c

s t a t i c v o i d

M a k e A W i t h d r a w a l ( B a n k A c c o u n t b a ,

 

 

 

d e c i m a l

m A m o u n t )

Глава 13.

Полиморфизм

 

293