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

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

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

/ / 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 a v i n g s A c c o u n t к а к

/ / вир,

B a n k A c c o u n t ; н е и с п о л ь з у е т в и р т у а л ь н ы е м е т о д ы , н о

/ / к о р р е к т н о р е а л и з у е т к о н с т р у к т о р ы

using

S y s t e m ;

namespace 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

{

/ / B a n k A c c o u n t - м о д е л ь б а н к о в с к о г о с ч е т а с н о м е р о м с ч е т а / / ( н а з н а ч а е м ы м п р и с о з д а н и и ) и б а л а н с о м

p u b l i c c l a s s B a n k A c c o u n t

{

/ / Н о м е р а с ч е т о в н а ч и н а ю т с я с 1 0 0 0 и п о с л е д о в а т е л ь н о / / у в е л и ч и в а ю т с я

p u b l i c s t a t i c i n t n N e x t A c c o u n t N u m b e r = 1 0 0 0 ; / / Н о м е р с ч е т а и б а л а н с д л я к а ж д о г о о б ъ е к т а p u b l i c i n t n A c c o u n t N u m b e r ;

p u b l i c d e c i m a l m B a l a n c e ; / / К о н с т р у к т о р ы

p u b l i c B a n k A c c o u n t ( ) : t h i s ( 0 )

{

{

public BankAccount(decimal mlnitialBalance)

{

n A c c o u n t N u m b e r = + + n N e x t A c c o u n t N u m b e r ; m B a l a n c e = m l n i t i a l B a l a n c e ;

// . . . О с т а л ь н ы е м е т о д ы . . .

}

/ / S a v i n g s A c c o u n t - б а н к о в с к и й с ч е т с н а ч и с л е н и е м // п р о ц е н т о в

p u b l i c c l a s s 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

{

p u b l i c d e c i m a l m l n t e r e s t R a t e ;

/ / К о н с т р у к т о р ы . П р о ц е н т н а я с т а в к а в ы р а ж а е т с я ч и с л о м о т

/ / 0 д о

1 0 0

p u b l i c

S a v i n g s A c c o u n t ( d e c i m a l m l n t e r e s t R a t e )

: t h i s ( m l n t e r e s t R a t e , 0 )

p u b l i c S a v i n g s A c c o u n t ( d e c i m a l d e c i m a l

m l n t e r e s t R a t e , m l n i t i a l )

: b a s e ( m l n i t i a l )

{

t h i s . m l n t e r e s t R a t e = m l n t e r e s t R a t e / 1 0 0 ;

I I . . . О с т а л ь н ы е м е т о д ы . . .

}

p u b l i c c l a s s P r o g r a m

{

/ / D i r e c t D e p o s i t - p u b l i c s t a t i c v o i d

а в т о м а т и ч е с к и й в н о с д е н е г н а с ч е т D i r e c t D e p o s i t ( B a n k A c c o u n t b a ,

d e c i m a l m P a y )

{

b a . D e p o s i t ( m P a y ) ;

279

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

}

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 = n e w B a n k A c c o u n t ( 1 0 0 ) ;

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

1 0 0 ) ;

 

C o n s o l e . W r i t e L i n e ( " С ч е т

{ о } " ,

 

b a . T o B a n k A c c o u n t S t r i n g ( ) ) ;

/ / 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 ( 1 2 . 5 M ) ;

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

1 0 0 ) ;

 

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

 

C o n s o l e . W r i t e L i n e ( " С ч е т

{ о } " ,

s a . T o S a v i n g s A c c o u n t S t r i n g ( ) ) ; / / О ж и д а е м п о д т в е р ж д е н и я п о л ь з о в а т е л я

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

}

}

}

Класс B a n k A c c o u n t определяет два конструктора: один, который получает началь ное значение баланса, и конструктор по умолчанию, не получающий никаких аргуи тов. Чтобы избежать дублирования кода конструкторов, конструктор по умолчанию зывает конструктор с передаваемым начальным значением баланса посредством

вого слова

t h 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 , принимающий

качестве аргументов величину процентной ставки и начальное значение баланса, перед вая в качестве последнего 0. В свою очередь, этот конструктор наиболее o6mei о вс передает начальное значение баланса соответствующему конструктору B a s e C l a s s это отражено на диаграмме на рис. 12.1).

Рис. 12.1. Передача параметров в цепочке вызовов конструкторов

П р о г р а м м а м о д и ф и ц и р о в а н а т а к и м о б р а з о м , ч т о б ы и з б е ж а т ь вызова внутренних

ф у н к ц и й I n i t . . . ( ) , з а м е н и в

их а в т о м а т и ч е с к и

в ы з ы в а е м ы м и конструкторами . Вы­

вод это й д е м о н с т р а ц и о н н о й

