Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 87
Текст из файла (страница 87)
Взгляните на правильную реализацию: роЬ11с с1еяя ЯЬарея<Т> еЬеге Т: 1ЯЬаре ( роЬ11с оосЬ1е Тога1Агез ( Бес ( ОооЬ1е асс = О! 330 Глава! 1 Гогеэсь( Т яияре 1п яьярея ) ( ясс += яьаре.Атея; ) геспгп ясс) ) ) риЬ11с чоьб Абб( Т яляре ) ( япярея.Абб( эьяре ); ) ргачасе Ьуяс<Т> яьярея = пен 11яс<Т> О Обратите внимание на дополнительную строку, которая следует сразу за первой строкой объявления класса и в которой используется ключевое слово нлеге. Она говорит следующее: "Определить класс Бпарея<Т>. где Т должен реализовывать интерфейс 15Ьэре". Теперь у компилятора есть все необходимое для обеспечения безопасности типов. и ЛТ-компилятор также получит все необходимое для построения кода во время выполнения. Компилятор получает подсказку, помогающую уведомить об ошибке времени компиляции, когда предпринимается попытка создать конструируемый тип, в котором Т не реализует 1ЯЬаре.
Синтаксис ограничений замечательно прост. Для каждого параметра типа может существовать одна конструкция нпеге. Вслед эа параметром типа в конструкции нлеге может быть перечислено любое количество ограничений. Однако только одно ограничение может указывать имя класса (поскольку СЬК не поддерживает множественного наследования), поэтому такое ограничение называется первичным ограничением.
Кроме того. вместо указания имени класса первичное ограничение может перечислять специальные слова с1яяэ или яггисг, используемые для указания того, что параметр типа может быть классом или структурой. Конструкция ограничения может включать произвольное количество вторичных ограничений, таких как список интерфейсов, которые должны быть реализованы параметризованным типом. И, наконец, можно перечислить ограничения конструктора. которые принимают форму пен () в конце списка ограничений. Они ограничивают параметризованный тип так, что в нем должен быть предусмотрен конструктор по умолчанию, не имеющий параметров.
В типах классов должен быть явно определен конструктор по умолчанию, удовлетворяющий этому ограничению, в то время как типы значений имеют конструктор по умолчанию, сгенерированный системой. Каждую конструкцию нлеге принято указывать в отдельной строке, в любом порядке под заголовком класса. Каждое ограничение, следующее за двоеточием после ипеге, отделяется от соседних запятой. Рассмотрим несколько примеров ограничений: ияапд Яуясеа.оо11есс1опя.йепег1св риЬ11с с1яяя Мууа1иеъуяс<Т> нлеге Т: ясгисс // А гях нельзя: // эьеге Т: ясгссс, пен() ( рпЬ11с чо(б Абб( Т ч ) ( ипр.Абб( ч ); ) ргучясе 11яс<Т> Тир = печ Ьтяс<Т>() ) риь11с с1аяя Епсгуго1пс ( ясяс1с чогб Ма1п() ( Обобщения 331 МуЧа1иеЬтвс<тпс> Бпсъбвг пен Муча1ие11вг<тпг> О я 1пгьтэв.абб( 123 )я // ТКК ЯЕЛЪЗЯ.
// МуНа1ие01вс<оЬ»есс> оЬ»01вг // печ Муча1ивь1ВС<ОЬ»ЕСГ>()я ! ) В этом коде показан пример применения ограничения в с си ос в объявлении контейнера, который может содержать только типы значений. Данное ограничение предотвращает объявление переменной оЬ» Ы в с. Это объявление помещено в комментарий, потому что иначе во время компиляции было бы выдано следующее сообщение об ошибке: еггог СБ0453: ТЬе Гуре 'оЬ»есс' вовс Ье а поп-пи11аЬ1е ча1ие Гуре яп огбег Го иве ьс ав рагавесег 'Т' Тп ГЬе Бепегбс Гуре ог веспоб 'МуЧа1иеьтвг<Т>' ошибка СБ0453: Тип оо»есс долмен быть типом значения, не допускающим пи»1, чтобы его мокни было использовать в качестве параметра Т в обобщенном типе или методе Мучв1иегбяс<Т> В качестве альтернативы может быть сформулировано ограничение, разрешающее только типы классов.
Между прочим, в версии компилятора Сй из Ч!виа( Впиб(о создать ограничение, включающее и с1авв и вггисс, невозможно, Конечно, это было бы бессмысленно, поскольку аналогичный эффект дает отсутствие обоих ограничений в списке. Тем не менее, если попытаться сделать это, компилятор выдаст следующее сообщение об ошибке: еггог 030449: Тпе 'с1авв' ог 'всгисс' сопвсгатпс вовс сове Ьетоге апу оспег сопвсга1пгв ошибка СБ0449: Ограничение с1ввв или всгисс долмно находиться перед любыми другими ограничениями Наверное, компилятору стоило бы сообщить, что разрешено только одно первичное ограничение. Кроме того, строка альтернативного ограничения, в которой предпринимается попытка включить ограничение пеы () для того, чтобы потребовать от типа Т поддерживать конструктор по умолчанию.
помещена в комментарий. Очевидно, что для типов значений это ограничение избыточно, но оно вполне безобидно. Тем не менее, компилятор не позволяет применить ограничение пен () вместе с ограничением всгисг. А теперь давайте рассмотрим чуть более сложный пример, в котором показаны две конструкции ограничений: ивтпч Бувгев; ив1по Бувгев.Со11есстопв.зепегтс; риЬ11с Тпгеггасе 1Ча1ие ( // Методы 1На1ие.
ривттс с1аэв Муотсгтопагу<ткеу, тЧа1ие> ниеге ТКеу: в<гипс, 1СоврагаЬ1е<ТКеу> ньеге ТЧа1ие: 1Ча1ие, печ() ( риЬ11с чояб абб( ТКеу Ееу, ТЧв1ие ча1 ) ( 1вр.абб( кеу, ча1 ); ) ргтчасе втсгтопагу<ткеу, тча1ие> 1вр = печ Шсгтопагу<ТКеу, ТНа1ие>(); ) 332 Гзэзз (( Класс му01сс1осагу<ткеу, тча1се> объявлен таким образом, что значение ключа ограничено типами значений.
Также необходимо, чтобы значения ключей были сравнимы между собой, поэтому выдвинуто требование, что тип ткеу должен реализовать интерфейс 10окрагаЬ1е<ткеу>. В пример показаны две конструкции ограничений— гю одной для каждого параметра типа. В этом случае типу Туа1пе разрешено быть структурой или классом, но при этом он должен поддерживать интерфейс 1уа1пе вместе с конструктором по умолчанию. В целом механизм ограничений, встроенный в обобщения С(), прост и понятен.
Сложностью ограничений легко управлять и расшифровывать ее с минимумом (или без) сюрпризов. С развитием языка и СЬК можно ожидать, что эта часть будет дополнена по мере нахождения областей для применения обобщений. Например, возможность использования ограничений с1азз и зсгссс была добавлена к стандарту относительно недавно. И. наконец, формат ограничений на обобщенные интерфейсы идентичен тому же формату для классов и структур. Ограничения на неклассовых типах До сих пор речь шла об ограничениях в контексте классов, структур и интерфейсов. В действительности любая сущность, которую можно объявить обобщенным образом, допускает применение ограничений.
В объявлениях обобщенных методов и делегатов ограничение следует за списком формальных параметров метода или делегата. Применение конструкций ограничения в объявлениях методов и делегатов порождает несколько странно выглядящий синтаксис, как показано в следующем примере: изгое Бузсеп; ровьтс с(е1еоасе к Орегас1ос<т1, т2, к>( т1 гз11, Т2 за12 ) кьеге Т1: зсгисг зьеге Т2: зггссг зьеге Рл зсгзсс( рсьгьс с1азз ЕпсгуРоьсе ( рсЬ11с зсасьс с(оиЬ1е Л<(О( 1сс ча11, Г1оаг за12 ) ( гесогс на11 е ча12г ) згаггс чоьб Маго() ( чаг ор = сез орегасьоп<ьсг, г1оас, с(ооь1е>( ессгуРо1сг.лоб ); Сосзо1е.нг1се(гпе( "(О) + (1) = (2)", 1, 3.2, ор(1, 3.2г) ); ) В коде объявляется обобщенный делегат для метода операции, который принимает два параметра и имеет возвращаемое значение. Ограничение заключается в том, что параметры и возвращаемое значение должны быть типами значений.
Аналогично, для обобщенных методов конструкции ограничений следуют за объявлением метода, но предшествуют его телу. Ковариантность и контравариантность Вариантность — зто то, что описывает возможность преобразования и способность делать то, что имеет смысл для типа. Например, рассмотрим следующий код. где демонстрируется ковариантность массивов, которая была возможной в С() со времен версии 1.0л Обобщения 333 пя1по Яуятещ; зсаСтс с1аяя ЕпстуРо1пС ( ясастс чосб Мата() яст1по() ясттпдя = пеи яст1пч() "Опе", "Тна", кТЬтее" )' Отяр1ауяст1пдя( ясттпоз )я // Правила аовариалеаооеи массивов // доаускаюз оладьев(ее лриовалвалие оЬбеое[) оЬбесев = еег1поея // Но что теперь произойдет? аЬбесСя[1) = пеа оЪбесс(); О1яр1ауэст1поя( яст1пдя ) зсастс чотб О1яр1ауяст1поя( ясттпд[) яст1поя ) ( Сопяо1е.втстес[пе( " †--- Вывод строк ††-" ) Готеасп( чат я тп ятт1пдя ) ( Сопяо1е.нтттестпе( я ); ) ) ) В начале метода Матп создается массив строк, который затем немедленно передается в О1зр1ауЯстспцз для его вывода на консоль, после этого переменной типа оьбессз () присваивается значение переменной яст1пдя.поскольку яст1пдз и оьбесся являются переменными ссылочных типов, на первый взгляд логично иметь возможность присвоить ест[поз переменной оЬ1есгя, так как тип ятт1по неявно преобразуем в тип оЬЯ ест.












