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

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

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

Компилятор не может придать смысл ключевому слову null в исходном тек сте обобщенного класса, поскольку обобщенный класс может быть инстанци

рован для любых типов данных. Вот почему в исходном тексте метода De­ q u e u e () используется следующая конструкция:

r e t u r n d e f a u l t ( Т ) ; / / З н а ч е н и е n u l l дл я т и п а Т

Эта строка указывает компилятору, что нужно посмотреть, что собой представляет тип Т и вернуть верное значение n u l l для этого типа. В случае P a c k a g e , который в ка честве класса представляет собой ссылочный тип, верным возвращаемым значением у дет n u l l . Однако для некоторых других Т это значение может быть иным, и компилятор сможет верно определить, что именно следует вернуть.

Если вы думаете, что обобщенный класс P r i o r i t y Q u e u e достаточно ги бок, то на прилагаемом компакт-диске вы можете найти еще более гибкую версию обобщенного класса P r i o r i t y Q u e u e и познакомиться с некоторыми принципами объектно-ориентированного дизайна, обратившись к де-| монстрационной программе P r o g r a m m i n g T o A n l n t e r f а с е .

Часто методы в обобщенных классах также должны быть обобщенными. Вы уже ви­

дели это

в примере в предыдущем разделе. Метод D e q u e u e () в классе Priori­

t y Q u e u e

имеет возвращаемый тип Т. В этом разделе рассказывается, как можно ис­

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

