Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 85
Текст из файла (страница 85)
Те из вас, кто забежал вперед в этой главе, могут недоумевать — почему нет никакой формы ограничения (об ограничениях речь пойдет дальше), чтобы подсказать компилятору. что это делегат? Но даже если бы такая форма ограничения существовала, компилятор может быть не известно, как вызвать этот делегат. Ограничение не может нести никакой информации о количестве параметров, принимаемых делегатом. Вспомните, что в отличие от шаблонов С++. обобщения являются динамическими, и закрытые типы формируются во время выполнения, а не во время компиляции.
Поэтому во время выполнения делегат, представленный бе1, может принять произвольное количество параметров. Только представьте себе головную боль, вызванную попытками придумать способ занести динамическое количество параметров в стек перед вызовом делегата. По всем этим причинам редко имеет смысл создавать закрытый тип из обобщенного, когда один из аргументов-типов является типом делегата, так как, в конечном счете, все равно нормальный вызов осуществить через него не удастся.
Чтобы помочь в этой ситуации, мол<но применить обобщенный делегат, предоставив компилятору немного больше информации о том, что необходимо делать с этим делегатом. В этом примере, используя обобщенньш делегат, можно на самом деле заявить: "Нужно, чтобы использовались делегаты, принимающие только один параметр обобщенного типа и возвращающие чобб". Втой информации достаточно, чтобы позволить компилятору воспринять блок и сгенерировать код, имеющий смысл для обобщения. В конце концов, если предоставить компилятору такой объем информации, то, по крайней мере, он будет знать, сколько параметров нужно затолкнуть в стек перед вызовом через делегат.
В следующем коде показано, как исправить предыдущую ситуацию: пя1по Зуятев! пятое Яуятев.Со11ест1опя.оепет1с; рпЬ11с бе1еоате яобб Мупе1едаге<Т>( Т 1 рпЬ11с с1аяя пе1еоасесопса1пет<т> ( рпЫТо тоьб Лбб( Муое1еоаге<Т> бе1 ) ( 1вр.лбб( бе1 ) 1 роЫ>с яотб Са110е1еоэгея( Т Н ) ( Тотеэси( Мусе1еоате<Т> бе1 1п Твр ) ( бе1( к ); рт1уаге Шят<иусе1едате<Т» Твр = пеи Еьяг<Мусе1едаге<Т» () ) рпЫТс с1аяя Епгтуроьпг ( ятаг1с яотб Маьп() ( ое1едятеСоптатпет<бпг> бе1еоаяея = пеи ое1еоягесопга1пет<1пг>() бе1еоагея.хбб( ЕпгтуРо1пС.Рттпг1пг ) 1 бе1еоэгея.са110е1епагея( 42 ); ) ятагбс уогб Ртьпт1пт( 1пя 1 ) ( Сопяо1е.кт1ге11пе( 1 ) ) 322 Глава 11 Преобразование обобщенного типа Как уже упоминалось ранее в атой главе, неявных преобразований разных конструируемых типов, сформированных иэ одного обобщенного типа, не существует.
Те же правила, которые действуют при определении того, является ли объект типа Х преобразуемым в объект типа у, в равной мере касаются определения возможности преобразования объекта типа ьазс<ьпс> в объект типа 11эс<оь1есс>. Когда такое преобразование желательно. должна быть создана пользовательская операция преобразования, как в случае преобразования объектов типа Х в объекты типа Х. когда они разделяют отношение наследования. В противном случае необходимо создать метод преобразования, чтобы превратить один тип в другой.
Например, следующий код неверен: // НЕВЕРНЫЙ КОД!!! роьььс ноьн Вотемесьог)( ььзс<1пс> сьеььзс ) 1 Ь1зг<оЬбесг> СнезаэеЬТзг = СнеЬТзг; г',г Так поступать нельзя!!! ) На заметку) С появлением в С№ 4.0 синтаксиса для обьявления обобщенных интерфейсов и делегатов как вариантных это уже не совсем так. Но в рамках этого раздела все верно до тех пор, пока все обобщенные типы трактуются как инвариангные, что получается по умолчанию, если обобщенное объявление не декорировано синтаксисом вариантности. Если вы заглядывали в документацию по 11зс<т>. то, вероятно, заметили там обобщенный метод по имени сопнеггА11<тоосрос>.
Используя этот метод, можно преобразовать обобщенный список типа 1 Тзк<№пг> в 11зг<оЬТесг>. Однако атому методу должен передаваться экземпляр обобщенного делегата преобразования. как было описано в предвгдущем разделе. Таким образом, чтобы преобразовать 1 Еэг<ьпг> в Ь1зк<оЬч ест>, придется предоставить делегат, который знает, как выполнить преобразование в экземпляр оЬч есс. Исцольэование данного делегата — это единственный способ для метода Сопчеггл11<ТОигрог> узнать, как ему следует преобразовать каждъш экземпляр содержимого из исходного типа в целевой.
Те, кто знаком с шаблоном 8)гасе)гу )Стратегия), увидят здесь нечто знакомое. По сути, метод сопчегсй11<тоисрис> можно обеспечить во время выполнения средствами осуществления преобразования содержащихся в коллекции экземпляров, которые, в зависимости от сложности преобразования. могут быть настроены под платформу, на которой выполняются. Другими словами, если есть намерение преобразовать ьхзс<ярр1ез> в ь1эс<Огапоез>, то придется предоставить несколько методов различных преобразований на выбор, в зависимости обстоятельств. Например, один из них может быть предусмотрен для среды с богатыми ресурсами, чтобы работать с максимальной скоростью. Другой метод может быть оптимизирован для минимального потребления ресурсов, но при этом работать медленнее.
Во время выполнения строится соответствующий преобразующий делегат для привязки к методу преобразования, который максимально подходит для выполнения работы в конкретном случае. Выражение значения по умолчанию Иногда при работе с определениями обобщенных типов и обобщенных методов нужно инициализировать объект или экземпляр значения параметризованного типа его значением по умолчанию. Вспомните, что значение по умолчанию для ссылочной переменной — это то же самое. что установка ее в по11, в то время как значение по умолчанию для типа значений эквивалентно установке всех его бит в О.
Необходимо построить выражение для обобщений. учитывающее это семантическое отличие, и для Обобщения 323 этой задачи можно использовать выражение значения по умолчанию [с[есаи1с),пока- занное в следующем примере кода: ивзпо Яувгепн риЬ1зс с1авя мусопсагпег<т> ( риЬ11с МуСопгазпег() ( у/ Начальное наполнение. 1шр = пен Т[ 4 ]; Тот( лпС з = 04 1 < Тшр.сепчппз ь+1 ) ( 1вр[ь] = бетаи1С(Т); ) рив11с Ьоо1 1яРегаи1С( 1пС 1 ) ( зс( с < 0 (( з >= 1шр.ьепдСЬ ) ( Спгон пен АгдишепгоиСОТНапоевхсерС1оп О 11( 1тр[1) == пи11 ) ( гесигп сгие; ) е1яе ( гегигп Та1ве) ) рг)лаге Т[] ппрз риЬ11с с1аяв Епггурозпг ( вгагзс чозб Мазп() ( МуСопгагпег<1пС> зпССо11 = пен МуСопсаспег<зпс>()з МуСопсазпег<оЬ]есс> оЬ]со11 = пен мусопсаьпег<оЬ]есс>()з Сопво1е.нгсгеьапе( ТпсСо11.1яоеГаи1с(0) Сопяо1е.нгсгеЬьпе( оЬ]Со11.1впесаи1с(0) )з Обратите внимание на синтаксис внутри конструктора МуСопгаз.пег<Т>, где каждый элемент массива явно инициализируется его значением по умолчанию.
Во время выполнения тип Т может быть типом значений или ссылочным типом. поэтому нельзя просто присвоить значение пи11 и рассчитывать, что зто будет работать с типами значений, Фактически, если попытаться присвоить 1ар [з ] значение пи11, то компилятор выдаст следующее сообщение об ошибке: еггог СЯ0403з Саппоп сопчегг пи11 Со Суре рагатепег 'Т' Ьесаияе 1С сои10 Ье а поп-пи11аьсе ча1ие Суре. Сопвсбег иззом 'бегаи1С(Т)' зпвпеаб. ошибка СЯ0403з Неаозмозно преобразовать пи11 к параметру тяпа Т, поскольку ОН ЛОЛЗЕН быть типом значения.
Взамен нспольэуйте бегаи1С(Т). Выражение с(еТаи1С также должно использоваться при проверке переменной на предмет ее равенства значению по умолчанию, поскольку оно означает разное для типов значений и ссылочных типов. Однако в этом случае компилятор не может помочь узнать, когда необходимо поступать так, как показано в примере. Запуск ранее приведенного кода дает следующий вывод: Еа1ве Тгие 324 Глава 11 Это ие тот результат, которого следовало ждать, потому что оии обе проверки должны возвращать г тие. Если модифицировать код тан, чтобы метод 1 япе Еаи1г выглядел, нак показано ниже, то получится вывод.
который соответствует ожидаемому: риЫтс Ьоо1 1япетаи1т( тит т ) ( 11( 1 < 0 (( 1 >= 1вр.ЬепчтЬ ) ( ЬЬтон пен Атчип!ептоитотнанчеЕхсерт1оп() ) 11( ОЬбеес.кпрпа1я(Тир[11, п(еааи1Е(Т) ) ) ( тетитп етое! ) е1ве ( петита га1яе! ) Типы, допускающие значения уш11 С предыдущим обсуждением связана концепция значений ии11 и семантический смысл, который оиа может нести. Состояние ои11 для ссылочиых типов легко представить. Если значение ссылки установлено в ии11, обычно это значит, что переменная просто ие имеет значения.
Это семантически вовсе ие то же самое, нан если сказать, что значение равно О. Семантически переменная, установленная в ии11, ие имеет зиачеиия — даже значения О. В отношении же типов значений представить семантическое значение ии11 традиционно намного труднее. Если установить значение О, зто может означать ои11. Но что делать, если действительно нужно представить значение О, а ие ои112 Многие приемы предцолагиог поддержку дополнительного булевсного значения, которое сопровождает значение, наподобие (.яни11. Чтобы избавить от необходимости использования такого нелепого, чреватого ошибками механизма, в библиотеке базовых классов .(ЧЕТ предлагается тип Яуятев. Ии11аЬ1е<Т>, применение которого демонстрируется в следующем коде, где показаны два способа использования типов, допускающих значения ии11: иятич яуятев! риЬ1>с с1авя Евр1оуее ( риЪ11с Евр1оуее( ятттич Е1тятнаве, ятгтпд 1аяткаве ) ( ЬЫя.т[тяткаве = 51тяткаве! тыя.1аятнаве = 1аятнаве! тыя.гетв[иат1опсате = ии11! ЬЫя.яяп = п)етаи1Г(ни11аЬ1е<1оид>) риЫтс ятт[пч 51тятмаве! риЫтс ятттпд 1аяткаве! риЬ1тс Ни11аЬ1е<Оатет[ве> тетв1паттоипате! риЬ1тс 1ои07 яяп; /У Сокращенная нотация ) риЫтс с1авя Епттуро[пт ятаттс пота Мате() Евр1оуее евр = пен Евр1оуее( "иаяуа", "Рирк[п" евр.яяп = 1238567890! Соияо1е.кт[те([пе( "(0) (1)", евр.т[тятнаве, евр.1аяткап!е Обобщения 325 11( епр.гегвгпаТ1опрасе.иаяуя1пе ) ( Сспяс1е.нгасеьгпе( "ЯТвгТ ВаТе: (О)", еягр.сеглггпаТ1спрясе )Г ) 1опо Теврввн = еар.яяп Зт -1; Сопяо1е.нг1Те)гпе( "ЯБН: (О]", Теярзян ); В приведенном коде демонстрируются два способа объявления типа, допускающего пп11.














