Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 132
Текст из файла (страница 132)
Давайте посмотрим, как использовать расширяющие методы для построения пользовательских итераторовна типах, реализующих 1ЕпциегаЬ1е<Т>. Например, представьте двумерную матрицу, реализованную в виде типа 11вс<11вс<1пс». При выполнении некоторых операций на таких матрицах принято использовать итератор, который перемещается по матрице в режиме ведущей строки. Это значит, что итератор сначала перемещается по элементам первой строки, затем второй и тд. — до тех пор, пока не достигнет конца последней строки.
Итерация по матрице в режиме ведущей строки реализуется с помощью следующего кода: цз1п9 Яузсеи> цв1пс Бузпеи.со11есо1опв.йепег1сп рцЬ11с с1азз 1пегапогЕхавр1е ( зпаоьс по1п( Наьп () паг иапгьх = пен 11вп<11зп<1пп» ( пеи 11вс<ьпп> ( 1, 2, 3 ), пеи 11во<1пп> ( а, 5, б ), пен 11зп<ьпп> ( 7, 8, 9 ) )' /! Один ив способов итерации по матрице.
Гогеасп( паг 11вп 1п тасг1х гогеаси( паг 1оеи зп 11вп ) ( Сопво1е.иг1ве( "(0), ", Тпеа ): ) Сопзо1е.кгьпе11пе()п ) ) Хотя этот код свою работу выполняет, его не слишком удобно использовать повторно. Давайте посмотрим, как сделать то же самое с помощью расширяющего метода; цз1п9 Яузпепы цз1пч Яузпет.Со11есп1опв.Оепег1ся 492 Глава ) 4 риЬ11с ясзгтс с1аяя Ссясоетгегаксгя ( рсыас веаеас 1впшвегаые<т> яеедонма)ог1еегаеог<т>( еыв ьавт<ьаве<т» заехал ) ( Тогеась( чвг гоч зп тасках ) ( Тогеасп( чаг 1вел 1п гои ) ( узе16 гвсигп ввел; ) ) рпЬ11с с1аяя тсегаСогЕхапр1е ( ясагтс чогб Натп() ( чзг еакгтх = пен 11яс<ьтяс<1пг» ( пен 11як<1пс> ( 1, 2, 3 ), пен Ьтяс<1пс> ( 4, 5, 6 пен 11яс<1пг> ( 7, 8, 9 ) )г // Более елея амелий спосое перечлслелюк елемелеов.
Тогеась( чаг Тесл 1п ласгзх.яесдоимабогтсегасог() ) ( Сопво1е.игзсе( "(0), ", 1вел ) г ) Сопяс1е.нг1сеЬТпе(); В этой версии итерация вынесена в расширяющий метод ВесРонма) ог1сегасог<т>. В то же время он сделан обобщенным, чтобы принимать двумерные вложенные списки, содержащие элементы любого типа; тем самым метод получился более повторно используемым. Заимствование из функционального программирования Вы наверняка заметили, что многие иэ средств, добавленных в СФ 3.0, упрощают применение модели функционального программирования. Возможность реализации функционального программирования присутствовала в СВ и раньше, но новые языковые средства облегчают это с синтаксической точки зрения, делая язык более выразительным. Иногда средства функциональной модели позволяют проще решать самые разные задачи.
Существует несколько языков, которые относятся к категории функциональных, и (Лзр — один из них. Если когда-либо доводилось программировать на Еззр, то известно, что одной из главных структур в этом языке является список. В СВ можно моделировать такой список с использованием следующего определения интерфейса: рсы1с Тпгеггасе 111яг<т> ( Т Неаб ( десг ) 111яг<т> та11 ( десг Внимание! Хотя в данном примере тип назван 1ьтяс<т>, не путайте его с тьтяс<т> из пространства имен яуясегх. со11есс1оп.
6епегтс. Если кто-то решит реализовать тип в том виде, как он записан здесь, то во избежание конфликтов лучше определить его в отдельном пространстве имен. В конце концов, в этом состоит одно из преимуществ пространств имен. Расширяющие методм 493 Структура этого списка несколько отличается от распространенных реализаций связных списков. Обратите внимание, что вместо одного узла, содержащего значение и указатель на следующий узел. она содержит значение в узле и ссылку на остальную часть списка.
Фактически эта структура по своей природе рекурсивна. В этом не ничего удивительного, так как рекурсивные технологии являются частью модели функционального программирования. Например, если представить традиционный список на бумаге, записав его элементы в скобках, то он может выглядеть примерно так: (1 2 3 4 5 б) С другой стороны, список, определенный с использованием интерфейса 111вс<т>, может выглядеть следующим образом: (1 (2 (3 (4 (5 (б (пи11 пп11) ) ) ) ) ) ) Каждый набор скобок содержит два элемента: значение узла и остальную часть списка внутри вложенной пары скобок. Таким образом, представить список только с одним значением, например, единицей, можно так: (1 (пп11 пп11) ) А пустой список может быть представлен следующим образом; (пп11 пп11) В следующем примере кода создается пользовательский список по имени муы я с <т>, реализующий 111яс<т>.
способ его построения не слишком вффективен, но очень скоро этой будет исправлено. иятпд яувсеа! петли Бувгеа.со11есгсопв.оепегсс~ РиЫТС гагегтвсе 111вг<т> ( Т Невс) ( сеС4 1етвс<т> тв11 ( дес4 РпЫТс с1ввв Муьсвс<т>: 111вс<т> ( рпЫТс всвстс 111вС<Т> Сгеагеттвс( 1ЕппаегвЫе<Т> ТСеав ) ( 1епппегзсог<т> 1сег = 1секв.сесеппкегвсог О 1 гегигп Сгевгектвг( Тгег ); ) рпЫТс вгвгтс 111вг<Т> Сгеагеьтвг( 1Еппаегвгаг<Т> Тгег ) ( 11( )Тгег.мотекехг() ) ( гегигп пеи Муьсвг<т>( оебзп1С(Т), пп11 ); ) гегигп пеи Муттвс<Т>( Тсег.сиггепг, Сгевгеттвс(тсег) ) Ргттвге МУЬТвС( Т аеас), 111вг<Т> Са11 ) ( Сп1в.севс) = )севс); СЫя.гв11 = Св11; ) рпЬ11с Т Невп' ( сес ( гегпгп певс); ) рпЫТс 1ьтвг<Т> Тв11 ( сес 494 Глава (4 геспгп Са11) ) ргачаге Т аеас) рг1чаге 1ьавг<т> са114 рпЫТс ввасас с1авз Совгопгсегасогз ( роыас веаеас Теппеегаые<т> ь1пхььаегеегасог<т>( еыв Ть1ва<т> еьеь1ве ) Тог( чаг 11вз = Евеььвс; 11зз.та11 ( па114 11вс = 11вЕ.Та11 ) ( у1е14( геазгп 11ве.
Неаб; ) ) рпЬ11с с1аяя 1сегасогьхаар1е ( ягагас чо1<( Маьп() ( чаг 11вг1пгя =-пев ьаяг<гпг> ( 1, 2, 3, 4 )4 чаг 11пИ1яв = МуЬ1вв<1пв>.СгеавеЬ1вв( 11яггпвв ); Гогеасл( чаг 1пеп гп 11п<Ь1яГ.Ь1п<ЬЬвс1вегапог() ) ( Сопяо1е.кгьве( "(0), ", Ьвеп ); ) Сопяо1е.кгьпеЬЬпе() Для начала обратите внимание в маап, что экземпляр муььяс<апс> инициализируется с использованием ь1вс<1пс>.
Статический метод сгеасеьаяс рекурсивно наполняет муь1яс<апс> этими значениями. После завершения сгеасеьаяс получается экземпляр муь1вс<1пс>, который может быть представлен следующим образом: (1 (2 (3 (4 (пп11 пп11) ) ) ) ) Возможно, возник вопрос, почему список представлен не в таком виде: а (г (3 (4 11)))) Можно было бы поступить и так, однако тогда бы обнаружилось, что с ним неудобно работать — как при формировании, так и при использовании. Если говорить об использовании, то может возникнуть необходимость выполнить итерацию по одному из этих списков. Для этого понадобится пользовательский итератор, код которого в примере выделен полужирным.
Этот итератор в Маап применяется для отправки всех элементов списка на консоль. Вывод выглядит следующим образом: 1, 2, 3, 4, Обратите внимание,что в этом примере метод Ььпльаяп1гегапог<Т> создает итератор в прямом направлении, исходя иэ некоторых предположений относительно того, как определять достижение конца списка и увеличивать курсор во время итерации. То есть итератор начинает просмотр списка с начала и завершает, когда хвост текущего элемента окажется равным пп11.
Что если вынести зту информацию наружу? Например, что если предоставить пользователю возможность параметризации порядка выполнения итерации, те, вперед, назад, циклически и тд.? Как это сделать? Если возникла идея применить делегаты, то ато правильно. Ниже показана измененная версия расширяющего метода итератора и метода Маьп: Расширяющие методы 495 риЬ11с вСвСХс с1ввв СивСоп1Сегзгогв ( риЬХЕс вгвС1с ХвпиаагвЬ1е<Т> Пепега11Сегазог<Т>( СЬЕВ 1Ывз<Т> СЬеЫэз, Типс<111ег<Т>, Ьоо1> Е1па1агаге, типс<1ывс<т>, Хьзвс<т» кпсгевепсег ) ( ии11е( )Екпа18Саге(СЬепзвС) ) ( у1е16 гезигп СЬепзвз.невк(; СЬеЫэз в 1псгеаепзег( Спевзвз )г ) ) ) риЬ1(с с1вяя 1Сегагогзхаар1е ягагзс чозс Мз(п() ( час 1(яС1пгя = пен 1(яС<зпС> ( 1, 2, 3, 4 )Г чвс 11п)гь(яС = Мущяс<зпС>.
Сгеагещяг ( 11вс1пкя ) 1 чаг 1сегвсог = 11п)сывс.ьепега1хсегвсог( <)е1едасе( хызс<зпс> 11вс ) ( гегигп 1кэС.ТвИ = пи11; ) бе1едазе( 1Ывг<1пг> 11вг ) ( гезигп 11вз. Та11; ) ); Есгвясп( чаг Хсеы Хп ХСегагог ) ( Сопво1е.игтге( "(О), ", ХСеи )г ) Сопво1е.нг(Сеьзпе()г ) ) Обратите внимание, что метод сепега11сегзсог<т> принимает еще два делегата, один из которых вызывается для проверки факта достижения курсором конца списка, а второй — для увеличения значения курсора.
В методе Мазп передаются два делегата в форме анонимных методов. Теперь метод сепега11сегасос<т> может использоваться для выполнения итерации по элементам списка просто модификацией делегата, который передается в параметре Хпсгешепсег. На заметку! Некоторые, возможно, уже знакомы с лямбда-выражениями, которые появились в С№ 3.0. Применив синтаксис лямбда-выражений вместо анонимных делегатов, можно существенно подчистить этот код.
Тема лямбда-выражений раскрывается в главе (5. В качестве финального примера применения расширяющего метода для операций над типом 11.).вС<Т> рассмотрим, как создать расширяющий метод для обращения списка и возврата нового экземпляра 111вс<т>. Существует множество способов сделать это, одни эффективные, другие — не очень. Однако рассмотрим пример, в котором используется некоторая форма рекурсии. Ниже показана реализация метода йенегяе<Т>: рибззс вгвСХс с1ввя Сивгот1СегзСогв ( риьз(с зсвсзс 111вс<т> кечегзе<т>( сь(з 111зс<т> сьеывс ) ( чвг гечегяе11вС = пвн 11вС<Т>(); Гипс<1ЫвС<Т>, 11вС<Т» гечегвекипс пи11; гечегвегипс = бе1едвсе(1ыяс<т> 11вс) ( 4Е6 Глаза!4 11( 11зс != пи11 ) ( гечегяеРипс( 11яГ.Тя11 11яс.тя11 != пи11 ) ( гечегяеЬ1яГ.Аси( 11яГ.Нези ) ) ) гесигп гечегяеЬЬяс; )' гесигп МуЬТяс<Т>.СгеясеЬЬяс( гечегяегипс(ГЬеЬЬяс) Если раньше не приходилось сталкиваться с таким стилем кодирования, его восприятие поначалу может вызвать затруднения.










