1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 68
Текст из файла (страница 68)
За базовыми классами!Глава/ / Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яC o n s o l e . W r i t e L i n e ( " Н а ж м и т е <Enter> для " +"завершенияпрограммы. . . " ) . ;Console.Read();public c l a s s S t u d e n t :IComparable// См. п о л н у ю в е р с и ю п р о г р а м м ы на п р и л а г а е м о м// к о м п а к т - д и с к еВ приведенном листинге имеется три инстанцирования 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 , как и для любой другой коллекции;добавление объектов, как по одному, так и сразу целым массивом;сортировка списка (в предположении, что элементы реализуют интерфейс IComparable);вставка нового элемента между имеющимися;получение количества элементов в списке;проверка, содержится ли в списке конкретный объект;удаление элемента из списка;копирование элементов из списка в массив.Это только небольшой пример использования методов 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 = n e w M y C l a s s < i n t > () ;toa 15. Обобщенное программирование347Оба являются инстанцированиями классов: одно — встроенного, второе — польвательского. Не каждый класс имеет смысл делать обобщенным, но далее в главе будетрассмотрен пример класса, который следует сделать именно таковым.Классы, которые логически могут делать одни и те же вещи с данными разныхтипов — наилучшие кандидаты в обобщенные классы.
Наиболее типичнымпримером являются коллекции, способные хранить различные данные. Еслив какой-то момент у вас появляется мысль: "А ведь мне придется написать верссию этого класса еще и для объектов S t u d e n t " , — вероятно, ваш класс стоитсделать обобщенным.Чтобы показать, как пишутся собственные обобщенные классы, будет разработанобобщенный класс для очереди специального вида, а именно очереди с приоритетами.Очередь с приоритетамиПредставим себе почтовую контору наподобие FedEx. В нее поступает постоянныйпоток пакетов, которые надо доставить получателям. Однако пакеты не равны по возможности: некоторые из них следует доставить немедленно (для них уже ведутся разработкателепортаторов), другие можно доставить авиапочтой, а третьи могут быть доставленназемным транспортом.Однако в контору пакеты приходят в произвольном порядке, так что при поступленияочередного пакета его нужно поставить в очередь на доставку.
Слово прозвучало — необходима очередь, но очередь необычная. Вновь прибывшие пакеты становятся в очередь на доставку, но часть из них имеет более высокий приоритет и должна ставиться если и не в самое начало очереди, то уж точно не в ее конец.Попробуем сформулировать правила такой очереди. Итак, имеются входящие пакетыс высоким, средним и низким приоритетом. Ниже описан порядок их обработки.Пакеты с высоким приоритетом помещаются в начало очереди, но после других пакетов с высоким приоритетом, уже присутствующих в ней.Пакеты со средним приоритетом ставятся в начало очереди, но после пакетов!высоким приоритетом и других пакетов со средним приоритетом, уже присутствующих в ней.Пакеты с низким приоритетом ставятся в конец очереди.С# предоставляет встроенный обобщенный класс очереди, но он не подходит дасоздания очереди с приоритетами.
Таким образом, нужно написать собственный классочереди, но как это сделать? Распространенный подход заключается в разработке класса-оболочки (wrapper class) для нескольких очередей:classWrapper//ИлиPriorityQueue!{Q u e u e q u e u e H i g h = new Queue ( ) ;Q u e u e q u e u e M e d i u m = new Queue ( ) ;Queue q u e u e L o w = new Queue ( ) ;/ / Методы д л я р а б о т ы с э т и м и о ч е р е д я м и . . .Оболочка инкапсулирует три обычные очереди (которые могут быть обобщенными)»управляет внесением пакетов в эти очереди и получением их из очередей.
Стандартныйинтерфейс класса Q u e u e , реализованного в С#, содержит два ключевых метода:348Часть V. За базовыми классамиE n q u e u e () — для помещения объектов в конец очереди;D e q u e u e () — для извлечения объектов из очереди.Оболочки представляют собой классы (или функции), которые инкапсулируютсложную работу. Оболочки могут иметь интерфейс, существенно отличающийся от интерфейса(ов) использованных в нем элементов.
Однако в данном случае интерфейс оболочки совпадает с интерфейсом обычной очереди. Классреализует метод E n q u e u e ( ) , который получает пакет и его приоритет, и наосновании приоритета принимает решение о том, в какую из внутренних очередей его поместить. Он также реализует метод D e q u e u e ( ) , который находитпакет с наивысшим приоритетом в своих внутренних очередях и извлекает егоиз очереди. Дадим рассматриваемому классу-оболочке формальное имя P r i orityQueue.Вот исходный текст этого класса:// 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(classProgram{^•//Main - заполняем очередь с приоритетами пакетами,// затем и з в л е к а е м из очереди их случайное к о л и ч е с т в оstatic void M a i n ( s t r i n g [] args){Console.WriteLine("Создание очереди с приоритетами:");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 > ( ) ;Console.WriteLine("Добавляем случайное количество" +" (0 - 2 0 ) с л у ч а й н ы х п а к е т о в " +" в очередь: " ) ;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 0Random r a n d = n e w R a n d o m ( ) ;// Случайное число в д и а п а з о н е 0 - 2 0int numToCreate = r a n d .
N e x t ( 2 0 ) ;Console.WriteLine("^Создание {о} пакетов:",numToCreate);f o r ( i n t i = 0; i < n u m T o C r e a t e ; i + + ){Console.Write("\t\treHepam^ и добавление ""случайного пакета { о } " , 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 ( " с приоритетом { о } " ,pack.Priority);15.Обобщенноепрограммирование+349pq.Enqueue(pack);}Console.WriteLine("Чтополучилось:");int nTotal = pq.Count;Console.WriteLine("Получено пакетов: { о } " , nTotal);Console.WriteLine("Извлекаем случайное количество" +" п а к е т о в : 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 + + ){pack = pq.Dequeue();if(pack != n u l l ){}}}Console.WriteLine("\ъ\ЬДоставка пакета "" с приоритетом { о } " ,pack.Priority);+}// Сколько пакетов " д о с т а в л е н о "Console.WriteLine("Доставлено{0} п а к е т о в " ,nTotal - pq.Count);/ / Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яConsole.WriteLine("Нажмите <Enter> для " +" з а в е р ш е н и я п р о г р а м м ы .
. . ") ;Console.Read();/ / P r i o r i t y - вместо числовых приоритетов наподобие// 3,. . . и с п о л ь з у е м приоритеты с именамиenum P r i o r i t y // Об enum мы п о г о в о р и м п о з ж е{Low, M e d i u m , H i g h1,2,}// 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 ,/ / должны р е а л и з о в ы в а т ь э т о т и н т е р ф е й сinterfaceIPrioritizable{// Пример с в о й с т в а в и н т е р ф е й с еPriority Priority { get;}}/ / 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 eclass PriorityQueue<T>w h e r e T : I P r i o r i t i z a b l e // <-- э т о т момент мы// обсудим позже{350//Queues - три внутренние (обобщенные!) очереди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 > ( ) ;Часть V.
За базовыми классы//Enqueue - Добавляет// приоритетомpublic void Enqueue(ТTвочередьвсоответствиисitem){switch(item.Priority)// Требует реализации{//IPrioritizablecasePriority.High:queueHigh.Enqueue(item);break;casePriority.Low:queueLow.Enqueue(item);break;casePriority.Medium:queueMedium.Enqueue(item);break;default:t h r o w newArgumentOutOfRangeException(item.Priority.ToString(),"Неверный приоритет в 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 из о ч е р е д и с наивысшим// приоритетомpublic Т Dequeue(){// П р о с м а т р и в а е м о ч е р е д ь с наивысшим п р и о р и т е т о м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)returnqueueTop.Dequeue();////Возвращаемэлементпервый}// Если все очереди пусты, возвращаем 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 queueHigh;if ( q u e u e M e d i u m . C o u n t > 0)r e t u r n queueMedium;if ( q u e u e L o w . C o u n t > 0)r e t u r n queueLow;:- r e t u r n queueLow;//IsEmpty public boolПроверка,IsEmpty()пуста//////////////лиОчередь с высокимприоритетом пуста?Очередь со среднимприоритетом пуста?Очередь с низкимприоритетом пуста?Все о ч е р е д и пустыочередь{ ,// t r u e , если все очереди пустыr e t u r n (queueHigh.Count==0) &Глава15.Обобщенное программирование351(queueMedium.Count = = 0 ) &(queueLow.Count== 0 ) ;}}/ / C o u n t - Сколько в с е г о элементов во всех очередях?p u b l i c i n t Count // Реализуем как свойство только{// для ч т е н и яget { r e t u r n queueHigh.Count+queueMedium.Count +queueLow.Count; }}}/ / P a c k a g e - пример к л а с с а , к о т о р ы й может// о ч е р е д и с п р и о р и т е т а м иc l a s s Package : I P r i o r i t i z a b l e{privatePriority priority;// КонструкторpublicPackage(Priorityбытьразмещенвpriority){this.priority=priority;}//Priority// чтенияpublicget-возвращаетPriority{приоритетпакета;толькодляPriorityreturn priority;}}//А/ / итакже методыдругие...ToAddress,FromAddress,Insurance,-версию на}////К л а с с 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 преднамеренно очень прост и написан исключительно для даннойдемонстрационной программы.