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

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

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

p q . E n q u e u e ( p a c k ) ;

}

C o n s o l e . W r i t e L i n e ( " Ч т о п о л у ч и л о с ь : " ) ; i n t n T o t a l = p q . C o u n t ;

C o n s o l e . W r i t e L i n e ( " П о л у ч е н о п а к е т о в : { о } " , n T o t a l ) ; C o n s o l e . W r i t e L i n e ( " И з в л е к а е м с л у ч а й н о е к о л и ч е с т в о " +

 

 

 

 

" п а к е т о в : 0-2 0 : ") ;

i n t

numToRemove

=

r a n d . N e x t ( 2 0 ) ;

 

C o n s o l e . W r i t e L i n e ( " ^ И з в л е к а е м

{0}

п а к е т о в " ,

 

 

 

 

numToRemove);

 

 

f o r

( i n t i

= 0;

i

< numToRemove;

i + + )

{

 

 

 

 

 

 

p a c k = p q . D e q u e u e ( ) ;

 

 

i{f

( p a c k

! = n u l l )

 

 

 

C o n s o l e . W r i t e L i n e ( " \ ъ \ Ь Д о с т а в к а п а к е т а " +

 

 

 

 

" с п р и о р и т е т о м { о } " ,

 

 

 

 

p a c k . P r i o r i t y ) ;

}

 

 

 

 

 

 

}

 

 

 

 

 

 

/ / С к о л ь к о п а к е т о в " д о с т а в л е н о "

 

 

C o n s o l e . W r i t e L i n e ( " Д о с т а в л е н о {0}

п а к е т о в " ,

 

 

 

 

n T o t a l - p q . C o u n t ) ;

/ /

Ожидаем

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

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 r i o r i t y - в м е с т о ч и с л о в ы х п р и о р и т е т о в н а п о д о б и е 1, 2,

/ / 3 ,

. . . и с п о л ь з у е м

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

enum P r i o r i t y //

Об

enum мы п о г о в о р и м позже

{

 

 

 

Low,

Medium,

H i g h

 

}

/ / 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 ,

/ /

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

э т о т

и н т е р ф е й с

 

 

i n t e r f a c e I P r i o r i t i z a b l e

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

// Пример

с в о й с т в а в и н т е р ф е й с е

 

 

 

 

 

P r i o r i t y P r i o r i t y { g e t ;

}

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

/ / P r i o r i t y Q u e u e

-

обобщенный

к л а с с о ч е р е д и с

п р и о р и т е т а м и ;

//

типы данных,

д о б а в л я е м ы х

в

о ч е р е д ь ,

о б я з а н ы

/ / р е а л и з о в ы в а т ь и н т е р ф е й с I P r i o r i t i z a b l e

 

c l a s s P r i o r i t y Q u e u e < T >

 

 

 

 

 

 

 

 

w h e r e T

:

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

//

< - -

э т о т

момент мы

 

 

 

 

 

 

 

 

/ /

обсудим

позже

