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

C# для чайников

.pdf
Скачиваний:
183
Добавлен:
27.03.2015
Размер:
15.52 Mб
Скачать

//Withdraw -

м о ж н о

с н я т ь

н е б о л е е т о г о ,

ч т о и м е е т с я н а

// счету;

м е т о д в о з в р а щ а е т

с н я т у ю

с у м м у

 

'public

d o u b l e

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 )

return

b a n k A c c o u n t

. W i t h d r a w ( m W i t h d r a w a l )

;

В этом

случае

класс 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

вместо наследования от B a n k A c c o u n t ) . Объект b a n k A c c o u n t включает номер счета баланс, необходимые для функционирования SavingsAccount_. Класс Savingaccount_ содержит данные, специфичные для депозитного счета и делегирует при

необходимости запросы к содержащемуся в нем объекту 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 ) .

В этом случае речь идет о том, что 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 .

Отношение СОДЕРЖИТ

Отношение С О Д Е Р Ж И Т фундаментально отличается от отношения Я В Л Я Е Т С Я . Это отличие кажется не столь существенным в следующем фрагменте исходного текста:

/ / Создание

н о в о г о

д е п о з и т н о г о с ч е т а

 

BankAccount ba

=

n e 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 :

 

S a v i n g s A c c o u n t _ sa = n e w S a v i n g s A c c o u n t _ () ;

 

s a . I n i t S a v i n g s A c c o u n t ( b a , 5) ;

 

/ / Вкладываем

1 0 0

н а с ч е т

 

 

sa.Deposit ( 1 0 0 ) ;

 

 

 

 

 

/ / Подсчитываем п р о ц е н т ы

 

 

s a . A c c u m u l a t e l n t e r e s t () ;

 

 

Проблема в

том,

что

тепер ь

S a v i n g s A c c o u n t _ не м о ж е т

и с п о л ь з о в а т ь с я как

BankAccount,

поскольк у не

является его н а с л е д н и к о м .

Он тепер ь содержит

BankAccount, а это далеко не о д н о и то же . Н а п р и м е р , с л е д у ю щ и й ко д к о м п и л и - роваться не будет:

/ / D i r e c t D e p o s i t - а в т о м а т и ч е с к и й в к л а д н а с ч е т void DirectDeposit(BankAccount ba, int nPay)

{

ba.Deposit (nPay) ;

}

void SomeFunctionO

{

// Этот к о д не с к о м п и л и р у е т с я

S a v i n g s A c c o u n t _ sa = n e w S a v i n g s A c c o u n t _ () ;

D i r e c t D e p o s i t ( s a ,

1 0 0 ) ;

 

// . . продолжение . . .

 

 

 

}

 

 

 

Функция D i r e c t D e p o s i t

( ) н е м о ж е т

п р и н я т ь S a v i n g s A c c o u n t _ вместо

BankAccount. М е ж д у

э т и м и

классам и не т

т а к о г о о ч е в и д н о г о о т н о ш е н и я , как в

случае наследования.

 

 

 

Глава 12. Наследование

269

Различие между отношениями Я В Л Я Е Т С Я и С О Д Е Р Ж И Т гораздо глубже, чем про сто предмет программного соглашения. Эти отношения проистекают из отношений в реа альном мире.

Например, " З а п о р о ж е ц " Я В Л Я Е Т С Я автомобилем. Автомобиль СОДЕРЖИТ мотор Если ваш знакомый скажет, чтобы вы заехали за ним на автомобиле, и вы приедете "Запорожце", ему будет не на что пожаловаться — вы приехали на автомобиле. Но если вы притащите к нему двигатель от "Запорожца", у него будут все основания обидеться на глупую шутку.

Класс Z a p o r o z h e t s должен расширять класс С а г не только для того, чтобы пол чить доступ к методам С а г , но и чтобы выразить фундаментальные отношения между этими классами.

К сожалению, начинающий программист может унаследовать С а г от M o t o r , как про стейший способ получения доступа к членам M o t o r , которые нужны классу С а г для управ ления. Например, С а г может унаследовать у класса M o t o r метод Go ( ) . Однако этот пример вскрывает одну из проблем, возникающих при таком подходе. Несмотря на то что "поехал звучит одинаково и в машине, и даже в ракете, "поехали" по отношению к машине совсем то, что "поехали" по отношению к мотору. Для того чтобы поехала машина, надо обязательно завести мотор, но это далеко не одно и то же — ведь для того чтобы поехала машина, и еще отпустить тормоз, переключиться на первую передачу, отпустить сцепление и т.д.

