Г. Шилдт - С#4.0 Полное руководство (1160795), страница 112
Текст из файла (страница 112)
В приведенном ниже простом примере демонстрируется механизм наложения ограничения на базовый класс. // Простой пример, лемонотрируюций механизм наложения // ограничения на базовый класс. пзьпд Бузгеи; о1азз А ( рпЬ11с чогг( Не11о() ( Сопзо1е.игьсевтпе("Не11о"); ) Глава 18. Обобщения 587 // Класс В наследует класс А. с1авв В ". А ( ) // Класс С не наследует класс А. с1авв С ( ) // В силу ограничения на базовый класс во всех аргументах типа, // передаваемвх классу Тевс, должен присутствовать базовый класс А.
с1авв Тевт<Т> ниеге Т : А ( Т оЬЯ; рпЬ11с Тевг(Т о) ( оЬ) = о; ) роЬ11с чогб ЯауНе11о() ( // Метод Не11о() вызывается, поскольку он объявлен в базовом классе А. оЬ).Но11о(); ) с1авв ВазеС1азвСопвггагпгоешо вгагтс ноьб Ма1п() ( А а = пен А(); В Ь = пеи В(); С с = пен С()) // Следукший код вполне допустим, поскольку класс А указан как базовый. тент<А> с1 = пен тенг<А>(а)/ С1.ЯауНе11о()/ // След)повий код вполне допустим, поскольку класс В наследует от класса й. Тент<В> С2 = пен Тент<В>(Ь)/ С2.зауНе11о()/ // Следукший код недопустим, поскольку класс С не наследует от класса А. // тевс<с> сз = пеи тезс<с>(с)/ // Ошибка! СЗ.ЯауНе11о(); // Ошибка! ) ) В данном примере кода класс А наследуется классом В, но не наследуется классом С.
Обратите также внимание на то, что в классе й объявляется метод Не11о (), а класс тезс объявляется как обобщенный следующим образом. с1азз Тезг<Т> мнете Т : А ( Оператор мнете в этом объявлении накладывает следующее ограничение; любой аргумент, указываемый для типа Т, должен иметь класс А в качестве базового. А теперь обратите внимание на то, что в классе тезс объявляется метод ЯауНе11о (), как показано ниже. 588 Часть (. Язык С(г роЫТо чотб Яауне11о() ( !/ Метод Не11о() вызывается, поскольку он объявлен в базовом классе А.
оЬЗ.Не11о(); ) Этот метод вызывает в свою очередь метод Не11о () для объекта ОЬЗ типа Т. Любопытно, что единственным основанием для вызова метода Не11о () служит следующее требование ограничения на базовый класс любой аргумент типа, привязанный к типу Т, должен относиться к классу А или наследовать от класса А, в котором объявлен метод Не11о ( ) . Следовательно, любой допустимый тип Т будет также определять метод Не 1 1 о ( ) . Если бы данное ограничение на базовый класс не было наложено, то компилятору ничего не было бы известно о том, что метод Не11о () может быль вызван для объекта типа Т.
Убедитесь в этом сами, удалив оператор ыпеге из объявления обобщенного класса Тезб. В этом случае программа не подлежит компиляции, поскольку теперь метод Не11о () неизвестен. Помимо разрешения доступа к членам базового класса, ограничение на базовый класс гарантирует, что в качестве аргументов типа могут быть переданы только те типы данных, которые наследуют базовый класс. Именно поэтому приведенные ниже строки кода закомментированы.
Тевп<С> СЗ = пен Тезп<С>(с); // Ошибка! СЗ. ЯауНе11о (); О Ошибка! Класс С не наследует от класса А, и поэтому он не может использоваться в качестве аргумента типа при создании объекта типа Тезс. Убедитесь в этом сами, удалив символы комментария и попытавшись перекомпилировать этот код. Прежде чем продолжить изложение дальше, рассмотрим вкратце два последствия наложения ограничения на базовый класс. Во-первых, это ограничение разрешает доступ к членам базового класса из обобщенного класса. И во-вторых, оно гарантирует допустимость только тех аргументов типа, которые удовлетворяют данному ограничению, обеспечивая тем самым типовую безопасность. В предыдущем примере показано, как накладывается ограничение на базовый класс, но из него не совсем ясно, зачем это вообще нужно. Для того чтобы особое значение ограничения на базовый класс стало понятнее, рассмотрим еще один, более практический пример. Допустим, что требуется реализовать механизм управления списками телефонных номеров, чтобы пользоваться разными категориями таких списков, в частности отдельными списками для друзей, поставщиков, клиентов и т.д.
Для этой цели можно сначала создать класс РЬопеМишЬег, в котором будут храниться имя абонента и номер его телефона. Такой класс может иметь следующий вид. // Базовый класс, в котором хранятся имя абонента и номер его телефона. с1азв РЬопеношбег ( роЬ1>с РбопенпшЬег(вттьпс и, вог1пд пшп) ( Маше = и; МшпЬет = ппш; Автоматически реализуемые свойства, в которых хранятся имя абонента и номер его телефона. роЫ1с вог1пс МошЬет ( Чеш веш ) роЫТс аьгьпс Маше ( сею вею ) Глава 18.
Обобщения 589 Далее создадим классы, наследующие класс РЬопенптЬех: Рхфепб и Бпрр11ех. Эти классы приведены ниже. О Класс для телефонных номеров друзей. с1аяя Рх1епб : -Рпопенпюпех ( рпЫТс Рххепб(ясхтпч и, ясхьпо пшя, Ьоо1 ию Ьаяе(п, ппю) ( ХянохХМшгЬех = ик; ) рпЫТс Ьоо1 1яиох)гМпкпех ( десг рх1тяпе яеЮ О О Класс для телефонных номеров поставюиков. с1аяя Яирр11ех : Рпопеншпбех ( риЫТс Бирр11ех(яххтпу и, яххгпд пшя) Ьаяе(п, ппю) Обратите внимание на то, что в класс Рх1епб введено свойство 1яиоххнпшЬе х, возвращающее логическое значение ххпе, если номер телефона является рабочим.
Для управления списками телефонных номеров создадим еще один класс под названием РЬопеЬТяб. Его следует сделать обобщенным, поскольку он должен служить для управления любым списком телефонных номеров. В функции такого управления должен, в частности, входить поиск телефонных номеров по заданным именам и наоборот, поэтому на данный класс необходимо наложить ограничение по типу, требующее, чтобы объекты, сохраняемые в списке, были экземплярами класса, производного от класса РЬопенип1Ье х. УУ Класс Раопеъьяп способен управлять любым видом списка телефонных номеров, при условии, что он является производным от класса РпопенпюЬех.
с1аяя Рпопеььях<Т> ипехе Т : Рпопенпюпех ( т(] рпьвяХГ 1пс епб; рпЫТс Рпопеъьяп() ( рпъьяс = пеи Т(10]; епб = Ог ) // Добавить элемент в список. рпЬ11с Ьоо1 Абй(т пеикпбху) ( 11(епб == 10) хесихп Тя1яе; РЬЬТяс(епб] = пеиЕппхуг епб+Ю хехпхп ххпе; 590 Часть (. Язык С() // Найти и возвратить сведения о рпЬ1гс Т Рьпс(вунаюе(ясг1пд пате) Рог(гпг 1=0; 1<епбг 1++) ( // Имя может использоваться, О относится к членам класса // базовым по накладываемому 1Г(РЬЬьяс(1].капе == папе) гегпгп рпъгяс[1]; ) телефоне по заданному имени. ( потому что его свойство Наше РЬопеншпЬег, который является ограничению. // Имя отсутствует в списке. ГЬгои пеи НосгоппдЕхсертьоп(); ) О Найти и возвратить сведения о телефоне по заданному номеру. рпЬ11с Т Р1ппвукшпЬег(ясгьпч ппюбег) ( Гог(зпс г=бг 1<епсб 1++) ( // Номер телефона также может использоваться, поскольку его свойство НшпЬег относится к членам класса РЬопекчтЬег, // который является базовым по накладываемому ограничению.
1Г(РЬъзяс(1].юшпЬег == пшпЬег) гесигп РЬШяс(1]; // Номер телефона отсутствует в списке. СЬгои пеи Ноггоппбкхсерс1оп()г ) с1аяя Ноггочппкхсерсгоп : Ехсергьоп ( /* Реализовать все конструкторы класса Ехсерсгоп. Эти конструкторы выполняют вызов конструктора базового класса. Класс ИоггоипбЕхсергьоп ничем не дополняет класс Ехсергьоп и поэтому не требует никаких дополнительных действий. */ рпбзьс НосгоппбЕхсерс1оп(] : Ьаяе() ( ) рпбьзс Носгоппбкхсерггоп(япггпч ясг) : Ьаяе(ясш ( ) рпЬ1гс Ногропппкхсерсгоп( ясггпя ясг, Ехсерс1оп 1ппег) : Ьаяе(ягг, гппег) ( ) ргогесгес) Носгочппкхсерггоп( Яуясею.Кппгыпе.зегьа11яас1оп.зегга1ггасьопгпто яь, Яуясесп.яппсгюе.зегга11гап1оп.зпгеапппдсопсехс яс) Ьаяе(в1, яс) ( ) Ограничение на базовый класс разрешает коду в классе РЬопеьфяь доступ к свойствам Наше и НпшЬег для управления любым видом списка телефонных номеров.
Оно гарантирует также, что для построения объекта класса РЬопепфяг будут использоваться только доступные типы. Обратите внимание на то, что в классе РЬопеЬ1яь генерируется исключение Ноброппс)Ехсергфоп, если имя или номер телефона не найдены. Это специальное исключение, объявляемое ниже.
Глава 18. Обобщения 591 В данном примере используется только конструктор, вызываемый по умолчанию, но ради наглядности этого примера в классе исключения Мотроцпс(Ехсер11оп реализуются все конструкторы, определенные в классе ехсерсьоп. Обратите внимание на то, что эти конструкторы вызывают эквивалентный конструктор базового класса, определенный в классе ехсерсаоп. А поскольку класс исключенил мосроцпс(ехсерс1оп ничем не дополнлет базовый класс ЕхсерС1оп, то для любых дополнительных действий нет никаких оснований. В приведенной ниже программе все рассмотренные выше фрагменты кода объединлютсл вместе, а затем демонстрируется применение класса РЬопеЬ1яг.