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

Books / 3_C#_2005_для_чайников_(Дэвис-2008)

.pdf
Скачиваний:
86
Добавлен:
24.03.2015
Размер:
15.46 Mб
Скачать

Использование необобщенных коллекций

Коллекции легче применять, чем массивы. Для этого нужно инстанцировать объект коллекции, добавить в него элементы и итеративно работать с ними (для этого лучше всего воспользоваться циклом f o r e a c h ) . Приве­ денная дальше демонстрационная программа иллюстрирует эту последо­ вательность действий.

// N o n g e n e r i c C o l l e c t i o n s

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

// классов

к о л л е к ц и й

 

 

 

 

 

using

System;

 

 

 

 

 

 

using

S y s t e m . C o l l e c t i o n s ;

 

 

 

 

 

namespace

 

N o n g e n e r i c C o l l e c t i o n s

 

 

 

(

 

 

 

 

 

 

 

 

 

public

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

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

//

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

A r r a y L i s t ,

S t a c k ,

Queue

и

H a s h t a b l e

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 r r a y L i s t

 

 

 

 

 

 

 

//

 

 

 

 

 

 

 

 

 

/ /

И н с т а н ц и р о в а н и е A r r a y L i s t (вы можете

у к а з а т ь

 

//

н а ч а л ь н ы й

р а з м е р ,

но

можете

э т о г о

и

не д е л а т ь )

 

A r r a y L i s t a L i s t W i t h S p e c i f i e d S i z e

=

new

A r r a y L i s t ( 1 0 0 0 ) ;

 

A r r a y L i s t

 

a L i s t

=

new

A r r a y L i s t ( ) ;

/ /

 

р а з м е р n o

 

 

 

 

 

 

 

 

 

 

 

 

//

 

умолчанию (16)

 

a L i s t . A d d ( " o n e " ) ;

/ / Д о б а в л е н и е в к о н е ц с п и с к а

 

a L i s t . A d d ( " t w o " ) ;

// В с п и с к е - " o n e " , " t w o "

 

a L i s t . A d d ( " t h r e e " ) ; // В с п и с к е - " o n e " , " t w o " , " t h r e e "

 

C o n s o l e . W r i t e L i n e ( " { 0 }

i t e m s

i n t h e A r r a y L i s t : " ,

 

 

 

 

 

 

 

a L i s t . C o u n t ) ;

 

 

 

 

 

 

// Цикл

с

и с п о л ь з о в а н и е м

f o r e a c h

 

 

 

 

 

 

f o r e a c h ( s t r i n g

 

s i n a L i s t )

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

//

Выводим

с т р о к у и

ее

и н д е к с

в A r r a y L i s t

 

C o n s o l e . W r i t e L i n e ( s + " в ( { о } ) " ,

a L i s t . I n d e x O f ( s ) ) ;

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

//

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/ / S t a c k

 

 

 

 

 

 

 

 

 

 

 

 

 

 

//

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

/ / И н с т а н ц и р у е м с т е к

 

 

 

 

 

 

 

 

 

S t a c k

s t a c k =

new

S t a c k ( ) ;

 

 

 

 

 

 

 

// Вносим

э л е м е н т ы

в

с т е к

и

снимаем

с

н е г о

один

 

/ / э л е м е н т

 

 

 

 

 

 

 

 

 

 

 

 

s t a c k . P u s h ( " o n e " ) ;

 

 

 

 

 

 

 

 

 

 

s t a c k . P u s h ( " t w o " ) ;

 

/ / " t w o " , " o n e "

 

 

 

 

 

s t a c k . P u s h ( " t h r e e " ) ;

/ / " t h r e e " , " t w o " , " o n e "

 

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

" ,

 

 

 

 

 

 

 

s t a c k . C o u n t ) ;

 

 

 

 

 

 

f o r e a c h

( s t r i n g s

i n s t a c k )

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

