C# для чайников
.pdf
|
/ / В е р н о : |
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 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 |
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 |