Словом, автомобиль просто не является видом мотора, и этого достаточно.

Элегантность программного о б е с п е ч е н и я — это не просто эстетический фак тор. Она способствует пониманию кода, повышает его надежность, облегчает поддержку, снижает количество возможных ошибок.

Специалисты в области объектно-ориентированного программирования для про стоты дизайна рекомендуют отдавать предпочтение отношению СОДЕРЖИТ.

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

Изменение класса

Программа может изменить класс объекта. Вы уже встречались с этим в одном из примеров. S o m e F u n c t i o n ( ) может передать объект 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 .

Это преобразование можно сделать я в н ы м следующим образом:

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 = n e w S a v i n g s A c c o u n t ( ) ;

270

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

 

/ / В е р н о :

ba = s a ;

// Н е я в н о е п р е о б р а з о в а н и е к

 

/ / б а з о в о м у к л а с с у р а з р е ш е н о

b a = ( B a n k A c c o u n t ) s a ;

/ / Н о я в н о е п р е о б р а з о в а н и е

 

/ / п р е д п о ч т и т е л ь н е е

 

/ / О ш и б к а :

sa = b a ;

// Н е я в н о е п р е о б р а з о в а н и е к

 

/ / п о д к л а с с у з а п р е щ е н о

sa = ( S a v i n g s A c c o u n t ) b a ; / / Д о п у с т и м о

В первой строке объект S a v i n g s A c c o u n t сохраняется в переменной типа B a n k A c ­ count. С# выполняет необходимое преобразование за вас. Во второй строке явным об­ разом использован оператор приведения типа.

Последние две строки преобразуют объект типа 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 — не обязательно 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 к типу S a v i n g s A c ­

count — достаточно опасная операция. Рассмотрим следующий пример:

p u b l i c s t a t i c

v o i d P r o c e s s A m o u n t ( B a n k A c c o u n t b a n k A c c o u n t )

{

/ / В н о с и м н а с ч е т б о л ь ш у ю с у м м у

 

 

 

 

 

 

 

 

b a n k A c c o u n t . D e p o s i t ( 1 0 0 0 0 . 0 0 М ) ;

 

 

 

 

 

 

 

 

/ / Е с л и о б ъ е к т — 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

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 ) 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 . A c c u m u l a t e l n t e r e s t ( ) ;

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p u b l i c

s t a t i c

v o i d

T e s t C a s t O

 

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

s a

=

n e w S a v i n g s A c c o u n t ( ) ;

 

 

 

 

 

 

P r o c e s s A m o u n t ( s a ) ;

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

P r o c e s s A m o u n t ( b a ) ;

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Функция P r o c e s s A c c o u n t ()

выполняет

несколько

операций,

включая

вызов

ме­

тода A c c u m u l a t e l n t e r e s t

( ) .

Приведение

b a к типу

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

необходи­

мо, поскольку ba

объявлено

как B a n k A c c o u n t . Программа корректно компилируется,

так как все преобразования типов выполнены явно.

 

 

 

 

 

 

 

Все нормально работает при первом вызове

P r o c e s s A c c o u n t

()

из

T e s t ( ) . Объ­

ект S a v i n g s A c c o u n t передается

методу

P r o c e s s A c c o u n t

( ) .

Преобразование

типа

из 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 не

вызывает проблем,

поскольку

объект ba

из­

начально

был объектом типа

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

 

 

 

 

 

 

 

Глава 12. Наследование

 

 

 

 

 

 

 

 

 

 

271

Однако со вторым вызовом P r o c e s s A c c o u n t () к типу S a v i n g s A c c o u n t не может быть разрешено . m u l a t e l n t e r e s t ( ) .

не все так гладко. Преобразован Объект ba не имеет метода Аса