{ / / Q u e u e s - т р и в н у т р е н н и е ( о б о б щ е н н ы е ! ) о ч е р е д и

 

p r i v a t e Queue<T>

q u e u e H i g h

=

new

Q u e u e < T > ( ) ;

 

p r i v a t e Queue<T>

q u e u e M e d i u m =

new

Q u e u e < T > ( ) ;

 

p r i v a t e Queue<T>

queueLow

 

=

new

Q u e u e < T > ( ) ;

350

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

/ / E n q u e u e - Д о б а в л я е т T в о ч е р е д ь в с о о т в е т с т в и и с

//п р и о р и т е т о м

p u b l i c v o i d E n q u e u e ( Т i t e m )

{

s w i t c h

( i t e m . P r i o r i t y ) / / Т р е б у е т р е а л и з а ц и и

{

/ /

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

c a s e P r i o r i t y . H i g h :

 

q u e u e H i g h . E n q u e u e ( i t e m ) ;

 

b r e a k ;

 

 

c a s e P r i o r i t y . L o w :

 

q u e u e L o w . E n q u e u e ( i t e m ) ;

 

b r e a k ;

 

 

c a s e P r i o r i t y . M e d i u m :

 

q u e u e M e d i u m . E n q u e u e ( i t e m ) ;

b r e a k ;

 

 

d e f a u l t :

 

 

t h r o w

new

 

A r g u m e n t O u t O f R a n g e E x c e p t i o n (

i t e m . P r i o r i t y . T o S t r i n g ( ) ,

"Неверный п р и о р и т е т в

P r i o r i t y Q u e u e . E n q u e u e " ) ;

}1

/ / D e q u e u e - и з в л е ч е н и е T из о ч е р е д и с наивысшим

//п р и о р и т е т о м

p u b l i c Т D e q u e u e ( )

 

 

 

 

 

{

 

 

 

 

 

 

 

//

П р о с м а т р и в а е м

о ч е р е д ь с

наивысшим

п р и о р и т е т о м

Queue<T>

q u e u e T o p

=

T o p Q u e u e ( ) ;

 

 

//

Очередь не п у с т а

 

 

 

 

if ( q u e u e T o p != n u l l && q u e u e T o p . C o u n t > 0)

 

 

r e t u r n

q u e u e T o p . D e q u e u e ( ) ; / / Возвращаем

первый

 

 

 

 

 

/ / э л е м е н т

 

}

 

 

 

 

 

 

 

/ /

Если

в с е о ч е р е д и

п у с т ы ,

в о з в р а щ а е м

n u l l

( з д е с ь

//

можно

с г е н е р и р о в а т ь и с к л ю ч е н и е )

 

 

r e t u r n d e f a u l t ( Т ) ;

/ / Что э т о

— м ы р а с с м о т р и м позже

//TopQueue - н е п у с т а я

о ч е р е д ь с

наивысшим п р и о р и т е т о м

p r i v a t e Queue<T> T o p Q u e u e ( )

 

{

 

if ( q u e u e H i g h . C o u n t > 0)

 

r e t u r n q u e u e H i g h ;

 

if ( q u e u e M e d i u m . C o u n t > 0)

 

r e t u r n q u e u e M e d i u m ;

 

if ( q u e u e L o w . C o u n t > 0)

 

r e t u r n q u e u e L o w ;

:

- r e t u r n q u e u e L o w ;

// Очередь с высоким / / п р и о р и т е т о м п у с т а ? // Очередь со с р е д н и м

/ / п р и о р и т е т о м

п у с т а ?

//

Очередь с н и з к и м

/ /

п р и о р и т е т о м

п у с т а ?

// Все о ч е р е д и пусты

/ / I s E m p t y - П р о в е р к а , п у с т а ли о ч е р е д ь

p u b l i c b o o l I s E m p t y ( )

{ ,

/ / t r u e , е с л и в с е о ч е р е д и п у с т ы

r e t u r n ( q u e u e H i g h . C o u n t = = 0 ) &

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

351

( q u e u e M e d i u m . C o u n t = = 0 ) & ( q u e u e L o w . C o u n t = = 0 ) ;

/}/ C o u n t - С к о л ь к о в с е г о э л е м е н т о в

p u b l i c i n t C o u n t / /

Р е а л и з у е м к а к

{

//

д л я ч т е н и я

 

g e t { r e t u r n q u e u e H i g h . C o u n t

+

 

q u e u e M e d i u m . C o u n t +

 

q u e u e L o w . C o u n t ;

}

в о в с е х о ч е р е д я х ?

}

 

с в о й с т в о т о л ь к о

 

}

}

/ / P a c k a g e

- пример

к л а с с а ,

который может быть р а з м е щ е н в

// о ч е р е д и с п р и о р и т е т а м и

 

c l a s s P a c k a g e : I P r i o r i t i z a b l e

{

 

 

 

p r i v a t e

P r i o r i t y

p r i o r i t y ;

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

 

 

p u b l i c P a c k a g e ( P r i o r i t y

p r i o r i t y )

{

 

 

 

t h i s . p r i o r i t y =

p r i o r i t y ;

}

/ / P r i o r i t y - в о з в р а щ а е т п р и о р и т е т п а к е т а ; т о л ь к о д л я / / ч т е н и я

p u b l i c

P r i o r i t y

P r i o r i t y

 

 

g e t

{ r e t u r n p r i o r i t y ; }

 

 

}

 

 

 

 

/ / А также методы

T o A d d r e s s ,

F r o m A d d r e s s ,

I n s u r a n c 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 преднамеренно очень прост и написан исключительно для данной демонстрационной программы. Основное в нем — часть с приоритетом, хотя реаль ный класс P a c k a g e , несомненно, должен содержать массу других членов. Все, что требуется классу 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 , реализуемый данным классом.

Оп р еделение возможных приоритетов

Приоритеты представляют собой перечислимый тип (enum) под названием Prior­ i t y . Он выглядит следующим образом:

352

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

//Priority - в м е с т о ч и с л о в ы х п р и о р и т е т о в н а п о д о б и е 1, 2, // 3, . . . и с п о л ь з у е м п р и о р и т е т ы с именами

e n u m P r i o r i t y

{

Low, Medium, H i g h

{

Реализация интерфейса IPrioritizable

Любой объект, поступающий в P r i o r i t y Q u e u e , должен знать собственный при­ оритет (общий принцип объектно-ориентированного программирования гласит, что кажцый объект отвечает сам за себя).

Можно просто неформально убедиться, что класс P a c k a g e имеет член для получения его приоритета, но лучше заставить компилятор проверять это требование, т.е. то, что у любого объекта, помещаемого в P r i o r i t y Q u e u e , имеется этот член.

Один из способов обеспечить это состоит в требовании, чтобы все объекты реализовывали интерфейс I P r i o r i t i z a b l 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 ,

II

должны

р е а л и з о в ы в а т ь

э т о т

и н т е р ф е й с

interface

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

 

 

 

P r i o r i t y P r i o r i t y

{ g e t ;

}

 

 

Запись { g e t ; } определяет,

как должно быть описано свойство в объявлении

интерфейса. Обратите внимание, что тело функции доступа g e t отсутствует, но интерфейс указывает, что свойство P r i o r i t y — только для чтения и воз­ вращает значение перечислимого типа P r i o r i t y .

Класс

P a c k a g e

реализует интерфейс путем предоставления реализации свойства

Priority:

 

public

P r i o r i t y

P r i o r i t y

get {

r e t u r n

p r i o r i t y ; }

Функция Main()

Перед тем как приступить к исследованию класса P r i o r i t y Q u e u e , стоит посмот­ реть, как он применяется на практике. Вот исходный текст функции Main ( ) :

//Main

-

з а п о л н я е м

о ч е р е д ь

с п р и о р и т е т а м и

п а к е т а м и ,

// затем

и з в л е к а е м

из о ч е р е д и их с л у ч а й н о е

к о л и ч е с т в о

s t a t i c

v o i d Main ( 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 ( " С о з д а н и е о ч е р е д и с п р и о р и т е т а м и : " ) ; P r i o r i t y Q u e u e < P a c k a g e > pq =

new P r i o r i t y Q u e u e < P a c k a g e > ( ) ;

C o n s o l e . W r i t e L i n e ( " Д о б а в л я е м с л у ч а й н о е к о л и ч е с т в о " +

 

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

353

" (0 - 20) случайных п а к е т о в " +

 

 

*

 

" в о ч е р е д ь : " ) ;

P a c k a g e p a c k ;

 

 

 

 

 

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

//

Нам

нужно

с л у ч а й н о е

ч и с л о ,

меньшее 2 0

Random

r a n d =

new Random () ;

 

 

/ /

Случ айно е

ч и с л о

в

д и а п а з о н е

0 - 2 0

i n t n u m T o C r e a t e = r a n d . N e x t ( 2 0 ) ;

 

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

{0}

п а к е т о в : " ,

 

 

 

n u m T o C r e a t e ) ;

 

f o r ( i n t i = 0; i < n u m T o C r e a t e ; i + + )

{

C o n s o l e . W r i t e ( " \ t \ t T e H e p a u H H и д о б а в л е н и е " + " с л у ч а й н о г о п а к е т а { о } " , i ) ;

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

C o n s o l e . W r i t e L i n e ( " с п р и о р и т е т о м { о } " , p a c k . P r i o r i t y ) ;

p q . E n q u e u e ( p a c k ) ;

}

C o n s o l e . W r i t e L i n e ( " Ч т о п о л у ч и л о с ь : ") ; i n t n T o t a l = p q . C o u n t ;

C o n s o l e . W r i t e L i n e ( " П о л у ч е н о п а к е т о в : { о } " , n T o t a l ) ; C o n s o l e . W r i t e L i n e ( " И з в л е к а е м с л у ч а й н о е к о л и ч е с т в о " +

 

 

 

 

" п а к е т о в : 0-2 0 : " ) ;

i n t

numToRemove

=

r a n d . N e x t ( 2 0 ) ;

 

C o n s o l e . W r i t e L i n e ( " ^ И з в л е к а е м

{0}

п а к е т о в " ,

 

 

 

 

numToRemove);

 

 

f o r

( i n t i

= 0;

i

< numToRemove;

i + + )

{

 

 

 

 

 

 

 

p a c k = p q . D e q u e u e ( ) ;

 

 

 

i f

( p a c k

! = n u l l )

 

 

 

{

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L

 

 

i n e ( " \ t \ t Д о с т а в к а п а к е т а

 

 

 

 

" с

п р и о р и т е т о м {О}",

 

 

 

 

p a c k . P r i o r i t y ) ;

}

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

/ / С к о л ь к о п а к е т о в " д о с т а в л е н о "

 

 

C o n s o l e . W r i t e L i n e ( " Д о с т а в л е н о { о }

п а к е т о в " ,

 

 

 

 

n T o t a l - p q . C o u n t ) ;

/ / Ожидаем

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

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

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

Итак, что же происходит в функции Main () ?

1. Инстанцируется объект P r i o r i t y Q u e u e для типа P a c k a g e .

2.Создается объект P a c k a g e F a c t o r y , работа которого состоит в формировании новых пакетов со случайно выбранными приоритетами. (Фабрика— это класс или метод, который создает для вас объекты.)

354

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

3.Для генерации случайного числа используется класс Random из библиотеки

.NET, а затем вызывается P a c k a g e F a c t o r y для создания соответствующего

количества новых объектов P a c k a g e со случайными приоритетами.

4.Выполняется добавление созданных пакетов в P r i o r i t y Q u e u e с помощью вы­ зова p g . E n q u e u e ( p a c k ) .

5.Выводится число созданных пакетов, после чего некоторое случайное их количе­ ство извлекается из P r i o r i t y Q u e u e .

6.Функция завершается выводом количества извлеченных из 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 a c k a g e , протестировать его, а затем "обобщить".

Вот небольшая часть необобщенного класса P r i o r i t y Q u e u e для иллюстрации ска­

занного:

 

 

 

 

 

 

public c l a s s

P r i o r i t y Q u e u e

 

 

 

{

 

 

 

 

 

 

//Queues

-

т р и в н у т р е н н и е ( о б о б щ е н н ы е ! )

о ч е р е д и

p r i v a t e 1

Q u e u e < P a c k a g e >

q u e u e H i g h

=

new

Q u e u e < P a c k a g e > ( ) ;

p r i v a t e

Q u e u e < P a c k a g e >

q u e u e M e d i u m

=

new

Q u e u e < P a c k a g e > ( ) ;

p r i v a t e

Q u e u e < P a c k a g e >

queueLow

=

new

Q u e u e < P a c k a g e > ( ) ;

//Enqueue - н а о с н о в а н и и п р и о р и т е т а P a c k a g e д о б а в л я е м е г о // в соответствующую о ч е р е д ь

p u b l i c v o i d E n q u e u e ( P a c k a g e i t e m )

{

s w i t c h ( i t e m . P r i o r i t y ) / / P a c k a g e и м е е т э т о с в о й с т в о

{

c a s e P r i o r i t y . H i g h :

q u e u e H i g h . E n q u e u e ( i t e m ) ; b r e a k ;

c a s e P r i o r i t y . L o w :

q u e u e L o w . E n q u e u e ( i t e m ) ; b r e a k ;

c a s e P r i o r i t y . M e d i u m :

q u e u e M e d i u m . E n q u e u e ( i t e m ) ; b r e a k ;

}

}

/ / и т а к д а л е е . . .

Написание необобщенного класса упрощает тестирование его логики. Затем, после тести­ рования и исправления всех ошибок, вы можете сделать контекстную замену P a c k a g e на <Т> (конечно, все не так прямолинейно, но и не очень отличается от сказанного).

355

Обобщенная очередь с приоритетами

Теперь пришло время разобраться с основным классом, из-за которого все и затева лось — с обобщенным классом 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 — оболочка, за которой скрываются три обычных объекта Queue<T>, по одному для каждого уровня приоритета. Вот первая часть исходного тек ста 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

- обобщенный

к л а с с о ч е р е д и с п р и о р и т е т а м и ;

// типы данных,

д о б а в л я е м ы х в

о ч е р е д ь , о б я з а н ы

/ / р е а л и з о в ы в а т ь и н т е р ф е й с I P r i o r i t i z a b l e c l a s s P r i o r i t y Q u e u e < T >

w h e r e T : I P r i o r i t i z a b l e

{

/ / Q u e u e s - т р и в н у т р е н н и е ( о б о б щ е н н ы е ! ) о ч е р е д и

p r i v a t e

Queue<T>

q u e u e H i g h

=

new

Q u e u e < T > ( ) ;

p r i v a t e

Queue<T>

queueMedium

=

new

Q u e u e < T > ( ) ;

p r i v a t e

Queue<T>

queueLow

=

new

Q u e u e < T > ( ) ;

/ / Все

о с т а л ь н о е

м ы в о т - в о т

р а с с м о т р и м . . .

В данных строках объявляются три закрытых члена-данных типа Queue<Т>, ини­ циализируемые путем создания соответствующих объектов Queue<T>.

Метод Enqueue()

E n q u e u e О добавляет элемент типа Т в P r i o r i t y Q u e u e . Работа состоит в том, чтобы выяснить приоритет элемента и поместить его в соответствующую приоритету очередь. В первой строке метод получает приоритет элемента и использует конструкцию s w i t c h для определения целевой очереди исходя из полученного значения. -Например, получив элемент с приоритетом P r i o r i t y . H i g h , метод E n q u e u e () помещает его в очередь q u e u e H i g h . Вот исходный текст метода P r i o r i t y Q u e u e . E n q u e u e ():

//

Д о б а в л я е т э л е м е н т Т в о ч е р е д ь на о с н о в а н и и з н а ч е н и я е г о

/ /

п р и о р и т е т а

p u b l i c v o i d E n q u e u e ( Т i t e m )

{

 

 

s w i t c h ( i t e m . P r i o r i t y )

/ /

Т р е б у е т р е а л и з а ц и и

{

/ /

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

c a s e P r i o r i t y . H i g h :

 

 

q u e u e H i g h . E n q u e u e ( i t e m ) ; b r e a k ;

c a s e P r i o r i t y . L o w :

q u e u e L o w . E n q u e u e ( i t e m ) ; b r e a k ;

c a s e P r i o r i t y . M e d i u m :

q u e u e M e d i u m . E n q u e u e ( i t e m ) ; b r e a k ;

d e f a u l t :

t h r o w new

A r g u m e n t O u t O f R a n g e E x c e p t i o n ( i t e m . P r i o r i t y . T o S t r i n g ( ) ,

356

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

" Н е в е р н ый п р и о р и т е т в P r i o r i t y Q u e u e . E n q u e u e " ) ;

Метод Dequeue()

Работа метода Dequeue () немного более хитрая. Он должен найти непустую оче­

редь элементов с наивысшим приоритетом и выбрать из нее первый элемент. Первую часть своей работы — поиск непустой очереди элементов с наивысшим приоритетом — Dequeue () делегирует закрытому методу TopQueue ( ) , который будет описан ниже. Затем метод D e q u e u e () вызывает метод D e q u e u e () найденной очереди для извлече­ ния из нее объекта, который и возвращает. Вот исходный текст метода D e q u e u e ( ) :

//Dequeue

- и з в л е ч е н и е

Т из

о ч е р е д и с

наивысшим

// приоритетом

 

 

 

 

 

 

public Т D e q u e u e ()

 

 

 

 

 

//

Просматриваем

о ч е р е д ь с

наивысшим

п р и о р и т е т о м

Queue<T>

q u e u e T o p

=

T o p Q u e u e ( ) ;

 

 

//

Очередь не

п у с т а

 

 

 

 

if

(queueTop

!= n u l l

&& q u e u e T o p . C o u n t > 0)

 

{

 

 

 

 

 

 

 

 

 

r e t u r n

q u e u e T o p . D e q u e u e ( ) ; / / Возвращаем

первый

 

 

 

 

 

 

/ / э л е м е н т

 

}

 

 

 

 

 

 

 

 

/ /

Если

в с е о ч е р е д и

п у с т ы ,

в о з в р а щ а е м n u l l

( з д е с ь

//

можно

с г е н е р и р о в а т ь и с к л ю ч е н и е )

 

 

r e t u r n d e f a u l t ( Т ) ;

}

Единственная сложность состоит в том, как поступить, если все внутренние очереди пусты, т.е. по сути пуста очередь P r i o r i t y Q u e u e в целом? Что следует вернуть в этом

случае? Представленный метод D e q u e u e ()

в этом случае возвращает значение n u l l .

Таким образом, клиент — код, вызывающий

P r i o r i t y Q u e u e . D e q u e u e () — должен

проверять, не вернул ли метод D e q u e u e О

значение n u l l . Где именно возвращается

значение n u l l ? В d e f a u l t (Т), в конце исходного текста метода. О выражении de­ fault (Т) речь пойдет чуть позже.

Вспомогательный метод TopQueue()

Метод D e q u e u e () использует вспомогательный метод T o p Q u e u e () для того, чтобы найти непустую внутреннюю очередь с наивысшим приоритетом. Метод TopQueue () начинает с очереди q u e u e H i g h и проверяет ее свойство C o u n t . Если оно больше 0, очередь содержит элементы, так что метод TopQueue () возвращает ссылку на эту внутреннюю очередь (тип возвращаемого значения метода TopQueue () — Queue<T>). Если же очередь q u e u e H i g h пуста, метод TopQueue () повторяет свои действия с очередями q u e u e M e d i u m и queueLow.

Что происходит, если все внутренние очереди пусты? В этом случае метод TopQueue () мог бы вернуть значение n u l l , но более полезным будет возврат од­ ной из пустых очередей. Когда после этого метод D e q u e u e () вызовет метод De­ queue () возвращенной очереди, тот вернет значение n u l l . Вот как выглядит ис­ ходный текст метода T o p Q u e u e ( ) :

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

357

//TopQueue - н е п у с т а я о ч е р е д ь p r i v a t e Queue<T> T o p Q u e u e ( )

{

if ( q u e u e H i g h . C o u n t > 0) r e t u r n q u e u e H i g h ;

if ( q u e u e M e d i u m . C o u n t > 0) r e t u r n q u e u e M e d i u m ;

if ( q u e u e L o w . C o u n t > 0) r e t u r n q u e u e L o w ;

r e t u r n q u e u e L o w ;

}

с

наивысшим

п р и о р и т е т о м

//

Очередь

с

высоким

// п р и о р и т е т о м п у с т а ?

//

Очередь

со

с р е д н и м

// п р и о р и т е т о м п у с т а ?

//

Очередь

с

н и з к и м

//

п р и о р и т е т о м п у с т а ?

//

Все о ч е р е д и пусты

Остальные члены PriorityQueue

Полезно знать, пуста ли очередь P r i o r i t y Q u e u e или нет, и если нет, то сколько элементов в ней содержится (каждый объект отвечает сам за себя!). Вернитесь к листингу демонстрационной программы и рассмотрите исходный текст метода Is Empty () и свой ства Count класса 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 i o r i t y Q u e u e не защищен от попыток инстанцирования для типов, например, i n t , s t r i n g или S t u d e n t , т.е. типов, не имеющих приоритетов, Вы должны наложить ограничения на класс с тем, чтобы он мог быть инстанцирован только для типов, реализующих интер­ фейс 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 да классов, не реализующих I P r i o r i t i z a b l e , должны приводить к ошибк времени компиляции.

Метод D e q u e u e () класса P r i o r i t y Q u e u e возвращает значение null вместо реального объекта. Однако обобщенные типы наподобие <Т> не имеют естественного значения n u l l по умолчанию, как, например, int или s t r i n g . Эта часть метода D e q u e u e () также требует обобщения.

Добавление ограничений

Класс 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 , должны реализовывать интерфейс I P r i o r i t i z a b l e , как это делает класс 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 в за­ головке своего объявления:

c l a s s 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 интерфейса I P r i o r i t i z a b l e .

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

358

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

Вы добавляете ограничение путем указания интерфейса 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 :

c l a s s P r i o r i t y Q u e u e < T > where T: IPrioritizable

Обратите внимание на выделенную полужирным шрифтом конструкцию, начинаю­ щуюся со слова w h e r e . Это принудителъ (enforcer), который указывает, что тип Т обя­ зан реализовывать интерфейс I P r i o r i t i z a b l e , т.е. как бы говорит компилятору: "Убедись, подставляя конкретный тип вместо Т, что он реализует интерфейс I P r i o r i ­ tizable, а иначе просто сообщи об ошибке".

Вы указываете ограничения, перечисляя в конструкции w h e r e одно или не­ сколько имен:

имя базового класса, от которого должен быть порожден класс Т (или должен быть этим классом);

имя интерфейса, который должен быть реализован классом Т, как было показано в предыдущем примере.

Дополнительные варианты ограничений включают ключевые слова s t r u c t , c l a s s inew(). С new () вы встретитесь чуть позже в этой главе, а об ограничениях s t r u c t и class можно прочесть в разделе "Generics, constraints" справочной системы.

Эти варианты ограничений повышают гибкость в описании поведения обобщенного иасса. Вот пример гипотетического обобщенного класса, объявленного с несколькими ограничениями на Т:

class MyClass<T> : w h e r e Т: c l a s s , I P r i o r i t i z a b l e , new ()

}

Здесь тип T должен быть классом, а не типом-значением; он должен реализовать интерфейс I P r i o r i t i z a b l e и содержать конструктор без параметров. Достаточно

А если у вас есть два обобщенных параметра и оба должны иметь ограниче­ ния? (Да, да — вы можете использовать несколько обобщенных параметров одновременно!) Вот как можно записать две конструкции w h e r e :

c l a s s MyClass<T, U> : w h e r e T: I P r i o r i t i z a b l e , w h e r e U: new()

Определение значения null д л я типа T

Как уже упоминалось ранее, у каждого типа есть свое значение по умолчанию, озна­

чающее "ничто" для данного типа. Для

i n t

и других типов чисел это 0 (или 0.0). Для

string— пустая строка " " . Для b o o l

это

f a l s e , а для всех ссылочных типов, таких

как Package, это n u l l .

 

 

Однако поскольку обобщенный класс

наподобие P r i o r i t y Q u e u e может быть ин-

панцирован практически для любого типа данных, С# не в состоянии предсказать, каким должно быть правильное значение n u l l в исходном тексте обобщенного класса. Напри­ мер, в методе Dequeue () класса P r i o r i t y Q u e u e вы можете оказаться именно в такой ситуации: вы вызываете Dequeu e ( ) , но очередь пуста и пакетов нет. Что вы должны вер­ нуть, что бы могло означать "ничего"? Поскольку P a c k a g e — класс, следует вернуть зна­ чение n u l l . Это сообщит вызывающей функции, что ничего вернуть не удалось (вызывающая функция, само собой, должна проверять, не вернулось ли значение n u l l ) .

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

359

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