(ma

15.

Обобщенное

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

 

 

 

 

341

s t r i n g s v a l = ( s t r i n g ) s t a c k . P o p ( ) ; / / " t w o " , " o n e " 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 ( s v a l ) ;

C o n s o l e . W r i t e L i n e ( " В е р ш и н а с т е к а : { о } " , s t a c k . P e e k ( ) ) ;

/ /

/ / Queue

/ /

/ / И н с т а н ц и р о в а н и е о ч е р е д и Queue q u e u e = new Q u e u e ( ) ;

// П о с т а н о в к а в о ч е р е д ь н е с к о л ь к и х э л е м е н т о в

q u e u e . E n q u e u e ( " o n e " ) ;

 

 

 

 

 

 

q u e u e . E n q u e u e ( " t w o " ) ;

 

 

 

 

 

 

q u e u e . E n q u e u e ( " t h r e e " ) ;

/ / " o n e " , " t w o " , " t h r e e "

 

C o n s o l e . W r i t e L i n e ( " { 0 }

э л е м е н т о в в о ч е р е д и : " ,

 

 

 

 

 

 

q u e u e . C o u n t ) ;

 

 

 

f o r e a c h

( s t r i n g s

i n q u e u e )

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " В ы в о д и з о ч е р е д и :

{ о } " ,

 

 

 

 

 

 

 

q u e u e . D e q u e u e ( ) ) ;

 

 

 

C o n s o l e . W r i t e L i n e ( " Г о л о в а о ч е р е д и :

{ о } " ,

 

 

 

 

 

 

 

q u e u e . P e e k ( ) ) ;

 

 

 

/ /

 

 

 

 

 

 

 

 

 

 

 

 

/ / H a s h t a b l e

 

 

 

 

 

 

 

 

 

/ /

 

 

 

 

 

 

 

 

 

 

 

 

/ / И н с т а н ц и р о в а н и е H a s h t a b l e ( с л о в а р ь )

 

 

H a s h t a b l e

t a b l e

=

new

H a s h t a b l e ( ) ;

 

 

 

S t u d e n t

s t u d e n t l

=

new

S t u d e n t ( " R a n d y " ) ;

 

 

S t u d e n t

s t u d e n t 2

=

new

S t u d e n t ( " C h u c k " ) ;

 

 

/ /

Д о б а в л я е м

о б ъ е к т

S t u d e n t ,

ключом

я в л я е т с я

е г о

имя

t a b l e . A d d ( s t u d e n t l . N a m e ,

s t u d e n t l ) ;

 

 

 

t a b l e . A d d ( s t u d e n t 2 . N a m e ,

s t u d e n t 2 ) ;

 

 

 

/ /

Порядок

н е и з в е с т е н

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " { 0 }

э л е м е н т о в в

с л о в а р е : " ,

 

 

 

 

 

 

t a b l e . C o u n t ) ;

 

 

 

/ /

Элементы,

в о з в р а щ а е м ы е

и з

с л о в а р я , имеют

тип

 

/ / D i c t i o n a r y E n t r y

 

 

 

 

 

 

 

f o r e a c h ( D i c t i o n a r y E n t r y d e i n t a b l e )

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

 

// П р и в е д е н и е

с в о й с т в а

D i c t i o n a r y E n t r y . V a l u e к

типу

 

/ / S t u d e n t

 

 

 

 

 

 

 

 

 

 

S t u d e n t s t u =

( S t u d e n t ) d e . V a l u e ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( s t u . N a m 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 ( ) ;

}

p u b l i c c l a s s S t u d e n t

{

342

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

p r i v a t e

s t r i n g n a m e ;

 

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 = n a m e ;

 

}

 

 

p u b l i c

s t r i n g Name

 