п р о г р а м м ы н и ч е м

не отличается от в ы в о д а ее предше

с т в е н н и ц ы .

 

 

280

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

С# предоставляет также метод, обратный конструктору и именуемый деструктором. Деструктор имеет то же имя, что и имя класса, но предваренное символом тильды (~). Например, метод - B a s e C l a s s ( ) является деструктором класса B a s e C l a s s ( ) .

С# вызывает деструктор, когда перестает использовать объект. Деструктор по умол­ чанию — единственный, который может иметь класс, поскольку деструктор не вызыва­ ется явно. Кроме того, деструктор всегда виртуален (о виртуальных методах будет рас­ сказано в главе 13, " П о л и м о р ф и з м " ) .

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

Сборка мусора и деструкторы С#

Деструктор в С# менее полезен, чем в ряде других объектно-ориентированных языков программирования, таких как С + + , поскольку в С# используется нектерминистическая деструкция. Этот термин и его важность требуют определенных

пояснений.

Память для объекта выделяется из кучи при выполнении команды n e w , например, new S u b c l a s s ( ) . Блок памяти остается зарезервированным до тех пор, пока имеет­ ся хоть одна корректная ссылка на эту память. Вы можете иметь несколько перемен­ ных, ссылающихся на один и тот же объект.

О памяти говорят, что она "недостижима", когда из области видимости выходит по­ следняя ссылка на нее. Другими словами, никто не в состоянии обратиться к блоку памяти после утраты последней ссылки на нее.

Когда блок памяти становится недостижимым, С# не предпринимает никаких специ­ фических действий. В фоновом режиме выполняется низкоприоритетный системный процесс, который проводит поиск недостижимых блоков памяти. Такой " с б о р щ и к му­ сора" запускается, когда в работе программы наступает затишье — чтобы не повлиять отрицательно на ее производительность. Когда сборщик мусора находит недостижи­ мый блок памяти, он возвращает его в кучу.

Обычно с б о р щ и к

м у с о р а

н е з а

м е т н о р а б о т а е т в

ф о н о в о м р е ж и м е и получает

управление тольк о

на

к о р о т к и е

п е р и о д ы в р е м е н и ,

когда н а ч и н а е т чувствоваться

нехватка памяти .

 

 

 

 

 

Деструкторы С#, такие

как

~ B a s e C l a s s ( ) , являются недетерминистическими, по­

скольку не вызываются до тех пор, пока объект не будет подобран сборщиком мусора, а это может случиться через продолжительное время после того, как объект переста­ нет использоваться. М о ж е т даже возникнуть ситуация, когда программа завершится до того, как будет выполнена очередная сборка мусора, и в этом случае деструктор не будет вызван вообще. Недетерминистический означает, что вы не можете предска­ зать, когда объект будет уничтожен сборщиком мусора. М о ж е т пройти немало време­ ни до того, как объект будет подобран сборщиком мусора и будет вызван деструктор этого объекта.

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

281

О с н о в н ой вывод — программист на С# не может полагаться на автоматический вызов деструктора, как в таком языке, как С + + , так что деструкторы в С# использую крайне редко. В С# имеются другие способы вернуть системе захваченные ресурсов которые больше не нужны, — с применением метода D i s p o s e ( ) , изучение которые увы, выходит за рамки настоящей книги.

282

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

Глава 13

Полиморфизм

>Скрывать или перекрывать методы базового класса?

>Реально ли создание абстрактных классов?

>Объявление абстрактного метода

>Создание новой иерархии поверх существующей

>Защита класса от наследования

аследование позволяет одному классу "приспособить" члены другого класса.

Таким

образом,

можно создать класс 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 . Однако

этого недостаточно

для имитации объектов реального мира.

Вернитесь к главе 12, "Наследование", если вам требуется освежить свои зна­ ния о наследовании.

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

распродаже... Хотя нет, именно последнее наверняка беспокоит вас больше всего ...

 

Для обычного потребителя отличия м и к р о в о л н о в о й печи от

о б ы ч н о й не так

важ­

ны— лишь бы они обе могли готовить л ю б и м ы е блюда, но

если взглянуть на

это

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

Как описывалось в главе 7, " Ф у н к ц и и ф у н к ц и й " , две или большее число функций могут иметь одинаковые имена — л и ш ь бы отличались количества и/или типы их ар­ гументов.

Простейший случай перегрузки функции

Д ве функции с одинаковым именем называются перегруженными.

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

p u b l i c

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

 

{

 

 

 

p u b l i c

s t a t i c

v o i d A F u n c t i o n O

 

/ / Н е к о т о р ы е д е й с т в и я

 

p u b l i c

s t a t i c

v o i d A F u n c t i o n ( i n t )

 

/ / Н е к о т о р ы е д р у г и е д е й с т в и я

 

p u b l i c

s t a t i c

v o i d A F u n c t i o n ( d o u b l e d )

/ / Н е к о т о р ы е д е й с т в и я , о т л и ч н ы е о т п е р в ы х д в у х

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 )