Некорректное преобразование типов генерирует ошибку в процессе выпол ния программы (так называемую ошибку времени выполнения (run-time error Ошибки времени выполнения гораздо сложнее найти и исправить, чем ошибки времени компиляции . Что еще более неприятно, такая ошибка может произо ти не с вами, а с другим пользователем программы . Обычно особого восторга у пользователей такие ошибки не вызывают.

Ключевые слова is и as

Функция P r o c e s s A c c o u n t () работала бы корректно, если бы могла убедиться, что переданный ей объект действительно имеет тип S a v i n g s A c c o u n t , перед тем, как вы поднять преобразование. С# предоставляет для этого два ключевых слова — is и as.

Использование оператора is

Оператор i s получает объект в качестве левого аргумента и тип — в качестве правого. Оператор возвращает значение t r u e , если тип времени выполнения объекта слева совместим с типом справа. Этот оператор можно использовать для проверки корректности преобразования перед его выполнением .

Предыдущий пример можно модифицировать с применением оператора i s , что по зволит избежать ошибки времени выполнения .

p u b l i c s t a t i c v o i d P r o c e s s A m o u n t ( B a n k A c c o u n t b a n k A c c o u n t )

{

/ /

В н о с и м н а с ч е т б о л ь ш у ю с у м м у

b a n k A c c o u n t . D e p o s i t ( 1 0 0 0 0 . 0 0 М ) ;

/ /

Е с л и о б ъ е к т — S a v i n g s A c c o u n t . . .

i f

 

( b a n k A c c o u n t i s 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 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 ) 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 . A c c u m u l a t e l n t e r e s t ( ) ;

}

/ / В п р о т и в н о м с л у ч а е п р е о б р а з о в а н и е н е в ы п о л н я е т с я . / / О д н а к о п о ч е м у B a n k A c c o u n t — н е т о , ч т о в ы о ж и д а л и ?

/ / В о з м о ж н о , э т о к а к а я - т о о ш и б о ч н а я с и т у а ц и я ?

}

p u b l i c s t a t i c v o i d T e s t C a s t O

{

S a v i n g s A c c o u n t s a = n e w S a v i n g s A c c o u n t ( ) ; P r o c e s s A m o u n t ( s a ) ;

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

P r o c e s s A m o u n t ( b a ) ;

}

Добавление оператора is дает гарантию, что преобразование будет выполнено, толь­ ко если объект 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 . При

272

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

первом вызове функции P r o c e s s A m o u n t () оператор is вернет значение t r u e , но при

втором вызове,

когда в функцию будет передан объект B a n k A c c o u n t , оператор

is вер­

нет f a l s e , что

позволит избежать некорректного преобразования типов. Такая

версия

программы не генерирует ошибку времени выполнения.

 

С одной стороны, я настоятельно рекомендую вам защищать все выполняемые преобразования оператором is во избежание возможных ошибок времени вы­ полнения. С другой стороны, я рекомендую избегать приведения типов вообще.

Класс o b j e c t

Рассмотрим следующие связанные классы:

p u b l i c

c l a s s M y B a s e C l a s s

{ }

p u b l i c

c l a s s M y S u b C l a s s

: M y B a s e C l a s s { }

Соотношение между этими двумя классами позволяет программисту сделать следую­ щий тест времени выполнения:

p u b l i c c l a s s T e s t

{

p u b l i c s t a t i c v o i d

G e n e r i c F u n c t i o n ( M y B a s e C l a s s mc)

/ / Е с л и о б ъ е к т д е й с т в и т е л ь н о я в л я е т с я п о д к л а с с о м

M y S u b C l a s s m s c = m c a s M y B a s e C l a s s ;

i f ( m s c

! = n u l l )

{

 

 

/ /

. . .

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

// .

.

. п р о д о л ж е н и е . . .

В этом случае функция G e n e r i c F u n c t i o n () в состоянии различить подклассы класса M y B a s e C l a s s с п о м о щ ь ю оператора a s .

Но как различить два не связанных между собой класса с использованием того же

оператора

a s ? Оказывается, С#

производит все классы от одного

общего

предка —

базового класса o b j e c t . Таким

образом,

любой

класс, который

явно не

наследует

другой класс, наследует класс o b j e c t . А

значит,

два следующих

выражения объяв­

ляют классы с одним и тем же базовым классом o b j e c t :

 

 

c l a s s M y C l a s s l

: o b j e c t

{ }

 

 

 

 

c l a s s M y C l a s s 2

{ }

 

 

 

 

 

Общий базовый класс o b j e c t позволяет написать следующую обобщенную функцию:

p u b l i c

c l a s s

T e s t

 

 

 

 

 

{

 

 

 

 

 

 

 

p u b l i c

s t a t i c v o i d

 

 

 

 

 

G e n e r i c F u n c t i o n ( o b j e c t o )

 

 

 

 

{

 

 

 

 

 

 

 

M y C l a s s l m c l = о a s M y C l a s s l ;

 

 

 

Глава 12. Наследование

 

 

 

 

273

i f ( m c l ! = n u l l )

{

/ / И с п о л ь з у е м о б ъ е к т m c l , п о л у ч е н н ы й п р е о б р а з о в а н и е м

/ / . . .

}

}

Функция G e n e r i c F u n c t i o n ( ) может быть вызвана для объекта любого типа. В ы ражаясь поэтически, ключевое слово as извлекает жемчужину M y C l a s s l из устрицы o b j e c t .

Использование оператора as

Оператор as работает несколько иначе, чем оператор i s . Вместо возврата значения типа b o o l он преобразует объект слева от себя к типу справа, но при этом возвращение n u l l , если такое преобразование н е к о р р е к т н о — вместо генерации ошибки времени , выполнения при использовании обычного преобразования. Так что вы всегда должны проверять, н е равен л и результат работы оператора a s ссылке n u l l :

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 =

 

b a n k A c c o u n t

a s

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

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

! = n u l l )

 

 

{

/ / П р о д о л ж а е м р а б о т у с и с п о л ь з о в а н и е м S a v i n g s A c c o u n t

}

/ / В п р о т и в н о м с л у ч а е м ы н е м о ж е м и с п о л ь з о в а т ь э т о т о б ъ е к т и / / д о л ж н ы с г е н е р и р о в а т ь с о о б щ е н и е о б о ш и б к е с а м о с т о я т е л ь н о

Какой из операторов предпочесть? Вообще говоря, оператор as более эффективен

Он сразу выполняет преобразование, в то время как оператор is

требует

двух этапов

проверки с его использованием с последующим преобразованием типа.

 

К сожалению, as не работает с переменными типов-значений, так что вы

можете применять его с такими типами, как i n t , l o n g ,

d o u b l e

и подобный

В этом случае предпочтительней использовать оператор

i s .

 

Программа I n h e r i t a n c e E x a m p l e , с которой вы встречались ранее в этой главе применяет функции I n i t . . . () для инициализации объектов B a n k A c c o u n t и Saving! s A c c o u n t и приведения их в корректное состояние. Оснащение этих классов конструкторами, определенно, правильное решение, хотя и со своими сложностями.

Вызов конструктора по умолчанию базового класса

Когда создается подкласс, всякий

раз вызывается конструктор по умолча­

н и ю базового класса. Конструктор

подкласса автоматически вызывает кон­

структор базового класса, что видно на примере приведенной далее демон­ страционной программы .

274

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

l

/ / I n h e r i t i n g A C o n s t r u c t o r - д е м о н с т р а ц и я а в т о м а т и ч е с к о г о

 

/ / в ы з о в а к о н с т р у к т о р а п о у м о л ч а н и ю б а з о в о г о к л а с с а

 

ping S y s t e m ;

 

 

 

 

 

 

 

 

 

 

n a m e s р а с е I n h e r i t i n g A C o n s t r u c t o r

 

 

 

 

(

 

 

 

 

 

 

 

 

 

 

 

 

 

public class Program

 

 

 

 

 

 

 

 

 

{

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 )

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " С о з д а н и е

о б ъ е к т а

B a s e C l a s s " ) ;

 

 

 

B a s e C l a s s b e = n e w B a s e C l a s s ( ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ п С о з д а н и е

 

о б ъ е к т а

S u b c l a s s " ) ;

 

 

 

S u b c l a s s

s c

= n e w S u b c l a s s ( ) ;

 

 

 

 

 

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

 

 

 

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 ( ) ;

 

 

 

 

 

 

 

p u b l i c

 

c l a s s

B a s e C l a s s

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p u b l i c B a s e C l a s s O

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Console.WriteLine("Конструктор BaseClass");

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

p u b l i c

c l a s s

S u b c l a s s

: B a s e C l a s s

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

p u b l i c

S u b c l a s s ( )

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р

S u b c l a s s " ) ;

 

 

Конструкторы

B a s e C l a s s и

S u b c l a s s не

делают ничего, кроме вывода

строки на

экран. Создание

объекта

B a s e C l a s s приводит

к вызову конструктора по умолчанию

BaseClass . Создание

объекта

S u b c l a s s

приводит к вызову конструктора

по умолча­

нию B a s e C l a s s

перед тем, как вызывается собственно конструктор B a s e C l a s s .

 

Это ясно видно из вывода рассмотренной демонстрационной программы на экран.

Создание

 

о б ъ е к т а

B a s e C l a s s

 

 

 

 

 

Конструктор

B a s e C l a s s

 

 

 

 

 

 

Создание

 

о б ъ е к т а

S u b c l a s s

 

 

 

 

 

Конструктор

B a s e C l a s s

 

 

 

 

 

 

Конструктор

S u b c l a s s

 

 

 

 

 

 

Нажмите

< E n t e r > д л я

з а в е р ш е н и я

п р о г р а м м ы . . .

 

Иерархия наследуемых классов весьма напоминает этажи здания. Каждый класс, по­ строенный на основе другого класса, представляет собой новый, верхний этаж. То же от­ носится и к конструкторам классов: прежде чем будет вызван конструктор верхнего эта-

[пава 12. Наследование

275

жа для его построения, надо построить нижний этаж. Очевидна и причина этого: каждый класс сам отвечает за себя, а значит, подкласс не должен отвечать за инициализацию членов базового класса. B a s e C l a s s должен получить возможность сконструировать свои члены до того, как члены S u b c l a s s смогут к ним обратиться. Лошадь нужной ста вить перед телегой...

Передача аргументов конструктору базового класса

Подкласс вызывает конструктор по умолчанию базового класса, если только не ука зано иное, например, из конструктора подкласса, не являющегося конструктором умолчанию . Вот немного исправленная демонстрационная программа, иллюстрируют сказанное:

u s i n g S y s t e m ;

 

 

 

 

 

 

n a m e s p a c e

E x a m p l e

 

 

 

 

 

{

 

 

 

 

 

 

 

p u b l i c

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 i n ( s t r i n g [ ]

a r g s )

 

C o n s o l e . W r i t e L i n e ( " В ы з о в

S u b c l a s s ( ) " ) ;

 

S u b c l a s s

s c l

= n e w S u b c l a s s ( ) , •

 

 

C o n s o l e . W r i t e L i n e ( " \ п В ы з о в

S u b c l a s s ( i n t ) " ) ;

S u b c l a s s

s c 2

= n e w S u b c l a s s ( 0 ) ;

 

 

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

 

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 ( ) ;

 

 

 

 

p{ u b l i c

c l a s s B a s e C l a s s

 

 

 

 

p u b l i c

B a s e C l a s s ( )

 

 

 

 

{

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р B a s e C l a s s

( п о у м о л ч а н и ю ) " ) ;

p u b l i c

B a s e C l a s s ( i n t

i )

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р

B a s e C l a s s

( i n t ) " ) ;

}

 

 

 

 

 

 

 

p u b l i c c l a s s

S u b c l a s s

: B a s e C l a s s

 

 

{

 

 

 

 

 

 

 

p u b l i c

S u b c l a s s ( )

 

 

 

 

{

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р S u b c l a s s

( п о у м о л ч а н и ю ) " )

p u b l i c

S u b c l a s s ( i n t

i )

 

 

 

{

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р

S u b c l a s s

( i n t ) " ) ;

276

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

Выполнение программы приводит к следующему выводу на экран:

Вызов

S u b c l a s s ()

 

Конструктор

B a s e C l a s s

( п о у м о л ч а н и ю )

Конструктор

S u b c l a s s

( п о у м о л ч а н и ю )

Вызов

S u b c l a s s ( i n t )

 

Конструктор

B a s e C l a s s

( п о у м о л ч а н и ю )

Конструктор

S u b c l a s s

( i n t )

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

Данная демонстрационная программа сперва создает объект по умолчанию. Как и ожи­ далось, С# выполняет конструктор по умолчанию S u b c l a s s , который сначала передает управление конструктору по умолчанию B a s e C l a s s . Затем программа создает объект, передавая целочисленный аргумент. Как и предполагалось, теперь С# вызывает конст­ руктор S u b c l a s s ( i n t ) .

Этот конструктор, в свою очередь, вызывает конструктор по умолчанию B a s e C l a s s , как и в предыдущем примере, поскольку никакие данные базовому классу не передаются.

Указание конкретного конструктора базового класса

Конструктор подкласса может вызвать определенный конструктор базового класса с использованием ключевого слова b a s e .

Эта возможность аналогична способу, которым один конструктор может вы­ звать другой конструктор того же класса с применением ключевого слова t h i s (см. главу 11, "Классы") .

Рассмотрим, например, следующую демонстрационную программу:

/ / I n v o k e B a s e C o n s t r u c t o r - д е м о н с т р а ц и я т о г о ,

к а к п о д к л а с с

/ / м о ж е т в ы з в а т ь к о н с т р у к т о р б а з о в о г о к л а с с а п о с в о е м у

 

/ / в ы б о р у с и с п о л ь з о в а н и е м к л ю ч е в о г о с л о в а b a s e

 

u s i n g S y s t e m ;

 

 

 

n a m e s p a c e

I n v o k e B a s e C o n s t r u c t o r

 

 

{

 

 

 

 

p u b l i c

c l a s s B a s e C l a s s

 

 

 

p{ u b l i c

B a s e C l a s s O

 

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р B a s e C l a s s

( п о у м о л ч а н и ю ) " ) ;

p u b l i c

B a s e C l a s s ( i n t

i )

 

 

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р B a s e C l a s s ( { 0 } ) " ,

i ) ;

p u b l i c c l a s s S u b c l a s s

: B a s e C l a s s

 

 

{

 

 

 

 

p u b l i c

S u b c l a s s ( )

 

 

 

{

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р S u b c l a s s ( п о у м о л ч а н и ю ) " ) ;

Глава 12. Наследование

277

}

p u b l i c S u b c l a s s ( i n t i l , i n t i 2 ) : b a s e ( i l )

{

C o n s o l e . W r i t e L i n e ( " К о н с т р у к т о р S u b c l a s s ( { 0 } , { l } ) " , i l ,

i2) ;

}

}

p u b l i c

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 i n ( s t r i n g [ ]

a r g s )

 

{

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " В ы з о в

S u b c l a s s ( ) " ) ;

 

S u b c l a s s

s c l

= n e w S u b c l a s s ( ) ;

 

 

C o n s o l e . W r i t e L i n e ( " \ п В ы з о в

S u b c l a s s ( 1 ,

2 ) " ) ;

S u b c l a s s s c 2

= n e w S u b c l a s s ( 1 ,

2 ) ;

 

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

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 ( ) ;

}

}

}

Вывод программы выглядит следующим образом:

В ы з о в

S u b c l a s s ( )

 

К о н с т р у к т о р

B a s e C l a s s

( п о у м о л ч а н и ю )

К о н с т р у к т о р

S u b c l a s s

( п о у м о л ч а н и ю )

В ы з о в

S u b c l a s s ( 1 , 2 )

 

К о н с т р у к т о р

B a s e C l a s s ( 1 )

К о н с т р у к т о р

S u b c l a s s ( 1 , 2 )

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

Эта версия демонстрационной программы начинается так же, как и предыдущие при! меры — с создания объекта S u b c l a s s с применением конструкторов по умолчанию как

для класса

S u b c l a s s , так и

для B a s e C l a s s .

Второй

объект

создается

при п о м о щ и

выражения n e w S u b c l a s s ( 1 , 2 ) . С# вызы-

вает конструктор

S u b c l a s s

( i n t , i n t ) ,

в котором используется ключевое слово base

для передачи одного и з значений конструктору B a s e C l a s s ( i n t ) . П о всей видимости, S u b c l a s s передает первый аргумент для обработки базовому классу, а со вторым рабо­ тает самостоятельно.

Обновленный класс BankAccount

Демонстрационная программа C o n s t r u c t o r S a v i n g s A c c o u n t , имею­ щаяся на прилагаемом компакт-диске, представляет собой обновленную! версию демонстрационной программы S i m p l e 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 может передавать информацию

конст-1

руктору B a n k A c c o u n t . Здесь приведены только функция M a i n ()

и ука­

занные конструкторы.

 

278

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]