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 |