мер, приведенный далее код показывает обобщенный метод Swap ( ) , разработанный дм обмена двух своих аргументов. Первый аргумент получает значение второго аргумента и наоборот (такой обмен проиллюстрирован на рис. 6.2 в главе 6, "Объединение дан­

н ы х —

классы и массивы"). Чтобы

увидеть, что он работает, объявите два параметра

Swap ()

с применением ключевого

слова r e f , чтобы аргументы типов-значений также

можно было передавать по ссылке, и посмотрите на результат работы метода (об исполь­ зовании ключевого слова r e f рассказывается в главе 7, "Функции функций").

Перед вами исходный текст демонстрационной программы, в которой объ­ является и используется метод Swap ( ) .

// G e n e r i c M e t h o d - м е т о д , к о т о р ы й может р а б о т а т ь с данными / / р а з н ы х т и п о в

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

n a m e s p a c e G e n e r i c M e t h o d

{

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

{

//Main -

//р а б о т а е т

// M a i n ( ) ;

s t a t i c v o i d

{

п р

о в е р к а д в у х

в е р с и й обобщенного

м е т о д а ; один

с

к л а с с о м на

том

же у р о в н е ,

ч т о

и функция

в т о р о й н а х о д и т с я

в обобщенном

к л а с с е

M a i n ( s t r i n g [ ]

a r g s )

 

 

360

Часть V. За базовыми классам»

/ / П р о в е р к а

обобщенного

м е т о д а и з

необобщенног о

к л а с с а

C o n s o l e . W r i t e L i n e ( " О б о б щ е н н ы й м е т о д из " +

 

 

 

 

 

 

 

 

" н е о б о б щ е н н о г о к л а с с а : \ п " ) ;

 

 

C o n s o l e . W r i t e L i n e ( " \ t n p o B e p K a

д л я

а р г у м е н т о в

тип а

i n t " ) ;

i n t

nOne

=

1;

 

 

 

 

 

 

 

 

 

 

 

 

 

i n t

nTwo

=

2;

 

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t n e p e f l :

nOne

=

{0},

nTwo

=

{1} " ,

 

 

 

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

/ / И н с т а н ц и р о в а н и е д л я i n t

 

 

 

 

 

 

 

 

 

Swap<int>(ref

nOne, ref

nTwo);

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ ь \ Ъ П о с л е :

nOne

=

{0},

nTwo

=

{ l } " ,

 

 

 

 

 

. n O n e , n T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t П p o в e p K a д л я а р г у м е н т о в " +

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

" т и п а

s t r i n g " ) ;

 

 

 

 

 

 

s t r i n g

sOne

=

" o n e " ;

 

 

 

 

 

 

 

 

 

 

s t r i n g

sTwo

=

" t w o " ;

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t f l o :

sOne =

{ o } , sTwo

=

{ l } " ,

 

 

 

 

 

sOne ,

s T w o ) ;

 

 

 

 

 

 

/ / И н с т а н ц и р о в а н и е д л я s t r i n g

 

 

 

 

 

 

 

Swap<string>(ref sOne,

ref

sTwo);

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t r i o a n e :

sOne

=

{ o } ,

sTwo

=

{ l } " ,

 

 

 

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ п О б о б щ е н н ы й м е т о д в " +

 

 

 

 

 

 

 

 

 

"обобщенном

к л а с с е " ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " ^ П р о в е р к а дл я а р г у м е н т о в т и п а i n t " ) ; C o n s o l e . W r i t e L i n e ( " \ t G e n e r i c C l a s s . S w a p с " +

 

 

" а р г у м е н т а м и т и п а i n t " ) ;

nOne

=

1;

nTwo

=

2;

GenericClass<int> intClass = new GenericClass<int>();

C o n s o l e . W r i t e L i n e ( " \ t \ t f l o :

nOne

=

{ o } , nTwo =

{ 1 } " ,

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

intClass.Swap(ref nOne,

ref

nTwo);

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t n o ^ e :

 

nOne

=

{ o } ,

nTwo

= { l } " ,

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t n p o B e p K a д л я а р г у м е н т о в " +

 

 

 

 

 

 

 

 

 

 

 

 

 

" т и п а

s t r i n g " ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t G e n e r i c C l a s s . S w a p с " +

 

 

 

 

" а р г у м е н т а м и т и п а s t r i n g " ) ;

 

sOne

=

" o n e " ;

 

 

 

 

 

 

 

 

 

 

 

sTwo

=

" t w o " ;

 

 

 

 

 

 

 

 

 

 

 

GenericClass<string> strClass

=

 

 

 

 

 

 

 

new

GenericClass<string>();

 

C o n s o l e . W r i t e L i n e ( " Д о :

sOne

=

{ o } ,

sTwo =

{ l } " ,

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

strClass.Swap(ref sOne,

ref

sTwo);

 

 

 

 

C o n s o l e . W r i t e L i n e ( " П о с л е :

sOne

=

{ o } ,

sTwo

=

{ l } " ,

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

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

п о л ь з о в а т е л я

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

(лава 15. Обобщенное программирование

361

} // e n d

Main

/ / s t a t i c

Swap

- обобщенный м е т о д в необобщенном к л а с с е

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

v o i d S w a p < T > ( r e f T l e f t s i d e ,

 

 

r e f T r i g h t S i d e )

{

T t e m p ;

t e m p = l e f t s i d e ;

 

l e f t s i d e =

r i g h t S i d e ;

 

r i g h t S i d e = t e m p ;

}

}// e n d P r o g r a m

/

/ G e n e r i c C l a s s

- обобщенный к л а с с с с о б с т в е н н ы м методом

//Swap

c l a s s G e n e r i c C l a s s < T >

 

 

 

 

{

 

 

 

 

 

 

 

 

//Swap '-

м е т о д

обобщенный, п о с к о л ь к у

п р и н и м а е т параметры

//

т и п а

Т;

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

ч т о

мы

не

можем

/ /

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

Swap<T>, и н а ч е

получим

п р е д у п р е ж д е н и е

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

p u b l i c v o i d S w a p ( r e f T l e f t s i d e ,

r e f T r i g h t S i d e )

{

 

 

 

 

 

 

 

 

 

T t e m p ;

 

 

 

 

 

 

 

t e m p = l e f t s i d e ;

 

 

 

 

 

l e f t s i d e

= r i g h t S i d e , -

 

 

 

 

r i g h t S i d e = t e m p ;

Первая версия Swap () в предыдущем примере (всего

в нем рассмотрены две вер­

сии) — статическая функция класса P r o g r a m , объявленная следующим образом:

p u b l i c s t a t i c v o i d S w a p < T > ( r e f T l e f t s i d e ,

r e f T r i g h t S i d e )

Объявление обобщенного метода схоже с объявлением обобщенного класса— за именем метода следуют обобщенные параметры наподобие <Т>. После этого можно ис­ пользовать Т в методе в качестве произвольного типа, включая параметры метода и воз­ вращаемый тип.

В примере функция M a i n () дважды вызывает статическую функцию Swap ( ) , спер­ ва инстанцируя ее для i n t , а затем для s t r i n g (в листинге эти места выделены полу­ жирным шрифтом). Вот как выглядят вызовы этих методов (все верно, инстанцирование

выполняется при вызове метода):

 

 

S w a p < i n t > ( r e f n O n e ,

 

 

r e f n T w o ) ;

/ / И н с т а н ц и р о в а н и е д л я

i n t

S w a p < s t r i n g > ( r e f s O n e ,

 

 

r e f s T w o ) ; / / И н с т а н ц и р о в а н и е д л я

s t r i n g

362

Часть V. За базовыми классами

При инстанцировании Swap () для i n t вы можете использовать i n t в качестве типа

^гументов в вызове.

Аналогично, при инстанцировании Swap () для s t r i n g можно

применять аргументы

типа s t r i n g . .

Обобщенный метод Swap () в обобщенном классе несколько отличается от описан­ ного метода. Эти отличия рассматриваются в следующем подразделе.

Обобщенные методы в обобщенных классах

В предыдущем примере имеется обобщенный класс G e n e r i c C l a s s , в котором со­ держится обобщенный метод Swap ( ) , объявленный следующим образом (здесь показан и заголовок класса, чтобы было понятно, откуда берется тип Т):

class G e n e r i c C l a s s < T > // П а р а м е т р <Т> и с п о л ь з у е т с я ниже в

//функции Swap()

public v o i d S w a p ( r e f

T l e f t s i d e ,

r e f T r i g h t S i d e ) . . .

 

Основное отличие между методом Swap ()

и его статическим аналогом в

классе

Program заключается в том,

что обобщенную параметризацию предоставляет

класс

GenericClass, так что метод Swap ()

в ней не нуждается (и более того, не может ее

использовать). В этой версии Swap ()

вы не найдете выражения <Т>, а сам Т применя­

ется только в самом методе Swap ( ) .

 

 

 

Кроме этого отличия, версии Swap ()

практически идентичны. Вот несколько вызо-

метода Swap () в функции Main ( ) :

 

 

G e n e r i c C l a s s < i n t > i n t C l a s s =

 

/ / С о з д а н и е о б ъ е к т а

new G e n e r i c C l a s s < i n t > ( ) ;

 

/ /

д л я i n t

intClass . Swap ( r e f n O n e , r e f

nTwo) ; //

Вызов е г о S w a p O

G e n e r i c C l a s s < s t r i n g > s t r C l a s s = new G e n e r i c C l a s s < s t r i n g > ( ) ;

/ / С о з д а н и е о б ъ е к т а / / д л я s t r i n g

StrClass. Swap ( r e f s O n e ,

r e f

sTwo) ; //

Вызов е г о S w a p O

В данном случае для типа

i n t

или

s t r i n g

инстанцируется сам класс. Тип Т

может использоваться в методе Swap ()

точно так же, как и в его версии в необоб­

щенном классе.

 

 

 

 

Ограничения для обобщенного метода

Вам могут понадобиться ограничения для обобщенного метода, с тем чтобы он мог принимать только определенные виды типов, отвечающих некоторым требованиям — ^какэто было сделано для класса P r i o r i t y Q u e u e ранее в настоящей главе. В этом слу­ чае вы должны объявить метод примерно таким образом:

s t a t i c v o i d S o r t < T > ( T [ ] t A r r a y ) w h e r e T : I C o m p a r a b l e < T > ( . . . }

Например, если методу необходимо сравнение параметров типа Т, то лучше потребо­ вать от Т реализации интерфейса I C o m p a r a b l e и указать это ограничение в описании обобщенного метода.

Шва 15. Обобщенное программирование 363

Вы уже встречались с обобщенными классами и методами, и вполне логично задана] вопросом — могут ли быть обобщенные интерфейсы! (Необобщенные интерфейсы рас сматривались в главе 14, "Интерфейсы и структуры".)

В этом разделе вы познакомитесь с примером, объединяющим обобщенные классы методы и интерфейсы.

Обобщенные и необобщенные интерфейсы

Давайте сравним обобщенные и необобщенные интерфейсы.

// Необобщенный

// Обобщенный

i n t e r f a c e I D i s p l a y a b l e

i n t e r f a c e I C e r t i f i a b l e < T >

{

{

v o i d D i s p l a y ( ) ;

v o i d C e r t i f y ( T c r i t e r i a ) ;

}

}

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

//Необобщенный

c l a s s M y C l a s s : I D i s p l a y a b l e . . .

//Обобщенный

c l a s s M y C l a s s : I C e r t i f i a b l e < M y C r i t e r i a > . . .

После этого следует завершить реализацию интерфейса в вашем классе:

//Необобщенный

c l a s s M y C l a s s : I D i s p l a y a b l e

{

p u b l i c v o i d D i s p l a y ( )

{

//Обобщенный

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

: I C e r t i f i a b l e < M y C r i t e r i a > / / И н с т а н ц и р о в а н и е

{

 

p u b l i c v o i d

C e r t i f y ( M y C r i t e r i a c r i t e r i a )

{

 

Обратите внимание, что когда вы реализуете обобщенный интерфейс в классе,] вы инстанцируете его обобщенную часть с использованием имени реального] типа, такого как M y C r i t e r i a .

Теперь вы можете видеть, почему интерфейс является обобщенным: он требует заме­ ны <Т>, используемого в качестве типа параметра или возвращаемого типа в одном или нескольких методах интерфейса. Другими словами, как и в случае обобщенного класса, вы указываете замещаемый тип для применения в обобщенных методах. Возможно, эти методы нужны для работы с различными типами данных, как и в случае класса коллек­ ции L i s t < T > или метода Swap<T> (Т i t e m l , Т i t e m 2 ) .

Часть V. За базовыми классами

Обобщенные интерфейсы — новинка, и я еще не рассматривал здесь все способы их _ использования. Основное их применение — в обобщенных коллекциях. Использование

Iобобщенной версии распространенного интерфейса С#, такого как I C o m p a r a b l e < T > , поможет избежать упаковки/распаковки типов-значений. Обобщенные интерфейсы мо­

гут также помочь реализовать такие вещи, как функции сортировки для применения коллекциями. (О том, что такое упаковка/распаковка, было рассказано в главе 14,

| "Интерфейсы и структуры".)

Приведенный далее пример достаточно абстрактный, поэтому он будет создаваться постепенно.

Использование (необобщенной) фабрики классов

Ранее в главе уже использовалась фабрика классов — хотя само назва­ ние "фабрика" вводится только сейчас — для генерации бесконечного потока объектов P a c k a g e со случайными приоритетами. Вот как вы­ глядит этот класс:

// P a c k a g e F a c t o r y я в л я е т с я ч а с т ь ю д е м о н с т р а ц и о н н о й программы // P r i o r i t y Q u e u e , н а х о д я щ е й с я на п р и л а г а е м о м к о м п а к т - д и с к е

//

P a c k a g e F a c t o r y -

нам нужен к л а с с ,

к о т о р ы й

з н а е т , к а к

//

создаются новые

п а к е т ы

нужного

нам

т и п а по

т р е б о в а н и ю ;

//

такой к л а с с н а з ы в а е т с я

ф а б р и к о

й

 

 

class P a c k a g e F a c t o r y

 

 

 

 

 

 

 

 

Random

r a n d

=

new

R a n d o m ( ) ;

//

Г е н е р а т о р

случайных

ч и с е л

/ / C r e a t e P a c k a g e

-

э т о т

м е т о д

фабрики в ы б и р а е т

случайный

// п р и о р и т е т , а з а т е м с о з д а е т п а к е т с э т и м п р и о р и т е т о м

public

P a c k a g e

C r e a t e P a c k a g e ( )

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

// В о з в р а щ а е т

с л у ч а й н о

выбранный п р и о р и т е т

п а к е т а .

Нам

//

нужны

з н а ч е н и я 0,

1

или

2

(меньшие

3)

 

 

i n t

nRand

= r a n d . N e x t ( 3 ) ;

 

 

 

 

 

/ / И с п о л ь з у е т с я д л я г е н е р а ц и и н о в о г о п а к е т а // П р и в е д е н и е к п е р е ч и с л е н и ю н е с к о л ь к о г р о м о з д к о , но

/ /

з а т о

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

/ /

к о н с т р у к ц и и s w i t c h

r e t u r n

new P a c k a g e ( ( P r i o r i t y ) n R a n d ) ;

Класс P a c k a g e F a c t o r y имеет один член-данные и один метод (фабрику легко реализовать и не как класс, а как метод — например, метод класса Program). Когда вы инстанцируете объект P a c k a g e F a c t o r y , он создает объект класса Random и сохраняет его в члене r a n d . R a n d o m — библиотечный класс С#, предназначенный для генерации случайных чисел. (Взгляните также на демонст­ рационную программу P a c k a g e F a c t o r y W i t h l t e r a t o r на прилагаемом компакт-диске.)

Использование PackageFactory

Для генерации объектов P a c k a g e со случайными приоритетами вызывается метод объ­ ЕКТА фабрики C r e a t e P a c k a g e ( ) , как показано в следующем фрагменте исходного текста:

Гшэ 15. Обобщенное программирование

365

P a c k a g e F a c t o r y

f a c t

=

new P a c k a g e F a c t o r y ( ) ;

 

 

I P r i o r i t i z a b l e

p a c k

=

f a c t . C r e a t e P a c k a g e ( ) ;

/ /

Обратите

 

 

 

// внимание

на

интерфей с

C r e a t e P a c k a g e () использует генератор случайных чисел для генерации случая го числа от 0 до 2 включительно, и применяет сгенерированное число в качестве приори тета нового объекта P a c k a g e , возвращаемого этим методом (и сохраняемого в пере менной типа P a c k a g e , а еще лучше — I P r i o r i t i z a b l e ) .

Еще немного о фабриках

Фабрики очень удобны для генерации большого количества тестовых дани! (фабрика не обязательно использует генератор случайных чисел — он потребо вался для конкретной демонстрационной программы P r i o r i t y Q u e u e ) .

Фабрики усовершенствуют программу, изолируя создание объектов. Каш раз при упоминании имени определенного класса в вашем исходном тексте! создаете зависимость (dependency) от этого класса. Чем больше таких завися мостей, тем больше степень связности классов, тем "теснее" они связаны дм с другом. Программистам давно известно, что следует избегать тесного CBHL вания. (Один из многих методов развязки (decoupling) заключается в примеви нии фабрик посредством интерфейсов, как, например, IPrioritizable! а не с использованием конкретных классов наподобие Package . ) Программ сты постоянно создают объекты непосредственно, с применением операм new, и это нормальная практика. Однако использование фабрик может сдем| код менее тесно связанным, а следовательно, более гибким.

Построение обобщенной фабрики

А если бы у вас был класс, который мог бы создавать любые необходима! вам объекты? Эту интересную концепцию достаточно легко спрограммирсвать, как видно из демонстрационной программы G e n e r i c l n t e r f a c e a прилагаемом компакт-диске.

/ /

G e n e r i c l n t e r f а с е - и с п о л ь з о в а н и е обобщенного

и н т е р ф е й с а

/ /

д л я

р е а л и з а ц и и обобщенной

фабрики

 

 

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

 

 

 

 

u s i n g S y s t e m . C o l l e c t i o n s . G e n e r i c ;

 

 

 

n a m e s p a c e G e n e r i c l n t e r f a c e

 

 

 

 

{

 

 

 

 

 

 

 

c l a s s

P r o g r a m

 

 

 

 

 

{

 

 

 

 

 

 

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 l o b б е з п а р а м е т р о в " ) ;

 

G e n e r i c F a c t o r y < B l o b > b l o b F a c t =

 

 

 

 

new G e n e r i c F a c t o r y < B l o b > ( ) ;

 

 

 

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

фабрики для

"

+

 

 

" п р о и з в о д с т в а S t u d e n t s , " +

 

 

" п а р а м е т р и з о в а н н ы х с т р о к о й " ) ;

 

G e n e r i c F a c t o r y l < S t u d e n t , s t r i n g > s t u F a c t =

 

 

new G e n e r i c F a c t o r y l < S t u d e n t ,

s t r i n g > ( ) ;

 

 

366

Часть V. За базовыми классам.

//

См. д о п о л н и т е л ь н ы е

к л а с с ы

на п р и л а г а е м о м

/ / к о м п а к т - д и с к е

 

 

 

 

/ /

Готовим

м е с т о

д л я

х р а н е н и я

о б ъ е к т о в

L i s t < B l o b >

b L i s t

=

new L i s t < B l o b > ( ) ;

S t u d e n t [ ]

s t u d e n t s

=

new S t u d e n t [ 1 0 ] ;

C o n s o l e . W r i t e L i n e ( " С о з д а н и е и с о х р а н е н и е о б ъ е к т о в : " ) ; f o r ( i n t i = 0; i < 10 ; i + + )

{

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

 

 

" в ы з о в к о н с т р у к т о р а б е з " +

 

 

" п а р а м е т р о в . " ) ;

 

 

B l o b b = b l o b F a c t . C r e a t e ( ) ;

 

 

b . n a m e = " b l o b " + i . T o S t r i n g ( ) ;

 

 

b L i s t . A d d ( b ) ;

 

 

 

C o n s o l e . W r i t e L i n e ( " ^ С о з д а н и е S t u d e n t с " +

 

 

" у с т а н о в к о й имени -

"

+

 

 

" в ы з о в

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

с

одним " +

 

 

" п а р а м е т р о м . " ) ;

 

 

s t r i n g

sName

= " s t u d e n t "

+ i . T o S t r i n g ( ) ;

s t u d e n t s [ i ]

= s t u F a c t . C r e a t e ( s N a m e ) ;

 

 

/ / . . .

с м .

полный т е к с т

н а п р и л а г а е м о м

к о м п а к т - д и с к е

}

 

/ /

Вывод

р е з у л ь т а т о в .

 

 

 

 

 

 

 

 

f o r e a c h ( B l o b b i n b L i s t )

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( b . T o S t r i n g ( ) ) ;

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

f o r e a c h ( S t u d e n t

s

i n s t u d e n t s )

 

 

 

 

 

 

{ C o n s o l e . W r i t e L i n e ( s . T o S t r i n g ( )

 

 

 

 

 

 

II

Ожидаем

п о д т в е р ж д е н и я

п о л ь з о в а т е л я

 

 

 

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 t u d e n t , B l o b

( с м .

 

также

к о м п а к т - д и с к )

//

B l o b

-

п р о с т о й

к л а с с

с к о н с т р у к т о р о м

по

умолчанию ( б е з

/ /

п а р а м е т р о в ,

 

п р е д о с т а в л я е м ы м С # ) .

Для

э к о н о м и и м е с т а к о д

//

опущен

( с м .

к о м п а к т - д и с к )

 

 

 

 

 

 

 

//

S t u d e n t

- к л а с с

с

к о н с т р у к т о р о м по

умолчанию

и

//

к о н с т р у к т о р о м с

одним п а р а м е т р о м

 

 

 

 

 

c l a s s S t u d e n t

:

ISettable<string>

//

Обобщенный

интерфейс

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/ / Ч а с т ь

к о д а

опущена,

с м .

к о м п а к т - д и с к

 

 

p u b l i c S t u d e n t ( )

{ }

 

/ / В ы

должны

о б е с п е ч и т ь

н а л и ч и е

 

 

 

 

 

 

 

/ / э т о г о к о н с т р у к т о р а

 

p u b l i c S t u d e n t ( s t r i n g

name)

/ /

к о н с т р у к т о р с

одним

{

 

 

 

 

 

 

 

 

// п а р а м е т р о м

 

 

t h i s . n a m e

 

name;

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

// Реализация ISettable

 

 

 

 

 

 

 

public void SetParameter(string name)

 

 

 

классами

 

 

 

 

 

 

 

 

 

 

 

 

 

 

367

this.name

=

name;

// T o S t r i n g ( )

- на к о м п а к т - д и с к е

}

 

 

// См. также

исходный т е к с т на к о м п а к т - д и с к е

// Интерфейсы ISettable, используемые фабриками interface ISettable<U>

{

void SetParameter(U u ) ;

}

interface ISettable2<U, V>

{ void SetParameter(U u, V v ) ;

}

//Фабрика для объектов с конструктором без параметров не

//требует реализации ISettable

class GenericFactory<T> where T : new()

{ public T Create()

{

return new T ( ) ;

}

}

//Фабрика для создания объектов с конструктором с одним

//параметром

class GenericFactoryl<T, U> where T : ISettable<U>, new()

{

//Создает новый объект типа Т с параметром U и

//возвращает Т

{

 

 

 

 

 

public Т

Create(U и)

 

 

 

 

Т t = new Т ( ) ;

 

 

 

 

t.SetParameter(и);

// Т должен реализовать ISettable,

 

// так что у него есть метод SetParameter()

return

t;

 

 

 

 

}

 

 

 

 

 

}

 

 

 

 

 

/ / Н а п р и л а г а е м о м к о м п а к т - д и с к е е с т ь к о д

фабрики

дл я

/ / с о з д а н и я

о б ъ е к т о в ,

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

т р е б у е т д в а

/ / п а р а м е т р а

 

 

 

 

}

 

 

 

 

 

Демонстрационная программа

G e n e r i c l n t e r f а с е

на

самом

деле создает два

обобщенных класса.

 

 

 

 

Фабрику для создания объектов, которые имеют только конструктор по умолча­ нию, без параметров.

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

368

Часть V. За базовыми классами

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

Обобщенное создание объектов

Создание объектов, конструктор которых не имеет параметров, очень простое, так как при этом отсутствуют аргументы, о которых вам пришлось бы беспокоиться (за ис­ ключением <Т> — параметра типа):

// Метод

C r e a t e ( ) к л а с с а G e n e r i c F a c t o r y < T >

public Т

C r e a t e ()

{

return new Т(); // Вызов конструктора без параметров

}

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

// Т

должен быть

к л а с с о м

MyBase или е г о п о д к л а с с о м

class

MyClass<T> :

w h e r e

Т:

MyBase

// Т

должен р е а л и з о в ы в а т ь

I M y l n t e r f a c e

class

M y C l a s s < T > :

w h e r e

T : I M y l n t e r f a c e

// T

может быть т о л ь к о

ссылочным типом

class

MyClass<T> :

w h e r e

Т : c l a s s

// Т

может быть т о л ь к о

т и п о м - з н а ч е н и е м

class

M y C l a s s < T > :

w h e r e

Т : s t r u c t

// Т

должен и м е т ь

к о н с т р у к т о р б е з п а р а м е т р о в

class

MyClass<T> :

w h e r e

Т.-

new( )

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

Ограничение new () представляет собой требование для каждого обобщенного класса или метода, которые хотят создавать объекты типа Т. Однако у этого ограничения нет версий наподобие new (U) или new (U, V) для конструкторов с параметрами.

Обобщенный интерфейс также может иметь ограничения:

i n t e r f a c e I S e t t a b l e < T > : w h e r e Т : n e w ( ) . . .

Поиск обобщенного решения

Вопрос в том, как поступить в случае, когда конструктору надо передавать парамет­ ры. Фабрика объектов, конструктор которых требует одного параметра, должна исполь­

зовать в методе C r e a t e ()

код наподобие приведенного:

 

public

Т

C r e a t e ( U

и)

/ / и — а р г у м е н т , к о т о р ы й мы

хотим

 

 

 

 

/ / п е р е д а т ь к о н с т р у к т о р у

 

| {

 

 

 

 

 

Т t

=

new Т ( ) ;

/ / Н о new Т( ) не может п р и н и м а т ь

а р г у м е н т ы

. . .

 

 

/ / И ч т о т е п е р ь ? . . .

 

Глава

15.

Обобщенное программирование

369

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