1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 93
Текст из файла (страница 93)
Все остальное C#делает за вас: создает класс-перечислитель и применяет его метод M o v e N e x t () дляитерирования. У вас уменьшается количество работы и размер исходного текста.Ваш класс, содержащий метод G e t E n u m e r a t o r ( ) , больше не должен реализовывать интерфейс I E n u m e r a t o r . В следующих разделах вам будет показано нескольковариаций блоков итераторов:обычные итераторы;именованные итераторы;свойства классов, реализованные как итераторы.Что такое коллекцияОстановимсяна минутку исравнимэту небольшуюколлекциюсколлекциейL i n k e d L i s t , рассматривавшейся ранее в главе. В то время как L i n k e d L i s t имеетсложную структуру узлов, связанных посредством указателей, приведенная простейшаяколлекция месяцев основана на простом массиве с фиксированным содержимым.
Но всеже следует расширить понятие коллекции.(Ваш класс коллекции не обязан иметь фиксированное содержимое — большинствоколлекций разработаны для хранения объектов путем добавления их в коллекции, например, с помощью метода A d d () или чего-то в этом роде. Класс K e y e d A r r a y , к примеру, использует для добавления элементов в коллекцию индексатор. Ваша коллекциятакже должна обеспечивать метод A d d ( ) , как и блок итератора, чтобы вы могли работать с ней с помощью f o r e a c h .
)Цель коллекции, в наиболее общем смысле, заключается в хранении множества объектов и обеспечении возможности их обхода, последовательно выбирая их по одномухотя иногда может использоваться и произвольная выборка, как в демонстрационнойпрограмме I n d e x e r . (Конечно, массив и так в состоянии справиться с этим, без дополнительных "наворотов" наподобие класса M o n t h D a y s , но итераторы вполне могут применяться и за пределами примера M o n t h D a y s . )Говоря более обобщенно, независимо от того, что именно происходит за сценой, итерируемая коллекция генерирует "поток" значений, который можно получить с помощьюforeach.Для лучшего понимания данной концепции ознакомьтесь с еще одним примером простого класса из демонстрационной программы I t e r a t o r B l o c k s , который иллюстрирует чистую идею коллекции://StringChunks - определение// фрагменты текста474итератора,возвращающегоЧасть VII.
Дополнительные главыclassStringChunks/ / G e t E n u m e r a t o r - и т е р а т о р . Обратите внимание, как он//(дважды)вызывается в MainpublicSystem.Collections.IEnumerator GetEnumerator(){//yieyieyieyieyielllllВозврат разных фрагментов текстаd return" U s i n g i t e r a t o r и /.d return"blocks";d return"isn't all";d return"that hard";dreturn".";накаждойитерации}Коллекция S t r i n g C h u n k s , как ни странно, ничего не хранит в обычном смыслеэтого слова. В ней нет даже массива. Так где же тут коллекция? Она — в последовательности вызовов y i e l d r e t u r n , использующих специальный новый синтаксис для возврата элементов один за другим, пока все они не будут возвращены вызывающей функции.
Эта коллекция "содержит" пять объектов, каждый из которых представляет собойпростую строку, как и в рассмотренном только что примере M o n t h D a y s . Извне класса,в функции M a i n ( ) , вы можете итерировать эти объекты посредством простого циклаf o r e a c h , поскольку конструкция y i e l d r e t u r n возвращает п о одной строке з а раз.Вот часть функции M a i n ( ) , в которой выполняется итерирование "коллекции"StringChunks:// Итерируем коллекцию с т р о кStringChunks sc = new S t r i n g C h u n k s ( ) ;// И т е р и р у е м - выводим т е к с т , помещая// в с в о е й с о б с т в е н н о й с т р о к еConsole.WriteLine("\Строки:\п");foreach( s t r i n g sChunk in sc){каждыйфрагментConsole.WriteLine(sChunk);Синтаксис итератораВ C# 2.0 вводятся два новых варианта синтаксиса итераторов.
Конструкцияy i e l d r e t u r n больше всего напоминает старую комбинацию M o v e N e x t ( ) и C u r r e n t для получения очередного элемента коллекции. Конструкция y i e l d b r e a k похожа на оператор b r e a k , который позволяет прекратить работу цикла или конструкцииswitch.yield returnСинтаксис y i e l d r e t u r n работает следующим образом.1. При первом вызове он возвращает первое значение коллекции.2. При следующем вызове возвращается второе значение.»3. И так далее...Глава 20.
Работа с коллекциями475Это очень похоже на старый метод итератора M o v e N e x t ( ) , использовавшийся в ко|де L i n k e d L i s t . Каждый вызов M o v e N e x t () предоставляет новый элемент коллекцииОднако в данном случае вызов M o v e N e x t () не требуется.Что же подразумевается под очередным вызовом? Давайте еще раз посмотрим нацикл f o r e a c h , использующийся для итерирования коллекции S t r i n g C h u n k s :foreach(stringsChunkinsc){Console.WriteLine(sChunk);}Каждый раз, когда цикл получает новый элемент посредством итератора, последнийсохраняет достигнутую им позицию в коллекции. При очередной итерации циклаf o r e a c h итератор возвращает следующий элемент коллекции.yield breakСледует упомянуть еще об одном синтаксисе.
Можно остановить работу итераторав определенный момент, использовав в нем конструкцию y i e l d b r e a k . Напримердостигнут некоторый порог при тестировании определенного условия в блоке итераторкласса коллекции, и вы хотите на этом прекратить итерации. Вот краткий пример блокаитератора, использующего y i e l d b r e a k именно таким образом://YieldBreakEx - пример// yield breakclassYieldBreakExиспользованияключевогослова{i n t [ ] p r i m e s = { 2, 3, 5, 7, 1 1 , 1 3 , 1 7 , 1 9 , 23 } ,/ / G e t E n u m e r a t o r - возврат последовательности простых// чисел с демонстрацией применения конструкции y i e l d//breakpublicSystem.Collections.IEnumeratorGetEnumerator(){foreach(intnPrimeinprimes){if (nPrime >yield return}}13) y i e l dnPrime;break;//Новыйсинтаксис}В рассмотренном случае блок итератора содержит оператор i f , который проверяетпростые числа, возвращаемые итератором (кстати, с применением еще одного циклаf o r e a c h внутри итератора).
Если простое число превышает 13, в блоке выполняется инструкция y i e l d b r e a k , которая прекращает возврат простых чисел итератором. В противномслучае работа итератора продолжалась бы, и каждая инструкция y i e l d r e t u r n давала очередное простое число, пока коллекция полностью не исчерпалась бы.Блоки итераторов произвольного вида и размераДо этого момента блоки итераторов выглядели примерно следующим образом:publicSystem.Collections.IEnumeratorGetEnumerator(){476Часть VII. Дополнительные главаyieldreturnsomething;}Однакоонимогут такжеприниматьи другиеф о р м ы — именованныхитераторови свойств классов.Именованные итераторыВместо т о г о ч т о б ы писать блок итератора в виде м е т о д а с и м е н е м G e t E n u m e r a t o r ( ) ,можно н а п и с а т ь именованный итератор— ф у н к ц и ю , в о з в р а щ а ю щ у ю и н т е р ф е й с S y s t e m .Collections.
IEnumerableвместоIEnumerator,котораянеобязанаиметьимяG e t E n u m e r a t o r ( ) — м о ж е т е назвать е е хоть M y F u n c t i o n ( ) .Вот, н а п р и м е р , п р о с т а я ф у н к ц и я , к о т о р а я м о ж е т и с п о л ь з о в а т ь с я д л я и т е р и р о в а н и ячетных ч и с е л от н е к о т о р о г о з н а ч е н и я в п о р я д к е убывания до н е к о т о р о г о к о н е ч н о г о значения — да, да, и м е н н о в п о р я д к е у б ы в а н и я : д л я и т е р а т о р о в э т о с у щ и е п у с т я к и !//EvenNumbers-//возвращаетчетные//порядкеclassопределяетчислаименованныйвитератор,определенномкоторыйдиапазоневубыванияEvenNumbers{//DescendingEvens - это "именованный итератор",в котором// используется ключевоеслово y i e l d break.Обратите// внимание на е г о и с п о л ь з о в а н и е в цикле f o r e a c h в функции// Main()publicSystem.Collections.IEnumerableDescendingEvens(intnTop,intnStop){////ifНачинаем с ближайшего к nTop четного числа,непревосходящегоего(nTop % 2 != 0)// Если n T o p н е ч е т н оn T o p -= 1;// И т е р а ц и и от nTop в п о р я д к е уменьшения до ближайшего// nStop четного числа,превосходящего егоf o r ( i n t i = n T o p ; i >= n S t o p ; i -= 2)if////yie}(i < nStop)yield break;Возвращаем очередное четноеитерацииldreturni;числонаккаждой}Метод D e s c e n d i n g E v e n s () получает два аргумента (удобная возможность), определяющих верхнюю и нижнюю границы выводимых четных чисел.