A F u n c t i o n ( ) ; A F u n c t i o n ( 1 ) ;

A F u n c t i o n ( 2 . 0 ) ;

}

}

C# в состоянии различать эти методы по их аргументам. Каждый из вызовов в фун| ции M a i n () обращается к своей функции.

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

Различные классы, различные методы

Не удивительно, что класс, которому принадлежит функция, также становится чася ее расширенного имени . Рассмотрим следующий фрагмент исходного текста:

p u b l i c

c l a s s

M y C l a s s

{

 

 

 

p u b l i c

s t a t i c

v o i d A F u n c t i o n O ;

p u b l i c

v o i d A M e t h o d ( ) ;

}

 

 

 

p u b l i c

c l a s s

U r C l a s s

{

 

 

 

p u b l i c

s t a t i c

v o i d A F u n c t i o n O ;

p u b l i c

v o i d A M e t h o d ( ) ;

}

 

 

 

p u b l i c c l a s s

P r o g r a m

284

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

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 )

{

U r C l a s s . A F u n c t i o n ( ) ; / / / / В ы з о в ф у н к ц и и - ч л е н а M y C l a s s m c O b j e c t = n e w m c O b j e c t . A M e t h o d ( ) ;

В ы з о в с т а т и ч е с к о й ф у н к ц и и M y C l a s s . A M e t h o d ( )

M y C l a s s ( ) ;

Имя класса является частью расширенного имени функции, так что для С# очевидно, какую именно функцию A F u n c t i o n () или метод A M e t h o d () вызывать в каждом кон­ кретном случае.

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

Итак, метод одного класса может перегружать другой метод того же класса, если ис­ пользует другие аргументы. Метод также может перегружать метод базового класса. Пе­ регрузка метода базового класса известна как сокрытие метода.

Предположим, ваш банк проводит новую политику, в соответствии с которой снятие с депозитного счета отличается от других типов снятия со счета. Предположим для кон­ кретности, что каждое снятие со счета обходится вкладчику в сумму 1.50.

При использовании функционального подхода вы можете реализовать эту политику посредством переменной-флага в классе, который бы указывал, принадлежит ли объект типу 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 . В этом случае метод снятия со счета дол­ га проверять значение флага, чтобы выяснить, следует ли снимать дополнительные

.50, как показано в следующем фрагменте исходного текста,

public c l a s s B a n k A c c o u n t

{

p r i v a t e d e c i m a l m B a l a n c e ;

p r i v a t e b o o l i s S a v i n g s A c c o u n t ;

/ /

Н а ч а л ь н ы й б а л а н с и ф л а г , у к а з ы в а ю щ и й , я в л я е т с я л и с ч е т

/ /

д е п о з и т н ы м и л и н е т

p u b l i c B a n k A c c o u n t ( d e c i m a l m l n i t i a l B a l a n c e ,

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

{

m B a l a n c e = m l n i t i a l B a l a n c e ;

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

)

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 A m o u n t )

{

// Е с л и с ч е т д е п о з и т н ы й . . .

i f

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

{

 

 

/ / . . . с н и м а е м л и ш н и е 1 . 5 0

 

m B a l a n c e - = 1 . 50М, -

}

/ / Д а л е е о б ы ч н ы й к о д i f ( m A m o u n t T o W i t h d r a w

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

>m B a l a n c e )

{

m A m o u n t T o W i t h d r a w = m B a l a n c e ;

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

285

m B a l a n c e - = m A m o u n t T o W i t h d r a w ;

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

}

}

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

{

p u b l i c v o i d S o m e F u n c t i o n ( )

{

/ / С о з д а е м д е п о з и т н ы й с ч е т

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 ( 0 , t r u e ) ;

}

.}

В а ш а функция должна указывать, какой именно счет создается, путем передачи полнительного аргумента конструктору B a n k A c c o u n t . Конструктор сохраняет файл затем используемый в методе W i t h d r a w () Для снятия дополнительной суммы 1.50.

Объектно - ориентированный подход скрывает метод W i t h d r a w () базом го класса B a n k A c c o u n t н о в ы м методом с тем же и м е н е м в классе Sat i n g s A c o o u n t , как показано в приведенной далее демонстрационной программе .

/ /

H i d i n g W i t h d r a w a l

/ /

м е т о д о м п о д к л а с с а

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

- с о к р ы т и е м е т о д а б а з о в о г о к л а с с а с т е м ж е и м е н е м

n a m e s p a c e H i d i n g W i t h d r a w a l

{

/ / B a n k A c c o u n t p u b l i c c l a s s

{

- б а з о в ы й б а н к о в с к и й с ч е т B a n k A c c o u n t

p r o t e c t e d d e c i m a l m B a l a n c e ;

p u b l i c

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

m B a l a n c e = m l n i t i a l B a l a n c e ;

p u b l i c

d e c i m a l B a l a n c e

g e t { r e t u r n m B a l a n c e ; }

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 A m o u n t )

d e c i m a l m A m o u n t T o W i t h d r a w = m A m o u n t ; i f ( m A m o u n t T o W i t h d r a w > B a l a n c e )

{

m A m o u n t T o W i t h d r a w = B a l a n c e ;

}

m B a l a n c e - = m A m o u n t T o W i t h d r a w ; r e t u r n m A m o u n t T o W i t h d r a w ;

286

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

// S a v i n g s A c c o u n t - б а н к о в с к и й с ч е т с н а ч и с л е н и е м // п р о ц е н т о в

p u b l i c c l a s s 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

{

p u b l i c d e c i m a l m l n t e r e s t R a t e ;

/ / S a v i n g s A c c o u n t - п р о ц е н т н а я с т а в к а п е р е д а е т с я к а к // ч и с л о от 0 до 10 0

p u b l i c S a v i n g s A c c o u n t ( d e c i m a l m l n i t i a l B a l a n c e , d e c i m a l m l n t e r e s t R a t e )

:b a s e ( m l n i t i a l B a l a n c e )

{

t h i s . m l n t e r e s t R a t e = m l n t e r e s t R a t e / 1 0 0 ;

/ / A c c u m u l a t e l n t e r e s t - н а ч и с л е н и е п р о ц е н т о в p u b l i c v o i d A c c u m u l a t e l n t e r e s t ( )

{

m B a l a n c e = B a l a n c e + ( B a l a n c e * m l n t e r e s t R a t e ) ;

/ / W i t h d r a 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 ) ;

 

 

 

 

/ / Т е п е р ь с н и м а е м с о с ч е т а к а к о б ы ч н о

 

 

r e t u r n b a s e . W i t h d r a w ( m W i t h d r a w a l ) ;

 

 

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

 

 

 

 

/ / С о з д а е м б а н к о в с к и й с ч е т , с н и м а е м 1 0 0 , в ы в о д и м

/ / р е з у л ь т а т

 

 

 

 

 

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

 

 

 

b a . W i t h d r a w ( 1 0 0 M ) ;

 

 

 

 

/ / Д е л а е м т о ж е с д е п о з и т н ы м с ч е т о м

 

 

s a

=

ne~w

S a v i n g s A c c o u n t ( 2 0 0 М ,

1 2 ) ;

 

 

s a . W i t h d r a w ( l O O M ) ;

 

 

 

 

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

 

 

 

287

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

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 : С } " ,

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

В этом случае

функция M a i n ()

создает объект

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

начальным балан

сом 200 и снимает с него 100. Затем те же действия выполняются с

объектом

Saving'

s A c c o u n t . Когда

функция M a i n ( )

снимает деньги

со счета базового класса,

метод

B a n k A c c o u n t . W i t h d r a w () снимает только указанную сумму (но не более суммы на счету). Когда же функция M a i n () снимает деньги с депозитного счета, метод Saving

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

снимает дополнительную сумму, равную

1.50.

Обратите внимание, что метод 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 ( ) , а не

работает непосред

ственно с балансом. Если это возможно — пусть базовый класс сам работает» своими членами-данными.

Чем сокрытие лучше проверки флага

На первый взгляд добавление флага в метод 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 не должен беспокоиться о деталях работы S a v i n g s A c c o u n t . Го воря формально, это нарушает принцип инкапсуляции. Базовый класс не должен ничего знать о своих потомках.

Все сказанное приводит ко второй, более сложной проблеме. Предположим, банки по очереди вводит новые с ч е т а — например, C h e c k i n g A c c o u n t , C D A c c o u n t , TBil- l 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 () начинает выглядеть слишком сложным . Каждый новый вид счета приводит, ко все большим изменениям этого метода.

Такое решение совершенно не подходит. Классы должны отвечать сами за себя.

Случайное сокрытие метода базового класса

Метод базового класса может оказаться скрытым случайно. Пусть, например, имеется метод V e h i c l e . T a k e O f f ( ) , который начинает движение транспортного средства. Поз­ же кто-то может расширить класс V e h i c l e , создав класс A i r p l a n e . Понятно, что метод T a k e O f f () этого класса совершенно не тот же, что у класса V e h i c l e . Очевидно, что это случай ложной тождественности — два метода не имеют ничего общего, кроме имени.

К счастью, С# в состоянии обнаружить такую проблему.

288

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

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