{

 

 

g e t { r e t u r n n a m e ;

}

Подробно останавливаться на том, как работают использованные в демонстрацион­ ной программе классы-коллекции, нет необходимости, так как сейчас вы познакоми­ тесь с более интересными обобщенными классами. Получить информацию о необоб­ щенных классах-коллекциях можно в разделе "System.Collections namespace" справоч­ ной системы.

Обобщенные классы

Теперь, при доступности в С# обобщенных классов, вы вряд ли захотите использо­ вать описанные в предыдущем разделе необобщенные классы. Обобщенные классы предпочтительнее по двум причинам: безопасность и производительность.

Обобщенные классы безопасны

Когда вы объявляете массив, вы должны указать точный тип данных, которые могут в нем храниться. Если это i n t — то массив не может хранить ничего, кроме i n t или других числовых типов, которые С# в состоянии неявно преоб­ разовать в i n t . Если вы попытаетесь поместить в массив данные неверного типа, то получите от компилятора сообщение об ошибке. Таким образом ком­ пилятор обеспечивает безопасность типов, т.е. вы обнаруживаете и исправляе­ те проблему еще до того, как она проявится. Гораздо лучше получить сообще­ ние об ошибке от компилятора, чем в процессе работы программы.

Необобщенные коллекции небезопасны. В С# переменная любого типа ЯВЛЯ­ ЕТСЯ O b j e c t , поскольку класс O b j e c t является базовым классом для всех других типов, как типов-значений, так и типов-ссылок (см. раздел об унифика­ ции типов в главе 14, "Интерфейсы и структуры"). Однако когда вы сохраняете

типы-значения (числа, b o o l , s t r u c t ) в

коллекции, они должны быть упако­

ваны при помещении в нее

и распакованы

при извлечении из нее (см. оконча­

ние главы 14, "Интерфейсы

и структуры").

Первое следствие небезопасности необобщенных классов заключается в том, что вам требуется приведение типов (как показано в следующем фрагменте исходного текста) для получения исходного объекта из A r r a y L i s t , так как этот тип скрыт при упаковке.

ArrayList a L i s t

=

new A r r a y L i s t () ;

 

// Добавляем п я т ь - ш е с т ь э л е м е н т о в ,

а з а т е м . . .

string m y S t r i n g

=

( s t r i n g ) a L i s t [4] ;

// п р е о б р а з у е м в s t r i n g

Глава

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

343

Второе следствие в том, что в A r r a y L i s t одновременно могут храниться объекты разных типов. То есть вы можете написать, например, такой исход ный текст:

A r r a y L i s t a L i s t = new

A r r a y L i s t О ;

 

a L i s t . A d d ( " a s t r i n g " ) ; / / s t r i n g

- - O K

a L i s t . A d d ( 3 ) ;

/ /

i n t

- - O K

a L i s t . A d d ( a S t u d e n t ) ;

/ /

S t u d e n t - - O K

Однако если вы поместите в A r r a y L i s t (или другую необобщенную коллекции объекты разных несовместимых типов, то как вы потом сможете узнать тип, например третьего элемента? Если это S t u d e n t , а вы попытаетесь преобразовать его в string то получите ошибку времени выполнения программы.

Для безопасности следует производить проверку с использованием оператор is (рассматривавшегося в главе 12, "Наследование") или альтернативного я ратора as следующим образом:

i f ( a L i s t [ i ]

i s S t u d e n t )

//

Объект -

S t u d e n t ?

{

 

 

 

 

 

S t u d e n t a S t u d e n t = ( S t u d e n t ) a L i s t [ i ] ; / / Д а

 

 

 

/ / или . . .

 

 

 

S t u d e n t a S t u d e n t =

a L i s t [ i ] a s S t u d e n t ;

/ /

Получаем

S t u d e n t

i f ( a S t u d e n t

! = n u l l )

 

/ /

Невозможно, " a s "

{

 

 

/ / в о з в р а щ а е т n u l l

// Можно

р а б о т а т ь

с a S t u d e n t

 

 

 

}

Избавиться от лишней работы можно посредством обобщенных классов, ные коллекции работают как и массивы: вы определяете один и только один тип, кото| рый может храниться в коллекции при ее объявлении.

Обобщенные классы эффективны

Полиморфизм позволяет типу Obj e c t хранить любой другой тип. Однако за удобство приходится платить упаковкой и распаковкой типов-значений при размете] их в необобщенных коллекциях.

Упаковка не так уж снижает эффективность, если ваша коллекция мала. Но если ва перемещаете тысячи или даже миллионы целых чисел типа i n t в необобщенной конлекции, это может занять примерно в 20 раз больше времени (и потребовать дополи тельной памяти) по сравнению с хранением объектов ссылочного типа. Упаковка танк может привести к некоторым трудно находимым ошибкам. Обобщенные коллекции» знакомы с проблемами, связанными с упаковкой и распаковкой.

Использование обобщенных коллекций

Теперь, когда вы знаете, почему обобщенные коллекции предпочтительнее необоб­ щенных, пришло время познакомиться с тем, как они используются. В табл. 15.2 пред-

344

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

ставлен частичный список обобщенных классов коллекций (в третьем столбце таблицы указаны их необобщенные эквиваленты).

Помимо указанных классов, имеются й другие, а также несколько соответствующих интерфейсов для большинства из них, таких как I C o l l e c t i o n < T > или I L i s t < T > . За более подробной информацией о них обратитесь к разделу "System.Collections.Generic namespace" справочной системы.

Понятие <Т>

В этой странно выглядящей записи <Т> обозначает место, куда будет помещен некий ральный тип. Чтобы вызвать к жизни этот символический объект, его инстанцируют пу-

тем указания реального типа:

 

|ist<int>

i n t L i s t =

 

new

L i s t < i n t > ( ) ; / /

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

Например,

в

следующем разделе

L i s t < T > будет инстанцирован для типов i n t ,

string и S t u d e n t . Кстати говоря, Т отнюдь не священная корова, и вместо него можно использовать все что угодно — например <dummy> или <mуТуре>. Обычно для паметра типа применяются буквы Т, U, V и т.д.

Использование List<T>

Если A r r a y L i s t — один из наиболее часто используемых необобщенных классов коллекций, то его обобщенный двойник — L i s t < T > . Его применение проиллюстрировано в демонстрационной программе G e n e r i c C o l l e c t i o n s . (Для того чтобы компилировать эту программу, вы должны закомментировать строки, приводящие [ошибкам времени компиляции.) Полный текст программы можно найти на прилагаемом компакт-диске.

// G e n e r i c C o l l e c t i o n s - д е м о н с т р а ц и я обобщенных к о л л е к ц и й using System;

using System . C o l l e c t i o n s ; using System. Collections .Generic; namespace GenericCollections

{

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

15.

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

345

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 r r a y L i s t д л я с р а в н е н и я

 

 

A r r a y L i s t

a L i s t =

new

A r r a y L i s t ( ) ;

 

 

// L i s t < T > :

о б р а т и т е

внимание

на

у г л о в ы е

с к о б к и и

// п а р а м е т р т и п а Т

 

 

 

 

 

 

L i s t < s t r i n g > s L i s t =

/ /

И н с т а н ц и р о в а н и е

д л я s t r i n g

new

L i s t < s t r i n g > ( ) ;

s L i s t . A d d C ' o n e " ) ;

 

 

//

Ошибка

к о м п и л я ц и и !

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

 

 

s L i s t . A d d (

 

 

B o i s " ) ) ; //

Ошибка

к о м п и л я ц и и !

new

S t u d e n t ( " d u

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

 

 

 

 

L i s t < i n t >

i n t L i s t

= new

L i s t < i n t > ( ) ;

 

 

i n t L i s t . A d d ( 3 ) ;

 

/ /

Никакой

у п а к о в к и

 

i n t L i s t . A d d ( 4 ) ;

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " В ы в о д

i n t L i s t : " ) ;

 

 

f o r e a c h ( i n t

i i n

i n t L i s t )

/ / Цикл

f o r e a c h

р а б о т а е т для

 

 

 

 

 

 

/ / в с е х к о л л е к ц и й

{

/ / О б р а т и т е в н и м а н и е : п р и в е д е н и я т и п а н е т

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

}

/ / И н с т а н ц и р о в а н и е д л я S t u d e n t

 

 

 

 

L i s t < S t u d e n t >

s t u d e n t L i s t

= new

L i s t < S t u d e n t > ( ) ;

S t u d e n t

s t u d e n t l

=

new

S t u d e n t ( "Vigil 1 1 ) ;

 

S t u d e n t

s t u d e n t 2

=

new

S t u d e n t ( " F i n c h " ) ;

 

s t u d e n t L i s t . A d d ( s t u d e n t l ) ;

 

 

 

 

 

s t u d e n t L i s t . A d d ( s t u d e n t 2 ) ;

 

 

 

 

 

S t u d e n t [ ] s t u d e n t s

= new

S t u d e n t [ ]

 

 

 

{

new S t u d e n t ( " M o x " ) ,

new

S t u d e n t ( " F o x " ) };

 

s t u d e n t L i s t . A d d R a n g e ( s t u d e n t s ) ;

/ / Д о б а в л я е м в е с ь

 

 

 

 

 

 

 

 

// м а с с и в в L i s t

C o n s o l e . W r i t e L i n e ( " С т у д е н т о в в s t u d e n t L i s t = { о } " ,

 

 

 

 

 

s t u d e n t L i s t . C o u n t ) ;

 

 

//

Поиск

при

помощи I n d e x O f ( )

 

 

 

 

C o n s o l e . W r i t e L i n e ( " S t u d e n t 2 в " +

 

 

 

 

 

 

 

 

s t u d e n t L i s t . I n d e x O f ( s t u d e n t 2 ) ) ;

s t r i n g name =

s t u d e n t L i s t [3] .Name;

//

Обращение при

 

 

 

 

 

 

 

 

 

//

помощи

и н д е к с а

i f ( s t u d e n t L i s t . C o n t a i n s ( s t u d e n t l ) )

/ /

Поиск

C o n t a i n s ( )

{

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( s t u d e n t l . N a m e + " е с т ь в с п и с к е " ) ;

}

s t u d e n t L i s t . S o r t ( ) ; / / С ч и т а е м , ч т о S t u d e n t р е а л и з у е т / / и н т е р ф е й с I C o m p a r a b l e

s t u d e n t L i s t . I n s e r t ( 3 , new S t u d e n t ( " R o s s " ) ) ;

s t u d e n t L i s t . R e m o v e A t ( 3 ) ;

/ /

Удаляем э л е м е н т

// name о п р е д е л е н о выше

 

 

C o n s o l e . W r i t e L i n e ( " У д а л я е м { о } " , n a m e ) ;

S t u d e n t [ ] m o r e S t u d e n t s =

 

 

s t u d e n t L i s t . T o A r r a y ( ) ;

//

П р е о б р а з у е м с п и с о к в массив

346

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

Глава

//

Ожидаем

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

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

 

 

 

public

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

: I C o m p a r a b l e

 

// См.

полную

версию

программы

на

п р и л а г а е м о м

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

В приведенном листинге имеется три инстанцирования L i s t < T > : для i n t , s t r i n g в Student. В программе также продемонстрировано следующее:

безопасность типов, позволяющая избежать добавления данных неверного типа; возможность применения для коллекции L i s t < T > цикла f o r e a c h , как и для лю­ бой другой коллекции; добавление объектов, как по одному, так и сразу целым массивом;

сортировка списка (в предположении, что элементы реализуют интерфейс ICom­ p a r a b l e ) ;

вставка нового элемента между имеющимися; получение количества элементов в списке; проверка, содержится ли в списке конкретный объект; удаление элемента из списка; копирование элементов из списка в массив.

Это только небольшой пример использования методов L i s t < T > . У других обобщен­ ии коллекций имеются свои наборы методов, однако все они схожи в применении.

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

Помимо встроенных обобщенных классов коллекций, С# позволяет написать собственные обобщенные классы — как коллекции, так и другие типы классов. Главное, что вы имеете возможность создать обобщенные версии классов, которые проектированы вами.

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

иже объявления:

 

LinkedList<int> a L i s t

= new L i n k e d L i s t < i n t > () ;

MyClass<int> a C l a s s =

new M y C l a s s < i n t > () ;

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

347

Оба являются инстанцированиями классов: одно — встроенного, второе — польва тельского. Не каждый класс имеет смысл делать обобщенным, но далее в главе будет рассмотрен пример класса, который следует сделать именно таковым.

Классы, которые логически могут делать одни и те же вещи с данными разных типов — наилучшие кандидаты в обобщенные классы. Наиболее типичным примером являются коллекции, способные хранить различные данные. Если в какой-то момент у вас появляется мысль: "А ведь мне придется написать верс сию этого класса еще и для объектов S t u d e n t " , — вероятно, ваш класс стоит сделать обобщенным.

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

Очередь с приоритетами

Представим себе почтовую контору наподобие FedEx. В нее поступает постоянный поток пакетов, которые надо доставить получателям. Однако пакеты не равны по возможно сти: некоторые из них следует доставить немедленно (для них уже ведутся разработка телепортаторов), другие можно доставить авиапочтой, а третьи могут быть доставлен наземным транспортом.

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

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

Пакеты с высоким приоритетом помещаются в начало очереди, но после дру гих пакетов с высоким приоритетом, уже присутствующих в ней.

Пакеты со средним приоритетом ставятся в начало очереди, но после пакетов! высоким приоритетом и других пакетов со средним приоритетом, уже присутст вующих в ней.

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

С# предоставляет встроенный обобщенный класс очереди, но он не подходит да создания очереди с приоритетами. Таким образом, нужно написать собственный класс очереди, но как это сделать? Распространенный подход заключается в разработке клас

са-оболочки

(wrapper class) для нескольких очередей:

c l a s s W r a p p e r / /

Или

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

{

 

 

 

 

 

Queue

q u e u e H i g h

=

new Queue

( ) ;

Queue

q u e u e M e d i u m

=

new Queue ( ) ;

Queue

queueLow

=

new Queue

( ) ;

// Методы д л я р а б о т ы с э т и м и о ч е р е д я м и . . .

Оболочка инкапсулирует три обычные очереди (которые могут быть обобщенными)» управляет внесением пакетов в эти очереди и получением их из очередей. Стандартный интерфейс класса Queue, реализованного в С#, содержит два ключевых метода:

348

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

E n q u e ue () — для помещения объектов в конец очереди;

Dequeue () — для извлечения объектов из очереди.

Оболочки представляют собой классы (или функции), которые инкапсулируют сложную работу. Оболочки могут иметь интерфейс, существенно отличающий­ ся от интерфейса(ов) использованных в нем элементов. Однако в данном слу­ чае интерфейс оболочки совпадает с интерфейсом обычной очереди. Класс реализует метод E n q u e u e ( ) , который получает пакет и его приоритет, и на основании приоритета принимает решение о том, в какую из внутренних оче­ редей его поместить. Он также реализует метод 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 - д е м о н с т р а ц и я и с п о л ь з о в а н и я о б ъ е к т о в

//

низкоуровневой о ч е р е д и

д л я р е а л и з а ц и и

в ы с о к о у р о в н е в о й

//

обобщенной о ч е р е д и ,

в

к о т о р о й о б ъ е к т ы

х р а н я т с я с у ч е т о м

// их п р и о р и т е т а

 

 

 

using

S y s t e m ;

 

 

 

using

S y s t e m . C o l l e c t i o n s

. G e n e r i c ;

 

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

 

 

 

(

class

P r o g r a m

 

 

 

 

 

 

 

 

 

 

{ ^ •

 

 

 

 

 

 

 

 

 

 

 

 

//Main - заполняем очередь с приоритетами пакетами,

 

 

// затем

и з в л е к а е м

из

о ч е р е д и

их

с л у ч а й н о е

к о л и ч е с т в о

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 ( " С о з д а н и е о ч е р е д и с п р и о р и т е т а м и : " ) ;

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 ( " Д о б а в л я е м с л у ч а й н о е к о л и ч е с т в о " +

 

 

 

 

 

"

(0

-

20)

случайных

п а к е т о в " +

 

 

 

 

 

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

 

 

Package

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

R a n d o m ( ) ;

 

 

 

 

/ /

Случайное

 

ч и с л о

в

д и а п а з о н е

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 ( " ^ С о з д а н и е { о } п а к е т о в : " ,

 

 

 

 

 

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

 

 

 

for ( 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 r e H e p a m ^ и д о б а в л е н и е " +

 

 

 

 

" с л у ч а й н о г о п а к е т а { о } " ,

i ) ;

 

pack

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

 

 

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

349

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. За базовыми классы