Вы находитесь на странице: 1из 1155

~ w w.willfamsрubIisl,ing...оm

Apress
;;
Эндрю Троелсе н
языK ПРОГРАММ'И'РОВАНИЯ
С#'2005
И ПЛАТФОРМА .NET 2.0
3-е из,дание

Эндрю Троелсен

Москва· Санкт-Петербург • Киев


2007
ББК 32.973.26-018.2.75
Т76
УДК 681.3.07

Издательский дом "Вильямс"

Зав. редакцией С.Н. ТрU2уб

Перевод с английского и редакция л.г. Сивака

По общим вопросам обращзйтесь в Издательский дом "Вильямс" по адресу:


info@w1lliamspublisblng.com, http://www.wilНamspublishing.com
115419. Москва, а/я 783; 03150, Киев, а/я 152

Троелсев, Эндрю.

Т76 Язык программирования С# 2005 и платформа .NEТ 2.0. 3-е издание. : Пер.
с англ. - М. : 000 "и.д. Вильямс~. 2007. - 1168 с. : ил. - Парал. ТИ1: ангЛ.
ISBN 5-8459-1124-9 (рус.)

в этой книге содержится описание базовых принципов функционирования плат­


формы .NEТ, системы типов .NEТ и различных инструментальных средств разработ­
ки. используемых при создании приложений . NEТ. Представлены базовые возможно­
СТИ языка программирования С# 2005, ВJ(ЛЮчая новые синтаксические конструкции.
появившиеся с выходом .NEТ2.0, а также синтаксис и семантика язьrn:а CIL. В книге
рассматривается формат сборок .NEТ, библиотеки базовых массов .NEт. файловый
ввод-вывод. возможности удаленного доступа, конструкция приложений Windows
Forms. доступ к базам данных с помощью ADO.NEТ, создание Web-приложений
ASP NEТ и Web-служб XМ:L. Книrа содержит множество примеров программного кода,
призванного помочь читателю в освоении предлагаемого материала. Программный
код примеров можно загрузить с Web-сайта издательства.

ББК 32.973.26-018.2.75

Все названия программных продуктов являются зарегистрированными торговыми маp1«iМИ со­

ответствующих фирм.
Никакая часть настоящего издания ни в к;utИX целях не может быть воспроизведена в какой
бы то ии было форме и какими бы то ни было средствами. будь то электронные или механические,
ВК1IЮЧан фотокопирование и запись на магнитный носитель. если на это нет письменного разреше­
ния издательства APress. Berkeley. СА.

Authorlzed trапslаtiоп from the EnglIsh language edltion pubUshed Ьу ЛPresэ. Inc .• Copyrlght © 2005.
АН rlghts reseгved. No part of thls work тау Ье reproduced ог transmltted lп апу (оrш ог Ьу anу
теans. electroDic or mechanlcal, lnclud!ng photocopy!ng. recordlng. or Ьу апу !nfопnаtiоп storage or
retr1eval system. without the pГior wr1tten permlSs!on of the copyrlght owner and the pubUsher.
Thademarked naтes тау арреаг !n thls book. Rather than use а trademark эутЬоl wlth every occu-
rrence of а trademarked пате. we иэе the патеэ Оn1у !n ап ed!toГial fash!on and to tbe beneflt of the
trademark оwnег, w1th по lntentlon of lnfrlngement о! the trademark.
Russlап language edltlon 18 pub1lshed byWlll1am8 Publlshlng Ноиэе accordmg to the Agreement with
R&l EnterpГises Intematlonal. Copyrlght © 2007.

ISBN 5-8459·1124-9 (рус.) © Издательский дом "Вильямс· . 2007


ISBN 1-59-059419-3 (англ.) © Ьу Andrew Тroelsen, 2005
--------------------------------------------------------------- ~

Оглавление

Часть J. Общи.е 0ведеНИR о языке С# и. платформе .NET 41


Гnaвa 1. Философия .НЕТ 4З
rnss8 2. ТеХИОЛОГ.14JI СО3Дани« приnожеиий на языке С# 79

Част!. Н. Я~ЫК программироваlilИR С# 113


Глава 3. Основы языка с# 115
Глава 4. Язык С# 2.0 и объеКТНО-QриеtПИpое8JoIНЬ!Й ПОЯХОА 203
Гnава 5. Цикn сущеатвованИII объеХТ08 249
r,naliia' 6. CrрУКтУ,рированнаА обрабоtta искmoчений 271
Гnава 7. ИJt,.ерфеiiiС~ и Коn.riеlЩ)ll:И 299
Гnава 8. Интерфейсы обратного sы0вз •• делегаты и соБЫТИЯ ЗЗ9
Глава 9. Специanькые приемы' построения nmОВ 371
Гnава , О. ОбобщенМfI 41~

Часть ш. Лрогра,ммирование комnоновочныхбnокоа .NEТ 441


Гnава 11. КоМПОНQВОЧНlWе бnОQl .NEТ 443
Гnава 12. О'rображение типов) динамическое СВЯЗЬJВэние
и, программироввние с помощью атрибутов 495
rnaвa 13. Процессы, домены приnожений, :контексты и )(OC~ CLR 537
ГnВ8~ 14, Соэданме, мtfогопоточных приложенltЙ 565
fnaвi 15. CIL и роль Динамических компоновочных 6110КОВ 599

Часть 'У. Программирование с ПОМОЩЬЮ библиотек .NET 645


tлава 16. Пространство имен System.IO 647
Гnава 17. СерИ8ПИЗIЦИ8 объектов 077
rnaBa 18. Y'дa!leН'Hoe В;1аИМОД~ЙСtви.е .NEТ 701
Гnава 19. Создаимеокон с помощыо Sys~em.,Windows..Forms 751
Гnава 20. Визуализация графичесхих данных средmами GDI+ 80З
rлава 2'1. Использование элементов Yl1равnеltия Windows Forms 859
Гмва 22. доступ к базам данных о nомощыо АDО.НЕТ 929
Часть У. WеЬ~прило:жения и web-сервисы XML 1007
Гnава 23. WеЬ..страницы и WеЬ·эnементы управлеlЩА ДSP.HEТ 2.0 1009
Гnaв.a 24.Web-ПРИЛОJCения ASP.NEТ 2.0 1081
Гnааа 25. Web-серви.сы XМL 1117
Предметный укаЗ'атеnь 1161
Содержание

Об авторе 30

Благодарности 30

Введение 31
Вы и я - одна команда 32
Обзор содержимото .книги 32
Часть 1. Общие сведения о языке С# и платформе .NEТ 33
Часть П. Язык программирования С# 33
Часть т. Протраммирование компоновочных блоков .NEТ 35
Часть IV. Протраммирование с помощью библиотек .NEТ 36
Часть У. WеЬ-приложешm и Web-сервисы XМL 38
Исходный код примеров книги 39
От издательства 40

Часть 1. Общие сведения о языке С# и платформе .NET 41


Глава 1. Философия .NET 43
ПреДыдУЩее состояние дел 43
Подход C/Win32 АР! 44
Подход С++ /MFC 44
Подход Уииal Basic 6.0 44
Подход Java/J2EE 45
Подход СОМ 45
Подход Windows DNA 46
Решение.NEТ 47
Отавные компоненты платформы .NEТ (CLR. CТS и CLS) 48
Роль библиотек базовых классов 48
Роль языка С# 49
Другие языки программирования с поддержкой .NEТ 50
Жизнь в многоязычном окружении 51
Компоновочные блоки .NEТ 52
ОДНОМОд.Ульные и МНОГОМОд.Ульные компоновочные блоки 54
Роль CIL 54
Преимущества C1L 56
Преобразование СIL-кода в набор ШlстрУКЦИЙ, соответствующих платформе 57
Роль метаданных типов .NEТ 57
Роль манифеста компоновочного блока 58
Общая система типов 59
тип класса 59
тип структуры 60
ТИП интерфейса 61
ТИп перечня 61
СодерJk'Эн.ие 7
ТIш дe.ttе!"ата: 62
Члены типов 62
BcтpoeJIНЫc 7иIIы данных cтs 62
ОрщеЯ"зъnювые спецификации 63
ThрЩlТ~ CLS-совмес:тимOQТИ 65
Qбщеязьшовая среда вьшолвени,н 65
РaзJtи<niя: МеЖДУ компоновоЧ}fblМИ б:IЮКaмJ1. ЦРОСТРЩlстваю-I имен и тиnвм:u 66
прcwpaммный доступ :к пространствам им.en 70
Сс:ылки на ВНenIШ1е :комuоиово...ные бло:ки 72
ИсIIОЛЬЗОВ8Щ'fе i1da8Дl.~e "72
Просмотр ClL-кода 73
Просмотр метаданных 'I'ИIIОВ '74
Просмо'I'p меrгэдam:tЫХRОМnОНОВОЧ1iЫX блоков 74
Инсталляция среды ВЪ1Полиеншt.NEТ 75
rIлатформещщя ~езавиcmvIOОть.NEТ 75
Резюме 77

Глава 2. ТеХНО110rмя создаН"1 цриложени" на языке С# 79


Уетае:ов:ка .NEТ Framework 2.0 SDK 79
1(омцидятор ~О:rd<ХНДШ;>ЙСТРЩЩ для С# (С5с.ехе) ВО
l']астроiШa :К(j)!"mИЩIТQра ко:мавдной: <:ТрOltИ Д1IЯ С# 81
Дополнительные cpeдcrвa командной f:;ТРОКИ; .NET 82
КоМIfОliOВК~ С#-npиложений С по'М"ощъю СБС.ехе 82
cqылkи на внeпnmй комnонdвочный бло:к 84
I:\oмnилнцщr мнржества файлов 85
Сс:ыщщ на мыояtество Б;frеп:lН:!'IX -кеьrпoновочных блоков 86
Работа с ответНhIМИ фaйJxцми С$С.ехе 86
Ответный фrol.n. используемый ПО умолчанцю (csc.J·spJB7
От.падчи:н: RОмандНаП стро:ки (cordbg.exe) 87
Отладка с щщандной стрmш 88
Н:омnoновка . NET-приложен:иЙ с 1;rc)МQщъю~d 89
Активизация nвeтовой схе.мы С# 89
Настройка фильтра файЛОl\ t .CS 90
ПОДIfЛючеЮfе csc.ex:e 91
Ассоцдация команд с nyн:ктами мБНЮ 92
ИСПОJ~Ование фрar.ментов праграммного кода С# 93
КоМnОlЮВI(З. . NЕТ~ПРИJЮжеВИЙ с 1JОМ.ощью Sh!ПJJDeve1ор 94
ВОЭМОORности ShщpDevdор 95
Окна проекrов и кдассов 95
Обзор 1ЮМПО1-IОВОЧНЬЖ бло:ков 97
Инструменты проектирования Windows FbtrDS 97
КоI\IШОНО1Ща .тт -при.ТJОЖ!lliИЙ С помощью V1sua1 С# 2.000. Express 99
Компоновка .NEТ·nPИЛQЖенИЙ с помощьщ УШи.а! Stuwo 2005 10О
ВОЗМQжваст.и Vi8Ua} Studio 2005 100
УтиJIИта обзора решений: на
Утилита обзора классОв 102
8 Содержание.

ОюlO определений программного кода 103


Утилита обзора объектов 103
Интегрированная поддержка факторизации npограммного :кода 103
Фрагменты программного кода и окружения 106
Средства визуального проектирования классов 106
Стенд тестирования объектов (ОТВ-тестер) 109
Интегрированная справочная система 110
Дополнительные средства разработки .NEГ- приложений 111
Резюме 112

Часть 11. Язык программирования С# 113


Глава З. ОСНОВЫ языка С# 115
Структура ПРОСТОЙ про граммы на С# 115
Вариanии метода МainО 116
Обработка аргументов командной строки 117
Использование аргументов командной строки в Visual Studio 2005 118
Несколько слов о классе Sуstеm . Епviroпmепt 119
Определение классов и создание объектов 120
Роль :конструкторов 121
Утечка памяти 123
Определение Мобъекта приложения" l23
Класс System.Console 124
Ввод и вывод в KТlacce Console 125
Форматирование консольного вывода 126
Флаги форматирования строк .NEТ 127
Доступность членов 128
Доступность типов 130
Значения, назначаемые переменным по умолчанию 131
Значения. назначаемые по умолчанию, и локальные переменные 131
Синтаксис ИНИЦИали3anии членов -переменных 132
Определение констант 133
Ссылки на :константы 134
Определение полей только для чтения 135
Статические поля только для чтения 136
Ключевое слово static 137
Статические методы 137
Статичес:кие данные 138
Статические конструкторы 140
Статические классы 141
Модификаторы naраметров методов 143
Способ передачи параметров. используемый по умолчанию 143
Модификатор out 144
Модификатор ref 145
МодифИRaтор рагaшs 146
Итерационные конструкции 146
СодержаttJ.1е 9
цикл fol' 147
ЦИШI lQ1:each 147
Roнстру!ЩИИ while и do/wblle 148
Конструкщ:rn выбора решен;ий 1:1 oцep~ срrщнeFIИЯ 146
Оnера:горLf/else 149
Оператор switch J 50
nПIЬ1 , .харакrеризуеМЫе значениями . и сСьшочные типы 151
ТШrы. характеризуемые значениями. СGI>IЛОЧНЬ1е типы и оператор
присв~ания 153
типы. характеризуеыы~ зn~ченnям:и и содержащие ссъшочные ТШIЫ 155
Лереда"'JЗ ССЫ.lIOЧЩdX типов ЩI ЭЩiЧeJlИЮ ] 57
Л~редача ссьщочных типов ПО ссылке 158
1'nПbI, характеризуемые значеюшм:и. и ССhIЛоч.н~е типы:
з.аключителЫlые эа..чечаниЯ ] 59
bтreр:ации ФЭ,ЦЯВИИ olJъe&rнoгo образа и воостановления: itЗ объeкtноro lJбpaзa 160
I1pЩidеры СQЗДания объeR'I'НЫX обраЭ()1П't восстано:вления значений 161
Восстановление из объ~т~оrо образа для I:10ЛЬ30:вател:ьских 'J'ИШ)В 163
Р-абcтrа. с переЧЮIМИ .NEТ 1.64
Базовый класс System.Enum 166
Мастер-:класс; .system.ObJect 168
Поведение System.Object. задainJое nь УМШР:IЭНИЮ 170
Переопределение элеме}JТOВ System.0l!Ject, эfiДанных nOУМОЛЧанию 172
Переonределение System.Objecf;lbString{} 172
П~реопреде;.iIeние System.Object.Equa1s() J 73
Переопределение Systеm.ОЬJеоt.GеtНазhСОdеО 174
ТестироВafШС nepеопределеыных членов 175
Ста:тичеCJ:Шe ЧllеНЫ S}'stem.Object 176
ТИцы ДШЩЬ1Х S~SteШ (и щ': Об:оз:н~чен.ия в СИ) 176
Экonери:менты с ч:исло.вы:м:иТ:ЮIa~ данных 179
Члены Sуstеm'вооlеап l~P
Члены System:Cha:r 180
Анализ значений СТРО1{()вык:дайпЬJ:Х 181
Эystem , Date1llne и SУБtem.ТlmеSрan ИН
тиц да:шц.IХ Syst;em.SЦing 182
Базовые операщщ со CtpOE-aIIЩ 183
Уtrpa1J:/Iлющие последова:reл:ыюсти 184
БукваЛЫlОе восnpоизве:дени::е СТРО}( в С# 185
Роль System.Thxt.StnngВui1'deJ' 186
'limы массивов .NET 187
Мас~ивы в :начестве параметрРВ СИ ВО8вращаеМЫХ8наченийJ 188
Работа с мнaroмерными массивами 189
Базовый масс System.Array 191
1Ии:ъt G раэреII1ение.~ npинимать значение ПШI 192
Рабо:ra с типами~ ДЛЯ которых ДОПУС.ТИМы зiiачения nulI 193
Операция ?? 194-
Пол:ыювательсние простращ:';Гва Ю4ен 195
АБСОlIЮТные имеда типов 196
1О Содержание

Использование псевдонимов 198


Вложенные пространства имен 199
Пространство имен по умолчamпo в V1sual Studio 2005 200
Резюме 201

Глава 4. язык С# 2.0 и объектно-ориентированный подход 203


тип масса в С# 203
Перегрузка методов 206
Использование this для возвратных ссьток в С# 207
Определение открытого интерфейса масса 209
Принципы объеRТно-ориентированного программирования 210
Инкапсуляция 210
Наследование 211
Полиморфизм 212
Первый прmщип: сервис инкапсуляции С# 213
Инкапсуляция на основе методов чтения и модификации 215
Инкапсулнцин на основе свойств класса 215
Внутреннее представление свойств в С# 218
Контекст операторов get и set для свойств 220
Свойства. доступные только для чтения, и свойства,
доступные только для записи 220
Статические свойства 221
Второй принпип: поддержка наследования в С# 222
Управление созданием базовых ,классов с помощью base 224
Множественные базовые классы 225
Хранение семейных тайн: ключевое слово protected 225
Запрет наследования: изолированные юшссы 226
Модель локализации! делегирования 228
Вложенные определения типов 229
Третий принцип: поддержка полиморфизма в С# 231
Ключевые слова virtual и оveпidе 232
Снова о ключевом слове sealed 233
Абстрактные юraссы 234
Принудительный полиморфизм: абстрактные методы 234
Возможность скрывать члены 238
правила приведенин типов в С# 240
Распоанаваниетипов 242
Приведение числовых типов 243
Парциальные типы С# 243
Документирование исходного кода в С# с ПОМОIЦЪю XМL 245
символы форматирования в ХМL-коде комментариев 247
Трансформация ХМL-кода комментариев 248
Резюме 248

Глава 5. Цикл существования объеkТОВ 249


Классы, объеRТЫ и ссьmки 249
Содержание 11
Оtиовные ~ёдения о существовании объектов 250
CТIrHQД дп:и .new 251
Ролр :&орlЮЙ при~щ:tJi:е~ 253
Th.tlершum о.бъектов 255
тип Systern.GC 256
АктИвизация сборки муоора 2'57
Создан:и~ оQъектов. прецvсматривающих фnнanи:зацию 260
Переопределение System. ОЬjесf.FtщЦ~еО 261
Детади процеr:са финализации 263
Создание объектов, прfЩYCматривaIOЩИХ освООоЩll.ение ресурсов 263
Снова о :кЛЮчевом слове using 11 С# 265
Создание типов, ~матривающих acвoбwtщeниe·ресурсов·u фИН3.J1изацию 266
ФОРJlo(aJIИэоващlЫЙ шаб.щН;f ОСВЩ)Qждеmm ресурсов 267
P~Me 269

Глава 6. Структурированная обра6отка МСКJ1lОчений 271


Ода, оnщбкам. и Щ:КJIЮЧeнmrм 271
Роль обрабо'ПW ис:кдюч~ний в .NEТ 272
Атомы обраоотI<И ИС}tJIЮче'f:I.ИЙ Ii .NEТ 273
Базовый класс Sygtern.Ex()eption 274
Проетeйnшй пример 275
~нерирование исЮI10чеНйЙ 277
Обработка исключени,й 278
Конфигурациn состoromя ИСIfJIIОЧений 280
Свойство ThrgetSite 280
Свойство S1ack'n-ace 281
СВОЙСТВО НеlрLJnk 282
СВQЙСТВЭ Оаи 283
Исrщючения сиc:reмn0I'о уровня (Sуstern:SysteщEхсерtiqri) 284
ИСКIJЮченияуроВИJi приложевllЯ (System.Application&oeption) 285
СОЗДiUiИе пользовательских ИCЮIючеЮlЙ. раз... ~$5
Создание полъзователъски:х иCКJlючemrit два... 287
СClЭ:цamre По.1IЬ3Oвателъских .исклюЧ'ениИ~ три! 287
Обработка Ml-IO!ЖfilСТВ исItШQЧeниif 288'
Qбщие одераторы catcb 2~1
Thнерирование вторичных ЙCI<J.IюченИЙ 291
ВНУТР~llшеискmючеиия 29~
В1шн Ш1aIly 293
Что ~ ч;ем I'енер.цруется: 294
ИCIЩЮчеаия. оетавдЩ.еся без обрарOТJЩ 29J:}
Отладнанеобработанных ИСЮIючений в Visual stшflо 2005. 298
Резюме 296

rna~a 7. Интерфе_йсы и ~оллеJЩИИ 299


Опреде.i-rс.tШе Иlперфейсов вС# 299
РеЗJIR3ЗЦИЯ интерфейсов в Cft 300
12 Содержание

Интерфейсы в сравнении с абстрактными базовыми классами 302


Вызов членов интерфейса на уровне объекта 303
Получение интерфейсных ссьшок: ключевое слово as 304
Получение интерфейсных ссылок: ключевое слово is 304
Интерфейсы в качестве параметров 305
Интерфейсы в качестве возвращаемых значений 307
Массивы интерфейсных типов 308
Явная реализация интерфейса 308
Разрешение конфликтов имен 311
Построение иерархии интерфейсов 312
Интерфейсы с множеством базовых интерфейсов 313
Реализация интерфейсов вVisua1 Studio 2005 314
Создание перечислимых типов (IEnumerable и lEnumerator) 315
Методы итератора в С# 318
Создание клонируемых объектов (ICloneable) 319
Пример клонирования 321
Создание сравнимых объектов (lComparable) 324
Сортировка по н/!,бору критериев (IComparer) 327
ТИпы. определяющие сортировку. и пользовательские свойства 328
Интерфейсы из пространства имен System.Collections 329
Интерфейс ICollection 330
ИнтерфейсIDictionary 330
Интерфейс IDictionaryEnumerator 331
Интерфейс IList 331
Классы из пространства имен System.Collections 332
Работа с типом ArrayList 333
Работа с типом Queue 334
Работа с типом Stack 335
Пространство имен System.Collections.Specialized 336
Резюме 337

Глава 8. Интерфейсы обратного вызова, делегаты и события ЗЗ9


Интерфейсы обратного вызова 339
ТИпа делегата .NEТ 343
определение делегата в С# 344
Базовые классы System.MulticastDelegate и System.Delegate 347
простейший пример делегата 348
Исследование объекта делегата 350
Модификация типа Саг с учетом делегатов 351
Реализация групповых вызовов 354
Более совершенный пример делегата 356
Делегаты в качестве параметров 357
Анализ программного кода делегирования 360
I<'овариантность делегатов 361
События в С# 363
Thyбинный механизм событий 365
Содержание 13
Прием достynающих событий 366
Упрощt:НН:ал регистраци;я событий в Visua1 Эщто 2005 961
"Разбо.р'tивые~ соБЫТИЯ 368
Анонимные методы в С# 370
Дocтyn- H "вн~IПНИМ· перем:енnым: 372
tpyпnовое r.q>eобра.зоввюre методав 11 С# 373
Реэюме374

Глава 9. Специальные приемы лостроеНИR ПЩОВ 371


Со:щWЦ[~ пользова'IJeЛЬCkИX индексаторов 377
Вари~ индексатора для ТИПа Garage 379
1Знy:rреnн_ее представлеНИе -индексаторов типов 380
3ашпоЧИТeлbliЫе замечания об ИНДекс;::аторах 381
Перerpyз:ка anер8ЦИЙ 382
Переrpузка бзщаpJlых оrn:рaщIЙ 383
операции += и -+ 384
Переrpузка унарных операций 384
Пере:грузкЭ. операций npоверки па тождествеНRОСтЬ 38-5
перqpуз;!щ операций cpabh-eRИ'.Я з86
Внутреннее представление перегруженных операций 387
ИСIЮЛЬ30ва1iие перетружеIo-Iык оперЩ1;ИЙ "~3iЫIШX, не Подцержи::ва.ющих
ш:рerpуЩ операций 389
3<Щ1IlOчителъ'НЫе эамечанил о переt"pузке оперanий 399
ПW1Ъэовательские преобр<!.ЗОвa.ниs:t типов 391
Преобра:юв!ШИ)! чиоел ЗЮ
Преобраэования т}тов Rдacca 391
СоздаНИе поЛЬЗовательских ПQДПРorpамм npеобразов_aнmr 392
Варианты mшого npеобрззования ДЛЯ типа $quare 394
Определение подпрограмм: -Неявного преобразования 395
ВнутреШIeе предст<uщение по.льзоватещ.с~ Ц0дrW0тpa.мм преобраэавания 396
Ключе:выtJ слова. С#. npедназначе:нные для более СЛОЖИЫХRоастру-нций 397
Rлючевое слово checked 397
Ключевое слово unchecked 400
Рабо'Щ с 'f.ипамиуказателя 401
Ключевое слово эizео! 408
Директивы црenpоцессора С# 409
Разделы программноro ROда 409
Условная КОМURiIЯЦИЯ 41 О
Резюме 412

Гnава 10. Обобщения 413


CHO!Ia О создани:и объеRтиых образов, восстановлении аRaчений
~ System.O~ect 413
Проt)лемы создания объeщ'JJЫX обршюв и :восстанавлбния значений 415
1'щtовЩ1 беэOIЩСНОСТЬ и строго Т'шц!эованные -коллекции 416
Проблемы создают объектш.rx образов и строго ТИIдiЗованНые нолленtI;ИИ 418
14 Содержание

Пространство имен System.Col1ections.Generic 420


'гип List<Т> 421
Создание обобщенных методов 424
пропуск параметров типа 425
Создание обобщенных структур (и классов) 426
Ключевое слово default в обобщенном программном коде 427
Создание пользовательских обобщеЮiЫХ коллекций 429
Установка ограничений для параметров типа С помощью where 430
Отсутствие подцержки ограничений при использовании операций 433
Создание обобщенных базовых классов 434
Создание обобщенных интерфейсов 435
Создание обобщенных делегатов 437
Имитация обобщеЮIЫX делегатов в .NEТ 1 .1 438
Несколь:ко слов о вложенных делerатах 439
Резюме 439

Часть 111. Программирование компоновочных блоков .НЕТ 441


Глава 11. Компоновочные блоки .NEТ 443
Роль компоновочных блоков .NEТ 443
РаСlIIИpенные возможности многократного использования
программного Iiода 443
Установка четких границ типов 444
Управление версиями 444
Самоописание 445
Средства конфигурации 445
Формат компоновочного блока .NEТ 446
Заголовок Win32 446
Заголовок CLR 446
пporpaммный код CIL. метаданные типа и манифест компоновочного блока 448
Необязательные ресурсы компоновочного блока 449
Одномодульные и МНОГОМОдУльные компоновочные блоки 449
Создание и использование одномодульных компоновочных блоков 451
Анализ манифеста 454
Анализ СIL-кода 456
Анализ метаданных типов 457
СОЗДaI-ше приложения-клиента в С# 457
Создание приложения-клиента в Visua1 Basic .NEТ 459
Межъязыковое перекрестное наследовюше 460
Создание и использование многомодУЛЬНЫХ компоновочных блоков 461
Анализ файла ufo.netmodule 462
Анализ файла airvehicles.dll 463
Использование многомодульного компоновочного блока 463
Приватные компоновочные блоки 464
Идентификация приватных КОМПОНОВОЧНЫХ блоков 465
Процесс зондирования 465
СIJ;церж.ание 15
Ко.нфюурация npиваТJ:1,ЫХ; JЩМДЩRJВОЧНЫХ блохов 466
Файлы ко.нфиrypацци и Vi~ua1 stшliо ~005 468
Утилита конфиrypa.щtИ .NEТ Framewotk2.0 469'
ощщед-осryn:нью :кйМ1JOНОв6чНЫе блоки 471
Строгая форМа имени 4:11
Создщmе C'.rpororp ~еЮ! ДJlЯCarLihrary.di1 473
Назначение строгого имеНPt в Viэual Shld10 2005 474
Установка и удаление общедостy:mtIiIX компоновочных бл'оков 4-75
Отложенная поДIIИСЪ 476
ИсПользование общедоступных :комIIонI!).зочиых бдоков 477
Л:l:щл:из ~щmфеста SharedCaI'UbClient 480
Кщrфиrypация общедос~ компоново~ блоков 480
ФЙКСgция версии общедоступного fЮМnОНОВОЧНОГО блока 481
Соэдание общtщоc-ryпноro RОМnОНQВ,ОЧ,НОГО блщш версии 2.0.0.0 481
ДIirnаШJЧескан привязttа R концретной версии ROъmоновоЧI;IОГО блоlШ 483
Снова об утилите 1Юнфиrypации .NEТ Framework 2.0 484-
.4iнализ внутреЩi;ei':i структуры GAC 485
Файлы пОлитики nyб.mщации щ>мдоновочныJt блспюв 487
Игнорирование. фЗЙJIа nолиLИКИ nyБJЩRaI:(Щi 488
$Jлемент <cod,e Base> 489
Пространство tJМ6Н System.CmШguгаtiCiП 491
Файл конфигурации M~ 492
Общая схема связей :КОМПОНОВОЧНЫХ блоков 493
Резюме 494

Глава 12. Отображение типов, динамическое связывание


'и программирование с поМощtalQ атрибутов 495
Метаданные 'типов 496
Анали~ метаданных перечня. EngJnеstэ.tе 497
Анщrцв метэдamIых ТЩ;Щ СМ 497
Анализ "!ypeRef 499
ПреЦСТaвJJеtmе метадашrыx КОJIЩОFIOВОЧ:ВОГО (Щока 499
Предст-авление ССЫЛОК на дрyrие КО1lof1]ОНОВОЧНВJе б.nоки; 499
Пр~дС"Гавлeюre строковых литералов 500
Отображение ТЩJов в .Nl!.l 50 1
.l<.дщс S%tem.1Jrpe 501
Получеtше 1)тре с ПОМОЩЬЮ Systеm.ОЬjечt.Gef.rypеО 502
Получение JYPe с помощью s~tещ.1:уре.Gеt1YPе(J 503
Получение Туре с помощьюtypеоfD 503
СD::щавИе riШ1ЬЗQВателъClЮГО npиложеmm ~ nPОСМ0Тра метf1данfiых 504
ОтображеЩfе методов 504
Отображение полей и с.воЙС-r.,в 504
ОтоБРaзlс€ние ре<wиаоваlIНЫХ ИБтерфеjiссJВ 505
Отображение вcnомогаl'ел:ЬНОЙ Щlформащщ 505
Реализация МainО 506
ОтображеЮfе па.раметров и возврauщемых ЗНачений метоДОВ 507
16 Содержание

ДИнамически загружаемые компоновочные блоки 509


Отображение общедоступных компоновочных блоков 511
ДинамичеС1(ое связывание 513
Класс System.Activator 513
Вызов методов без параметров 514
Вызов методов с параметрами 515
Программирование с помощью атрибутов 516
Потребители атрибутов 517
Применение встроенных атрибутов С# 517
Параметры конструктора для атрибутов 519
Атрибут Obsolete в действии 520
Со:кращешюе представление атрибутов в С# 520
Создание пользовательских атрибутов 521
Применение пользовательс:ких атрибутов 521
Ограничение использования атрибута 522
Атрибуты уровня компоновот.mого блока (и уровня модуля) 524
Файл Assemblylnfo.cs в Visual Studio 2005 524
Отображение атрибутов при статическом связывании 525
Отображение атрибутов при динамическом связывании 526
Перспективы отображения, статического и дИнамического связывания
и пользовательских атрибутов 528
Создание расширяемого приложения 529
Создание CommonSnappable1Ypes.dll 529
Создание подключаемого компонента в С# 530
Создание подключаемого компонента в Vistlal Basic .NEТ 531
Создание расширяемого приложения Windows Forms 531
Резюме 535

Глава 13. Процессы, домены приложений, контексты и хосты CLR 537


Выполнение традпционных процессов Win32 537
Обзор потоков 538
Взаимодействие с процессами в рамках платформы .NEТ 540
Список ВЬПIолняемых процессов 542
Чтение данных конкретного процесса 54з
Список множества потоков процесс а 544
Информация о наборе модулей npоцесса 546
Начало и остановка процессов с помощью программных средств 547
Домены npи.ilОжеНИЙ .NEТ 549
Список доменов приложения процесса 550
Программное создание новых доменов приложения 552
Программная выгрузка доменов приложения 553
lPаницы н:онтекста объекта 555
Контекстно-независимые и контекстно-связавные типы 555
Опреде.пение kohtekctho-связанных объектов 557
Провер:ка I(онтекста объекта 557
Срде~ние 17
Еще .аеOROЛЬ:КО·· СЛQВ о процесqa:x., доменах .приложения и нонтмстах
559
Хоcтинr 06щ~.язьnwвОЙ среды .вышJIнепия: 560
Параллелыюе вьmoЛiiеюrе CLR 560
Зarрузка кониретной версии СIЖ 561
Доnoлни:rеЛЫIblе XdСТЫ C L R 5 6 3
Pe~Me 563

Глава 14. Соэдание МНОГОnОТОЧНblХ приложени" 565


вэa:m.roCВffiJЪ ПРОЦеССDВ, доменов 1IpиложеНиi%. контекстов и потоков 565
Проблема КОНRуренции и po.iIЬ синх;ронизации пото:ков 561
КратlШЙ обзор делегатов .NEТ 568
Асйн.х;роннав прирdда деJ;lеГ'dТОВ 570
Методы Беg1nIпvokеО и Endlnvok.eO 570'
Интерфейс System.IAsYl1cResult 571
Аcи:юs:pо.ННЫЙ вызов методов 571
Синхродиза:ция :вы;зывающего потрщз, 512
Роль делеrа::l'а AsyncCallback 574
Родь класса Лsync.Resu.It 575
Передача и получеШiе ПОЛЬЗ0ва1'елъсю~ данных СОС'rofJНИЯ 576
Прострaшrrвоимен Sy.s1em.ThIead1ng 577
кла.сс $уst~ .ТhrеаФДg .Thrеаd 578
Получен:ие Щ:lформации об· oт.цeJIhНOM ПОТОRе 579
Свойства Narne 58Q
Свойство PrtoIity 580
ПроГрамм.ное СQздание ВТОРИЧНЫХ ПDТОI(ОВ 581
Рабе'i'а с делега'Т'ОмТhrеаdstart 581
·P~l$oTa с дещ:гатом Pa;r:arneter1zed'ThreadStart 584
I1риорwreТElЫе и фщ:юв.ые ЦОТРЩ1 585
пробт:ма )(ою.-урелwоtCl доступа 586
·Синхронизация с помощью ключевого сло:ва: lock вС# 589
СИНХрОН:'И:'1iщ;ИЯ с помощью типа Sys.tem.'Тhreadiцg.Monitor 590
СIiНxpО.imзatJ;Ия с помо:iЦЬЮ типа $ystem.Тllreadrng.Interlocked 591
Си,н.х;рони<iЭЦИЯ с ПОМОЩЬЮ атрибута {Synchronizat1on], 593
Т1рограммирование с помощыр таймеров обратного Щ>I30ва, 593
Пул ПQ1:'ОIЮВ CLR 595
Резюме 597

Гnава 15. CIL и роль динамических компоновочных бnоков 599


Природа nРОI])aм.IO:fPОВaюrя
11 те}!)мицах: CIL 599
ДИрективы. атриб}'Ты и жщы операций CIL 600
Роль дирeItтив CIL 601
Po,rrъ атрЩ1Y1'QВ сп.. 601
PoJIЬ КОДОВ опс:рaцRЙ C:IL 601
Различия.между мнемоН'ИЮ)й ц Ж)домопер~ CIL 602-
Добавлеnие и извлечение данных: стеновая природа CIL 603
18 Содержание

Челночная технология разработки 604


РольMeTOI{ в программном коде ClL 607
Взаимодействие с CIL: модифИRация файла".11 608
Компиляция CIL-кОда с помощью ilasm.exe 609
Компиляция С!L-Rода с помощью SharpDevelop 610
Компиляция СIL-Rода с помощью ILШЕ# 611
Роль peverify.exe 611
ДИрективы и атрибyrы CIL 612
ССЫЛRИ на внешние Rомпоновочные БЛОRИ 612
Определение текущего компоновочного БЛОRa 612
Определение пространств имен 613
Определение типов масса 614
Определение и реализация интерфейсов 615
ОпределениестрYRТУР 616
Определение перечней 616
Компиляция файла CIL1YPes.il 616
Соответствие между типами библиотеRИ базовых l{Лассов .NEТ. С# и CIL 617
Определение членов типов в CIL 617
Определение полей данных 618
Определение конструкторов типов 619
определение свойств 619
Определение параметров членов 620
Анализ кодов операций CIL 621
Директива .maxstack 623
Объявление локальных переменных 624
Связывание параметров с локальными переменными 625
Скрытая ссылка tШв 625
Представление итерационных RОНСтрукций 626
Создание компоновочного блока .NEТ в CIL 627
Соэдание CILCars.dll 627
Создание CILCaгClient.exe 630
ДИНамичеСl{Ие компоновочные блоки 631
Исследование пространства имен System.Reflection.Emit 632
Роль System.RefiectiO'n.Emit.lWenerator 633
генерирование динамич.ескогО' компонО'вочного БЛОRa 634
генерирование КОМПОНОВОЧНО'ГО блока и наБО'ра модулей 637
Роль типаModuleBullder 638
генерирование типа HelloClass и принадлежащей ему
строковой переменной 639
генерирование конструкторов 639
генерирование метода HelloWorldO 640
Использование динамичесl{И сгенерированного компоновочного блока 641
Несколько слов о' System.CO'deDOM 642
Резюме 643
Содержание 19

Часть JV. Програ",мирование С помощью БМБnмоrек .NEТ 645


Глава 16. ПростраtfС.тво имен Syst~m.IO 647
АНаЛИЗ пространства имен Syst-e:m.IO 647
Типы Dire~tOl"y[LrJfa1 11 File(Info) 648
Абстрактв:ы;й 6азовьЩ кдасс FДе$ys'temInfо 649
Работа с типом DirectoryInfQ 650
Переченъ FtleAttrlbutes 651
Перечисление файло13 G ПОМОЩЬЮ DirectoryInfo 652
СQздromе ыqдкаталогав (: ПОМОЩЬЮ DirectoryInfo 653
Работа с типом Directory 654-
Рабо'ta с:тшroМ "пасса Ddvelnfo 655
Работас классом FileЫfo 65(':;
Метод F"llelnfo\CteftteO 657
M~I)Д File1nfo.OpenO 658
Meтoдь.r }<lleInfo;O~ReadO и Fi1eImfo.OpenWrlte() 65g.
Метод File1nfo.Open'IЦtO 660
Meтo)lbl FlleInfo.Create1bctQ и: FilеInfо.Арр!щd1&iО 660
Рабcrrа с nrnOM Fi1e 660
Новые члены F:lJe в .NEТ 2.0 661
Аt5стракгный ддасс Stream 662
РаБQта с F11eS1ream 664
Работа с StreaInWrtier .I'I StreamRead~r 665
Запись в текC'ТCffl1.IЙ файл 667
Чтение из тeKCТQ130ГO файла 667
НепосреДGтве:mюе со:щaшrе типовStreamWritеr (StreamReader 668
Работа с'ЩЛаМИ Stтi:цgWrtter и Str1пgRеаder 669
P-аБDта с B1naryWriter н БiшuуRе&dег 670
проrpаммный моmtгoРШIГ файлов 673
.Асщ-pq>онвыИфай..'Ювый B13pд -вывод 675
Резюме 676

Глава 17. СериализаЦИII объектов 677


ОСНОВЫ еери:ализации объеюrnв 677
РQЛЬ O~ графcm 679
Itoнфшурирощnше оБЪeRТЩJ для сt;p;иaдIiшщии 680
ОткрЫ1'QIе ПОЛЯ. прива:гцые поля и открытые С.БрЙетва 681
Выбор формата сериализaцI'I:И 681
Интерфейсы IFonnatter и IRemotlngFOnnatting 682
Выбор-формата и тоcrnoсть J'ШНШ 683
Сериалйзация об'Ье:к~'I'UВ с помо!Цыо BiЦ~Fonnatter 684
РекоНСТРУ1ЩИЯ объеКтов с ПОМОЩЬЮ ЩnвхуFbJ;"D1аttе. 685
Сер~ализацх1Ji объектов с IЮМОЩью SQарR)J"щаttet- 686
СериализаЦИR uбъектов t помощью XmlSeria1izer 687
14mтролъ tенерируeмьr;x XМL- данных 688
Сохранение 1Wллешщй оОъектов 689
20 Содержание

Настройка процесса сериализации 691


Более глубокий взгляд на сериaJШЗaциIO объектов 692
Настройка параметров сериaJШЗации с помощью ISerIalizable 693
Настройка параметров сериализации с помощью атрибутов 696
Поддержка версий сериaJШЗации объектов 697
Резюме 699

Глава 18. Удаленное взаимодействие .NEТ 701


Понятие удаленного взаимодействия .NEТ 701
Пространства имен удаленного взаимодействия .NEТ 702
Каркас удаленного взаимодействия. NEТ 704
Агенты и сообщения 704
Каналы 705
Снова о роли форматтера .NEТ 707
Общая картина 707
НеСIЮЛЬКО слов о расширении стандартных возможностей 708
ТерминыудаленноговзаимодеЙствия.NEТ 708
Варианты маршалинга для объектов: МВR и МВV 708
Варианты акгивизации для МВR-тиnа: wкo и ело 710
Варианты конфигурации WКО-типа:
синглеты и объекты одиночного вызова 712
Сводная характеристика МВR-объектов 712
Инсталляция приложения. использующего удаленное взаимодействие 713
Создание распределенного приложения 714
Создание общего компоновочного блока 715
Создание компоновочного блока сервера 715
Создание компоновочного блока ЮIИента 716
Тестирование приложения. использующего удаленное взаимодействие 718
тип ChannelServices 718
тип Remot1ngConfiguration 720
Снова о режиме активизации WКО-тиnов 722
YCTaнoBI<a. сервера на удаленной машине 722
Использование ТСР-каналов 723
Несколько слов о IрССhaпnеl 724
Фaйльr конфигурации удаленного взаимодействия 725
Создание файлов ·.config сервера 726
Создание файлов • .config клиента 727
Работа с МВV-объектами 728
Создание общего компоновочного блока 728
Создание компоновочного блока сервера 729
Создание компоновочного блока ЮIИента 730
Объекты. активизируемые ЮIИентом 732
Схема лизингового управления циклом существования САО-типов
и WКО-синглетов 735
Схема лизингового управления. используемая по умолчанию 735
Изменение параметров схемы лизингового управления 738
Содер.*<Iн~е 21
НаСТРОЙIШ парам(!тров лизинrа. на. стороне серве-ра 739
Настройка пара:;метpqв ли.зинга:на cropoнe клиеmа 739
Спонсоры лизинrа сервера (и ЩIиеща) 740
АльТ€.pJia'ГИВньre xocты Д1Jя УДЗJ"1енных объектов 741
Хостинг удаленн-юх объектов с помощью сервпса Windo~~ 742
ХОС'Т'ИI'П' удаленных QБЫНт.QВ С ПОМОЩЬЮ тв 746
АоИIOq)ОJfiюе удаленаое JJЗа1!Модействпе 748
Роль атрибута !On~Way] 749:
РеЗюМе 750

Глава 19. Создание окон с ПОМОЩЬЮ System.Windows.Forms 751


Обзор цространства ЩW:eJi Systenl.Windows.Fbrms 751
Работа с типами Windows FQ:rms 75з
СазданЙ'е· главного окна :вручную 753
ПрИНЦШl ражраничении обязанностей 755
Роль :класса Арр'цсаt,10П 755
Возможности :класса ApplicatiOn 7216
Делегат System.Eve-пtнandl~г 7§8·
~Анатомия~ формы 759
Фующиональные возможности класса Control 7~O
ИСПОЛЬЗQВание возможностеЙ класса Соntro] 763
Ответ па собьция: MouseMove 764
Регистрации щелчков кн~шок мьnпи 765
Ответ на собьrrия ЮI;з.виатуръ;r 766
ФунЕци:овальные возможности масса Fortn 767
Цикл существования 'rипа Fbгm 768
Создание W1ndOWS-llРИЛОЖе.н:ий в vtsш:u Stlldio 2005 770
Получение доступа lj: Уf;'J'а.:ре:БШИМ элементам упраВления 772-
Анализ npoe1cra Windows Fолщ; .в VI$ua} stш:I.iо 200'5 773
Обра:ботка собыrrий в режимеnpoеRТИРОвa.mщ 775
:класс Ptograro 776
Неоmюдимъте компоновочные блоЕИ 776
Работ~ !J М.епuStr1р и ContexfМenuSbip 776
ДОбавление элемента "R:xtbox в MenuStIip '779
Создание KOJITeRcTНЫX мещо 780
Лроneрка СОСТОЯНИя ЭJ.Iементов .меню 783
Работа 0 ЗtаtlisStriр 785
Создание СИС11еМЬ1 меню 786
НаСЧ'щша Statusstrtp 'иЗ6
Работа с типом Thner 789
БюnочеlIие отобршце:ния 790
Вывод llОДCЮlЗОК для выбранных элементов мещо 791
СОСТОlUiие roтов:ности 791
Работа с Tho1Эtrtp 7&J2
Работ~ С 'ЦJolStripCont:.alner 797
Соэдаиие мш- ПРИJЮ)f(ещщ 799
22 Содержание

Создание родительской формы 800


Создание дочерней формы 801
Соэдание дочерних окон 801
Резюме 802

Глава 20. Визуализация графических данных средствами GDI+ 80З


Обзор пространств имен GDI+ 803
Обзор пространства имен System,Drawing 804
Утилитарные типы System.Drawing 804
Тип Point(F} 806
Тип Rectangle(F} 807
Класс Region 807
Класс Graphlcs 808
Сеансы Paint 81 о
Обновление области клиента формы 811
Доступ к объекту Graphics вне обработчика Paint 812
Освобождение объекта Gгарщсs 814
СистемыкоординатGDI+ 815
Едиюща измере~mя, предлагаемая по умолчанию 816
Выбор альтернативной единицы измерения 817
Изменение начала координат 818
Определение цветовых значений 820
Класс ColorDialog 821
Манипулирование шрифтами 822
Работа с семействами шрифтов 822
Работа с гарнитурами и размерами шрифтов 824
Список установленных шрифтов 827
Класс FbntDialog 829
Обзор пространства имен System.Drawing.Drawing2D 830
Работа с типами Реп 830
Концы линий 833
Работа с типами Brush 835
Работа с HatchВrush 836
Работа с 'ThxtureBrush 837
Работа с LinearGradientвrush 839
Визуализация изображений 840
Попадание в заданную область и операции перетаскивания для PictureBox 842
Про верка попадания в область изображения 845
Проверка попадания в область , отличную от прямоугольной 848
Формат ресурсов .NEГ 850
Пространство имен System. Resources 851
Создание файла *.resx проrpаммными средствами 852
Создание файла" .resources 853
Добавление файла" .resources в компоновочный блок . NEТ 853
Работа с ResourceWriter 854
Генерирование ресурсов в Visual Studio 2005 855
СОДlфжаНltlе 23
ЧтеJ-ше ресурсов nрогрaммJ;I!ЫМИ среДC'rВa'МИ 857
Резюме 858

Глава 21. Использование элементов управления Windows Forms 859


Эле1l«еНThl упра:влеЮf,я. Wiщ:lI;JWS Fbrtns 8'59
ДобaW1ение элемеНТ-О8 упра:влеюш:в форму ВРУЧ1-IYЮ 860
тип Control,Contr{)lCollectlon 862
ДобавлеJШеалементов управ.ленил '13 форму в Vfsual St1.ldto 2005 863
Работа ~ базовыми эл~меflТами ynpа:иlie't:Wя 864
Элеме:~IТ l.:abel 865
ЭlIемеItГ TextВox 866
Элемент Маskеd'Ib.."tiЗох 869
Элемент Button 871
ЭлемIШ'r:ът CheckВox. RadlоВuftоп ~ GroUpBox 873
Элемент CheckedListВdx 877
9дeМf$T Listbox 879
Элемент СоmЬоБох 880
Порядок переходов по нажатию JЩЗВШJIИ табуляп:и.l1 881
Мастёр настройки переходов по табуJIJЩИИ в82
'Установна lЩОЩИ. выБИраемой ПО умолчанию "8&2
Рабма с щэугими эдементами yup<Щllения 883
ЭЛемент' MonthC;ilendar 883
Элемент 1bolТip 885
Элемент ThbConttol 886
Элемент ТraсkБш 888
Элемент Рапеl 891
Элементы UpDown 892
Элеменt ЕпоrProvidеr 895
Элемент 1teeView· 897
Элемент Wе.ЬБтаwsеrs 903
СfjЩЦ~е ЛОЛЪЗ0ва'I'елъС1:МК ,э.т.tсментов упра:в,П,eюm Window$ Ропns 904
Соа;цщще ~зЬбражениИ 906
Соэдание ПОЛЫlQвательекого интерфейса режима npое;ктирования 906
Реади..-"ffiЦИЯ CarControl 907
Оnре:делецие noilIЪЗ0Ва,телъскщ< (i;ООЫТИЙ 909
Опреде.neЮilе ПОЛЬ3Qвателl,СIЩX свойс;:тв 909
КОнтроль анимации 9] 1
Отображение назвю·tИя 911
Тестиpuвщrnетипа CarContJ;o1911
Создан:ие П03IрЭовательской формы дщI Са:!;'Сопtrol 912
ПространСТВD имен System. СоmроnепtМоdе] 914
Совершенс'ХВоваНИережИМа npое'КТ'ированил CarControl 915
Оцредroiенн. е выбираемых по умолчзнmо свойств и событи;й 916
Вмбор Jofз0брщкеFtшI для ПaRеТlИинструменro:в 917
Создaнnе польsоватeдьaJi.ИX диалоговых окон 919
Свойство DJa1ogResu1t 920
24 Содержание

Наследование форм 921


Динамическое позиционирование элементов управления Windows РЬrшs 924
Свойство Anchor 924
Свойство Dock 925
Табличное и потоковое размещение элементов 926
Резюме 928

Глава 22. Доступ к базам данных с помощью ADO.NET 929


Высокоуровневое определение ADO.NEТ 929
Две грани ADO . NEТ 930
Поставщики данных ADO.NEТ 931
Поставщики данных Мiсrоsоft 932
Поставщики данных других производителей 934
Дополнительные пространства имен ADO.NEТ 934
ТИПЫ System.Data 935
ИнтерфейсIDЬСоnnесtiоп 936
Интерфейс IDЬ1Гапsасtiоп 937
ИнтерфейсIDbCommand 937
Интерфейсы IDbDataParameter и IDataParameter 937
Интерфейсы IDbDataAdapter и IDataAdapter 938
Интерфейсы IDataReader и IDataRecord 939
Интерфейсы и абстрактные поставщики данных 940
Файлы конфmypации и гибкость приложений 941
Модель источника поставщика данных .NEТ 2.0 942
Зарегистрированные источники поставщиков данных 944
Рабочий пример источника поставщика данных 944
Элемент <connectionStrings> 947
Установка базы данных Cars 948
Соединение с базой данных в Visual Studio 2005 949
Связный уровень ADO . NEТ 951
Работа с объектами соединения 952
Работа с ConnectionStringBullder в .NEТ 2.0 954
Работа с объектами команд 955
Работа с объектами чтения данных 957
Получение множества наборов результатов с помощью
объектов чтения данных 959
Изменение содержимого таблиц с помощью объектов команд 959
Вставка новых записей 961
Удаление записей 962
Обновление записей 963
Работа с объектами параметризованных команд 963
Указание параметров с помощью типаDbParameter 964
Выполнение хранимых процедур с помощью DbCommand 965
Асинхронный доступ к данным в .NEТ 2.0 968
несвязный уровень ADO.NEТ 969
Роль DataSet 970
Содержание 25
Члены Dataset 971
Работц с DataColUДUJ 973
СОЗД81Ще Dat.aColumn 974
Раэреше}Ще автоnpирamения:.Dдft полci1 975
Добавлeшrе DataColttmn в DataTh.ble 9.75
Работа с DataRow 976
Сврйство DataRow.RowState 977
Pa(io1'a с DataThl:;Jle 978
Работа с DataThbleReader в .NEТ 2. О 960
с.охранение DataSet (и DataThble] в формате XМL 980
Привязка DataThbleE .инreрфеЙсу iIOльзователя 982
Програ:ммиое удаление "трок 984
Применеrцiефильтров и COPT:ЦPOB~ 985
Об!Ювление строк 987
Работа СТИIЮМ DataVieW 989
Раба.та с адаптерами данных 990
-$эnолнение DataSet с ПOМD1ЦЬю aд~pa ДаННЫХ 991
Q'ro5раже;ние 11М~}I баi,JЫ данных в Понятные имена 991
Обновле1Цlе базы данных с ПQ~ОЩЬ1О объекта адaлrера дa.в.ных ~9З
Устмовка свойства InsertConttnand 994-
'Y'CТa80BRa свойства UpdateCommaIld 994
Уctанов!Ш своЙСтва DеlёiеСоmtiJ.and 995
Thн.еwrРОI!ание SQL-RОМaRД с ПОМОЩЬЮ типов пострoиreля ка:манд 995
Объекты DataSet с множеством таблиц и об~ты Dаta.RiЩtiоп 998
l1аввгаци:оюn.re возможности для связанных 'fаблиц )000
-Возможности мастеров данных 1002
стрorотишlзованныe объекты DataSet 1004
A1IтомаТИ'letЕИ тенерируемый компон€ит данных 1005
Рещоме 1006

Часть УI WеЬ.. п'ри.nnжени~ и WеЬ .. сервисы XМL 1007


Гnава 23. Web-страницы и Web-зnеменtы управления ASP.NEТ 2.0 1009
Ponь Н1ТР НЮ9
Web-npиложенил :и Web-серверbl 1010
Работа с вйрТуал:ън:ыми к<ttaш.Jгами пs 1011
Сервер разрабоТ1СИ ASP.NET 2.0 1012
PмьHrM,L 1013
Струи;rура HTML-докумеИТft 1014
Разработка IfI'ML-Формы 1014
Соэдание пользовательского интерфейса:на базе НТМL 1015
Роль сцenариев 1:UJИmiТа 1017
n:PkJМep ~ценария клиента. 1018
K-ОFIТролъ допустимо!;ти :вВOдm4ЪPf д8.ЮiA1X 1019
Лода1:fа запроса формм (G. EТ и POSТJ lQJ9
Создание ~классическоЙ· АSI?-crрающы 1020
26 Содержание

Ответ на отправку POST 1021


Проблемы классической ТеХНОЛОГИИ ЛSР 1022
DIaвHыe upеимущества ASP.NEТ 1.Х 1023
IЛавные преимущества ASP.NEТ 2 .0 1024
Пространства имен ЛSР.NEТ 2.0 1024
Модель программного кода Web-страницы ЛSР.NEТ 1025
Модель ОДI-ЮМОдУЛЬНОЙ страницы 1026
Модель страницы с внешним КОДОМ поддержки 1032
Структура каталогов Web-узла ASP.NEТ 1036
Роль папки Вiп 1037
Роль папки App_Code 1038
Цикл компиляции страницы ЛSР.NEТ 2.0 1039
Цmш компиляции одномодУЛЬНЫХ страниц 1039
Цикл компиляции многомо.цульных страниц 1040
Цепочка наследования типа Page 1041
тип System.Web.UI.Page 1042
Взаимодействие с поступающим IfITp-запросом 1043
Получение статистики браузера 1044
Доступ к поступающим данным формы 1045
Свойство IsPostвack 1045
Взаимодействие с исходящим НТГР-ответом 1046
Генерирование НТМL-содержимого 1047
Перенаправлениепользователей 1048
Цикл существования Web-страницы ASP.NEТ 1048
Роль атрибута АutоEveпtW1геUр 1049
Событие Еrroг 1051
Природа Web-элементов ynpавления 1052
Обработка серверных событий 1053
Свойство AutoPostвack 1054
тип System.Web. Ш.Сопtгоl 1055
Список вложенных элементов управления 1055
Динамическое добавление (и удаление) элементов управления 1057
Основные члены типа System.Web.uI.WebControls.WebControl 1059
Категории Web-элементов управления ASP.NEТ 1059
Несколько слов о System.Web.UI.HtmlControls Ю60
Создание простого Web-узла ASP.NEТ 2.0 1061
Работа с шаблоном страниц Ю61
Определение страницы Default.aspx 1064
Создание страницы lпventоry 1066
Создание страницьI BuildCar 1071
Роль элементов управления. связанных с контролем ввода 1074
Элемент RequiredFieldVa1idator 1075
Элемент RegularExpressionVa1idator 1076
Элемент RangeValidator 1077
Элемент CompareValidator 1077
Созданц:е отчетов по проверкам 1078
Резюме 1079
СФДElржан~е 27'

Гnава 24. Web-ПРИnО)J(ен"я ASP.NEТ 2.0 1081


Проблема состоаниа 10~l
Те:хволоrии управления состоннием .ASP.NEТ ]083
РОЛЬ состоЯ1:lIiЩ преДСТaDJlений АЗР.NEТ 1084
Демонстрация ИСП(ЦJ:ьзgвания СОСТО.!lНЩ.l представлeшrn 1084
Добав:JIение полъзовате':/IЬСКИХ дан.ньш состощшя преД;СТЗJЩf;8,ИЙ 1087
Несколыю СЛОВ о , данных состояния элемеmОI1 1087
Роль фaiЩа ЩQЬ;:tl.авах Ю88
Посдедиий пюбatIЪ,ЩiI;Й шанс ДЛЯ обработки мсКЛючени-й 1'090
БаООВЬ!Й класс HttpApp1icafion 109@
Различия меащу приnожеRНем и севнсом 1091
Поддержка д8ННЪrX СОCIГотrия прил.ожеНИR 1092
Изменение данных сОстощшя приложения 1094
ОбрабOТ]tа эа.:верmеmm рабоТы Web-npиложеmш 1095
Кэт npилu:щещm 1095
Кэшnрование данных 1096
Изменение фааlIа • .а$РХ lО98
ОбрабОТШl- сеансовЫх давшlX 1101
Дошщнителъпы:е члены HttpSesSlonState. ] ]03
Дщшые oookie 1104
соэдание д~ сооИе 1105
Чтение поступающих данных coo}de 1107
Нас-:гройкз. Web-ПР1-ШОЖенил ASP:NEТ с ПОМОЩЬЮ Wt:b.conftg 1108
Разрешение траCCЙJ>OВ':КИ с помощью <trace> 1109
НастроiЩа B~oдa со66щemill об ОIIШб:ках с ПОМОЩЬЮ .;:custoroErrors> 1110
СохрэдeJniС :цан.н.ых ср!--'Тоядияс помощью <sessloiiStare> 1112
Утилита адмщmcтрированин ~a A.SP.NEТ 2.О 11i4
Наследование конфигурации 1115
Резюме lH6

Глава 25.Web-сервисы XML 1117


.Роль Web-(~ервисов xмt 11 :i7
ПреиМуЩества WеЪ-сервисов XМL 1118
ОпределеЮfе клиента W.eb-сервиса,хМL 1118
ДOм:rtOHeнт:ы Web~серви:са XМL 1119
Служба ро~с,fЩi Web-сервиса XМL 112()
Служба описания W~Ь·серщша XМL 1120
Тран<'IIОРТJ:lЫЙ протщ<ол 1121
Пространст.ва имен .NEТ д,1IЯ Web-серВПСQБ XМL 1121
Пространство имен Syste:m.Web.Services 1I22
СоаДaшIе Wеь-сервиса XМL вручную 1122,
Тест:ирова:н:ие Web-c~c!l XМL J:J помощью WebDev.WebServer.e:x;e 1123
ТеСТRIpовщmе Web-серицса, XМL с РОМ;ОЩЬЮ IIS 1124
ПРОСМGТР WSDL-докумен'I'э, 1125
Ав1'аматичесЮf Г!tнер:ируемая страница 'ТеСТИРОВaIЩfl 1125
СОЗ,дaFIИе 'пользовательской стр.анlЩЫ теetrиpованю~ 1125
28 Содержание

Создание Web-сервиса XМL в Visual Studio 2005 1126


Реализация Web-метода ThllFbrtuneO 1128
Роль базового класса WebSeтvice 1129
Атрибут [WebSeтvice) 1130
Namespace и Descnption
Свойства ]130
Свойство Name 1131
Атрибут [WebServiceBinding] 1132
Иrнoрирование проверки соответствия правилам ВР 1. 1 1132
Отмена проверки соответствия правилам ВР 1.1 1133
Атрибут [WebMethod) 1133
Описание Web-метода с помощью свойства Description 1133
Устранение конфЛИRТOв имен WSDL с помощью свойства MessageName 1134
поддержка данных состояния WеЬ-сервисов с помощью
свойства EnableSession 1135
Язык описания Web-сервисов (WSDL) 1137
Определение WSDL-документа 1138
Элемент <types> 1139
Элемент <message> 1140
Элемент <poIiТype> 1140
Элемент <binding> 1141
Элемент <service> 1142
Снова о !Ipотоколах связи Web-сервисов XМL 1142
Связь НТГР GEТ и НТГР POST 1143
СвязьSOАР 1144
Утилита командной строки wsdl.exe 1145
Преобраэование WSDL-кода в серверный программный код Web-сервиса 1146
Преобразование WSDL-кода в npограммный I<ОД агента для клиента 1147
программный код агента 1148
Конструктор. заданный по умолчанию 1]48
ПОДдержка синхронного вызова 1]49
ПОДдержка асинхронного вызова 1150
Создание приложения клиента 1]50
Thнерирование программного кода агента в VisuaJ Studio 2005 116]
Доступ к пользовательским типам Web-методов 1152
Доступ к массивам 1153
Доступ к структурам 1153
DataSet ADO.NEТ
Доступ к типам 1155
Клиент Windows Fbrms 1155
Представление типов на стороне клиента 1157
Стандарт поиска и взаимодействия (протокол UDDI) 1158
Взаимодействие с UDDI в VisuaJ Studio 2005 1158
Резюме 1159

Предметный указатель 1161


ПОСВНЩ;:L1() эту книгу моей матери. Мэри 'Гроелсен. Мама!
Благодарю тебя за ПQДЦерЖlo/В проmлОМ. настоящем и буцущем.
Ах. да! И опас.lfбо за ТО. чro 1'ЬJ не стала наказыва'ТЪ меня. ИО1;да Я,
прmпеJ1 ДОМОВ с "ирокезом".

Люблюmeбя.
Лух.
30 Об авторе

06 авторе
Эвдрю Троелсев (Andrew 1roelsen) носит титул МVP (Most Valuable ProfessJo-
nal- "самый ценный специалист") по Visual С# в Мicrоsоft. а также является
партнером. преподавателем и консультантом Intertech Тraining (http:/ / www .
IntertechTraining. сот). центра обучения разработчиков ,NEТ и J2EE. Он явля­
ется автором множества книг, среди которых Developer's Workshop to
and AТL СОМ
3.0 (Wordware Publishing. 2000). СОМ and .NEТ lпtеroреrаЬiШу (Apress. 2002). Visua1
Basic .NEТ aпd the .NEТ Plaiform: Аn Advanced Guide (Apress. 2001). а также книга
С# and the . NEТ Plaiform (Apress. 2003). которая была удостоена ряда специальных
наград. Кроме того . он является автором множества статей по вопросам .NEТ для
MSDN online и MacThch (В зтих статьях рассматриваются различные аспекты меж­
платформенной независимости .NEТ) и часто выступает с докладами. посвящен­
нЫМИ .NEТ, в конференциях и грушrах пользователей .
В настоящее время Эндрю Троелсен проживает в Миннеаполисе. шт. Миннесота.
со своей женой Амандой. В свободное время он мечтает о том. что Wild выиграют
Кубок Стэнли. Vikings въщграют Суперкубок (он бы хотел. чтобы это произошло до
его пенсии). а Тimberwolves станут многократными чемпионами NBA.

о научном редакторе
Гэвив Смит (Gavin Smyth) являегся профессионалом в области программного
обеспечения с многолетним (и. как он считает, слишком большим) опытом разра­
ботки программ - от драйверов различных устройств до приложениЙ. предназна­
ченных для многоузловых серверов на таких разных платформах. как "несгибае­
мые" операционные системы реального времени UnJx и Windows. и таких язьшах
программирования. как ассемблер. С++. Ada и С# (не считая множества других. не
менее достойных языков). Он выполнял заказы для таких компаний. как вт и Nortel.
а в настоящее время работает для Мiсrosоft . Thвин Смит имеет ряд собственных пе­
чатных работ, вьпnедш:их в научных издательствах (ЕХЕ и Wrox- где они сейчас?).
но в какой-то момент он пришел к заключению. что критика чужих публикаций ока­
зывается куда более плодотворной. Помимо этого. когда он не борется в своем саду с
сорняками и насекомыми. он пытается заставцть роботов LEGO делать то . что они.
по его мнен:ию. должны делать (зто все исключительно ради детей- честноl).

Благодарности
Выпуск третьего издания этой книги был бы просто невозможен без поддержки
и помощи многих окружающих меня людей. Во-первых, следует выразить благо ­
дарность всей команде издательства Apress. Каждый из ее ЧJJенов приложил не­
мало усилий. чтобы превратить мою "сырую' рукопись в безупречный np0дYIiT.
Далее, я должен выразить признательностъ моему научному редактору. Thвину
Смиту (известному также под псевдонимом Eagle Еуе - Орлиный IЛаз), который
вьшолнил огромную работу. чтобы уберечь меня от множества ошибок Все остав­
шиеся оnrn:бки (опечатки, неточности в программном коде и т.д.). которые смогли
"пробраться" в эту книгу. конечно же. лежат на моей совести.
Спасибо моим друзьям и членам моей семьи. которые (В очередной раз) терпели
мои цейтноты. а иногда и связанную с 'этим несдержанность в поведеНШi. Такой же
блarодарности заслуживают мои друзья и сотрудники из Intertech ТrainJng. Я очень
ценю оказанную вами поддержку (как прямую. так и косвенную) . Наконец. отдель­
но е спасибо и " все фантики" моей жене Мэнди за ее любовь и содействие.
ВведеН\1В 31

Введени'е

Я
менталрн~
ПOМfПО .цреАЩ, ~oгo лет ' тому J:1aЗЗД, котда я npeдлDЖИI1 ИЭД<'i-1'елъ.етву АргesБ
юпrry. посвященную' еще не выпущенному 11а тот момент пан~ту Щ:J:стру­

cpelXC'FB рааработки ПОД нз.эвзниеМ Next Generatlon WiDdows Servfices


[NGWS- cepBuq;r Windows с.ледYКIIЦего поколения). Вы, навеРlIое. зна~. что цз­
кет NGWS в }~онечном CtfeTe стал тeM~ что сеroдня :называется платформой ,NEТ.
ПараЛДeJlbl:fО с моими ПССЛf{ДОВа:ния:ми в области разрабonи языка прогрaммиpG­
вания С#. 'и rurатформы .NEТ:ш'.JIO [!оад.ание началъп.ого 1Jaрианта соответствующей
руноrmси.. Это бы,л фантастич.есRИЙпроект. но 11 должен IiриЗШ!.ТЬСЯ. что писать
о' т~ологlЩ'. 't'(:)roрая претерпевала весьма эm1ЧИтеЛЫlые изменения ~ ходе сво­
ей раэработ.ки,бъmо занятием, очень действyR)ЩИМ на нервы. Н. ечастью, Ц9СЛе
МJЮг.их. бессонных ночеИ. toдe-тo 1\ началу лета 2001 года. первое и~дание КFIИГИ С#
artd the .NEТ Plt1yotm было опуБJШRовано пОЧТй одновременно с выходом .N:EТ 1.0
Веtз2.
С того ~ремени я был чреавычaiiно рад и благодарен 'Тому. что ЭТа I<FЩГа- очень
благоск.ЩПiНО Пpй:FIИМaется прессой 'И' самое IЛaВНое, читатедnми. За эти годы KНU­
га пpeдлar~асъ в на честве S'оминанта на npемшо JoJt Award (л. To~a ~пролe'l'(Щ" ... )
и на премию Referenceware ЕХсеI1enсе .Award 2003 года s ilатегории кшrг по цро­
граммиРQва,нию(и я с-частпив, wro на этот раз мне повeзJIO),
BТOPQ~ ИзДЭlШе этой КJШrи (С# Ш1li the .NEТ Platjorm. SeCQnd ЕdШon) Дал!;) мде
возможность 'ВН'.JIЮЧИТЬ в нее Maтep1laJI, соответствующXdЙ версИИ 1, 1 платформы
.N;Eт. Хотя второе издани.е Illiиm содержало обсущдение :мно\Жества H€lBыx. ';ГеМ. ряд
тап и примеров .8ЮIЮчи'1'Ь в OI«)нчs.тМЬНЫЙвариaнr кнши все же не удадоер.
T~epъ. ROгда :книrа предt'тaliJ.rена в третьем издаюш, $I. с удо:вдетвОрe:щ<tем MOry
зая,вИТh, что она содержит (почти) все темы и nptwepbl, КОТQръщ-я н:е смог предста­
BJp'h lJ цpeды.uyщиx изДаниях, это издание не ТОJJЫФ учит~ает все i!rШого'Щ(Щен­
ные усовершеНствования, пре)(Лarnемые в .NEТ 2.0. но вюno~ет и ряд !'Щiil!. ЕОТО­
рые. буцучидавно написamIЫ.Мй, оставались до сих пор неопубдикоВ;ШНЫМИ- это
тmсается, наприм:ер, оnис.аБ:ин CIL (t:ommon Intennediate L.angu:age - 9БII(ИЙ про­
межуточный .fIзык•.
:Как и в предыдУЩИХ иВдаНИЯХ.lJ ЭТОМ. третьем иадании IGЩГИ FЩ ос;в:ове про­
стото И пошi:I'н.arо материала предетавлены .,нз:ьrк прогрlЦl4МИроБa:fЩН С# и библи­
отеки базовых &Ласroв .NEТ. Я шnwгда :не ПО!lИJ!,{ал СКJIо}Шо~ть HeКQT~ЫX авторов
Н, t.QЗДа.нию технических книг. более похожих на руководство по ПОДГО1.'Ощсе Е: ЗКГ
замену GRE. чем пригодный ДЛfI Ч'I'ения: теке!.: Задачей этоro нового дздания по­
иреж.вему осТается предоставление вам информации. необходимой ДJШ D.острoemш
прогр.а.ммных реше'ЮIЙ сегоДItя, а не расточительная трата BpeMe;mJ на рассмотре­
ние :мнЬжеЕ1'Ва. деталей. 1<оторые окаmuваются важпь:щи ДЛЯ ОЧe,JiЬ УЗROrd крута
спе.цИа:лИстов.
32 Введение

Вы и я - одна команда
Публикации разработчиков новых технологий предназначены для очень требо­
вательной аудитории (я должен знать это не понаслышке - ведь я один из них).
Построение программных решений дл1f. любой платформы требует чрезвычайной
детализации и учета множества особенностей соответствующей отрасли, I-tOмпа­
нии, .клиентской базы, а также учета сути дела. Вы можете работать в электрон­
ном издательстве, разрабатывать системы для федерального или местного пра­
вительства, работать в NASA или каком-то оборонном ведомстве. Я, например,
разрабатывал программное обеспечение для обучения детей, создавал различные
N -звеШfЫе системы и участвовал в многочисленных проектах для медицшlСКИХ и
финансовых учреждений. С вероятностью почти 100 процентов тот программный
код, который вы создаете на своем рабочем месте, не имеет никакой связи с про­
граммным кодом, который пишу я (если, конечно, нам случайно не приходилось
работать вместе).
Позтому в этой книге я сознательно избегаю примеров, в которых программный
код связан со спецификой определенных отраслей производства или сфер про­
граммирования. Я пытаюсь описать возможности С#, объектно-ориентированно­
го подхода, CLR и библиотек базовых классов .NET 2.0 с помощью примеров, не
использующих такой специфики. Вместо того чтобы в каждом примере заполнять
таблицы реальными данными, рассчитывать платежки или выполнять КaI(ие-то
другие специальные вычисления, я буду рассматривать объекты, с которыми могут
иметь дело все, - например, автомобили (с их геометрическими формами и слу­
жащими соответствующего предприяти1f.', добавленными для полноты картины).
И'ryТ на сцену должны выйти вы.
Моей целью является как можно более понятное объяснение возможностей яэы­
ка программирования С# и описание различных аспектов его применения в рам­
ках платформы .NEт. Я сделаю все, что будет в моих силах. чтобы вы, используя
знания и навыки, полученные в процессе работы над этой книгой, могли продол­
жить дальнейшее освоение соответствующих технологий.
Вашей целью является освоение этой информации и применение ее к вахпим
конкретным задачам программирования. Я, конечно, понимаю, что ваши проекты
вряд ли напрямую связаны с автомобилями, имеющими имена домашних любим­
цев, но так уж заведено в прикладных науках! Уверен, если вы поймете КОfЩепции
платформы .NEт. представленныe в этой книге, то сможете предложить и реализо­
вать решени1f., подходящие ДЛ1f. вашей конкретной среды программирования.

Обзор содержимого книги


Книга Язык npогра.м.мupованuя С# 2005 11 платформа.NEТ 2 .0, 3-е 113даНl1е де­
лится на пять логически обособленных разделов. каждый из которых состоит из
глав, тем или иным образом связанных между собой. Если вы имели возможность
оэнакомиться с одним из предыдущих изданий зтой книги, вы сможете заметить
некоторое сходство в названиях ряда глав, но знайте, что здесь почти на каждую
страницу был добавлен новый материал и дополнительные примеры. Вы можете'
также заметить, что некоторые темы, также освещенные в первом и втором изда-
Введение 33
ШU1X (нацример. сериал:изация объе~ro:в и сборЩ1Щ мусора .NEТ). ::щесъ представ­
лены в ~дe O'IДелъных IШm.
Кроме 'ГОго. и ВhI вправе это оЖЩta.:n.. третье издание ~IИГИ содержит :несколько
глав е совершенно H08ым материалQМ (В Ч~СТRОСТИ главу.. посвюценную сиН.тaJ~СИ­
су И семан:rике CIL) и noдробвоеОDИсaщrе опецифичес:ких ВО3МОЖlЮСТей .NEТ2.0.
Теперь. после всех ~делаиНых ОГDВарСщ. мы перейдем:к .IqJа.rn<ОЙ .харантериCТИitе со­
держимоro R1JИI'И по <.!астям И rnавам.

Часть 1. О.бщие сведения о языке С# и платформе .NEТ


Целью ЭТОЙ части IQIИГИ Шlляется описание баз(щЦIX npпнцилов фуmщиони­
рования платформы .тт, системы Т}ЦlOВ .NБТ И раз.n:ичиых инструыеitТaJlliНЫХ.
средств разрабопm.. используемых при СОЗДании ПРИJl0Жециif .NE"Г (многие из Ta~
кик иаструмеНТaJIЬНЫХ средств ЯВJШЮ1'СЯ lIРorpам;мн:ьrми продуктами с QТttрытым
исходным «адом). 3десь же npедстmшеНЪ1 базовые ВОЭ:МОЖН0СТff языка npограмми­
роlЩНЩ1 С#.

Глава 1i Философия ..НЕТ


Материал ЭТОЙ :r:лаэы ЯВШieТСЯ фундамеJ-IТОМ ДJm пmm:мания всего остального
материала lUiИI'и. 1Лава наЧИl!aетаК с обсуждения традициОl:Iныхвозможностеи
разработки цporpaмы в среде WtndQWS ·и ytcfJ3ы8a'f на недостатки Э'I'~го. испОЛЪ3D­
~шегосS ранее подхода. Но главной ЦeJIblQ .ЦafЦIоЙ Г.,'ШБЫ является зl:lаномство с
иае10РОМ "строительных блоков
Н

.NEТ.ТaIoIx ЮIJI! СЩ (Соmпщл Languзgе Rnntime-


общензыковая среда въmoлнеНинj. c'ffi (Coдunon 1УРе System - общая си(!Тема ТИ~
пав). CLS (Common Language Speciflcation - о(5:UJ.ензьщовые спецификации) и би.­
{)лиоте$ базовых Юlассов. Эдесь же предmп'ается вводнщ! 'иНфIilРМация о ЯЗhlКе
uрогр~ровaJrШ] С# и формате КОМlЮНOВОчtl:БЦ{ БДQЩ)В .~ раесматриваютc.JI
МеЖIJJIатформеIIЩUJ незави~ШОеть. вытенающая из· ~ОЙ npирoдJ>1 плR!J'формы
.NEТ. .и: PQ/U> си (СоIЩDОП Language fufras1:ruCture - общеязыковал инфраcтpymypa).

Глава 2. Технология создания приnо_ений на IIзыеe С#


в ;:JТОЙ Т'лаве прeдлaraетСJ1 :крат.кое описание npоцес.еа КОМПИЛRЦИИ и отладки
файлов IIСхадноro кода Д1Щ ПРOI·рамм.. написанных Н3 .Я3БIRе С#. а таюве об~-
1O'IOI CoopJ~e инструменты и те.хишюгии . Сначала вцrузнae-re. ~ . ИСПОЛЬ­
зовать КОЫIШТ1ЯТ()Р КОМаЕДJ:tой C'I'pоки (osc.e~eJ и. файлы ответдых сообщений.с#,
После этого будут рассмотрены lIекоторые из множества nЩl,eТОВ. ПРeдJIaI'aIOЩИХ
.ин.тегрирова:н:нуlО среду разработни. - ЭТО. В ч-астности. 1{>xtPam. Sha:rpDevelop.
Visua1 С# 2005 ЕХРГеБЭ .и (конечно Же) V1sua1 Studlo 2005, 1Jт же будет npедстав­
.лен: ряд a:mщoГ1fЧНЪ1X пакетOI~ с открытым ИС:ХОдИым водам (VU. NJ\nt. NDoc и т.д.).
которые на 'ВCЯ'.fCИЙ случай должен иметь .!1ЮбоЙ разработчйк .NE'Г.

Часть II~ Язык програММИРОВ'ания С#


в этой частщ ~сслt:.цуют(;я ocHoB1:Iыe ВО.8МОЖНОСТИ языка пр6граммщ:ювани.я: С#.
включая H()B~e синтаксцчесlЩе конструкции. появивI1tИеся с ВЪ1XOДO~ .NEТ 2,0,
.. Кроме TOfO. часть П поэmmомит вас с элементами crs [классы. интерфейсы' струн­
тjpьt.neречни и д~eг.aтъr) u ко:астрJlЩИJ1МИ общих типо.в .
34 Введение

Гnава З. Основы языка С#


В этой главе рассматриваются базовые конструкции языка программирования
С#. Вы освоите технику построения классов, ВhIЯСЮlТе разницу междУ nmами. ха­
рактеризуемыми значениями. и ссылчиы:ми типами, приведением к объектному
типу и восстановлением из "объектноro образа", а таюке роль moбимоro всеми ба­
зового класса System.Object. В этой же главе ПOlшзано, как ruIaтформа .NEТ за­
ставляет работать самые простые программные ICOнструкции, такие как перечни.
массивы и обработчики строк. 'Наконец. в этой главе рассматривается ряд специ­
фических для версии 2.0 вопросов. включал типы данных. для которых имеется
разрешение принимать значение null.

Гnава 4. Язык С# 2.0 и объектно-ориентированный ПОДХОД


Целью главы 4- является выяснение того. как ЯЗЫI( С# сочетается с базовыми
принципами ООП - инкапсуляцией, наследованием и полиморфизмом. После рас­
смотрения ЮIЮчевых слов и синтаксиса. используемых при построении иерархии

классов. будет выяснена роль XМL-комментариев в программном коде.

Гnава 5. Цикл существования объектов


В этой главе выясняется, как в рамках CLR с помощью сборщика мусора .NEТ
организовано управление памятью. В этой СВRЗИ вы узнаете о роли корней прило­
жения. генераций объектов и nmа System.GC. После изучения базовых вопросов в
оставшейся части главы будут рассмотрены тема объектов, преДУсматривaIOIЦИX
освобождение ресурсов (через интерфейс IDisposable). и процесс финализации
(реализуемый с помощью метода System.Object.Finalize (»).

Гnава 6. Структурированная обработка исключений


В этой главе обсуждается стрyкrypированный подход к обработке исключений
(т.е. исключительных ситуаций), которые могут возникать в среде выполнения.
Бы узнаете о ключевых словах языка С# (try, catch. throw и finally). которые
позволяют решать соответствующие проблемы. а также выясните разницу между
исключениями системного уровня и уровня ПРКjl0жения. Дополнительно В главе
обсуждаются специальные средства Visua1 Studlo 2005, призванные упростить за­
дачу выявления и обработки исключений. ускользнувших от вашего внимания.

Гnава 7. Интерфейсы и колnекции


Материал этой главы опирается на понимание принципов разработки приложе­
ний с использованием объектов и охватывает вопросы программирования на базе
интерфейсов. Вы узнаете. как определить тиI1ы' поддерживающие множество эле­
ментов поведения, как найти эти элементы поведения во время вьmОJПIения и как
выборочно скрыть такие элементы поведения. используя явн.УЮ реализацию шt­
meрфеikа. Б оставmейся части главы рассматривается пространство имен System.
Collections. с помощью котороro демонстрируется польза типов интерфейса.

Гnава 8. Интерфейсы обратного вызова, AeneraT"1 и события


Цель главы 8 заключается в разъяснении nmа делегата. Упрощенно roворя, де­
легат .NEТ- это шаблон. "указывающий~ на методы в приложении. С помощью
r

тaROl'O шабдОНэ' вы nOJl}"'taeтe БОЭМ()ЖНОС1'РСТРОИТЪ cв:cтeМh1. в которых множество


Qбъеитов могут быть евязmIЫ двусторо:щшм обменом. После выонet-mвB ВОЗNОЖ­
FЮстей использования делега-rов .NEТ(вwпoчая мнржество noэмoжностeii. rro.t!IШВ­
шихсц с версией 2.0, - НЩIpI1Мер. ahOl-шмные ме.тоДЪ1) .в главе рассматривается
юпочевое СЛОВО С# event, КОТО)XIе ИСfl().l'1b3ye-I:tя ДJm ТOГQ, <rroбыynpocrитh npоце.сс
ЦРОГРaмIYIИpOвании о UОМОЩЫО делегатов.

Глава 9. Специальные пр~емЬ! построеНИII типов


Эта гдэва поаволит вамrлубже ПОНЯТЬ во"!Мо»IВОcrи 8ЗъtRa цроrраммироuЭJiИЯ
С# путем :изучения более соверше~tных методов проrpаммцровщщя, Вы узнаете.
RaК 'ИСДользовать переrpУЗlty оцеpaциji и создавать ПOJIЬЭовэ.тельС1Ще npоrp3ммы
преобрааовamm (ЯВl'IOJ;'О и.rш неявнего), IЩR С1'РОИТЬ индексаторы ТЮIОВИ работатfo
с указателлми C-ТFЩa в файле" .cs.

Глава 1О. 'Обобщения


в СВЯЩfс разрабоо:кой _NEТ 2.0 язык прогptl.ММИрования: C:N б:ылрасmиpен с
I:I,~Ю додцержки новой возможности СТ$. с:вnзatЦi(JЙ ее тцк rIащ.mаем:blМИ обоб­
щениями (geneI1csJ- Вы увидите. что программирОвание с ПОМ()ЩblO оБQБщений
ускоряет процесс создщmя приложеицй и обеецечивает типовую беЗОПЭС1l0СТЬ.
Эдесь pa:CCM(t)Weны раЗЛl;Iчные обобщещныс 1'IIIUJ.1 из ПрОСТРЗ1-IСТВа. име:1-I System.
Co1;lcectioJls.Generic, а также 1I0НЗ:зaнQ, ,сак строить свои собственныe обобщен­
Ные методы и 'rlЦlbl (:как с orpаидчеы:ищми, так и без таневых).

Ча~ть ш. Программирование IfQМПОНО.ВОЧНЫХ бл,оков .НЕТ


в э:той части раССМQ'IpИБаеТея формат IЮмпоновочны:х блоков .NEт. Бы узнаете,
lШК разверпyrь Е CRонфи;гурировать библиQ'l'еRИ цporp~oгo ROда .NEТ, и ~c­
ви:re ввутрet-uпoю cт~тypy бинарного образа .NEТ. В этой те чаСТIi объ8СIШется.
~ атрибутов .N!CТ и 1iiОНСТРунция. М60rопO'l'O'lНl>Щ l1plЩожений, В последних rла­
вак этой час"J'Ц рассматриваются НИЗ1щуровневые в@проcы ГТаЕие Kf\К. НanpI1Мер,
объtJ1P'НЫЙ KOHтeRCT). а таюRе СШn'aJ(СИС JI семацтика CIL.

Глава 11. Компоновочнwе б1l0Q .НЕТ


с ТОЧКИ зреmmвыокоуровневогоo пццхода, кo,мnoнoВОЧfLble БJl.OJCИ- ЭТО файлы
~ ,d 11 щи "'. еке-. НО такая J-штерпретaщ.rn JЩМIIОНОВОЧНЫХ, бlIО1ЮR .NEТ очень да­
ДеIro от QисчерпpmающеЙ·. ВЫ узнаете, чем. ОТJ1ичаютсц ОДЦомо.цульные и МНОГО­
»ОДУЛЫIblе 'КOМlД)новочные 6локи и RЦ СТРОJiXтсл И ИlJСТЗЛ:ЛИJ!IУЮТСЯ такие оБЪj$­
ТI!I. Вы H~Tecь Iсонфигурировать npиватяще и оt:5щедО€ТJIЩЫе компоновочные
БJljО~. щшольауя ДЛЯ этого XМL.фaiШы ... config и 1Юмпоновочные бщжи полити­
ки поСта:вщщш. По ходу дела БУдУТ выя:снены внyrреlЦlЯЯ C'ТpY'J(тypa аАС (Global
Asэещрlу Cache - щоба:льный НЭШI(ОМПОНОВОЧНЬ1Х блоков) и роль yт:идиты IWн:фи­
rYPaIJ):IR .NEГ F'гam:eщJJ"k 2.0.

Гпава 12. Отображение типов, динамическое СВЯ3ыilаtt..е


и npограммироваliие с ПОМОЩЬЮ атрибутов
Б щcme 12 изyq:ение RОМnОН080Ч:НЫХ БJЮ1со:а .NEТ прододщэ'e'I'СЯ - эдесь рассма:­
триваетскпроцесс обнаPY1Remm ТИПОВ в epeд~ ВЬЩОШl~rШВ с помохщ.ю ПРОi::'Тран-
36 Введение

ства имен System.Reflection. С помощью этих типов МОЖНО строить приложеЮiЛ.


способные читать метаданные компоновочных блоков U Ha лету" . Вы узнаете. как
динамически активизировать и обрабатывать типы во время выполнения програм­
мы. используя дUНQJКuчеСICое связываиuе. Здесь же исследУется роль атрибутов
.NEТ (как стандартных. так и создаваемых программистом). Чтобы иллюстриро­
вать возможности применeЮiЛ обсуждавшихся подходов. в конце главы рассматри­
вается конструкция расширяемого приложения Windows Fbrms.

Глава 13. Процессы, домены приложений, контексты и хосты CLR


Здесь выполняется более глубокий анализ структуры загруженного выполня­
емого файла .NEТ. DIавная цель - иллюстрация взаимосвязи междУ процессами.
доменами приложеI;IИЙ и границами ко}{текстов. Определив эти объекты. вы смо­
жете понять, как обслуживается CLR в рамках операционной системы Windows. и
расширить свои знания о mscoree.dll. Представленная здесь информация может
оказаты;:я очень полезной при освоении материала главы 14.

Глава 14. Создание многопоточных приложений


в этой главе объясняется. как строить мноroПОТОЧНIiIе приложения. и иллюстри­
руется ряд приемов. которые вы можете использовать для создания программного

кода. безопасного с точки зрения многопоточных приложеНИЙ. В начале главы сно­


ва обсуждается тип делегата .NEт, и это делается с целью выяснения внутренних
механизмов делегата. используемых для поддержки асинхронного вызова методов.

Затем исследуются типы пространства имен System.Threading. Здесь обсуждает­


ся множество типов (Thread. ThreadStart. и т.п.). позволяющих очень просто соз­
давать дополнительные потоки.

Глава 15. Понимание CIL и роль динамических


компоновочных блоков
В этой главе ставится две цели. В первой половине главы рассматриваются
синтаксис и семантика CIL. намного более подробно. чем в предыдУЩИХ главах.
Остаток главы посвящен выяснению роли пространства имен System.Reflection.
Emit. С помощью соответствующих типов можно строить программное обеспече­
ние. позволяющее генерировать компоновочные блоки .NEТ в памяти во время
выполнения программы. I\oМПО}Iовочные блоки. определенные и выполняемые в
памяти, формально называют дuШLМUчеCICUМU комnоновочиымu блокамu.

Часть IV. Программирование с помощью библиотек .НЕТ


к этому моменту вы уже имеете достаточно информации о язьmе С# и формате
компоновочных блоков .NEТ. Часть IV предлагает расширить ваши новые знания
и исследовать целый ряд пространств имен в рамках библИотек базовых классов,
в частности файловый ввод-вывод, слой удаленного доступа .NEт. конструкцию
Windows Fbлns и доступ к базам данных с помощью ADO.NEт.

Глава 16. Пространство имен System.IO


По названию указанного пространства имен можно догадаться, что Systern.IO
обеспечивает взаимодействие со структурой файлов и RaТалогов соответствующей
F

машины. Из этой I:iшвы вы узНаe:tе, как пporрЭММНNМИ ср~~вами м.0ЖJr0 создать


IИJЩ~) систему каталогов, как размеЩа.тъдaвю.tс:ануТри раЗЛИЧНhlX Пd­
"I:QКЩ'I (ф.овых' строIroвых,.в tш:мкrи и т.д.) И хак ВЫВОДИТЬ их олуда.

Глава 17. СериаnИЭIЦИЯ объектов


в ~ТОЙ главе рассматриваютCiЯ сервисы сериа.лиз.ации объектов Дl!IЯ I1.Лsтфор­
мы .NEТ. ~щенно говоря. сериаmзация позволяет "консервировать" сОСТ()яние
объе~та (ил:ii МНОЖ.ества свиэанныхобъeRТOВ) В пото:ке j1.J"Ш использования в бw­
щем . Д~сериализlЩия (кан вымажете догадаться сами) является процессом 113-
ВJIечеИИII объекта из потека ДЛ.iI восстановления -в памяти с целью иCnоJlЪЗOВания
Этого оБЪепта в npильжении. Пошш базовые прИ1ЩИnЫ этих процесеов, вы сможе­
те y.пp;mmпъ цроцессами сер.иализации с помощью интерфейса ISeria1izable И
:множества fЮБЫХ атрибyroв . .npeдnarаемых ,NEТ 2.0.

Глава 18.. Удапе'нное взаимодейотви,е .НЕТ


~'рек:и распроСТраненному убеждению. web-сервиcы XМL не ЯВЛЯЮ'I'с.я: еДИН­
ственным средством построепил рас.предменйых прило!Жений АЛЯ платформы
.NEТ. Из этой главы вы узнаете о слое УдаЛенного дocryпа .NE'Г. Бы yвидmе. что
CIМ подцеРЖИ'вает npocтьre возможности обмена об'Ьектами мещцу приложеJШЯ­
ми из разНых доменов и аа рааных машинах. используя семантику МВV" (marshal-
by-vзlUе- маpmали:вг по 3На'leНИЮ) и МВR '(marзhal- Ьу-rеferenсе- маРЩaJIИНI' по
ccьmкe) . По ХОду'дела вы узнаете. кaR .B деклараТИБНОЙ форме во время ВЬПIщrнепия
можно изменить .поведение распределенного .NET-прИJJожения. исподьзул XМL­
файлы конфmypaцnи.

ГlШва 19. Создание OkOH с помощью System.Windows..Forms


в ' этойгл:аве 1iачинае:rсв ваше 'знакомство с пространством имен S уз 'с enl _
Windows . Fo l'InВ. ПQДРОбно обсужцается вопрос построения традиционных npи­
ложеRи'и с графическим иНтерфейсом. пьддержившощим систеМЫ меню. ПIUJМИ
ИRструментови строки сас:rонния. как :и следует ожидать. здесь рассматриваются
различные аcnеКты.npоектировани.я форм в Visua1 Studio 2005. а дm! .NEТ2 . 0·­
цeлыйяд ТМОВ Windows Fbлns (Me nuStrip. тооlstriр:ит.п _).

Гnава 20. В'изуализация графических данных средствами 001+


ВЭ1'ой гла:веговоритсн: о ·том, :как реализовать динам.иЧес:кую визуализaщt10
графическ:их дa.IO:i:ЫX в приложенИи WIndows Fbrms. Кроме обсуждения вопросов
обрабOТКJlI ~фтlШ. цветовых даШIbIX. reoметрJolЧССRИX образов .1'1 файлов иэобра­
жеEmй. .в этой г.паве рассматриваются воnpооы проверRП: попадаЮfН в заданную об.
ласть и теXFJИНa перетаскИвinmя об'Ьеlftов в рамиах графичесROrО ин-терфейса, вы
уэнаете о новом. формше ресурсов .NEГ. который. пах в.ь.t уже моясете. догадываться
.и этому MOMesтy. ОСНован на ХМ1....npeдстав.itении данных.

Глава 21. Испоnьэование эле.ментов управлекияWiпdоws Forms


Этз "Глава НВJlRеТСll пdследней из глав ХНИТIiJ, связанных с обсуждением пряло·
жений для Wfndows, й зде<:ь будет ' рассмотрено множество элеменroв графического
ИВТq>феЙса. преДлагаемых в .NEт Framewotk 2.0. вы Raучи-гесъ исuользовЭТI. раз-
38 Введение

личные элементы управления Windows Fonns. узнаете о приемах разработки диа­


логовых окон и наследовании форм. В этой же главе рассматривается возможность
построения пользовательских элементов управления Windows Fbnns. которые мож­
но интегрировать в ШЕ (lntegrated Development Envlronment - интегрированная
среда разработки).

Глава 22. Доступ к базам данных с помощыo АDо.NЕт


ADO.NEТ - это АР! (Application ProgrammJng lntеласе - интерфейс программи­
рования приложений) доступа к данным для платформы .NEт. Вы увидите. что с
тИIJами ADO.NEТ можно взаимодействовать как на связном уровне ADO.NEт. так
и несвязном. В этой главе будут рассмотрены оба эти режима ADO.NEТ. а также
некоторые новые воэможности. связанные с .NEТ2.0. включая модель источника
данных. построители строк соединений и асинхронный доступ к базам данных.

Часть У. WеЬ-приложения и Web-сервисы XML


Эта часть книги посвящена созданию Web-приложений ASP.NEТ и Web-серви­
сов XМL. Из материала первых двух глав этой части вы узнаете. что ЛSР.NEТ 2.0
является значительным шагом вперед по сравнению с ASP.NEТ l.x и предлагает
множество новых возможностей.

Глава 23. Web-страницы и Web-элеменТbI управления ASP.NEТ 2.0


В этой rлаве начинается изучение Web-технологИЙ. поддерживаемых в рамках
платформы .NEТ с помощью ЛSР.NEТ. Вы увидите. что прогpaммный нод сценариев
серверной стороны теперь заменяется Иреа.llЬНЫМИ~ объектно-ориентированными
язьmами (такими как С#, vв .NEТ и им подобными). Здесь буJIYГ рассмотрены КJПO­
чевые для ЛSР.NEТ вопросы. такие как работа с файлами. содержащими внеIШПIЙ
программный код поддержки, роль Web-элементов управления ASP.NEТ, использо­
вание элементов управления, связанных с контролем ввода. и взаимодействие с
новой моделью Иmаблона страницы". предлагаемой ASP.NEТ 2.0,

Глава 24. WеЬ-приложения ASP.NEТ 2.0


эта глава расШИрЯет ваши знания о возможностях ASP.NEТ с помощью рас­
смотрения различных способов управления состommем объектов в рамках .NEт.
Подобно классической модели ЛSР. npиложение ЛSР.NEТ позвол.яет создавать фай­
лы cook1e. а также переменные уровня приложения или сеанса. Однако ЛSР.NEТ
предлarает и новую технологшо управления состояниями - это кэш приложения: .

Рассмотрев многочисленные способы обработки состояний в ASP.NEт. вы сможе­


те выяснить роль базового класса System.HttpApplicatioI1 (скрытого в файле
Global.asax) и научиться динамически менять поведение Web-приложеиия в сре­
де вьшолиения, испол:ьзуя файл Web.config.

Глава 25. Web-сервисы XML


В этой последней главе книги выясняется роль Web-сервисов XМL и рассматри­
ваются возможности их создания в рамках .NEТ. ГРубо говоря. Web-сервuc- это
компоновочный блок. активизируемый с помощью стандартных НТГР-зanросов .
BBeдetl~e 39
ПреИМуЩество ЭТОГО подхода ~aюnoчаетсн в том. что HrтP ЯВ1ШетЦJ: eeT~ прq­
Токоло'М. npименяемы:м почти повсеместно. поэтому O}'J дрекра,сtю UQДXO,lWi:1' ДЛЯ
йспользовa:mm в распределенных CIfCTeMax. нейтралъfJЫX в ОТНОд:IetIЩJ ра.'iЛИЧНЪ1X
IШатформ й языков. Эдесь же вы уэпаете о мношеСТIlе со:путствующщ технОJ1IОI'ИЙ
(WSDL, SOAP и UDDIJ. «оторы:е обеспечив;uoт га.Р~О:ЮП\:) В3а»Модейр1'13ИЯ Web-cep-
виса п ВнeIПн.еtо ltJIИе~.

ИСХОДНЫЙ КОД примеров книги


Прогрзм:мды::Щ над всех дршyrеро~ из этОй RНЮ'И (с ТDЧНОСТЬЮ ДО встречающихея
кое-где l'IеБОJПi>llIЩ фра:гмеитов) доступен ДЛЯ загруз:ки и3 раздела ИСХОДl10rо Iro,ца
Web-узла иэдательетва. Быполн:ив поИСк по названию книrи, перейдите па ее "до­
машнюю· cтpшnщy . откуда в~сможете загрузи,т.ь файл 7/. zip с fiCXOдньtм КОДОМ
примеров . После раСЦа1ЩIЩИ содержиМОТ'О ЭТОГО файла вш обfIаружите со ответ­
СТВ~ЩИЙ npогрaмщrы:й код. разделенный по tлавам.
Обрат'ите Bl-ШМа1Ще H~ ТО, ЧТО В книrев разДeJ1aJC с названием Исходн.ый 1<:00
уиа~ано. ГАе СОДер3mТСЯ прОllРaммJ'JblЙ:КОД обсущдаёмьго примера. Этот npоrpaм­
мI'Jый нод МОЖНО зarруrштЬ в VjsuW stud10 2005 Д1Ш npoверки и модификации.

ИОХОАныЙ "о,ц, 8 таком при-ме'Jан.ии 'Ук<!з~!Ваетi::я сqЫЛJql на I(атэ.лor, содержащий ИСХОДНЫЙ "ОД
соотвеТСтвующего примера.

ДлЯ ЗЩ'рузкиnримера откройте Файл *.sln в YRазанвом пОДI(ЭТWIОl'е.

Связь с автором
Если У вас :во:ш:mсН)'Т BOIipQCbl в связи С иоходным кqдом примеров, потреб­
ность в дЩНшнителъных раэъяснеJllUD( или просто желание поделитЬ<'.JI СВОИМИ

.цдеими в QТRоmении ru:ra:rформы .NEТ, без всяхоro стеспе.ни.ч IIИUIИТe мне на мой
~eC ЭJreRТровнuй почты а t roelsen@Irite'rte<:hTra:tni n g. с от (чтобы трантпро­
~т.ь, что Ba1ne "со6БJцение не она.жетсл в Roрз~mе моей почтовой системы. укажите
·C#1E~ встро:ке 'темы).
Я постараюсь сделать .все ВОЗМDжное. чтобы ответить в при~м:.леМhlе CPOKIl, во
прошу учесть TD. что Я. JtaR и вы. время от Вр6мени бываю, очень занят, Еслн а не
QТ'ВечaIO в Течение недели или lI!JY'X. то, знайте. "<11'0 это не.из вредности и не потому.
что Jf не хочу общатьм с вами . .н просто зa1l.я.т (или. если ,выпад о сЧаС1:'ъе. НЮ!;0)JCyСЬ
где-то на o'JДЫXe),
T~ что, :вперед!, Спасибо за то. что вы кynшm эту книгу (W1И. jtЮ( ~.
~i:J.JЛНН)i'Jm в вее :в книжном магазине. об;цуМЫВая возможвоC'rЬ ее понудк:и). Я ,ва.­
деюсь, 'rtO вам будет IIpИ.flт.Но читать ее . и им CMow.eтe прим:ени1Ъ nQлyчeIщыIe эва­
щш в блш'орьдвых целях.

Берегцте себя.
8нiJрю Троелсен.
40 Введение

От издательства
Вы, читатель этой книги, и есть главный ее критш( и комментатор. Мы ценим
ваше мнение и хотим знать, что было сделано нами правильно, что можно было сде­
лать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересно услы­
шать и moбые дрyrие замечш-uш, которые вам хотелось бы высказать в наш адрес.
мы ждем ВaIIIИX комментариев и надеемся на них. ВЫ можете приспать нам бу­
мажное или электронное письмо, либо просто посетИJЪ наш Web-сервер и оставить
свои замечания там. Одним словом, moбым удобным для вас способом дайте нам
знать, нравится или нет вам эта книга, а также выскажите свое мнение о том, как

сделать наши книги более интересными для вас.


Посылая письмо или сообщение, не забудьте указать название книги и ее авто­
ров, а также ваш обратный адрес. мы внимательно ознакомимся с вашим мнением
и обязательно учтем его при отборе и подготовке к изданию последующих книг.
Наши координаты:

E-mail: info@williamspublishing.com
~: http://www.williamspublishing.com
Информация для писем из:

России: 115419. Москва. а/я 783


Украины: 03150. Киев, а/я 152
,

ЧАСТЬ I
Общие сведения
о я3ыIеe С#
и платформе .NET

в этой часТи ....


Глава 1..филоСОфИЯ ..NEТ
Глава 2. Технология создани.я прилажениЙ.на s:lзыке С#

ГЛАВА 1
Философия .NEТ

К аждые несколько лет программист должен быть готов кардинально обнов­


лять свои знания, чтобы идти в HOry С новыми технологиями. Языки (С++,
Visua1 Bastc 6.0. Java), каркасы приложений (MFC, AТL, SТL) и архитектуры (СОМ,
CORВA, EJB), которые сегодня составляют Изолотой фонд" разработки програм­
много обеспечения, в будущем непременно уступят место чему-то более совершен­
ному или, по крайней мере, более новому. Несмотря на разочарование, которое вы
можете ощущать при обновлении своей базы знаний, это неизбежно. ПЛатформа
.NEТ - это сегодняшнее предложение Мtcrosoft в области разработки программно­
го обеспечения.
Целью этой главы является построение концептуального фундамента, необхо­
димого для успешного освоения всего остального материала книги. DIaвa начина­
ется с обсуждения ряда вопросов .NEТ, ОТНOCЯIЦИXСЯ к высокому уровню, - таких
как компоновочные блоки, CIL (общий промежуточный язык) и JIТ-компиляция
Uust-in-time- точно к нужному моменту). Вдобавок к вводному обзору некоторых
ключевых возможностей языка программирования С#, будет также обозначена
взаимосвязь между различными элементами каркаса .NEТ, такими как CLR (общая
языковая среда выполнения), CТS (общая система типов) и CLS (общие специфика­
ции языка). как вы вправе ожидать, эти темы будут исследоваться более подробно
в других частях книги.

Эта глава также содержит обзор возможностей, предлагаемых библиотеками


базовых классов .NEТ, для обозначения которых иногда используют аббревиатуру
BCL (Base Class Libraгies - библиотеки базовых классов) или, как альтернативу,
FCL (Framework Class Librartes - библиотеки каркасных классов). Наконец, в главе
обсуждается независимая от языков и платформ сущность платформы .NEТ (это
действительно так- .NEТ не замыкается на операционной системе Windоws).

Предыдущее состояние дел


Перед рассмотрением специфики .NET будет полезно рассмотреть некото­
рые проблемы, стимулировавшие появление предлагаемой сегодня платформы
Мiсrоsоft. Чтобы получить соответствующее представление, давайте начнем зту
главу с краткого урока истории, чтобы напомнить об истоках и понять ограниче­
ния, существовавшие в прошлом (в конце концов, признание существования про­
блемы является первым шагом на пути ее решения). После этого мы обратим наше
внимание на многочисленные преимущества, которые обеспечиваются языком С#
и платформой .NEТ.
44 Часть 1. Общие сведения о языке С# и платформе .NET

Подход СjWiпЗ2 API


Традиционно разработка программного обеспечения для операционных систем
семейства Windows предполагает использование Языка программирования С в со­
четании с Windows АРI (Application Programming Interface - интерфейс програм­
мирования приложениЙ). Несмотря на тот факт. что в рамках зтого проверенного
временем подхода было создано очень много вполне успешных приложений, мало
кто станет оспаривать то, что процесс создания приложений непосредственно с
помощью АРI оказывается очень трудоемким делом.
Первая очевидная проблема заключается в том, что С является очень лаконич­
ным языком. Разработчики программ на языке С вынуждены "вручную" управлять
памятью, использовать безобразную арифметику указателей и ужасные синтакси­
ческие конструкции. К тому же, поскольку С является структурным языком про­
граммирования, ему не хватает преимуществ, обеспечиваемых объектно-ориенти­
рованным подходом (здесь можно вспомнить о "макаронных" программах). Когда
вы объединяете тысячи глобальных функций и типов данных, определенных в
рамках Wln32 API, с языком, который и без того выглядит устрашающе, не следУет
удивляться тому, что среди используемых сегодня программ оказывается так мно­

го ненадежных.

Подход C++/MFC
Огромным шагом вперед по сравнению с подходом, предполагающим использо­
вание C/API, явился переход к применению языка программирования С++. Во мно­
гих отношениях язык С++ можно рассматривать, как объектно-ориентированную
надстройку над С. Поэтому, хотя при использовании С++ уже можно использовать
преимущества известных "краеугольных камней ООП" (инкапсуляция, наследова­
ние и полиморфизм), зтот подход оставляет программиста во власти многих болез­
ненных аспектов языка С (управление памятью "вручную", безобразная арифмети­
ка указателей и ужасные синтаксические конструкции) .
Несмотря на сложность, сегодня существует множество каркасов программи­
рования на С++. Например, MFC (Microsoft Foundation Classes - библиотека базо­
вых классов Wcгosoft) снабжает разработчика набором С++-классов, упрощающих
создание Wiп32-приложениЙ . DIавной задачей MFC является представление "раз­
умного подмножества" Wln32 АР! в виде набора классов, "магических" макросов и
средств автоматического генерирования программного кода (обычно называемых
мастерами). Несмотря на очевидную пользу указанного каркаса приложений (как
и многих других средств разработчика, использующих С++), программирование на
С++ остается трудной задачей, и на этом пуги нелегко полностью избежать ошибок
ввИдУ ·тяжелоЙ наследственности", обусловленной связью с языком С.

Подход Visual Basic 6.0


Благодаря искреннему желанию насладиться более простой жизнью, многие
программисты ушли от "мира каркасов" приложений на базе С(++) к более друже­
ственным языкам, таким, как, например, VisuaI Basic 6 .0 (VВ6). Язык VВ6 стал по­
пулярным благодаря тому, что он дает возможность строить сложные ннтерфейсы
пользователя, библиотеки программного кода (например, СОМ-серверы) и системы
f

Глава 1. Философия .NET 45


ДOC'I)'I1а к данным, затрачивая минимум усилий. В сравнении с MFC, VВ6 еще глуб­
же скрывает от глаз разработчика сложность Win32 ЛPI, используя для этого целый
ряд интегрированных мастеров, внутренних типов данных, классов и специфиче­
ских для VВ функций.
IЛавным недостатком VВ6 (который был устранен в Visua1 Basic .NEТ) является
то, что VВ6 является, скорее, Мобъектно-осведомленным" языком, а не полноцен­
ным объектно-ориентированным. Например, в VВ6 программисту не позволяется
связывать типы отношениями Мподчиненности" (т.е. нет классического наследова­
ния) и нет внутренней поддержки конструкции параметризованных классов. Кроме
того, VВ6 не дает возможности строить многопоточные приложения, если только
вы не готовы Мспуститься" до низкоуровневых вызовов Win32 API (что в лучшем
случае достаточно сложно, а в худшем - опасно).

Подход Java/J2EE
Было предложено использовать Java. Язык программирования Java является
(почти) полностью объектно-ориентированным и имеет синтаксические корни в
С++. Многие знают, что поддержка межплатформенной независимости - далеко
не единственное преимущество Java. Java (как язык) избавлен от многих синтак­
сических несообразностей С++. Java (как платформа) предлагает программисту
большое число встроенных Мпакетов", содержащих различные определения типов.
С помощью этих типов, используя "только Java", можно строить приложения, пред­
лагающие сложный интерфейс пользователя, обеспечивающие связь с базами дан­
ных, обмен сообщениями или работу клиента в Web.
Хотя Java - очень злегантный язык, его потенциальной проблемой является то,
что использование Java в цикле разработки обычно означает необходимость ис­
пользования Java и для взаимодействия клиента с сервером. как следствие, Java
не позволяет возлагать большой надежды на возможности языковой интеграции,
так как это идет вразрез с декларируемой целью Java (единый язык программи­
рования для всех задач). Реальность, однако, такова, что в мире существуют мил­
лионы строк программного кода, которым бы идеально подошло взаимодействие
с более новым программным кодом. К сожалению, Java делает эту задачу пробле­
матичной.
Java в чистом виде просто не подходит для многих приложений, интенсивно
использующих графику или сложные вычисления (в этих случаях скорость работы
Java оставляет желать лучшего) . Для таких программ в соответствующем месте вы­
годнее использовать язык более низкого уровня (например, С++). Увы, пока Java не
обеспечивает более широкие возможности доступа к "чужеродным" API, истинная
интеграция различных языков оказывается практически невозможноЙ.

Подход СОМ
Модель СОМ (Component Object Model- модель компонентных объектов) была
предыдущим каркасом разработки приложений Мiсrosoft. По сути, СОМ - это ар­
хитектура, Мзаявившая" следующее: если lCЛQ.Cс будет noстроен в соответствии
с nравuлами СОМ, то получится блок двоичного када .мноzoкратноzo исnoльзо­
ванuя.
46 Часть 1. Общие сведения о языке С# и платформе .NET

Прелесть двоичного СОМ-сервера в том. что способ доступа к нему не зависит


от языка . Поэтому программисты. использующие С++. могут строить СОМ-классы.
npигодные для использования в VБ6. Программисты. применяющие Delpbl. могут
использовать СОМ-классы. построенные С помощью С. и т.д . Однако. и вы. возмож­
но. об этом знаете. независимость СОМ от языка несколько ограничена. Например.
нет возможности получить новый СОМ-класс из уже существующего (поскольку
СОМ не предлагает поддержки классического наследования). Вместо этого для ис­
пользования типов СОМ-класса вам придется указать несколько неуклюжее отно­
шение "обладания".
Еще одним npeимуществом СОМ является npозрачность дислокации. используя
такие конструкции. как идентификаторы приложения (АррID). "заглушки" и "заме­
стители" в среде выполнения СОМ. программист может избежать необходимости
непосредственного обращения к сокетам. RPС-вызовам и другими низкоуровневым
злементам . Рассмотрим. например. следующий программный код VБ6 СОМ-кли­
ента.

, Этот бnох прогрuacного хода VВб ..о.ет аХ'1'Иllизировать СОМ-xnасс,


, созд&JUUIЙ на JUDбо.. .зыхе, поддер.ива.пце.. СОМ, и раз..ещеННЫЙ
, в JUDбой точхе сети (ВJCJIJ:)ча. вашу noxa..nьHYJO ..ашину) .
Dim с as MyCOMClass
Set с = New MyCOMClass , Размещение выя с няет с я с п о м о щью AppID.
c.DoSomeWork

Хотя СОМ можно считать очень успешной объектной моделью. внутренне она
чрезвычайно сложна (по крайней мере. пока вы не потратите несколько месяцев на
изучение ее внутренних механизмов - особенно если вы программируете на С++) .
С целью упрощения npоцесса разработки бинарных СОМ-объектов было создано
множество каркасов разработки приложений с поддержкой СОМ. Среди них. на­
пример. библиотека ATL (Active Тemplate Library - библиотека активных шабло­
нов). которая обеспечивает еще одно множество С++-классов. шаблонов и макро­
сов. упрощающих создание СОМ-типов.
Многие другие языки также в значительной степени скрывают инфрастрyк'Iy­
ру СОМ оТ глаз программиста. Однако поддержки самого языка оказывается не­
достаточно для того. чтобы скрыть всю сложность СОМ. Даже при использовании
относительно простого совместимого с СОМ языка (например. VБ6). вы все равно
вынуждены бороться с "хрупкими" параметрами регистрации и многочисленными
npоблемами. связанНыми с инсталляцией приложений (в совокупности называе­
мыми "кошмаром DLL") .

ПОДХОД Windows DNA


Ко всем указанным выше сложностям еще добавляется такая мелочь. как
Интернет. за последние несколько лет Мiсrоsoft добавила в свое семейство операци­
онных систем и других продуктов множество связанных с Интернет возможностей.
К сожалению. создание Web-npиложений в рамках совместимой с СОМ архитекту­
ры Windows DNA (Distгibuted interNet Applications Arcbltectuгe - архитектура рас­
пределенных сетевых приложений) также оказывается очень непростым делом.
Не которая доля этой сложности вытекает из того простого факта . что Windows
DNA требует использования множества технологий и языков (ASP. НТМL. XМL.
JavaScrlpt. VБSсгlрt. а также СОМ(+) и АР! доступа к данным. например АОО).
1'"

Глава 1. Философия .NET 47


Одной из проблем является то, что с синтаксической точки зрения многие из этих
технологИЙ совершенно не связаны одна с другой. Например, в JavaScript исполь­
зуется синтаксис, во многом подобный С, а VВScript является подмножеством VВ6.
СОМ-серверы, созданные для работы в среде выполнения СОМ+, по виду сильно
отличаются от АSР-страниц, которые их вызывают. Результат- чрезвычайно за­
путанная смесь технологий.
К тому же, и зто, возможно, самое важное, каждый язык и каждая теХНОЛОгия
имеют свои собственные системы типов (которые могут быть совершенно не похо­
жими одна на другую). Например, нельзя сказать, что ~int" в JavaScript и Mlnteger"
в VВ6 означают в точности одно и то же.

Решение .НЕТ
Слишком много для короткого урока истории. Основным выводом является то,
что жизнь программиста Windows была трудна. Каркас .NEТ Framework является
достаточно радикальной ~силовой" попыткой сделать нашу жизнь легче. Решение,
предложенное .NEТ, предполагает ~изменить все" (извините, вы не можете обви­
нять посыльного за такое известие). Вы поймете из дальнейшего материала книги,
что .NEТ Framework- это совершенно новая модель для создания систем как в
семействе операционных систем Windows, так и множестве операционных систем,
отличных от систем Microsoft, таких как Мас OS Х и различные варианты Un1x/
Linux. Чтобы зто продемонстрировать, вот вам краткий список некоторых базовых
возможностей, обеспечиваемых .NEТ.

• Полноценная возможнocmь взашиодейcmвия с существующим npoгpaммным


кодом. Это (конечно) хорошо. Существующие бинарные СОМ-объекты мо­
гут комбинироваться (т.е. взаимодействовать) с более новыми бинарными
.NЕТ-объектами и наоборот. Кроме того, сервисы PInvoke (Platfoгm Invocation
Serv1ces - сервисы вызова платформ) позволяют вызывать библиотеки на ба­
зе С (ВlUlЮчая АР! операционной системы) из программного кода .NEт.

• Полн.ая и moтальная интеграция язъucов. В отличие от СОМ, платформа .NEТ


поддерживает межъязыковое наследование, межъязыковую обработку ис­
ключений и межъязыковую отладку.

• ОбщиЙ.механизм ВЫnOJlНeния npoгpaмм для всех язъucов с noддeржкой .NEТ.


Одной из особенностей этого механизма является четкий набор типов, ~по­
нятных" каждому языку.

• Библиотека базовых классов. Эта библиотека позволяет избежать сложно­


стей прямого обращения к АР! и предлагает согласованную объектную мо­
дель, используемую всеми языками с поддержкой .NEТ.

• Omcyтствие детализации СОМ. В собственном бинарном .NЕТ-объекте не


будет места для IClassFactory, IUnknown, IDispatch, IDL-кода и ~злобных"
типов данных наподобие VARIANT (BSTR, SAFEARRAY и т.д.).
• Упрощенная модель uн.cmaлляциu. Согласно спецификациям .NEТ, нет необ­
ходимости регистрировать соответствующую бинарную единицу в реестре
системы. К тому же .NEТ вполне допускает существование множества версий
одного *.dll на одной машине.
48 Часть 1. Общие сведения о языке С# и платформе .NET

На основе информации этого списка вы. вероятно. уже сами пришли к заклю­
чению. что платформа .NEТ не имеет ничего общего с СОМ (за исключением того.
что оба зти каркаса разработки приложений исходят из MIcrosoft). Фактически
единственным способом взаимодействия типов .NEТ и СОМ оказывается исполь­
зование возможностей слоя взаимодействия .

ЗIМIЧIНМI. Описание возможностей слоя взаимодействия .NET (включая Plnvoke) выходит за рам­
ки зтой книги. Если вам потребуется подрОбное освещение этого вопроса, обратитесь к моей
книге СОМ and .NEТ /nteroperability (ApreSB, 2002).

Главные компоненты платформы


.NEТ (CLR, CTS и CLS)
Теперь. когда вы знаете о некоторых преимуществах. обеспечиваемых .NEТ.
давайте рассмотрим три ключевых (и взаимосвязанных) компонента. которые и
обеспечивают эти преимущества: CLR. cгs и CLS. С точки зрения программиста,
.NEТ может интерпретироваться как новая среда выполнения программ и всеобъем­
лющая библиотека базовых классов. Слой среды выполнения здесь называется
C1R (СоmmопLanguage Runtime - общеязыковая среда выполнения). DIавной за­
дачей CLR являются размещение. загрузка и управление .NЕТ-типами по вашему
указанию. Кроме того. CLR отвечает за ряд низкоуровневых вопросов. таких. как.
например. управление памятью и проверка безопасности.
Другим строительным блоком платформы .NEТ является CТS (Соmmоп 1Уре
System - общая система типов). Спецификации CТS полностью описывают все
возможные типы данных и программные конструкции. поддерживаемые средой
выполнения. указывают. как зти злементы могут взаимодействовать друг с другом
и как они представляются в формате метаданных . NEТ (более подробная информа­
ция о метаданных будет представлена немного позже).
Вы должны понимать. что конкретный язык. совместимый с .NEТ. может и не
поддерживать абсолютно все возможности. определенные CГS. В связи с зтим ис­
пользуются связанные спецификации CLS (Соmmоп Langиage SpecIftcation - обще­
языковые спецификации). которые определяют подмножество общих типов и про­
граммных конструкций. понятных всем языкам программирования. совместимым
с .NEТ. Позтому. если создаваемые вами .NЕТ-типы опираются только на возмож­
ности. соответствующие CLS. вы можете пребывать в уверенности. что исполь­
зовать их сможет любой совместимый с .NEТ язык. А если вы используете типы
данных или программные конструкции. выходящие за пределы CLS. вы не можете
гарантировать. что с вашей библиотекой программного .NЕТ-кода сможет взаимо­
действовать любой язык программирования .NEт.

Роль библиотек базовых классов


в дополнение к спецификациям CLR и CГS/CLS. платформа .NEТ предлагает
библиотеку базовых классов. доступную всем языкам программирования .NEт. Эта
библиотека базовых классов не только инкапсулирует различные примитивы. та­
кие как потоки. файловый ввод-вывод. визуализация графики и взаимодействие с
r

Глава 1. философИЯ .NET 49


различными внешними устройствами, но и обеспечивает поддержку целого ряда
сервисов, необходимых для большинства современных приложениЙ.
Например, библиотеки базовых классов определяют типы, упрощающие доступ
к базам данных, работу с XМL, поддержку программной безопасности и создание
Web-приложений (а также обычных настольных и консольных приложений) кли­
ента. Схема высокоуровневых взаимосвязей между CLR, crs, CLS и библиотекой
базовых классов показана на рис. 1.1.

Би6лиотеК8 6830BIoII КЛ8ССОВ

06щеязыковая CPeДI выполнения

Рис. 1.1. CLR, CTS, CLS и библиотека базовых классов

Роль языка С#
С учетом того, что приНI~ПЫ . NEТ так радикально отличаются от предшествую­
щих технологий, Мiсrosоft разработала НОвый язык программирования, С# (произ­
носится "си-диез"), специально для использования с зтой новой платформой. Язык
С# является языком программирования, по синтаксису очень похожим на Java (но
не идентичным ему). Однако называть С# "переработанным" вариантом Java будет
неверно. С#, как и Java. основан на синтаксических конструкциях С++. Так же, как
и Java, С# можно называть "рафинированной" версией С++ - в конце концов, зто
языки одного семейства.
Многие синтаксические конструкции С# построены с учетом решений, приня­
тых в Visual Basic 6.0 и С++. Например, как и в VВ6, в С# поддерживаются фор­
мальные свойства типов (в противоположность традиционным методам get и set) и
возможность объявления методов с переменным числом аргументов (через масси­
вы параметров). Подобно С++, в С# позволяется перегрузка операций, а также соз­
данне структур, перечней и функций обратноro вызова (посредством делегатов).
Благодаря тому, что С# является гибридом множества языков, он является про­
дуктом, который синтаксически так же "чист", как Java (если не "чище"), почти так
же просТо как VВ6, и обладает почти такой же мощью и гибкостью, как С++ (без со­
ответствующих "ужасных" конструкций) . По сути, язык С# предлагает следующие
возможности (многие из которых присущи и всем другим языкам программирова­
ния, обеспечивающим поддержку .NEТ).
50 Часть 1. Общие сведения о языке С# и платформе .NET

• Не требуется никаких указателейl Программы на С# обычно не требуют пря­


мого обращения к указателям (хотя имеется возможность получить к ним до­
ступ на более низком уровне, еCJШ вы сочтете зто абсолютно необходимым).

• Автоматическое управление памятью через сборку мусора. По этой причине


в С# не поддерживается ключевое слово delete.
• Формальные синтаксические конструкции для перечней, структур и свойств
классов.

• Аналогичная С++ перегрузка операций для пользовательских типов, но без


лишних сложностей (например, вам не требуется контролировать Uвозвраще­
ние *Шз для связывания").

• В С# 2005 имеется возможность строить общие типы и общие члены с ис­


пользованием синтаксиса, очень похожего на шаблоны С++ .

• Полная поддержка техники программирования, основанной на использова­


нии интерфейсов.

• Полная поддержка технологии аспектно-ориентированного программирова­


ния (АОП) через атрибуты. Эта ветвь разработки ПОзволяет назначать харак­
теристики типам и их членам, чтобы уточнять их поведение .

Возможно, самым важным для правильного понимания языка С#, поставляемо­


го Мiсrosoft в связке с платформой .NEТ, является то, что получаемый с помощью
С# программный код может выполняться только в среде выполнения . NEТ (вы не
сможете использовать С# для построения Uклассического" СОМ-сервера или авто­
номного приложения Wln32 API). Официальный термин. который используется для
описания программного кода. предназначенного для среды выполнения .NEТ.­
управляемый nрогра.ммныЙ "ад (managed code). Бинарный объект. содержащий
такой управляемый программный код. называется "омnoновочным бло"ом (под­
робнее о компоновочных блоках мы поговорим немного позже). С другой стороны.
программный код. который не может непосредственно управляться средой выпол­
нения .NEТ. называется неуправляемым npoгpa.ммным "адом (unmanaged code).

Другие языки программирования


с поддержкой .NET
Вы должны понимать. что С# является не единственным языком. ориентиро­
ванным на платформу .NEТ. Когда платформа .NEТ была впервые представлена
общественности на Профессиональной конференции разработчиков Мiсrosоft в
2000 году. ряд производителей объявили , что они уже разрабатывают версии СООТ­
ветствующих компиляторов. СОвместимые с .NEТ. На момент создания ЗТОй книги
десятки различных языков подверглись влиянию .NEТ. В дополнение к пяти язы ­
кам. которые предлагаются в Visua1 Studto 2005 (С#. J#. Visua1 Basic .NEТ. Managed
Extensions для С++ и JScript .NE11. имеются также .NEТ-комnилятoры для Smallta1k.
COBOL и Pasca1 (это далеко не полный перечень).
Материал ЭТОй книги почти исключительно посвящен языку С#. но в табл. 1.1
приводится список других языков программирования, совместимых с . NEТ, и ука­
зано. где найти более подробную информацию о них (учтите. что соответствующие
адреса URL могут измениться) .
f

Глава 1. ФилосОфия .NET 51


Твблица 1.1. Некоторые из языков программирования, совместимых с .NET

Адрес Web-страницы языка .НЕТ Описание

http://www.oberon.ethz.ch/oberon.net "Домашняя" страница Active ОЬегоп .NEТ

http://www.usafa.af.mil/df/dfcs/bios/ "Домашняя" страница А# (порт Ada для


mcc_html/a sharp.cfm платформы .NEТ)

http://www.netcobol.com Для тех, кого интересует COBOL .NEТ


http://www.eiffel.com Для тех, кого интересует Eiffel .NEТ

http://www.dataman.ro/dforth Для тех, кого интересует Forth .NEТ

http://www.silverfrost.com/ll/ftn95/ Для тех, кого интересует Fortran .NEТ


ftn95 fortran 95 for_windows.asp
http://www.vmx-net.com Оказывается, доступен даже Smalitalk .NEТ

Следует учесть, что информация табл. 1.1 не является исчерпывающей. Списки


компиляторов для .NEТ имеются на многих Web-узлах. и один из таких списков
должен быть на странице http://www . dotnetpowered. сот/ 1anguages. aspx
(опять же, точный адрес URL может измениться). Я рекомендую посетить зту стра­
ницу, поскольку вас непременно заинтересуют хотя бы некоторые из языков .NEТ
(может. кому-то понадобится LISP .NE11.

Жизнь в многоязычном окружении


Б начале процесс а осмысления разработчиком языково-агностической при­
роды платформы .NEТ, у него возникает множество вопросов и прежде всего. сле­
дующий: "Если все языки .NEТ при компиляции преобразуются в "управляемый
программный код", то почему существует не один, а множество компиляторов?".
Ответить на этот вопрос можно по-разному. Бо-первых, мы, программисты, бы­
ваем очень привередливы, когда дело касается выбора языка программирования
(я здесь тоже не исключение). Некоторые из нас предпочитают языки с многочис­
ленными точками с запятыми и фигурными скобками. но с минимальным набором
ключевых слов. Другим нравятся языки, предлагающие более ·человеческие" син­
таксические лексемы (как VisuaI Баsiс .NE11. А кто-то не пожелает отказываться от
своего опыта работы на большой ЭБМ и захочет перенести его на платформу .NEТ
(используя СОБОL .NE11.
А теперь скажите честно: если бы Мiсrosоft предложила единственный ·офици­
альный" язык .NEТ, например, на базе семейства БASIС, то все ли программисты
были бы рады такому выбору? Или если бы "официальный" язык .NEТ был осно­
ван на синтаксисе Fortran, то сколько людей в мире вообще проигнорировало бы
платформу .NEТ? поскольку среда выполнения .NEТ демонстрирует меньшую за­
висимость от языка, используемого для построения управляемого программного

кода, программисты .NEТ могут. не меняя своих синтаксических предпочтений,


обмениваться скомпилированными компоновочными блоками со своими коллега­
ми, другими отделами и внешними организациями (не обращая внимания на то,
какой язык .NEТ используется там).
52 Часть 1. Общие сведения о языке С# и платформе .NET

Еще одно полезное преимущество интеграции различных языков .NEТ в одном


унифицированном программном решении вытекает из того простого факта. что
каждый язык программирования имеет свои сильные (а также слабые) стороны .
Например. некоторые языки программирования имеют превосходную встроеJШYlO
поддержку сложных математических вычислений. В других лучше реализованы
финансовые или логические вычисления. взаимодействие с центральными ком­
пьютерами и Т.д. Когда преимущества конкретного языка программирования объе­
диняются с преимуществами платформы .NEт. выигрывают все.
Конечно . вы можете разрабатывать программное обеспечение. не выходя за
рамки своего любимого языка .NEт. Но, изучив синтаксис одного языка .NEТ, вам
будет очень легко освоить любой другой. Это тоже очень выгодно, особенно кон­
сультантам. Если вашей специализацией является С#, но вы оказались на узле
клиента, который "привязан" к Visual Basic .NEТ, то сможете почти сразу разобрать
соответствующий программный код (поверьте!), воспользовавшись указанным пре­
ИмyIЦеством .NEт. На STOM и остановимся.

Компоновочные блоки. NET


Независимо от того, какой язык . NEТ вы выберете для программирования, вы
должны понимать, что хотя бинарные .NЕТ-единицы имеют такие же расширения
файлов, как СОМ-серверы инеуправляемые программы Win32 (*.dll или *.ехе),
их внутреннее устройство совершенно иное. Например, бинарные .NЕТ-единицы
* . d 11 не зкспортируют методы для упрощения коммуникации со средой выпол­
нения СОМ (поскольку .NEТ- зто не СОМ). Бинарные .NЕТ-единицы не описыва­
ются с помощью библиотек СОМ-типов и не регистрируются в реестре системы .
Наверное, самым важным является то, что бинарные .NЕТ-единицы содержат не
специфические для платформы инструкции, а независимые от платформы IL-ин­
струкции (Iintennediate Language - промежуточный язык) И метаданные типов. На
рис . 1.2 это показано схематически .

Исходный код
Компилятор с#
С#
.~

Исходный КОД
Компилятор Perl .NEТ
Perl.NEТ IL-инструкции
• и

метаданные

Исходный КОД (*:dll или *.ехе)


Компилятор COВOL .NET
COВOL .NET
• •

Исходный код
Компилятор Managed С++
Managed С++

Рис. 1.2. Все . NEТ -компиляторы генерируют IL- инструкции и метаданные
r

Глава 1. Философия .NET 53

Замечание. Относительно сокращения "IL" здесь уместно сказать несколько дополнительных слов.
В ходе разработки .NET Официальным названием для IL было Мiсrоsоft
lintermediate Language
(MSIL). Однако в вышедшей версии .NET зто название было изменено на CIL (Соттоп
Intermediate Language - общий промежуточный язык). Позтому вам следует знать, что в публи­
кациях, посвященных .NEТ, сокращения IL, MSIL и CIL обозначают одно и то же. В соответствии
с терминологией, принятой сегодня, в тексте зтой книги используется сокращение CIL.

После создания * .dll или * .ехе с помощью подходящего .NЕТ-компилятора, со­


ответствующий модУЛЬ упаковывается в JCOМ1Wновочный блоlC. Подробное описание
компоновочных блоков .NEТ име~тся в главе 11. Однако, чтобы продолжить наше
обсуждение среды выполнения .NEТ, вы должны знать основные особенности фор­
мата этих новых файлов.
Как уже было сказано, компоновочный блок содержит программный код CIL,
который концептуально напоминает байт-код Java в том смысле, что он не КОМПИ­
лируется в специфические для соответствующей платформы инструкции, пока это
не станет абсолютно необходимо. Обычно "абсолютная необходимость" означает
момент, когда на какой-то блок СIL-инструкций (например, реализацию метода)
выполняется ссылка для его использования в среде выполнения .NEт.
В добавление к СIL-инструкциям, компоновочные блоки также содержат мета­
данные, которые подробно описывают особенности каждого "типа" внутри данной
бинарной .NЕТ-единицы. Например, если вы имеете класс с именем SportsCar,
соответствующие метаданные типа будУТ описывать такие злементы, как базо­
вый класс SportsCar и интерфейсы, реализуемые SportsCar (если таковые име­
ются), а также содержать полные описания всех членов, поддерживаемых типом
SportsCar.
Метаданные .NEТ более совершенны по сравнению с метаданными СОМ. Вы,
возможно, уже знаете, что бинарные СОМ-объекты обычно описываются с помо­
щью библиотеки ассоциированных типов, а это почти то же самое, что и бинарная
версия IDL-кода (Interface Definition Language - язык определения интерфейса).
Проблема использования СОМ-информации в том, что зта информация не обяза­
тельна, и IDL- код не может документировать внешние серверы, которые нужны
для правильного функционирования данного СОМ-сервера. В противоположность
этому метаданные .NEТ присутствуют обязательно и автоматически генерируются
соответствующим .NЕТ-компилятором.
Наконец, в добавление к CIL и метаданным типов, сами компоновочные блоки
также описываются с помощью метаданных, для которых используют специаль­

ное называние манифест (manifest). Манифест содержит информацию о текущей


версии компоновочного блока, информацию о "культуре" (используемую для лока­
лизации строк и графических ресурсов) и список всех ссылок на внешние компо­
новочные блоки, которые требуются для правилъного функционирования. Из сле­
дующих глав вы узнаете о различных инструментах, которые MOryт использоваться

для исследования типов компоновочного блока, расшифровки его метаданных и


манифеста.
54 Часть 1. Общие сведения о языке С# и платформе .NET

Одномодульные и многомодульные
компоновочные блоки
Во многих случаях компоновочные блоки .NEТ- это просто файлы двоичного
кода (*.dll или *.ехе). Поэтому. если вы строите *.dll .NEт, можно считать. что
файл двоичного кода и компоновочный блок - это одно и то же. Точно также. если
вы строите выполняемое приложение для настольной системы. файл *.ехе тоже
можно считать компоновочным блоком. Но из главы 11 вы узнаете. что указанное
соответствие не столь однозначно. Строго говоря. если компоновочный блок со­
стоит из одного модуля *.dll или *.ехе. вы имеете одно.модульныЙ комтwновочный
блок. Одномодульный компоновочный блок содержит весь необходимый код CIL.
метаданные и манифест в одном автономном отдельном пакете.
Много.модульные JCOмтwновочн.ые блоlCU, в свою очередь. складываются из мно­
жества бинарных .NЕТ-единиц. каждая из которых называется .модулем. При зтом
один из таких модулей (он называется первuчнbIМ модулем) должен содержать
манифест компоновочного блока (и может содержать также СIL-инструкции и ме­
таданные различных типов). Остальные связанные модули содержат манифест
уровня модуля. CIL и метаданные типов. как вы можете догадаться. в манифесте
первичного модуля компоновочного блока документируется набор необходимых
"второстепенныхМ модулей.
Но зачем создавать многомодульные компоновочные блоки? Когда вы делите
компоновочный блок на отдельные модули. вы получаете более гибкие возможно­
сти инсталляции. Например. если пользователь ссылается на удаленный компоно­
вочный блок. то среда выполнения загрузит на его машину только необходимые
модули. Поэтому вы можете сконструировать свой компоновочный блок так. чтобы
редко используемые типы (например. HardDriveReforrnatter) были выделены в от­
дельный автономный модуль.
Если все ваши типы размещаются в компоновочном блоке. представляющем
собой единый файл. конечному пользователю придется загружать большой набор
данных. из которых в действительности могут понадобиться далеко не все (а зто.
очевидно. означает лишнюю трату времени). Итак. компоновочный блок на самом
деле логически сгруnтшpoван. в один или несколько модулей . которые должны ин­
сталлироваться и использоваться. как единое целое.

Роль CIL
Теперь. когда вы имеете начальное представление о компоновочных блоках
.NET. давайте немного подробнее обсудим роль общего промежуточного языка
(CIL). CIL- это язык. находящийся выше любого набора инструкций. специфиче­
ского для конкретной платформы. Независимо от того. какой язык .NEТ вы вы­
берете для использования. соответствующий компилятор сгенерирует инструкции
CIL. Например. следующий npогрaмМНblЙ код С# моделирует тривиальный кальку­
лятор. Не пытаясь пока что полностью понять синтаксис этого примера. обратите
внимание на формат метода Add() в классе Calc.
r
,
I

Глава 1. Философия .NET 55


/ / Саlс.СВ
using System;

паmезрасе CalculatorExample
(
/ / Этот JCJIacc содержит точку входа DpИJIо.еНИJl.
public class CalcApp
(
static void Main()
(
Calc с = new Calc();
int апз = c.Add(10, 84);
Console.WriteLine("10 + 84 is {О}.", апз);

/ / Ждать, по ха ПOJJъsоааТ8J18 не на.мет JCJIааиury ввода.


Console.ReadLine();
// C'-хanъКУЛRТОР·
public class Calc
(
public int Add(int х, int у)
( return х + у; )

После того как компилятор С# (сзс.ехе) скомпилирует этот файл исходного


кода. вы получите состоящий из одного файла компоновочный блок *.ехе. кото­
рый содержит манифест. СIL-инструкции и метаданные . описывающие каждый
аспект классов Calc и CalcApp. Например. если вы откроете этот компоновочный
блок с помощью ildasm.exe (мы рассмотрим ildasm.exe немного позже в этой же
главе). вы увидите. что метод Add () в терминах CIL представляется так .

. method public hidebysig instance int32 Add(int32 х, intЭ2 у) cil managed


(
11 Code size 8 (Ох8)
.maxstack 2
.locals init ([О] int32 CS$l$OOOO)
IL_OOOO: ldarg.1
IL 0001: 1darg.2
IL 0002: add
IL 0003: stloc.O
IL 0004: br.s IL 0006
IL 0006: ldloc.O
IL 0007: ret
11 end of method Calc: :Add
Не беспокойтесь. если вы пока не в состоянии понять СIL-код для этого мето­
да- в главе 15 будут описаны основы языка программирования CIL. Здесь следует
сконцентрироваться на том. что компилятор С# генерирует СIL-код. а не специфи­
ческие для платформы инструкции.
Напомним теперь. что это верно для всех .NEТ-компиляторов. для иллюстрации
предположим. что вы создали аналогичное приложение с помощью Visual Basic
.NEТ (vв .NEТ). а не с помощью С#.
56 Часть 1. Общие сведения о языке С# и плаТформе .NET

, Calc.vb
1mports System
Namespace CalculatorExample
, VВ • пт 'Модуn.' - ато JCЗIасс, содер.ащий тоn.ко
, статические ЧJlенw.
Module CalcApp
Sub Main ()
Dim ans Аэ 1nteger
Dim с Аэ New Calc
ans = c.Add(10, 84)
Console.WriteLine("10 + 84 is (О}.", ans)
Console.ReadLine()
End Sub
End Module
Class Calc
Public Function Add(ByVal х Аэ 1nteger, ByVal у Аэ 1nteger) Аэ 1nteger
Return х + у
End Function
End Class
End Namespace
Если теперь проверить СIL-код для метода Add ( ) , вы обнаружите подобные ин­
струкции (слегка Мподправленные" компилятором vв .NEТ) .

. method public instance int32 Add(int3 2 х, int32 у) cil managed


{
/ / Code size 9 (Ох9)
.maxstack 2
.locals init ([О] int32 Add)
1L 0000: пор
1L 0001: ldarg.1
IL 0002: ldarg. 2
1L 0003: add. ovf
1L 0004: stloc.O
1L 0005: br.s 1L 0007
IL 0007: ldloc. О
1L 0008: ret
} / / end of method Calc: :Add

Преимущества CIL
Вы можете спросить, зачем компилировать исходный код в CIL, а не прямо в
набор специальных системных команд. Одним из преимуществ этого является ин­
теграция языков, поскольку вы уже убедились, что все компиляторы .NEТ выда­
ют приблизительно одинаковые наборы CIL-инстрУКЦИЙ . Поэтому все языки MOryт
взаимодействовать в рамках четко обозначенной двоичной Марены".
Кроме того, поскольку CIL демонстрирует независимость от платформы, каркас
.NEТ Framework тоже оказывается независимым от платформы, обеспечивал то, к
чему так привыкли разработчики J ava (единую базу программного кода. способно­
го работать во многих операционных системах). Фактически уже имеется между-
r
Глава 1. Философия .NET 57
народный стандарт для языка С#, а значительная часть платформы .NEТ реализо­
вана для множества операционных систем, отличных от Windows (более подробная
информация об этом имеется в конце главы). Но, в отличие от Java, .NEТ позволяет
строить приложения, используя язык вашего предпочтения.

Преобразование CIL-КОАа в набор инструкций,


соответствующих платформе
ВвидУ того, что компоновочные блоки содержат СIL-инструкции, а не инструк­
ции для конкретной платформы, программный код CIL перед использованием
приходится в фоновом режиме компилировать. Объект, который компилирует
программный код CIL в инструкции, понятные процессору машины, называется
JfГ-ICОМ11ШlЯmoром Uust-in-time - точно к нужному моменту), который иногда М по _
дружески" также называют Jitter. Среда выполнения .NEТ использует JIТ-компи­
лятор, соответствующий конкретному процессору и оптимизированный ДЛЯ соот­
ветствующей платформы.
Например, если ваше .NЕТ-приложение предназначено для выполнения на
MKoмnaктHoM" устройстве (таком, как, например, КПК), то соответствующий JIT-
компилятор будет иметь специальные средства для учета условий ограниченности
памяти. Если это компоновочный блок для сервер ной системы (где объем памяти
редко оказывается проблемой), то соответствующий JIТ-комnилятор будет оптими­
зирован ДЛЯ работы в условиях достаточного объема памяти. Таким образом раз­
работчики получают возможность создавать только один блок npограммного кода,
который с помощью JIТ-комnиляции можно выполнять на машинах с разной ар­
хитектурой.
К тому же, при компиляции СIL-инструкций В соответствующий машинный код
JIТ-комnилятор поместит результаты компиляции в кзш В соответствии с тем, как
зтого требует соответствующая операционная система. Так, при первом вызове
метода с именем PrintDocument() соответствующие СIL-инструкции комnилиру­
ются В конкретные инструкции платформы и сохраняются в памяти для использо­
вания в дальнейшем. Позтому при следующих вызовах PrintDocument () необхо­
димости в повторной компиляции CIL не возникает.

Роль метаданных типов .NET


Кроме СIL-инструкций , компоновочный блок . NEТ содержит исчерпывающие
и точные метаданные, описывающие все его типы (классы, структуры, перечни и
т.д.), определенные в бинарном объекте, и все члены каждого типа (свойства, ме­
ТОДЫ, события и т.д.). К счастью, задача создания метаданных всегда возлагается
на компилятор (а не на программиста). По причине того, что метаданные .NEТ так
подробны и точны, компоновочные блоки оказываются единицами, способными
себя полностью описать, - настолько полно, что для бинарных .NЕТ-объектов не
возникает необходимости регистрироваться в реестре системы.
Для иллюстрации формата метаданных типов .NET давайте рассмотрим ме­
таданные, сгенерированные для метода Add () С#-класса Calc, представленного
выше (метаданные, генерируемые для VВ .NЕТ-версии метода Add () , оказываются
аналогичными).
58 Часть 1. Общие сведения о языке С# и ппатформе .NET

TypeDef #2 (02000003)

TypDefName: CalculatorExample.Calc (02000003)


Flags : [Public] [AutoLayout] [Class]
[Ansi Class] [BeforeFieldInit] (00100001)
Extends : 01000001 [TypeRef] System.Object
Method #1 (06000003)

MethodName: Add (06000003)


Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : ОхООО02090
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: 14
2 Arguшents
Argument #1: 14
Argument #2: 14
2 Раrашеtеrs
(1) ParamToken : (08000001) Name : х flags: [попе] (00000000)
(2) ParamToken : (08000002) Name : у f1ags: [попе] (00000000)

Метаданные используются средой выполнения .NEТ, а также различными сред­


ствами разработки. Например, возможность IntelliSense, предлагаемая в Visual
Studio 2005 в режиме проектирования, основана на чтении метаданных компоно­
вочного блока. Метаданные используются различными утилитами просмотра объ­
ектов, инструментами отладки и самим компилятором С#. Для полноты картины
заметим также, что использование метаданных лежит в основе множества .NEТ­
технологий, включая удаленный доступ, отображение типов, динамическое связы­
вание, Web-сервисы XМL и сериализацию объектов.

Роль манифеста компоновочного блока


Наконец вспомним, что компоновочный блок .NEТ содержит также метаданные,
описывающие сам компоновочный блок (зти метаданные называются манифест).
Среди всего прочего, в манифесте документируются все внешние компоновочные
блоки, которые требуются текущему компоновочному блоку для корректного функ­
ционирования, указан номер версии компоновочного блока, информация об автор­
ских правах и т.д. Подобно метаданным типов, генерирование манифеста компо­
новочного блока тоже является задачей компилятора. Вот некоторые подходящие
для иллюстрации элементы манифеста CSharpCalculator.exe .
. assembly extern mscorlib
{
.publickeytoken = (В7 7А 5С 56 19 34 ЕО 89 )
. ver 2: О : О : О

.assembly CSharpCa1culator
{
r

Глава 1. Философия .NET 59


.hash algorithm ОхООО08004
.ver 0:0:0:0

.modu1e CSharpCalcu1ator.exe
.imagebase ОхО0400000
.subsystem ОхОООООООЗ
.fi1e a1ignment 512
.corf1ags ОхОООООООl
По сути, этот манифест содержит указания на внешние компоновочные бло­
ки, необходимые для CSharpCa1culator.exe (для этого используется директива
.assembly extern), а также различные характеристики самого компоновочного
блока (номер версии, имя модуля и т.д.).

Общая система типов


Компоновочный блок может содержать moбoе число четко определенных "типов".
В мире .NEТ "тип" - зто просто общий термин, используемый для обозначения лю­
бого злемента из множества (lCЛQCс, cтpyкmypa. uн.meрфеЙС, neречен.ь, делегam}.
При построении решений с помощью любого языка .NEТ вы, скорее всего, будете
взаимодействовать с каждым из этих типов. Например, компоновочный блок мо­
жет определять один класс, в котором реализовано ряд интерфейсов. И, возможно,
ОДИН из методов интерфейса будет принимать перечень в качестве входного пара­
метра, а возвращать некоторую структуру.

Напомним, что crs (общая система тшIOВ) - это формальное описание того, как
должны определяться типы, подходящие для использования в среде CLR. Обычно
внутренние механизмы CТS важны только тем, кто создает средства разработки
и/или строит компиляторы для платформы .NEТ. Но для любого программиста
.NEТ важно знать, как работать с пятью типами, определяемыми спецификациями
CТS для выбранного разработчиком языка программирования. Ниже предлагается
краткий обзор соответствующих вопросов.

Тип класса
Любой язык, совместимый с .NEТ, поддерживает, как минимум, тип к:ласса. ко­
торый является "краеугольным камнем" объектно-ориентированного программи­
рования (ООП). Класс может состоять из любого числа членов (таких, как свойства,
методы и события) и элементов данных (таких, как поля). В С# классы объявляют­
ся с помощью ключевого слова class.
// тип к.nacca Cjf.
public c1ass Calc
{
public int Add(int х, int у)
{ return х + у; }

Процесс построения типов класса crs в С# будет рассматриваться в главе 4, но


ряд общих характеристик типов класса приводится в табл . 1.2.
60 Часть 1. Общие сведения о языке С# и ппатформе . NEТ

Таблица 1.2. Характеристики классов CTS

Характеристика класса Описание

Изолированность Изолированные классы не могут быть базой для создания других


классов, Т.е. не позволяют наследование

Наличие интерфейсов Интерфейс - это набор абстрактных членов, обеспечивающих


взаимодействие между объектом и пользователем этого объекта.
Спецификации CTS не ограничивают число интерфейсов, реализу­
емых в рамках класса

Абстрактность или Абстрактные классы не позволяют неnoсредственное сознание их


конкретность экземпляров - они предназначены для определения общих эле­
ментов поведения производных типов. Конкретные классы могут
быть созданы непосредственно

Видимость Каждый класс должен иметь атрибут видимости (visibility). По сути,


этот атрибут указывает, доступен данный класс для использования
внешними компоновочными блоками или он доступен для исполь­
зования только внутри определяющего этот класс компоновочного

блока (как, например, при ватный класс справки)

Тип структуры
Понятие структуры в CТS также формализовано. Если вы знаете С, вам будет
приятно узнать, что зти пользовательские типы "выжили" и в мире .NEТ (хотя вну­
тренне они ведут себя немного по-иному). Упрощенно говоря, структура- это
"облегченный" тип класса с семантикой на базе значений. Более подробная инфор­
мация о структурах предлагается в главе 3. Обычно структуры лучше всего под­
ходят для моделирования геометрических и математических данных, и в С# для
создания структур используется ключевое слово s t r u с t.

// тип струхтуры ct.


struct Point

/ / Струхтуры ио:ry'l' содер.атlo ПОnR.


public int xPos, yPos;

/ / Струхтуры ио:ry'l' содер.атlo паракеТРИSО8aннuе ХОИСТРУХ'l'орu.


public Point(int х, int у) ( xPos = х; yPos = у;}

/ / СтрухтypiI иоЖ'У'1' определR'l'1o ие'l'ОДЫ.


public void Display()
(
Console.WriteLine("«(O}, (l}", xPos, yPos);
r
Глава 1. Философия .NET 61

Тип интерфейса
Инmeрфейс- это именованная коллекция определений абстрактных членов,
которая может поддерживаться (т.е. реализовьmаться) данным классом или струн­
турой. В отличие от модели СОМ, интерфейсы .NEТ не являются производными
одного общего базового интерфейса, такого как IUnknown. В С# типы интерфейса
определяются с помощью ключевого слова interface, например:
// тип ИИ'1'8рфейса С*.
public interface IDraw
(
void Draw () ;

Сами по себе интерфейсы не очень полезны. Однако, когда класс или струн­
тура реализуют данный интерфейс своим собственным уникальным образом, вы
можете запросить доступ к соответствующим функциональным возможностям, ис­
пользуя ссылку на интерфейс в полиморфной форме. Программирование на базе
интерфейсов будет рассматриваться в главе 7.

Тип перечня
Перечень - зто удобная программная конструкция, в которой группируют­
ся пары Нимя-значение". Предположим, вы создаете видеоигру, в которой игроку
позволяется выбрать персонажа в одной из трех категорий: Wizard (маг), Fighter
(воин) или Тhief (мошенник) . Вместо того чтобы использовать и отслеживать чис­
ловые значения, соответствующие каждой из возможностей, вы можете построить
перечень, используя для этоro ключевое слово епиm.

// тип пер8ЧИR С*.


public епиm CharacterType
{
Wizard = 100,
Fighter = 200,
Thief = 300

По умолчанию для каждого зле мента выделяется блок памяти, соответствую ­


щий 32-битовому целому, но при необходимости зто значение можно изменить
(например, в случае программирования для устройств с малыми объемами памя­
ти, таких как КПК). Спецификации crs предполагают, что типы перечня должны
·получаться" из общего базового класса, System.Enum. Из главы 3 вы узнаете, что
этот базовый класс определяет ряд весьма полезных членов, которые позволяют
программно извлекать, обрабатывать и преобразовывать соответствующие пары
·имя-значение" .
62 Часть 1. Общие сведения о языке С# и платформе .NET

Тип делегата
Делегат- это .NЕТ-эквивалент обеспечивающих типовую безопасность ука­
зателей ФУНКЦИЙ С. IЛавное отличие заключается в том, что делегат .NEТ - это
класс, получаемый путем наследования System.MulticastDelegate. а не просто
указатель на КОНIqJетный адрес в памяти. В С# делегаты объявляются с помощью
ключевого слова delegate.
/ / Э'JIо'1' '1'ИD де.пеrа'1'а С# lIO.е'1' I yxuыaa'l'J. I на .moбоЙ ме'1'ОД, воsвращаl:lЩPlЙ
/ / цепое значение и ао.пуч8.DЦИЙ на вход два Ц8.1JIIX sначеИИJI.
public delegate int BinaryOp(int х, int у};

Делегаты полезны. когда требуется обеспечить элементу возможность передачи


вызова другому злементу. что создает основу для архцтектуры обрабOТI<И событий
.NEТ. В главах 8 и 14 будет ПОJ(aзано, что делегаты имеют внутреннюю поддержку
методов многоадресного (предназначенного для множества получателей) и асин­
хронного выэова.

Члены типов
Теперь после рассмотрения всех типов, имеющих формальное определение в
CТS. вы должны осознать, что большинство типов может иметь любое число чле·
нов. Формально член тиna- зто любой элемент множества IlCонструlCmoр, де­
cm.РУlCmoр (flnal1zer), сmaтичесrcuй lCoнcmpylCmop. ВJWженный тип, операция. ,ме­
moд. cвoйcm.вo. uндelCcamop. rwле. rwле толысо для чmeнuя, lCонсmшuna, событие}.
Специфю<ации CТS определяют различные ~характеристю<и", которые могут
связываться с данным членом. Например. каждый член имеет признак. характе­
ризующий его доступность (открытый, частный. защищенный и т.д.). Некоторые
члены могут объявляться как абстрактные. чтобы навязать полиморфное поведе­
ние производным типам. или виртуальные. чтобы определить фиксированную (но
допускающую замену) реализацию. Бо,IIЬшинство членов может обозначаться как
статические (~привязанные" к уровню класса) или как члены зкземпляра (~привя­
ззнные" к уровню объекта). Конструкция членов типа будет подробно обсуждаться
в следующих главах.

Замечание. Как будет показано в главе 10. в .NEТ 2.0 подцерживается конструкция обобщенных
типов и обобщаннbIX членов.

Встроенные типы данных CTS


Еще одной особенностью CТS. о которой следует знать. является то. что специ­
фикации CТS определяют четкий набор базовых типов данных. Хотя каждый язык
обычно предлагает свое уникальное ключевое слово. используемое для объявления
конкретного встроенного типа данных CТS. все эти ключевые слова в конечном
счете приводят к соответствующему типу. определенному в компоновочном блоке
mscorlib.dl1. Взгляните на табл. 1.3. предлагающую информацию о том. как ба­
зовые типы данных CТS выражены в разных языках .NEТ.
r
Глава 1. ФИЛО~ОфИR .NET 63
Та6nмца 1.3. 'Встроенные типы дaHNblX CTS
Тип Д8ммых CТS КnIO"teIlO8 cna~ КIuo'Ieloe CnО8О Кrooчееое СnО80
VВ.NEТ С# Managed Sxtens1on. for С4-+
System.вyteByte Byte byte un.зi gned char
Вуэtеm.$ВytеSВуtе SВyte sbyte siqned e:har
System... Lntiб $hort short shlDrt
System.Int32 Integer iцt int ~nИ lOrJ,g
3"уs'tеm.Iпtб4 Ьоnч ~Clng int64
Sуstсеm.1Ппt16 OShort usЪо~t Un8igned s ·h az;t
sузtещ, Ulnt32 UInteger uiht uDsigl1ed 'i.nt "ИЛИ
uns,i gned long
System, UIпtб4 ULong ulOJlg \.lnзigпеd ~int64

Syste1!l~$ingleSingle Single float Float


Sys te1Il. Do,'lbl,eDouble Double dOl.lble Douыle

Syst.em.Qbjel::tO.bjeet Object obje,ct Object~

System.CharChar СЬат сЬа:: wdllar t


System.Stringstring S-tri n 9 S.tring 'Stririg~

,S Y5tem.DeeiIl)alDecimal Decimal decima;l DeQimal


System.Boolea.nВooJ,ea,n Boolean bool Воо1

Общеязыковые спецификации
Вы. конечно, <!Баете, что разные JIзыкiI программироlЩRИ.R вьq>3Жаю'I: ОДНИ и
те же прогрa.м:мm,tf: 1СВfICТPYlЩJm в своих уникальных терминах. НanРJIМс:р. в С#
кониаrreнaцJoШ строкобознач:ается ЗНaItOМ "плюс" (+}. а Ц' VВ .NE;Т для этого исполь­
~eтca. aЫlТepcaнд.[ ~) , даже 'J'O.I."Aa. кorдa.двa язша ВЫраж81Q"Г одну и ту _ :пpoi'JЖМ­
ЩI}'Ю' цдиому (JiЩIpимер, функЦИю, не возйрзщшо1ЦYJCl.lQП\aКОro знач~), весьма
вероятно ТО, ч';ГО при атом ИСПOJlьэуется pagт.m сиmаксис.

, Нв .озара . .-.di .....e~ ~OД vв .. _.


l?\J.blic Sub MyMethod ( )
I Ие1СO'rOpJiIЙ Dp01"раМКDIЙ ход • .•
End ЗиЬ

/I IJe воsвреща---* ~~ 11i1l1'oA ct.


publ i c voidMyMethod()
I
/ !ИВJЮlllорЩ tgi)0Ж'р8МJПIМЙJtо;ц • ••

Вы уЖе mщели. ЧТО эти небодыпве отличиа в синтаксисе весуществеНШiI с


точки зрения среды вьnroЛНeflЩl ,NEf, досколыо/ соответствующие компиляторы
(BдaннriM случае это ,,"bC.e~e и е:.3, с.ехfЗ) rенерирJ1DТ аналог.ичвые множества CJL..
инструкЦИЙ. Но язъпm моryт-таюке отЛИ'Щт~я по фyншuIOналы-IOСТК. КоIOq>етНblЙ
язык .NEГ может. И'anpим:ер. иметь Ю1Ючево~ слово или не имeтr. eto J1iПSI npeACТaв-
64 Часть 1. Общие сведения о языке С# и платформе . NEТ

ть или не подцерживать ТИПЫ указате­


ления д81fных без знака. может подцержива
необходимо иметь базовый уровень,
ля. С учетом таких вариаций возможностей
подцержкой .NEт.
которому соответствовали бы все языки с
Спецификации CLS (Соттоп Langtщgе Speci
ficatl on - общеязьшовые специфи­
подробностях описывают минималь­
кации) - это набор правил. которые во всех
рые должен подцерживать данный
ное и полное множество возможностей, кото
ный код. подх:одЯIЩIЙ для CLR. и
.NET-коМI1ИЛЯТОР. чтобы генерировать программ
для всех язьшов. предназначенных для
в то же время быть одинаково доступным
можно рассматривать, как noдMНO­
платформы .NEт. Во многих отношениях CLS
жностей. определенного в рамках
жеcmвo полного набора функциональных возмо
crs.
счете набором правил. которых долж­
CLS являются в конечном
Спецификации
ра, если они собираются создать npOД}'RT,
ны придерживаться создатели КОМIIИЛЯто
"незаметно" для пользователя . Каждое
который во "вселенной .NEТ функционирует
М

правило имеет простое имя (например, UПрав


ило CLS номер и описывает. какое 61
тем, кто fCaRим-то
тем. кто создает компиляторы, и
отношение это правило имеет к
цией CLS является могущественное
образом взаимодействует с ними. Квинтэссен
npaвШlO 1.
• Правшro 1. Правила CLS применяютел только к тем компонентам типа. кото­
новочного
елами определяющего их компо
рые открыты для доступа за пред

блока.

ть (правильное) заключение. что все


с учетом этого правила вы можете Сдела
используемой для внутреннего устрой­
остальные правила CLS не касаются логики.
которые должны согласовываться с
ства типа .NEт. Единственные аспекты типа,
параметры и воз­
CLS, - это определения членов (т.е. соглашения о выборе имен,
етного члена может использовать лю­
вращаемые типы). Логшm реализации конкр
только это будет скрыто от Uвнешнего
бые несогласованные с CLS технологии. если
мира".
согласованным справилами CLS. по­
Так. следующий метод Add () не является
значений используются данные без зна­
скольку для параметров и возвращаемых
ка, которые в CLS не указаны.

publ i c class Calc


(
11 Э'1'И o'l'кpW'1'll8 Д8.JIНWB без знака не cozoJlacyJD'l'cSI с CLS!
publi c ulonq Add( ulonq х, ulonq у)
{ rеturл х + у;}

только внутри типа, как указано


Но если вы используете данные без знака
ниже

publ i c c lass Calc


(
publ ic int Add( int х, int у)
{
KO внутри '1'ИП&,
11 Здесъ пвреllени&Sl ulonq иcno.n.вуе'l'СSl 'l'OJIЪ
11 ПОЗIrОИУ nP_ИJlа CLS не HapYlQJll'l'CSl.
ulong t e mp;
Глава 1. ФИЛОСОфИЯ ,NEТ 65
ret1;irn х + у;

то правила СLЭ остаются выполнен~. и вы может~ быть уверены. что теперь


любой язьnc.NЕТ CMO,m€'l' BЫ3Ba~ метод Ad.d () .
Коцечцо, J\'PQмe правица 1 в CtS опре;целен;о много дрyr-и.х npащ1Л. Нацример. в
CLS ОIЩQЫВЗ.e'l"СЯ. ЩIl{ ЯЗЫR доЛ"лtен npeдставлять строки текста. пере'-п-m. статяче­
СИпе' чледы и т.д. К с'qастью, со;всем не обязателыfo запоминать 13ce &т1i npавилд,
чтобы отать :искJ'сцbI)\{ разработчцом .NEТ, Снова повторm.J. что r.JIY6oKoe и полное
ш:щимаииеспецифИI<aЦИЙ CТS 'и CLS необходимо TO;JJЪRO соаца.тeщц.t соответствую~
:щих ннС'Тр~еитов разрабоТЮl и КОМIIИЛЯторов.

Гарантия СLS~совместимости
}(.ан вы уанаете из теЕста ЭТОЙ IGщrи. В с.# и;меетс:я PJIд rrpoграммнЪ1Х ксщструк­
ций. которые 'Не явJШЮтся CLS-G:о:вмеcтщ.fblМИ. ОднаIW хороlIIИ\l!I известием явШl­
е'ГСЯ '1'9. что BJ:d можете заставить компилятор С# вьmол::н.ятъ проверку вашегО' дро­
грам:много JЮда На соответствие CLS, ИСПOJrьзуя ,!J,IЩ9ТОГО QДИН атрибут.NEI'.

11 УХа.аавиiet 1I:омrlИil1Ji1'Z'ОРУ С. InШOnнw.tЪ проверху ка соО_В'Z'C'PJIИe CLS.


Lassambly; .sуjtеrn.СL5сФnрliаnt (цце} ]

В главе 12 будут расс-мотрецьу ТОНJI!()СТЦ программировщ·щ:я аа oe'ЦOB~ 0СПРдЬ­


зования атрибутов, Пока QTO важ:но просто ПОПЯТЬ, что атрибут 1СLSСQщрliantJ
дает IЮМШ'l",ЩlТОрУ С# YJЩзmще провеРЛTh :ка.ждую строцу ПРОГРШifМВQr0.кода На со­
OТ!i1етствие npавилЩ{ CLS. Если обнаружится Нарynrtщде прав~ СLЭ. вы Щ)дrIQ:те
соо(}щение об опmбне 1I10.мщmяциn и оnиcанде ~еIюрреRТНШ'О проrр8МJ'dНОГО кода.

Общеязыковая среда выполнения


в дополнениекспецифишщинм cтs и CLS. IIOCЛедней 1la данныЙ мамеш аб­
бревиатурой, которую мы расtmотрим, будет аббревиатура CLR (СОПllпоп Langиage.
Run.time - оБIЦeЯэblRОВая среда ВlJl'полнеmm:). ДI1я про:tраммированив. термин сре­
да выlIoлненuя можно Пониматъ. шш набор внеtrni"ИХ tерви:сов. необходимых ДЛJI
вьmа.illiеR'ип данной о:компил:ировaIIНОЙ едиmщы прогрaммJiога Кода. Нвпример.
IЮгда разработчmt при создании ПОВОГQ ПРИ.!Iоже1-htя использует библиотеку базо­
вых ltJJacCQ» Microsoft (MFC). шtзнает. ЧТО дгт ВЫШJlШения етО про:граммы потре­
буется СОOlI'Вeтствующий iJЫПОЛНЯ€МЫIЙ :МOдYJГЬ библиотеки MFC (т.е. П\Zс42.dll).
Другие популярные ЯЗ-Jill(И также n"pедлагают соотвеТС"ГВуЮщие ВЪШОЛНЯ€М1iIе мо­
дУЛИ. Прогр.аммисты VВ6 привяза~гы :к одному или двум ВI:;ШОШlяемым МОдУлям
(например. msvЬvmбо.dl1). РазрабоГlИНИ Java ПРИВЯЗallЫR виртуальной машине
Java (JVМ) и 'i:д.
Платформа .NE:Тпредлагает совсем .црyrоЙ ПfJШЩИI1 орТa1llиaдJm среды Bыno.iI­
I-reюIЯ. Ра3l-Iица меж,пу средой выполнения .NEТ и средой выполнеяин, о :fСОТОРОЙ
roворИЛОСь BblII1e. эакшочаетсн в том. что сре,па вьmОJlнения .NEТ обеспечивает
единый u вполне onpеделею·JЫЙ "слой" выполненин. общй:й для всех язьncов И rmат­
форм~ совместимых G .NEТ.
66 Часть 1. Общие сведения о языке С# и плаТформе .NET
ваемой
ОСНОВНОЙ механизм физически заключается в библиотеке. назы
CLR
msco ree.d ll (известной также под названием Соmmоп
Objec t Runti me Ехесutlоп
на компоновоч­
Eng1ne - общий объектный модуль механизма вьmолнения). Когда
ный блок ссылаются для использования. rosco
ree.d ll автоматически загружается
уемый компоновочный блок. Механизм
и. в свою очередь. зarружает в память треб
Прежде всего. и это самое главное. за
выполнения отвечает за целый ряд задач.
го блока и нахождение запрашиваемо­
выяснение расположения компоновочно
я содержащихся там метаданных.
го типа в бинарном объекте с помощью чтени
преобразует СIL-код в соответствующие
Затем среда CLR размещает тип в памяти.
ходимые проверки безопасности. а за­
платформе инструкции. выполняет все необ
тем выполняет полученный программный код.
оновочных блоков и СОЗДaJlllЮ поль­
Вдобавок к зarруэке созданных вами комп
одимо. взаимодействует с типами.
зователъсК1Dt типов. CLR также. если это необх
ов .NEт. Хотя вся библиотека базо­
содержaIЦИМ"СЯ в библиотеках базовых класс
компоновочных блоков. ·ключевым"
вых классов разбита на целый ряд отдельных
компоновочным блоком является msc orlib
.dll. Фай.'l msc orlib .dll содержит
ют в себе решеция широкоro спектра
множество базовых типов. которые объединя
ые типы данных. используемьzе все­
оБIЦИX задач программирования. а также базов
жеНИЙ вы автоматически получаете
ми языками .NEт. При построении .NET-прило
ному блоку.
доступ к этому специальному компоновоч
икающих мещцу вaI1IИМ исходным ко­
На рис. 1.3 показана система связей. возн
базовых классов) ..NEТ-ком.пилятором и
дом (использующим типы из библиотеки
механизмом выполнения .NEт.

Различия между компоновочными блоками,


пространствами имен и типами
отек программного кода. Цель библи­
каждый из нас понимает важность библи
разработчику готовый набор блоков
отек. таких как MFC. J2EE или AТL. - дать
о строить но­
го кода. опираясь на которые можн
уже существующего программно
библиотек с программным кодом для
вые приложения. Но язык С# не предлarает
использовать .NEТ-библиотеки. ней­
конкретного языка. Разработ'lИКИ С# могут
чтобы все типы в библиотеках базовых
тральные в языковом отношении. для того
форма .NEТ предлarает использовать
классов были правильно организованы. плат
понятие пространства имен.
тся группой связанных типов. содер­
Упрощенно roворя. пространство имен являе
пространство имен Syste m.IO содер­
жaIЦИXСЯ в компоновочном блоке. Например.
-вывода. npостранство имен Syste m.Da ta
жит типы. связанные с операциями ввода
базами данных и т.д. Важно понимать.
определяет основные типы ДЛЯ работы с
что один компоновочный блок (такой как. напри
мер. msco rlib. dll) может содер­
ое из которых может. в свою очередь.
жать любое число пространств имен. кажд
содержать любое число типов.
на рис. 1.4. на котором показан
Чтобы СИ'1Уация стала более ясной. взгляните
снимок окна Objec t Broweг иЗ Visua1 Studi o
2005. Этот инструмент позволяет видеть
текущий проект. пространства имен.
ко:мпоновочные блоки. на которые ссылается
существующие в пределах данного
содержащиеся в компоновочных блоках. типы.
го типа.
пространства имен. и члены каждо
r
Главе 1. Философия .NП 67
Обратитевнимamte нато. что mscorlib.dll содержит очень МНОГО самы:х раз­
ных npоtтранствИМен. и в кащдОМ из этих простршю:rв имев содержатся свои се­
МЭ1IТичеC1i.И СIШзанные ТИШ:il.

ОСНОВНЫМ отличием этого подхода от TВJWX э.ависящих ОТ I«!НR'peТВOJ'Q Я3Ъrna


библиотек, как MFC. НВJ.яется 'f'O, что ~ рЩ-лътате все .8ЗьШи, поддерживаемые В
среде въпroлнеНИR .NE1: используют оо/-ш U те же npостранC"I'Вa имен и одН:и и те
жетиnы:.

И~ДI'IЫЙ кeд.NET
f\a базе
любого яэыl!t:t .NEТ

I<oМПОНOlOЧНЫЙ блок
",dllили ·.ехе
(СIL:-щ М8ЩQaНные и r.taНИфecL)

_UН,IQ.- 8.bJ.IIOIIII8IW1.НEТ
(mscoree.dll)

Бкблм.от,кИ
базовых кnaCc08

(inSфl'IiЬ;dll и др.)

Рис. 1.3. Мрдуль mscоrее.dП в д&йствии


68 Часть 1. Общие сведения о языке С# и платформе .NET

Рис. 1.4. Один компоновочный блок может содержать любое количество пространств имен

Для иллюстрации рассмотрим следующие три программы. представляющие


вариации вездесущего примера "Неllо World" соответственно на С#. vв .NET и
Managed Extensions for С++.

11 He1.10 wor1d на IIзыке С#


using System;
public class МуАрр
(
static void Main ()
(
СОПSОlе.WritеLiпе("Привет из С#");

, 8е110 wor1d на .зыке vв .NET


Imports System
Public Module МуАрр
Sub Main ()
Сопsоlе.WritеLiпе("Привет из VB .NET")
End Sub
End Module
11 8е110
wor1d на языке Мanaqed Extensions for С++
#include "stdafx.h"
using namespace System;
int main(array<System: :String ~> л аrgs )
(
Сопsоlе::WritеLiпе("Привет из managed С++");
return О;
r
Глав:а 1. фИЛQООфl'lЯ . NET 69
3аме'):'ИМ, что 1Щесъ 11 любом из язьщоц используетсJ:l класс Console. (щреде,лен­
ный'впространетве имен S~уstеП\. Если отб]юситъ неаначител:ьныесинт<шсичеСlЩе
вариации,то ЭIJ'И три приложепия ВЪU1IЯЩIТ очень похонщми, :как по форме. TaIt и:
по'лorике.

Очевидно. вашей главной цеJIЬЮ. Н,ах разработчика .NEТ. нвлнетс.FI получеJ-ше


'Исчерпы.вaIOщеЙ m:rфорМЗЦJЩ обо вСеМ разнообразии типов, onpеделеFЩЫX в рам­
ках (мнorочислениых,) пространс:гв 1Щен .NEТ. IЛавным из пространств имен. о ~p­
торых следует знать, является S'ys't:em. Это ДР0СТра:нство имен предлarае-т базовы:й
набор ТИПОВ', :которые ВЫ, 'RaН ра.зрабо'ГЧ:ЦЕ .NEТ. будете испощ;эовать снова и сно­
ва. Фащuчес:ки !3ы не СМQЖете ПЩ:ТРQИТЪ ни одного реа.лцно работающего С#-цри­
.llPженин., не СОСJ:raвщисъ. кaR~. 113 пространство Щ\II'ен System. В табл. ).4
преД1larаютс.в кратк;ие одис.amзл: неноторых [НО, КОНeчJ:lО Же. не все..х:) пространств
.и:мен J\lEТ,

т.Cinица 1.4.. Пространства имеlit .NET


ПростраНGТВЭ иМен .NEТ ОnИС8tt и е

sуst.ещ в рамках System ВЫ найдете множество полезных 1111П05,


связа~IНЫХ с ВНУТРенними даtlНЫМИ" ма:reмаП1'lескими ВI:!IЧИС.
ле'НI.fRМИ, перемеЖ-IЫМИ ОКРУЖения, генеРl.1рааанием Сllучай­
Ных Ч1llсеl1 и сбаром мусора, а таlCЖе с 06раlDаткой ТИПИЧНЫХ
ИОlU1ючитem.Ных си1'jflЦИЙ и атрибуroв
System,Collec~ions Э'(\IIпростра:~стsa имен опред.еляют р;щ IШнтейнерных абъек­
Sys,tem.Gollecti0115,Gene:rio тав (ArrayList. 'Ql.leue и т,д,) , '3 также базовых ТИПОВ и
интерфейсон" которые ПОЗВРЛЯlOт t;ТpОИТII полйзавателЬСlC:ие
КQJ1J1екции. В .NET2.Q П1ПЫ коллеlЩИЙ обладают даполнитель­
ными QЩИМl-1 вдэможностями

Sysbem.Data Эти npaCTpa~ICTBa име~ ИСПРЛЬЗУЮТGiI ДЛЯ взэ.ИIv!QдеIi!СТВИI1 G


Systern .Data . Odb t: Вазами A<lHllbl)( на основе ADO,NET
8yst!3m. Data .. ОrасlеСliепt
sУstеm.D~tа.ОlеDЮ
&ys'tem. Оа1:а. 5qlClie:nt
s
Ву Ьет" Di ачпс) 5 t 1сs, 'ЗДесь вы Н2Йдщте М~lOже(lтватиno'В, lI:0'fQpble могут ИСПОЛЬЭQ­
El.a~ ДI1M npаграммной, отладки и трQc;cJ.tровки ИСХОl\н.оro кода

Зуs'Ьеrn .Drawirrg 3:цесь вы flаидете МНlJжествот~пав для работы с графt.t4ески,


~уstеm.Drаwing.Ьr,аw.iпg2D мй лримитиl3aМИ, таl<liми кш растровые изображения, шриф­
System.Drawipg.printing ты и пикroгрclММЫ, а т8.юке ДЛЯ ВЫIВOДa >la пе'lаТh

System.IO ЭТи прастранства имен ВКЛЮLIJ'lЮТ cpeДl::твa фаЙJ10ВОГО ввода­


вУsctе.\1\.Щ.СощрrеSS!Оr! аывода, буфер"ЗSЦИИ \JI т.д. В .NEТ 2.0 npoc;rpaнcтвa имен !с
&ystem,IO.Parts предпзгают ПОМеРЖЧ ЩЮ'ИА' и работы j) портами

SуstеЩ,Nеt Это проотранVТВQ имен (как и дрvгие pOACiГВeHtllbIe npостщrн­


ства имен) сод&ржит 'j'ИПЫ,овя3at-lные с сетевым программи­
РOlJанием (запросы/отвеТЬ!, OOKвIbl, ItOHe"IHbIe 1'О"lIЩ ОО~<дИне­
ний и т.)1, ..)
SУБт,ет .1!.efl.e cti оп эти ПРОфJранства I4MeH определяют типЫ, 1iJвязаflf.jЬ/е с I)ООа'
SysteI!1.Ref1er;ti'on.Emit ружением типов в среде ВЬlполнеНИR И' динамичеСkИМ' созда­
ниемТИПQВ
70 Часть 1, Общие сведения о языке С# и платформе .NET

Окончанне табл. 1.4


Пространства имен .НЕТ Описание

System.Runtime. Это пространство имен обеспечивает средства взаимодей­


InteropServices СТвия типов .NEТ с ~неуправляемым программным кодом"
(это, например, DLL на базе С и СОМ-серверы)

System.Runtime.Remoting Это пространство имен (среди прочих) определяет типы, ис­


пользуемые для построения решений на основе возможно­
стей СЛОя удаленного доступа .NET
System.Security Безопасность - зто неотъемлемый аспект платформы .NП
В пространствах имен, объединенных идеей безопасности, вы
найдете множество типов, связанных с разрешением ДОС1УПа,
криптографической защитой и т.д.

System.Threading Это пространство имен определяет типы, используемые при


построении многопоточных приложений

System.Web Ряд пространств I/IMeH, специально предназначенных для раз­


работки Web-приложений .NEТ, включая Web-сервисы ASP.NEТ
и XML
Sуstеm.Wiпdоws.Fоrщs Это пространство имен содержит типы, которые упрощают
процесс соэдания традиционных GUI-приложений (приложе­
ний с графическим интерфейсом) для настольных систем

System.Xml Связанные с XML пространства имен, содержащие множество


типов, используемых для взаимодействия с ХМL-данными

Программный доступ к пространствам имен


Не помешает повторить снова, что пространство имен является ничем иным,
как подходяIЦИМ для человека способом логически понять и организовать свn­
занные типы. Снова рассмотрим пространство имен System. Можно считать, что
System.Console представляет класс, названный Console и содержащийся в про­
странстве имен System. Но для среды выполнения .NEТ это не так. Механизм сре­
ды выполнения видит только отдельную единицу, названную System.Console.
В С# ключевое слово using упрощает ссылки на типы, определеШIые в данном
пространстве имен. Вот как оно используется. Допустим, вы строите традицион­
ное приложение для настольной системы. В главном окне должна отображаться
некоторая диаграмма. основанная на информации, полученной от внутреI:шей
базы данных. и логотип компании. Конечно. ДIIЯ изучения типов в пространствах
имен требуется время. но вот несколько очевидных ~кандидатовМ, подходящих для
ссылок в описанной выше программе.

// пространства киев, нвобхоДИll»В ,цnJl данного DpЮJо.еНИIII.


using System; // Общие ТИПЫ базовых классов,
using System.Drawing; 11 визуализация графики,
using System.Windows.Forms; 1/ GUI-элементы,
using System.Data; 11 общий доступ к данным,
using System.Data.SqlClient; 11 ДО СТУП к данньш MS SQL Server.
[лава 1. Ф~ЛОСОфI1Я .NEi71

Выбрав Hel~oтopoe число цространств l>IМeн (я уназав CCдI.JntI4 на .компоновочные


БЛОiШ,1tоторые их опреде..ляют).. БЫ получаете В03МОЖlIQСТЬ создаватъ эRЗеl\(IIJIЯ­
ры соответствующих типов. Например. если вы хотите соэдаlJ"Ь э}(3емrшлp класса
Bitrnap (И3 ЦPOCTpaнcTBt1 имен 5ystero.Drawing), ВЫ можете }ICnOJIЬЗовать следую­
щий npогрaммUblЙ код.

1/ ~ список пpDС:Щ'РiUlС!rS ИКQ, ~caom..sY4DlJDt. ~о.. ф&ii:n••


'usingSуstеш;
u"ing Systern .. Drawing;
class МуАрр
{
plJblic "Qid DisplayJ;.ogQ ()
{
11 СоSJtа.ии. иао(SpucевИll:20Х20'.
Bi tlnap ефmрапУLоgL> =- new Bitmap (2,О, 2 ()) ;

ПQСКQJIbКy ва:ше прилож~е ссыдается на $ystern. Drawi.ng, кmиmля.ТОР смо­


жет ВЫЯCНflТЪ. что :клаОС Bi tmap явзщется членQМ j'Щi~анного проотрщrства имен.
Если бы 'вы не}'lШЗали здесь пространство имен Sys'tem.Drawinq. то получили бы
ошибку RО'МIIИJIЯЦИИ. НО ДJПi оnpеде.левв'Н переменны:х можно также ИlЛI0ЛЪ30ватъ
rw.iZн:oCmt:aO D~деJJeюфе, или йikoлюmнпe wи.я..

/J Здесь Systetil. Drawinq ив }'1t~ae'1'QJI~


\181П9 Sys temj

clas-s МуАрр

f
ри:ЬН с v.oid Displ ayLogo О
(
11 Иcnom.ЗО_aJWе а.БС0lIJD'L'801'О ии. . .
Sy.stero. пrаwiug. Вi:tmap r:;ompa,nyLogo =
new Syst.em.Drawing.Bitmap('20, 20);

йспоЛЬЗование абсолютliЫX именДJIJI определeнuятипа, :КОrreЧЕО~ упрощаетпо~


нимание npограммното кода. 'НОЯ ..цумаю. И вы не стайете .возражать. что кпючевое

c.1l0BO С# '.lSing yМeHыnaeт объем необк.оди:моro ввоДа. В aтoMТce8Cre мы будем ИЭ~


беrаТЬИСП0ЛЪ30вайИЯ абсолютных имен (за ИCКlIЮченвемтех алучаев, Kor,цa из'-за
~TOГO возникает явная неОДfIозвачность)', о'Гдавая Dpедпочте1tИе использованию
RJiЮчевого СЛова l1'si пч,
При сЭтом: следует понимат.Ь. что Использование us:tng явnя.стcs.c JIИUIЬ aлI:n'~риа­
ТИВОЙ ИСПDЛЬзовани" абсолютных имен. nосколысу в ПРОrpaмм1ЮМ к-()де CIL Boerцa
ИСIIOЛbЗyIOТся абсолютные имена. ~ ~Т() оба yRaЗэ:IOlЫX подхода Дa:JIOТ с.овершеН1W
rЮU1ia1Фвые реэу..!Ilй'аты в CIL 11 с l'оЧRt1X зрения nPOИЗВО,mneдыюcnt, и С ТОЧЮI эре­
дИЯ ~aMepa кOl\ЩОnOВОЧН:ОГО о.."1Ока.
72 Часть 1. Общие сведения о язЫке С# и платформе .NEТ

Ссылки на внешние компоновочные блоки


Вдобавок к указанию пространства имен с помощью ключевого слова С#
using, необходимо указать компилятору С# имя компоновочного блока, содержа­
щего реальное СIL-определение соответствующего типа . Выше уже упоминалось.
что многие базовые пространства имен .NEТ содержатся в mscorlib.dll. Однако
тип System.Drawing.Bitmap содержится в другом компоновочном блоке с именем
System.Drawing.dll. Большинство компоновочных блоков .NEТ Framework разме­
щается в специальном каталоге. называемом GAC (Globa1 Assembly СасЬе - rло­
бальный кэш компоновочных блоков). На машинах Windows это может быть ката­
лог %windir%\Assembly. как показано на рис. 1.5.

dl!Sys!мI.lXedDrv$erv ... 2.0.0.0 ьозf5f7f1!dSOaЗе мsn.


CSy.Iem.lXedDrySeni... <.0,0 ,0 ЬDЗf5f7flldSOaз..
IIIJS\l$Iem.Dr"wi1!I LO.5000.D bDJf5f7fl!d5DllЗе
,. '4;;; 2.0,0,0 ЬDЗf5f7flldso..з. МSIL ..,
.Sysll!m.Dr"W1ПQ,Oesi!jr1 1.0.5000.0 Ьо3fSf7fl1d508з..
::tфsys_ .Dr..\W1II. ~ 2.0.0.0 bDЗfSf7f11d5Dllз.. МSJl. .~
;tj!~iУJйtf1rJt~~:5J:;:,<_;;л~i!;jjЛ~{:i:&:i~I;Дz:{'iJi/Git1j;~1;irffЖ}<й..fl{ff!);~ffi1:.~~ffJi!JfiA7/ifIf!i!f!fJ~1 :II"J
Рис. 1.5. Библиотеки базовы)( l<Лассов в GдC

В зависимости от используемого инструмента разраБOТIШ приложений .NEТ мо­


жет быть несколько разных способов информирования компилятора о том, какие
компоновочные блоки следУет включить в цикл компиляции. Подробно зто будет
обсуждаться в следУЮщей главе.

Использование ildasm.exe
ЕсJШ вас цугает перспектива освоения всех пространств имен платформы .NEТ,
вспомните о том, что уникадьность любого пространства имен заключается в ТОМ.
что оно содержит типы. некоторым образом семантически связанные между со­
бой . Поэтому. например. если вам не нужен интерфейс пользователя для простого
консольного приложения. то смело можете забыть (среди прочих) о пространствах
имен Sуstещ. Windows. Forms и System. Web. Если вы строите приложение для рабо­
ты с изображениями, то вам вряд ли понадобятся пространства имен для работы
с базами данных. К тому же. как в случае любой новой бибJШотеки готового про­
граммного кода. вы можете учиться по ходу дела.

Утилита i ldasm. ехе (Intermediate Language Disassembler utillty - утилита ди­


зассемблера промежуточнorо языка) позволяет загрузить любой компоновочный
блок .NEТ и исследовать его содержимое. включая соответствующий манифест.
программный код CIL и метаданные типов.
По умолчанию файл ildasm.exe должен быть установлен в папку C:\Program
Files\Microsoft Visual Studio 8\SDK\v2.0\Bin (если вы не можете найти
r
rfliBa 1. Философия .NEТ 73
i1dаэm.ехе в указанном месте. npoCТi) выполните поиск файлало юпочу Wi1dasm.exe"
на своей маШине).
Обнаружив и ~апустnв Э'I'()l' файл> з ОТRpывшемся: окне выберите КQмзиру
Мt!'ню Fileq OP~'h и перейдите R коt.шонов()чному блоку . :который вы хотите »с­
следовать. С целью иллюстрации здесь (рис. 1.6)показм вJомxrоновочiiый бло:к
C5harpCalc1Jlatar.exe. о "оторомуже шлз речь выше. Утилита ildаэm.еке npед­
C'taвляет c:rpyнтypy RОМПОНОВО'LШОГО блона, исnoльзуя: всем 3НaRОМЫЙ формат .де­
рева nPОСМотра.

~ МА rJ.1 r-'es т
f' • Сill~"Iato!Ex/IП'fP1e
" • Qlw&!II'Ex".CI:Ik:
~ ,d_.~bk ЬШо n'be(oteFleldrtJit
• ,dDrJ~~()
дdd , IrtЗZ(IntЗ2,J1'1tЗ2)

g.~8mpl&.~
.. ,CJМS pojttc ",,10 «.1>1 befщl'i8ldln~
• .МI'ЮIdI)
а Maln':~

Рис. 1.6. ваш НQВЫЙ лучший друг i ldasm.€'xe

Просмотр СIL-кода
в ДоПOJntение.к тому. "11'0 ВЫ можете видеть прострадетfЩ имен. ТДПЫ и Щ
члены в Щ)мпоновочном блоке. ild<lsrt1.exe позполя:ет также прос'Мотр~ СIL-иц­
струкции Л1Обого '<Ше:на. Напр1-IМеРJ есл8. выбраТ:Q' ДВОЙlD>Щ Щ6JIЧШ):М МС1'ОД Мдin ( j
масса Са1.сАрр.'!'О noлвцтс.я: отдельное 01ЩО, в :котором будет QТображаТЬCSj: соот­
.g'еТствующиЙ CIL-ICОд. (рис. 1.1).

• ntlt<YPllint
11 J:Od!! $12'"
.lIГoIхst;щlC S
.1DUl!i tnit ([11] t:l~s Calctйilto"E)(~lJ!.Cal.c с.
11] tnt32 ilnS)
n... ll!lJlj
ll_ •••• : inst.anc... void Cil1C1J1atnl'ExaJIIII}~.Calc::,.ctorO
JL_"I5~ rtlDf:.1
1L "16~ ld]:nc.1
ICall1: ldc-.1!I& •.5 l'
1L-•• I9= ldf;.tJI.s .~
lL:"Ib~ culvil't inst_ancl.' 1nt32 &illculatorEllaI'lPJ ... ,.Cillc::Add{ .... L~~~

P...~ 1.7. Г1росмотр OIL-КQд<!


74 Часть 1. Общие сведения о языке С# и платформе .NET

Просмотр метаданных типов


Если вы захотите просмотреть метаданные типов для эarpуженного в настоя­
щий момент компоновочного блока, просто нажмите <Ctrl+M>. На рис . 1.8 ПОRaза­
ны метаданные для метода Calc.Add ().

IIrthodКil .. : Add (1611.113)


fligs : [Publ1c] [Hide.ySig] [ReuS.Slot} ( .......6)
RUR : "".12'"
l.,lFligs : [IL] [Кilniged] ( •••••••• )
Cil1Cnuntn: [DEfAULT)
h.sThis
ReturnTJP.: 1'' -
2 Argu. .nts
Argu..nt 11: 1.-
Argu. .nt 12: J.-
2 P.r.... t.rs
(1) P.. r ...Taken : ( ......11) Н.... : х flags: [попе] ( •••••••• )
(2) Para.Token : ( ......12) На.е : у fl .. gs: [попе] ("'.'.II)

Рис. 1.8. Просмотр метаданных типов с помощью ildаэm.ехе

Просмотр метаданных компоновочных блоков


Наконец. если вы захотите просмотреть содержимое манифеста компоновочного
блока. то вам нужно двойным щелчком открыть пиктограмму MANIFEST (рис. 1.9).

Рис. 1.9. Двойной щелчок на этой строке покажет


манИфест компоновочного блока

Не сомневайтесь в том, что ildasm.exe имеет не только те опции. о которых


говорилось BЫ1lIe . Дополнительные возможности этого инструмента будут обсуж­
даться и дальше. По мере чтения материала книги я рекомендую вам просматри­
вать ваши компоновочные блоки с помощью ildasm.exe, чтобы вы видели. как
ваш программный код С# транслируется в неэависимый от платформы програм-
f

МНЬ1Й :код Сп,. :Конечно, "JТOбьr быть сyne,рзвездой С#,. сов(':ем не о(5язатe.юmo быть
ЭR€пертом по проrраммному K€lд.Y CIL, но ПОНИМЩие е~тахсИCCl CIL 'TWXЫto укре­
r:;r:ит'ВЩШJ "мyc1WJIЫ лрогра:мм:ировaшut~,

Инсталляция ер,еды выполнения . NET


ДmI B~e не ДОЛЖНО быть сюрпризом то, что компоновочные блЬки .NEТ MO:ryт
ВЪ1Полн.ятьсл TOJIЪ1W на машине, на 'Коroрой УС1Га:новден :каркас .NEТ FrameW'ork.
Для вас, как ДЛЯ раэрafimчm:кa .NET-прил:ожеНИЙ. это не дрлжно быть npоблемой,
пOCШШЫty :ваша мащина буд~т долЖным образам СI~ОВфиrypиpoвана уЖе в процес­
се у:станоШtИ свободно Aoc-rynнoro па:кета .NEТ FrameWork2.o ЭDК (или любой ном­
мерчеСRОИ среды разработки .NEТ-цpmюжений, например V:isual Stud10 20(5).
Одна:ко, есJ!Й вы развернете КDМПОНОВОЧНblЙБЛОR I"Ia, номпъютере. Который не
имеет установленной системы .NE:I', этот в:омпоновочныИ блок выполняться не
СМожe'J:. По Этой причине Мicrosoft npеДJIагае"1 УстановоЧEblЙ Ilf:illeTdotnetfx.exe.
который .может быть бe€ПJlaТНО .Il0ЛУЧен и установлен ВМесте с вашйм npотрамм­
:н:ым обеcnечением.. Эта. уставовочнанпрщ-рамма ВЮIIOЧeна в .NE1' FrclIDewod 2.0
SDКo а также дacтyrma для беcnлaтнайз~рyзIOi с }'3.!"Ia Мicrosoft.
Поме установки dotnetfx. <.;Ха ваша мапrииа будет еодержать БИБлиотеки базо­
вых 1tлaссов .'NEт, фaйJIы. среды въщоmшшш ,NEТ (:rrusi.:::oree.dll) и доttOЛНИ'I'e.nblt11J
инфраструктуру.NEТ (например, GAC).

ЗаМечание~ Пр~ поСтроении WеЬ"'приложеf<JИЙ .NEТ не предпрпаГается, 'П"о на маШИНе коне,ЧliОО"О


nольэователя б)/дет устаНовлен каркас .NET Framework, n.О!ЖО!l1Ь~ .браузер KOl-lеQtlого nОJlЬЗОВQ­
.еля лpDCТО Т1GJIIучает общий HTML'K{)~ и. ВОЗМDЖНО, Jаva6сгip\·код ЮJИента.

Платформенная независимость .NEТ


в зав~ршеFЦfе этой ГJrавы позвольте мне сказать неаКОлРRО слов ПО поводу не­
З~И(JИМt~сти ШIатфорМ1:!I ,NEт. Неожиданноотью дml многщ раарабоТ'ШКов ЯВJ1Н­
f;ТШi ТО, 'Что RшщtQНОВОЧJfbl6 блоки .NET MOryт ра;.зрабатьmaться и выполняться в
операциоНl:lhl4 С'J!СТе:мщ, Отmrчн:ыx оТ олсрациоиных систем Мic:rosoft (~C 05 Х.
'МНОГQЧЩ:JJeщiЫе ва,РJJ;адии Liщщ, в частцост,и Веов и FreeBSD и: др.). ЧТQбы по­
НЯТЬ. lJQчему ЭТQ возмоцщо., мы С вам,а доllЖЩ>f paCCMOТP~TЬ еще- одну аббревlIЩ-
1УРу, используемую во "вселеаноЙ· ,NET: это аббрев~атура си (CommonLa.nguцge
Infrastrцcture - общеязщкова.а илфраС"'.rpywгypа}.
Доуда Мicr()soft в:ы:пустила ЩlЫК ЛРОГРa1l,2МИрования С# и р:ла1'форму ,NE'f, она
выпус.тила ТЩOJtе МНЩRес~во Ф9рl\taJIЬ:щ,IК AOНYМewroJ3 .• поторы~ описamr сшr;raxt::.ис
n .ССМЩIтику языков G# и CIL. фор~~Т .('!.ОМЦОНО;ВОЧlj0j'О t!дOK3 .N$Т. б~()ВI:Щ! про­
CTp~'I;вa ИМен: l>i papory ТИDОте~~roI'{) МeJЩ.FЩЗAoIа среды ~ЫПDДН~ .NEТ (.JJз­
.весТНQГОТаюке пет наэвани~м VШ:;, иди VirtLЩ.1 E;reС1ЩЩl Sуstf!Ш - :виртуальн;э.я:
система ВЪ1noлненщJ,). Еще лучше ТО. что все эти докуменТЕ предеТaщtещ.r в орга­
низацию Еста lntеП1аtiе:iш1 (ht tp ;! / W'WW , еста- interna ti 01"1&1 • org) ;D,II.П ~е]ЦЩе­
ния их в качестве официальных междунцроДfIЫX СТЩIдарl'QВ. Иитереq10ЩЦМИ н.ц.с
GПе:цификaцИ.FIМИ .яв.лmoтс.я:
76 Часть 1. Общие сведения о языке С# и платформе . NEТ

• ЕСМА-334: спецификации языка С#;

• ЕСМА-335: общеязыковая инфраСТРУК1ура (CLI).


Важность этих документов проясняется, если эаметить, что они предоставляют
третьим сторонам возможность строить дистрибутивы платформы . NEТ для лю­
бого числа операционных систем и/или процессоров . Спецификации ЕСМА-335,
наверное, более "содержательны", поэтому они разбиты на пять разделов, I<aк по­
казано в табл . 1.5.

Таблица 1.5, Разделы CU

Разделы ЕСМА·335 Описание

Раздел 1. Архитектура Описывает общую архитектуру CLI, включая правила CTS и CLS,
а также работу механизма среды выполнения .NEТ

Раздел 11. Метаданные Описывает структуру метаданных . NEТ

Раздел 111. CIL Описывает синтаксис и семантику CIL-коАа

Раздел I~ Библиотеки Дает аысокоуровневый обзор минимальных и полных библиотек


классов , которые должны поддержиааться дистрибутивом .NEТ

Раздел V, Дополнения Коллекция · вспомогательных" элементов, таких как рекомендации


по проектированию библиотек классов и подробности реализации
компилятора CIL

Заметим. что в разделе IV (Библиотеки) определяется минимальное множество


пространств имен, которые представляют только базовые сервисы, ожидаемые от
СLI-дистрибутива (коллекции, консольный ввод-вывод, файловый ввод-вывод. по­
точная обработка. отображение, сетевой досryn, базовые средства эащиты. XМL-Ma­
ниnyляции ит.д.) . Такой СLJ-дистрибутив не определяет пространства имен. упро­
щающих разработку Web-приложений (ASP.NEТ). досryn к базам данных (ЛDО .NEТ)
или построение графического интерфейса пользователя (WIndows Fbrms).
Благоприятным. однако, является то. что главные дистрибутивы .NET рас­
пространяют библиотеки CLI с зквивалентами Мicrosoft дЛЯ ASP.NET. ADO.NEТ
и Windows Forms. чтобы обеспечить полноценные платформы разработки про­
изводственного уровня . На сегодняшний день есть две главные реализации CLI
(кроме предложений Microsoft, предназначенных только для Windows). Хотя в
зтой книге рассматривается создание .NET-приложениЙ с помощью . NЕТ-дистри­
бутива Мicrosoft. в табл. 1.6 представлена ТaI<Же информация о проектах Мопо и
РоГШЫе.NEТ.
как Мопо. так и Portable.NEТ обеспечивают ЕСМА-совместимость компилятора
С# и механизма вьmолнения .NEТ. примеры проrpаммного кода, документацию, а
также многочисленные инструменты разработки приложеНИЙ. которые. по функци­
ональности ЭI<вивалентны инструментам, входящим в состав . NEТ Framework 2.0
SDK от Мiсrоsоft . К тому же Мопо и PortabIe.NEТ поставляются с компиляторами
vв .NEТ, Java и С.

Замечание. Если вы хотите узнать больше о Мопо или PortabIe.NEТ, прочитайте книгу М . J. Еаstол
и Jason King, Cгoss-P/atfoгm . NEТ Deve/opment: Us/ng Мопо, PortabIe. NEТ, and Micгosoft. NEТ
(Apress, 2004).
r Глава 1. ФИЛ'ОС{)фИЯ .NEТ

Табnица 1. 6 •. NЕТ~дистри6утивы с ОТХРЫТЫМ ИСХОДНЫМ KQдOM

.h .ttp;./ /w,wW.mОГiO-рl:оjес"t:.соm Проект Мапо является дистрибутивом Cll с 01ХрЫТЫМ ис­


ХО.QЮ>lм КОДОМ, предliазkаченl1ЫМ ДЛЯ раЭnИ"tныхвариаfiТQВ
.unux (Н2IТ1РИМflр, SuSE, Fеdоra:и т.д.), а твкже Дl'IЯ WiпЗ2 и
МQcOSX
ht tp; l/wWw. dоt.gШ:J. org Portable.NEТ ~ ЭТО другой дистриб~в CLI С urnpытЫМ ис­
ХОДНЫМ KQДOM, преднаэftа"!е~ныlll ДJlЯ множест~а операци­
ОНfiыхсистем. PortabI~.NEТ ",аЦе-лен на ТР, ' чтобbj обсЛyJКl1-
8Зп. как можно БQлЬше операЦИQftНIiIХ систем' (Wln32, AIX.
BeOS, Мае os К, Solar/'s, вое maвHыe варианты UnlJ)( и 'r.l(.) .

Р· езюме
Целью ЭIТОЙ ТJЩВЫ бl1LЛО оn:исamю базОВЫХ :концепций. :неоБхQдимых дшя освое­
щщ QCTW~OrO 1I:iаJ'ериала этон:книги, Сначала БЫЭIЙ рассмотрены ограничения и
сложн;щ:'n1 технwюпЩ, поSmИВТrrиxся Ao..NE'Y,· а змем БЬUI предложен обаор того.
1mE' J'tlEТ и С# цытаютс'я: УПРОС'FИТЬ существующее IЮложение вещей.
В СУЩНостИ •. NEТмОЖНО свёсти К механи3Мусредъгвыnoлнения (mscaree. dll) и
БЩinиoтe]:щБаэовых классов (msc"rlib.dll и сonyтcтвyюП:J;Иe файлы). ООщеязhtКOвая'
среда ВJaПlо.tIНении (GLR) способна Р:РИШl.'IЪ любой БШiaрlЦ.IЙ .NE'f-06ъеRТ (назьtвае­
мый RОМп~нов!JЧНblМ блnиОМ), е.слиТОЛЫ{О этот бинарнЫй объект ПОДЧИRЯe'I'CЯ I1pa-
вшщм. упр<шл.вемoro пporpaммRorо кода. как вы убеДИлись. коМПоновочные блоки
сод€ржат СlIгинструкции (в дапо.11lieiIИe х метаданным ТИПОВ и манифес-l)' компо­
новочного БЛQБа). ItOTOpble С 'IШмOЩьюJ1Т-КОМПИЛЯТо'ра КОМnИЛИPYЮТСЯ: 11 спеЦlХ­
фИ"'reс.mе ИНстр}'1ЩИИ плаТфQрМы. Кроме 'I'Oro. была выяснена роль общеs3blКОВых.
спецификаций (CLS) и общей сис)'емы тИПОВ (CТS),
3&тем БЪL"Jа рассмотрена утилита i 1 dasm" ~Xe .. а также то, ltaк с помощью
dotnetfx .ex€ наетроить мз т ттину Д2Ш исполЬзования .NET-пр.иложенйЙ. Б закmoче­
вне было с:каз.ано несколъко слова незавпсимоп от платформ природе С# и .NEr.
r,

ГЛАВА '2
Технология соз,дания
u
приложении

ная3ыIеe С#

К ах разработчик проrрам.м на sыьще С#. вы имеете возможность выбрать


JiЮбой из .множества дocтynныx: инстрУ14"eFIТОВ разработgи .NET-приложС}­
НИ'Й. Цt'J1ЪЮ э'tOЙ rnааы ЯSляетс.IJ обзор C~ J;'азных инструментов разработки
.NEr. в:кmoчая. КОRe'ШО же. Vlsua1 Stud10 20(J5. Однако :начнется глава с расс:мо­
:rрения Rомm:lлятора Щ)мандНОЙ СТрОIЩ С#. с.:,; с . ехе. для- раБОТht с которым буд~т
достаточно самого IrpостщотекСТQlJОro редантора. налример проrpaммы Бдокнот
tnotep.ad.€xe). :Кроме того. МЫ с ВaМIJ ВЫЯСН:IW. КЗR 1IЫп0ЛНiП'ь отладку КОМПОНО­
вочнЬrx блоков .NEТ (' командной СТРQJШ С IIОМОЩ:ЫО Cordb.g. ехе.. Освоив компи­
ляцию и O'l"Лад'КУ К0МЦОНОВОЧНЫХ блоков бе;з графичеCIЮГО mrгeрфеЙса. мы затем
выясним. :как моЖJ-IО редаJCТироватъ JiJ J{IiJМПилИровзть ФаЙЛЫ исходного 1tода С# с
помощью при.лОНСе1ПfЯ Th:XtFad. .
Конечно. с тeltcToM этой :IOIДг.и можно раБОТагп.. ИСIiОЛЬЗУЯ ТОЛЬко csc .e~e и
Б:!rоюют /ThxtPad. но я ~. вы 3aИFГгересованы: в освоении б'О. . Iее широ:ких В0'3-
можвосте'Й. предлarае:мъrx в рамках: со.временных IDE {Ihtegrated Development
Envl.ronment- интетрировщma:Rсреда рцэрабоТRИ], Поэтому:мы рассмотрим тая­
же SharpDevelop _. интеГРИРОВa:нFIj711i1 среду разработки с oткpьrrым h'схоД'6ЫМ тек­
СТОМ. По ф)'1Ul:IUiоналЬНQСТИ она шщкурирует со многими коммерч-ескими сред­
ствами разработки ,NE'f, обладая тем дщюл:ните1Ibl:Iым преимуществом. что она
бесплli'n.Iа. А nQC1Le краткого oocymдel;IтJ воsмoжностd! VIsua1 С# 2005 E~ress· мы
npис'I}'IIйМ к рассмотрению Visua1 stщ;liо 2005. ЗакоНчится глава небоm.щим об­
зором це.iIQrо ряда ДОПU.lllЩ';Гe.llpНЫ инструментрв разраБОТ!GI .NEТ [многие JIЗ, хо­
i"OрыХимеют откры1ый и~одиый щщ1 и реКОМe.JЩaЦИЯМИ по ПОВОЩ" того. ШlК эти
инструменты получить.

Устан0вка .NET Framework 2.. 0 SDK


Пр!':щде чем Ш1ча'l'.Ь строить .NEТ~ПРИJIОЖения • .исnолъзуя язык про;грам:миро­
В$И;R С# И к.1.p~e разработки прmlOжeRИЙ .NEГ Ftamework, СНаЧала li;Y'ЖНЬ уста­
ПОВИТЬ своБQдщJ д9Сryпны:й шшет .NEт Framework 2.0 SD}{ {Software Devt:lopment
IШ - КОМПЭ'ЩК'1' средСТв р~раБЬткI-I прогрaммttогообеспечеция}. Gдед,ует 3ВаТЬ о
80 Часть 1. Общие сведения о языке С# и платформе .NET

том, что .NEТ Framework 2.0 SDK автоматически устанавливается при установке
VisuaI Studlo 2005 или Visual С# 2005 Ехргевв. поэтому если вы rшанируете уста­
новить одну из указанных систем, то загружать и отдельно устанавливать пакет

программ . NEТ Fгamework 2.0 SDK нет необходимости.


Если у вас нет Visual Studio 2005 или VisuaI С# 2005 Express. то откройте стра­
ницу http; //msdn. microsoft. com/net framework и выполните поиск по клю­
чу ~.NEТ Framework 2. О SDK". Перейдя на соответствующую страницу, загрузите
setup.exe и сохраните этот файл в подходящей папке на своем жестком диске.
Затем двойным щелчком запустите этот выполняемый файл, чтобы установить
соответствующее программное обеспечение.
После завершения установки на вашей машине появится не только необходи­
мая инфраструктура .NEТ, но и множество инструментов разработки с очень хо­
рошей справочной системой. примерами программного кода и обучающими про­
граммами. а также различные официальные документы с описанием системы.
По умолчанию пакет .NET Framework 2.0 SDK устанавливается в каталог
С: \Program Files \Microsoft Visual Studio 8\SDK\ v2. О. Там вы найдете файл
StartHere.htrn . .который (в полном соответствии с его названием) может служить в
качестве отправной точки для доступа ко всем разделам документации. В табл. 2.1
предлагаются описания некоторых подкаталогов из корневого каталога инсталля­

ЦИИ.

Таблица 2.1. Подкаталоги корневого каталога установки .NET Framework 2.0 SDK
Подкатаnог Описание

\Bin Содержит большинство инструментов разработки .NET-приложениЙ.


В файле StartTools.htm предлагаются описания всех утилит

\Bootstrapper Почти все содержимое этого каталога можно игнорировать, но следует


знать, что именно здесь, в подкаталоге \Paekages\DotNetFx, на­
ходится dotnetfx.exe (см. главу 1)
\CompactFramework Содержит программу устаноаки ,NEТ Compact Framework 2.0
\ Samples Содержит программу установки набора примеров .NEГ Framework 2.0
SDK О том, как установить примеры, говорится в StartSamples .htm

в дополнение к файлам. установленным в каталог С :\ Рr оg r а m Fi 1е s \


Мiсrоsоft Visual Studio 8\SDK\v2.0, программа установки создает подкаталог
Microsoft . NET \ Framework в каталоге Windows. Там вы обнаружите отдельные под­
каталоги для каждой версии .NEТ Framework, установленной на вашей машине.
Внутри подкаталога, соответствующеrо конкретной версии. содержатся компиля­
торы командной строки для каждото языка, предлагаемого в рамках Мiсrosоft .NEТ
Framework (зто CIL. С#. Visual Basic .NEт. J# и JScrtpt .NEТ), а также дополнитель­
ные утилиты командной строки и различные компоновочные блоки .NEТ.

Компилятор командной строки ДЛЯ С# (csc.exe)


Для компиляции исходного кода С# есть целый ряд возможностей. Не касаясь
VisuaI Studlo 2005 (и различных ЮЕ сторонних производителей) , здесь можно от­
метить компилятор командной строки для С#. csc.exe (где еэс означает аббревиа-
t

Глава 2, iехнолorИR СОЭ~НИЯ ПРИЛOJ:еIotИЙ на языке С# 81


туру для с-Эhшp Cornpi1:er- RОЫl1ИЛJl'ТОР С#), 1; ПОМОЩЬ1f.l Koтoporo МОЖНО создавать
RОМ:nОНОВОЧНЬ1е блоки .NEт. Указанный файл В,Х;ОДИ'I' в КОЮIJlект поставки .NEТ
Framewnrk 2.0 ЭОК. Вы. хонеЧН9 Ж~ не захотите создавать большие npиложеНiUI с
помощью' компилятора ХО~ОЙ строки, НО :umТb, кан itомпиЛJфОвать '*. св-фай·
.ttы ВРУЧНУЮ. все , же важно . Можно yпaзa:r:ь несю:шь:ко 'причин. ПО ,ROТOpыы : вы ДО1IЖ­
RЫ иметь представление 1) соотвеТС'l'вyIOщем процес~.

'.' самыы очевИДf'IЫМ ЯIiJlЯетспто, что ~Ы можете цросто не :иметь Vlsual Sttidlo
2005~

• в IЩШИК nлaнax :Может быть ИСПОЛЬЗ0ва.ние а~томаТИЗtfрованных средств


разработки, ' таких кal( MSBuJld ЮIИ NAnt.
• Вы можете отремИТI>СЯ к расширению своего ЛОНИl\olЩlИЯ си. При щполъзо­
IЩ,НИИ графичecJ:ШX средств раэраБОтни прююжеюIЙ вы все рацио mt-eтe ~­
cтpyrщии csc.exe о том • .кан: о.брабаn.rвать ИСXQДНые фaйлъJ С,. С атой TO'ЦGI
~peIn'J1 вecь~ полезно ЗJlЗТЬ~ что происходит "за кулисами",

Полезным "цобочным эффектом" раБOТbI с С$С .ехе ЯВЛfle"F(,"R ТО, ЧТО ~ будет
проще ИСШЩВЗОВаТЪ .цругиеинструменты JtомацднОЙ стро:ки. ВXDдящ;це Б НоМIlЛetn'
nocтaвlUl .NIЦ Framewo, k 2.0 эок В лроц~ссе изучения :материала этой ~И вы
увидите . что. МQGГИe очень важНЫе утилиты ОЩlЭ!JВаются: ДОСryIПiЫ TOДЬНQ из во­
мmщвой странн.-

Настройка компиnsrrора командной строки ДIUI С#


Чтобы ИЩIОJЩЗO;вать RОМПИЛЯТОР к.омандв.ой стРОlt,И для O~> нужно, чтQБываша
система, мота ~ти файл 'С8С.ехе. ECQIИ машина сконфигурирована 1Jещ>8впль­
но, то JIP~ RОМПИЛЯЦИИ файл{)Б С# Ba..t.I придется указать полный цуть J(, фащ
cSC;.exe.
чтоБы ~И;С~ могла1tOМIIilJIИраза1JЪ файлы "'.св ~дюбого:ка.тaJlQra. рЫnолните
еледу:ющи;е шarи (они соответствую'!' установке ,Б Windows ХР; в Wшdaws NТ /2000
эти ШaI"И буЦV1' аналоtичНblМИ).

1. Щeлкюrre па nп.юограмме Мой Компьютер и выберите пункт Свойства из рас·


КРЬПШIеrocя КЩl"I'е~стноro меню.

2. Вы~ите ВIOIЗ.ДJ(y Дополни.телыщ и щелкните на к.нОПRе Переменные ореды.


'3. Двойным щелчвом iUt uмe~щ ~емешlOЙ Path в окне Системные переменные
откройте о.кно ее ИЗМенения.
82 Часть 1. Общие свеДения о языке С# и платформе .NEТ

Если все было сделано правильно. вы должны увидеть список опций настройки.
ПО)1ДеРЖJШаемых компиляторОМ С#.

Замечание. В списке аргументов командной строки для любого средства разработки . NEТ в каче­
стве разделителя можно использовать - или I (например, csc -? или csc I?) .

Дополнительные средства командной строки .NET


До начала uсполъзовашш csc.exe добавьте в сuстемную переменную Path сле­
дующее значение (снова не забудьте проверить правилъностъ указанного пути).

С: \Program Fl1e.s \ Microsoft Visual Studio 8\SDK\v2. O\Bin


Напомним, что этот каталог содержит инструменты командной строки. которые
используются в процессе разработки .NЕТ-приложениЙ. После создания двух уна­
ЗaшIЫХ путей поиска у вас должна появиться возможность выполнять любую ути­
литу .NEТ из любого командного окна. Чтобы проверить правилъность внесенных
изменений, ЗЭRройте все открытые командные окна, затем откройте новое команд­
ное окно и введите в нем следующую команду, поэволяюIЦYЮ просмотреть опции

утилиты GAC. gacutil.exe.


gacutil I?

Совет. Теперь вы знаете, как вручную настроить свою машину, но есть и более КОРОТКИЙ nyn,.
Среда .NEТ Framework 2.0 SDK предлагает уже сконфигурированное командное окно, рас­
познающее все утилиты команДНОЙ строки .NEТ Используя кнопку Пуск, выберите из меню
Все Программы~Мiсгоsоft .NET Framework SDK v2.0 и активизируйте строку SDK Command
Prompt (Командная строка SDK).

Компоновка С#-приложений с помощью csc.exe


Теперь, когда машина распознает csc.exe, с помощью компилятора командной
строки С# и программы Блокнот мы построим простой оДНомо.цульНЫЙ компоно­
вочный блок. который назовем TestApp. еке. для начала нам нужен исходный код .
Откройте npограмму Блокнот и введите в ее окно следующее.

11 Простое Dpиnо_ние на HSWJCe С#.


using Sy stem;
class TestApp
{
public s tatic vo i d Main (J
{
Сопs о lе.Wri tеLinе("ПРОЕерка! 1, 2, 3");

3аверпnm ввод. сохраните файл с именем TestApp.cs в подходящем месте на


диске (например, в каталоге C:\CscExample). Теперь рассмотрим основные оlЩИИ
компилятора С#. Прежде всего нужно понять, кан: указывается имя и тип создава­
емого компоновочного блока (это может быть. например. консольное приложение с
именем MyShell.exe. библиотека программного кода с именем MathLib.dll, при-
[лава 2. ТеХН.ОJlОIИЯ со~даlН4Я прило*еНI4Й ",а ЯЗ1>lке С# 83
ЗIожеице Windows F01J115 с именем 'М~W.inАрр. ехе и т.д.}. Raждан. из .возможностей
обозначается соответствy:IOЩ-ИМ флатом, передаваемым в са с . ехе в виде ОПЦИИ ко­
МaвдRой строки (тl:Ifiл.2-.2).

Та6лица 2.2. Опции компилятора С#, укаэыцющие выходные лараматры

ОnциSl Onис~ние'

!out Используется Дnя указаНИR имеtМ СОЗДавае",рго 'IЮМПОRОВО'Jt~ого бло·ка.


ПQ умолчанию имя КОМIТOкОВОЧНQГО блока coвnaдae, с иl,tekем исходного
файла'" .СВ (в. СЛУ'Ш6 ". ·d ll) или с имвмем типа, содержаЩеiО метод
Mai г. () програММрl (8 СJ1учае +. ехе)
lt a l'get ~ ехе Иопользуется ДI1я создщ~Ю1 It:ОНСОЛЬКОГО ~Л(})ltения , ДанНЫЙ тип 8i;1Jl:OA-
НОГО файла подразумевается П'О УМОЛ''1ЭНИIO, поэтому ЭТУ опцию при nQ-
строении КОНСОЛЬНОГО приложения NlОЖНО ОПУ.СТИТl'

tt'aJL-gеt ~ 1 ibIar~' Используe-rся для ПОDтрое~lИgОд.ном(щУлЬногО КОМПЬНОВОЧi-JОГ(!Jблока '*. dll


!target :JnQdцlе' ИспоЛЬЭуе;rся дnя ПQС'Т'рtiJВНИЯ MPAYfI!'!, МодулLo1 Rвляmся составляющl'IМИ
MHorOMOAyJ1I;.HbIX компоно8Qчныx блоков (см. главу 11)
/t arg,et: wi,~e'Xe ПриложеН14я Window5 МОЖIФ строить и с пемощы() фд~п~ I ta r:get: ехе.
не флаг l t a rg,e t;winexe исключает nояlmение ·оict1а l(О~СОДИ в фоно­
вом режиме

Чтобы скомп.и.лир .Qватъ 'Ге $t Ар Р .с$ в консольное прИ.J1DженЙ'е с именем


Text.App~ еКе . переii,п;ите в ЩlТ~ОГ. соле.ржэщиti файл с исходным кодом . и введите
следУЮщую строку комщ-щ {обратите внщ.щние ЦЕ:} ТО, что флar'и командной стр<ЛCli
должны бьrrь указаны ДО IiМeJi ВXQAНl;dX qщйлод, а :щ~ после).

еэС Itarqet~exe 'I'б"st.Aрр. с:s

Здесь не указан нв:В:о флаг I out, tlОЭ"I'Ом:У :выполняемый фaiiЛ будет На3ЩiН
т e.s.t-A р р • е х е. поскольку 1tJI.aCC. :определяющий то~шу Юi:ОJ1;а прогрзммы (метед
Мai-n (,) , у наОШЩЬЦ'iается ТеБtAр,р, СледУеТ знать о том, 'iТO почти все флаги }(()М­
ПИЛiIТОра С# :И:МЩОТ СОКJmЩeн1:iЬ1е версии написания, Например. МОЩНО исдолъзЬ­
ватъ !t 1il\iCCTO /tar ge t (:все сcmращения мо}КНо увидetъ с помощью ВЩЩ8 еэс /?
в командной (:трi;>ке).

'С 'В ::: I t: аха TestApp, са

1{ тому же, поскольку флаг / t : ex~ опред~ет вывод, испо.п:ъзуемblЙ 1ЮМ1:mmrrCl ~


рОМ С# no УМОJ1'I8.lIИ1O. ДJШ компиляции 1;es tApp. c.$ можно использовать след1Ю~
щую простую строку.

<:;3:'с Test_A.pp. сs

Рис. 2•.1. ПРl'1ложеНL<tВ TestApp. в действl'lИ


84 Часть 1. Общие сведения о яЗЫке С# и платформе .NET

Ссыпки на внешний компоновочный блок


Теперь выясним, как компилировать приложение. использующее ТШIЫ. опреде­
ленные в отдельном компоновочном блоке .NEТ. Здесь. чтобы было ясно. почему
при построении указанного выше приложения компИлятор С# понял ссылку на тип
System.Console. следует вспомнить о том (см. главу 1). что ссылка на mscorlib.dll
при КОМПИЛЯЦИИ предполатается aвmoмamuчeСICU- Если же по какой-то особой при­
чине Э'ry' ссылку необходимо отключить. следует использовать флат /nostdlib.
Чтобы иллюстрировать механизм ссылки на внеIШIИе компоновочные блоки, мы
модифицируем приложение TestApp так, чтобы оно отображало окно сообщения
Windows Fbrms. Откройте файл TestApp.cs и измените его следующим образом.
using System;
// Доб. .~~е это:
using System.Windows.Forms;
class TestApp

public static void Main()


(
Сопsо1е.WritеLiпе("Проверка! 1, 2, 3");
// ДобаВ~'1'е это:
МеssаgеВох.shоw("ПриВеТ ... ··) ;

Здесь с помощью ключевого слова using С# (см. главу 1) добавлена ссылка на


пространство имен System.Windows.Forms. Напомним. что при явном указании
пространств имен. используемых в рамках файла *. сэ. нет необходимости исполь­
зовать абсолютные имена (рукам легче).
В командной строке компилятору esc.exe следует сообщить о том. в каком из
компоновочных блоков содержатся Киспользуемые" пространства имен. Так. при ис­
пользовании класса MessageBox с помощью опции /reference (которую можно "со­
кратить~ до /r) следует YIсазать компоновочный блок System.Windows.Forms.dll.

csc /r:Sуаtem.Windоws.FОЖ'IDs.dll testapp.cs


Если теперь снова выполнить наше приложение, то вдобавок к вьшоду на кон­
соль вы должны увидеть окно. подобное показанному на рис. 2.2.

ПrМIeт ...

Рис. 2.2. Ваше первое приложение Windows Forms


Глааа 2. Технол~г~ясоздаНИII ПРJ,мо,женкй на языке С# 85

КОМПИЛflЦИR множества файлов


в данном варианте придожение TestApp. ехе использует ОДИН файл и:рходнего
кода *.сэ. ВПQлне:возмож:но, ч:I!об:ывсе пmы .NET-прилОжеНИfl были пред(:т~ны
в. Oд:QOM файле ".СВ. ВО болыIIинтво Щ>оекТ(JВ :комдо1iYетсниз множества фаЙ/JОВ
'* .С;5, Ч1i'Oбы npoгра.ммньЩ код был более rиf5IЩМ. СщщзйтеlJDВЫЙ класс и помести­
те еro в DТделъиый фa:йJ;J HeHoM5g.CS.
11 ltIIacc Beи~aaap
lJs.i:ng S",'stem;
u.зiлg Sуs'tеrn.lAiпdQW.э .]'ОТпls;

class HelloMessage
{
public vo:Ld speak (:)
{
Мessag'eBox. S,how ("I1ривет ... ") ;

Теперь обновите исхоцмый клаGC '1'estApp так, чтобы в ые;мисПOЛhЗOВaJ'1СЯ этот


tIQВblЙ nЩ. а предыд.УЩУЮ логдку Windows Ebrms закоммеьrrиpуЙге.

uзiг"g SY5tent;
/ / Э~ бom.sae не ~буе!l.'cJa:
11 цsinq Syst&il1. Windows. FоЩ8 ;
сlаэs Te'stApp
{
publ l cstatic void Main()
{
СОЛ S<'Jlе.Wri ,tе'Linе ("оро:вер.ка! 1. '2 ( З");

1/ и $'l"Q '\\'O_ ,~
11 ИеsааgеВох. $ЬОМ, ( "Приве~ •.. ") ;

11 Ис,аom.sоаание JUIacca B.,I lolfe,S 8aqe:


НеllЬМезsаgе h = 11,ew Hel1oMe~~age (} ;
h. Speak. О;

С1(омцилируйте эти фaйJ'Ibl С# С UО1\1JDЩbЮ их явного укаащmя в качеСТве мод­


lЩX, фа:й,.rl()В.

С.БG: Ir :SY5tem. Wi1rldows . Forms .dll t •• upp.cs h~~OIUq. С8

В качестве адъте,рнативы .компилятор С#' прзвоrшет ИОПО:1IЬЗоватъ груцповей


символ '("}. ИНфОРМИ:РУЮщиi ('J 5с. ехе О том"ЧТfi) следует Вitлючиn. в тецущий про­
eк:r все файльr .. . ~a. содержащие~я в naпке npoекта:

С5'С I r ;s.yste.m . Windows. F.опщ;. dll .,.. са


Резу:lIЩ',ат выnoлнеmtR НОВОЙ црограмм.ы не буде1'Отличать.ся от предыдущего,
EдmrСТВdНJП:ifМ отличием 3Т1-1ХДВУХ npиложений бу~ Т<ЩЬКQ ТО. что тenepb исхо­
Jwый код разделен на два фай.ла:.
86 Часть 1. Общие сведения о языке С# и платформе .NET

Ссылки на множество внешних компоновочных блоков


в связи с рассматриваемой темой возникает следующий вопрос: "Что делать,
если при использовании с s с . е хе нужно сослаться на множество внешних KOM~

поновочных блоковТ Просто перечислить все компоновочные блоки, используя в


качестве разделителя точку с запятой. ДJI.я рассмотренного выше примера не тре­
бовалось указывать множество внешних компоновочных блоков, но вот соответ­
ствующий пример.

ese /r:Syatem.Windowa.Forms.dl~;Syatem.Drawinq.dl~ *.еэ

Работа с ответными файлами csc.exe


Очевидно, что при создании сложных С#-приложекий из командной строки
было бы очень неудобно набирать ВРУЧЦУЮ все флаги и ссылки, необходимые для
указания множества компоновочных блоков и входных файлов *.cs. Чтобы умень­
шить объемы ручного ввода. компилятор С# допускает использование ombe1TU-lblX
файлов.
Ответные файлы С# содержат инструкции, которые бу1JYТ использоваться ком­
пилятором в процессе компиляции входного потока. По соглашению это файлы с
расширением *. rsp (сокращение от response - ответ). Предположим, что вы соз­
дали ответный файл TestApp. rsp. содержащий следующие арryмеиты (как видите.
комментарии в данном случае обозначаются символам i).
,. Э'1'0 оorвеor1UolЙ ф&й'n
# д;n:_ Теа tApp. ехе иs ГЛaJIlol 2.

• СCIoIЛХИ на внешние ItОМnОНОВОЧНlolе блохи:


/r:System.Windows.Forms.dll
,. опции BloIВoдa и ф&Й.JDol дn_ ltоМПИJIRЦИИ
# (здесь испольsуеorс_ групповой символ) :
/target:exe /out:TestApp.exe *.cs
Предполагая, что QТOT файл сохранен в каталоге с компилируемыми файлами
исходного кода С#. мы можем построить наше приложение с помощью команды.
ПОRазанной ниже (обратите внимэ,ние на использование символа @).
ese @TestApp.rsp
При необходимости можно указать несколько входных файлов *. rsp (скажем,
ese @FirstFile.rsp @SecondFile.rsp @ThirdFile.rsp). При таком подходе
следует учитывать то, что компилятор обрабатывает командные опции в порядке
их поступления. поэтому apryмeHTЫ командной строки в последнем файле *. rsp
могут "переопределить" опции предыдущих ответных файлов.
Учтите и то. что флаги. указанные явно в командной строке до ответного файла,
будут ~переопределены" теми флагами, которые будут указаны в соответствующем
файле *.rsp. Так. если вы введете

esc /out:МyCoolApp.exe @Testf\.pp.rsp


Р1ава 2. Технология СQздания ПР.ИЛОЖ{JНИЙ НIi 1\'3),11\6 С# 87
то именем :КОМI101'lовочноroблона псе равно будет Tes:tApp.exe (а Не MyCoolApp .еХ$).
пO€;кольt,ty 11 oTBe:rнoм файле Te.stApp.l'Sp ук,ащш фJiar /out :ТеstАрьц~.хе. Но еGЛИ
УШ13ат.ъ флш пос:ле ответного файла. то уже флаг ОDЮНИТ опции ответного фail:лa.
.1
ТаЕ, в результате выполнения ·сдедующеЙ "Команды КО1liПIОВОБО'<tНb1;Й бло:к получит
I
имя MyCoolApp. ехе.
С'ЗС @'I'e-<;:tАрр.rsр /out ;1!fyCoolApp. еже

замечанке. Флаг /reference Я'J;lЛЯЕПСЯ J<YМYЛЯТИВНЫМ . Н6за8ИС~МО ,от того, где вы укажете
внешние ~омnрнов0чныБл\).I(ии (до,. :ПОDле ИЛL1 8~УТРИ о,-вет.ного файла). результатом будет
обьедйненwв- всех ССЬiJIОХ.

Ответный фам, используе, мый по умолчанию (osc.rsp)


БотнощeнIЩ ответныХ файлов CJIeдyeT ;mать то, Ч'ГО ком:пилыор с# Юvlеет OT~
Вe'J1НblЙ файл, используемый по умолчaнmo,. э-roqщй.J:r еэс .EBp~ размещенный в ТОМ
Ж8 kЗТaJ,Iоге. ч"то и СЗО.е х е (соответствующим каталогом может быть. например,
C:\Wlndows\.1'1icr9BCoft.NET\ Fr:amewDtk\v:2.0. 50215). Если oтъ.-pьrrь файл с:в с.твр С
помощью npогрЭмм:ы 'Блокн.от. вы УВIfЦИТ~. ЧТО 11 ~eм: с помощью флага / т; уще
yr<aэa1-I це;IJЪШ набор RОМI10НQВОЧНЫ" б1JOlФВ ,NE'J:
Прц КОЩПОl;f911RС С#-npоrpаммы с помощью ,: :sC;: .6.xe ·ccъmкa на этот файл В1.!-­
полн.цется автоща'I1ИЧесхи. даже кОГДа вы ytWЗЫЩLe<l'С СВОЙ файл *. r эр, С УЧ~ТОМ
ответного файла. испол:ьзуеМQГ(J ПО умолчанию. наш~ приложение Tes tApp.e;xe бу­
детycnennro скоМпилировано и при ИСПQJIЬ30в-a.-щцJ СJщцующей команды (ТЩ< IЩ1f в
csc.rsp есть С()ы1П~а на 'System .Windows.J'orms .d11).
с &С i G bl t ; Tes.tApp . е1{е ... . С5
Ec1nr НУЖНQ 01"КЛЮ'ПJТЬ авТQмаПJЧеское чтение файла cS{: .rs p. CJ1eдyeт указать
ОIЩИЮ /т!Осолfiщ.

С5С @1;est-App. rsp /noconfig

ltомпиля.трр командной стрОRИ С# ~MeeT мвощество друтих оПnИЙ. кoтopы~


можно цсполрзQiз-ать ДЛЯ управления пр:оцессом генерирования RОМ:ЦОНОВОЧнЫХ

блоков ,.NEТ, Если вам требуется более подробная 1Цlформация о функцианальныx


возможностях csc.e-ке.11рОчитайте мою статью ~Wогldпgwith the С# 2.0 СОПШlапd
LIne CompЦeг~ (Работа с JiОМlIИЛJJТОРОМ tl;оман:дщэIr CTpi1IOI С# 2.0). которую монщо
1ЩЙ:rИ на страницах httр:/ lmsdn.щiСI Q5 0ft.сот.

О'тладчик командной 'строки (cordbg.exe·)


Прежде -чем переЙТ!I R раССМDТpеJШЮ возможностей :КОМIlОЦОВКИ C#-iIрил:оже­
ни:й с помощью ThxtPad. слецует отметить. что .NEТ Framework 2.0 ~DК пре.цлаг,а­
ет ()'DlaДЧИR }{ОМaндlfой строки c;:o.l·dbg .exe. ЭтотинструмеI-JТ имеет ЩIQж.ество оп­
ЦИЙ, которые uо.зволюот ВЬДlолнить OTlI8;II;Кj' I<т.ШQповочноtо бдока:. "'ITO(5bl увццеть
епвсо:К эт.их arший. ИСnОJIPзуЮ-е фщ!г ./?
.cotdЬQj n
88 Чаоть 1. Общие сведения о языке С# и ~латформе .NET

в табл. 2.3 ПOlсаззны некоторые (но. конечно же. не все) флаги с указанием их
сокращенных форм. раcnознаваемые отладчиком cordbg. ехе в сеансе отладки.

Таблица 2.3. Некоторые флаги командной строки отладчика cordbg .ехе

Флаг Описание

b[reak] Установить или показать текущие точки останова

del[eteJ УдалИТЬ одну или несколько точек остановв


ех [it] Выход из отладчика

g[o] Продолжить отладку текущего процесса до следующей ТОЧI<И останова

о [ut] Выйти из текущей ФУНI<ЦИИ

р [rint] Напечатать все загруженные переменные (локальные, аргументы и т.д. )

si Войти В следующую строку

so Перейти через следующую строку

Большинство из вас предпочтет использовать интегрированный отлэдЧШ( V1sual


Studio 2005. поэтому я не собираюсь комментировать все флаги c;:ordbg.exe. Но
для тех. кому это интересно. в следующем разделе предлагается краткое пошаговое

описание основных этапов процесса от.ладки с использованием командной строки.

Отладка с командной строки


Перед началом отладки приложения с помощью cordbg.exe следует сгене"
рировать отладочные символы для текущего приложения. указав для csc.exe
флаг /debug. Например. чтобы сгенерировать данные отладки для придожения
TestApp.exe, введите следУЮЩУЮ команду.
esc @testapp.rsp /debug
В результате генерируется новый файл. в данном случае с именем testapp.pdb.
Без соответствующего файла *.pdb использовать cordbg.exe тоже можно, но при
этом в процессе отладки вы не сможете видеть исходный код С# (что, как правило,
важно, если вы не хотите усложнять себе жизнь чтением программного кода CIL).
Сгенерировав файл *. pdb, откройте сеанс отладки. указав для cordbg. ехе свой
компоновочный блок .NEТ в виде аргумента командной строки (при этом файл
*.pdb будет загружен автоматически).

cordbg.exe testapp . exe


Начнется режим отладки. и вы получите возможность применять любые допу"
стимые флаги cordbg.exe в командной строке (cordbg) (рис. 2.3).
Чтобы выйти из режима отладки eordbg.exe. следует просто ввести exit (или .
сокращенно. ех). Если вы не являетесь стойким приверженцем использования ко­
мандной строки. вы предпочтете использовать возможности графического отлад­
чика. предлагаемого интегрированной средой разработки. В любом случае для по­
лучения дополнительной информации обратитесь к разделу в документации .NEТ
Framework 2.0 SDK. посвящешfOМУ cordbg.exe.
Г71вва '2.. ТехнолоNtи еоздэнн!! nри.nажениЙ на языке' С# в9

,1

РИс. 2 •.3. Оm8ДКЭ nрИ'ложеliИR [) ПОМОЩЬЮ cordbg,exe

Компоновка .НЕТ -приnожений


с помощью TextPad
Бесп.i1'атНJ:iI;Й редаRlТQР БлоКRО~ neco1l(ReRИo. подходит;цля создания простых
прОl',РаММ .NEТ. :tш о;н не может ничего предложить ДJЩ повышения. nponзводителъ­
H~n 'труда рщ!работ'чИКЗ. ХОрошо. кorдa peдaI<ТOp . ~ ПОМОЩЬЮ которого создают­
ся файлы '*. СЭ. поддерживает (lt.aR М~) .вьщедение цветом ЮnOчевых СЛОВ 1!I
блопов npoграммного .хода. а также предлагает :интеtрaдmo с компил.ятором С ,#.
1Цщ и ~eдYeT ожидать. такой ин:струмеиr еуществу~т-' Это'ThxtPаd.
Редактор 1eXtPad можно использовать для созд;цrnя и компиляции програм­
мцоro кода HeTOJIЫro на яro.rкe С#. но и М}.1QrИX; друI:ИX яаьшах hpоrраммироВЗ1lИЯ.
Dta.внoe пр~~ес:tво этого 11pOдV1(ТЭ. заключается в тои, что он. с ОДНОЙ стороны.
очень пр()Щ' в иcnom.эовamш. а с дРугой - обеспечивает ДDcтaTo~HO широкие воэ­
МОЖЩ1СТИ для упрощения процесса ооэдания програм:много вода.
ЧтоБЬ! шmучить Te.xtPad. перейдите на стрa.н:иuy http~ I /www. textp~d, сол\ и
загрузите текущую версша этого peдaRТOpa (во время с(')здaiIи.я нашей КШIr8,Щ'О
была версил 4.7.3). Установив ЗТО"1' редЗIqQР. вы сразу получите полноценную ,вер­
сию 1С1Шl'ас:1, с .поJIFi:blМ набором е!'о ВОЗМQЖНОСтеИ. J-ю3НаЙте. что ЭТОТ продукт не
бесдлатен. Пока вы не иynкге лицензию (ее OТOllМOCТЬ окало $30 ДЛJi одното П(!)ЛЬ·
аовil-rеля:).:вы будете видеть "дружеские напоминания- npи 1iWIЩОМ запуске ЭТОГО
JIpИ!I,ожеНИя.

Активизация цветовой схемы С#,


ИВЩ!чRлыrо редактор ThxtPad не настроен fЩ .ao~ КJIlC\чевых CJЮв С# И РаБО1У
С РВС, еже. Чтобы настроиrъ ero СОoтвeтcтвyIOЩШ4 образом, нужно устэ.НOIЩТЬ ~ДXO­
дmцee расширенИе. Orкpойте crраницуhttр~1 /WilW .textpao., com/aM-ons/$УI19.~g .html
иза:грузите фэйJ.I osharp.8.,zip по ссылке С .. ~005'. Соответствующее расширение
90 Часть 1, Общие сведеttия о языке С# и платформе ,NET

учитывает новые ключевые сло:еа. :е:ееденные в С# 2005 (в отличие от файла, загру­


жаемого по ссылке С#. в котором учитываются только возможности С# 1.1).
Развернув архив с shаr р 8. zi р. поместите копию извлеченного файла
csharp8. syn в подкаталог Samples каталога инсталляции Thxtpad (например. в
C:\Program Files\TextPad 4\Samples). Затем запустите ThxtPad и с помощью
New Document Wizard (Мастер создания нового документа) выполните следующие
действия,

1. Выберите ConfigureqNew Document Class из меню.


2. В:еедите имя С # 2. О в поле редактирования Document class пате (Имя доку­
мента нласса).

3. Затем в:еедите *. cs в поле редактирования Class members (Члены класса).

4. Активизируйте подсветку синтаксиса, выберите csharp8.syn из раскры:еаю­


щегося списка и закройте окно мастера.

Теперь вы можете настроить поддержку С# в 1extPad. используя узел Document


Classes (Классы документа). доступный из меню ConfigureO::>Preferences (рис. 2.4).

General optlo".'
Fie •... ---------.
j-.a
o MaII1Iain indent~on
Ed;tor ; {. . . . . . . . . . H . H . . . ~
'11
О A!tomatic<li!ynder1l Ь1ООО
, О
О
Indude tri>i!ing 'Расе. when SeIeCtirJ<;j
SIлp trallI'1g орасео from lIneO when
D.efautt I '
ltl I ! О ''\е.. irt ,"еь browser
, i
[~ Бi""'У' О Wnt. lJr;CO<Je and UТF·I!ВOM
I :
it1 u.nmand R_ О Word wщ> 10119 .,..
;tj SeO!Ch
I
.
'

~ \Vord"""PPed te>:l
.I I
(±J С#
с,,_ !
I
I
oSave.,dh по I>re;!ks in hne.
1 О Save WitI1 haro b!eak.
Cola~ I
~ Check speIIlПg af:
Fant
PJinting
Synt""
Tabuiatibn O!tloro Ьм"" aI COUm ~ [[]
гfi с-с- Dlwtt1hese !~I<>" ~ daua
iiJ J.ПМL у"
ОК )1 ~]

Рис. 2.4. Установка параметров редактора TextPad

Настройка фильтра файлов * .CS


Следующим шагом конфигурации является создание фильтра для файлов ис­
ходного кода С#. отображаемых в диалоговых окнах Ореп (Открытие документа) и
Save (Сохранение документа).

1, Сначала выберите ConfigureO::>Preferences из меню. а затем- элемент File Name


Filters (Фильтры имен файлов) дерева про смотра.

2. Щелкните на кнопке New (Создать). а затем :ев едите С # в поле Description


(Описание) и * .cs в текстовый блок Wild cards (rpyrшовые символы),

3. Переместите свОй новый фильтр в начало списка. используя для этого кноПI<у
Move Up (Вверх). а затем щелкните на кнопке ОК.
ГЛi\ва 2" ТеХliО1JОГИЯ СОЗАани~ приложений на языке С# 91
Создайте новьrй файл (испоцщуИте Flle~New} 1J 'сохраните его в, подходящем мe~
сте на ДИСl(е (например. в пашю
C:\'1extPadTestApp) Щ)Д Нменем ТехtРаd'I'еs,t.СЭ.
Затем введите тривиалыюе onредerrение хласса (рис. 2.5).

class Progpm
(

CottэQle.writeUne("Н.?;J,.... UDfl1 Te.'ct}'Ii!.;"r.


~.Re.i.:!l1n!!\,!:

РМС. 2.5. файл TextPadTest.c-g

ПОАключение csc.exe
Последним дз основпых m:a:гoB RoRфиrypации PMaI<ТOpa TextPad будет связь с
еве .ехе. которая поэвол;ит КОмnи.iШpоватъ с#~файл:ы. с этой Ц~ МОЖНО. напри­
мер, выбрат'ь ToolsQRun из меню. Вы увидите диалOl~овое OЮIQ. lЮторge позвОлит
унааать ИМЯ соответствующей npoI]>a.'dмыI и необходимые флarи командной СТрО­
КИ. Тш(, чтобы CK~poвaть TextPadTest.c:s в вьmоЩ;tЯемъЩ RОНСО.п:ьиый файл
.NEr, выпо.IIНИJ'e CJlедyющnе marи.
1. Вве,ците ПОJII;{ЫЙ ПУТЬ к файлу свс.ехе в текстовое поле СоmrТlC!пd (Команда).
например С: \Windоws\Иi c-rosoft .NEТ\Frarnew6rk\v2. О .502И\сзс.ехе.

2. НеОбходимые опции 1roМatiДНой строки введи'rе в текстовое поле Parameters


(Параметры)-нanрпмер. /otJt:myApp.€xe ~ ..cв. Дляynpощ~ния npoц~cca
настройки МОЖ1Ш ужаэать O!Г.вeтныji файл (например, @rnyInp.ut.rsp}.
3', В ТCRC1"QBOM Поле Inltial folder (Исходный ~~TaдDг1 y:кaжiI"re ldiTailOt, содержа­
щий входные фaibIы {для нашего ПРЩdера это C;\Te-хtРаdТе5tАрр).

4. Если вы XQтите ,. чтобы редактор Thxtpad Зах.Батьтвал BbIВOД КОМПИJIлтора


(а не цщщзывал его в отделЪ!юм командщ:>м ОRJЯе). уе.тзнови:.re флажок Сэрtuге
Output [Захват вывода).

На рио. 2.61юltазаны все необходимы;е ДДН нашеrо цримера установки компи­


ДfЩИИ.
92 Часть 1. Общие сведения о языке С# и платформе .NET

Run I:EI
~~ ;@\W~S~.~Ei\~Z;21
br,,-.:~mylw.-·~ .--- .. -- ]
....·f...·, 'jC:\TIOdPadTeot1Q:> J
OIP~c.1i;".rd 0~1!!*
Da..t.OOS ......... an . . D~~

I ОК J( ~ I [ е.... ]1,--J:leb---'
Рис. 2.6. Установка параметров команды Аил

Свою программу вы можете запустить либо с помощью двойного щелчка на


имени ее выполняемого файла в программе Проводник Windows. либо с помощью
выбора TooIs9Run из меню редактора ThxtPad. указав туАрр.ехе в качестве теку­
щей команды (рис. 2.7).

Run ~,

~
"--.а:
t;i;p-;';~~-:---~--- -~=-~_ ~
C~ .. ~_~_._._.'::~_-'- ]
. .
1n(jS1!*Ior.. ~\TooctP!ldT~ --~ _О]

D~~ 0CII*W~
Dao.oos ......... ..,_ OfQ1МOnUod

Г of< i j c.пceI I [ I!ra_.. I Г!-:Нojp~--'


Рис. 2.7. Указание редактору TextPad запустить туАрр.ехе

После щелчка на кнопке ОК вы должны увидеть вывод программы (MHello from


ThxtPad"). отображенный в документе Command Results (Результаты команды).

Ассоциация команд с пунктами меню


Редактор ThxtPad также позволяет создавать пункты пользовательского меню.
представляющи:е заданные команды. для выполнения компиляции всех С#-файлов
в текущем каталоге мы создадим НОВЫй пунит меню Compile С# Console (Консоль
компиляции С#) в меню Tools (Сервис).

1. Сначала выберите Configuгe~Preferences из меню. а затем- элемент Tools де­


рева просмотра.

2. С помощью кнопки Add (Добавить) выберите Pгogram (Программа) и укажите


полный путь к csc.exe.
З. Вместо еБе. ехе можно указать для меню более информативную строку. - на­
пример Compile С# Console. - щелкнув на соответствующем имени. после
чего следует щелкнуть на кнопке ОК.

4. Наконец. выберите ConfigureqPгeferences из меню еще раз, но на этот пере­


йдите к элементу Сотрilе С# Console узла Tools и укажите значение *. СБ в
поле Paгameteгs (Параметры). рис. 2.8.
r
Глзвй 2. Технология СОЗА8ИИЯ 'nРИJ1OJкеflИЙ 118 яаJ,lке С# 93

O~fer-,-"" 0с.,. ....


OIbl'--..кI Dщ.р...OIIIU~-""'"
OS-4 ............... 0S01.N *1 whm CII СМ d

, ]

еж

Рис. 2.В~ СОз,цaнJIIEl эnеме,нта MeHIO Тооlэ

Т~перь вы получите возможность компилировать все файлы С# из те«ущего }Цi­


ТaJщга с помоЩью нового пункта:меню TooIs,

ИСПОЛhЗ0вание фрагментов программного кода С#


Перед ИСШШЪЗ0в<1нием 1extPad следует упомяну-тъ еЩе 06 одном бееnлатво:м,
рзсшИрений. которое ВЫ можете установит~. Перейдите на с-rpаницу ht tp: 1/
ww:w.tex-tраd. , соm/аdd-CII1э/сl1рliЬs.Ьtm,1 й загрузите фaЙJt с.эhаrр _l,zip с
библиотекой фрагментов C;/t. доторую дредлагает ШОН Гефарт (Эеап Gephardt).
Извлеките иа архива файл с: s h а rp . t с 1 и поместите 'этот файл в поДКаталО}'
Samples. Снова запус~ ThxtPi:td. вы обиэfJYЖm'e новую бибJ1ИОТ-еку фрЩ'М~нтов
nPО1'р8ММ1rого IФда С Sharp Helpers, ДОС'I'Упную из рас:крывающегося списка Сliр
LiЬгагу(БиБЛlIотека. фРCU-ментов), рис. 2.9. С ПОМDщьюдвойвоro ще.лчка lЩ любом
из ее алемен-tов вы мож~ге доб~11> соответCТIiующий пporраммный lЩД С# :в той
wчне .aRrИБноrо докумеI;lта. r,цe в f{Щ':ТР.ящий МОМеНТ находm;ся курсор,

Рис, .2.9. Фрагменты npbrpaMMHoro kOдa с# В TextPad

Наверное, вы не станете возражать. 'iJ"O 110 срзвtЩНИЮ с программой Блокнот


и RОмандн:ой етpakОЙ испольэовauие ред.антора "Ib."tPad- шах в прави.льном на­
правлении, Однако ~t:Pad (пока что] не ~ДJIaТaeт ВОЗМоЖ1-iОС-ТН IntelliSen~ ДДЯ
.

94 Часть 1. Общие сведения о языке С# и платформе .NET

программного кода С#. графических средств разработки, шаблонов проектов и


средств работы с базами данных. Чтобы представить такие ВОЗМОЖIЮСТИ, рассмо­
трим слецующий инструмент .NЕТ-разработки: SharpDevelop.

Компоновка.NЕТ-приложениЙ
С помощью SharpDevelop
SharpDevelop является интегрированной средой разработки с открытым ис­
ходныIM кодом И богатыми возможностями. которые вы можете использовать для
создания компоновочных БЛОRОВ .NEТ на основе С#, vв .NEт, Managed Extensions
для С++ или CIL. Кроме того, что эта среда разрабоТRИ совершенно бесплатна,
следует отметить то, что она ЦeлиRом создана на язьmе С#. Причем вы можете
либо загрузить и СRомпилировать необходимые файлы *.сэ самостоятельно. либо
использовать готовую программу setup.exe, RОТОРая установит SharpDevelop
на вашей машине. Оба дистрибутива можно загрузить со страниц h t tp : //
www.icsharpcode.net/OpenSource/SD/Download.
После установки SharpDeveJop выбор меню Fileq NewQ Combine позволит указать
вид (и язык .NEТ) проекта, который вы хотите создать. В терминах SharpDevelop
сотЫnе (Rомбинат) обозначает отдельную коллекцию проектов - то, что в Visua!
Studlo называется
solutiOn, 'l:e. решение. Предположим, что вы указали С#-прило­
жение для Windows и назвали его MySDWinApp (рис. 2.10).

N",w P r O j E ' C ! ' IХ I

i'iindow. Se<vice

-- ~ - ~ " ' ~--- - ~ -~. ". ~ -- -- ~ - ~ - ~- ..


~ .~~ ". -~~~ .- -',.......~~ ," ."--"-,~ - .
-~- ~",.

•АрЩect that crates JO!\ 5PPI.<:ation with 8 \>J!t'odiм" inlelfllC~.

location:E\~';";.'~
-.< -_.
s~~\~~;J$eпl~ТЕRтЕсff~~"i~j CJ
......... ...... . _ ... ..
Г"" ~

Ne>Ii Pmiect Name:LМy_~1?~~ ~_ ~_! О Cre?Jt. <hdщ foг sout:es


o >цо create l>f'Ii!Id lIЫir
Project wШ ье creafed at СА. ,\МJi Documertllt\5МqJOevгk>p ProjeoIs\Мy$Q\"'~

Рис. 2.10. Диалоговое OКliO создания проекта в SharpDevelop


1
Г~з.ва 2. Те)(flOЛnГИЯ создания ПРI1J1DЖ&FlИЙ на flзыке С# 95
J
ЗаМe>tа~ме. приложеЮ1е $tlафI;)еvеloр S~РGИ~ 1.0 наС1:Роено на ИСПОi1ьзоваНйе kомпиЛ\пора
С# 1.1 . Чтобы испадьэова-ть новые ВQЗможнооти языка С# 2005 и прrэстраНС'f8а имен . NEТ
FmmeworR2.0, еы6ери-rеProjectQPrQjecf ,options из менlO и укаЖI11;е НО8;'Ю версию КО~ПИЛ~­
"Тора на страюще настроек Runtime/CompiJer (ерем ВЬ!f1QfI~lения/I<DМп.илятор).

Возможности SharpDevelop
Среда разраБОrrRJ1 SbarpDevelop 'Рр~длШ'ает разНообразные возможности цо­
выmениSl nPОИЗIlОДИТе.лъности труда црограммиСта. и во МНОгих Dтношеаинх эта

среда разрабоТIШ СТОЛЬ :же богата ВQЗМОЖНОСТНМИ. :как и V1S'Ua1 studio .NЪ"Т 2003
lНС1 Н,е наtтолы< •. хщс Visual StшЦо 2005). Вот список OCHOnH~ цреи:муществ
SharpDevelop:
• по,пдержка ношшлнторов С# от' Wcli'osoft и МЬnо:

• ЩIЗr4ШЕFЮСТИ lntеШSensе и раСIlIиpеиин программ:ноro нодщ

• наличие ДИfl.Jiогового окна Add Reference (Доба:вл.ение ССI:ЩRИ) для СС:ЫЛОR ,на
внеmние КOМnOllOВo'lНb1e блоки. включая КомпоноВочные блоки. установлен­
ные в GAC (G10Ьal ЛssешЬ1у СаеЬе - глобa.лbliЬJ:Й F.ЭЦI номnщlОВОЧНЫХ бло:ков)~
• нa.JIИЧИf:' инструмеЩОВ визуальНОГО проектированиц Wfndows FQrms;
• различные онна[в SharpDeve!op, они нaaъmаюТСЯ scouts - развeдчиRИ) ДJ/Я
обзора СТРУКГУРЬJnpoекта и его '.состa:вля::iOЩИX:
• и:НтеГРИРОВlШИaJI утилита браУ3ера объеIC'ГОВ '- Asseтb1y Scout (раз.ведчик
ВОМIIОНОВОЧIIЫX блоков);

• yt'ИJIИ'FЫ Дт.iЯ работы с базами' ДaщlblX;

• угилита .конвертирования прОrp3'ММНОГО ко.да С# в vв .NE1' [и паобоpO'r}:


iI интerр1ЩИ~ С NUnlt (ут:ишгга :rестироваиия .NEТ -модулей) и NАЩ. (У'ТИЛИТ~
RОМПО1iОВю;r. .NEТ);

• интеграция с ДОRYМенгаци~ .N:в."Т Fгamework ЮК

Вnечатлюоще для бесплатной JDE, не так лй? В этой главе мы не собираемся


обсуждать IШ.ЖДЬJЙ И~ yкa~ llУНlПОВ ПОдРОбно . но давайте раССМОТРИМ цаи­
более йН'тереСnЫе из НИХ. Если вас интересуют Dодробпост.и.. то за:мети~, что
SharpDevelop npедлагаеl' очеЩ:! подробную доКументацию. дocтyIIнyJO nPJ:J B~Ope
Нelp<:;>Help Topics И~ меню.

Окна проектов И классов


Создав НОВЫЙ lЮ\I4бинат; lJb! можете иеmшъ:ювать Oltно Projec1.s для nPОQМртра
файлов. ССЫЛОК и P~CYPCOB сооme'i'стцуЮщих npоек'ТоВ (рИС. 2.11).
Чтобы в текущем прое:кте оосцатъсн на, внешний ТЮМПОНОВО'ШblЙ бщж . .в шще
ProJects щелкните правоJ& :кнощщJj щ.mrи на JIИКтограмме Rеtе.геrюеs (СсЬ1lIНИ) Ji из
пшmившегося ко}fl'eКОТИОГО M~ЦP выберите Add Reference (Добавц']Ъ есыnку).l1рсле
этоrо на .8RЛадке GAC или .NET AssembIy Browser (Обзор I(О:Мnано;воЧ!iЫX бло.ков
.NEТ) вы сможете выбрать КОМДОНОВОщп;Ш блок. размещеннЫЙ. coo11Jeтr;ТEeнno., в
ОАС или в дpyJ'QM месте (РИС. 2 . 12)'.
96 Часть 1. Общие сведения о языке С# и платформе .NEТ

Рис. 2.11. Окно проектов

. ~Ye

ок

Рис. 2.12. Диалоговое окно добавления ссылок в SharpDeve10p

Окно Classes обеспечивает объектно-ориентированный взгляд на комбинат. ото­


бражая пространства имен, типы и члены типов, определенные в рамках проекта
(рис. 2.13).

G3 GII MySOWnApp
Gj. lfjJ MySOWnдt<p
s (}~

'·-0
Е! .~ М..nA>nn

• Мiin($tmQШ
;r,. Jri!i8iZ!!C"""""""t.(}

Рис. 2.13. Окно классов


ГЛаБ'а 2. ТехtIОЛОГИ~ СОЭДqния ПРИIIQJkеН\fЙ НЭ gзыке С# 97

I Двойной щеЛ'-IOIi на .любом элементе 01'RPЫВan- соответствующий файл, i1Oме,­


щая X}Ip(:ОР мьпци па определеmre эл~а.

Обзор компоновочных БЛ'ОJОВ


У:гилита: AssernbIy Scout (РазВед~ ~0МПОНОВОЧНЫХ 6ЛОlЮВ), ДOG~ из меню
View. цpeдnaraeT обзор RОМПОНОВОЧНЬЦ бщ:JКРВ, JЩ которые имеютси СCЫJII{И :в про­
екте. Это Gред('тщ> преДlщгает информацщо. н ДВУХ nа;нелях. Левая naнeльпредла­
rae:r дерево npосмотра. П~01IЯЮЩee '"войrи~ ВНУТРЬ RОМIIОНОВОЧНОro блока , чтобы
ущrде:гь Пр'остраl~ства:имен ~ <щответствующие типы (рис. 2. 14).

I "

1
1
:::::
1:1
-"--
QjJ S',"$\"emЖml
· ~. 1'1 SI'Stem:1fтd"tI
i1iI () 5~$'Ii!m
1 ftJ IJ ~ ,COfl1POl"iOrtIМйdel
'W l t W - '
ctl' (J Syslem ,!ImI.~
~ [) S~.)[rnj$~ton
(i f} .&~,xmI.JIP6!!1
• П S _,xn.lJ!",
fji 11 Sf*m.XtN:.~.~
~; . 1 "" I'~ Ш-

r1iI ~. , с., ..", . ,•.


\~ ~ ",
iilQ ~
\i! §!) R.~=
. "----_ _ __

Рис. 2.14. npOcMorp компонаl:lOЧНЫ)( блоков в


окне АээвтЫу Soout

Цравал панелъ. утилиты ПОЗВQЛ"Яет увидеть содержимое элемента, вцбранвого


в левой панели. При зтом можно увидеn. не толыю основные харwcreР.ИСТJI}tИ эле­
мента. ИСПОJJЪjyЯ ДJlЯ эror.о ВкладкуIl1fo (Информация), но И СООТВ~ТСтвующий. про­
грaммI1ЫЙ .код CIL. МоЖilо также coxpaIOIТb оиределецие элеме~та в файле XМL.

ИнструмеНТbI прое' ктирования Windows Forms


Windows Fonns являетсSI средством создания приложеНИЙ. воам.ожнoc-rи. 1ЮТОро­
ТО ' МЫ рассмотРим IWзже. А сейчас, чтобы продолжить рассмотрение SbarpDevelop,
щemrnw.rе на ярлъrnе ВКЛад1СИ Design внизу OlОШ програм:м:ного К.Ода M<a.inFor:m:. С .5.
Откроетс-н интеГрИрОБaI-lciое ою-ю проеJtТйрованил Windows RJпn:>.
С ПОМ(1)Щью элементов из раздела Windows Forms в ОlЩе Tooll> МОЖНО построить
графический интерфейс (GUI) для создаваемой фарм:ы. Ддл прuмера помеСТ~Те
один элемент типа Bl.ittOIi '(кнопка) в свою главную форму. сначала "Выбрав ПИК"
тограмму BlJtton. а затем щеЛRНУВ в окне проектuрощuщя. Для. изменения ~1Ща
любоro элемента МОжноиспалъзовать О1сно Properties (Свойства}. которое ЗRТIЩИ­
аяруe-rся с помоЩЬЮ выбора VieWQli'roperties из меню (рщ:;. 2.15). Выберите Button
из раскрывающеГося СПИСRa этого ОRНЭ. И уважите .цужные параметры ддя свойств
wtoro типа (иanpимер. ВасkСоiож: или Text).
98 Часть 1. Общие сведения о языке С# и платформе . NEТ

l.uttOnl System.windows J'onnsJ!utmn

~ i ЩI~ ~ 1ii\i
~~-----

Default
F.....
Тор. L.ft
. I . . . r.... Red
'. Backgrnundlmag. D (П""")
. Clu.esv.Hd<!tiorI TГU!!

. , С оntextМ.nо (попе)

Cursor Default

111. bкkground coIor us~ 10 dlsp\.., tat ..,d grophia


i. the tопtrоl. .

Рис. 2.15. Окно свойств

в этом же окне можно указать параметры обработки событий соответстJJYЮщего


элемента графического интерфейса. Для этого сначала щелкните на IIИR'I'Oграмме
с ИЗображением молнии (вверху окна свойств). а затем выберите из раскрывающе­
гося списка графический элемент. с которым вы хотите работать (В данном случае
это Button). Наконец. задайте правила обработки события Click (щелчок). напеча­
тав имя метода. который должен вызываться при каждом щелчке пользователя на
данной кнопке (рис. 2.16).
-
Prop"rt"" ®
buttoal System.Wi ndows.forms.Button '\i;
iТit} II l,i j i©1--------~ . ..
- ~-(f#:'

Ocaws wt. ... the mntrollS ditk~.

Рис. 2.16. Установкв правил обработки


событий в окне свойств

После нажатия клавиши <Enter> ShaгpDevelop сгенерирует npограммный код


заглушки для вашего нового метода. Для данного примера добавьте в npограмму
обработки события следующий программный код .

void ButtonClicke d( obje c t s ende r, Sys tem.Eve ntArgs е)


{
11 Поместим в загО1.l0ВОII: фориы новое сообщение .
t his. Text = " Пре кратите щелкать на мо ей :кн оп ке!";
r
Глава 2. ТеХi'lQЩJГИЯ ~е:Зl1.а~ll!~ JфlllЩjжемий на языке С# 99
теперь мО'жна запустить прОТр~ l:!a ВЫПОJШеаие (;вьфр<ill Debug~ Run из
меню). ЯСНО'. ЧТО В дщпюм С'ЛуЧэ.е при щеJ1'nte на КfЮПКе М;Ы ДОЛЖНЫ увидеть из­
меНИвшийси заголовок О'ЮШ формы.
Сейчас :8bl: ДО'ЛЖНЫ об.nадать достаТО'ЧНОЙ и:нформацией дlJЯ ТОТО. чтобы начать
:ИСПOJIьзование шпегрйроваююй среды рааработщr ~harpDevelop. Я надеюсь. чТО'
вы с'Моrли предСтавить себе ее основные ВО3МО'ЩДОСТИ, хотя, очевидно. ЭТОт ИН­
струмент может предложить ГQР.аэдо больще. чем была пoЩiвэноздесь.

Компоновка .NЕТ-приложениЙ с
ПОМОЩЬЮ Visual С# 2005 Express
Летом 2004 ТOДfl Мioro:soft предлО'жила . сов,ерше.вно новую серию ID&'ПРQДУКТОВ.
обоэначeюr,yю сдовом uExpress" (,СМ. bt tp: //msdn. microso,f t . сот! eXpres ~). На се­
ГOДJЩ вьщущено шесть пa1tе1'ов Э'rOГО семейства.

• 'Vuщаl WеЬ Developer 2005 ExjJress. ~ОблегчеШll:lIЙ'" вариант средств разра­


боТ>ки динаj\ПIчес;,ких Web-УЗ'lОВ и Web-се:рвисов XМL, ИСПО;JIЬзующих АЗР.
NEТ2.0.

• Visual Basic 2005 EJt1press. И~струмент~ ЦР9ГРаммирован:ия.. идеалыrые Al1Jl


программистов ·fiез большого опыта. 150торые хотят научиться строить .NE1'-
npmюжения с помощью дружесТВt;JU:{Оro СInl'Гakеиса V;i$Ual Basic .NEТ.

• ViSua[ С# 2005 Елргеss. V'Lsual Сн 2005 Express u vt...'>Ш11.,)# 2005 Express.


Специальные ИНС1'рументы разработки для УЧ~Я и ;шту~иаСТdв. npeAlIO-
'ЧИ'l:aIOЩИX ИЗуЧать OCHO~Ы' Иiiформатш<И в рампах I;:ИНт::щсиса соответствую­
щеro языка.

• SQL Вегиег 2'()05 E'xpress. СистеМа ynравленИfl базами ДEUlНЫХ нач:адьного


уРо1ilfШ. nреДПазнач:енная. для люБИ'rелеЙ. энтузиастов и учаЩJЦ:ся·р~работ­
'ЧИltов.

За~ечанм~. Во время подготовки ,ЭТОЙ tcМИГ:1-1 к печати семейство продуктов Expгess I! В\tIдe бета­
верСИЙ npeдnага:пось соверще!1I'!О б!аСnлатно_

По большому счeryr ПР.Qду1~ТЫ серий E1qJres& явлтотся "редуцировав:н~и' ие,р­


оиям:и их aIifJЛОl'ОВ из· VjЭI1а1 SbidIo 2005 1:1 ПР6дназначены rл.авН1:JМ образl))М ДJЩ
трбителей .NEТ и учащихся., кан и в .shazpDeve]bp. в Viзuаl С# 20015 Expre$s npeд­
лaraют~ IЩ:3J;пrnные средства. просъютра. оъ"Но проеюнрования Windows FoIП1S,
дJflЩоговое O~HO Адд References (Добавление ССЫ:IIОК). возможности IпtеШSeпsе и
шаблоны распmрения прnгpаммноro Кода. KptJМe того. в Visuш С# 2005 Ех"Ргеs-s
пр~nагает(щ несколько (очень важных) возМО'жпостеЙ. в настоящее время в
SlщrpD~Jор НfЩЭС1J1IIНЫX. а именно:

• интегрированный граф~есюа:й О17ЩЦЧИК:

• средства уцрощеюm доступа ~ Web-сеfЩЩ'~ XМL.

Ввиду того. что по 'вИДУ И пр~ испольвовшшв Vislla] С# 2005 Express


ОЧ~НЬ ПОХGЖ па VisuaI Stud10 2005 .(~ в I-IeНОТDрей сreпени на Sha.rpDevelop). здесь
обсуждеНйе указанно:й среды разработЩi не npедл.агаетсВ. Если вы ХО<J'ИТe узнать
100 Часть 1. Общие сведения о языке С# и платформе .NET

об зтом продукте больше, прочитайте мою статыо ')\n Introduction to Programm:ing


Us1ng Мicrosoft Visual С# 2005 Express Edition" (Введение в программирование с по­
мощью Мicrosoft Visual С# 2005 Ехргевв Edition), доступную на страницах http: //
msdn.microsoft.com.

Компоновка.NЕТ-приложениЙ
с помощью Visual Studio 2005
Если вы являетесь профессиональным разработчиком проrpаммного обеспече­
ния .NEт, очень велин:а вероятность того, что ваш работодатель согласится купить
для вас лучшую интегрированную систему разработки от Мicrosoft - Visual Studio
2005 (http://msdn.microsoft . com/vstudio). Этот инструмент по своим возмож­
ностям существенно превосходит все другие IDE, рассмотренные в зтой главе.
Конечно же. зто отражается и на его цене. которая зависит от приобретаемой вами
версии Visual Studio 2005. Нетрудно догадаться, что каждая версия предлагает
свой уникальный набор ВОЗМОЖНостей.
В дадьн:еЙIJIем при изложении материала книг," будет предполагатьCJI. что вы
предпочли использовать в качестве среды разработки Visual Studio 2005. С другой
стороны. и вы должны зто понимать. "по обладание копией Visual Studio 2005 для
изучения материала книги не является обязameльным. Самой большой проблемой
из тех. с IЮТОрЫМИ вы можете столкнуться в таком случае. зто обсуждение опций,
которых нет в вашей среде разработки. Но программный код всех примеров дан­
ной книги должен компилироваться с помощью любого выбранного вами инстру­
мента разработки.

Замечание. Загрузив исходный код примеров этой книги из раздела Downloads (загрузка) Web-
узла Apress (http://www . apress. сот), вы сможете открывать программный код приме­
ров в Visual Studio 2005 с помощью двойного щелчка на соответствующем файле *. sln. ЕСJ1И
вы ке ИСПОJ1ьзуете Visual Studio 2005, вам придется ВРУLIНУЮ настроить свою средУ разработки
так, чтобы вы могли ВЫполНить компиляцию соответствующих файлов *.cs.

Возможности Visual Studio 2005


Как и следует ожидать. Visual Studio 2005 содержит все необходимые средства
проектирования. средства доступа к базам данн:ых, утилиты обзора проектов и
объектов. а также интегрированную систему справки. Но, в отличие от средств
разработки. которые были рассмотрены выше, Visual Studio 2005 предлагает мно­
го дополнительных возможностей. Вот список некоторых из них.

• Средства визуального проектирования/редактирования XМL

• Поддержка разработки программ для мобильных устройств (например, для


смартфонов и КПК)

• Поддержка разработки программ для Мiсrоsоft Office


• Возможность записи изменений исходного документа и просмотра таких из­
менений

• Ин:тегрированная поддержка факторизации программного кода


)
Гiщ~э 2. rехнедОГИАСОЭДВНЙЯ nРИ11ОJК~НИЙ Аа А9ыке С# 101
• ){МL"библиотека раcmи.рeRИЙ nPОГРaммlЮГQ кода
• Визуальиые средства ЦОС'IJЮ.еИии КJ1accOB и }'-ТИЛЙТЫ теСТИрtii:QQЩrn объектов

• Окно определений nPQ1"paмм.EJ@TQ ~oдa. JЮторое rrpeдJIameТСЯ вмес.то утилиты;


Windows' Fbrmз 01а58' Vlewe:r; wiЪJ,c'7. ехе. npедлагавшейся в .NEТ версии 1.1 и
более ранних в.ерсий

Чеетно говоря. Visua1 8tud1o ~005 дредлarает так много воэмщкностеЙ. "<JТO д.лв.
их nOJ!Нoro оnисЩlИЯ требуется ~делънlШ книга (по объему больше ЭТОЙ). Наша
кнша решает другие~ада~rn. Тем fie менее, .в счел целесообразltЫМ потраТИ'lЪ не­
CKOJThК() стрaI:ЦЩ на OIЩсание !:'ШЦill[ 'ГлавНых усовершенс.т:воВЗНИЙ. IIpeдпожеШU>lX
в Vlsual Зtudiо 2005. По мере ''fТ('}ЩЯ материала книги вы будете подучать допоmш­
тeльнyI<) инФормщtmo О воз~ожв:остяхэтой интегрированной среды разраБOТRИ,

Утилита обзора реwений


Если вЫ С.11едуете УК~aI'IИЮOf Э'FОЙ главы, ТО создайте новое КOНCOJIЬHOe npилQ.­
жевие С# '(С' И:l\iеВeu Vs2005ExaцlpJe), выбр~Fil~NеWQРrоjесt из меню. УТИЛИ1'а
Solution ЕхрТогег (Утилита обзора решещm). дocryпна.п из мemo View. ПОВВOJIЯет про­
сматрШlа'tъ ююжество файлов и: ItОЩЮНОВОЧRЫX БЛОltОВ. ИЗ которых соогавленте.­
кyIЦИЙ npoeЮ' (рис_ 2.17).

,. 'гI
s Щ'~_
~. ~~Infi""s
~ ..» I\.~tnce
''!iIБ~~
-Q ~I!!m . o..~
.~ SffiI"I\.XmI
'ii\\lP~_.a

Рис. 2.11. Окно оБЗОРEl реШStIий

Обратите внимание на то, что пaпka References (Ссылки) в окне Sol'ution Explbrer
отображает СПИСОК цомц:оновоч;ны:х блохов, :на .коТ{)рЫ:е вы ссылаетес~ 11 В"асто­
ящий момент (KOНCOJ1ЪBыe npoeJ;rТБI по· умолчанию ССbIlIaIO'ГСЯ на S yst elТ1.. ct 1 ~ .
System.Dat-.а.dl1 и sуs·tещ •.XLml.dl1). Если нужно сослаться на другие компоно­
воЧНЫе БДoIOl. ЩeлRЮ1Тe правой JЦЮIIRОЙ МЬШJина IUl.П1<е ~eferences и выберите из
КOнтeI(C'tНoгO MeJ:llQ Add Reference (ДобавINъ ссылку). В появившем:ся диалоговом
ОШlе вы сможете вътбратъ вyжньtй вам :к<1МIIОНОООЧНЫЙ блок

ЗаМ.чан"." В VlsualStudlo 2пО5 noзволяеroя y~ывaтb ССЫПКИ на выполfllfемblе ((ОМПО~О80'ЖЫ8


блоки (В отличие от ViSLJal Studio .NЕТ2.00З, rде IiAaHHOM KOl'lreK~e можно было и.сrrOJiЬЗО88lЪ
только бибm.,оТ8' КИ программного фда ..... dll,).

}Iаконец. обратите внимание JIa ШIктоrрамм:у Properti'es (СвоЙс.тва) в овне


Solution ЕJфlоrег.В реЗультате двоmюго ЩeJ"lЧJ(З. на ней поНВЩiется: окно раCIПИpeн-
1IЩ'0 p.eдaJ(.Topa хонфигурaцшt. ироекта (рж. 2.18).
102 Часть 1. Общие сведения о языке С# и платформе .NET

.---=:=-=.::=::;
-- - -·c~·- . - - -- ~_----
о -._,-.-'-----. "-- ---.---
- -~'-
'

,i дpp/ication
:-- ~ -- -'--- l

i~ l
, bld Events !,

Рис. 2.18. Окно редактирования свойств проекта

Возможности изменения naраметров проекта в OIше PrOject Properties (Свойства


проекта) будут обсуждаться в книге по мере необходимости. Но даже при беглом
его изучении становится ясно, что в нем можно установить параметры безопас­
ности, изменить имя компоновочного блока. указать ресурсы командной строки и
задать конфигурацию учета событий компиляции.

Утилита обзора классов


Следующим инструментом, который мы собираемся рассмотреть. является ути­
лита Class View (утилита обзора классов), и ее тоже можно загрузить из меню View.
как и в случае ShaгpDevelop. эта утилита обеспечивает возможность обзора всех
типов. входящих в текущий проект, с объектно-ориентированной точки зрения.
Верхняя па:нель соответствующего окна отображает множество пространств имен
и их типов, а нижняя панель представляет члены выбранного в настояш;ий момент
типа (рис . 2.19).

,jJ Q Project Refer_nces


S () ",2005Ех"",*
5 т Prog-....
,jij Q 6аое Турео

Рис. 2.19. Окно обзора классов


[
Глава .2. lехмологиSl СОЭД~НИI1 лри.riожеыиЙ На ЮI:же CJ 103

.окно определений п· рограммног() кода


Если ВЫ имеете опыт лрограммированин В .NE'Т 1.]. то должны знать об .)"ТИЛИ-
1'€ Windows Fbnns Class View~r,~incv. ехе
(уrШlИl'а Qб:юра клаССОJJ WшdОW$ Fbлns).
этот Иifструыент ПQЗВQJщет задать ИJ\m .NEТ-тпna и цросмртрет!> его С#-определе­
НИе. В верСЮi ..NEТ 2,0 УГШЩТbl wi otJv. ехе уже Нет. но зато есть усоверШенство­
ванная версия этогр средств:а. интегрировammя В V1sщll С, 2005 ЕхртеSБ и VisuaJ
Sttldio 2005. СОQтветствущщее ОЮlО Code DEtfinition (Оюю Qпределе~ про.грамм­
ного K~дa) МОЖНО OTRpЫTp череЗ меню View. Пом~е курсор ~яа любой ИЗ
типов :в проrpaммно~ KOД~ С'#. и вы увидите определeIOlf: соот:веТC'ТВJ1Oщего тИnа.
Например. ес;щ 'lдeтmyT~ ~a 'tИов:е ~stлпg~ в рам}щ''{ метода Main () , будет похазано
опptделеf1не типа мО!.сса S.y;;;teIТI.Etring (рис. 2.20).

99 11!11ОР-:-"Р"се S.~"Ц
-. f \
-S 1> _,.p-UOl1. С 'Э'е~lе>d cl ~Zla 5:::::-.:.:JйЩ : ~ ~: ~·'М;f·~ J.--~ ,
:;,t ' ,..,...,- .,,~. .--, ,..'" ,-"
.: _, r~ ~~~'" St"i1!.V {Dn !>" · val"'!!';
" ,i!JJ: ~'~J':!." зtri:>q (cil.. з; [J v'а1 ЩtI I
'_>!t . .'~l. ~ " Scringt"b"x; .... yal "...}·,;
~. J, 1~,cl:;li~ sц.l.t1litы-~!:lz 1:, Lm: с6;,ш't.,) , ; '"
.~ ~~'~~r~~~~i.j~ ~c~ , .- -- - - -

Рис. 2,20. Окно Ol1ределений nporpaмMHOrn ~oдa

Утилита обзора объеJ<ТОВ


Вы дOЛЖНЬJ ПОМIШТЪ из r.лaвы 1, что В Visual StudiQ 2005 eCTI;o yтwmтa Д.IlR по­
смотра RОМnОIIOlЮЧНЫ)J. блоков, на ROторые ссылается UPOffitT; АкТИВI!!~ируйге оlUЮ
Object 8r{)wser с ПОМQЩЫOме.ню Viеwё>оtЬег Wihdows. а затем ВР1'беРИ'J"е компоНовоч­
ный блок. который вы ж~те из}"'.lить (рис. 2.21).

ИнтеГРИРО'В8нная поддержка.
фактор,изаци,и программного J(oAa
Одним из rлавных усовершенствовщпШ. лредлага~мЫ1f в VisuaJ stщ:Но 2005,
лвЛЯется встроенная поддержка фак'1'ОР:ИШЩИИ nporpaMMHoro кода. ГоворН уцро­
щенпо. фа.кториззnиs о>Значает форм;щьщ;тй -механический" арОЦССf;:УСGJвершен­
с'rnОВaюm существующего базового JЩЩI.. В прomпом процесс факторизации пред­
по.чагал orpoмные об:ъемъJ ручн()го труда. В Visaal StudJo 2005 значителънЩi часть
соответствующей работы ВbПIолняетсн автоматически. Используя меню Refactof
(Факторизация). tоотвеТСТВд'ющие крмбпнации клавИ!П . смарт-теги иjиди вы-
308М :контекстного меню !l: по)1"ощыо щелчков мы:ши . вы Moat eTe придат:ь своему

протраммном;у КОдУ совершенно новый БИД. затратив на это МИНИ~ уси:л:и'й.


в таб:IL2. 4 приведены н:еl,оторые общ»е в:оманцю факТdриэациn. расПО3ЮШаемые
в V15ual Studio 2005.
104 Часть 1. Общие сведения о языке С# It платформе .NET

~\т'~~'I~~~'
<Seiord1>

Рис. 2.21. Утилита обзора объектов в Visual Studio 2005

Таблица 2.4. Факторюация в Visual Studio 2005


Метод факторизации Описание

Extract Method Позволяет определить новый метод на основе выделен­


(выделение метода) ных операторов программного кода

Encapsulate Field Превращает открытое поле в при ватное, инкапсулиро­


(инкапсуляция поля) ванное в свойство С#

Extract Interface Определяет новый интерфейсный тип на основе множе­


(выделение интерфейса) ства существующих членов типа

Reorder Parameters Обеспечивает изменение порядка следования аргумен­


(перестановка параметров) тов

Remove Parameters Удаляет данный аргумент из списка параметров


(удаJ1ение параметров)

Аепаmе Позволяет переименовать лексему программного кода


(переименование ) (метод, поле , локальную переменную и т.д.)

Promote Local VariabIe to Parameter Перемещает локальную переменную в набор параметров


(перемещение локальной пвременной определяемого метода

в параметр)

Чтобы проиллюстриронатъ возможности npименения средств факторизации на


практике. добавьте в метод Main () следующий прогрaммный код.

static void Main(string[] args)


{
// Определение хоисолъиоrо иишерфейса (CUI)
Console.Title = "Мое приложение";
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Blue;
f"nasa 2, Технаnoгия созцаНI4Я ПРИ'ложений на Я3Ыkе С# 105
Cori'soJe . Wr i t-eLirl~ ( .. ** ......... ". *'** * ..... * ....... "' ..... 1< ... " ....' ~ ........... ..-., .... ** ." . . "" ) ;
СФnsоlе.WritЕ;Li'nе("*·"**~?i*" Э'])О ·}.юе nриложе.нИе! *",оН"''''''''};
COnS.o le. W,riteLins (....... ". **,о ."..... " .... * .... ~ ........ * *,... * **'* ... ,.... * -k-k* * .. ") ;
con'S-01е. Ба сkgrочndСоlОЕ = C.onsoleColor. Glac-k;
/I о.и~ilиие ВахiJ.'X!PlЯ JC.llliIВJOIIИ Дт'I завериtelWJl ра.бо!rW.
COn.sole. Reac:Ц.,ihe ( ) ;

Этот np~Грaммньrй IЩЦ .вполНе рабo'tоспособен, но представъre себе. ч:ro B~ хр,­


тите QтобраЖать генерируемую им подсказку в разных :мес.тах вашей программы .
Вместо того' чтобы ВВОДИТЬ вручную операторы ()пределeIЩЯ интерфейсС1, СНОВа
~, сНова, бьmо бы цдеan:ьио иметь помощника. ltO'Торый МФГ бы делать э:гоза вас.
~ счастью. в данном случае вы може'I'е npимеJIЙТ1>
JC с,-уществующему прОГрWdМНО·
му КОДУ :метод фaRтО}ШЗЗЦИИ Extr~ct Меttюd (Вьщмение метода). Сначала в онне ре­
даитора выберите все операторы nрffi"paмIOЮГО кода (за иc:wnoч~ни.ем: последнего
вызовасо:ns'ыle .ReadLirie (», Затем щелкните npaвoii taIОIПЮЙ мыши И из ПOЮlИВ~
шегося новте.кс'ГНого меню Refactor выберите оIЩЮO
Extract Methpd. в nOЯВИ;ВШ~М­
си диалоrовом окне унажите ими нового метода - Conf igureCUI (). Б реэу.лщат~
вът обн:аружите, "Л'О' теперь метод Main(} вызывает новый VI'CFJeРИРОВa:ню.rjii метQД
Соп!:' iч,urеСUl ( ). содержащий ранее выделеннъtй про.rpaмr.щый код.

сlааз Е'з::оgram
[
~;'tatic void Z,Ia:l:n (stringI ] args')
I
Conf.i.gureClJI () ;
j J Ожидание наж.аТИJiJ 1ШавИЩИ для '3.аll~рще!iИЯ работ~.
C;onsole. ReadliIle () ;
}

private atati с void СОnf:tgti1;еСЩ ()


l
IJ оupe~е.певие ~OHCO'-.O%'O ИII'1'еptеЙса (CUI)
C0l1s.01e. Ti tle "" "~e nРИд'ожеtiи-е'" "
CO!'Jsole. ForegroundColor = CQ.lliJolecolo·r , 'iellow;
СОnЭоlе .. БасkgrоundСоlоr "" COlls'oleColor . .Blue;
'.CoX'lso1e .. Wr:i teLi,ne ("'" *" ~ *..... * * ",..... * * .........** .. r.*" '"*" ** .... '* .., .* * "*"") ;
СОnЭQlе ,.W.r·.ite.ЬiDе('''** .... * " ... '* Это мое ПРИЛ.ожение! ..... ,j,*** ...... );
c'onsole .. WriteLine с"" ***,;, "** *- *т"" . ,." * "",*"" * " ... ~ . .... " *" ... "-"*'1) ;
'qon:;o le .• Bac~groun dColoiL ~ CGf1S01eCelo.I. !Uack;

'Замечание. Если вЫ хотите Зf1аrь бо:nьше


() процессе факторизации и ее псщдержке в Visua1
Stщtiо 2(105, лрочитайте мою статЬю "Refactoгing С# Code Uslng VlsualStudl'o 2005'"
(Фа~торизаЦIilIf rt:рограммнагф КОД;! С# в Vlsual Stщ:ljо 200.5), дротуnн)'ю на стран.\1Цах
hHp: //mscm • micr050ft . cdПL
106 Часть 1. Qбщие сведения о языке С# и платформе .NET

Фрагменты программного кода и окружения


в Visual Studio 2005 (как и в Visua1 С# 2005 Express] предлагаются разнообраз­
ные возможности автоматического добавления сложных блоков программного кода
С# с помощью выбора вариантов меню, контекстно-зависимых щелчков кнопкой
мыши и/или комбинаций клавиш. Число доступных расширений протраммного
кода весьма впечатляюще и может быть разбито на две главные группы.

• Фрагменты програм.много кода. Это шаблоны блоков программнаго кода,


вставляемые в том месте, где размещается курсор мыши.

• Окружения. Это шаблоны окружений, в которые помещаются выделенные


блоки операторов в рамках соответствующего контекста.

Чтобы воспользоваться соответствующими фУНlщиональными возможностями,


щелкните правой кнопкой мыши на пустой строке в пределах метода Main () и ак­
тивизируйте меню lnsert Snippet (Вставка фрагмента). Выбрав соответствующий
пункт меню, вы увидите, что указанный программный код будет добавлен автома­
тически (нажмите клавишу <Езс>. чтобы СI<РЫТЬ всплывающее меню).
Если вместо этоГо щелкнуть правой кнопкой мыши и выбрать пункт меню
Surround With (Окружить с помощью ... ], вы увидите другой подобный список опций.
Найдите время и без спешки исследУЙте встроенные шаблоны расширений про­
граммнаго кода. так как с их помощью можно значительно ускорить процесс раз­

работки программ.

Замечание. Все шаблоны расширений программного кода представляют собой XMl-описа­


ния программного кода, генерируемые средствами IDE. В Visual Studio 2005 (и в Visual С#
2005 Ехргезз) вы можете создавать свои собственные шаблоны. Подробности этого про­
цесса описаны в моей статье "Investigating Code Snippet Technology" (Исследование тех­
нологии применения фрагментов программного кода), которую можно найти на страницах
http://msdn.microsoft.com.

Средства визуального проектирования классов


в Visual Studio 2005 есть возможность конструировать классы визуально
(В Visua1 С# 2005 Ехргевз такой возможности нет). Утилита Class Designer позволя­
ет просматривать и изменять взаимосвязи типов (классов. интерфейсов, структур.
перечней и делегатов). включенных в проект. С помощью зтого инструмента можно
визуально добавлять., модифицировать и удалять члены типов. а результаты мо­
дификации будут отображаться в соответствующем с#-файле. Аналогично, если
изменить данный с#-файл. соответствующие изменения будут отражены в окне
диаграммы классов.

для работы с этими возможностями VisuaJ Studio 2005 сначала нужно создать
новый файл диаграммы классов. Это можно сделать по-разному. и один из вариан­
тов - щелчок на КНOIше
View Class Diagram (Просмотр диаграммы классов). которая
размещается вверху справа в окне Solution Explorer (рис. 2.22).
После этого вы увидите пиктограммы классов, входящих в ваш проект.
Щелчок на изображении стрелки будет по:казываьь или скрьшать члены ТШIа (см.
рис. 2.23).
r
Глава 2. Т~ХНОJ10ТИЯ с:озцаН~1Я f1l11lWоже.н I1 И ttа. RЗЫкг С#· 107

a~
':i.1 ~l!\fD.",
ii1 !Q Referen=
, .:1 .$!NI!1
·0 Sy_ .'!II>ta
.Q 5~K'"

Рис. 2.22. СQэда~е ФI!J'iIlа диаграммы классов

Рис. 2.23. Просмотр диаrpаммы клаО(;/i)В

Э1уynшиту удобноиспольэо~ать с двумя другими :воэ,мОЖНОСТЯМИVisuа1 Studio


2005- окном Сlз.Ss Details (ахтивизируетс.я из Merцo ViewQOtherWindoWS) и .p~дe­
лом Class Deslgneг панели инструм:ентов (ав.ти:вющруется из :меню V·iew~Toolbo)().
ОIO.l.о Class Details не roлыю щща.зъmает струк.туру выбранного в наСТOЯIiJ;ИЙ момент
элемента дцагрэммы. 80 и поавол.лет .МРДИфИЦЩJооа'rЬ СуЩествующие и вставлять
новЫе ч:дeньt ~aeca (рис.2.24).

-.• .• ., .... , - ~ .. " ' . ~I ••

e l"l~

'" JjfIПIIII уо1с!


I.Т\II SIrn!!IfJ

Рис. 2.24. Окно содержимоrо КЛаССОВ

Паве;:IД I:IHCTpyмeHTQB [рис. 2.25) поэ:волне:г с ПОМОlЦью визуальНЫХ средств


щ:-rавлятъ в проект НQI"ы~ ТИПЫ из раздела Class Oesigner {и создавать связи МеЖДУ
этими 'типами). (чтобы э'r!)т раздел панели :инструментоt! был ВИДИМЫМ. ОННО диа­
граммы КJIЗ;ССОВ дол:.ащо бi!1ТЬ юстивНbl.М_) При ЭТОМ ЮЕ автоматически создает но­
вые' Qnpeделения Q#-ти;пов в фоновом режиме.
ДтJ. пр~ера щ~ре';l'q:щ~ новый нл:асс из раздела GlaS9 Designer панет! ШIСТРУ­
Me~ron в окно диаг~ КТiaCCOB. В соответствующем диалOl'овом окне укажите
Д1lН Э!I'OГО HOBorO щщ~са m.w Car (а:втомоБНЛЪ). Затем. используя оюro CfaS$ Details.
добавьте в.класс рт:крытое СТРОRШlOе Dол.е. на.'П1ачив , ему .иМя petNa.1.tI€ (имя ;noбим­
да), каж показано !'Ia рис. 2.26.
108 Часть 1. Общие сведения о языке С# и платформе .NET

о Delegall!
4-lnheritoInce
*'- A~1iO(1

tAc~

Рис. 2.25. Вставка нового класса с помощью визуальных средств Class Designeг

Рис. 2.26. Добавление поля в окно содержимого класса

Если теперь взглянуть на определение С#-класса Car. вы увидите. что оно соот­
ветствующим образом обновлено.

public class Car


{
/1 Испоnъзо.а~ъ o~xp~e данные без необходииос~
/1 не рехонеидуе~ся:, но здесъ з~о сде.пано дnя: npoc~olJ!lol.
public string petName:

Добавьте в Ш,НО диаграммы I\Лассов еще один новый класс с именем SроrtэСаr
(спортивный автомобиль). Затем в разделе Class Designeг панели инструментов
выберите пункт Inheritance (Наследование) и щелкните на пиктограмме Iтасса
SportsCar. Не отпуская левую кнопку мыши, переместите указатель на пикто­
грамму класса Car. Если все было сделано правильно. вы должны получить класс
SportsCar. являющийся npоизводным класса Car [рис. 2.27).
Чтобы закончить построение примера. добавьте в сгенерированный класс
SportsCar открытый метод PrintPetNarne ().
public class SportsCar : Car
(
public void PrintPetName()
(
petName = "Фредди";
Сопsоlе,WritеLiпе("Имя этой машины: {QJ", petName);
}
ГЛ8'ва 2. Технология с{),зtJ,аНИI1 ПРИЛDжений на язше 0'# 109

GiI Meltiolf~
ti.(# М.fп

": " tsalr' I?]


"'Cw·
[
............... ~

РIЮ. 2.27. В~Эо/a!I1hНое ПСJЛУЧ!'!f1ие llРОИ380ДНrJГО клас!!:\} из класса, имеющеrnСFl в 'наличии

Стенд теСТИРОlвания объектов ' (ОТВ-тестер)


Еще ' одним удобным инструМентом Бизуэль}Iый рawаб()'J'IЩ р Visuai Sfudio 2005
)'jВ.i1Яетея ОТВ-тестер (Object Thst Bench - стеНД, Te.C'I1'1pOIlaDRfi абъеюuн}. ЭТОТ ин­
струмент ШЕ noзвалнет быстро соэдать экземцлнр ющеса и ВЩIЮЛНИТЪВЫЗОВ его
членов без КОМIIИЛЯЦИИ:и вьnroЛ1t~нии вСero ПРWIоже~$I. Эте очень удобltО в тех
СЛ:У-Ч<l:ЯX. J<OFД8. вы хотите проверить работу KOEtкpeтI'IOrO М{:ТQда" но в обычных
усл'ОЮ1RX дЩI этоrо требуется "пройти" через .дeиrrJ(И стрОК IJРОГp3ММI:ШI'О кода.
Дли рабоТБ! с ОТВ-'l'естером щеЛЮIите правой JffiОrшpй j'dЫlIIИ на типе. кото­
рый ВJ:d <:o~дaeTe с ЕО:м.ощь.ю 01rn.a nPОeRТИР0Ванюl. хлзссов. Например, щeJIКни-re
uращш К.Е!!mКой МЪПlIИ на типе Spor,t sCar и:из по.явившегочя кoнтexcТllOro ме:ию
выб~рите Create InstалсеQSрогtsСагО. По.\Ши'ГС'Н диалоТщюе ОJШd. которое позволит
зада'IЪ:ИМЯ вашей временной оБЪентной переменнОil (и , ecJ,IИ нужно. предоставить
ROнс,труктору н:еобхоДимые аргументы). После заверmеmm :цроцесса ВЪJ обнаружи­
те С:Щ)Й Qбъект в рамках IDЕ.ЩеЛl(1Щте право~ инощщlt МЪШIИ на шштограмме
об1>екта и вызовите Метод PrihtPetName ()(рис. 2.28).
ВЬ! увщите соо.бщение ~Имя этой мannпIЬt: Фредди', J;юторое ПOflВИтся В ViSua1
s;tцd.iq 2005 в рамках ~toнсоли Quick.

Рис. 2.28. СтеНд теОТ1llровани.11 объеКТQВ' BVisual Stud,iQ 2005


11 О Часть 1. Общие сведения о языке С# и платформе .NEТ

Интегрированная справочная система


в завершение давайте обсудим возможность Visual Studio 2005, которая по
определению должна быть удобной. Речь здесь идет об интегрированной спра­
вочной системе. Документация .NEТ Framework 2.0 SDK исключительно хороша.
очень удобна для чтения и содержит очень много полезной информации. С учетом
огромного количества встроенных .NЕТ-типов (их число измеряется тысячами) вы
должны быть готовы закатать рукава. чтобы погрузиться в глубины предлагаемой
документации. Если же вы к этому не готовы. то при разработке .NET-приложеНИЙ
вы обречены на бесконечные трудности и многочисленныIe разочарования.
В Vlsual Studio 2005 предлагается окно Dynamic Help (Динамическая справка).
которое (динамическиl) изменяет свое содержимое в зависимости от того, какой
элемент (окно. меню. ключевое слово исходного кода и т.д.) является активным в
настоящий момент. Например. если вы поместите курсор мыши на класс Console.
одно Dynamic Help отобразит набор разделов справки. имеющих отноmение к типу
System.Console.
Тю<же следует знать об одном очень важном ПОДl\аталоге документации .NEТ
Framework 2.0 SDк. В разделе .NET Development~.NET Framework SDKqClass Ubrary
Reference документации вы найдете полныIe описания всех пространств имен из
библиотек базовых классов .NEТ (рис. 2.29).
каждый 4 узел М здесь определяет набор типов данного пространства имен, чле­
ны данного типа и параметры каждого члена. Более того, при просмотре страницы
помощи для данного типа сообщается имя компоновочного блока и пространства
имен, которые содержат рассматриваемый тип (соответствующая информация
размещается в верхней части страницы). Я предполагаю. что при изучении ма­
териала данной книги вы доберетесь до очень и очень глубоко спрятанных узлов,
чтобы получить важные дополнительные сведения о рассматриваемых объектах.

~by;

ft( D",'elopm."t ТooI. ond L.m_


ltt En~'ise s..-v .... and Development
(~ Мoы. апd Emtжjd~ D~/е/ОР"'епt
6: .NEТDeve~t
Ы_ ,tEТ Framework SDtC

~{ Deblt~)
'*' дcc..sibility
1):) IEНostEl<ecut..
r:tJ Мicrosoft . Дspпеt.Snapoп
i±I Мicrosoft. ВuoId .ВUIIdEngOne
(-11 Мicrоsoft.SUIId.Fт'ame'л-or<
liJ' Мlaosoft.~.Tasks
$ Мicrosoft.Вuid . Tasks . DepЮyment . ВОоl$tr. .
""', ......""",. .. .а..< Т. . . . , , - , " , _ " ' /oobrn..... ~'
~:.~~~~~~:~~:;'%:r:~:.:; ~ ~} ::'-,_ " A~
,U} ~~СОП,~,I$ !#~ Favorf~/

Рис. 2.29. Справка по библиотеке базовых классов . NEТ


r
!
j
1

Глав,а 2. fеХНQЛОГИЯ ооздани-я при/!{}жений rta языке С# 111

Дополнительные средства
р'азработки . NEТ •прило,жений
в зaиn:юqеНJfе' хотедосъ бы обратить ваше »нимание на ряд инструменто!3 раэ­
работхи .NE'f. которые MOry"I' ДОiЮlIlIИТЬ фунхционэ.ды1lыыe щ>Змqжнос:ги ныбрашюй
вами, ЮЕ. МllOrJ'!e из упQМЯНуТЫХ эдесь ияструментов ИМ!ЖIТ O'J'RрытыИ ИСХОДНЫЙ
код. и псе он:иffiесплат.щ.I. В этой Ю-Iиге нет места ДЛ;А подроб~ого onисания этих
yтиnИ't. яО в табл. 2.5 пji>едставлены ОIIИсанив mtCтрумеитов, которые я счятi110
чрезвычайно поле~, а тaIo!re URL-адРеса. до которым можно найти дополни­
тельную информацию.

r..бпи~а2.5. Подборка ОРМСТEl разработки .NЕГ-ЛРИJ1DжениЙ

Название Описание URL-аАрес

FxCGp Эшг инструмеlfГ ВХQДИТ а разряд оБЯ.ззтеЛ!>­ ht !.p~ / / www.g0tdotne!: .с от!


ных дпя любого разработч~ка .NЕТ-прможе­ team/fxcop
НИЙ, ЗIbIнrерщ;osаннаг.о ~ совершенствова­
нии СВОJ.1Xпрограмм, FxCop ПРОВElРИТ любой
КОМnОНI'JВО4НЫЙ блOIC .NEТ на сощtleТC"rnи.е
ОФИЦЖlЛl:!НЫМ требованиям и рекомендациям
MicrO$oft .NET
Lulz Roeder's Этот усosе/i>шеНСТВОВaRНЫЙ д~){емлИJ1ЯТОрI b t. tp: 1/ W'w w .,ai s"t6. coml
RefleC10r 6раузер оБЪeuОВ , NEТ П'ОЭВQляет пр.о~на­ t'D.eder/dGtnet
ДЛЯ .NE1 лизирGlFrJТЬ реализацИЮ тобаго .NEТ -"Типа,
ИСПОЛЬ3УlOщего CJL, С# . Object Рвэсаl , NEТ
(Delphi) ИЛI1 Visuэl Basio .NEf
NAnt NАпtявляется .NET-ЗКВИ,!ЩЛентом Ant - по< http :// s ou.rce f org.e .net f
nyiтяРНQГО автомаТИ3l:1роиаJ,iНОГО средства рrоjес t зlПarJt
'СDЗДВИШl модулей' Java. NAn1nоэволяет
определять и ВЫПОЛНЯТЬ tтOAlJOO!ibIe оценарии
,I(ОМПОНОВКI1 , используя {;интаксис, ХМL

,0000 О помощыQ NDoc можно reнeplilpoвaт, b фай­ h tt:. p: I !:s Qurc efarge:. <Ie't J
лы документации ДЛЯ программного кеда pr.ojее t-s /ndoc
С# (.или , компилИрованных f<,OMnOAOBO'l!fbIX
блоков .NEr) s самых пот1,улярныx форматах
( ". сЬт MSDN, XML" HТМL" Ja~do1: и LзТеХ)
NUnit NUni1 являеТСЯ.NЕТ ,..эхвиаалентом инстру­ httр,:/Jwww.ТIJ:щit.оrg
мент.а JUni1. преДНаЭНЗ"lещюго ДJ1Я l'eCTl<lpo-
НВЮ'lЯ Jwa-модулеЙ. С ПОМOt;'цЫ0 N1Jnlt можио
ynроО1'ИТЬ "рсщеtс лроверки управляеМQГО
Ilрогрвммного кода

Vil Восп~нимайте ViI Ka~ ·с;таршего брата" раз­ h:ttP:/{WW'W'.lbot. сот


рабоrчика .NЕТ.Этот ИАСТРУмент ПРОЗИaIm,м­
рует лрограммный КОД, .NEТ и предложит ,РЯД
рекомендацИй отно.ситеnьно того, как 'YllY'l-
ШиТЬ его о ПОМОЩЬЮ фаКТОРИЗЗLIИИ, с1"рУКТУ­
рированной обработки ИСКJ1ip!-lf!НИЙ и "Т.Д.
112 Часть 1. Общие сведения о языке С# и платформе .NET

Замечание. Функциональные возможности FxCop сейчас интегрированы в Visual Studio 2005.


Чтобы в этом убедиться, выполните двойной щелчок на пиктограмме Properties (Свойства) в
окне Solution Explorer и активизируйте вкладку Code Analysis (Анализ программного кода).

Резюме
Как видите, в ваше полное распоряжение предоставлено множество новых
игрушек! Целью этой главы было описание самых популярных средств создания
программ на языке С#. которые могут ускорить процесс разработки. Обсуждение
началось с описания того. как сгенерировать компоновочный блок .NEТ, не имея
ничего. кроме бесплатного КОМIIИЛЯтора С# и программы Блокнот. Затем мы рас­
смотрели приложение ThxtPad и выяснили, как настроить этот инструмент на ре­
дактирование и компиляцию файлов * .св с npогрaммнblМ кодом.
Были также рассмотрены три интегрированные среды разработки с более ши­
рокими возможностями: сначала SharpDevelop с открытым исходным кодом , затем
V1sual С# 2005 Express и, НaIюнец. Visual Stud10 2005 от Мicrosoft. Эта глава толь­
ко коснулась всего богатства функциональных воэможностей каждого из этих ин­
струментов, чтобы вы могли приступить к самостоятельному изучению выбранной
вами среды разработки. В завершение бьш рассмотрен ряд инструментов разра­
ботки .NEТ с открытым исходным IЮДОМ, которые могут преД1IOЖИТЬ разработчику
дополнительные возможности.
ЧАСТЬ 11
языIK
программирования С#

в этой части .••


Глава З . ОСНОВЫ языка С#
Глава 4. ЯЗ,ык С# 2.0 И объектно-ориентированный' ПQДХОД
Глаl:1а5. ЦИЮl суЩествования объеlCFОВ

Глава 6. СТруктурироваfilная обработка исключений

Гпава 7. иl'fтерфейсы и коллекции

Глава 8. Интерфейоы обратного вызова, делегаты И' события

Глава 9. Специальные приемы построения типов


Гnава 1'0. Обобщения
ГЛАВА 3
основыI Я3ЬIка С#

В оспри.r:iIJМaЙТе 3rLY r.паву вак КOJtЛeJЩJUO тем. по:свmценных ОСНО:вщ,IМ вопро­


сам J;Ip~нeшtя Jlзыкc't С# и йсполъэовa1'lИЯ n.тщтфQРмы.NЕ'I~ в Q'ПЛИЧИеQТ
CJIeдyIQЩИХ глав, зцесь нет одной Ве,цу'Щеi'1 'ТеМЫ, а npе.длаг'dЮТСЯ WШIOСТрации це­
лого рида узких тем. 1<Оторые вы должны освоИ'.тъ. Это. п чщ:тности, ТИIII:iJ данных.
характеризуемые зНаЧеНИЯМИ. и ССЫЛочные JiШЫ ДЗlЩhIX, RОНСТРУКЩИИ УСЛQВНOI'd
ВhlQО~:Щ Ц ЦИ;JUIа, механизмы лриведении R объеК'1'НОМУ ТИ11У и 130сетащтJreНИЯ из
~объеК'ГНог() образа~.· POJIЪ Syste-m.ОЬjесt и 0030ВМТехдИ,~а' поетроеЮ1-Я .классов.
По ~oдy Д~ вы тав:жеузнаете. кав в рамках СИНfl'aRсиса С# обрабатываютсЯ cтpo~
I~И. ма:GCИf:!J!d. перечви н структуры.
ЧтоfЩ ИЛЛIOс:трировать баЗО13bIe npииципы прщ.lенеНШl языка, ~ рассмотрим
биfuwо1i'CКИ бааовых юraс-Сов .NEТ и UOC'I'роим рл:ц. npим:еров ПРJЩожеНИЙ. ИCIЮJIЬ~
зуя рaщ:mчныетнn:ыз пространства И:М:ей SyS'tem. В зто;й главе также рассмаТри­
lЩет~ такая RO-ванвозможность яэыI<a С# 2005', КЗ1( тип данных с разрешением
ПРИЩJматъзнач.еШJе ou 11. Haкo~. вы узнаете. как .в С# с помощью ЮlЮ'Ч:евоrо
слрва nатезрасе объединить тЙlIЫ :в o1ДeJlЬНoe пространство имен.

Структура простой программы на С#


Язык С# требует. Ч1"обы ВсИ nоrmш ДРОГРаммы содержаласъ в рю.iКах определе­
нйя. нек0ТОрого ТШIа (эсдомнИ'rе JЩ rла,вы 1, ЧТО термин mun.:и.спользуется для Qбр­
энач-ени.я любоrо элемента: MR-tj,Жества {JCJI.CICC. J.JJ1Л"!РфеЙс. cmp1JJcmypa.. neречен.ь.
делеzam1). в o:I1JIИ1n1е ()!I' с(н). 13 С# не ПО:ПЮЛ.Щ.~"n:Я- создавать глобальные фующий' и
глобалыше элементы да:е:ных. В просте;йш~й своей форме прorpамма на С# может
быть записана J;J следующем виде.

11 по CO%'~"eJIJII:) Сt-фaйn:к ИIIeJOi'LI раCDiИp8ИИ8 * . С8.


l.lВir'9 3ys,t em;
cld.'S S HelloCla5,~

!
publ ic static in't Ma.:tn (эt.ring 11 args)
t
Console .. Wri teL;:ln-е ("Mello W.o :rld! П) ;
Cons·ole . ReadLine () ;
rеt\Jrл О;
116 Чвщ. 11. Яаык про.граммир.ОR;tf\l\it С#

~Cb oд.p~~~[ '.111Щ,:кцаСlJа /8<el1o,:la:ss). nодцерЖЮ\-аюЩliЙ f}дmIСПlешIВIЙ


.м'eTQД. Щ>ТОРQiИf в:аЗl::l'а'i.ево имя Ма j.!1. ( ) • Каждо~ В'ы.ло.лниемое С#-.приложе.яие
40Щl(Нl:) ' сщreржа'l'Ь марс, оцредм.шощий :метод мaitj\.), ROторый 11It:1"10ЛЪЭуетс.я Дl1)I
rOбomщчe:I:I}Щ 'ТОЧRИ B~дa.I;I~ !\.ах .mЩИ'r~. здесь с мeтDДQM MaifJO св.яз,а­
JlЫ К;?JЮчeвъ,rе с-,,'Юва publi€ и st.a.ti.c, Пelаже БY.цyr ПРe,ll;ставлеiIЫ их форма.льRЬre
QдредеnefDiЯ. а ЩOJ:Ш Чt~0 вам достаточно:шmъ. что onqJblTble членЫ (рubliф) .fIP-
С'хуцны д:!ш ДРУГИХТШЮВ', а СТa'ГI!Чеcюre ЧJreНЫ(:sUtic J раосматриваютсJ'l на уров­
не юr.aеса (11 Ji'eJiВ,. ~не0'бъеE't"a) и ПQЭ'tl~ могут &JЗЫИaТЪСЯ: без еоа,цa:RИЯ 80001'0
экзeмn.qщ>а rщцеса.

3амечание. ЯЗ/)lk Ц#: ЯВJтяеТQЯ Я3ЫIC'О~, '1увотвитеft1;НЕоIМ к реп-ф;гру O\'fМBQJ:IOB, j.i,aпримерс, Маln
З\D,есь отл.имаетСА, от l'I1airL, а Readliue - от ~еаa:L_ihе> П~ЭТОМУ ~i1~yeT ПQД4~J)1(НУГЬ. "11'0
B~ Ю11О'Ч~аые,споеа i! C#'-СQСТQflТ из БУКВliЮlOiего pэmcтpQ (p,tufllk, lc.ck., g1aballl1 т,Д.), а
пр!')сrpа~тlШ ИМS/ii, Tl1nЫ., иМена 'lЛен~в, а также, ~ce Йi'1Т~ГР1о1РО83l'1ные а HVIXi CJ1Ott$ на.:мнaJPТОЯ
+rтo IЩrnjiwению) .с ПРОПИGНbUr ~ (НВnpl1М9p, U:m:sole. Wi.l.t~L..iD<e, system..Wiri:dow~,.
Fоrm.s.Ме5заgеВ'Ох , ~'l:Ste:m, ~t~. Эq.lСliеnt 1,1 Т,Д,).

Dдоб~о)\ ц ~!:ЛЮче.В_Ы:М: ~oвaм publ ic аз tatic. 'этот uепщ Маiп


() имеет один
rra.paмe:rp, RQТОРЫЙ в дщrвoЪfс~ае stмя€То.я: юсG:ItВQМ строк (!> t r i n:g [] а.!ч s).
В-ущстoю:n;oй MffiI,~CI:fТ вОароо обрaliЬYlm. этот<о Шсеи»а Мы обсyщn,ат1. не будем. но
следует за.м~тI.rгь. ч;ro э:roтпара:м.етр' :може--г f1pИШl1'Ъ любое число арl"УМffiПОВ ко­
маfllIНОЙ стрщ;{и(BCf\iGpe ~T уз.на.&ге. нав п~ :к.ним доступ).
~прсугра:мм:н:а.а Jюr1.mа 1ilelloClaas Q.()дерЖЙТСя: в ра:мках м.ain (). Зде.сь КСё
П~дъ..'ЗУет~ юшcr; tJb:nsol~. RОТОpblЙоupеделен в n;pОСТРШtC'гnе имен SY·5tf:>rn:. Cp~
ЩН-РЖef."f1Щ др~'ИХ 'I:1Юliов 'IШ.I имеетс!! ·с~crmй ' зJmМен1' wr i:t~Ыпе ( ) . 1iОТQpъПt.
НaR Bы мо!Щ:ете. дoM.цa"J'ЬCli. посылает T~ строку на С'I'mJдартпС1е устройс1lЮ
;В~Boдao' 8дес;ь же вызывается сал SIЗ~ ,Re,adLl.tJe (J • чт.ООы информация RОмандВОЙ
М1JОlЩ ~~О'Й. е. 2\оде сеанса О'J''lЩЦ1G1. У1зшl StuШо 2005. шжа JThl J-le иазхм:е­
те ~'ЦШ~ <.Eцt~_
ВIЩZJy 1'OI'6. что эдесь мщод ма irl () ()ыpeд~fl, к<Ш мe:roд. воз~pamaюnщй. дan~
иые ~ .i;~teger (це-~еlШЫе даниые). перед вы.'tQДОМ п3 метода IЮЭ8РElJЩl~
e'tCJI нуль tранач~ycnemнoе завepmеНИё). Нанонец, :как 'ВЫ можете ПОJW1Ъ
~~Qпр~;цеJЩmm типв. BelloClass, в языне С# .испольэуетсJ'i тот IШД lсо~рие:в,
цр.'ГОp'blЙ был пршmт в С и С++.

Ва,риации метода M:afnO


:Пре~ в;lpиaiп Ма i n () бьш Оllред~лен с ОДНИМ параметром (массивом
C;ТP(l~ я; :в~э~ращал ДЗНJШe T.mm.i1'I t. ОДFIШtоатQ tre единcmeнво воэмoa{ШlЯ фор­
ма Иji iIJ, [) • дли tIOСтр.oщnm Т'OЧЮI wroдa приложеitия МО:nrno И~IIОЛЬ308атъ Jnoбро
ИJ3 ~,p;eд;J..'1ОЩИХ си:rнaтур (о npе,фJOm»Rе'rnи. что она Gодер.житсн. В, рамнаХ{)~I{;"ЩСсэ.
I-ЫIИ о'Пр~енШr C'J"PYЮ"Y.PыJ.

11 ВО1l8р~е.ио:rQ 'D.Шa JII-EI~, масQИa "C~OX а 1QS.ч:8Clt'Зе вpr,yиеи"l'а,


риЬНс .<;:1;..а,Ц с "'9i.d Ма.iл.[ s t r lng-О ~ юgs.)
!
J
r Г.nава З, OCNOBbl языка С# 117
11 ВОS.Р~8JIOЖlО '1!ИD& .8'1', ap~OB В8'1'.
public s tatic void Maio 'O
{
I, )
1/ ВoeJipau~ 'щ!п :in1: (цааое), арХОУМ8В'1'оа S8'1' ..
p:l1bHa sta'.ti о int Main (J
!
i

Замечание, МеТt)Д !'1ai n () МОЖНО также определить, как pr ivate tч~стныи, приватнbiй), а не
рцЫ ic (ОТКРЫТIiIЙ, общедоступный) . Это будет ·Q3начаПJ, 'ПО други~ '1С:ОМПОИОlЮ'Iные бn<Жи нв
CMOтyr HenocpeAcтвeH~Ii;J вызвать ,очку ilХО.цз прилажеtll1Я. В Vlsual Studio i!D05 метод Main I )
прогр!lММЫ авТQматичеСIGI 'Оi1реАеnfl~ТСЯ, lq!К npивэтный.

Очевидно., ч.то при выБОрё. вари:анта оnpедедения .Маiп () :нужно учитывать от­
веты на следующие два ~опрооа. Во-первых, npедпола:I;a~ТСЯ .ли при ВЫnO.J1Нe1I.ИИ
прсграммы обраба:гывать предоставленные nO!1IЬ;3ona:r~ naраметры КQМaнд1J.ОЙ
.стро:ки? Ec.Iщ да. то значения параметров ДО:ЛЖЮ,ц запОМЩlатьсл в масснве строк
Во--вторых. gyЖНО ли будет по за.аерП1eFUШ работы Main () предоставить системе
возвращаемое значеЩt't"? ЕедИ да, то возвращаемым тЩIОМ да.н:ных должно быть
int .. а не \10il11.

Обработка арrументов командной СТРОI(И


Давайте из~еним II:JЩСС НеllоСlаээ так. чтобы он Mor обрабатывать n:араметр:ы
КОМaJЩНой ш·рохи.

11 П'РО8ери'1'Ъ, nepe~aвanкc. .nи lI~eв_ XOМilB ~ОЙ СТрО",


uэiпg S~'stem;

class И еll0Сlаsэ
{
pttbllc .stat:lc int Ma:iin (stri.ngLl args)
{
C.o fis.ole . Wri te.L::\:ne (" ... ...,. Аргумен'I'bl. командНой СТРО1':И .... ** . ,) ;
fQ·r (int j: "" О; i < args.Length; i++.}
е0пsоlе .Wri teLine (" Аргуме'Н!]/ .: {О I ., r aLg$ (i] ) ;

Эдесь с ПОМОЩЬЮ СВОЙСТQа tепgthобъеICТa 'Syste.m..Array провеРЯ~.Я j содер­


жит ли массив строк RaIФе -либо эле!4еНТЫ (КаН вы убеДtiтесъ в далы-I~ем. все
Щ~СИВЫ: i! С# на самОМ деле' имеют 'rJttJ System.J\t:ray и таким обрftЗOМ lJМеют об­
щее множество 'Ч.iIеНQВ). В реаулнг.ате црохода по всем элемен'1,"ЗМ .1Щ(CC}fВа их 31:Iа­
ч~ниЯ 1JБIВ(jДltrся :в ОКН(!) КОН:СО:1IИ. Apryмeвты В командной строке уш\зывaIOТСЯ тан.
иак ПI~казано на рис. 3·.1.
PI«:. 3.,1. АргумеН~ЫВЫЗО1Щ ,fТIi)и'ПQ~НИЯ '6 К!ЭМ8trдRсЩ 1Щi91(е

Вмест.о {:таIЩaРТНQI\О щщла [,/Д' ДIЩ wreра,ций ШIД JIU1ccивамп вхо.цных строк
МО1ИНО l~ШnОlIЫава.ть КЦЮ'lевое ,\;'Л'О1'IО С# 1:0'.:::еасЬ. ЭТ'ОТ эn:еМСl'rr СIП~СIlоа бу~
hoдроБШJ расс"Ма':tриваты:;,я IЩзm~., lЮ JЩТ вам пример ~() , ИСПОЛЪ3OВaНШl'.

Ij O~a'1'М'1'e 'ВИJDQI~е ИА !rO, 'WШi ирк ИIШОnUО8&J1JQ1l r ' ~Q~e.clt· ,


It иа>2 JrеОбх~ РРОI!IЕ!.Р.а. ~ 1IIaC~&:.
puhli С st~ti-l: i111;. ~in (5t;r:i/щ i 1 атчs)
t

foreacl1 (,5t.ring s .in a3:'gs)


C"JflSro Le. ~tl te.Line (~АР~~МЕЩ:J!~, 10'.} ", -"'};

Након-ец. ~С':ryп К, аргументам :комавдной строка обеспе!'lИRQе'!1 ~eeTaтк~


"ieСk"НЙ: мeroд .Gеt:бt7m.maлrlLitiеl\r9~ {l'B-ша Sj>Э1:'..Е§IJ1'.Еnvirщ.mе:nt, &~Цра.ЩаеМым
ЗВа1{ецием ЭТР,ГО ~даввл.нется .:масСИВ строк. Его первый oomм~нт ~­
руе-г IЩТЩl1I')J: содержащий np}UЮЖ~mrе. а остa.льm..rе ~е:юты JJ 'сМЗ;СОИВfi содержат
ПО O~CТ;I:1 apгyмeнТbJ КDюш'ДнОй С1'роRИ (при З'I'OМ. нет s:еобхOДhМOCТИ оцреде"
JШТЬ Д;1Щ M'e~ДI:!. Mal Q () шtрам,етр в виде мас..сива. CJj;'Po)i).

publ i ,c 'вса tlc iп t: !'!аirl · t3triЛ9" [.] a.cg's)


.1

11 По.JtYЧeние 'iP~И!L'O. с: Д0~ Sу.Ииi. ЕnПrONlliel1t.


s t:r ing [] th;eArgs = К!lvtЛЭIнriеnt. Get-Соnu:цаfidL:lп~~9's () ;
СQП8G];е .W.~i teL.ine ("11)<"1'1:> l( ЦРU:!l!Q~:еНЮ) I f О}", HleAr9s [й] ) ;

J.

и с П,ОЛ ьзsаани е ,apГYMeHJOB комамДНОИ


строкм в Visual S1udio2005
Уi>OН~ЧНЫЙ: ДО',ЛМО.flатель yzшзьmает, apryм~K'Ibl ш>мацдной CТPOfQi DjiI'W Зaдjrcxе
nporpaMldbl. БЩ)QЦ~се I?взработlШ Прилmrreнmr Вbl ''"QЖ6'~ ~. фдarи:комавд­
ноп СТpt!JЩ С дerльщ теCТJ1РGВaншi арограммы. Чтобw: сде.патв 'ЭТа в Vis\lцl StuЩD
20~5_. 8lйncщнщ'емойнЫt Щe.J1ЧGS на пиктоrpa.мме Proper1ies lС.воЙc-rва) BQG;В.e
5оlutiJ.JП Exploreг (Обзор решений) и в:ыбеpR're' BКJ1~ Debug (Оmщд!ЩJ, после ЭТG-
1'0 ~Te ~e з1il'ач.e'Flия apryмeнтoB BnollIe тe:Itcтa Comrnan'd line argUriiE;m1:$
(Aprp.rcm:w R@Ш1JДЦОЙ<С1')Ю1m). рвс. 3.2'.
)
Глава ~. ОСНОВЫ, язы~а С# 1'19
..
- -''-' -' -'- '-
=~--~

'.- .
"

i~
1!!ui6 ,SIIn,AriIart ~--"~-- ~~ /"

I ~t;;~_ 9 ---...
~_...........
''~~:i
, 1

ГD!tJWg J О Sl8'ted!:mlt~ ·Г
! ' - - -- - -- - - - ' ~)

I t ~---'J о SJ8t:w-'A!ItJtA: ! '!


[~
.', .1
t'
1.,-
~ f~ ':it:".;~ ~~~f;.":~~.1.~1
_'С: -=-=,=- ____
-::-:-:::='-:-,~~
li
-I
Рис . 3.-2. Уот.ановка apryмeНToB l(ома~IДНОЙ СТР6Р' а V.isu~1 Stud10 2005

Несколько СnОВ о классе System.Environment


Давайте раесмоТримхласс Sу, st€:Пi.Еnvi:tоnmеht подробflе,е. ЭтОТ щrасс сщдер­
жит рцц ста'ЛИЧеCЮIX членов, .позволяющих :подучить информацию ОТНОlШтелЬ,l:Ю
оперсщ~щWIОЙ сиctеМЬ1. в RОТОРОЙ выпОлняется .NEТ-приложение. Чтобы иmuo­
СТРklРОВЭ::ГЪ возможности aтo'ro RЛ8сса 1 Изме'НИТе метод Ма in ,() I! сор:тветствии со
СДеДУЮЩ~Й логикой.

public s'tati:c int мain (s.trl.ng(] щ:gs)


{

11 ИКФ6р.а:ЦIID( об~ииой c:кc~...e.


СОДЗ91,е. Wri teLine ("'Иcn ользуемая. ОС: j :O} ",
Eт!vironment , OSVer:s i ОП );

11 lCa'1'ЗJIоZ', :а 1I:o~opo. иаходиor~ ДРЮtО..-ии•.


СQП,sо lе.WritеLinе(,'f РекуЩИЙ катаХQ:Г: (О} ",
Ешtir.оnmеn t . CurrentDirect.:ory) ;
11 СпксQЖ ДИ~О.QДО. И~ да.ивсЩ кашиие.
str i ng [ о) dr.Lves = Enviro nment . 'GеtLоqiGаlD~i-'1еs () ;
f 'o t' ( i nt i = О; i < drive-$ .Length~ iH)
Сопsоlе.Writ~tiн-е(ltДиCJ< {О} : jl} ",1., driv e s[i]У;

11 :8ep~ . NEЖ-пnaтФарlA1., 'В1i1П0JIИA_а8 ивН&IIDOiа.


C.0 Tlsole. \iI,t'i te.Li P e (' "БЫПОЛJ,lя..емая :версия •NБТ ' : (О I •• ,
'Environment. Versicl'n} i

ВОзможный вариант вывода ПОКЗЗa:R на PQ:c. 3:3.


'ИО. 8.3. napeMeHIti>I:8 Оtlружеl~И,n 'зэ. рабaroй

ъm: Бу.s-tеm . Ел.vi r:onq; e:rrt '!Ю;Ц'еР~ ОI1:реДt-JJe1JШl PJ: дliIyrих ЧЛ$ОВ, a.tre 'Z'o..IIЪКO
I1редставле нных Б ,данном 11 РИМf:рt: . В :rабл', 3.1 1щRaiщиы пt:IroТ,оры:е mrr-еPf'свые:
свойства. во нettpeмeнн0 зaJ'J'iИЮ1Те вдо~еEl'1'Э.ЦИю' .юcr'F"ramеm:д'k 2.0 SDK. "ЧТО­
бы узнать noд;робпоCnL

т.щ~l4ца 3.1. НеlC'Оторые свойс:rвa S}/s'tem.El1vi rOnIRent

М-a.chl!1etfame ~.lIfЯ ~ маШИН\!!


NewLin6' Символ~реIФД€i l'Ja ~OBY\O С'tрок.у ДЛЯ текущего ClкружеfotИЯ
?toceSSOl:Ct'J:tlIlt Ч~CiJО ПРОЦ~ССQ'pоа, текущей маШИНЫ
,sу:s ,tеПr[ri.rеctD.r:у ПОЛI;IЫй' fiYТb " ои.i:iВМI110МУ катЭJТorY
1} S',H:'N';un9! ИШ! ШЩ\ап(. ~ПУСТ"8ШЕ!ГQ ДilHKoe ПРIll1JОЖetl~1е

Определение классов и создание обьектов


Теперь. 1!отда 8bl знаете () роли Ma;tn (). nереЦцем R " щдв:че DОСТроёнци o(jъеи­
'I'ЩJ. 8'0 оса оБЪеR'IНQ-ОРИeuтиpова.я.RЬ1X ВaЬTh"ЗХ Дl!Л8eтСй чei'k;ое j)aЗ1lFlИе ~ещцу
кла:сС8МIi: 1<1 объентами. Термин 1CJIGGC ЯСIlО,l1Ьау(tТГ.j{, )1)ш tщp!;ДС..леfmJ'i ЩЩЫЩJЩТe/Jlr
ClЮro типа (User-Dclined '])'ре - UD'I), (mн" e'CJIИхо:m're. шШiлона. А терМИI:i фъеlaJ1
(tр~ется: Jl,Ш1 оБС!ЗRaЧетш ЗUЗe.мtIЛяра Jl;oН1ilpeтнoro КJIfiC€a в пЭМвтП. КIIЩ"l~..во~
·ещОВQ n~"" 0 С# обе{;печива.e'l'с'По~юб ~Q3Дания 06ъeR0roв. В <m1ичпе от дpyцm оБЫIl1*
тщ:rориe.RТ'ИpQва:нных языков ('raJ<ИХ Kai(. наприМер, С-н-), в C~ НеБqзмоЖНо РЩJ'м~
сти.n ти:п ЮI.асеа .Б степе. noэтому е.слк.вы IlОПЪ1таёТёСh использtJВа:ть nep~Iн.yIO
класса. tCQ1r0PS :не. была Е:.аздааа с ЛQМОЩЬ!О эеw, :вы пол;,rm"Ге ,ашв.бку ~
Тarщм рбраэом. СJI~JOЩИЙ программ'йЫй КlO;Ц С#' ОRa.зъmaeтt:а ~доПУG·J111.LI\1tьыt;.

u·slng 8уЗ'с:€1!L;

ciass aelloClasa
{.
раЬНс srt:atic ilrIlt мaiд (!':1 t-r~tlg: [J ar'J$)
i
11 OJIIие~f ИсаС)m..)'e':tI~ R~Q8аiпl" лох........
11 ~. ~e'l' Иcnon,ltsо:аёl!"' t n~' .
Глава 3. OCfJ08'bl языка С# 121
И.еIl0сlа·s·s оС1;
е1 . ЭоmеМеthо,d () ;

Чтобы ИСПQ<лъэоватъ правиЛbl:lЫе .процедуры ДIl.Я создания об'ЬeJtТЬВ, внесйте


сле.цvю-щие измеиеИИЯ.

using .l3y.s.t.e.m;
сlаsэ H~110~1<l~$
!
publ.i'C static int Main (sti ing [) arg:S)
I
1/ Момио об'li!D_ И ссз. ._ ос5ИJtт • О~Й С'1'роке •••
HelJ:o.Cla$s с1 = new RеЙlоС:lаs.s () ;
/ I ... ИJ1М, ухаза~ об~"~.JlИе и СОSД&lOl8 • p u _ с!Рроха.х..
B€йlQClass (32;
с2' = new Неl1 o'Class (') :
.. ,
}

КЛючеJlое слово peW отвечает за В'Ыqиcле~е 'Ч)rсла байтDВ, необходимых Д1IlJ


~ада1Шого объewrа, и выделение достаточногоо~ У'щщ:вляемои ДИИaмJ<lчесвой
naмяrrи (managed пеар}. Б данном случае вы раэмещаете д:в~объeкrа '1"1ша юr:асса
Не110Сlаsз. Следует ш:шимать. что QБЪeJqНЫ~ церем:енныё CIt на самом деле на­
ЩПОТСЯ CCbиucaмu нвоБЪеRТ в пам,я;ru. а не Ф~еcmrми Объекта-1IIIЙ. Таж '<{тос1
и с2 CCblI'laIOTCsr вц унииалъ~й Щiъе.К1I't1е110СlаS5. paaм~ в JnpaБ.!LИемо:Й
динамической ТlЗМ.fJТJo1.

Роль КОflСТРУКТЬРОВ
До сщ пор объ~ты Hel1oClas:s строилиоь с ПОМiiШU>1O Т<'P~l!:mpyfCmDpa.. эй.да:юш.­
;Ю' по УМОil1ЧЦНШQ, :который. :00' определению, не }lМeeт ap:ryмeвron. Каждый класс
С# автоматцчесци Сl'щ~жа~тсS:l ТШ10IJ1>Щ понструктором, нотррЪ1Й Qbl МОЖе'.Ге при
.необходимости переоnpеделить, Этот ТИПОВОЙ lЮНСТРУКТQР ИСДО11ЬЗУется по ум:од­
ЧЦНИЮ и гарантирует.. что все ЧЛен&Х-дaщIЫе по умолчаншо ПOJ;IyЧаТ" подходящие
тиnрвые аначения ('1'аУ.ое првед(ЩИе xapa1tl.'ePНO ДЛЯ всех :констру!('щров). Сравните
ЭТО ~ ситуацией s Ct+. где неиящща~ЦiЗироваJ-щъrе двнщ.tе. укаЗbUJ8ЮТ на "мусорМ
(инщда ~едочи ов:аэывщо.тсg· очсщъ.важнымц).
Об~с rq)OMe KOHCTPYНTQpa. задВНJ-fОГО до умолчанию. массы предлaraI01!' и
другие конструкторы. Тем C<lМPIМ .вы обеспечиваете возможность. инициaaдIЗSЦИИ
СOC"I'()ЯНJJЯ об:ьекта щ) цреМЯ et'Q созда.ння:, Подобно Java и Сн, lWHcтpym:oPblB С#
.имеют има.. еоотвеТствующе<: имеf,IИ w;racca, ~оторый ОУЩ RОНСТРУИРyюr. ~ ЩЦllIИ­
](Ot'ДaHe 1Юввращ,аюТ значения (д~e значения vaid). Ниже сно.ва расс~иваетОя
тип Не1l0Сlаsэ, НО' с ПОJIЬЗоватеll;ЬС~ конструктцром. переоrrpeделe:нIO!IМ ЗВДШJ­
~ по умо~анию КOHCТPYКIТOPOM, и элементом ОТЩJЫТЫХ: стрщщl:lЬЦt дВВНр]Х.
11 НeH«Д,~~ о EOJf~leIop2JiIИ.•
w3 :Lйg В\x''5t::ertI/o

clt.s"", itеl10б;1!!~.
i
{l ~_ oa:oqw рек .;11.. . , . . . .

рм tit. s г-:rjn.g\ll$'еd~~~11: ~
/1~op, аqaчRМf.:ео~"
puЬHё J;;'ё ll о l:: iаз'!5 {1
r CoawlE;. !"'~3. t-e1.;:!.x,e ("'!#Ii1З:Е?'f:I ж;;ш;~~:t=uр_, E<dд;a.H~ ПО, Умt'JJ1Чaw.шР ....} ;

п naАаоаа.~ J;O.iiC,..""e~~·, ~lISIID'e


// е ~ D~S(1• .• ~.Я.
* Д~ ~Oc.~

~bl;J..c }IelloCIAliis (stE1Fl,q mз~ 1


f
r
С,QD$Фlе.~.i t ·",I,ll.Li.' "!ШэВ8'В IlI.'J.IН:,.З·~'J!e_;recм'1ЙI !l:t,!~~,!;'l'Q1:'! " ~~ ;­
!i::';9ie:rМe~GВlge = msg,'

// '~ф""' ;iIXQ,1I& пр~.


p El>l;:1 e ~t-..a:t!..>:, int МБ.Ь1 ~sb:in'i1 11 fl'.!·9~.Y
!
11 ·& qqaIt~~, З~' nO~,
l'iellБСНs'Ео c'l '" Jt~w 1JIe-1l0li:.lаэs f.) :
C1\Jn:s-оlе •.Wri ~1i'!!Lj:д:rt!! i "':;!Б&:JJеН'Ие L,Is'€:~ge.: {~! 'Ii.,t'i " r
·cl.~,~ss=~liW1:1 $sil3'e 1·:
11' 8Шав, а.реНfiIIIPНS~~ ·аФ~а,.
Ие 11ф: J" ~$'jii' ,;;'2 ';
r.;;2' -= :!J~ii !j,el1ФСlа,l\3 ("rrРФ'Eiер.1j:а .• 1,. lr з" 1 ..
c·oo&vl.e· . ~lNп ,t.е-ъ.i(l.е {ТfЗIi4<i~&щ'(" \1S..;>:!·~ЗS<Jge, : f 0) '\1] " ,
!:А' .. u'S'.er·~~ag,el ;
'Cm!,s/J le~ ~adliiJi'!.e ,Н
!:'~ t ll1:' t1 {J;

3'ам~.Ю;te.. ЕМи 'M'1-nup~tJsеr "'Л~ (B'.JW'Ia'A ~ :~с1wк'!'!)ры) а' DIro'lщ,iJ(ОРЫМ!,! :иМl:ifl~М/IJ" I'CЮ­
p~ ~ичаюЦ;fI ri)J1blkQ ЧIoffi'Д()jI t l1/.'11'1 i~liItiJlIК1 л~, ~ 'Сr.IOТJ3е1tJJJyIOщ.tЙ ''Wz11 kа3tЮ11l'ЮТ'
'Ii1ВfIeгp~" В rnaae'~ J')~~ 6~T PQ:cql\I'p:rpeffl'.\ ' ГI!if,l:WQt1i.Ю.•

Пра анаj1ие~ ~Q>,Цa. ~lif , ~~ b(~ 'aaмem-rь. "'11m Ш')~lC!l11J"1aор. ~~


ны:it.дo ~~ ДРQ'{.'щm:ц~'F erpc:!!(Gвtn.iy '1'iDJIlIЗ :шачеliн.6 (nYС':I'ое). ~Q .
трemrtil€.па }f).,I[~Ю. И· W в.м1llUil ю;m ~~ 'U!ЛiI:сщрyкrор OnP~ДiUI
ч.1temt SНR'tеюre. ~О~:I1ав.iR.кmю ~сша"J.'eЛeМ fрйс. З.4}.

Эам.e<lаlJМ8. f'rnоле Ot1PecIEl~ nr.;l1.lrЭfJ6ЗТЩlЫQIOOi'1JfroЩ)'ф~ра ,Qftя 'РЮ:;' ~ 101OIfC1'p~. sa-


~al.ffJl>l~ П.О )lМf:i:.m;lОЮОО, :(.:\lA~r ,УАвие~. Llrotlbl, s ~l'OM ' сnуузе у nOj'i~\'.I8~п:е~~ QQbl'КI'ПI. Q.C1'aJl00b
SO~).tHoCJ'b,I W1дВi1ail~ Э~~IЗ)11П1:iЯР1>j ~ИПВ . ~ J1DМnЩЫО II!OftC1'PYIoITOj,14-, эвд!J!:mcJIОО ЩJУМOlIq3J'I\~IO,
T~~ KOlцC~~ ~o IШIЮ 11ереаl1pe.t:!p1lиn.. ШI{ -это t:МI",IlHr:lff IJ~Щем, 1Тj::Ц1Щ!iре,
ГлаваЭ, ОСНОВЫ языка С# 123

Рис. 3.4. Простая логика конструктора

Ут-ечка памSlТМ
Если вы имеете ОlШТ программиро:в-ания на язьucе С++. то У вас в связи с' предм­
дущимипримерами программ:иощ кода МЩ'yr вознmtатъ вопросы. В -частности.
сдецует обратпrь внимание на то. "rro Me'I'O):!; Mail1 (} nrnа HeiloClass не i{Мeeт ЯВ­
ных операторов yt-Iи'tггожеюm ссыJo~~ с1 И <;2,
Это не ужасное УПУЩение. а npaвЩJo .NE'f. 5юс 1-1 прorpаммистзм: VfsuaJ Вastc
и Java. про~мистам: С# 6е требуется YUI1Чтож;ать управляемые об1;.екты явно.
Механизм сборки мусора . NEТ освобощцает память автоматичеСI<Jf. поэтому в С#
не ПQддерЖИJtается кдю-чевое СЛОВО delet-e. В ГJI8:Вe5 процесс сборки мусора будет
расClИотрен noдробlJО. До Toro :вp€меШl 13ам достаточно Знать ли.щь о ТОМ, Ч1'о сре­
да вьmолнения ,NEТ авrrоматически у:fIИ~rтожит размещенные вами ynравляемыIe
объекты.

Определение "объекта ПРИl10жения"


в 1Шстоюn;ее время ТИП НеllоСlаsз решает две ;Jадачи. Во-первых. 'эТот класс
оnpедeJШет roчку входа в цриложeш.tе (метод Main ()). Bo-ВТt1РЫХ, HelloClass под­
держивает элемец:т .дaJПIЬJX и НескOЛЪRО конструкторов. Все эrrо хорошо и синтм­
сически прэвиль:но, но H€Мi{oro стpazlНbIМможет Dощюатьс.!1 ТО> что статич-еС:КИЕ
метод Main () создает Эlt3eмnляр ТOI'Q же КJ1acca, g котором этот метод определен.

class HelloClass
r
pulHic static int Маiл (.st.rl.ng [] ад:чв)
[
Fle1l0Class сl ;: new HelloCl азз О ;

Такой IIOдходадесьи в дрyrиx примерах используется только ДlLН тоГо, чтобы


сосредо'l'O"ЧИ'tЬс.я на JIiIJJ.IOcТpaЦИ решения соответствующей зада--m. Более есте­
ственным ПОДХQДОМ 'была бы факгоризациятипа 1ielloClass с разделе:mreм его на
два отдельных класса: ИеllОСlд.SS и HelloApp. При компоновке G#-приложенил
об:f:iIЧiIО один тип используется в качестве "объекта приложem:tя" (это тип.опреде~
лнющий MeTQA Main ( »). в то время кан остальные типы и составляют собственно
lI}JИ.liОЖеНИе.
-
124 Часть 11. Язык nрограммирования С#

в терминах ООП это называется разграничение обязанносmeй. В сущности. этот


принцип проектирования программ требует. чтобы класс отвечал за наименьший
объем работы. Поэтому мы можем изменить нашу npограмму следующим образом
(обратите внимание на то. что здесь в класс HelloClass добавляется новый член
PrintMessage (».
class HelloClass
(
public string userMessage;
public HelloClass()
{ Console. Wri teLine ("Вызван конструктор, заданный по умолчанию!"); }
public HelloClass(string msg)
(
Сопsоlе.WritеLiпе("Вызван пользовательский :конструктор!");
userMessage = msg;

public void PrintMessage()


{
Console. wri t eLine ("Значение userMessage: {О) \п", userMessage) ;

class HelloApp

public static int Main(string[) args)


{
Hell o Class с1 = new HelloClass ("Эй, вы, там ... ");
c1.PrintMessage();

Исходным код. Проект HelloClass размещен в подкаталоге. соответствующем таве З.

Класс System.Console
Многие примеры приложениЙ. созданные для первых r:лав этой книги. исполь­
зуют класс System.Console. Конечно. интерфейс СШ
(Console User Interface- кон­
сольиый интерфейс пользователя) не так "соблазнителен", J(ЗК интерфейс Windows
или WebUI. но, ограничившись в первых npимерах интерфейсом CUI. мы можем со­
средоточиться на ИЛJПOстрируемых базовых понятиях. не отвлекаясь на сложности
построения GШ (Grapblca1 User Intenace - графический интерфейс пользователя) .
как следует из его имени, класс Console инкanсулирует элементы обработки
потоков ввода, вывода и сообщений об ошиБIiах для консольных приложеииЙ.
С выходом .NEТ 2.0 тип Console получил новЫе функциональиые возможности.
В табл. 3.2 представ.iIен список HeltOTOpыx наиболее интересных из них (но. ко­
нечно же. не всех).
[лава 13 . . ООНОВЫ Slзыка С# 125
Таблица:3.2. ПQдборкв членов System.Col:ls61e, НОВЫХ ДЛ!! .NEТ2.0
Член ОпксaJtИ6
ВасkgтDl1пdс.оlо± свойства, УСУQНВ8ливающие цвеТ изоБJ)Зжен-ия/ФОна Д!1Я текущего пото­
Fо!',еgrоuлdСQlоr IШВЪ!80да" Moryт получатьзнаlitени~ ИЗ nеречl'lЯ C.on$oleColor
B\.if" ferHeig11 t Оаойства, контролирующие вЬ!соту/ширину буферtfо61 области КОНСОЛИ
BufferWidth
a€lax () Метод, ВЫnOJ1l>-lЯIOЩИЙ очистку буфера и облаети ОТCJбраж.еАИЯ КОНСОЛИ
Title Свойстsо, устанамивающее заголовок текущей KOHCOn~
W'inclow8.eigh·t Овойства, контроflирующие размеры I<QНСОЛИ отщ~сителы4o э:аданного
wimdowWiclth буфера
Win,jowTop
Wind:owLeft

Ввод и BblBOA В клаосе Console


JЗдобавО}i н: ЧJl~ УЩ13;ЩНЫМ в табд. 3.2. тщ] C;:onsole опред{!Jlяет МliЮЖество
~eTOДOB. обрабатывающих -вво;дщ 'ВЫВОД, цричем ~pe ЭТ}f методы Опреде.цевы RaR
статичесн:,ие (.static). по~тому 0fU1" 1!ЫЗЫВaIOТС:Я 1ЩУРЩlНе :класса. Вы уже видели.
что WritеLiле () вставляет теJiРТQВУЮ строну (ВКilI9ЧtЦi символ~озврата RaРeткиj
11 ВЫХОДНОЙ поток. Метод Wr i t;.e (} вставлнет ~CТ в ~ЩОДНОЙ ПОТOR без возврата
lШретIЩ. Метод ReadLi.ne' (:) I10ЭВОЩi:е'Т ПОJlуqmъ ив:форыаци1о из ВХОДНОТО потока
ДО символа возврата каретки. а Read () и"пол:ьзуе'l'С:П ДJШ :захвата ОДНОГО символа
я:з входного ПO'Fока.

ЧТQбы D ро~,;;rлюстриро~ать ЬСЯ€lвные возможности вв,ода~въпюда класса


Console, рассмотрим сл~дy1OIII;ИЙ :мeтUA Main () , который запрашивает у пол:ъзова­
теJШ некоторую инфор1\'!!ЩйJO и повторяет каждый элемeвrn в llO'l'O:Re стандартншо
вывода. На рис. $,5 DоRaзан npимер .вьmoлнеmш тmroй щюrраммы.

11 ИсПоm.sоэа,кие )(Лаоса ОоПзо.lе дn;_ _ ОА& И .qOAa.


эtа:t;iс 1(o:J:q Mail1 ($·tring [] args)
{
IJ Э~ м,. Н8l1:0!JtQPWX СШРО1С..
СОlьsоlе.Wхi.tе ("Введите свое иыя: ");
strlnqs = COrr,s 0'1 е . ReadLine (J;
Сопsо1е .• Wri teLi!\e ("ЦРtlEе'Г, {О} ", 5');

Conso1e. Wri.te ("Укаl1КИте В'ОЭРВ'СТ J '1) ;


S = Сопэо~е ..ReadLine () ;
CO!Tsol€. Wr.itеLiле ("Вам [О.} год (а) /ле'I"/, З);

Рис. 3.5. Ввоц и вывод с помощЬю syste.rn.Cons'ole


126 Часть 11. Язык nрограммироваНИR С#

Форматирование консольного вывода


в этих первых главах вы много раз видели в строковых литералах символы {О 1.
{11 и др. В .NEТ вводится новый стиль форматирования cтpOl(. немного напоми­
нающий стиль функции printf() в С. но без загадочных флarов %d. %5 и %с. Вот
простой пример (соответствующий вывод показан на рис. 3.6).
static void Main(string[] args)

...
int thelnt = 90;
double theDouble = 9.99;
Ьооl theBool = true:

/ / :Код '\n' • C'1'pOIC08IIX nитерanах вwпonнке'I.' :ВСТ_lCу


/ / симвоnа перехода на H08YJD строху.
Console.WriteLine(
"Int равно {О} \пDоuЫе равно {l} \nвool равно {2}",
thelnt, theDouble, theBool);

Рис, 3.6. Множество "пустышек» В строковых литералах

Первый параметр метода Wri teLi пе () представляет собой строковый литерал.


который содержит опции-заполнители. обозначеН}{blе {О}. {l}. {21 и т,д, (нумерация
в фигурных скобках всегда начинается с нуля). Осталъ}{blе naраметры WriteLine ()
являются значениями. которые должны быть вставле}{bl на место соответствую­
щих заполнителей (в данном случае этоthelnt. theDouble и theBo·ol).
Также следует знать о том, что метод WriteLine () перегружен. чтобы можно
было указьmать в качестве значения заполнителя массив объектов. Так. строкой
формата следующего вида можно представить любое число элементов.

/ / Замена заПОnНИ'l'"елей э.nекен'I.'аки иассива об'J08Х'I.'О8.


object(] stuff ~ {"Эй", 20.9,1, "Там", "83", 99.999331 ;
Console. Wri teLine j "Мусор: (О), {1}, (2), {3), {4}, {5}", stuff);

Можно также повторять заполнитель в строке. Например. если вы являетесь


поклонником Beatles и хотите построить строку "9, Number 9, Number 9". то
можете написать следующее.

/ / Д-ОН ГОЭОРИ'I.' ..•


Co nsole.WriteLine("(O}, Number (О), Number !О)", 9);
ГлаElil З. ОСНОВЬ! язык-а С# 127

3вмеЧ8Мtt8. Если имеетоя неQоотвеТ~ТflИВ междУ чиtJюм раЗЛИЧНЫI)( заполнителей в фигурных


cr.оБКi!lj( и числом ~ПОЛI{RIO~Х их apryмel'lfDa, то в оред& 8ыполнеRИЯ генерируется искnю.че,
Jiие rоrmаtЕхс;ерtiрл .

Флаги форматирования строк. НЕТ


Если требуется более tложно,е фо:рма:тирование. каждый ЗЩlоонитель может
ДUПОЛШiтeJIЬво содержать различные символы форматироВ8}ЩSI (В .верхнем или Б
вижнем регйстреJ. как п()казано g тafur. 3.3.

Табnица3.З. Симво~ форматирования CТPQK .• NEТ

СиМItОIlW фcp.ta­
Onмcaнмe
пtpOltани" СТРО·

.с или с ИСПОIlЬЗyIO:rся Дiiя форматирования денежных Зf!ЭЧl:JflИИ . По умолчанию пе­


ред ЭТИм $лаГQМ будет размещаться символ ЛQхаЛЫНОЙ , ;ЦенеЖНо:И e.nиН~щы
~cкa*e~, знак домара [$] дЛЯ \).S. EngllShj
Dиnи d ИСlЮJlьзуются дm1 фррматирования де,еяти'jНЫХ \'Iисел. Этот флаг raкже
указывает ~нимальное чисг.tO знвков, ИCfТ.Oльэуемое А/1Я преДСТ~~~IИЯ
'значения

Е или е ИCnQЛЬЗУЮroя для представления в , ЭkспонеНЦИaJlliН(')М формате

Fили f ИОf\ОЛЬЗУIOТСЯ ДЛЯ преДC1ЩlJlения в 'формате с фиксироваННЫМ разделителем


GИЛj.l ,g, 1
Обазtlцчaюr geneтaJ (-общий формат]'). эти СИМВОЛЫ МCJжно использовать
для предстаsneния чисел в Формате с фmСИРОВЭННI>IМ разДелит~!,!М или в
эк~поненциальнаМфармвте
N ИЛl'ln ИСflОru.aylbтся для' базового числового форматирования 1с раэделени:ем
ГРУ"П раэРЯДРВ)
х или х ИспоnьэyI01'СЯ ДНЯ представлеЮ'tя а шестнадцатеринном формате. Если не·
польз~ся Х (8 ,sepXlfeM регистре), то ~ wестнадцатеРИ'ЧНDМ представлении
используются OI1мволЫ верхнеro регистра

СИМВОДЬТ форматирования Дббaвлsnoтcя в виде суфф:ююа :к соответствующему


ЗЩIWIlШ1:гелю 'Через двоеточие (например. {о: G}. ! 1: а}, {2, Х} и т.д. ). ПредпOJtожим.
ЧТО вы добавили в Маiл О сле,цуюIЦЙЙ прol;раммный код.

11 ИCnО&8YQII " • •О!1'ОpJoie А8CJ!pIШШOpJ( фориа!1'4_


static vold Маin(striлgtJ aurq's)
{

:C:ons o le', Wri t,BL i ne ("'ФОРЫЭ'I' С: {O;CJ '" . 51 ~98 9.987) ;


СОnSolе.WritеLinеl"Формат O'9~ {О:В9}", 9999'9) ;
Co'n sole,. Wri teLln~ ("Формат Е: i О, ,:"&},(, 99'9 99.7 БS4 З) ;
С;оns:оlе.WritеLinе('·Формат FЗ: {О:FЗ}"( 99 99:9.9999);
Cansole.WriteLine '''Формат '1'1: (О~N}П, 999 .SJ9~;
СОnSQlе.WritеLinе("Формат Х: (O:Xj'(, 9 9'9'9 9);
СаnSQlе.WьitеLinе{"' Формат х: (O ' :oX~". 99'9'S1 9J;
r
128 Часть 11 . Язык программирования С#

Использование символов форматирования в .NEТ не ограничивается консоль­


ными приложениями. Те же флаги можно ИСПОЛЬ30вать в контексте статического
метода String .Format () . Это может быть полезно тогда, когда в памяти нужно по­
строить строку с числовыми значениями, подходящую для использования в при­

ложениях любого типа (Windows Fbгms . ASP.NEТ. WеЬ-сервисы XМL и т.д.).


I
static vOidMain(string[] args)
{

// Испоnъзо.ание статичесхо~о метода strinq.Fоrшat()


/ / дmr посorpoeииJI НОВОЙ C'J!POХИ.
string formatStr;
formatStr =
striпg.Fоrmаt("Хотите пол учить {О:С} на свой счет?",
99989.987);
Console.WriteLine(formatStr)i

На рис. 3.7 показан пример вывода данной программы.

РМС.3.7. Флаги форматирования строк в действии

Исходный КОД. Проект BasicConsolelO размещен в подкаталоге, соответствующем главе 3.

Доступность членов
Прежде чем двигаться дальше. мы должны обсудить BOnpOC доступности. ИЛИ
"видимости" членов. Члены (методы, поля. конструкторы и т.д.) данного класса или
структуры должны указать свой уровень доступности. Если член определяется без
указания ключевого слова, характеризующего ДОС1YПRость. этот член по умолча­

нию определяется как private. В С# используются модификаторы доступности


методов. перечисленные в табл. 3.4.
Вы, наверное, уже знаете, что доступ к открытым членам можно получить
с помощью объектной ссылки, используя операцию, обозначаемую точкой (.).
Приватные члены недоступны извне по оБЪeRТНОЙ ссылке, но могут вызываться
объектами внутри. чтобы экземпляр мог выполнить свою работу [т.е. это частные
вспомогательные функции).
iвблица3.4. Ключеsые слова С#, указывающие уровень ДОСТУПНОC"rИ

Модкфмкат()р
Описшntе
доступности С#

pl\blit Помечает метод, It8.K ДШПУЩlыil! из объекrной "epeMeHHoJli. в ТЭ-ЮI<Э


!Oi~р.ытblЙ, общедооryпныи) И3 всех ПРОИ3ВОДI-IЫХ классов

р:!: i vate Помечает метод, какдоступныJii толь~ 113 класса, ОПРЕ!деляющего


('"IВ-СТНЫ:Й, приваТJ-LЫй) юот метод,' В С# любой член по уМdлчаflИЮ опрe,lJ,f!ЛЯ-етоя, как
p:c1vate
prcJ-t.еctеd Помечает метод, как доступный AN1 апр-едetlяющего IW1Baca. в' так­
(зarЩllщеНнРli1) же ДlЖ люБО'го проиэводного KJ1acca. ОДНШ<О 'защищеНные меrоды
не доступRЫ из объектной перемеННфЙ'

iпtеrпаl QпредеЛR8Т мето,!)" как достyn~IЫЙ ДЛЯ люБОго Т\llпа ,олыl:o BI:fYТpI1
(В~lугренний) данного КОМПQНРВОЧНОГо блока, нО не снаружи

protected 1 n-t е rnal Onpвдел_Я8Т метод, доступ 'К которому ограflИ~ИВЩПСЯ рамками те·
(ВНУТР~'НН-ИЙ защищенf,(Ы~) кущего КQМПОI'iОВQЧНОГО б'nокэ ИЛИ тиnам\ll, СО3дз.ННЫМИИ3 Оr1ре-де··
ляющеrо класса в данном KOMnOHOBO"IHOM блоке

3аrцищеШIЫе члfшы ока.зывa:IOТ{),fI ПОЛе3НЬL\1.И ТОЛllКО при создании иерархии


:классов, что будет темой обсуждеШUl :rnaвN4, Что Шlсаетсв :внутренних и защи~
щенных членов, то они обычно испо;льзyIOТСН при со:щании библи-оте:к протрамм­
ШiГО Koдa ..NEI' {например. упр-авл.яемЬ!х библио:гек .". dll. что ~e:! обсуждатьCJI.в
:маве 11J.
Чтобы проилл:юстр1Ipовать nри.-•.н!:НeJ1йе унааа:нных EЛlOчевых СЛОВ. создадИм
:класс (SошеСlаsз 1, в 1tOTOPOM ИCnОЛЬауЮтся все уназаЮJЬre МUДlrфикаторы доступ­
ности чле<.нов.

/1 С>цtщиД()C1rymIOсll.'И члеНiОВ.
сlазз БоmeСlаsв

11 Дoc'!!yц~ Вti!ЗД~.
publi с ~юid РЬЬ} i,~МetJlod () ! J

11 Доc:mynен 'J.'олъJtо из ТИПОВ SomeСlаsз.


private void Priv,:i__teМe~,hodO\ )

/I ,дОС'J1YПее из SomeC~ass и nO'!!QlП(OВ.


protected void PrO,teC1;eclMe-tj\JDGI() ( ]
11 ДосшynеВ>r-eЛЬi!J:О в раи1С:ах дающrp ХО:ИnОнФвClЧВОГО Опоц.
iпt-€НlaJ void IntеПJаblеthоd () [}

11 3-а"QlЩeвю.iй ;ЦОС'!!YDЗИУ'1'РИ хомnоновочиоrо б~'О1l:а.


protect.ed irit.erna:l vClid Рг.аtе(~'tеdI!1tеrпа1ме-thod () {f

11 в с. при o-тсу'!!с!l'ВИИ >laИJolX YJ(a!Jаний


11 члены .110 уКOJtЧaJUSZj С'ilИ'1'aD.rСR npиваТRЪDD'l.
void S@meMet"h..<:;,,::i О {)

Теперь, создав ЭJtземплнр ЕЛасса 'Sоm.еС.la.БВ. ПОIlЫl'.aшvlСЯ вызвать Rажды:й ИЗ


его методой. исполъзуа операцИю. О'бозначаем)'ю ТОЧКОЙ.
130 Часть 11. Язык nрограммирования С#

static void Main(string[] args)


(
/ / СоздаетCJI объеJCТ и :а~OJtНSlетсSl ПОInlТжа _ызо:аа ч.nено_.
SomeClass с = new SomeClass();
c.PublicMethod() ;
c.InternalMethod();
c.ProtectedInternalMethod() ;
c.PrivateMethod(): 11 ОшиБJCа!
c.ProtectedМethod() ; 1/ Ошибка!
с . SomeМethod ( ) ; 1/ Ошибка!

Если скомпилировать Э'I)' проrpамму, вы обнаружите. что За1ЦИЩенные и част­


ные члены вне объекта не доступны .

Исходный код. Проект MemberAccess размещен в подкаталоге, соответствующем таве З .

Доступность типов
1)шы (классы. интерфейсы. cтpYК'I)'Pbl, перечни и делегаты) также могут ис­
пользовать модификаторы доступности. но только pl1blic или internal. Когда вы
создаете общедоступный тип (public). то гарантируете, что он будет доступным
для других типов l(aк в текущем компоновочном блоке. так и во внешних компо­
новочных блоках. Это может оказаться полезным только тогда, когда вы создаете
библиотеlty ирограммного I<ода (см. главу 11). но здесь мы можем привести пример
использования этого модификатора доступности.

/ / Этот тип мо.е'1' испоnьзо_атьсSI moбым JCОМnОНО_ОЧJAD( блоком.


p ubl ic class MyClass{}

Внутренний (internal) тип, с другой стороны. может использоваться только


компоновочным блоком, в котором этот тип определен. Если создать библиотеку
программного l(ода .NEТ. в которой будут определены ВNYТренние типы, то компо­
новочные блоки. ссылающиеся на Э'I)' библиотеку (файл * .dll), не смогут увидеть
эти типы. создать их экземпляры или Kal(-TO иначе взаимодействовать с ними.
ХаРal(теристикой доступности, npинимаемой по умолчанию для типов в С#. яв­
ляется internal, поэтому если вы не укажете явно ключевое слово public. то в
результате будет создан внутреНЮ1Й тип .

1/ Эти lcJlaccы MOI'Y!t' иcnодьзова'1!ЬСSI '1!оnько -IIY'1'PИ


11 '1!еJCущего компоновочного бnоJC&.
internal class MyHelperClass()
class FinalHelperClass() // По умолчанию тип будет внутренним.

Замечание. В таве 4 будет говориться о вложенных типах. Вы узнаете, что вложенные типы тоже
могут быть объявлены, как приватные.
Глава 3, O(;!,loabl языка С,# 131

Значения, назнача,емые
переменным по умолчанию
ЧЛ!lliaМ-перемеЕ:НЬtм :классов автоматичесви ЦРU~1JaИВaЮТСj'l ТJQдх.одяпurе аца,
т.regин. лрецусмотренныe ПО умолчанию. 3ТН 8начения ДдН ~oгo типа дaIщых.
СВОИ, но правила их выбора ДОС1:'ЗТ(!}ЧНО просты:

• для тИПа bobl устанашшвает('я значение false;


• чиC1J:UВЫМ Данцым цp:цcBaJ:[Вae.тC& звачеЩi€ О (ruш О, О. е€ли это данные с ПJЩ-
вающим разделителем);

• для типа string устанaвmmaется ЗlffiчеIlИе Iшll;

• ддя типа сЬа.!:' устанашшвается: значение' \0':


• для ссылОЧНЫХ типов устанавливается значение null.
С учетом этих правил проанали.аирyliте слецую!ций прогрaм:J\ll1'I.ыii 1(0)):.
11 ПОЛ. 'DИпа xnacca ПQI!YЧ~ Звач:еним по умоn.,aRИID~
clas:s 'I"est

p1Jbli[; in1; mylf'lt; 11 УСМiЦIaJIJ1ИiВЗ,е'1'CSI pa!lИIIIIIII о.


pHblic g·triпg I'II.yS>tring; /1 УО'1!аиавnизаеORСМ раэН- null.
рuЬНс booi :шуВооl; 11 УС!N.Rцлиааеtt!СJl piUSИJoIМ false.
p1JlJli с ebj ее'!; myCbj; 11 УсмаВ&S1IИЗае!1lС. PaвJD.IН nulJ..
}

Значения, назначаемые по умолчанию"


и локаnьные переменные

Cf1DCeM Iio-друтому обстоит дело тогда, I«Iгда объявляются локальные пер.емен:­


Iiblf!. ВИДИМ:ЪЮ В 'пределах дaннo:ro члена. При onpедепении локальной пере:менной
вы доJtЖн.ыназначитъ ей начальное значение. прежде чем начать ее и.оI10ЛЬ30Ба­
ние, IlОСКО.ш.в:у такая переменнаяне получает В'ача.J:iЪНОГО зааченин по умолчанию.

Нaп:pш.;iер. следy:юIЦШi uporРЮ\IIМНЫЙ JЮД приведет "к mmIБIre компиляции.

11 ОШИбка ЖDlЩШIJЩЮI.! ПереиeщtЦ • ioca.1:[nt' Д~ J;!onyч:мml;o


JI на~иое значение до ее иcnoт.зоааКtOl.
s'ta ti<:: vo:Ld Маiл (s trillg [) arqB)
{
int lQc:alI nt;
CO!'JSole. W.rit eLin8 (100<11 Нlt);

Исправить npоблему очень просто. СЛедУет присsоить переменной наЧaJ1ЬJ:lOе


значение.

11 То nучше: !rепеJ?Ь все А!IЗOJIЪifIW.


static void Маin(з'tr:lng[] arqs)
(
int 10calInt = О;
Ce>n'S'9~e.WriteLin(;) (localIntJ;
132 Часть 11, Язык программироваНИR С#

Замечание. Правило обязательного присваивания начальных значений локальным переменным


имеет одно исключение: если переменная используется в качестве выходного пара метра (это
понятие будет рассмотрено немного позже), то устанавливать начальное значение такой пере­
менной не требуется.

Синтаксисинициаnизациичnенов-переменных
ТИпы класса обычно имеют множество членов-переменных (также называемых
полями). Если в классе можно определять множество конструкторов, то может воз­
никнуть не СЛИШRом радующая npограммиста необходимость многократной запи­
си одного и того же программного кода инициализации для каждой новой реализа­
ции конструктора. Это ВПОJШе реально. например. в том случае. когда вы не хотите
принимать значение члена. предусмотренное по умолчанию. Так. чтобы член-пе­
ременная (myInt) целочисленного типа всегда инициализировался значением 9. вы
можете записать следующее.

/ / Все 3'1'0 хорошо, но 'I13X3R изБы'1'чнос'I1ь•••.


class Test

public int myInt;


public string rnyString;
public Test() ( myInt = 9;
public Test(str i ng s)
(
myInt = 9;
myString = s;

Альтернативой может быть определение вспомогателы{ой функции. вызывае­


мой всеми конструкторами. При зтом уменьшается общее число повторений ДЛЯ
операции прu.cваuванuя. но теперь возникает следующая избыточность.

/I Все равно OC'1'ae'1'cJt избы'1'ОЧНОС'1'Ь •..


class Test

public int myInt;


public string myString;
public Test() ( lnitData();
public Test(string 5)
{
my S tring = s;
Ini tDa ta ( ); }

private void InitData()


( ту! nt = 9; )
r Глава З. ОСfЮjjЫ St,зЫJ(В ,С# 1ЗЗ
Оба 'этИ IfОДХQда вполне леtитИМПЫ. но в С# nО3В(JляеТGЯ ~знцчать ~ле.нам
типа наЧ8..1IЪНЪie значения в рамRЭX деllЛа:рации (ВЫ. наверное. з;н:аете. что дрyrие
объеКТНо-ориентированные КЗыки [например.. С++], не позволяют Тa:I<ую i!IJ:[иц.иа~
ди3IO.(Ию ·членов). В сле.цующем Фрarменте 11рограммнorо I\ода обратите вндмщще
на ТО. Ч'I'о ШIИциали~ацйя может выD$яться и ДШI. lЩУТРeRJЛЦ ooъeKTных ссы­
ЛОК. а lШ тQЛЬКО ДЛЯ числонЪU: типов даnных.

1J Ec:nи ~O OTn,S &TJo<C& О'2! s.кa"f~, 'rIpeдУСИО'1'р8ЯfDD( по ~аSИl),


11 ЭJ1l& 'l'8XВИ1ta позаonяет иs6е.а'1'Ь ПОВ'I'ориой ааtiИCИ JI1)Cl'pёUlМН.O~o
11 хо.ца ИиицkaJlИS<Щlm а X~tiJ.. ICОИС~g:oоре.
cla!;!S T'e st
{
p.tiblic iDt .rпуInt = 9;
pJJbl H :: strin-g mY-3tr = \"}ro е в.ачаЛЬJ:Ю€ ЗR,а, че l;'!Ие. ";
р иЬНе: Spo:rts G.a.r v i pe:r ~ riew .sр0r't,эСа :t (Co lo'r . Red ) ;

3а~ечанме. Иl'tиц~ализаlJИЯ 1Ij,neHOB ВI:ilПDnняется ДО выполн-еН~fI 'f1рограммtlой ЛОГИКИ "ОНI::,ТРУК;ТQ­


ра . Если' ПРI1СВОИТЬ зliачеНIiI6 полю в са.МОМ К'онсrm;кщре, ЭТО оведет на нет инициализauию
члена .

Определение констант
Итах. вы знаете, ЩiR QQЪЯВМ"ГЬ шwеМeю;LЫе Юlасса. Теперь давайте Вl>lilСНИМ. как
ооределmъ дэпньщ, »зменят:ь :которые ~(' преДПOJJагается. для определения пере­
менных с фИКСИРОВандым, неизмеЩIе1>1ЫМ 3НRчением в С# предлагается ключевое
слово cons t .• После Gпр~деJIeНИ.'I~ значения константы .то.бантroпыtка изменить это
значешrе npиводит IC Qши6ке \l\О!!IfДЩIJЩИИ. В о-тличие от е·н. н С# }(J[Ючевое СЛОВО
const НедЬ3Я укаЭ1ЦВать д.r1Я па,РЩv1етров и возвращаемых значений- оно оредна­
щючено ДJIJlGоЗДaнщI ЛОRалъl'IblX ДCЦIFIЫX и дэпfIых УРОННfI экземпляра.

ВажнQ .понимать. что эначеЩ1е. присlЮенное канстанте. :во вреJИЯ 1CO'.мntlJUI:ЦЦI1


уж~ ДОЛ:ЖNО быть ИЗJJeGТНР, цоэтому нонстанту неяьэя mпщиализироватъ объект­
ной есъJJП<ОЙ (значеJ-ше ПОCJJt:ДFreй вычисляется в "Среде ВЫПOJIВеmш).. Чтоб1:>l ПРО "
иллюстрировать ИCnОJ;lliЗЩJание IOnoчевогослова .c ,onst. рассмотрим следующий
тиn класса.

сlаsз Со пstDаtа
r
11 ~а!Jеиие I ПРИCJl&К8а_ое 1tоиС'Ж!аюre I р,()ЛJI[ИО бvи. И.9.еС~JlС
11 80 apeмst xoмmt.n~.
pubiic G:O'! 'Ist з t.riп~ BestNbaTe.aJIl = "T :Lmbe:r wolv,es";
рщ,liс COТl'st dO):lblE Simple-Pl =- 3 .14;
риЬНс сопst bp,o l Tr\:!th = trus;
p-иЬН ё con:S1;. Ью оl Falsity = lТ:nJ:tb;
J
Обратите внимание на то, "ЧЧ'О зна1{ени.я всех констант известны во Bpeм.f.r 1tQМIIИ­
JШЦШi. 11 деm::твитеЛЪНQ. еCJП1 просмотретъ эти константЪ1 С fI(i)МОЩЬJ(!)· i ldasm .€Ke,

L
134 Часть 11. Язык программирования С#

то вы обнаружите. что их значения будут "жестко" вписаны в компоновочный блок,


как показано на рис. 3.8. (Ничего более постоянного получить невозможно!)

Ffnd Ffnd Noxt


.field pUblic !itatic literal !itriпg BestNbaTealll = "HJlberlllolues"

Рис. 3.8. Ключевое слово const вписывает "свое" значение прямо в


метаданные компоновочного блока

Ссылки на константы
Если нужно соСлаТься на константу. определенную внешним типом. вы должны
добавить префикс имени типа (например, ConstData.Truth). ПОСIЮЛЬКУ поля-кон­
станты являются неявн.о сmаmuческu.мu. Однако при ссылке на KOHCТaнry. опреде­
ленную в рамках текущего типа (или в рамках текущего члена). указывать префикс
имени типа не требуется. Чтобы пояснить это, рассмотрим следующий класс.

class Program
(
public const string BestNhlTeam = "Wild";
static void Main(string[] args)

/ / Печать значений констант, определеlUWX друrиии '1'ИI1аии.


Console.WriteLine("KoHcTaHTa Nba: (О}", ConstData.BestNbaTeam);
Console.WriteLine{"KoHCTaHTa SimplePI: /О}", ConstData.SimplePI);
Console.WriteLine("KoHCTaRTa Truth: {OJ", ConstData.Truth);
Console.WriteLine("KoHCTaHTa Falsity: (О}", ConstData.Falsity);
/ / Печать значений хонстант члена.
Console. Wri teLine ("Константа Nhl: {О}", Веэ tNhlTeam) ;
/ / Печать значений хонстант ЛОJtaJJьно:tfо уровнн.
сопз t int LocalFi~edVal ие = 4;
Console, Wri teLine ("Константа Local: (О)" I LocalFixedVal11e);
Console.ReadLine();
}

Обратите внимание на то, что д;ля доступа к константам класса ConstData не­
обходимо Уliаэать имя типа. Однако класс Program имеет прямой доступ к констан­
те BestNhlTeam. ПОСliОЛЬКУ она была определена в пределах собственной области
видимости класса. Константа LocalFixedVal11e, определенная в Main (), конечно
же, должна быть доступной толыio ИЗ метода Main ().

Исходный код. Проект Constants размещен в подкаталоге, соответствующем главе 3.


ГлС\вв З. Ос.~lOвы Азыка С# 135

Определение п'оnей только ДЛЯ чтения


как упоминanoсь Bblnre., значенце. npисваив~емое константе. должно быть ИЗ~
БеСТИО во время КОМnИЛЯ.lU-Ш. НО ЧТО делать, ес:ли:еужно создать неИЗМеннемое
попе, начальное значение Которого будет иавестно TOtJ;!biКO в среде выnnднеl-tuн?
ПредположйМ, что вы создали класс Tire (покры:;mка), в ROTOPOM обрабатыва.етея
эначеmrе ID (}щентйфи~а1'орJ npоиз~одителя. KpQMe ТЩ"О~ предположим. что:вы
ХОТите Сl{Онфиrypировать 'эшт тип :класса тaR.чтобщ в :н;ем подцержива:ласъ пара
известных ЗJ{земmшров Ti те,. ЧЬИ знаЧeJШЯ не ДРЛ,2;}fliЫ ~зменятьсн. ЕtШЙ ИсIЮЛЪ­
зовать кmачевое слово c,pnst. вы по~чите ошибку ~ОМПИJlЯЦНИ. Щ}СКОJII:>RY адрес
объекта в памяти cтaiiОБИТс.н }13~ТНЫМ только в cpeiie BbIJ1DJl:Н.eHI1JL
C'lass Т'1. т-е
1
/I IJocltOnЬxy a~ca оcS'И,Jt'1'О8 о~дe.ruDl'!1'CJI 11 среде UШOJПlеllИR /
11 здеоь иe.nъз. иСпOJ1ЬЗова!1'Ь 1tJDDЧезоs МОЗО 'oonst' !
public canst 'I'ire GQodStone = new Tire(90); // OJDиб~а'
publi c c:onst Tire E''ire'{ear =' Гlew 'ri.re(lOD) (. 11 O\IrиСn,!

publ :ic in.t manu.fa clnreID;


publ.io Tire () {)
p1,lb1i 1t Tire tl.nt l Щ
{ mаJШ:fа-сtоrе.ID = ID;

Поля, доступные TDJlhКO длн чтения, Щ)ЗВOJlfIЮт со;щавать gлементы данных., зна­
че:виn которых осхaIOтся ll~lIзвестными в npоцесс~ транС.JIЯЦИИ, :но о 'которых из­

ве.стно. что они никогда не будут иаменяться после IЦ создания. Чтобы ШIредел.и'I'Ь
поле. доступное тom.KO дла чтеНИfl, использ;'Йте JЩIОЧе:вое слово С# L-е"iiidОhlУ·

clii$ S Tire
.\
publi c :readpp.1.y Tir~ G(,l eJ dStол е = ne.w 'Tire' (90 ) ;
pljb1ic. .:с:еаdoЫу Ti re Fi:r:'e'te.ar = nе," T'i re ( 10 0,) ;

pul's-lic int 1Ilanufactli:teID;


p.t jblic ТНе (,) [ J
pub l.ic Tl r:e (111t. 1D') {mi3.L1,Q fa.o tu:z:'eID = 10;
'}

с -такой МОДИфМЩЩи'ей :ВЫ сможете не ТОJIЫЩ ~IJОЛ;НЯТЬ RОМnИЛЯЦИЮ , но И


гарантироваn.. ЧТQ ПРJil Изменении эначе-цийпоJТeiii Good,St:one и FireY'earB npо­
грамме вы nonyчnтесоо6Щение об оnrnБIre.

static vo'i d Маiл ('s tгiлg'[] ',Н ЧS,)


(
// OID;ибха!
/ / Flenьзя:, ИЗИeJUr.1Ъ З'Иачs.ние ПОдИ I ЦОС'1'упвorОТO.tIь1to Д;ПИ qтеиия:.
Tire t = г,еУ.' 1ir. e ();
t. F'ire Ye.ar = леw Т i rе( ЗJ);
1

1
136 Часть 11. Язык программирования С#

Поля. доступные только для чтения, отличаются от констант еще и тем. что
таким полям можно присваивать значения в контексте конструктора. Это может
оказаться очень полезным тогда. когда значение. которое нужно присвоить доc-ryn­

ному только для чтения полю, считывается из внешнего источника (например. из


текстового файла или из базы данных). Рассмотрим другой класс, Employee (слу­
жащие), который определяет доступную только для чтения строку. изображающую
SSN (Social Security Number - номер социальной страховки в США). Чтобы обе­
спечить пользователю объекта возможность указать это значение. можно исполь­
зовать следующий вариант программного кода.

class Employee
(
public readonly stril1g SSN;
public Employee{string empSSN)
(
SSN = empSSN;

Здесь SSN является значением readonly (только для чтения). поэтому любая по­
пытка изменить это значение вне конструктора приведет к ошибке компиляции.

static vo id Main{st r ingl] args)


{
Employee е = new Employee("111-22-1111");
e.SSN = "22 2 - 22 - 2222 "; // Ошибха!

Статические поля только ДЛЯ чтения


в отличие от данных-констант. доступные только для чтения поля не причис­
ляются автоматически к группе статических. Если вы хотите использовать значе­
ния дос1УПНЫХ только для чтения полей на уровне классов. используйте I;:лючевое
слово static.
class Tire

public зtаtiс readonly Tire GoodStone = new Tire(90);


public зtаtiс readonly Tire FireYear = new Tire(100);

Вот пример использования нового типа Tire.


static void Main(string[] args)
{
Tire myTire = Tire.Fir·eYear;
Сопsоlе.WritеLiпе("Код ID моих шин: (О}", myTire.manUfactureID);

Исходный код. Проект ReadOnlyFields размещен в подкаталоге. соответствующем главе З.


Глава 3 Оснав;ы ЯЗbjlilЭ С# 137

Ключевое слово static


I\эн уже roворилоаь в .этоЙrщm·е, члены Ю1зссов (и струюур) В С# мщ-ут Ciшреде­
лнтъсн с JЩЮЧtЩЪJМ словом static. В этом случае соответствующий qЩ~Н должен
вызыватьсЯ непосРtЩt1:твенно на ypoВI:Je КJДюса. а не ш<эемnлнра типа. Для и.плщ­
страдии рассмотрим "ЗI:IaJtо:IWiIЙ" т.ип sуstеrn.СОЩ1О1е, Вы уже могли уfiе;.1ЩТЬСЯ,
'по ~eтoд Writ,eI,ine () выэъшает.ся не с об'Ъe1l'ТНОГО уровня.

/1 0IIIи6ка.! W:ii teLine О - .:ио не Jlе'ЮQiЦ уровви ЭX!l8мIшRpа!


COI1Bole со = new Consol~ () ;
e.Wr-:itеLinе ("Та« I1е'Ц·ilтать я не Mo·r'y •.. 11);

Вместо этого НJЖНО просто добави'UЬ Iipефи:кс именитmrа R имени статичес:коJ'G


члена Writ€Liпе ().
1/ Празиm.Но! Wri teLine () - Э~О с'%'а~ео1СИЙ "е'1'ОД.
СDnзоlе. Wri teLine ("·сП,аСИБО ••• "'1 ;
МОЖ:80 СJЩЗатъ, что стаp.Iчесние члецы явл.нются элемeu:raми. щ)торы;е (по мн;е­
нию разраБОiFЧИКЭ тиuа) ОJiВ3f,тнаютсн "ОJШШIЮМ- банaтrъНhIМ]1J", ч:rобы соэдаватр,
ДJПl них экзеМШlflJ>Ы ТЩIEJ.. Ц:р:и созд~ типа класса вы можете (шредел:и:т~ любое
LIИ!.'дО СТCiJ,тических членов J!iJ.и:mJ тцщдов УРОВUЯ:Эi{зeмruщра.

Статические методы
~Сj:Шотрим следующий ШlJЩС Teenager (подросток), который определне'Г ста­
тический M~TOД Complain О. ВО3EiРащающий случайную СТРЩ"У. полученную с I10-
МОЩЫО щ,I30ва Чi';I.стной всдомогателыюй фytцщ1ДI GetRl;ifldomNul)\ber () .

classTeenaqer
{
pri vcat.e s"tatie Rand'cmt r = new Ra.ndсяn () I
private stati.c int GеtF.аn,dоmЫumbе:r !sho:tt uppet.LimitJ
I return r.Next (upperLiimH); .)
publlc sta.tic atrirlg Сощрlаin (:'!
{
:striлgIl шеS;%lgеs = neV.t string 151 f "А почему F!?",
"Он первый начал! ", "Я ~aK устал •.. ~ r
"Еена1!lИЖУ щкодУ! "., "Это ~еч,е С']!tЮ !" } ;
r-et1;lrn m~SGages. t$еtRaлclоrriNuпфer (,5) ] ;

Обратите ,вJil1::мание На то, что "Iiлен"перемениая Sys'telТl.RandplТ1 и метод


GetRaI'J.domNumber ( j. определяющий вcnомогателыryIP фУИКЦЩО, также ОО'I;>ЯВЛены
как ста:rи<Iеские ЧЛЩ:rЫ масса Teenager. согласно прави.тIУ, по которому статиче­
ские члены могут оперировать JТIQЩ:.КО ста:тичеСI\ИМИ qлеnа:ми.,

3аме'IaIlИе.. Позвольте мне·n08ТОfJlo1ТI:.СЯ. GтВТИЧ8Сrnе чдень/ MOryr ВЩlдеАствоват~ roлько.на стати­


ческие члены, Если э статичеСII:ОМ методе вы Г1аПbfтаетеСh 'использовать нестатические члены
(также J,fQзыааемые Д81*/hlМИ уровня Эf(земnляра)~ вы пол)''ЩТе оши:бку КОМпИЛЯЦиИ.
138 Часть 11. Язык программирования С#

как и в случае любого другого статического члена. чтобы вызвать Complai n () ,


следует добавить префикс имени класса.

/ / Вызов статичеСRОГО метода Соmрlаiл класса Теелаgеr


static void Main(string[] args)
(
for(int i = О;i < 10; i++)
Сопзо1е . WriteLine ("-> (О}", Teenaqer.Complain(»); }

И. как и в случае любого нестатического метода. если бы метод Compla in () не


был обозначен, как static, нужно было бы создать экземпляр класса Teenager.
чтобы вы могли узнать о проблеме дня.

/1 НестатичеС1СИе дaннwe AO.J1I1:Иbl вызываться на об'ИlJCТНОИ уровне.


Teenager joe = new Teenager();
joe.Complain() ;

Исходный код. Проект Sti:lticMethods размещен в подкаталоге , соответствующем главе З.

Статические данные
Вдобавок к статическим методам. тип может также определять статические дан­
ные (например, член-переменная Random в предыдущем классе Teenager). Следует
понимать. что когда класс определяет нестатические данные, каждый объект дан­
ного типа подцерживает приватную копию соответствующего поля. Рассмотрим ,
например, класс. который моделирует депозитный счет.

11 Это'!' JCJIacc _еет элеиент неС'l'aтичесJCИX ДaJUIWX.


class SavingsAccount
(
public double currBalance;
public SavingsAccount(double Ьа1апсе)
{ currBalance = balance; }

При создании объектов SavingsAccount память ДЛЯ ПОЛЯ currBalance вы­


деляется для каждого экземпляра. Статические данные, напротив, размещают­
ся один раз и совместно используются всеми экземплярами объекта данного
типа. Чтобы привести пример применения статических данных, добавим в класс
SаviпgsАссоu п t элемент currlnterestRate.
class SavingsAccount
{
p1.lblic double currBalance;
public static double currInterestRate 0.04;
public SavingsAccount(do uble balance)
{ currBalance = balance;}
Если 'reIrepL создать три экзем:nляpa S'9.vingsApcoU;rlt, ICaR ПОЩ1Зан~ ниже

st.at{c -.тоid Main tt>tring [J args}


{
11 kaJIcдый ОбrJ.eХ!l1 SаviпqsAcСОlШt веет свою JltОПИЖ/ ПOШ!l currBal.ance.
S:av±ngs.f!;ccount з1 = new SаviпgSАССО\l:nt. (50) ;
8avingsAc.cO!;;ll.lt '82 = 118W SavingsAcc~.nt (1ОО) ~
SavifLgslI'ccount 53 = nе", Sаviпg.sАССОi)лt (10000. 75);

то размещение ДЩIНblX в !ЩМНТИ ДОilIжgQ БМTh примерно таким. к<щ noкaaaHO на


рис, 3.9,

SаViцgЗАtСОUJ1.t: $1 ,

~
c'urrBalance=5Q

sa1fingM\'CCOUrit: g2
I curr Iлtеrев t'Ra te=. 04
tt!frВаlЩ'\GЕ!=100
,
I
-
$8vint)Mccoun.t : 1;3
c:urrВalance=10001J.75
/
, _.:. -,,'
Ркс. 3.9. Ctатические данные совместНо. испо.lIЬЗУЮТ-СЯ всеми
экзеМnлярамиопре.дenяющef'о .их класса

Давайте обноnим класс Savi!lgsAccOUht. оnpe.целив два статических метода д.тrл


nолучеRИ~.и устано1'lШ1 зnачений npоцеитной СТaDRИ. Как БЬiЛО замечено выше.
статические Мe'I'оДЫ могут работать l'О.lIЫЮ со стаmчесRИМИ данными.. Однако не-­
cтanJчесЮiЙ метод. может иеполыюnaть ШU\ статические, так и нестатичесmIе дан­
нъre. Это -имеет смысл. пооко:лъку статиt.rесItИе дm-щые.доступны всем экземплярам
ТЩJа. С УЧf1'roм этоrо давайте добавим еше два метода ypoB1-1Я ЭJCзeмnлnра, :которые
будУТ ИСШ)JIЬЭОватъ переменную npоцентНОЙ ст-авки.

class: S.а1lin·чэАсСФ\JIпt
(
риЫ i tdouble сU.rrЕаlщпсе;
public st<itic double ca=:rnt.e~estRabe = 0.011;
p:UGlic Sаv:LпgS~ССОUElt Idcшblе Jэ.aJалсе)
( <;щ r rВаlад с.е = Ьа} ап-се; J
11 СТiI!.rичеежиа кетоды поnуqeJUUJ/УСТавовхи проц-.иоЙ li:'l'iUХИ.
public stat.ic void SetInteres,tRa1'.e (double леwRаtе)
( cllrrIoteres'lRate = pewRate; J
public $taUc .double GetlnterestRate ()
( retucrn СdrtIпtеrез.tRаtе; )
11 Мewoд:ы Э.ЕЗeмzшиp8 пonyrrеиин/УС'l'aRОВ«И
11 '+Iе](}'ЩeJ4 npoце,в:-'J:',!;!'оJ;i СТ&вICИ.
public: void SetJnte:rj'j$tRateObj (do1jble newRc.te)
[ currln'terestRa·te = 118wRate; f
р u1:Й,lt:: double .GetlnterestRateObj О
( return currInterestRate;)
-
140 Часть 11. Язык программирования С#

Теперь рассмотрим следующий вариант использования этого класса и соответ­


ствующий вывод, показанныйна рис. 3.10.
static void Main(string[) args)
{
Console.WriteLine ("*** Забавы со статическими данными ***");
SavingsAccount 81 new SavingsAccount(50) i
SavingsAccount в2 = oew SavingsAccount(100);
/ / Получение и установха процентной ставки.
СОПSОlе.WritеLiпе("процентная ставка: (О}",
sl.GetInterestRateObj (»;
s2.SetlnterestRateObj (0.08);

/1 Соз~аиие HOBoro об~хта.


/ / Это НЕ • переустаиавливает' процеитнух! ст&Вху.
SavingsAccount 53 = new Saviog5Account(10000.75);
Wri tеLiпе ("Процентная ставка: (О} ",
Со пвоlе.
SаviпgsАссоuпt.GеtIпtеrеstRаtе(» ;
Console.ReadLine();

Рис. 3.10. Статические данные размещаются один раз

Статические конструкторы
Вы уже знаете о том, что конструкторы используются ДЛЯ установки значения
данных типа во время его создания. Если указать присваивание эначения элемен­
ту статических данных в рамках конструктора уровня экземпляра. вы обнаружите.
что это значение переустанавливается каждый раз при создании нового объекта!
Например, изменим класс SavingsAccount так.

class SаviпgsАссоuпt
(
public double currBalance;
public static double currlnterestRate;
public SavingsAccount(double balance)
{
currBalan ce = balance;
currInterestRate = 0.04;

Если теперь выполнить предыдущий метод М а i n () . вы увидите совсем дру­


гой вывод (рис. 3.11). Обратите внимание на то, что в данном случае переменная
cllrrlnterestRate переустанавливаетсл каждый раз при создании нового объеI{та
SavingsAccount.
r
I
rJ13B3 З,. ОСНОВЫ !'Iэыка С# 141

Рис. 3.11. ЛРИСВI\и-вание ~на!iенI/JЙ статическим даНtjЫМ


8 KOHG'rPYKTOplJ "rтереустанаВЛl'!вае-r" эти Зt'JачеНmiI

БыI всет.ца можете устанОliliИТЪ на~а;лъное зшчение сw.тическихдз!uIых. I!JСПОJ1Ь~


зуя. сШIТaRCИС ИlUщиализзции ч;ле~. но что делать. если знзчеltи-е .моЯ статич~­
C'l<ИХ Дaиl-I1aX нужно IIОлyчи:tЪ }f8 (1a~ы данных шш внешаеrо файла? Для реmеmrя
таких задач требуется. ЧТQбь'I13 KOHTeRC-re метода МОЖНО было ИCI:юлъэоватъ соот­
ветствующие операторы IIРОГР~ОГ() ROда. По этон причин:е в С# ДQпускается
определеnие сmamцчеСIC020 fCQнсrпрук:rru:;pа.

class sаvinяsА'СС(JШlt
{

11 С'1'a<rКЧеCJtИЙ'ltОНС'1'рY1t'1'ОР.
sta tic S-аvings,АС'СОuht (')
j
CODsole . Wri teLi:n.e (П В .СТC;lТ'I>Dчес,ком I';OHC'J',pY~To.P6. ") ;
сдrrlntеrеgtRаt:е = 0.04;

Вот несколько интересных зa;r.fечaнШi, 'lЩсающихся стат.ических J(OHCTpy:m'0pOB.

• Л:ioбоЙн.ласс (й.Л:й CТP)l1(тypa) МЩI~ет опреде.лять толькm ОДИН статический КОН­


структор.

• Статический :КОНструктор вьmолняется тодъко один ра., . независймо от ТОГО,


QКОЛЫЮ ооздается объектов данного тпnа.

• Статическиj{ liOH.CTpYКTOP не может иметь модифИНзторов ДОC'I}'ЩlOсти И па­


piIмeTpOB.

• Среда выдолне\IИ!l в,ызыйе-тT СТ!lТlfЧeСкиИ КОПСТРy-Rтор. !ЮI'Дa соз~ае1'СЯ ЭК­


земwнrp масса, или церед тем, Imlt ШШУЧИТЬ доступ }( первому Bы3ывемр:му
С'1'аnrческому ЧJl~ о

• Отатичесttин :КОНстру1<тор ВbI110.тшНeтi~я до 'Выполнения .rпoбоrо1tоНC'tpуктора


уровня ЭRЗeмIJJlЩ)а.

Теперь значение статичеOlЦ<JjX. дапRblX при создании новых объектов


Savil1gsAC"C"dUnt с@Хранпется. и €ортвет'СТВУЮЩИЙ вывод БУдеТ идентичен nCН'Ш­
занному на рис. З.10.

Ст'атические кnЭ.ССЫ
ЯзЫR С# 2005 расширил. область ПРЩ\а'енеюtя ЮUoч.евого С.iЮВЗ static путем
введения в рассмотрение crл.c:vпuч.ес~ клаСсов. Korдa:кп.acc опредмен. :((аХ стати­
ческий. он ile допускает создания Э1Ремrrл:яР0В с ПIЭМОЩЬЮ ЮIЮчеilоrо C;JIOBa пеw И
142 Чвсть 11 . Язык программирования С#

может содержать только статические члены или поля (если это условие не будет
ВЬПIОJllJено, вы получите ОlIШбку компиляции) .
На первый взгляд может показатъся. что IUIaCC, для которого нельзя создать эк­
земпляр, должен быть совершенно бесполезным. Однако если вы создаете класс,
не содержащий ничего. кроме статических членов и/или констант. то нет никакой
необходимости и в локализации такого класса. Рассмотрим следующий тип.

/ / СтатичеСlCИе lCЛассы могут содер.ать толыtо


/ / статичесlCИ& члены и ПОЛR-КОНСТ&НТIoI.
static class UtilityClass
{
public st a tic void Pri n tTime (J
{ Co n.sole.WriteLine(DateTime.Now.ToShort TimeString());
publ i c st a tic v o id PrintDate ()
[ COn SOle.Wr i teLine(Da t eTime.Toda y .To Sho rtDat e String());

При наличии модификатора static пользователи объекта не с,Могут создавать


экземпляры UtilityClass.
st a tic v oid Маiп(striлg!] ar g s)
(
UtilityClass.PrintDate();
1/ Ошибка Jc:ОIШИJlRЦКИ!
/ / НелЬЗR создавать ЭJc:земnлJtpIoJ статическиХ lCJ1aCCOIJ.
Utility Class u = n e w u t ilityClass( ) ;

До появления С# 2005 единственной возможностью для запрета на создание


таких типов пользователями объекта бьшо или переопределение конструктора. за­
данного по умолчанию, 'Как приватного. или обозначение класса, как абстрактного
типа. с помощью ключевого слова С# a b stract (подробно абстрактные тЩIЫ об­
суждаюТСJI в главе 4).
cla s s UtilityClass
(
private utilitуСl аэ s() {}

abstract c las s UtilityCla s s

Эти конструкции по-прежнему доступны. но с точки зрения типовой безопас­


ности использование статических классов является более выгодным решением.
поскольку указанные выше варианты не исключают присутствия нестатических

членов в определении класса.

Исходный код. Проект StaticData размещен в подкаталоге, соответствующем главе З.


Глава З" O~HOBЫ изыка С# 143

Модификатор_ып'араметров методов
Методы (и статические. и УР(}IЩ.Я ЭJщeмплiiра) могут исп{ЩЬ.зоватъ параметры.
передаваемые вызывюсщей сторonо.й'. Однако., в О1lIИ'lИе Q']; яекО1'орых друтих яЗы­
ков цро.граммиро.вa1lИИ. В С# предлагается Мliржество МDдифи:ка1'ОРОВ naраметрьв.
которые RОЦТР(ЩИРУЮТ способ передачи (ц. возможно, возврата) .аргументов ДЛЯ
дшпtого метода, кaI< n.о.казано в Табл, 3.5.

Тaбnицl!l 3.5. М'О'ДИф1-1каторы naраметров С#


'МОДИфикатор
napaMeтp08

(н'ет) ECJ1j.1 парэ:метр не ' помечен МО,Q,ификатqром, то пр.едполагаетоя передача па­


раметра no значению. Т.е. В.blзыва6МЫЙ метод по,nучит ко'f1\1ю Щ:>.\'1ГИН!У1ЬНЫХ
дaнг.tыx

Qut 8Ы)(ОДНЫI;I пвраметры УGniН<Шливаются выыыwемыыM методом (Н. таКиМ 06-


раз~lVI. передаются по ссыпке). Если ВI:lIЗы:вавмый метод !Не irl8знаLiИТ BbIXOA-
ные параtAeТры, ГЕЖерируетс~ ОШl'1бка компиляции

ПОЗ80ляет переелат!) ПРОИ3ВОl1~ое число аргумеН1'Р!iI ОДИlfаКQВQГО типа 8


виДе единого параметра. Для любого метода :nО(1)'скаеТСSП(lЛЬКО ОДИ~ моди­
фикатор pe.rams И TO:ll.loKO Д1IЯ tlОсл.еднеrо параметра меюд!i

ref СоотвеТСЛ\УIO:I.lЦее значение задается ВblЗhlваlOщей стороной, но ВЪtЗЫВав.­


мь!й метод ,.юже~ это знаLlеl~Lillе изменить (ГJQСI'ЮПЬКУ naHtIble передаются
по ССЫJ1l!:е). Если BbI<3blSOOMQI!:1 метод не nРИОВО!1'Т . ЗН~'iение параметру re'f.
ошибка компиляции не гвнерируеТСi1

способ передачи параметров..


иопользуемый по умолчанию
ло умолчанию napm.le1'p передается n фymщиrQ ПQ' ЗfЩ~J1.I.D(I. Попросту говорн.
еСЛй не определить ЦЛЯ a:pryмeHTa МQД~lШатор, то в ф>'Н,Кцию передаетс.я К(iIIИЯ
перемСI-ПIОЙ.

11 До УИQnUИИJO арryнeив.r neреДiUn'СIi по sиачеИИl>.


publio э'tаtiс in.t Add.!i'tlt х, iлt у)
{
int алs = Х + у;

I/ ~II CJ1iopoн.a не yaJQ.'lWJ! З'DQ( mlме,иений,


11 пос-ttOn"JIУ НОRИ4IицPSpУe'rСII ХОI:ЩJr
11 DриZ'ИВil.DЫD,IXI ~аиИNX.
Х. = 10'000;
У = 8ВНЭ8;
ret.uit:Jl апэ;

Эдесь в,,"шдные цеЛОЧИс'i'JеННЩе п~раметрЪ! uередаются по авачeБИID. Поэтому,


если изменить значения napame-rров в:вyrри дaннo1'9 метода. 'го вызывающая сто.­

рона об этом не узна.ет. na~О.lThКy .ИЗМt;НЯЮТС.я а:ьrачен~ кОПИЙ цеJIDчислеяных


д;nmых ВЫ3Ы8aIOщеГ0 объекrrа.
r
144 Часть 11. Язык программирования С#

static void Main(stri ng[] args)


{
int х = 9, У = 10;
Сопsоlе.WritеLiпе("До выз о ва: Х: {О}, У: (l}", х, у);
Co.nsole . Wri teLine ("ответ: {О}", Add (х, у) );
Console. Wri teLine ("После выз ова : Х; {О}, У: (l}", х, у);

Как ВЫ и ДОЛЖНЫ ожидать, знач.ения х и у остаются теми же и после вызова


Add ().

Модификатор out
Теперь рассмотрим использование параметров out (от output- выходной).
Если метод определен с выходными параметрами , то необходимо назначить этим
параметрам подходящие значения до выхода из метода (если этого не сделать, бу­
дет сгенерирована ошибка компиляции).
Ниже для иллюстрации предлагается альтернативный вариант метода Add ( ) ,
использующий С#-модифuкатор out и возвращающий сумму двух целых чисел в
виде выходного параметра (обратите внимание на то, что возвращаемым значени­
ем самого метода теперь будет void ).
/ / Выходные параметры задaJD'l'СЯ чnено",
public static void Add(int х, int у, out int ans)
{
ans = х + у;

При вызове метода с выходными параметрами тоже требуется указать модифи­


катор o ut. Локальным переменным. используемым в качестве выходного параме ­
тра. не требуется присваивать значения до их использования (эти значения после
вызова все равно будУТ потеряны) , Например:

static void Main(string[] arg s)


(
1/ Нет необходимости задавать значения
/ / JlОJC&7iЬJU.IИ выходным перемеНlDoDC .
int ans;
Add(90, 90, out ans);
Conso le. WriteLine ("9 0 + 90 = (О) ", ans);

Этот пример предлагается здесь только для иллюстрации: нет никакой необ­
ходимости возвращать значение суммы с помощью выходного параметра. Но сам
модификатор out играет очень важную роль: он позволяет вызывающей стороне
получить МножесТво возвращаемых значений от одного вызова метода.

/1 Воавращение иножес!1'В. выходных парамеТРОII.


public static void FillTheseVals(out int а, out string Ь, out Ьооl с)
{
а = 9:
Ь = "Радуйтес ь своей строке .":
с = trllei
1 Глава 3.. OCfloebl ~эыка С# 145
ВJ;>13рШaIOЩая сторона может вызвать этот метод следующим образом.

s1:.,atw voi'd Наln '(st rin. gIJа r gз)


[
int- i: st.Iing st:t; bool l~;
FillThl?se\'als \out i, out ,s U:, out Ь);
Console,. Wr j teL:bEls \ "I n't равНо: {О)", i);
Console .W-:JtitеLiо€(flS1:riпg рав-НО: (О}", str) ,;
Со,твЫе. ViJri t-еL i!\е C"J,;o.olean равно; {О 1-" г Ь) ;

МодИф'икатор ref
Теперь ра<::СМОТРИМ1IСПОЛЪЗОВeu-UI~ в С# МОДИф](IКaтора ref (от reference- t;Cbl-
лочный). Ссылочные параметры ЦУЖН!if тогда, Щ}гда требуется ПОЗIIОllИТЬ методу
И3МеНЯть,дадньте. оБЪ1mлеН-J:lliIe:В .контексте ВbIЗов~ (ю.шрИме'р. В фyнmumx сорти­
ровки или обмена даннъiМИ). Обратите lщима:аи:е на различие междУ ВЫХ:оДl:lьщи
и ссhТЛОЧНl1lЫИ па;раметрами.

• В:li/ходные пар~етры не требуеТСjl И){иuиалирирrл~ать перед передачей их


методу. Причина в том. что сам метоД должеl'l присвоить аначеНи;я ВЪnCOДНJ>IМ
параметрам.

• ссы.Ioчныыe паРЩ{e"I'Рhi неоБХодuмо инициадизировать до 1'ОГО. как они будут


переданы .метоцу-. Цричи:па Б тоМ •. что передз.€:Тся С"сылда на 'существующую
перемеНkJyю. Ес:гш не прпсвQИТЬ rю-ременной lf.a,чальнре ,!начение. это будет
означать ИСШ;>:JIьэовawrе неИl-IИЩrализцровa1ПlО:Й nереме,}щQИ"

Давайте npодемоnстри:руем ИСПQЛЪЗование ШI.ю>IевО1'О СJщва ref с .помощью ме-


тода. в котором осущеr;1'ВJtя.ется Dбмен значениями ~Y"'CТPOK.

IIC~~MpDe~.
Swа_ р.str·iйgs (ЕеЕ stril1g $_lz
publ:ic static v-Oici ref s ,trit1g 52)
r
s t-:rl.l;1g tempS-t-r = s 1;
si s2';
э2 = temp!Su;

Этот метод МQЖ}iО вызвать так.

э-t.,tiс voiJj foitain (st:rJ.ngJ J аЮ':;j8}


j
str i 'n 1 ::;= "Церва' >I строка";
S -t.:rln,g ~2 = "BT{J,pa-я стро,:ка";
CO'I'1 S01e.W-r-itе1ih,еl"До, ; {0}, \1 ,. ", 5, 82);
sйарstrings (r~ 8, re' з2);
C,onsols' . W-ri t-eLiпе ("П Q,сле: ! О}, J1] ", 5" 82) i

Эдесь БЪiЭЪШающая сторона щщс~аивает .началЬНое значение лок~ CТpO~


новым данным ($ и 8:2). По завершении вызова SwзрS ·tring.s (1 етрока э содер:IIWТ
3HaqeН1ie "Втьра-я CТPOKa~r. а з2 -, <Щачение "IIeрвая строка".
146 Часть 11. Язык программирования С#

Модификатор params
Нам осталось рассмотреть модификатор params. позволяющий создавать мето­
ды, которым можно направить множество однотипных аргументов в виде одного
параметра. Чтобы прояснить суть дела, рассмотрим метод. возвращающий сред­
нее для любого числа значений.

1/ Возвращение среднего .ц.пR 'нехо'1'ОРОГО ЧИCJIа' значений.


static double CalculateAverage(params double[] values)
{
double sum = О;
for (int i = О; i < values.Length; i++)
sum += values[i];
return (sum I vаluеs.Lелgth);

Этот метод принимает массив параметров. состоящий из значений с двойной


точностью. Метод фактически говорит следУЮщее: "Дайте мне любой набор значе­
ний с двойной точностью. и я вычислю ДЛЯ них среднюю величину". Зная это, вы
можете вызвать CalculateAverage () одним из следующих способов (если не ис­
пользовать модификатор params в определении CalculateAverage (). то первый
из указанных ниже вариантов вызова этого метода должен привести к ошибке
компиляции).

static void Main(string[] args)


(
/I Передача в аиде списха значений, раздеnеВJGIX ЗёI.ПИorAJМИ, ...
double averagei
average = CalculateAverage(4.0, 3.2, 5.7);
Сопsоlе.WritеLiпе("Среднее 4.0, 3.2, 5.7 равно: (О)", average);

/ / ... пnи передача в виде массива значений.


double[] data = { 4.0, 3.2, 5.7};
aver'age = CalculateAverage (data) ;
Сопsоlе.WritеLiпе("Среднее равно: (О)", average);
Console.ReadLine();

Это завершает наше вводное обсуждение модификаторов параметров. мы снова


обратимся к этой теме немного позже (в этой же главе). когда будем обсуждать раз­
личия между типами значений и ссылочными типами. А пока что давайте рассмо­
трим итерационнъrе и условные конструкции языка программирования С#.

ИСХОДНЫЙ КОД. Проект SimpleParams размещен в подкаталоге, соответствующем главе З.

Итерационныеконструкции
Все языки программирования предлагают 'конструкции обеспечивающие воз­
можность повторения блоков программвого кода, пока не выполнено условие завер­
шения повторений. Если у вас есть опыт программирования. то операторы цикла в
С# будут для вас попятными и не должны требовать пространных объяснений.
Глава з. ОСНОВЫ я~ыка с.# 147
в С# обеспечивrotrrся следУЮщие q~rpe итерщщtmные КОНСТРУ'КЦИИ;

• Ц1mл for;
• ЦИЮI foreach/ in;
• ЦИlt.lJ wЬil€';
• 'ЦШUI 00 /:whi 1е.

Давайте раССМQТРИМ Вс.е ука3aЩffiI~ ЩДfСТрукци:и по ОчереДй.

Циклfоr
Когда требуетел ПОВТОРИТЬ бщш лрограмщшго ROAa определенное qnсщ) раз ,
оператор for подходит Д!IЯ этогО J:IY'Щ1е всего. ВЫ можете указа'tь. сI{олы(o раз ДQЛ~
~H DDВТОРИТЫ:Н блоК npотрflМl\Iтого Iюда. а также условие окончании цmwз. Без
лищн:тс объяСнений. ВОТ шrм соответствуюJ.ЦJiI;Й образец син.таКCI«:a.

11 База ДJt~ циж.па.


s tatic void м.а i n (s'tr iлg r 1 a1:gs,)
{
/ / Цt1iреlllеииа& 'i.' ДОC'J'YПКа '1'OaIЪt«) iI lI:o~lI:c!t'e 'зorоrо ~& fo~.
fQr (ibt i = l!; j < 10; 1.++)
{

J
// ЗД$СЪ веремеНИ&JI ' i ' ие,чос'Ж'уIЦUl,.

Все ваши привы'Uц.re приемы использования ЦИЮЮ8 ii С, Ct-J- И Java примени­


мы и при построеJ1ИИ оцераторав f or в си. вы моЩете СОздаваТЬ сложные условил
ОКОНЧания ЦИJUIа, строить бе(;кенечны:е ЦИКilЫ. а та:кще ИС11ОЛЬЗО'Вa'rЬ ключевые
слова goto. Conti [ше и break. .я думаю, ЧтО эта ИТfW<ЩИQН"fi!Ц конструкция будет
вам поняnщ. Если же вам требylOТ('.R дальнеЙ!Пие объяснeщI,fI по rтoвoдY ЮIЮчевоrо
слова for в С#.. jJСIlОЛЫJуйтедонум~ltГanию ,NEТ Framework2.0 sпк.

Цикл foreach
Кmoчевое слово С'# fO.r-еасh. llОЗВО~IНет повторить 0лредеденпые действия для
всех элементов MCj.~cl!IВa без необходимости ВЫ$JСЛfНИЯ разм~р.ов массива. ВОТ два
ПРJiмера Ис;ПОЛЬЗО:вaI01я f'orea.cn. один ДЛП маСШIВа CTPort, а дpyrdЙ- рдя массива
Цe.iIЬШ ЧИС~ .

11 Прохождение III&ссиза .о nOMOIIItЪJO :foreaoh.


s .tatic "91а Ma,in (stliiлg (] arg.s)
f
sti:in.g [] Ь,юl!;s = [ " ' \:дожные алгоритмы",
"1fJIaCCYNEotKa~ ТЭ>:J{Q.i10 ГИ;Я СОМ",
"ЯЗЫК с# и п'Лii!Il форм а ,NET i ' [;
for.~ac~ (strinq s lJ1 Ь.сюks )
СОЛsо1е. v,riteL1Tle (5) i

int [1 mу!пt.s = { 10, 20, ЗQ, 40 J:


tore.ach (int: i in :rt1'Ji'1пts)
'Consol е •.Wr i j:>eLi..rH:~ ( i) :
148 Часть 11. Язык программирования С#

Б дополнение к случаю простых массивов, f o reach можно использовать и для


просмотра системных и пользовательских коллекций. Обсуждение соответствую­
щих подробностей предполагается в главе 7, поскольку этот вариант применения
кmoчевого слова for e ach предполагает понимание интерфейсного npограммирова­
ния и роли интерфейсов IЕпu rпеrаtоr и IEnumerable.

КОНСТРУКЦИИ while И do/while


Цикл whi1e оказывается полезным тогда, когда блок операторов должен вы­
полняться до тех пор. пока не будет достигнуто заданное условие. Конечно. при
этом требуется. чтобы в области видимости цима wh ile бьmо определено условие
окончания ЦИ1\Ла, иначе вы получите бесконечный цикл. В следующем примере
сообщение "в цикле whi1e" будет печататься до тех пор. пока пользователь не
введет значение "да" в командной строке.

s ta ti c void Main (s tri ng [] args)


(
string userIsDone = "нет";
1/ ПрОlSерха на соо!t'Зе'l'С'l'lSие строхе в нижнем регистре.
while{userIsDol'l e.To Lower() != "да")
{
С ол sоlе.Writе("Выудовлетворены? (да] [нет]: 11);
userIsDone = Console. Read Line();
Console. WriteLine ( "В цикле while") ;

Цикл do / while подобен цикпу while. как и цикл whi1e. цикл d o/ whi1e исполь­
зуется для выполнения последовательности действий неопределенное число раз.
Разница в том. что цикл d o /wh i1e гарантирует вьmолнение соответствующего бло­
ка программного кода как минимум ОДИН раз (простой цикл while может не выпол­
ниться ни разу. если условие его окончания окажется неверным с самого начала).

stati c v o i d Main(string[] arg s )


{
string userIsDone = "" ,.
do
{
Co nso1e.WriteLin e ("B ци кле d o /whil e ");
Conso1e.Wr i te ( "Bw удовл етв о рены ? [д а ] [ нет]: 1');
us e rIsDone = Co nsole.ReadLine();
}while(userIsDone.ToLower() ! ~ "да"); 11 Обратите ISнимание
/ / на 'l'ОЧJCУ с З&ПJI'l'ой!

Конструкции выбора решений


и операции сравнения

в С# определяются две простые конструкции. позволяющие изменить поток вы­


полнения программы по набору условий:
r
l Глава 3. ОСНОВЫ lIэЬ!ка С# 149
• оператор if/else:
• оператор swit; c h.

Оператор if/els.e
в отличие от С и С++. оператор if l e He в С# может работать только с булеШ'!I­
ми "Выраженидми, а Ве с произвольныыи знаЧ~ЩМR -l. О, Поэтому в операторах
it/el.s'E': обычно исщwьэуют.с'я операЦии С#. дон.аЭанНые в табл . 3.6. чтобы полу­
Ч:ИТЬ ОУЮЩ1Lb1;lЬre булевы эRaЧ'еIiИи.

Та~loЩа3..в. Оnераци ~ r;раВI1ения. в С#

Операция
При мер ~о:n ..зоевИИR ОпиcaJlие
сравнения

if (a ge == 3 О ') Возвращает t r lIe (ИD'Гина ) ,ОЛЬКQ в том cnY"Iae,


когда аыаже~RR QдинаКQВЫ

i f ("Fd C> " '= rny:s a ) Возвращав. tr u e' (истина) ,олька в "том Сl1уча~.
~.oгдa вырюкения раЗЛИ'IНЫ

< i f (Ьоп цо; < .2.000 ) 6.0звращает t ru e (истина) ,ОЛЬКО в ,ОМ олу'!ае,
> i f (b OТl US ;. 20'DiO I когда выраЖЩJИе А COOTBeтtтвeHHO меньше,
<= i f ( Ь отшs <'" 200 0 ) болы:uе! меньше или равно., 6оfJьше WliIJ равна
>= i f фQ ПUJS >= 2Q.QП.} выражению 8

Проrpаммистам. использующим С 11 С++. с,ледУe'I' обратить внимание :на ТО, что


их привычные приемы no npоверке уеловий "Jja р@енство нулю" в С# работать
не будут. Нацример. вы xoТl\ITe выяснить. 6уде:т ли данная строка длиннее лустой
строк». Может ВО3J;IИlrn:yть ис:кушение нщmСjЗ.ТЬ. следующее .

/I в С. эта недоnyс'1"ИИО, l.1ocJComoxy Length· 80ЗЗРащае-т з.n t, а ~e .bool .


st~1ng t hо uч hW f'ТhеЬ ау = " С'l' арую с о ба:r<-У I;fО:ВЫМ 'I'рюКiU<I Н~ЧИ'r Ь t10ЖRО ";
i f ( t Л оu g ht:ОЦ'hе. Dау. Len q ,tJ:",)
!

в данном c.nт-Iae ДЛЯ исполъзова:,rIЩl сваиства Str i ng. Lеп g t t. ЩЖЩ>. изменитЬ
УCJ,Iовие ттс . ЕаЕ показано 1:iЩJtE .

/I Э'l'о .цопу~, ~&JtIl:U:' ре8~'l'а'l'фМ будет tl:Ue ~ fа.1зе·.


if ( О ! == t ho uq]зJ:О fТFI'еD а у . Lerl g~ h)

Чтобы обеспечить БCrllее сложную upo:верку. оператор i f может СQДержатъ слож­


иыевырщнени.я и другие QператорI;>I , Сивтаксис С# в данном случае ИДt;:JIтичен
С(Н} и Java {и не CЛИI1l1\ом О'J'JUIчэетсs: от Vlsшtl ВаЮс} . Ддн построения (ц:ф.жных
ВhIpWI~l'ЦfЙ С# имеет вполне отвечающий ожидВRшLl\! набор УСЛОВ1iЫХ операЦИЙ,
011исан;и.н которых предлагаются в табл. 3.7.
150 Часть 11. Язык программирования С#

Таблица 3.7. Условные операции в С#

Операция Пример Описание

&& Н( (age 3О) && (пате "Fred") ) Условная операция AND (И)
Н( (age (пате "Fred") )
" Н(! myBool)
30) 11 Условная операция

Условная операция
OR
NOT
(ИЛИ)

(НЕ)

Оператор switch
Другой простой конструкцией выбора. предлагаемой в С#. является оператор
switch . как и в других языках типа С. оператор switch позволяет обработать по­
ток выполнения про граммы на основе заданного набора вариантов. Например.
следующий метод Main () позволяет печатать строку. зависящую от выбранного
варианта (случай default предназначен для обработки непредусмотренных вари­
антов выбора).

/ / Переaumчение по числовому зиачеНИlD.


static void Main (strin g [ ] args)
I
Console.WriteLine("l [С#], 2 [VB)");
Сопsоlе.Writе("Выберите язык, который вы предпочитаете: ");
string lan g Cho ice = Co ns o le.ReadLine();
int n = in t .Pa rse(l a ngChoic e );

switch (п)
(
case 1:
Сопsоlе.WritеLiпе("Отлично! С# - это прекрасный язык.");
break;
саэе 2:
Co n sole.WriteLine("VB .NET: ООП, многозадачность и т.Д. !");
break:
default:
Console. Wri teLine ("Хорошо ... удачи вам с таким выбором! ") ;
break:

Замечание. В С# требуется, чтобы каждый вариант выбора (включзя default), содержащий


выполняемые операторы, завершался оператором br~ak или goto, во избежание прохода
сквозь структуру при невыполнении условия .

Приятной особенностью оператора switch в С# является то. что в рамках это­


го оператора вы можете оценивать не только числовые. но и строковые данные.

Именно это используется в следующем операторе switch (при таком подходе нет
необходимости переводить пользовательские данные в ЧИСЛоВые значения).
Глава З. ОсновЫ ЯЗЫ.ка c:t 151
s'latic void Иаin ($tring l} args:)
r
Co[']s'Qle. WIiteLine ("С# ИJIl1 ·VB"):
СопsоIе.W'тit:е("Выберите q:зыR' 1QОТС:ФЫЙ Бъl лредrюqитает.е·: ТО),

s ,tring lаш~Сrн;)iое = COnSGJ.le . ReadLiNH·) ,;


scwi t:ch (lапgСгюiсе)

саЭе "С#":
С0n:юlе •Wri tеLiпе( "'ОТ:JtИЧt!О! с* - этё I<рекраСI;lЬ1.Й ЯЭb!JiI: ... j ,;
b:r:eak:
C~ эе "VEI":
СОПS'0 1е .. Wri teLi пе .("\m . NБТ: ООП, МI(0 го_заД<l".Щос'rЬ И т .д. ! .. ) ;
break:;
defa.ul t;
Сфо'Sоlе. Wri t eLin5! (·'ХGliЮШG •• , удачи аам с таКИМ ВОООРОМ! 'О) ;
break;

'И~ХQДНblЙ ко,ц. Проеfa Iteration$AndPeois'iQl15 размещен в пощ:аталфге, еоответt;ТВующем главе S.

Типы, характеризуемые значен' иями,.


и ссы,лочные типы
Подобно JlЮбому другому язьщу программЕРОВ<1НИй, язык С# оцре-делнет ряд
нJдoчс8ых СЛОВ. предС'тавляющих баЗО5Ые типы данных. такие RaК цельте ЧИCJtа.
символьные данные, Ч1ICJ1а с ПЛавЩОЩПМ деснтичнымраздemпenем и лorич.еские

((jyJleBbl) значеНFJI. Ег.ли вы работсщи с 8.Зbl1!iQМ С++ .ТО БУДeJ1е рады узнать. что
эдесь, эти 'ВНyrрemше 'I'щlы1 я,вJL"IЮТС.R "фиксированными 1<O}{CT~Taмl"". Т,е., I:1anpи­
~ep. после создания э)tемента ЦеЛочщсленных данных все Я3'blJ(И .NEТ будут пони­
мать природУ ЭТОГО типа и .диапаЗО1;:I его значений.
Тип д;aннъrx . NEТ машет либо ~рalcrеризова'I!bся змчеuи.ем, д-ибо бы:rъссыло'С{­
ным типом (-т.е. хаРaRтеРИ30В</.ТЬСJiсCБIJшоИl. К типам. харантери.зуемым значени­
ем. ОТНОСЯ:ГСЯ Все ЧИС)tо.вьre тицы Д3RНЫX (int, flQat и т.д.), а 'также перечни и
структуры. размещаемые в стеке. ПОэтому типы. харакТt:pдзуем:ыезнач~ниями,
можно сразу же удaлnть НЗ памяти, как ·тольн:о они оха;~ьшаю'ГСЯ 1Ше контекста :их
dfIре.цеJreНИЙ.

11 ЦМ'Очи~е,н!QoU> .цаиине xa.p~p~ca: зщачениеи!


public VQid SonleMeth~d ()
i
int i = О ,,
Соnsоlе . Wri teLine (i) ;
11 здесь '!. r у.цa..nО'l'СS kl5 C'1Iexa.
152 Часть 11, Язык программирования С#

Когда вы присваиваете один характеризуемый значением тип другому, по умол­


чанию выполняется "почленное" копирование. для числовых и булевых типов дан­
ных единственным "членом", подлежащим копированию, является непосредствен­
ное значение переменной.

// ДNЯ ТИПОВ, харах~еризуеиых значениями, в резуль~а~е ~aKoro


/ / присваивания в c~eK помещa.J'l'СЯ две незазисимwе переменные.
public void SomeMethod ()
{
int i 9 9;
int j i;

/ / После следуацеrо присваивания значением I i I ос~ане~ся 99.


j = 8732;

Этот пример может не содержать для вас ничего нового. но важно понять. что
,NET-СТРУКТУРbl (как и перечни, которые будут рассмотрены в этой главе позже)
тоже являются типами, характеризуемыми значением, Структуры. в частности,
дают возможность использовать основные преимущества объектно-ориентирован­
ного подхода (инкапсуляции) при сохранении эффективности размещения данных
в стеке . Подобно классам, структуры могут использовать конструкторы (с apryмeH­
тами) и определять любое число членов.
Все структуры неявно получаются из классаSystem.ValueType. С точки зре­
ния фуmщиональности, единственной целью System.ValueType ЯJщяется "пере­
определение" вирryaJIЬНЫХ методов System.Obj ect (этот объект будет описан чуть
позже) с целью учета особенностей семантики типов, ЗадаННых знаЧениями. в про­
тивоположность ссылочным типам. Методы экземпляра. определенные. с помощью
System,Val ue Type, будут идентичны соответствующим методам System.Object.

/ / Cтpyx~ypы И перечни RВЛRЮ'1'ся расширениями System. ValueType.


public a bstract class ValueT y pe : objec t
{
риЬ! ic virtual bool Equals (obj ect obj) ;
p ubli c virt u al i n t GetHa shCode ( );
p ublic Туре GetType();
p u blic virtual string ToString();

Предположим , что вы создали С#-струнтуру с именем MyPoint, используя клю­


чевое слово С# struct.

/ / Cтpyx~yp.ь1 nЛRЮ'1'CJI типами, Ko~opыe хараJC~еризYJDТСЯ зиачениlDllИ.


struct MyPoint
{
риЫl с int х, у;

Чтобы разместить в памяти тип структуры, можно использовать .ключевое сло­


во new, что. кажется, противоречит интуиции, поскольку обычно подразумевает­
ся, что n ew всегда размещает данные в динамически распределяемой памяти. Это
частица общего 'тyмaнa , сопровождающего
h
CLR. Мы можем полагать, что вообще
Гnaва З, OC~1D8DJ ~~-IbIКf! с., 153
nее IJ проrpa1'o1М~ ЯВJIЯ~С1f объектами И ан.аЧёНИН!"Пi. создаваемыМ}! с помощью Q<:W.
Dдшщо в TO~ СiJ}'ЧfЩ. 1j:Щ'Да ореда выnо:zwенип обнару:ж:иваеттиtJ. полученный из
Sуэt!'!mNаluеТуре, В;blПIШН.яется обращение Jt стеку.

11 Все равно ИСПОЛ1оsуе'N!Я C'1'eJ(!


l1УРоiпt р ;: t1ew MyPoi!'Jt () ;

cTpyItrypы могут СОЗДiШа'Гъ~ fI без ИСПОдЪЗ0Вания ключевоrо сдова new.


MyPr>in t- рl;
pl.K z l00;
рl.у = lQД:

Но при ~!СПОЛЪЗОВанн:и ЭТОГО подхода вЫ должны 13IiШО.111:ЩТЬ инициали:;JaДИ19


1IC~ шщеа ДaJ:lНЩX ДО их использования. Если ЭТОI'O не сде.11ать, ВО3НИI<нет ODIffБЩ'J.
1tPМIIИЛящш.

типы' характер~зуеМЬJе значениями, ссылочные


ТИПbl и оператор nрисва ,ивамия
ТenеРЪИЗУЧИТf: слецующий lI,fетqд Mai [l () и рассмотрите его вывод. ПОнаЗаНН:ЫЙ
на рис. 3.12.
.stati9 void Main (B,t L ing [) arg.,)
(
COn$o~e. Wri t~Li Щ! (

"ir,*'" Типы, хара :r;'I'€ри:зуеМЫЕ: зн,ачение~ I сс"Iлiэчныe ТИПЫ *-1< *") ;


Console. W:r it eLit'\~ , (Н'_ > Соз:nа'Ние р1")- J
.муР{)lпt Р! = ОЕ?'" Му:Рсйп ,t () ;
1"1.]1(. = 10 0,;
р1.у=100:;
Сол-sоl.е, W:r..i.t·еЬiпе t" -:.> Орис:ваиэание рl ТI/1Щ7 Р2\П" i ':
MyPoint р2 = р l ;

// Эт о р1,
Consol,e.Writ el,.Ln,e\ "'p l.x = ! О}", 1='l.х) ,;
СО!'.эа 1е . Wr i:с.Е!Liле (П р l. у = (~I} ", рl. у) ;

1/ ЭТР р2.
COJl:Sole. W'ri teLine ("р2 .k = \ О) "1 р2 .. Х) .;
Consol~.WriteLin.t"p2.y ~ /О}", р2.у);

./ / ИЗ'Ь1!еRение р2. х • .31'0 }IE влияе·т на pil .l::i.


Солвоlе • W'r i teLi11e ( "- > Заl04ена ЭJol'ilч·еНИ!il р2. Jj 8.Q. ·,9:0 О") ;
р2. х = ~OO;

11 ВОвС!я печа':!'ь.
COnSQle. W:r itеLiле (" -> Это сн.о·ва ,значения х ..• "'у ;
co.ns.o le.writ€Line!"pl.x'= (Л}", pl.Xjl
CDnsol е. W:r i te1.Jne ( "р2. х. = 1'0 J' ", р2 .•.х);
CODs.ole .Rfl1:Ld:Line !) ;
154 Часть 11. Язык программирования С#

Рис. 3.12. Для типов, характеризуемых значениями, присваивание ОЗНачает букваль­


ное копирование каждого поля

Здесь создается переменн:ая типа Mypoint (с именем рl), которая затем присва­
Ивается другой переменной типа MyPoint (р2). Ввиду того, что MyPoint является
типом. характеризуемым значением, в результате в cTeRe будет две копии типа
MyPoint, каждая из которых может обрабатываться независимо одна от другой.
Поэтому. когда изменяется значение р2.х, значение pl.x остается прежним (точно
так же, как в предыдущем примере с целочисленными данными).
Ссылочные типы (классы), наоборот, размещаются в управляемой динамиче­
ски распределяемой памяти (managed Ьеар). Эти объекты остаются в памяти до
тех пор, пока сборщик мусора .NEТ не уничтожит их. По умолчанию в результате
npисваивания ссьmочных типов создается новая ссьтка на тот же объект в дина­
мичеСRОЙ памяти. для. иллюстрации давайте изменим определеrше типа MyPoint
со структуры на класс.

/ / kласCJol всегда оказwваются: сcыnоЧIWМИ ТИПаии .


сlазз MyPoint / / <= Теперь ЭТО к.nacc!
{
public int х, у;

Если выполнить про грамму теперь, то можно заметить изменения в ее поведе­


нии (рис. 3.13).

Рис, 3.13. Для ССЫЛОЧНblХ типов присваиваНl-1е означает КОПl-1роваliие ссылки


[naва З,. ОСНОВЫ !rnыка с# 155
в ./tэнном. случае имеется две ссылки на один и тот Же объект в управляемой
дин~~ской памяти. ПQЭТOму. если изменить аначеШIе х () помощью ссЬimtи р2.
то l\fЫ }'Видим, ЧТQ р1. х укажет на измененное значение.

Типы, характеризуемые значениSlМИ и


оодержащие ссылочные типы

Теперь. когда вы чувствуете рааi-IИЦV мещ,ц,у 'ШшiМИ. харaitтеризуемы:мизна­


чением. и ссылочными типами. давайте рассмотрим более сложный nрИМе.р.
Предположим. что ИМеется следующий ССЫJНi'IНЫЙ тип (класс). обрабатывающий
йнформацио:нную IЛpОh-У. которую можно ус'Таповитъс DОМОЩЬЮ 1ЮJ1Ъ3ОВательс:ко­
го :конструктора.

claS5 S'h<;!pelnfO
1
public stIing .iп.fоst.riнg;
I
publi~ Shapelnfd (.s<tri.n g info)
1
t infoString = iпfФ,' }

ПредIIР'ЛОЖИМ также. что вы хотите помесТ1IТЬ леременную этого типа :клас­


са в тЮI С именем MyReet angie (nрямоугоJlыlк)•. хараJ\."ТериэуеМЫЙ значением.
Чтобы ПОЗВОЛИТЬ :ннепщим объектам устанавливать значение вцутреннега поля
Shape1nfo. вЫ ДОДЖRbl сщэдахь :новый KOHCTpyRТOP (:при ЭТОМ гонструктор струк­
туры. эадЩIЩ;IЙ по УМОЛЧIOЩЮ. является зарезервировшllП:.IМ и не доду{:кает пере­
определения:).

at,r'u ct MyRectangle
{
IICтp:Y~TyPa :td:YRecцngJ.e coдe~ ч.ден Ca.tn01iR0:I10 ~a.
publ i-c Shap.elnfo rectInfo;

p'iblic iлt tOPI left, bat1:om, rig1it;


p\lblic МуRе.С'Цhglе (,stri,ng info)
f
rесtIлf!)ll = new Shap.e'Inf:o! info) ;
t ор = .1 ef t '" 1 О ;
b .o ttGm = rig.ht = 100;
}

Теперь вы имееТе ссылчн::ь:rn тип lШУ'FрП тица. харc;t:К:щр:изуемого значе)illем.


И здеt:ь возникает вопрос на миллион долларОв; ЧТО ·СЛУЧИТOfl. еl1ЛИ присвоитъ одну
переМfJннyIOтШla MyRec-tanglедругой такой же перемe.uнnй? С уч;етом тortJ, что вы
уже знаете о типах. характе~эуемых знач~. вы маж~ сдела':l'Ъ прщ!:Идыroе
npедnолож.еНие о :roм:, что целые данные (кэторые ИRС;:IiМОМ деле J.I форЮ1руют FnY
структуру) для ка·ждой переменной MyRe-с1;:.а11g:1е ДOЩКI:i!:il БЫТJ> lJезависимыми Э~­
ментами . Но 'Что МQЖН'о с:казать о внутреннем ССЬ/.iЮЧВОМ; TJnre? Будет ~1I0IШp01Ш-
1-10 полное С:ОСl'OЯНИе этого объекта или будет CKo:tmPOBi:цJa, ссылка на Э1'ОО' обьентl'
Проaнa:uи:эйруйте следующий upoграммный ПIЩ и paCI;MOТPIJ'fe рис. 3.14, RОl'ОРЫЙ'
МОЗl{ет ПОДСIШвать 'npавильпый OТBe'l:
-
156 Часть 11. Язык программирования С#

static void Main(stringlJ args)


(
// Создание первого об~ек~а МyRectangle.
Console.WriteLine(n_> Создание rl");
MyRectangle rl = new MyRectangle ("Это мой первый прямоугольник" );
// Присваивание новому МyRectangle значений rl.
Console. Wri teLine ("-> Присваивание rl типу r2");
MyRectangle r2;
r2 = rl;

// Изменение Значений r2.


Сопsоlе.WritеLiпе("-> Изменение значений r2");
r2. rectlnfo. infoString = "Это новая инФормация!") ;
r2.bottom = 4444;

1/ Print "alues
Сопsоlе.WгitеLiпе("- > Значения после изменений:");
Console. "у! tеLiпе ("- > rl. rectInfo. infoString: (О)",
rl.rectInfo.infoString);
Сопsоlе.WritеLiпе("-> r2.rectInfo.infoString: (О)",
r2.rectlnfo.infoString) ;
Сопsоlе.WritеLiпе("- > rl.bottom: {О}", rl.bottom);
Сопsоlе.WritеLiпе("-> r2.bottom: (О}", r2.bottom);

Рис. 3.14. Внутренние ссылки указывают на один и тот же объект

Как видите, при изменении значения информирующей строки с помощью ссыл­


ки r2 ссылка rl отображает точНо такое же значение. По умолчанию, когда тип.
характеризуемый значением. содержит ссылочные типы, присваивание приводит
к копированию ссьuюк. В результате вы получаете две независимые структуры.
RaЖДая из которых содержит ссылки, указывающие на один и тот же объект в па­
мяти (т.е. "поверхностную копию"). Если вы хотите иметь "детальную копию", когда
состояние внутренних ссылок полностью копируется в новый объект. необходимо
реализовать интерфейс ICloneable (это будет обсуждаться в главе 7).

Исходный код. Проект ValAndRef размещен в подкаталоге, соответствующем главе З.


Глава З. ОСАшrы' языка С# 157

Передача ссыочныыx ТИПОВ поз.начению


Очевидно. ч:те соылочпые ТИПЫ MOryT п~редimать.ся членам типов. как naра­
Merrpbl. Но переда"':lа объекта по CCЪLflК~ "РтЛi,-ч~тсл ОТ его передачи по значению.
Чтобы цонять суть различий, П~ДПОJ1ЩIQ1М. ЧТО У нас есть масс PerS@1] (nepcnнal.
оцределенн:ый следующи;м образом.

class Реузоп

Р(.1Ь] 1 с
st::rlng, fullblame ;
public byte "'og·e -;
pub1i с Реув (Ц1 ( s t ri ng 1\, Ьу te -а)
{
ful1.N.ame = !);
age = а,'

]'
public Per~on() {J
р1щliс 'toid Print1nfo ()
{ COnE:cle .WriteLinet "[O}, !l} года ( дет ) ", 1E"l1llNam!?, оач~ ); !

Теперь создадим :метод. который позволяет БЬL8ЫВающей стороне пересла'ГЬ


тип Perso:n Ш> ЗНач:евию (обр~'Ц1,'е внн:мание на отсутствие модифmtaтaров пара­
метра).

рщxliС static vo:ld SеТJ dА.Ре>IsсщВуVаluе fPer.s .o n Р!


{
/ / J,lз~eиste'1' iI1И 3'1'0 aosp<l.C'l' I р I ?
p.a,ge- = 92;
1/ У.I!идJ'%!I! JIИ В1ШUJIВabI(ЗоВ О'1!орока. ~1IXИе и~е]Jеаии'?
р = l1ew Рех sоол (" }ш..-.:.ки" r 192.) ;

Обратите вI:tи.1vEaliИе на ТО , что метрд SendAPt;!r$otJ.ByValUie () пщтается изме­


I1И1'Ъ получаему'ю ccы.тnty РеТзоn ца lЩВьЩ объект. а ТЗ\РЕе Юlмев:ить некоторые
дalIНble СОСТОЯНИЯ. Дющ:Йт.е провер:им работу этqго метода, ИСI[рЛЬВУЯ следующий
метод Main ( ) .
j ~tatic YDid Main (st.r·ing[] args)
о,

{
I/ Пере~а",а; C~OIblU.1X ~OB по зкаЧ8JfИ1D.
С~)Гjоsоlе.WхitеI,iпе(""'''* Передача объекта Persor. по значению "''''~'');
Pe.r50n frt'!d = nsw Ре.rsо оог!(НФред~, 2};
(0115<01 е . Writ;eLi ne i " Реr-sол до ЗЫЗ'ова -ПQ З liaqоению: ") ;
frеd.РrintIпfа();
SertdAPer.sOnByValue (fred) ;
Сош:юlе. WriteL ion-е ("1'о0О:I1Ю11 JПiJсле вызова .ею значению: "");
fred. P:riTJt Info (! ;

на p~c. 3 ,15 покаааЕ соответствующий ВЫВОД.


....

158 Часть 11. Язык программирования С#

Рис. 3.15. Передача ссылочных типов по значению блокирует соот­


ветствующую ссылку

Как видите, значение возраста (age) изменяется. Кажется, такое поведение при
передаче параметра противоречит самому термину ~по значению", Если вы спо­
собны изменить состояние получаемого объекта Person, что же все-таки копиру­
ется? Ответ здесь следующий: в объект вызьmающей стороны копируется ССЬика.
Поэтому. поскольку метод SелdАРеrsопВуVаluе () и объект вызывающей стороны
указывают на один и тот же объект, можно изменить состояние данных объекта.
Что здесь невозм.ож·но, так это изменить саму ссыJшy так. чтобы она указывала на
другой объект (зто напоминает ситуацию с постоянными указателями в С++).

Передача ссылочных типов по ссылке


Теперь предположим. что у нас есть метод SendAPersonByReference (). кото­
рый передает ссылочный тип по ссылке (обратите внимание на то, что здесь при­
сугствует модификатор параметра ref).
public static void SendAPersonByReference (ref Person р)
(
1/ Изменение нехо'1'ОРЫХ даlUnJX 'р',
р. age = 122:

11 Теперь 'р' ухазwвае'1' на новый об'ЬеХ'1' В динамической ПВИR'1'и!


р = пеМ Реrsоп("Никки", 222);

Как вы можете догадаться сами. это обеспечивает вызывающей стороне полную


гибкость в управлении входными параметрами. Вызывающая сторона не только
может изменить состояние объекта, н:о и переопределить ссылку так, чтобы она
указывала на новый тип Person. Рассмотрите следующий вариант.
static void Main(string[] args)
(
/1 Передача ссылочиwx '1'ИПов по сcwnхе.
Console.WriteLine("\n*** Передача объекта Person по ссылке ***");
Ретзоп mel = new Person ("Мэл" , 23):
Console.WriteLine("Persol1 до вызова по ссылке:");
mel.PrintInfo() ;
SendAPersonByReference(ref mel):
Солsоlе. Wri teLine ("Person после вызова по ссылке:") ;
mel. Printlnfo () ;
Гпава З . ОСНОВЫI!ЗЫК8' С# 159
Из рис, 3,] 6 ВИДцо. "ЧТ(j), ТJШ С и;менем Мзл ВQзвращаетс:я: после BJ>JaO,El:l lШR тип С
именем 'НИККI1.

РиС.3.16. ПереДВ"iа ОCblЛОЧНЫi( типов, тю ССЫЛКЕ! позволяет пере­


направить ССЫ11Ху

ЗОлотым .дра-вилом при :передаче ccьцlo'т;EIых ТИЦОВ' по ссылке являетtя cil1eцy­


JOщее.

• Если ССI:!IЛОЧНЫЙ тип передается по ссылке. ТО вызъшающан сторона моmет


И-З)iе~n' нетол::ЬКо состояние данных соответС'rвующего объекта. но сам
об~юn CCblJU<iU-

J(CJtOAНIdQ \СОД. T'Ipoen RеfТуреVа!ТуреРагаms размещен I;J подката)1оге, ООО~ТСТ&уЮщвм главе З,

Типы, характеризуемые Зl:lачениями, и ссылочные типы;


заключительные замечаниS1
Ч7'Обы зав~рщить обс.ужде,ние данной темы. ,изучите информaдkЮ 1'абл, 3.8,. в
~оТороЙ nPlffiОДИТсЯ краТRЩ[ сводка оcшuшыx. 'отличий МеждУ типами:. харaJt'Гери-­
зуемьц.щ з~аqением. ~ ссылоЧНЫми типамИ.

1аБJ1КЦЗ 3.8. Сравнение ntПОJ;l, характеризуеМЫХ:il~Щ(lе~lием" и СОbl110ЧНЫХ ТИПО8

Тиn~ харanеризуемыJi
BOnPQc
ЗН8че",МtЩ

Где разм~щщщ;я тип? .В Oi~Ke Вynравl1Я8МОl1 J!ИflаМlll'lе­


'СКОЙ памяти

Как ГlреДGТ81ЩIЩТС9 л~ременнзя? в виде m.жальной kопии' В виде ССЫЛКJ.1 на место в


памяти, заfl~tDе соответст.ау­
ЮЩИМ эк3.еМГlЛярам

Чiто RВJ1яеn:я базовЫм типом? QкаЗ~lвается npОИЗВОДНblМ Может получэ.Т~СЯ иэлю­


от S'y·stem. V,э.luеТуре бqго типа (Iфоме Sуэtеm.
va1 -u етуреJ, не Я8ЛЯЮЩ6ru·
СЯ ИЗ,олированным (подро.&­
НОСТ\4 в mЩlе 4)

Мржет ли тип бъrrь б~овым ;цтl Нет. Типы. хараt(теризуемые . рР., Если тип .не ИЗОl1I-1РQ.Ван,
Щ)у.гих типов? ЭNаЧ8l'tиями, вcsща lII.ЗоJiи " он' может быть базовым iЦЛi1
раваны и не могут бt:llТЬ РаС­ друrиx ТИП09
ширены

1
-
160 Часть 11 . Язык программирования С#

Окончание табл. 3.8


Тип, характеризуемый
Вопрос Ссылочный ТИП
значением

Каким является поведение, при­ Переменные передаются по Переменные передаются по


нятое по умолчанию при передаче эна'iению (т.е. вызванной ссылке (например. в вызван­
пара метров? функции передается копия ную функцию передается
переменной) адрес переменной)

Может ли тип переопределить Нет. Типы, характеризуемые Да, неявно (подробности в


System.Object.Finalize()? значениями, никогда не раз­ главе 4)
мещаются в динамической
памяти }1 позтому не требуют
финализации

Можно ли определить конструкторы Да. но конструктор, задан­ Безусловноl


ДЛя этого типа? ный по умолчанию. является
зарезервированным (т.е . ,
другие конструкторы обяза­
тельно должны иметь аргу­

менты)

Когда переменные данного типа Когда они Оl<азываютсЯ вне Когда для управляемой дина­
прекращают свое существование? контекста определения мической памяти выполняет­
ся сборка мусора

Несмотря на удазанные отличия, и типы, характеризуемые значением. и ссы­


лочные типы могут реализовывать интерфейсы и поддерживать любое число по­
лей, методов. перегруженных операций, констант. свойств и соБЬГГИЙ.

Операции создания объектного образа


и восстановления из объектного образа
ВвидУ ТОГО, ЧТО В .NEТ определяются две главные категории типов (характери­
зуемые значением или ссылкой), может понадобиться представление переменной
одной категории в виде переменной другой категории. В С# предлагается очень
простой механизм. называемый оnераЦllей создш-шя объектного образа (boxing),
позволяющий превратить тип, характеризуемый значением, в ссылочный тип.
Предположим, что вы создали переменную типа sho rt.
11 Создание значеНИJI ~ипа short.
short s = 25;

Если в приложении потребуется конвертировать этот тип значения в ссьmоч-


ный ТИП, ВЫ должны ~упав:овать" это значение так, как показано ниже.

11 "Упаковка" значении в объек~ную ссыпКУ.


object o bjShort = s;
Операцию создания объектного образа можно формадьно определить, КЮ< про­
цесс явного преобразования типа, характеризуемого значением. в соответствую­
щий ссылочный тип с помощью сохранения переменной в Syste m.Object. Когда
значение преобразуется в объектный тип, среда CLR размещает новый объект в
глава 3, ООНОВЫ яэыаa С# 161
дина1\ШЧedroй naмя:rй и Еоnиpует аначениесоответствующе:го типа (:в данном слу­
чае это значение 2q).в создaнны'й эвЗeмnnяp. Вам воэвращается ссЫлка на новый
размещеШШIЙ в na.мЛТИ об'Ьеm При иcnam.зова:нйИ тaкoro ПО$Ода у разра6отЧи:на
.N:EТHe 1IоэНИRaет необхоДИМОСТИ ИCIJолъзоватъ интерфейСНЪ1е .н.лш:сы.чтобъr Bpe~
меНно 06ращатьс.ll с данными стека как с оБЪектами., раа:мещеННЫмИ 13. динамиче­
СНОМ ПQМЯТJ.L
Обратная операция 'l'оже предУСМОТРена, и шl3"ыветсяя она ВОСё1l1Ш-ЮВленuе..м из
о.бъеЮШ·Ю20 образа {Uhboxing}. Восстановление из объектного обраэа явл.яетсн про~
цессЬм обрз"ГlIOГО преобраэования значенин. (:QдерЖаЩетосп в объе:ктной ССblJПCе. в
значение соответствующего. типа. размещ,аемое в стеке. Операция ВQС'.станОВЛения
иа объе:ктного Qбразз наЧиыается. с npоверки того, ЧТО hпl Дalllihтх., в IЮТОРЫЙ вы­
полняется восстановление. ·эквивалентен типу. который был при:веден:к объекту.
Если это так. :го вьmолннетсн обратное копировaнI1е соответствующегознаЧСНfl:Я
1'J ЛQRaлЬную перем/'шиуто 11 стеке. Например. сЛедУЮщая операЦl1Н ВОССТановл:ени.я
И~ объектного образа буде'! вьmОШ!ена ycneIillf(), 1ЮС}ШЛЬКУ соответствующий тип
Q-ЬjShоrt действительно имее-; ТИП S:flort (операцию преО:бразовJ.ШWI т1-шов в С#
мы рассмотрим подрооно 11 следующей. шаве, а пока ЧТО не слишко:м:беСШ)RоИ:теСЬ
'0 деталях).

/I Об~'Z!~ое преобраЗОВaJ:1Ие СOIoШXИ в соq'IlВетотвущее з·начеии:е short.


shGrt. anotberS'};HЭI:t = (Enort) objSlr,Grt;

Снова Qбратим ваше внимание на то·, что ВQсстано:влenие следует ВЫlIOJIНWГЪ в


соответствующий тип данных. Так, следУЮЩая програ:ммнаи логина восстановле­
ния из объшtт1шrо образа l'енерирует исключение IHvalidCai3tExcept.ion (обсуж­
дение вопросов обработки ис:ключений содержится в главе 6).
/ / HexoppeK!l'HOe ВОf:С'1'зковnеНИIEI ~З Qб'Ъех!t'НО:J;lО образ...
stati{: U"oid Main(stJ;iDg[] a.rgs,)

trj
~
// 'l'иn..s "УПa.lllозхе" ~ Э!I!О НЕ З-nt, а short!
irJ.,t i = ~ int.J obj Shcirt;

oatch (lnvalidC,astExceptHm е}
!
12onsc)le.Wxi tеИnе\"QЙ! \n [О! ", Ii!. TbStrir1g () ) i

Примеры создания объектныx образов


и восстаНовления значений
Вы. Haвepnoe. спросите. ItOrna действительно БЫвает необходимо вручную nЬJ­
ПОЛliЯТЬ преобраэовани:е в. объt".1crный ТИП (или восстановление из объектного об­
раза)? П~ДЪЩУЩИИ прим:ер был иr.КТiliiЧИТелъно иллюстратИRНЫfi>I. поскош.tty n нем
ДЛЯ данных short не было никa:Itои реадьной необходимости привед!ШШlК объ­
ектному ТИiIy (с последующИМ вос€тановлением даЮIЫX из объ~тного образа).
-
162 Часть 11 . Язык программироваНИR С#

Реальность такова. что необходимость вручную npивоДИть данные к объектному


типу возникает очень peДRo -. если возникает вообще. В большинстве случаев
компилятор С# выполняет такие преобразования автоматически . Например. при
передаче типа, характеризуемого значением, методу. предполагающему получение

объектного параметра, автоматически "в фоновом режиме" происходит приведе­


ние к объектному типу.

c lass Program
(
static void Maln(string[] args)

/ / Создание значеНИJl int (тип, характеризуемый значением) .


int mylnt = 99;

/ / myInt передае~ся: методу I преjЦПо.nагаnцeму


/ I по.nучение объекта, поэтому myInt приаОДИ'1'ся:
/ / к об'Ъе!l:'l'НОМУ '1'ИПУ АВ'1'ома'1'Ичесхи.
UseThis Obje c t(mylnt);
Co nsole.ReadLine() ;

stati c v o id UseThis Object(object о)

Console. Wri teLine ("Значением о является: (О}", о);

Автоматическое преобразование в объектный тип происходит и. при рабо ­


те с типами библиотек базовых классов .NET. Например, пространство имен
System.Collecti o n s (формально оно будет обсуждаться в главе 7) определяет
тип класса с именем ArrayList. Подобно большинству других типов коллекций.
ArrayList имеет члены, ПОЗВОЛЯЮIЦИе вставлять, получать и удалять элементы.
public c lass System.Collectlons.ArrayList : object,
Sys t em. Co llection s .IList,
System.Collections.ICollecti o n,
System.Co llection s .IEnumerable,
ICl o neable

public virtual int Add(object value) ;


pиbli c virtual v oid Insert(int inde x, object value);
public virtuai void Remov e(object o bj);
public vir t ual object this [int inde x ] ( get; s e t; }

Как видите. эти члены действуют на типы S ystem.Object. Поскольку все, в


конечном счете. получается из этого общего базового класса. следующий програм­
мный код оказывается вполне корректным.

static vo id Main( st ring[] ar g s)


Гnвва 3. ОQНОВЫI1Эli/JCВ С# 163
A=ayblst 1tIylnt.s = new AIrayLiBt(J:
myJnts.Add(S8) ;
/ну] !) ts .Add (З. 33) ;
ту] I) t .S . Add I f31se) ;

Но теперь с учеТQМ !3аше.го пониманпя ОChШQЧНЫХ ТИПОВ И типов. хаРflктери­


эуемыхан.аче:J:lИем:, вы можете епро"Ситъ: что же l-m самом де;ле размещае'I'СJI в
ArrayList? (ссы.1Ilщ? Копии ссъшок? Коmш струитур?) Как 11 в сдучае. с рассмо­
тренным выше методом U~еТhis.()Ьjес-t (). ДОЛЖНО быть 8СНО. что кa.жд.ьul 'Из ТИnOiJ
данны:,к System.Int32 перед размещевием.в Ar.rayL.ist в действителыIOСТИПРИВО­
ДИТСЯ R объеJqНОМ), пту. Чтобы восстзнg:виrь элемент ив nша АrrауList.требу­
~СЯ ЦЫll(ЩfШТЬ. uоответствуiomую операцию BOCCTaI-Ювления.

stаtiё v,oid BoxAndUnbo xInts()


1
I/ "VD&IC08~a!' дl"ВIfVX int в Arr.yList.
ArrayList my]rjt.s =' new ,/\,тrауLiэt (') ;
mуIпtS.Аdd(28) ;
rnylnts . Add jЗ .33);
mylnt&.Add(fal.se);

11 Иs:вnеЧeI.Ще пеР:ВОi!О э.nемеи'Ж'& из ArrayLi.st.


inl i .i rs,t1tеш = !iпt.)mуlntз [01.
Соnза J е . Wri tеLiпе ("дepВ'!,>IЪ<1 Э;rJеме.нтvм >Iвлg~·р.с: я [О!", f i 1: st.1 tero) ;

Беэ сомнения. операции upив€дel:IШI .к объектному nmy и восстановления из


объеК'ТI-Iогообраза требyкtt времени И, ёсли Э'fl) операции использовать без огра­
Щ)Чt'юЩ, это может ЮIИЯТЬ на llpoи;mодительность npйложения. Но t использова­
нием ТaR(IГО подхода ЯЕТ.вы можете с:имметрИЧI-to ра(50тать с nmами.. }щрanерщ­
~емыми значенИем, и со сcыл.чвьrми тиnам.-и.

Замечание. !3 С# 2' Р flотери ГlРОИ~ВОДИ1ельн~с"и из-за пр~едения " с€)ылочному' типу И аосCi.тэ­
нов:tlения !ltз объектного 06раза Можно нивелvrроооть ПуТем испопъзоваНИII 0606щеN",Й (gene-
rics), к,оторые бvдут pacqMorpeHbI в главе 10.

Восстановление из объектного образа


ДЛЯ пользовательских типов

Котда методУ. llредrшл.arающему получение ЭR3емnлнров Sys tem.. Object. пере­


.даются llолъэоваТf::J"1Ъс:ltae cTpyRтypы или перечни. ТО>il:е. происходит их приведеlЦ1:ё

R оБЪектному типу. Oдnaкo после получения В1({)дноrо оораметра БЫЗВанным мето.­


.дом IIЫ не ~ожет€ ПОЛУТ;ШТЪ ДОC"ry11 J\ каким бы то НИ было "щ:енам CTPYI<Тypbl fИJm
перечня), п6Rа не ВЫIIол::в.m:е операциlO ВОССТaIlOвлеНШI из объеКТ'НQГО образа для
даmшrо типа. Вспомним cтpy1cJypy MyPoint. определенную 11 этой Fшше выше .

$truct MyPb:LFlt
{
public iл .t х, у:
-
164 Часть 11. Язык программирования С#

Предположим. что вы посылаете переменную MyPoint новому методу с именем


UseBoxedМyPoint () .
stat1c void Main(string(] args)
{

MyPoint р;
р.Х = 10;
р.у = 20;

UseBoxedМyPoint(p);

При попытке получить доступ R полю данных MyPoint возникнет ошибка ком­
пиляции. поскольку метод предполагает. что вы действуете на строго типизован­
ный System.Object.
static void UseBoxedMyPoint(object о)
{
// ОШибха! System.Object не имеет чnеНQв-перемевных
// с именами 'х' и 'у'.
Console.WriteLine("(OI, (11", О.Х, о.у);

Чтобы получить доступ к полю данных MyPoint . вы должны сначала восстано­


вить параметр из объектного образа. Сначала можно использовать Iшючевое слово
С# i s для проверки того. что этот параметр на самом деле является переменной
MyPoint. Ключевое слово iз рассматривается в главе 4. здесь мы только предлага­
ем пример ето использования.

static void UseBoxedМyPoint(object о)


{
if (о is MyPoint)
(
MyPoint р = (MyPoint)o;
Console.WriteLine("{O), {1}", р.х, р.у);

I
else
Console.WriteLine("Bbl прислали не MyPoint.");

Исходный код. Проект Boxing размещен в подкаталоге, соответствующем главе 3.

Работа с перечнями .NET


Вдобавок к структурам в .NEТ имеется еще один тип из категории характеризу­
емых значением - это neреч1-tu. При создании программы часто бьmает удобно соз­
дать набор символьных имен для представления некоторых числовых значений.
Например. при создании системы учета оплаты труда работников предприятия
вы предпочтете использовать константы Manager (менеджер). Grunt (рабочий].
Contractor (подрядчик) и "Р (внце-президент) вместо простых числовых значений
Глава З" ОСНОВЫ языка С# 165
{О / 1, 2, 3 f. Именно пО этой причине Е С# поддерживаются IIольэовательcюrе
I1ереЧНИ. Нanpш..tер. вот IIереЧенъ Ептр-Тур.е.

/ / ДOJIЬ$овaorеiiIЬОХИЙ' ПЕ\речень.
enurn ЕтрТуре
j
Manager, /1 = о
Gxunt, 1I :;1
Cont.r", ct. о!' , '} == 2
ур
11 =3

Перечень EлtрТуре определ,нет Че'ГЪ1ре именованные itOHcтmIТbl, соо'тветот:вую­


щие н.онь:реТНЫ:М числовым зна чеШIЯМ. В С# схема нумерации по умолчЭНИ1О пред­
полагает наЧillIО с нулевоrо э:л:еМf."нТа (О) и нумерацию после.цуюших элементОВ ПО
правилам арифметичеСRОЙ IIpоrресс:и--иn + 1. Пр1J необходимости вы имеете ВО3-
МОЖRbC'IЪ изменить такое поведение на более удобное.

/I B.1ЦJIo ИУ*patUDI со "~еВИJ( 102.


enшn ElТlpType

!
Matla:ge'I' = 10~,
Grunt, 11 == 103
COh:,tract6r, /1 == 104
\rlP 11 = 105

Лереtrnи не обязан};l следовать стротому ПОCJIедо:вательноМ:У порЯJU\Y нумераnии


элементов. Ест! (по некоторой причине] вы сочтете разумным создать EтpT~'pe
так, НaR покЭ;эа:ао Ш1Же. 1(ОМПИ:ЛНТОр npим:.ет ЭТО бе,а возражений.

1I эnемвиlJ!'ы перечs,я se обяз;uш спедОИ&!l'ЬВ сorpОI'OЙ noc.n~OBa'1'em.HOClJ!'H!


елum Етр'Туре
j
МаШН]$Х = 1 О,
Grнпt = 1,
COHtrac:tor = 100,
VP = 9

Тип, используемый для .каждоrо элемента в переЧ}iе. по УМОЛ"IaR1iЮ отобража­


etся в Буs t~m. It:i't32. "ГЩtое повеД~ние при ftеобхоДИМОС'ТИ тоже можн:о измениIЬ.
Например. если вы Хотите. чтобы со();тветСТlJyющее хранимое эначеЦие Ernp'J':ype
было byte. а не i JiJt.• вЫ дoJ1жuы1 налиеать следующее.

11 ТеаеРЬ EпlpТуре ОIRобра,,.е'l'CJI, а byte.


епат ErnрТур& , byte
{
MaJ1ager = :1 (1 I
Grutlt ~ 1,
Сопt.'!:а'Gtбr = 100,
VI'= 9
)
-
166 4аеть 11. Язык программирования С#

Замечание. Перечни в С# могут определяться в унифицированной форме для любого из числовых


типов (byt e , sbyte , s h o rt, ushort, int, uint, l o ng или ulong) . Это может быть по­
лезно при создании программ для устройств с малыми объемами памяти, таких как КПК или
сотовые телефоны, совместимые с .NEт.

Установив диапазон и тип хранения для перечня, вы можете использовать его


вместо так называемых "магических чисел". Предположим, что у вас есть класс,
определяющий статическую ФУНКЦИЮ с единственным параметром ЕmрТуре.

stat ic v o id A s kForB o nus(EmpТype е)


{
switch(e)
{
сазе EmpТype.Contractor:
Console.WriteLine("BaM заплатили достаточн о •.. ");
break;
сазе ЕшрТуре.Gruпt:
Co nsole.W r iteLine("Bbl должны кирпичи укладывать ... ");
bceaki
c ase EmpType.Мanaqer:
С оп s о l е.Writ е Liпе("Лучше ска жите, чт о там с о пци о нами!");
breaki
сазе EmpТype.VP:
С о пsоlе.WritеLiпе("ХОРОШО, сэр''');
bre ak;
default: break;

Этот метод можно вызвать ТаЕ .

static v o id Main(string[ ) arg s )


{
/1 Создание типа contractor.
ЕmрТ ур е fred;
fred = EmpType.Contractor;
AskForBonus (fred);

Замечание. ПРi1 ссылке на значение перечня всегда следует добавлять префикс имени перечня
(например, использовать ЕmрТуре . Grl.lnt, а не просто Grunt).

Базовый класс System.Enum


Особенностью перечней .NEТ является то, что все они неявно получаются из
System.Enu m. Этот базовый класс определяет ряд методов . которые позволяют
опросить и трансформировать перечень. В табл. 3.9 описаны некоторые из таких
методов. и все они являются статическими.
ГnВBa 3. ос/щвы ~ЬШI С# 167
Табл.ща 3.,9. PSjA стаТИ'Iеских 4Лe!-I'ЬВ System .Eflum

Чnем Оп"'самме

Fo.rmal (:), Преобраэует эначен.ие д.аннего Пfпа пере-щя в эквивалентНое ctp.()j(O-


Вое. ПРЕЩставлеНfl~IJСООТВе'ГСТВИИ с указанным ФQрматом

GеtNаще() Возвращает ИМ~ (И1lи МВООИ8 ИМ81'1) дЛЯ константы с yкaМJ,HHЫM значе"
'GHNames' () ~ием

'1;e.tUnde·Йу il)gType ,( ) В'озВ]Защэет n1пдзнных, I-fдп(!)льзуемый Д11Я хранения значений' дlmHOгo


пере'lНЯ

G"tVaJ иеЕ ,() 8QЗвращает массив ЭН.ClчениЙ КОКСТElН1 данноФ перечня

I.$Def ir1e'd ( ) Возарщдает прlIIЗ'NВК существовакl'Il'I в ,lJЩIНQМ Гlepe 1 1He кi:J1:ICТW1ТbI с УII:Э­
заННbiМ 3НВ\l6Нием

Преобразуе'т cтpokOBoe представnвние имен или чИСflDВьр\ ЗНаче~lIйЙ


ОДНОИ ИЛИ 1-I6СКОlll:КИХ IЮнстзН-Г перечня в эквиваilентtfый объект перечня

Статический метод Е·nшц.Fоrmаt () МОЯСН0 Jl!t::IIОЛЪ3(ПЩТЪ С флагами форма­


'l1ИРОВaJ-ШЯ. :КOTOpы.~ расуматривlYЦIС;Ь ~~ цри обсуждении S'УS'!1., еm.Соп.s:оlе.
Например. Mm'КHO из.sдечь· строку с именем (уIЩЩlВ G). шестнщщатеричное (Х) или
<ЩС)lовае значение (Ь. Fи т.д.).
В .system.Eтi.um ,$Же определяется статичесК1'lЙ метод GetVBloes О. Эт{')т метод
возnращает экэе:мrI.i1др Sy~tem,Array (мы I1Jбсуди;м ~TOT оБЪСЕТ немногоnозже). в
котором liЮtщдrй элемент соответ<:т:вует царе "имн-эначение" Данного переЧНR. Аля
nримера рассмотрите следующий фрагме;нт прDграм:мнIJrО :кода.

5latic щ)l ·d Main{stIiag [) G·rgs)

/ / Пе'\fl1оп. юtфориации .Ц.ttR пер8!1ИR hpТypit.


Array obj = Егш'ffi . ·G et\i'alues (typeof (EtnРТУРЕ!) ) :
C'o.nsol е. Wti teL·i!lE: ("Б ЭТОМ f:iерёчне [О) членов.", o.bj' .l,ength) ;
foreach {.Етртуре е in: ш'j)

I
с'()!1;$ыl.writе(''с'1'рокц r и;менем~ {О ' },'·, e . To5tl-ing(» ,;
СGnэ:оl?WIitе("int: (IDJ), "1
Елum.Fожme.tltуре:()f(EmрТуре), е! "D"));
COQ5o.l.e . W:c i te ( "hex ~ ( ! О f j \ц" ,
Е'nuш •.Fо:r::шat.{tуреоf(Еmp'Туре), е, "Х" · ));

.1
КfШ вы сами ,1\'fOжете догадатЬСН. этот МОЕ ПРОГРа:'ММНdГО 1юда для nеречня
ЕтрТуре Пе'Ч.атцет пары ~11JV1Я-зна~ение" (В десятичном и шеGТНадцатеричном фор~
Ma-re).
Теперь и"Сс.rrедУем СВОЙСТВQ IзDеfin,еd. Это свойство IImшолnет вьrnсНить. ЯВ­
ля:ется ли да:ещa.FI строка "l,леl-ЮМ данного переч:ня. llредшшожим, чтФ I-IYЖНQ ВЫ~
яс}шть. ff.IЩнеТС.ЗJШ ЭI:Iа'[Jение g·alesPer:,sDfi (продавец) частью перечня EropType.
Д1гя этого вы доmItfllil по~лать укаванвой фy1nщии информацию о 'тиnе перечня и
crpol\Y. цоторую требуе'fCJl .Р pOBep:mъ (информац;пю о. типе можно. получить с по­
мощью оцерацщr t:ypeof, 1j:оторая подробно рассматривается в r:naвe 12}.

L
-
168 Часть 11 . Язык программирования С#

static v o id Main(string(] args)

// Ес'1'Ь nк значение SalesPerson в ЕшрТуре?


if (ЕпшТL. IsDefined (type o f (ЕтрТуре), "SalesPerson"))
Сопsоlе.WritеLiпе("Да, у нас есть продавцы.");
else
Console. Wr i teLine ("Нет, мы работаем без прибыли ... ") ;

с помощью статического метода Enum.Parse () можно генерировать значения


перечня. соответствующие заданному строковому литералу. Поскольку Parse ()
возвращает общий System.Object. нужно преобразовать возвращаемое значение
в нужный тип.

// Печа'1'ае'1' "Sally is а М&naqer" .


ЕтрТуре sall у = (ЕmрТуре) Епиm. Parse (typeof (ЕтрТуре), "Manager");
Console.WriteLine("Sal1y is а (О}", sally.TOString());

и последнее. но не менее важное замечание: перечни в С# поддерживают раз­


личные операции. которые позволяют выполнять сравнения с заданными значе­

ниями . например:

stati c void Main(string[] args)

// !СакаJl кз З'1'ИХ переиенных ЕшрТуре


// _ее'1' боnьшее чкcnоное значение?
ЕmрТуре Joe = EmpType.VP;
ЕтрТуре Fran = EmpType.Grunt;
i f (Joe < Fran)
Сопsоlе.WritеLiпе("3начение Джо меньше значения Фрэн.");
else
Сопsоlе.WritеLiпе("Значение Фрэн меньше значения Джо.");

Исходный код. Проект Enums размещен в подкаталоге , соответствующем главе 3.

Мастер-класс: System.Object
Совет. СледУЮЩИЙ обзор System.Object предполагает, что вы знакомы с понятиям и виртуаль­
ного метода и переопределения методов. Если мир ООП ДЛЯ вас является новым, вы можете
вернуться k. этому разделу после изучения материала главы 4.

в .NEТ каждый тип в конечном итоге оказывается производным от общего базо­


вого класса System.Object. Класс Object определяет общий набор членов, поддер­
живаемых каждым типом во вселенной .NEт. При создании класса. ДЛЯ которого не
указан явно базовый класс. вы цеящю получаете этот класс из System.Object.
Глав~ з . OCHOeJ~ gзы~а c~169

/I 1Iе_ное пощчеиие 1UIaCca из System. Obj_ct.


с1"ЭБ f.iеl10Сlэ.ss
{, . -}
Если I!1Ц же.тщете уточнить свои нам:ерен:ин. ·операция: CfI. оБОЗВ:ачаемая двоето-
чие~ (:). поэволяет явно JЩlзать базовый ЮIaС{1 :mna (rнmример. System. Obj ect).
11 в обоих CJ'Jуч&а х.пасс авио поnучается: из ЗуS teuC ОЬ] ect .
;:1<'155 ShapaInfo $уstеш. _ОЬjе~t
{ ... }

cla~ 13 St!apelnfo ~ obj ect.


{ ... }
' Тип 8уstепt.ОЬjесt Ol1.рс;щел;нr;:т дабор членов :эв:эeмnлнpa:и: членов тmacca (ста­
тическщ членов}. 3aмeтlD(, ЧтО некоторые из членов экземпляра объявляютсн с
иЩ!ользоианием ключевО1'О сщща v;i r tual и .поЭтому мnгyт nереопреёk:nяrtu:;cя. по­
рощцаемым классом.

11 1tпасс, sаllИllапф!й B&Jt'8ЫC1II)'1D позиЦИю:в . nт :


/ / System.opjeat
паmеsрасэ System
(
public class Object
(
рцЬ1iс БЬjесt () ;
pUblic vi:Z:'щаl Вооlем Щ'qu.а~s (Object obj) i
public vЦtual. гntЗ2 GetЛаshСоdе() ;
publ ic' Туре .G,e tType () ,.
public virtual St:rlng TQSt.ting (') ;
prdt'e :cted virtua'l 170id Fi n aliz,e () ;
prCltected Obj ect МenФеrwi$е~lОIiе () ;
public s'tatic Ьс:юl ЕщuаJs (object objA, оЬjесt, оЬj:Б);
pUblic static ЬоЫ ReferenceEquaJ.$ (cbje:etQbjA" object objB);
}

в табл. 3 . 1Q предлагаются описания функциональных В/!)ЗМОЖiiостей методов


8l(3еМJJ.!lЯра Д71Я указаннoroобъен:та.

'rабmща 3.10. Наиболее важные членВI Sys tero .Obj ect


Метод экзеМIIJUlра
Описание
kJJacca ObJect
Bquals (1 Пр умолчанию вазвращаетtru€ (klС1Игlа), кщца сравниваемые эдемен­
ты .ссылаются на ОЩ1Н м тот же эл'эмент в памяrи. Поэтому иr:пользувт­
СА ДЛl'I cpaSHeHIIIII об~т-ных оcыl01c,' а I'te состояниЙоОъек:тов. ОБЬ/чно
nереоп~еделяетсятаlC'l 'побы ЗIiа.чеНие trl.ie ttО.э~ращалось ТOC:Д~I
KOrдa оравниваемые объекn.lимеют адинаковыe значения 8Hyтpelfflero
состояния (т.е. одинаковую семантику значений). При переanрвделенltИ
Есчuаl;; () следует также, переоnpедSIlИТЬ GetJlashCode ()
80З&ращает цeno~ значенйе~ иДентифицирующее объеu в псщS!ти .
ЕсЛli1 вы собlltраетв.оь разместить оnpедеnяемые вами' ТИf11'i1 в Т&:Iпе
Sуstею.Соlleсtiоns. Elashta151 е,. рекОМендуется Т1ереоnредели'l'Ь
эа.о.аi'!НУЮ по УМQJlча~IИlО реализациlb этого члена

...
170 Часть 11. Язык программирования С#

Окончание табл. З.10

Метод экзеМПЛllра
Описание
клаоса Object

Equals() По умолчанию возвращает true (истина), когда сравниваемые элемен­


ты ссылаются на один и тот же элемент в памяти. Поэтому использует­
ся для сравне~IИЯ объектных ссылок, а не состояний объектов. Обычно
переопределяется так, чтобы значе~lие true возвращалось тогда,
когда сравниваемые объекты имеют одинаковые значения внутреннего
состояния (Т. е. одинаковую семантику значений). При переопределении
Equals () следует также переопределить GetHashCode ()
GetHashCode () Возвращает целое значение, иде~lтифицирующее объект в памяти.
Если вы собираетесь разместить определяемые вами типы в типе
System.Collections. Hashtable, рекомеl1дуется переопределить
заданную по умолчанию реализацию этого члена

GetType () Возвращает объект System. Туре, полностью описывающий данный


элемент. Это АТТ\·метоД (RunTime Туре Identification - идентификация
типа в среде выполнения), доступный для всех объектов (соответству­
ющие вопросы обсуждаются в главе 12)
ToSt.ring () Возвращает строковое представление данного объекта в формате
пр ос траНСТВ0 Имен.имяТипа (т.е . полное, или абсолютное имя) .
Если тип определен не в рамках пространства имен, возвращается
только имяТипа . Этот метод может переопределяться подклассом и
возвращать не абсолютное имя, а строку пар имен и значений, пред­
ставляющих BHyтpe~IHee состояние объекта

Finalize () Этот защищенный метод (если ОН переопределен) вызывается средой


выполнения ,NEТ; когда объект удаляется из динамической памяти.
Соответствующий процесс сборки мусора рассматривается в главе 5
МеmЬеrwisеСlопе () Защищенный метод, возвращающий новый объект, который является
"почленной" копией данного объекта. Если объект содержит ссылки на
другие объекты, то копируются ссылки на соответствующие типы (т.е.
выполняется поверхностное копирование). Если объект содержит типы,
характеризуемые значениями, получаются полные копии значений

Поведение System.Object, заданное по умолчанию


Чтобы продемонстрировать некоторые особенности принятого по умолчанию
поведения базового класса System.Object. рассмотрим класс Person (персона),
определенный в пользовательском пространстве имен ObjectMethods.
11 J(.nючевое слово 'патезрасе' обсуждае'l'Сfl в Itонце Э'l'ОЙ I!Л&В~.
паmеsрасе ObjectMethods
{
class Person

public Person(string fname, string lname , string З, byte а)


(
firstName = fname;
lastName = lname;
SSN s;
age = а:
Гл.аsа З. ОСНО.ВЫ !lЗЫkа С# 171
рм1 ic PerSC[J)! ) I I

/1 .пероовa..пыote ~ёtJiКi1e (д!!flJlliП! СОС'1'ОRИИII') •


pllbl ic ,s 'lr iлq fi r st Ni:П[lе;
public s1.riiJg lastNarne;
рцЫ:!, с s t ring SSN;
P'l.1DblC .bytE ag€;

Теrщръ ИСIД)щ,зуем тип t>e'J: 50Л.в рамках метода Mai n ( ) .


stац,с vol.d Маi п :,эtrir!g[! arg.s)
t
Соn,ю1е. ,WritеI,iле("** "' ''''' Работа с :классом ()bj~c;t*"**"\JI"!;

Ре.t::юп fred = new Реrsол ("Фре.д" т IJ'К)1ар,к", 1'111-11-1111", 20) i


С-опsоlе. Writ'e Line (.1_> fred. ToS:triJ!:C;p {D J ", fred. Tostring О ) ;
Сош;оls .Wlri teblne (11,_> f:ted. GetHashCode: IO}" т fr'ed . Ge'tHa.sl'ICode (.) } ;
Console .WriteLi:ne ("-> базовый масс длR 'fred': (О) ",
fred.GetTypeO .ВаsеТуре);

11 СО\9давие ;ЦОПО.nвитe.льR1tlX CC:WnOIQ .на 1 fred' .


Ре! $Оn р2 = f r 'ed;
O'bje-~t о = р2;

11 Y~&SJil8i!1JD'1' .пи lIсе 3 Э, КЗeJIПJIRp& Jla О;J(ЙН об'bl!lХТ :в I1ёUaI'mf?


if (0. Eqaa:ls (fred) Н р2 .:Eqt1al s (, о) )
Со!) 5<;>1 е. Wri teLlne (" fred, р2 и: .о се;ылаютсн на OД~H .Об'1;е,~Т'! "")' ;
Солsо1е.RеаdЫnе(};

Na рис. 3.17 покasaн вариаит BЬJВOдa. полученного при TeLTOBCIМ запуске :upo-
граМмы.

Рис. '3.11. РеализаЦИя члеНОв Sys,t em. ОЬ j е с !:', заданная по УМОJ{qа~1ИJO

'Обратите ВIiиыatmе на ТО, что SМЮllЩК I10 умолчанию реализа'ф!я ToS'tring ()


ПРОСТО возвращает полноеи.~'ТИlIа (нэцример, в виде лр.оtтраRСТВОJlJ.М61-I.имяТипа).
Меroд GetType О возвращает объект System.Type. который о-пределяет cDой­
ства В:аэ'еТуре (как вы .можете ДQГЗ.lЩТРСЯ сами. оно идентифицирует полное J(МЯ
базового ЮIасса: дацноro 'ГИШ:l).
Теперь раССМОТРт! ЛРОГРaММI-IЫЙ ПОД, испо:льзyюiЦИЙ метод Equals П. Здесь в
управляемой ДU1:Iа.'fo,Ш'qеСRОЙ пам1JТИ ра~щаетси новЫЙ объект J?€rso.n. и cCblJlНa
на этот объект зanом:инаетс,n в ССIo1ЛО<;IНР:ir перемеimОЙ fХ'Бd. Пере....rенн:ая р2 тоже
-
172 Часть 11. Язык программирования С#

имеет тип Person. однако здесь не создается новый экземпляр класса Person. а
присваивается fred переменной р2. Таким образом. и fred, и р2, а также пере­
менная о (типа object. которая была добавлена для полноты картины) указывают
на один и тот же объект в памяти. По этой причине тест на тождественность будет
успеIIIны •.

Переопределение элементов
System.Object, заданных по умолчанию
Хотя заданное по умолчанию поведение System.Object может оказаться вполне
приемлемым в большинстве случаев. вполне обычным для создаваемых вами типов
будет переопределение некоторых из унаследованных методов. В главе 4 предлага­
ется подробный анализ возможностей ООП в рамках С#. но, по сути, nEреоnpеделе­
ние - это изменение поведения наследуемого виртуального члена в npоизвоДНом

классе. Как вы только что убедились. System.Object определяет ряд виртуальных


методов (например. ToString () и Equals ()). задающих предусмотренную реализа­
ЦИЮ. Чтобы иметь друтую реализацию этих виртуальных членов для npоизводного
типа, вы должны использовать юпочевое слово С# override (букв. подменятъ).

Переопределение System.Object. ToString()


Переопределение метода ToString () дает возможность получить "снимQ'l~" теку­
щето состояния объекта. Это может оказаться полезным в процесс е отладки. Для
примерадавайте переоnpеделим System.Object.ToString() так, чтобы возвраща­
лось текстовое представление СОСТОяния объеJ<та (обратите внимание на то, что
здесь используется новое пространство имен System,'Text).
11 Нужно СОCnil.'1'ься: на System. Text ДЛЯ: доступа к StrinqBuilder.
using System;
using System.Text;
class Person

11 ПереоnpедenениеSystem.Object.ToString() .
Pllblic override string ToString ()
(
StringBuilder sb = new StringBuilder();
sb.AppendFormat(" [FirstName={O}; ", this.firstName);
зЬ. AppendForma t (" Las tl'{ame= { О } ; ", this .las tName) ;
sb.AppendFormat(" SSN={O);", this.SSN);
sb.AppendForrnat(" Age=jO)]", thi,s.age);
retufn Sb.ToString();

То, как вы форматируете строку, возвращающуюся из Sу s t е m. О Ь j ес t .


ToString (), не очень важно. В данном примере пары имен и значений помещены
в квадратные скобки и разделены ТОЧl\ами с запятой (этот формат используется в
библиотеках базовых классов .NEТ).
Глава :). ОСН.ФВЫ язьrка С# 173
в этом nPИМl1Jе ИСП0JIb3уется новый тип Slys.tеm.'!екt:.strinч)3u,i1dеr. Jtщ'Qрь1Й
будет подробно ОПИСa1i . ПОЭ-Же. Эдесь c.т:reдyeт толыtр ПОдчepRНy'P>. ~ТQ Str;iпg;6uildеr
обеспечивает более эффективную альтернативу :конкатенации СТРOI( 1'1 С#.

Перео'лределение System. ObJect. Equals()


Давайте переопредеЛЙ1\.1 и поведе·вие S'i'stem.Obj ec.t ,Equals (), чтобы ЦМe-FЬ
воамОЖНОСТЬ работlПЪ с ceMaнmwcoй. QCН08ЩiНOй на .знпч.енuяx. На:п~. что по

I умолчанию Equa.lS () возвращает


указываю'! на один И тот )ке объект
:tru:e (истина}. коrда обе сравЩIВаf;J':tI.f.!Iес(;ЪD'IЮI:
11 дmlзмичеC'RОЙ пщ.tити. Одн.цно чаСто быва­
ет нужно не. то, чтоб.l>I;:ще ССЫЛКИ уваэывали :на один объект в .J;faм~. а чтобыI
два о(!iъекта..имели OД1>UtaJCOBble сост\i)ЯliИЯ (В случае Рех БОn ЭТО Qз~ачает равенство
значеImЙ пате, 53}'] и аче).

риыi: overr1d€ bool Ечtlаls{оЬjесt о)


J
/ / УбеjфDlСR r '1'1.10 IIWЗwaа:ацая: C'1l0pc»l. ~оcшn.е'1'
I/ ~ейсшви'1'anъJWЙ оkеюr: Person.
i f (о :! = n:ull && о is person)

I
/I 'Теперъ npollep_, Ч'l'О ~&JПWЙ 'o&J;ex'1' P'erson
/I k 'l'е1l:УЩИЙ оl$иJW (thia) teeCY!1l
I I /qAЮlа-.сО!l~ ~фо~.

I Pers.o'n temp =
if (temp. firstName ==
(РеrSQЛ)С);

thiэ .• firstName &&

I temp.la·stName == th:i5.1astNa:me· &.&


ternp.SSN == this.SSN &&
temp. age == thi s .a,ge)
retUIIJ t.J:ue;

retuI n fаlзе; / / Не одиааКGa)l.1D'

Эдесь с помощью ключевого слова is язы1-ta С'# .ВЩ сначала npoверяете, что цы­
зъtвaюiЦая CТOPOI'Ia. действи:телъв:о перецает методу Equals. () объект :Per so:n. ПQсле
атоl."О нужно сравнить ЗШiЧ'еНИе поступающего napaMerpaco значениями що.де9
,цанных текущего объекта (Dбратите Dнnмaние на иorюцьзование ЮIЮчевоrо с.лР.Бil
this. которое ссылается на текyщJIЙ объект).
Пр.ОТQТJ-Ш Sy st е tn.. Obj ее"!: . Ечиа 1 g () npeдnoдaгaeт ll.Oлучекие единственно­
го аргумеtlта ТИШl object . nQЭТОМУ вы доЛжны вьшалиитъ щшый ВЫЗОВ МieTOД~
Еquэ.ls () . 'iТобы ПOЛJ"Ч}fТЬ доступ 1\ членам тнпа I.'ersot:\. ЕС,lЦifэначения пате, S5N
и age двух. объектов будут иденти~ы. вhi имеете даа объек:га: с одина:кОВЫМИ' дан­
ными СОС.!fOЯRин. I1OЭ'roму воз:вратится true ()IC'IШЩ). Если :ка:к.ие-то д~e БУду'f
раЭJrИ'Чаться. вы получите false (ложь).
Пбреоnpеделив System. Object .ToString () ддя данного класса. вы получает~
QЧem. простую возможность переоnределенmtSуst~m ..abj E'ct .'Eq'llals () . ЕCJЩ :ЦО3-
вращаеМ'Ое й3ТоЗtr:iл'g () значение учитывает все члены тепущerо WIJ3.cea (;и дан­
ные базовых кnaccoв). то метод .Equals {) может nPO~TO сравнить эначeцщJ соот­
ветСТВУЮIЦЯX стро:ковых ТИПОВ.
174 Чаоть 11. Язык программирования С#

public override bool Equals(object о)


{
if (о != null && о is Person)

Person temp = (Person)o:


if (this.ToStrinq() == o.ToString(»
return true:
else
return false:

return false:

Теперь предположим, что у нас есть тип Car (автомобиль). экземпляр которого
мы ПОIIЫтаемся передать методу Person .Equals ().
/I A!lТОlllобили ~ это не moди!
Car с = new Car();
Person р = new Person();
p.Equals (с);

Из-за проверки в среде вьшолнения на "истинность" объекта Person (с помо­


щью оператора is) метод Equals () возвратит false. Теперь рассмотрим следую­
щий вызов.

// Ой!
Person р = new Person() i
p.Equals (mJll):

Это тоже не представляет опасности, поскольку наша проверка предусматрива­


е'!' возможность поступ.пения пустой ссьшки.

Переопределение System. Object. GetHashCodeO


Если l{ласс переопределяет метод Equals (), следует переопределить и метод
System.Object.GetHashCode (). Не сделав этого, вы получите предупреждение
компилятора. Роль GetHashCode () - возвратить числовое значение, которое :иден­
тифицирует объект в зависимости от его состояния. И если у вас есть два объеItта
Person. имеющие идентичные значения пате, SSN и age, то вы должны получить
для них одинаковый хеш-код.
Вообще говоря, пере определение этого метода может понадобиться только тог­
да, когда вы собираетесь сохранить пользовательский тип в коллекции. использу­
ющей хеш-коды. например, в System.Collections.Hashtable. В фоновом режиме
тип Hashtable вызывает Equals () и GetHashCode () содержащихся в нем типов,
чтобы определить правильность объекта. возвращаемого вызывающей стороне.
ПосItольку SysteПI.Object не имеет информации о данных состояния для произво­
дных типов, вы должны переопределить GetHashCode () для всех типов, которые
вы собираетесь хранить в Hashtable.
Есть много алгоритмов. которые можно использовать ДЛЯ создания хеШ-кода.
I{aк "изощренных". так и достаточно "простых". Еще раз подчеркнем, что значение
хеш-кода объеI<та зависит от СОСТОЯНИЯ этого объекта. l\ласс Sуstеш.Striпg име­
ет довольно солидную реализацию Getl-lаshСоdе ( ). основанную на значении сим-
ГЛВjJ8 З . Оснрвы ЯЗЫJfа С# 175
вDлъных да.нных. Поэтому. если .можно наЙти cTp0КDBoe поле , КОТО'рое будет jI-ПJ ·
палъны1M ДЛЯ :всех рассМатриваемых объе.ктов (например> поле SSN длн объе1tТов
perso:n). то можно вызвать Ge.t!1asb'CQde () для стро:к"вого преДС::rnI!ленИи такогО
ЛОШL

J !вo.sараза.ае.'l' хеш.-·XQn росвозе SSN.


pu.bliq ove:rride iot GetHashCode()
j
ret.urn SSN[.GetHashCQde ();

Если вы не сможете указать nодходmциii элемент дa1JЛЫХ. но переопределите


ToStriIl'g (). ТО Мf)ЖНО просто возвратить хеш-кодстрщm, возвращенной: вашей ре­
азшззцие:Йтоs.tring () .
1/ Вопра$е!1' ~81It-JI:O~ ка ОснО8е П()ШоЗОЖ&'l'em.схо'J:'О ToSt:ring () .
Pll\Jlic oyerride iDt Get.НashCoQe ()
t
retu:!'n ToStr:ing {) . Get;HashCode( ) ..
}

Тестирование переопредеnенных чnенов


Теперь можно прове.ритъ обновленный ЮIaСС Person. Добавьте е.ле,цуЮщий про­
грщ.1МНьaf КОД В MttrOA Main() и сравните резуnътат его ВЬШО;lЦl.eНJUf с тем. что по­
lЩЗа:но ~a рис. 3.18.
static vфid Mal:n (strin.g [] .arg~.J
{
11 ВНИМАНИЕ: аши ·о&мIC1nt A~ БЫ!1'Ь идеи'l'И~.
Per$or: рЗ = ne-w P~rson("Fred", ".Jco nes" ., "22.:2-22-2222", 98);
Person р4 ~ new Pe,r.son·(·"Fred", "Jопев"., ~2Z2-2:2-?2'2~"" , 98);
/.1 ToI."дA З1rИ X8IК-XOЦR ,; . С'1'рО1СИ буцу'l' ОдиааХО.ВJВ4И.
Console .·W.'r.l teLine (" -;> ХеЩ'-КQД для р3 = {() J", р3. GetRashCode () .;
C;or,sol е·. W~ i teLine '\ "- > Хеш- ю;;,:о. ;!lnн р4 = f О ) ", р4. GetHa~tJCo.de () ) ;
Со,ns.Qlе"W·ritеLiпеt~-> СТРбка для рЗ = 10}" , p3 .T ~$,tring(»);
Сош~оl е .. 'Wr i teLipe (", -> Стрcmа ддя р4 = ! О)", р4. 1'O'St.r iлq () ) ;
11 Эде~ COC'1'O~ Д~ !S5IТЬ одинаJl:OВUШ.
i f (р .з .E.q;aa-ls )!р4)
'Сопэ.)} е _.WJri teL.i 1те (" -> С ОСТО"1m!Я рЗ И f!J4 оДИнаков!>! \ ") :
else
/)QЛsоlе .:Wri t€Lil;1e (" -> СЬС'ТО'ЯНИЯ рЗ И р4 р.а.з.пичны!") ;

11 ИЗмеlODt "9'е ;ц.J'IR р4.


Gonsole. W.riteLine (" \п-> Изменение age Д..'1>J3 р4 \011) ;
р4. аче = 2;

!I Теперь СОС'1'О"iDIR иеодlЩaJЩJW·; .хеш-ходы и C'1'pOJ.Or б1,fдY'l'· pa'$IIJoIМК.


Соnsоlе.WгitеLiле(,"-> С~рш~а для р] = j.Oj"T р3, ''rоstriщ/());
Ссйзо],е. Wri teLine ("-> СТРб!(а :Ц.iТJt р4 = i О) ", рс4. тоstriпg- () ) ;
C(linSi:JJe. Wri teL.ine ('''-> Кеш-код для рЗ = ! О}", рЗ . GеtЯа·,shеоdе- () ) ;
Сопsоlе.Wг-i.'te.Linе ("-:> Хеш-код для р4 ~ [О}". р4·ЧеtНаЗhСоdе (») i
176 Часть 11. Язык программирования С#

if (рЗ.Еquа1s(р4))
Сопsоlе.Wri~еLiпе("-> Состояния рЗ и р4 од и на к овы!");
e1se
Сопsоlе.WritеLiпе("- > Сос тояния р3 и р4 различны!");

Рис. 3.18. Результаты переопредепения членов Sу stem. Obj е ct

Статические члены System.Object


в завершение нашето обсуждения базового класса .NEТ. нах:одящегося на вер­
u.mнe иерархии классов, следует отметить , что Sy s tem.Objec t определяет два ста­
тических члена (Ob j e c t.Eq ua l s () и Object.Refer enc eEquals ()). обеспечивающих
проверку на равенство значений и ссылок соответственно . Рассмотрим следующий
программный код.

stati c void Main(string[] a r gs)


(
11 Два обrъеJtта с идентичной хонфигураци8Й.
Pers o n рЗ = n e w Р еrs ол ("Fr ed ", "Jones", " 22 2- 22 - 2222 ", 98);
Pe r son р4 = n ew Per s on ("Fr ed", "Jon e s ", " 222 - 22 - 2222 ", 98);
11 О.цинахоlnl ли СОСТОllНИfI р3 И р4? ИСТИНА!
Conso l e . Write Line ("Од и на ко вы ли с о ст о яни я р3 и р4 : {О} ",
оЬj е сt.Еquаls(рЗ, р4));
/ / ЯвЛRXn'Сfl ли ОНИ одник об'loE!Jt~ОИ В памити? ЛОЖЬ!
Conso le. Wri te Lin e (" Ук азыва ют ли р 3 и р4 н а о ди н о б ъек т: {О) "
object.Reference Eq ualS (p 3 , р4));

Исходный код. Проект ObjectMethods размещен в подкатanоге, соответствующем главе 3,

Типы данных System (и их обозначения в С#)


Вы, наверное, уже догадались. что каждый внутренний тип данных С# - это на
самом деле сокращенное обозначение некотороro типа, определенного в простран­
стве имен Sу stвrп. В табл. 3.11 предлагается список типов данных System, указаны
их диапазоны изменения , соответствующие им псевдонимы С# и информация о
согласованности типа со спецификациями CLS.
,
Глава з . Ос;мевы языка С# 177
Таблица 3.11. 1иnы Sys t e.m и их оБОЗНС1чеfolИЯ в С#

С)6ОЗНll1е· Соrnарован-
нне 11 С# НОСТII с c~e
Тип System ...
д .,.азоН мзменеН... Описание

sby t e Нет sу~t еm.БВуt:е Ф ~ 128 .дo 121 8-битово.е ЧИСЛО


со знаком

b yte да Sys t em.By te от Одо 255 8:-битовое 41о1СnО


без зна ка

sJ10rt да sу st8IП_Iп t lб от - 32768 ДQ · Э2767 16-битовое ~ИС[,Jо


00 знаком

1,Js·hor t He~ Sуstе m.UI n"t l'б от .о до 65535 16-битовое число


без знак~

i nt да S,/s·!'. еro.lл t3'2 ar -21474НЗ648 32~битовое ЧИСЛО


ДО 2147483647 со знака",

uint Нет sу st еm .lJI г.t З 2 от .о ДО 4294967295 З2-битовое ЧIIIСЛО


без· знака

l O;t'I.g Да s у s t.еm.I Тitб4 от-9223372.о36854775808 64-битсвое чием


дО 9223372.о36В547758.о7 со знаком
ul Oi1g . Нет S ув t еm.Ul пt б4 QTOAO Q4,битовое число
18446744,.o73J09551615 без зна~а
ch a r Да S)'Btern. Ch a r от UOOOG Oтnельный 16·
доUffff бl1ТОSЫЙ символ
Unicod&
f.lCfa.t Да SystE;ffi.SinqJ:e от ·1.5x1'.o-41? до 3.4)<. 1(jза 32-БИтовое число
с пnавающим

деОЯТИLI~JЫМ раз-
дели.телем

dOllble Да Sуst.еЛl. О о иЬа е от 5..O~ 1(jIJ24 до 1.71<.1 ОЗ 08 64·'битщюе ЧИСЛО


с плавqющиl'.1

деQЯТI1 ~НЫМ р"3З'


Д6JUIIТелем

bool Да Sузtе m;во.оlеа n t,t"ue J,oIJ1I11 f aI&e Предстаliляет "\о1с-


T~HY Wl~1 110ЖЬ

d e ciroal да. SYS'tern. Deci rnal от 100 до 1028 96-битовое число


со знаком

,St .! :in g да S)':;te m. Stri o,g Qграниченосистемной Представляет


памятью набор СИМВОЛQ8
Unicode
9bj ect Да S}lste.m. Object ЛI0f>ой тип можно ,оохрэ.· Баэов.ыЙ клаСIJ
нить в оБЪЕ!1ffiI0Й пере - асех типов вш
меНflОИ вселенНоЙ .NET

Зам.ечанме . По УМ'ОЛ'lаА ИЮ .деЙствительныЙ чИt:ловвй литерал справа от ·операции ПРИСВЗИВIlI'IИR


I11I1rеРl1реТИР'lется, )(;~ double. Поетому; чтобьт J,1НИЩ1миаирщщть перемеwную тИпе floa t .
йспользуйте оуффИКС f ми F' (fjВПРJdМер. ':' . ЗР ),
-
178 Часть 11 . Язык программироваНИR С#

Интересно отметить, что и примитивные типы данных .NEТ организованы в


иерархии классов. Отношения междУ этими базовыми типами (КЭR и некоторыми
другими типами, с которыми мы познакомимся чуть позже) можно представить
так. как показано на рис. 3.19.
КЭЕ видите. каждый из этих типов, в конечном счете, получается из Syst em.Object.
Ввиду того. что тание тшIы данных, как. например, int являются просто сокращен­
ными обозначениями соответствующего системного ТШIа (в данном случае типа
Sys tem. Int32), следуюIЦИЙ вариант СИНТaRсиса оказывается вполне допустимым.

/1 ПОМИК'l'е! в С, i.nt - это просто сокращение дпя Sуstem.IпtЗ2.


Console.WriteLine(12.GetHashCode()) ;
Сопsоlе.WritеLiпе(12.Еquаls(2З)) ;
Console.WriteLine(12.ToString() );
Console.WriteLine(12); // ToStrinq() вызываеТСR автоматичесхи.
Console.WriteLine(12.GetType() .BaBe~ype);

Туре

- String

- Aц~y

I!юб&й,JМh•.
- " Excepti:on ~t.i~Ът·: h--====:::::::::"~____ .....J

. YAlи~~yp~.. '
Явnяeтся .
aтp~,
ИЛИ~~М;
1 .
но нelJcnacCOU.
. '" .

Мultica;stOelegate
...... .

SByte

Рис. 3.19. Иерархия типов System


ГЛЗ!JЭ 3" OCI'tQltbI ЯЗЫК~ С# 179
к тому же, nО:СКОЛЬ.fl;У в'се типы значений имею.т 1tORcтpYRTOP, 3адa.I'JныЙ п'О·
умо.lt1'i!';llП1l0, можно создавать системные. типы с ПОМО1ЦЬЮ lШ1ОчеВQГО СJ.IOЩ'J П€1lI1. В

p~i,\YJ1bTaTe чего переменноЙ.. IС тому же. будет ПРИСВОeJiO ЗНачение по УМОЛЧ<ЦllIЮ .


Хотя использование .IOIючевОГQ f'.iIOB.a .!i·ew при создании типов ·данных S ув te:fl1 вы-
1ЩIДWГ нееколыю "1ilеyтwюжИм". с:ледylOШ!U! конструкIIйЯ ок<!:зываеff-Я в С# си:втан­
fЩЧ!еш& npавилыrой.

1/ Cnе,ч)']lilЦИe опера!Dli;ipы ЭJCВJIВ&.!"1eJ1"ИЫ.


bQol р1 = nE'M Ь~зJ () ; 11 .ыI = .f"als:e.
Ьо:сй Ь2 = false;

Кстати. заметим. что :можно создаваТJ> сис_теМНЪJе тцnы ДЗНRЫХ. Иf:1l0ЛЬ3yst аб­
солютные имена.

/I c.nе.ч)'XlllVi!l~ опеflа'l'q~ !1'arat8 сеыаи'Ш!'iU!C1IIИ э~вива.пеlWlUiI.


Sys.tem.Bool bl = new S yst.€ H[I.Bocl(); // bl = fals$ ,
Sys.tem.BClol БЬ2 = false;

Эксперименты с числовыми ·тиnами данных


Числовые ТИПЫ .NET поддерживают СВОИСЦЩ И9хVаlщ", 11: M::i. :nValu€.'. сооб­
щающие Иifформаnю() о диапаз~ше цанныJ!:'' Fю'горы" может хр.анить данныИ
тип. ЛрСД1IDЛОШИМ. "{'I'O мы создали нес:колыr.o переменяы.:х 'I'И'llil зу.stеm.1JJf!tlб
(uлsigпеd sbo:rt - короткое целО(" бе.ззнака.) . КIЩ показmю НЮRе.

static void Mail'1 (string!J агgя)


(
Sys teJJl. Olnt16 .!nуП Int 16 = 30"00 0;
Сопsоl€.W.ritеLi.пе("[i1аксимум для Olnt1f.: !О] ", .UInt) ·6 .)4axValue);
СОПS(i)lе.WritеLinе("Ииниму.м .tIдя
UInt.16:JOI' ", Ulлtlб.МiпVа.lue.);
СощJ:оlе.Wr-it..еLirН:J(""Е!н.ачение равна. : {О) ", ПlуU: lntlб,);
С·опs-оl'е.. Ыrit'S'L iпе ("'Я есть: (Ь 1 ", D'lyLJJnt 16. Getтype 1) ) ;

/ / Теперь ;цmr сmo:pащЕ!lЩЯ system. UIn,t16 {,.. е.цт! ushort) .


l1shc-rt mуоtnе rl1Iл t1f, = 12000;
C:OftSole. W'r i te.Line ("Иаю::имУ-м для UInt 16: 1О .) ", ltshort .М4xValue) i
С':!)nsОlе.Vlrit~Lirl~("МИЯ~5М ,фl,>t UlntJ 'б: {О !' '1/ IJsStш:t.Мi.nVaJ.Uе).;
GQllS01e. Wr iteLine{ "ЗНiiJ.чеы:ие равно: {О} "1 m.уОtЬеrщ n Ц'6) ;
С'ойsоlе.WritеLi,п€'('''Я' ~CT·Ь: {О} ", JJlyot+l,e rlJIL1t Lб..Get'l'ype О 1;
СОПБоlе. Re,adLin,e () ;

.адобавок к свойстваМ Mi o\Z.al ue!MaxVa lue системuые тицы ~OГYT опред~.lJЯТЬ


другие nOЛезные члены. Например. тшt ЗУ5tещ. Double ЩЩDОЛЯет ПОдy'Цf'ГЬ :,П-IЗче­
Щ:IЯ ЕРБНОП и lnfioity.
Console. W-r::LtеLl!1е (П -> dQurЙе. БрэilОt1: { О 1 ", dour.,l.e . Epai.lonJ ;
сол:sоlе.!4rl LeLlne ("-> double.Po:sit .i-vеIп,finitу: (О } ",
doubl.e. РоsitivеInf'lпi ty! .;
COMJole .Wri teLine (П -> dO\lble. Ne.ga t,i vel[:nfiait у ~ j О} " ,
doJ:Щlе . NеgаtiVfil~J.n.itу) :
СО.шsо1е. •.Wri teLi 'Qe (" -:> double-. t-1ах\7;аl iJе: j.o 1", d01;Jb le .мaxVEйue) i
соnэоl.е ,Wri teI.ine ("->dJ)Ublе,.furNаlЩ6: {:O:l ", ,dщiblе .МinValueJ ;
180 Часть 11 . Язык программирования С#

Члены System. Boolean


Теперь рассмотрим ТИII данныхSystem.Boo1ean. В отличие от С(++). в С# един­
ственными возможнъrм;и значениями ДЛЯ boo1 являются Itrue I fa1se}. В С# вы
не можете назначать типу boo1 импровизированные значения (например. -1. О. 1).
что считается (большинством IIpограммистов) IlРавильным нововведением. С уче­
том этого должно быть понятно. почему System.Boolean не Ilодцерживает свой­
ства MinVa11le/MaxValue. а Ilоддерживает Tr1leString/FalseString.

// в С# не')! npоиsвOJlIoНWX '1'ИПо. Boolean'


bool Ь = О; / / Не,цопусo:rико!
bool Ь2 = -1; /1 Тах-е ие,цопустино!
bool Ь3 = truei /I Без npоблен.
boo1 Ь4 = false; 1/ Беs проблен.
Сопsо1е.WritеLiпе("-> bool.FalseString: (0)", bool.Falsestrinq);
Сопsо1е.WritеLiпе("-> b oo l.Tr ueS tring: (О}", bool.TrueStrinq);

Члены System.Char
Текстовые данные в С# представляются встроенными типами данных string
иchar. Все . NEГ-языки отображают текстовые типы в соответствующие базовые
типы (System.String и System.Char). Оба эти ТИIIа в своей основе используют
Unicode.
тип System.Char обеспечивает широкие функциональные возможности, дале­
ко выходящие за рамки простого хранения символьных данных (которые, кста­
ти, должны помещаться в одиночные кавычки). Используя статические методы
System.Char. вы можете определить, является ли данный символ цифрой, буквой,
знаком nyнктуации или чем-то иным. Для иллюстрации рассмотрим следующий
фрагмент программного кода.

static void Main(string(] args)

// ПроверЬ'J!е рабо'J!У сле,цующкх onepa'J!opo•...


Сопsо1е.WritеLiпе("-> char.IsDigit('K'): (О)",
c har.IsDiqit('K'));
Сопsоlе.WritеLiпе("-> cha-r.IsDigit('9') ; {О}",
char.IsDiqit('9'»);
Сопsо1е.WritеLiпе("-> char.IsLetter('lO', 1}: 10)",
char.IsLetter("10", 1»);
Сопsоl е .WritеLiпе("-> char.ISLetter('p'): {О)",
char.IsLetter('p'»;
Console.WriteLine(n_> сhаr.IsWhitеSра се ('ЭЙ, там!', 3): {Oj",
char . IsWhiteSpace ("Эй, там!", 3»;
Сопsо1е.WritеLiпе("-> сhаr.IэWЫtеsра с е('ЭЙ, там!', 4): (О}",
char. ISНbiteSpace ("ЭЙ, там!", 4»);
Соn sоlе .WritеLiпе("-> cha.r:.IsLetterOrDigit(' ?'): (О)",
char.IsLetterOrDiqit('?') );
Сопsоlе.Wri tе Liпе("-> сhаr.IsРuпсtuа t i ол (' ! '): (0\",
char.IsPunctuation(' I '»;
Глзвз З. ОСНОВЫ языка С.# 181
С'0л.sеl.е. WriteLine (" -;> t:ha.r. IS'P unctuation ( '>' ): ,{.О J"•
char . ul?UnC!tuq.tlon ( '>') -) ;
С'~n:эоlе. Wl-it€liJloе (""-> cfi ar. 1 sPLinctuatioГt (' , , ): j б Т" ,
~Ъ.aт . ISРШlсtuаt,.iоn ( ". ' ) ) ;

Ka,g видите. Щiя всех ЭТИХ crrатических члеJ;lОВ Sуstеm.СIщт при ~Ы.зове ИСДОдЪ­
зуетоя следУЮщее (ЮГJ'ЫШение: следует YR'oiIзать J,!Ибо еДИНственн1>IЙ Cn~IliOJ1. лиБQ
~poкy с чисдовым индексом, ко-rорьtй yR1;!.3ыв~TT мёстоположение проверяеМ0l10
CДМi1oдa.

Анализ. значенийстроковы)( данных


тиды д<iRнь1.x .NEТ обеспечивают .вО~OOЩiОСТJ> .генерировать переменную того
типа. который gaдaется данным TeKC'I'OВblM э"КВ'.щщлеНТОМ (J".e. ВЫПQЛНЯТЬ ctl1Щ1ЦЮ­
CUt(eCК'tIIl aнtVW3). Эта ВО3МОЖJ-10Cn может ОRaЗaТы;щ чрезвычайно ПOJIезnой тогда.
IЮГДq требуется upеобразовать ВВОДимые пол:ьзо:вате.ilСМ данные (:например. выд-­
денные в РIШЪ.<рЫIШJощемся списке) J,f числовые знэчения. Ра(;смотрите слtдующий:
фраI'~ент прorpаммtюго кода.

-sta.tic V'oi,j Mai!'i (' 8triл"1:[] args)


{

bQol lТ1'~/Boi::'l = 11001. Раrзе ("'I'rш?") ;


C011s01 е. Wri t eLihe ('" -;. зна'!еР.~е myEool: 1О')' "1 шу8шJl);
ct0'Jble :/'цуDbl = c:fьublе . .Раз:$~(Н99,884"J;
СО1}$1.;lе. Wr i-tеLiле ~.,,-> 'Знаqение myDbl: \:01" I myDbl ~ ;
int. rnуlпЕ = int.Parse("8") 1
СС1nзсЙе. Wrl·teLine ("-::> Значеl'l:ие mуlпt: f О t 11. шуlПtJ';
chgr myCtlar = cn·a :r . 'Раnе I п w П);
Ссшsоl-е. Wr i teLine ("-); 3наЧSJ?ие myCrlax;;1 'О J \в", J1"lусЪаr);

System.DateТime и System.ТimeSpan
в завершеНйе нашего обзора базовых типов ДillJ;Н:ЫХ позвалнге обратить ваше
внимание на '1'.0'. ЧТ(l) пространство имен Syst ет одределяет нес1ЮЛЬКО ПОдеЗ1U>LХ

ТИПОВ дапных. ДЛЯ которых в С# не, цредусмотрено ЮIЮчeвblX слов. Это. в qacт­
~()сти. ·тшrы D.at:el'ime и TimeSpan (задачу :ИСCJI6ДQван:ин ТИПОВ System .Gl1id и
Syst;em .'Vo1d. которые среди про~ ЦОIЩЭаны I;Щ рис. 3_19. мы оставляем на усм.о­
J'ре:рще aaн:в:TepecOBa1mЫX ЧИ1'ателеЩ.
11щ Dat-€'Iiтe соДержит данные. представлнющи;е коннретные дату (месяц. день,
j"OД) и время. которые можно отформатировать РJiЗЛИЧНЫМИ cnособа:.1\о1И с ПОМ:ОЩЫо
соотвеТ('твующюt членов. Е качестве ЩЮСТОГО прщ.1ера рассмотрите слецуюrщПi
набор операт.оров.

stiЭtiс void Main (.$tring-[) aJrg9)


1
...
182 Часть 11. Язык программирования С#

1/ Этот конструхтор иcnоnьsует (год, месяц I день)


DateTime dt = new DateTime(2004, 10(17);

1/ Какой это день недели?


Сопsоlе.WritеLiпе("День (О) - это (11",
dt.Date, dt.DayOfWeek);
dt.AddМonths(2); // Теперь это декабрь.
Console. WriteLine ("Учет летнего времени: (О 1",
dt.IsDaylightSavingTime()) ;

Структура TimeSpan позволяет с легкостью определять и преобразовывать еди­


ницы времени с помощью различных ее членов. например:

static void Main(string[] args)

1/ Этот хоиструктор использует (часы, МИНУТЫ, сехуиды)


TimeSpan ts = new TimeSpan(4, зо, О);
Console.WriteLine(ts) ;

11 Вычте" 15 мину'l' из текущего sначеНИR TimeSpan и


/ / распечатаем реsультат.
Console.WriteLine(ts.Subtract(new TimeSpan(O, 15, О)));

На рис. 3.20 показан вывод операторов DateTime и TimeSpan.

Рис. 3.20. Иопользование типов DateTime и TimeSpan

Исходный код. Проект DataTypes размещен в подкаталоге, соответствующем главе З.

Тип данных System.String


Ключевое слово string в С# является сокращенным обозначением типа System.
String, предлагающего ряд членов, вполне ожидаемых от этого класса. В табл. 3.12
предлагаются описания некоторых (но. конечно же. не всех) таких членов.
глава 3. ocнoBы ~3bIKa Cft 183
ТабтщаЗ.1~J HeJroTopble 4леНЬ!Sуstеm..Stri,ng

Член ОомсаRИе
Lengtt1 ОвоИоrво. ВGавращающее длину 'Текущей .СТРО1<И
сооt.аiщ~ · О МеrсОД, пр",меНЯeN\i;J.IЙ ДПЯ выяснения ТаТа, содержИ1 111'1 1екуlJ,l,ИЙ СТРQКDВ15IЙ
Сlбwkт даJ;J;Ную строку
Format () 'Стаnl'lескии метОд., применяемый для фQJ,)матираВflНlIIЯ сrРЙJ<Ь8Ьi)( лМтераПО8
С ИСГЮЛD3cJВa/iием примитивов (ЧИ\::110ElpJХ дa~I~JblX и дрv.rих строк) и ОQозна4е­
Щ"14 типа j 1).\ ,уже встречаВШИХСR' ранее в этой {,паве
l!)s~rt () MeroJJ.. ИCfl,ФI1ьзуемый ДЛЯ получения КОПИИ текуЩей СТРОКИ , содержащей до­
баВmlемые СТРО1(081;)[8 даННt>/е
.P adlieft () МетоДЫ, возвращаЮЩие I<ОIН1И т~кущей CTpO'-I'I" дополliеf:lf,iЬ/е указэнныIII
J?adRiqht (1 ЛII~JHbIМJII в качеctВе заПОI1нитем
Remo,;!e ( ) Методы , ИQЛольэуемые ДЛЯ ПVJlуЧ'еНmt !(опии сороки с С~)QТВeтG1ВуюЩИМИ 1010-
Е.ерlасе() диQ>.l.1КЩI4ЯМИ (п.Ри удаленllWl или заме~lе СИМВОлОВ)
S'u]:)stril'Jg () Меroд, возвращающий строку, которая предСТЗ8ляет подстроку щкущей СТРОКИ
ToCi::IarAr)';a~"( ) Метод, возвращающий маССИ1l сltfМВОЛQВ, из которых еоатоит те~ущая стрща
T0Upper(J Методы, создающие КОПИIIiJ Д!&lНОЙ строки, предстаВJiенную СИМВОIlами в
Тф,оwеr () 'bepX1-lем ИJ:1и, COdТBeYCтtteHI'tO, НИЖIi~М регистре

Базовые операции со СТРОl(ами


дли ИЛ.1lЮстрации некоторых: бцзовых оцераций со стрorщми раССМО'ГРИМСМДУ­
ющий метод Mai n ( ) .
static voic Main (;,:ч:..r iпg [) д; rgs I
[
Console . Wr i teLine '( " ** ~ * * Зао:авы СО' СТрО~l'Й1. " .,. "''' '') ;
$1::r-ing Е' =' ,1 Воу, t.b is is ,taking а 10(19 t iщ;:? "';
, <: олsаlе,. 'WritеLiПе("--> С одеР)frЯТ ли s 'ау'?: jD)",
s . Contai.ns("оу" ) ) ;
C(,\Jls01e.W.riteL~[[>e ("--> СО'nеРЖI4'l' ли s 'В 9У '?: (DJ ",
э. COntafns ("воу"
) J ,;
Console,WriteLine ,(.s. .,Replacel'.', 'J'));
с.1\)!1stIJ1.е.t"1litеLiпе(З.Iщsrt(О. "НО,! 01 "));
CQr,csol€. Rе,э.dЦiГJе () ;
}

a~ъ..мы Cd:щаем тип s.tri пч, аызьmающий методы' CGHlta iпs О . Eepl.ace (,) и
Iпsеrt (). Соотвeтr;ТВУЮЩИ:Й В:ЫВОД показЭ1:I на рис. 321_

Рмс. 3.21. Базовые onерации со строками


-
184 Часть 11. Язык программирования С#

Вы должны учесть ТО. что хотя 5tring и является ссъmочным типом. операции
равенства инеравенства (== и ! =) предполагают сравнение значений со строковы­
ми объектами, а не областей памяти, на которые они ссьmаются. Поэтому следУЮ­
щее сравнение в результате дает true:
5tring 51 = "Не110 ";
5tring 52 = "Не110 ";
COn501e,WriteLine("51 == 52: (Ol", 51 == 52);

тогда IЩ}{ следующее сравнение возвратит Еа15е:

5tring 51 = "8е1 10 ";


5tring 52 = "World!";
COn501e.WriteLine("81 == 52: {O l", 51 == 52);

Для конкатенации существующих строк в новую строку. которая является


объединением исходных, в С# предлагается операция -1-, RaR статический метод
String .Concat (). с учетом этого следующие операторы оказываются функцио­
нально эквивалентными.

/I Конкатенация строх.
5tring newString = 5 + 81 + 52;
COn801e.WriteLine("8 + 51 + 82 = {О}", newString);
COn801e.WriteLine(n 5 tring.Conca t (5, 51, 82) = IО} ",
5tring.Concat(8, 51, 52) );

Другой полезной возможностью, присущей типу string, является возможность


выполнения цикла по всем отдельным символам строки с использованием СИНТaR­

сиса, аналогичного СИНТaRСИСУ массивов. Формально говоря, объекты. подцержи­


вающие доступ к своему содержимому. подобный по форме дос'fYIIY к массивам,
используют метод индеlCсаmора. О том, как строить индексаторы, вы узнаете из
главы 9, но здесь для иллюстрации соответствующего понятия предлагается рас­
смотреть следующий фрагмент программного кода, в котором 'каждый символ
строкового объекта в1 вьmодится на консоль.

// Sуstеш.String опредеnяет индексатор ДЛЯ доступа


// к к_дому символу а строхе.
f or (int k = О; k < 51.Length; k++)
COn501e.WriteLine("Char [ О } i5 {l}", k. sl[k]);

в качестве альтернатив,Ы взаимодействию с индексатором типа можно исполь­


зовать строковый класс в конструкции foreach. Ввиду того, что System.String
поддерживает массив индивидуальных типов System.Char, следующийпрограмм­
ный тоже выводит каждый символ 51 на консоль.
foreach (char с iп sl)
Conso1e.WriteLine(c) ;

Управляющие последовательности
как и в других языках, подобных С. строковые литерaльr в С# могут содержать
различные управляющие последовательности. которые интерпретируются как

определенный набор данных, предназначенных для отправки в выходной поток.


[лава '3. ООНЬЖЫ языка с# 185
Каждая упрЩJJШiOща.в последовательность на.<1й1Щется: с Qбратной 1I:0СОЙ черты. за
~оторQЙ~. llе'дует интеpupетируеМЬ1Й зНaR . На тот сЛ)'Ч<Щ. ~JЩ вЫ пQдз:а.Быml зна­
чеюш уцравJl.ЯiЮЩИХ последоВательностей. в та.бл. 3.13 предлarаютоя ОI1ИCаня.я тех
из них. которые иtпОЛЬзyJотся: чаще всего.

Табтtца 3.1,3. Управляющие ПОСП8доваТеl1.ЬНОGТVI строковых литералQ8

УправЛllJOщjtя
O"K~aнмe
посnеД08а:rел,"носп.

\1 Вставляет в СТРОICОВEi1Й ли.тералЗН8J( ОДИНDЧАоiil JfflВЫЧ'к и

\" BCтaВJ1~eT 11 сrРОI<ОВЫЙ дитэрал 'знак ДВОЙ!-10Й кавыi<ии


\\ BcraВ:nl;leT встроковый ЛИl'ераЛЭI'lЭk 'Обратной КОСQЙ черты',
Это может оказа:rъся п'олеЗНblМ при укаЗЗНlII1II П'(11l

\а Инициирует системt-IblЙ::IВ'УICОВОЙ сигнал (be~p). Для консоnы-lых


припожеl1l-1И это может быть аУДИОrlодскаЗКQЙ пользователю

\n Вставляет знак перехода f.l8 HOBYJ() cтpo~ (н.а платформах Wm32)


\r Вь,.а!;l'ЛЯ~Т знак ВОЗВРilта каретки

\t Всщвляет в СТDО~ВЫЙ литерал 3НЗIC ГОРIIIЗОНТал'ЬНt>й Табуляции

Ta.k, чтобы напечаТать строЕУ. в цотuрой между любыМи двyr.m словами ИМеете$'!
3НaI< табуляции. МОЖJIО Иt:'полъ:roватъ }"Цра:вляющую лоследо.ватеЛЬН1LIСТh \ t.
1/ Cтpo~o.ыe ~e~· Mor:y'J1 со;цераа.'1'Ъ mDбое ЧМQJ.IО
11 ynpав.1IJlDЦИX nо.cnеДова'1'еnькос",,9Й.
string 53 = Л'Эй, \ t!3bl , \ t'1'ёЩ!, \ t олят ь 1" ;
:COlisole. Wr i .teLine ("53) ;

для другого прm..Jера предиоложим. "ГГО вам .нyжI:IО с{}'здат~ Сl1ЮRОвы:'Й литерал.
Itотор1!IЙ содержит НаБЫЧКИ. литерал. указьmаюIIЩЙ путь в: HaTa.j]ory~ п. наконец,.
литерал, КОТОРЫЙ' :вeтaвJJHeт три Пустые СТРОRИ после вывода l!!uex L'ИМВЬ.!lbliЬJX дан­
ных. Ч;тобы не допу.с1'И'tЬ nолвлени:s: сообщений об опщбкщ НОМПИЛ1.ЩЙИ, исполь­
зуйте ур.раБЛНЮщи:е СИМВОЛЫ \ ". \ \ и \n.
Соnэоl е • Wri teLi!"'!e ("Все .Ilю('}:я.'r \ "Неl10 Wor-ld\ n "J; Сопsо1е.
Wri tеLiПе" (" 'С : \ \МуАрр\ \Ьin' \ \d-e·b ug") ;
еопэсЙе., W~ i teLirle ("Все за:аершенр. \п\п \n:") i

Буквальное воспроизведение стро,к в С#


в С# ВВОДИТСЯ И'спольЗование префик.са @ ДЛЯ cтpo.!t , 1\О'горые тр.ебуется вocnpo­
uз!3ecrnu БУКВQJ!ЫШ. Используя букв:;щьное воспроизвеДение стран, вы ОТltЛlочаете
обработку управляюших сifМВоло.в строк. 81"0 может быть полеэnым при работе со
строками. представЛfl1ОIЦИlI,Ш Rаталоги и сетев:ые ЦУТИ. Тогда вместо nСПОЛЪЗОQа­
ния y:npaвляющих СИМВОЛО:$ \ \ можно И:СПОJlliЭОliаТЬСЛедУЮщее.

11 СnедYDЦ&1'r c>zopoJ:a ДО.цм;1ji1 · аоcnpоиs:аоДlП'loСН буца.т.во,


11 ~рэ~оиу :асе I vарaJJJ1RX1ЩИ1I!I cюraОml" б:у;цу~ O""O~.IU!t.
Coosole. WriteLine {@"С: \'My,App\biQ \cj.eb 'lg") ;
186 Часть 11. Язык программирования С#

Отметьте также и ТО . что буквально воспроизводимые строки могут использо­


ваться для представления пропусков пространства в строковых значениях, ~pac­
тянутых" на несколько строк .

// в буквально ВОСПРОИSIIОДИМЫХ строках


/ / пропус!щ ПРОС'.l'pаНСТllа сохраюlЮo:tIся.
s tr ing rnyLongString '" @ "Это оч ень
оч ень

о ченъ

длинная строка ";


Со пs о l е.W ritе Liпе( mуLоп gStriпg) ;

Двойную кавыч:ку в таl<ОЙ строковый литерал можно вставить с помощью ду­


блирования знака ". например:

Console . Wri te Li ne( @" Cerebus said ""Darrr ! Pret - ty sun-sets""");

Роль System.Text.StringBuilder
тип зtriпg прекрасно подходит ДЛЯ того. чтобы представлять базовые строко­
вые переменные (имя. SSN и т. п . ). но этого может оказаться недостаточно. если вы
создаете протрамму. в КОТОРОЙ активно используются текстовые данные. Причина
кроется в ОДНОЙ очень важной особенности строк в . NEг. значение строки после ее
определения изменить нельзя. Строки в С# неизменяемы .
На первый взгляд. это кажется невероятным. поскольку мы привыкли присваи­
вать новые значения строковым переменным. Однако. если про анализировать ме­
тодыI Systern .String. вы заметите. что методы. которые. как ка;жется. внутренне
изменяют строку. на самом деле возвращают измененную копию оригинальной
строки. Например. при вызове ToUpp er () для строкового объекта вы не изменяете
буфер существующего строкового объекта. а получаете новый строн:овый объеICТ в
форме символов верхнего регистра.

s ta tic void Main(string [] args)

/ / Думаете, что иЗменяете strFixed? А IIОТ и не'1'!


Sys tern .S t r ing strF ixe d = "Т ак я начин а л свою жи знь";
Console.WriteLi n e(strFixed) ;
string upperVersion = strFixed.ToUpper();
Console.WriteLine( strFixed);
Console.WriteLine("{O}\n\n", upperVersi on );

Подобным образом. присваиван существующему строковому объекту новое зна­


че ние. вы фактически размещаете в процессе н.овую строку (оригинальный строко­
вый объеI<Т в конечном итоге будет удален сборщиком мусора). Аналогичные дей­
ствия выполняются и при КОНRатенации строк

Чтобы уменьшить число копирований строк. в пространстве имен Systern.Text


определяется класс StringBuilder (он уже упоминался нами выше при рассмотре­
н ии Sy stern.Object). В отличие от Sys tem.String. тип StringBuilder обеспечивает
Глава З. Oct1~B'bl язЫКа С# 187
прямой доступ к буферу строки. ПодобklО SysEem.String, тип Эtrin.gВ1'lildеr пр~д­
латает МlЮжecnю "f.шенов. ПО3IЩnЯlШТ{I.,Ц добавл.вть, форматирова1'Ь. щ::т.авлятр и уда­
ЛЯ'tЪ p;aJiНЫe (подробности вы найдете в ДОRy-ментации .NEТ Framewor~ 2.0 SDк).
При создании оБЪеКта StringBui J:de:c М0ЖJ;10 указа'rЪ (через ap:ryмeН1' КСЭflСТР,yR­
'тора] начальное число СИ:Мl:IQЛОВ. КО1'ор(}е может СDдержа-nь об-ьеn Если этого ВЕ'
сделать. '[о буде1' ИСПQЛЬЗоватьCJi "стандарТНr'lЯ. емкость' Str ingHu i1.der, по умол­
чанию равная 16. Но в любам 'CJIj'Ч4е. ~СЛИ выУВ<;>ЛИЧИте St:ringBuilq-еr бощ,ше за­
данного числа символов. то размеры буфера будУТ переопредe.nены динамичеСIШ.
Вот пример ИCUОДЬЗОВ<Ш:ИIl этого типа у.лareа.

11s,il)g S:'t·stem.;
4.siлg SY$t,em,Тe~ti !1 Эдесь ) JPfВe11" str.ingВuilder .

Cla,,8 'S:tr iпяА.рр


I
s tatic ll~i.d Mai.n j striDg [] args)
j
StJ;'iлgtlui1dеr m;yBuf'fer = Ш,'W s!:r.:!.ngБUildеr:("МОЯ· СТРО'ка");
Ccas.ole. Wri te-Liae ("Емкость ЗТ,QГQl St.y;i r!gB,uilder: {О]",
,.Ca.paqi ·ty) ;
(nуВцffеr
щувuf fer .Append (~ содержит также ~ИС'.д·Е!: ");
rrryBuf'fer.AppendFormat("jO} , р}.", 4-4, 9~n;
f.;с,n:юli'!. W:ri teLi.!le ("~CTё '"'того s·tringB\Jildar: ! О}" I
my.вuIfer.Capacity);
СОI'1З0'lе .. W:r.i teLine (туВи[ fei:') ;
}

Во' Многих случаях наиболее подходящим длнвас текстовым объектом бу,дет


5уэtе'l1l. Зtring. i'J,ля UОЛЬiUЩiства приложени'й потери. СВЯ3аНllЫе с ВQ;з.вращени­
ем иэыен:eннъrx КОffiх.Й сИМ)ЗольН!>lX данн;ых, буцут везнзчптелыlми.. Oдuaкo цри
цостроеНШI приложmmй, nНтеНCИJ3lfО ИСПОJIl:!зуЮщ:их тстстовые дaнnыe (например,
1"ef!:стов:ыхпроцессоров]. nl, скорее всего, обнаружите. что JfCIIОJ.lЬ30вание Syst€\1t1.
I'ext. Stri{]gHuilder повьцuaет ДРОИЗВQЩIтельвость.

ИСJlОАНЫЙ Kq~, Проект :Strings размещен Q по:цкаталоге, сщпвететвующем r1IВBe З.

Типы массивов. NET


Формащ.но fOBOPR, массив - ЭТО ко.тmеh'ЦИЯ указателей на дав:ны.е одн.ого u IУЮг:О
:же вполне (щределе:inrо:го ТlJпа. доступ I'С которым осуще~твляется. JЮ числовому

ШIДeRСУ. MacUffibl двляЮтCR ссы1Iочнымй типами и получаЮТСЯ из общего базОВОI'О


K'Iacca 5y~te-m .Array. По умолчанию для .NЕТ-:t,'laе~и;вов начfl.льный индекс равен
н.ул1О, .НО С помощью статического метода Эуst~.m.Аrrа.у,Сrеа.tеInstа.nсе О ,цля
любого массют М9ЖНО задать любую НИЖ1:tюю гран:.и:цу ДJЩ ·его индексов.
МаосивЬ1 ц С# можно объявлЯть,nо-разиому. Во·первых. если вы хотите создать
массив. значеНИR которого· будут опреде.деНbt позже (~озмomпо лосле ввода ооот­
ветствyYQЩЩ<: Дt'Ц-.IНЬJ.X ttОJThЗ0вател:ем). то, ИCllо.дьауя I<ВaДpllTHble скобки (л). yI<aжII­
те размеры массдва во время его создания. Н.алример:
188 Часть 11. Язык программирования С#

11 Создание массива с'1'рох, содержащеro 3 элемента {О - 2}


string[] booksOnCOM;
booksOnCOM = new string[3];
11 Икициa.nизаЦИR lOO-злеиеаorного массива с нумерацией {О - 99}
string[) booksOnDotNet = new string[1001;
Объявив массив. вы можете испольЗовать синтансис индеl\.сатора, чтобы при­
своить значения ето элементам.

11 Соs.цание, заполнение и печать массива из '1'рех С'1'рОХ.


string [} booksOnCOM;
booksOnCOM = new striпg[З);
book sO nCOM (01 "Developer's Workshop to СОМ and ATL 3.0";
booksOn COM(l) = "Inside СОМ":
booksOnCOM (2] = "Inside ATL";
foreach (string s in booksOnCOM)
Console.WriteLine(s) ;

Если значения массива во время его объявления известны. вы можете исполь­


зовать "сокращенный" вариант объявления массива. просто указав эти значения
в фиrypных скобках. Указывать размер массива в этом случае не обязательно (он
вычисляется динамически). кан и при использовании ключевого слова new. Ta1t.
следУЮщие варианты объявления массива э1tвивалентны .

11 'Краorхий' вариант об~леник массива


11 (значеНИR во вре_ об....леНИJI ,цоJtЖJВ1 БW"1'1o иsаестНJoI) .
int(J n=newint() {20, 22, 23, О};
int(J п3 = { 20, 22, 23 , О };

и нанонец. еще один вариант создания типа массива.

int[) п2 = new int[4] { 20,22,23, О }; /1 4 элемента, {О - Э}

в данном случае указанное числовое значение задает число элементов в масси­


ве. а не граничное сверху значение для индексов. При несоответствии между объяв­
ленным размером и числом инициализируемых злементов вы получите сообщение
об ошибке компиляции.
Независимо от того. 1taн вы объявите массив, элементам в .NЕТ-массиве авто­
матически будут присвоены значения, цре,цусмотренные по умолчанию, coxpaнmo­
щиеся до тех пор. по1tа ВЫ Yl'ажете иные значения. Тан, в случае массива числовых
типов, каждому его элементу присваивается значение О (или О. О в случае чисел с
плавающим разделителем). объектам присваивается null (пустое значение). а ти­
пам Boolean - значеНl~е false (ложь).

Массивы в качестве параметров


(и возвращаемых значений)
После создания массива вы можете передавать его. 1taн параметр. или получать
его в виде возвращаемого значения. Например, следУЮЩИЙ метод PrintArray ()
получает входной массив строк и выводит каждый злемент на консоль. а метод
GetStr ingArray () "наполняет" массив значениями и возвращает его вызывающей
стороне.
Глаза Э. ООНОВЫ языка С# 189
statLc 'fold Pr1nthrr,ay (inttJ 'DlyZntз)
{
for (i.пt i = О~ i < mуlлts. :Len.gtJ-.,· i ++ )
Сопзоl е . Wri teL.1,ne ("ЗJ1, е~ент {О) равеf'l (l}", i, roylл'сS' [i] ) ;

stat.icsUing [] G"tStrin.gATray ~)
(
str.ir1ЧjJ thеStr:iЛ;jВ = ! "ПрИвет", "с' т", "GetStringArray" };
,r -et.urn thеStri.ngэ;

Эти .метсщы МОЖНО вызвать из метода Main О. хан. пока.,·WНQ ;!iИже.


$tatic vcid l'1aln (gtring!] эр;!;;,>' )
(
in"t,lJ ages, = {20, 22, 23, О) ;
PrintArzay (ages,) ,;
s:ti: iпg 1] 's t rЗ = GetStringAz:ray (1 :
fDreacb~string s i n strs)
Caг.s() 1.е . W'r i t.eLine ('09) "
console . Rещ:lLiпе ~) ;

Работа с многомерными массивами


Вдобавоlt к ОДl-IOмерным массщщм, ~oтopыe мы рас~атривали до СИХ пор. 11
С# подцержива:ются два в~риавта многомерных Maccnвo:в.. Первый из RИК- это
Тlр.ямауооЛЬНbtЙ массив. Т.е. ммотомерный ~СCЩi-, Б .котором RШfЩая СТрО1>а оказы­
вается однои и той же длины. Чтобыоб~.Ii!'JЪ"И заполнить многомерный прямау·
лшьный массив. действуйт~ Тан,. как JJоказано НИЖfl.

э!;.аЦс void Ma.:inisUi ·r .g[] а!'ЯВ)


{

11 ПрmюyrоJIЬ.!WЙ массив МD.


irlt [, ] myMatrix;
,l1'\yMatri,x = a~\'" iпt16,БJ:

11 Зanоnяе.кие массива (6 ,. Ei).


f 'o1' 1irit i = О; i <: 6 ; д +-+ }
for(ir.t j = О; j <. б: :1++1
myMa t r~x (:i., j] = 1 * J;
/1 Печать массива (6 * '6).
forCint i =0: i '< 6; i++)
I
for ( in t j =01 j < 6; j-+
СолщЙе.Wгitе(m>tМаtriх[ir j) -+ "')"t~);
Consble. Wri :teLitle () ;-
190 Част ь 11. Язык программирован ия С#

На рис . 3.22 показан соответствующий вывод (обратите внимание на прямоу­


гольный вид массива).

Рис. 3.22. Многомерны й массив

Второй тип многомерных массивов - это невыровнен.ныЙ массив . Как следует


из самого названия . 'ГaJЮЙ массив содержит некоторbIЙ набор массивов. каждый
из которых может иметь сво й верхний предел для индetссов . Например:

stat i c void Маiп(s tгi п g[ 1 a r gs )

/ / неsыpовненный массив МD ('1'. е. массив мa.cCJВoa) .


1/ Здесь мы имеем· массив из 5 разных маСCJOов .
int[ ] [ ] ш уJаgА rrау = new i nt [ 5 ] [];

// Создание неawpовненного массива.


fo r (in t i = О ; i < myJagArray . Leng t h ; i ++)
myJa g Arr ay [ i] = new int [ i + 7] ;

/ / Печать :В:iЦCдой CТPOJm (не забъtвай'1'е о том, ч'1'О


/ / по умолчаНИIID все эnемен'l'Ы будут равны нуЛlO! )
fo r (int i = О; i < 5 ; Н+ )
l
Соп s о lе.Wг itе( " Ддина стро к и {О} р а в на {l } :\ t",
i , myJagArray[i ] . Length ) ;
f o r(int j = О ; j < myJagA rray [ i) . Length ; j++)
Солsо 1 е. Wr i te (myJagArray [i] [j] + " ") ;
Co n so le. Wr i t eLi ne();

На рис. 3.23 показан соответствующий вывод (обратите . что здесь массив имеет
"не ровный край").

Рис. 3.23. Невыровненный массив


rlli!8B З . OCflOВbl языка С# 191
Теперь. Rorna ~ З:I;Iаете. как етраитъ и заполнятъ маCClШы 11 С#. обратщм 'ВН;И­
мание на 'базовы;й класс- mof;ioгo массива:' 'Systern . Аr:rау.

Вазовый класс System.Array


Каждый создаваемbIЙ 1Iами массив в • NET авТQМаТliчес:ки получ-аетсл ИЗ,
$Y~tem ..Array, Этот R,1Ia'cc Щlредf:ЛЯет ряд полезных мет_ОДОВ для ynрощещщ рабо­
ты с маССI1Вщ.m:'. В ~бл. 3.14 предлагаюто.я ОП:ИcaIOfЯ некоторых из наЩ50л~е инте­
ресных членов укаэШПIОГО ~JЩQС~.

Табnи.цаЗ.14. Н\3которые члены System.Array


Опмсание

Bin.arySearch (1 СтаТИ Llеский метод, ВЫПОЛНЯЮЩИЙ поиск зсщанноro элемента в (пред­


варитe:nЬНQ отсаРТИРОЕ!3НtlQМ) массиве. Если массив скомпонован ИЗ
ПQlIьзовательсК»х 7ИП08. ИСКОМЫЙ ТИГI должен реализовыВ8lЪ ИН"tерфей~
ICoruparer (см. глаl>у7), чтобы задействовать двоичный nОИСk
Clear() С;татический метод, ОЧИUЩIQЩИЙ !3i.1Дat-lНЫЙ диanаЗО11 элемеНтОв в ,масси"е
(УСТВliавливаето!! (:) ДЛЯ ЧI1СЛОВЬ/)( T~nOB и n-ull - дI1Я GCblJIОЧ1:lblХ типов)
Со.руТо. () Метод, ИСt:ЮЛЬЗ}'8мыlil для КОnИРОВЗIiИЯ ЭJlемеtlТ0В из IiIIвссива~ИСТОЧНИ1<а в
~еле89М массив

Length СвcrЙство. до.ступное fОЛЬКО ДМI'1теI'lИЯ И И-ОП(i)1Тt;зуеМDе для выя:снения


Ljи~ла элемен:Гав в массив-е

R.шk Свойство. возвращающее зНаченИе р8змерности дaHH~ГO массива

Rever.se() СтэтичеСI<ИЙ метод. инверТ\1РyIOЩИЙ ГlОРЯД01> следрваНИ1J элемен'I'ов ОДНО­


мернorо Maccl1Вa

Метод, СОРТИРУЮЩИЙ OAHOMS-РНI:141it массив внутренних типов. Если элемен­


ты в маосиве реаnиз.уlOТ инreрфеЙ· с IComparer. можнр таl{Жe сортировать
nользователрские TJI1fIbl (С!iОВЗ ОМ . mэву7)

РllССМОтрим примеры использования HeKOTQPbrx иа этик "ЧЛевЬВ . БСЛlщующем


црограмм:нОМ ходе используются статнчecIЩl'" методы Re,,-e:cse () и Clear О (а также
свойство Lепg"tJ-,))J;ЛЯ вывода не}(оторой ивфQРМации о массиве С'ЕрDК first.Names
на RОИСОlIЪ.

11 Создавме С~()ХОЗJdX ~ССИВОЗ и прове.рltЗ


11 _ие!Со~()рых. 'iШевоз System . Array.
stEtlc y~id Мa.i(\ (stringl] OizgS)
1
11 Массив cqюи..
s't..tinq [J firзtNаmеs = 1" Stev:~ ",1 "j)ominic", .. Swall.::}w", "Baldy" 1

11 Пе:r.и~ пиеа • об'bllВJI8ИJIОJII зкде.


Ccmsole . ,WriteLi-ве ("Вот вам массИв: ") ;
f,or (iлt i = О; i < f:i :r:stNаП)ез. Ьength; i Н)
1 Со.ГJ SОlе.Wгitе(ПИмя ,; (O/\t", first.NаmеЗ[i]);
COIl's~,lE'. WTi teLine (~\n") :

I 11 ИивеР'1'IiIpОВiiиие nQp"~lI!Ca ~ иа~с;кве Jr печа!t'Ъ.


If Arra-y. r'(~verse (firs:tNames) ;

1
192 Часть 11. Язык программирования С#

Console, Wri teLine ("Вот вам инвертированный массив:") ;


for(int i = О; i < firstNames.Length; itt)
Сопsо1е . Writе("Имя: (O) \ t", firstNames[i));
Console.WriteLine("\n");

// Очистка всех Дi1ИИWX, хроме Baldy.


Сопsоlе.WritеLiпе("Очист~а всех данных, кроме Baldy ... ");
Array.C1ear(firstNames, 1, 3);
for(int i = О; i < firstNames.Length; itt)
Сопsоlе.Writе("Имя: (O}\t", firstNames(i]);
Console.ReadLine() ;

Обратите особое внимание на то. что при вызове метода Clear () ДЛЯ массива
оставшиеся элементы массива не сжимаюТСЯ в меньший массив. Для подвергших­
ся очистке элементов просто устанавливаются значения по умолчанию. Если вам
нужен контейнер динамического типа, ПОИIЦите подходящий тип в пространстве
имен System.Collections.

Исходный код. Проект Arrays размещен в подкаталоге, соответствующем главе 3.

Типы с разрешением принимать значение null


Вы уже видели, что типы данных CLR имеют фиRсированный диапазон измене­
НИЯ. Например, тип дaянъiX Sуstеm.Вооlеал может ПРШ-Iимать значения из мно­
жества {trtJe, fa1se}. B.NEТ 2.0 можно создавать тШlЫ сразрешенuемпрuнu.м.amь
значение ли11 (ТИПЫ nиllable). тип с разрешением принимать значение лиll может
представлнть любое значение, допустимое для данного типа. и, кроме того, значе­
ние null. Так, если объявить тип System.Boolean с разрешением ПРШiимать зна­
чение null. то такой ТИП сможет принимать значения из множества Itrue. false.
nulll. Очень важно понимать, что тип. характеризуемый значением, без разреше­
ния принимать значение null это значение принимать не может.

static void Main(string(] args)


(
// Ошибка кокпилицки!
// Типw, характериsуе~е значением, не допуск~ значений null!
Ьоо1 my Baa l = null;
int myInt = null;

Чтобы определить переменную типа пullаЫе. к обозначению типа данных в


виде суффИIiса добавляется знак вопроса (?). Такой синтаксис оказьшается доny­
стимыM толыю тщ'Да, КOI'Да речь идет о типах, характеризуемых значениями, или

массивах ТaI~ИХ типов. При ПОIIЫТке создать ссьmочный тип (включая строковый)
с разрешением значения nu 11 вы получите ошибку компиляции. Подобно перемен -
ным без разрешения принимать значение nUll, локальным переменным с таким
разрешением тоже должны присваиваться начальные значения.
г.лава З. ОС.НОВЫ языка С# 193
эt. аtiс voi~ Main (Bt.ring[] .args)
{
11 И8С11CQ;JIЬКО ОПР8Д831е~ nO:KВJlЬВJ.D!: or~pв
/I с раsреu!JНЫМИ звачеllИ!QQ!f null.
iл:t? Dullablelnt = 1 О:
do .uыl?? l~l.JllableDoub,J; ~ = 3 .14;
:bool? f!ullableHool = rлй 1;
ch.ar? nulLableChar = la ' ;
;i..n:t? [] аrrауОfNul1аblеIл-ts = I!ew int? [10J;
11 OIIIИбха! Стражи ~CB СOlШО!lИlollCИ ~8ХИ!
s·tring? 5 ' = "QЙ!":

J
СуффИНС ? в Cft Является санра:щIШ1ЮЙ записью ДЛЯ указания создзтъ перемен­
нyto СI рукгуры обобщеНного тIШa System.NuJ.lab1e<T>. Мы рассмотрим оооБЩе­
нин в Лlаве 1О, а сейчас важно IiOНЯтb то, ч.то тип Sys tem. Nulla1Jle <'Г> предлагает
ряд членав, :кoтnp:ыe .могут использовать все типы с разрешением зиачения null.
Например. используя свойство HasValue или OIIepaцm.o 1=. вы можете nporpaмм­
ным nyFем выяснить, содержит ли соответс1f'ВУЮЩая переменная з:вачеЮiе Ilull.
3начение.присвоенное типу с раЗрешением значенйn 1'1t111, можnо получать не ·
посредСтвенно или с помощью св.оЙtт.ва уаlие.

Ра'бота с типами, .для которых допустимы значения null


типы с рШfреnrен:ием принимать 'ЗIUlчение .rlul1 могут оказаться исключителъ~
Н0 полe3Jlы:мирии взаимодействии с базами данных. ~ столбцы в таблице -мо­
тут оказат.ьс:я пустыми (т.е. неоnpеделelIНымиl. Для примера рассмотрим следую­
ЩИЙ.кnaсс, моде.1ШРУЮЩИЙ доc-ryп}( б.азедтnn.tx с табшщеfi, два столбца которой
могут оставатъс.!l неоnpеделеnнъtм:и. Обра.тите внимание на то, tцo здесь метод
GetlntFr.omDat.abase () не присваивает зна.чеНИе ч.l1e:tiy-переме:нноЙ целоч.иедщ·
ного типа с разреше.в:ным 3IШ.чеШ<lем null. в 'То время ~ GetBc).olFromDat.abas·e (}
назначает ПОДХО;Ц8Щее эначение члену Ьооl ?

~lазs DatabaseReader
1
11 Пonе ;qаившr с pa~ellleJUffJ. aH"~ n1l111 .
pupl:j..c iпt~ пшnbеriсVаluе:
рuЬН.с bool?' 0001 Vаlщ: = true;

1/ Обраwmrе :ВВИМaнmli :.Ja рltЗРSIll8JP18 пuЦ дп:• •os1tp~eкoro '1'ипа·.


pub1ic int? ~etIt1t.FroI1'1Database ()
f П~Гlrrn Nшi.bеriСVаl1Jе; }
// oepa~ ВJQSМa.иие п Р.$реllteиие null ДnR Ж().!Jвр~иоro !l!иna.
pub1icbOQl? Get,Вooltro mDatabasEi1 ()
i r .e turn baolV,'ilue-; }

Теперь рассмотрим следущщий метод М а i n (). ~1:!1аы:вающий "fЛеНБI клас­


са Datab aseReader и демонстрирую~ ЛРЩ::;ВЩ:J:lн;ые имзначеJtИя с помощью
RasValue и Val11e в соответствии с сщпаксисом С#.
194 Часть 1/. Язык программирования С#

static void Main(string[] args)


[
Console.WriteLine("***** Забавы с разрешением null *****\п");
DatabaseReader dr = new DatabaseReader();

// Попучение int из 'базы данных' .


int? i = dr.GetlntFromDatabase()i
i f (i.HasValue)
Сопsоlе.WritеLiпе("3начение 'i' равно: (О}", i);
else
Сопsоlе.WritеLiпе("Значение 'i' не определено.");

1/ Поnyчение 0001 иЗ 'базы даlUWX' .


bool? Ь = dr.GetBoolFromDatabase();
i f (Ь != null)
Сопsо1е.WritеLiпе("Значение 'Ь' равно: {О}", Ь);
е1эе
Сопsоlе.WritеLiпе("Значение 'Ь' не определено.");
Console.ReadLine() ;

Операция ??
Еще одной особенностью ТИПОВ с разрешением принимать значения n-ull, о ко­
торой вам следует знать, является то, L{TO с такими типами можно использовать
появившуюся в С# 2005 специальную операцию, обозначаемую знаком?? Эта
операция позволяет присвоить типу значение, если его текущим значением ока·

зывается null. Для примера предположим. что в том случае. когда значение. воз­
врашенное методом GetlntFromDatabase (), оказывается равным null, соответ­
ствующему локальному типу int с разрешением значения null нужно присвоить
числовое значецие 100 (конечно, упомянутый метод всегда возврашает null. но я
думаю, вы поймете идею. RОТОРУЮ ИЮIIOстрирует данный пример).

static void Main(string[] args)


{
Console.WriteLine("***** Забавы с разрешением nu11 *****\п");
DatabaseReader dr = new DatabaseReader();

1/ Еcnи GetlntFromData.base () возвращае'1' nu11,


/ / '1'0 nОJCa..nьноЙ перекенной npисэаивае'1'СЯ значение 100.
int? myData dr.GetlntFromDatabase() ?? 100;
=
Сопsоlе.WritеLiпе("ЗнаgениеmyData: (О}", myData);
Console.ReadLine();

Исходный КОД. Проект NullabIeType размещен в подкаталоге, соответствующем главе З.


r Гnава З, ОСНD8Ы IIзыка С# 195

ПОЛЬЗ0ватеnъски'е пространства имен


До этого момента -мы1 создавал~ не.60дьшие ~CTo:выё npограммы, используя
пространства :имен. сущестJ!УЮUUiев среде ,NEТ (В частности. прОС1])8Нство й:мен
System). 110 nНbi'Дa при создании ПРИJIOж~нияБЫйает 'уДОбн'о объединить свя3аН­
m.re типы в одном полъзоваТeJIЪCJ{РМ пространс~ве :имен. В С# это делаетсst с по­
МОШЫО ключевого Сдова nam~spac;e,

I1pеддOJIОЖИМ, что вы создаете 1ЮЛЛ~JЩЩO rеQметрнческИх классов (' названия­


:ми Square (квадрат). Circle (крут) и Be:xagon (шеСтИуголъвин:). Учитывая их POjЦ­
ство. вы xomтe сrpуппировать их в общее np~aв:cтвo им.ен. Здесь вам предла­
гаютсSI д8а основных подхода. С одной CТOPOJfЫ. можно OOIpедenи.тъ все массы в
ОДНОМ файле (shapeslib.c:s). как показзно :ниже.

11 shaреsЦю, сз
ц.s iI1g' в)' s ·t вт ;
nащеSРi'lсе ~1'y.sj1..ape'5
I
11 км..с;;с CiZ,"cle.
clAss Circ:le{ .1'" и,н-тер.еСНБlе методы •• , "( )
11 1tпАt:о lIexaqon.
cla5s Hextigbl'll /'" Водее ин!Ге.реС!iые М8'1'РДЫ ••. */ }
II~Aco Square.
сlавэ S·gl.lare { Jj Еще более ИiI.~·ересkЫе ме:то,цы. " *1

3nме;rим. Что ЦРОСТРЩlство I:Щен MyShapes J.П'р8еТ роль абатраlCnlОГD "lWН1'ей­


вера ~ указанных ТИПОВ·. л,<:ьтернаnmным :варианто.м НВJ1fJется размещение едино­
ro пространства имен в неСItолъ~их c#-фай:Лах. длв этого достаточно "аавернуть"
onр.еделel-nJ)'I ра3.JПJЧНЫX щrщ:с()в в ОДFЮ пространство имен.

1/ c.i.x~le. са
llэiпg Sysr.erol
лamеsрас:е МySMp8B
t
11 JCnaсс Cirel •.
сlавв Circle( }
}
11 hex.gon. св
usiпg- .system;

na:m6эрасе Иу'Shареs
{
11 Ьаос, R~pn.
сlазs ;rе;jjа~н:ю I J
}
1/ squait"e .СВ
using Буэtещ;

namespace МyShapss
t
11 I(.nacc Squu•.
cla$s StГJare { '}
}

1
196 Часть 11. Язык программирования С#

Вы уже знаете. что в том случае. когда одному пространству имен требуют­
ся объекты из друтого пространства имен, следУет использовать ключевое слово
using.
/ / ИCnOJIЪsование '1'ИnОВ из простраиС'1'Ва имен МyShapes .
using System;
using MyShapes;
паmеsрасе МуАрр
{
class ShapeTester
(
static void Mainlstring[] args)
(
Hexagon h = new Hexagon();
Circle с = new Circle();
Square s = new Square();

Абсолютные имена типов


Строго говоря, при объявлении типа. определенного во внешнем пространстве
имен, в С# не обязательно использовать ключевое слово using. Можно использо­
вать полное, или абсоmoтное имя типа, которое. как следУет из главы 1, состоит из
имени типа с добавленным префИRСОМ пространства имен. определяющего дан­
НЫЙтип.

/ / Заие'l'Ъorе, Чorо sдесь !ie иcncmьзуеorСJl 'using MyShapes' .


using Systemi
namespace МуАрр
(
сlавв ShapeTester
(
static void Main(string[] args)
(
MyShapes.Hexagon h = new MyShapes.Hexagon();
MyShapes.Circle с = new MyShapes.Circle();
MyShapes.Square s = new MyShapes.Square();

Обычно нет необходимости использовать полное имя. это только увеличивает


объем ввода с клавиаrypы, но не дает никаких преимуществ ни с точки зрения раз­
меров программноro кода, ни с точки зрения производительности программы. На
самом деле в программном коде CIL типы всегда указываются с полным именем.
С этой точки зрения кmoчевое слово цsiпg в С# просто экономит время при наборе
программного кода.
r Г.I1ава З. основыlзыыаa С# 197
Oдnaкo абсолютные ~eнa MQl"YТ быть весьма IЮJre3НЪt (а инor,ztа и необходимы)
тогда. ROrдa ВОЗНШtaIOТ "Щ>нфJЦ!Ц!ТЫ при использовании пространств имев, содер­
лоащихтипы с одинаковыми им~намд. Предположим, что есть еще одно npостpan­
ство имен My3DShapes. в KOТOP0l.l Qпределяютсятри ЮIa!;;са ДЛЯ визуализации про­
crpанствещ{ых форм,

11 Дpyroe ПРОС!IIP~о форм ...


using Systeml
I'Iarne~pace МуЗ.D-Sh,аре s
{
11 ТреXliep~ масс Ci:Ec.1e.
C'l,a Ss Circ1el 1
11 Тре.кериъlй JtJtaC::C Rexagon.
clas's Неха.чоп, { }
11 ~ ж.nасс Зчuarе,.
оlа'sэ Square I }

Еc.nи обновить SrlapeTe.ster, как покашшо ниже. вы полyqите цедый рад со­
общений об ошибках .IЩМПИJlИЦlЩ. ПЬСКО.iIЬJo/ два пространсmа имен определяют
i"ИПЫ с одинaщmъnvm н:азВ8..НИЩQi.

11 ~ec!l!80 вео;цаозначвоС'1!ей!
\1's ing S.ystem:
using MyShapes;
u'Sing Иу3 'DShаре's;
n ame:space МуАрр

1
elass S-hареТеstег

эtаt: i G; void Ma.i[! (striлg [] агч э .\


{
11 на ЖtlC:ое прОС'1'р&!lC'Пlо их8в СCIШёШ'1ICJI?
Нехачо п h = n~'w Rexagbn ( ) ; 11 Ошиб:ка JCОIlШЮЦЩКИ!
c ircle с = new Ci r cle (1; 11 O-б.41ССIШИJI8ЩИИ1
$glJ.ilre s = леw Square (.); / I OaIибn )tОIШJШIIJ,UCЖ(!

J-leqДtЮзначность разрetitИтся. если ИCnOЛЪ90Ватъ абсол:ютnое -имя типа.

JI Теперь кеOдJiОSIl&ЧКОС'l'J, .11ИJtaИДМp9аааа.


st<3.'t1c Vbi,d Main (strin'g [J ar9'S)
(
МуЗDShаре,s. Hexago1). h = new МУЗРS!1ёiрев . !Jехаg{)л () ;
му3Ь5Ьаре_s .('ircle G = Ilew МуЗDShаре.в .Circle ();
MyS-hареt>. 'Sq'Jare s = new МyShapes. Sq1:i'a :r e О ;

j
198 Часть 11. Язык программирования С#

Использование псевдонимов
Ключевое слово С# using можно также использовать ДЛЯ создания псевдонимов
абсолютных имен типов. После создания псевдонима вы получаете возможность
использовать его как ярлык. который будет заменен полным именем типа во время
компиляции. Например:

us ing Sуstеш;
using MyShapes;
using My3DShapes;
11 Ликаи~ациR иеоднозиачнос~и с помощью псевдоника.
using The3DRexagon = My3DShapes.Hexagon;
namespace МуАрр
{
class ShapeTester
{
static void Main(string[] args)
{
11 На самок дenе здесь создае~СR ~Иn МуЗDShареs. Hexaqon.
ТhеЗDНехаgоп h2 = new The3DHexagon();

Этот альтернативный СИНТaItсис using можно использовать и при создании


псевдонимов ДЛЯ ДЛИННЫХ названий пространств имен.
ОДНИМ из ДЛИННЫХ названий в библиотеке базовых классов является System.
Ruotime. Seria lization. Formatters .Bina.ry. Это пространство имен содер­
жит член с именем ВiпаrуFоrшаttеr. ИСПОЛЬЗУЯ синтаксис using. экземпляр
BinaryFormatter можно создать так. как показано ниже:

using МyAlias = Sуstеm.Runtimе.Sеriаlizаtiоn.Fоrшаttеrs.Вiпаrу;

пашеsрасе МуАрр
{
class ShapeTester
{
static void Main(string[] args)
{
МyAlias.Binary Formatter Ь = new МyAlias.B i nary~ormatter();

или же с помощью традиционного варианта использованиЯ директивы usirlg.


using System.Runtirne.Serialization.Formatters.Binary;
namespace МуАрр
{
class ShapeTester
r Глава З. ООНОВЫ "зыка С# 199
static v~.Ld f\1ain (st:r;iцg 1)' args')
(
Binary F'оз;:mаt :t.е-r Ь = n,ew BinaryForrnatter() ;

ЗамеЧaJ.Illle. Теперь 1:1 С# предnагается и механизм разрешеt'l/llЯ КОl'lфЛИl(.Тоа' дrlf1 OдlllHilKOBb н.азван­
lil:!IX прострвн()тв имен, OCHOBrJ,f.lHblt1 на ИСПОЛЬЗDвании спецификатора' псевдонима ПРОСТJDан­
ОТВВ' име/-l (: :) и ·глобалыlОЙ" mErrKI-I. К счастью, указаНl:tы.Й ~n КОЛnИЭИМ возни~ает I1'CKlIiO'-Iи­
~ПI;IН() редко. ЕсГlИ ваМ требуется ДОПОЩlИтЩ1ЬН8.я ИНф('Jрмаци.я по ,. ЭтоЙ теме, hрочитайте маю
статыо ·Workin.g With the С# 2.0 Command Line Compiler!l (Работа с КОМПИllfIТQРОМ КОМfllJДiiOЙ
СТрОКИ. с.# 2. 0), .которую МOJI(tfQ найти На ·страницах http://msdn . microsoft. COlI\,

Вложенные пространства имен


Со~ерmенствуя ОРГЩЦfааци1О своих типов, вы цмееТе ВОЗМQ)кноеть onреде­
ЛSiТЪ пространства имен в ра:мж,ах други:х пр.остранств ИМеН. Библиотеки базовых
1(J!aCCOB .NET часто иcnоДЬЗУЮт такие вложеНИiЯ, чтобы обеспечить бол~ ~fi[СО1ЩЙ
уровень органИЩЩiJ1(J ти;па. Например, ПРOO'I'рансТltо имен C(!Il1~ctiorLS' вложе­
НО в SY$tem, что{)ы в резу.1шлiте tto.лy'DL1ЮСЪ SY$tem.CollectiQn;:;. Чтобы ~оздать
;корневое npocтpaнCTBO имен, roдержащее уже (.'УЩествyroще~ пространство имен

муЗЬSЬареs., можно цзменять :ваш программныИ !Сод -так, Katt nОJЩЗaНО ЩlЖе.

/1 Бnа.еюсе npocvrpаи.С'Х'3а имен.


oariJ.espa.ce Сh.!\рtЕ!rЗ
{
nатеэра'с е МуЗDShареs
f
1/ ТрехиеРJDIЙ 1aIаао Ci%:'ole.
class Circle { }
IJ Тр~XII8РIWli м-асе Hex&gon.
cla·ss Нexagan{ ]
11 ~РВЫЙQаса S~re.
cl<;isS SgLlare { }

ВО м:нorих С.JI)"I8ЯХ едшi:Ст.вemюйвадаЧ'еЙ Кор:невого простpat:Iс-:ша ~eн ЯВJIJ:lет­


cs раС!.IЩре.ние области вщим.ости, поэто-му llепосредствешю ~ paм1WX Т81Юro Щ>О~
странства цмен может вообще не оnpедеЛЯТЪСR mша:ких типов (ltaк в случае nPo~
странстванмен. СhарtеrЗ). В Т8'RИX случаях вложенное прDСТран:с:твоим(Щ MP~P
определить. 11ClIQЛЪЗУЯ СД~дУЮщую :комnaктйyro форму.

11 в.пожевие. прос~~а Юl8Н (иаркаи'1' 2) .


nanlespacs ChарtеrЭ .МуЗDSnареlt
{
1/ ТреxweрlWЙ 1'tЛВ:сс CiZlcle.
сlаЗ5 Ci.tclej }
11 Тре~p1UIЙ xnасс Hexagon.
200 Часть 11. Язык программирования С#

class Hexagon{ )
11 Тре~еРJWЙJU1асс Square.
class Square ( )

С учетом того. что теперь пространство имен My3DShapes вложено в рамки кор­
невого пространства имен Chapter3, вы должны изменить вид всех соответствую­
щих операторов, использующих директиву using и псевдонимы типов.

using Chapter3.My3DShapes;
using The3DHexagon = Chapter3.My3DShapes.Hexagon;

Пространство имен по умолчанию в Visual Studio 2005


в заключение нашего обсуждения пространств имен следует отметить, что при
создании нового С#-проекта в Visual Studio 2005 имя пространства имен вашего
приложения по умолчанию будет совпадать с именем проекта. При вставке новых
элементов с помощью меню Projectc:::>Add New Item создаваемые типы будут авто­
матически помещаться в пространство имен, используемое по умолчанию. Если
вы хотите изменить имя пространства имен. используемого по умолчанию (на­
пример. так. чтобы оно соответствовало названию вашей компании), используйте
опцию Default namespace (Пространство имен по умолчанию) на ВJtЛадке Application
(Приложения) окна свойств проекта (рис. 3.24) .

• ~I~ __ - - --______ '" ___~_~----,~"'---~--,,- _________ ~ - -____ -__________ _________."'__~__


~~::-:.::--=- =:c.::--- ------ --;с-- ..- --~ - - - - --- - - -:--"~_.': - ., _.-_-,
_- -,,---- ',' 1, ~ ':':: - -'--=-----=-- -"::"":lll:
<' i)i"if;'1U: \:11)~#J > f;1.I.A . ~. _ . "· п ,- ." ',

- - .. - - - - .. - - - . -- .. - - - -- - - - - - . . - ' -- -- - " . .- - - . . - - - - - - - - - - -- - -- - - - - -. . - , - - . . - .. - - .. - - - , - _ __ о -- 1:_


Bui\d
[1
11
i1;l
Aиemblу lпforlnlltion,. '
I

Рис. 3.24. Изменение пространства имен, используемого по умолчанию

После такой модификации любой новый элемент, вставляемый в проект, будет


помещаться в пространство имен IntertechTraining (и о очевидно. чтобы исполь­
зовать соответствующие типы в рамках другого пространства имен, потребуется
указать подходящую по форме директиву using).

ИСХОДНЫЙ КОД. Проект Namespaces размещен в подкаталоге. соответствующем главе З.


rJ'1SBa З. ОСfю!tЫ !1зыка С# 201

Резюме
в ЭТОЙ (ДОВОЛЬНО ДЛШll-ЮЙ) главе оfkу:ждалисъ самЫе разные acпel\ты яэьma: про­
граммиравания С# я zша.формы.'NЦ Б центре вниМания бы.ли КОНСТРУКЦИIJ, КО­
торые наиболее qacтo ИСПОJIЬЗyЮТщr В IIJ?ИЛЬжениях и которые могут понадобитъс:я
вам при СОЗ)iании 1'aI<ИХ fiPИJlожещШ.
Вы могли убедитьCJL ч.то вее Щ:IYтрeюmе типы данных в С# соотвe:t'C'DJyЮТ Qпре­
делеННЬ1li.r типам из пространства Иl\1ен SysteiIl. Каждый такой ~СЙ:ltfеМlIЫЙ" 'ГЯи
Прeд.i1агает набор член:ов, с ПОМОЩ~ которых npограммныии средс:rзами мm:кЩ)
ВblЯОНИТЬ диапазон изменеНИJl' TJЦIa. БЫЛИ: такЖе рассмотрены особенности 00-
строеiПtЯ1'ИПОl\ .lШaсса в С#, различные правила передачи параметIЮВ. изучеНl;il
ТИПЫ. хаpakТeризуемые вначе;щщми. и ссылочные ТИПЫ. атаиже вьшснена роль

МQгущ.ествеlUiОГО System.Cbject.
Кроме ТОГО. в D1ЩJе обсyждaJШРЬ :возмоЖНости среды CLR, ttОЗВQляюnnrе иcrrощ.­
зовать объектно-ориентирщщнный ПОдхОД с оБЩИМИ npограммны.м:и JWHCТPYКrw­
ЯЮI. т"зJtliМ}) lЦitH массивы, строки. CтpYJa'YPbl и пере~lНИ.3.десъ же БЫJЩ ра.ссмо­
тpeНh1 операции npиведещц:r R объектному типу и ВОССТa1Iовлеюш из объеКП:ЮI'О
Обрава.8тот оростGЙ M~M nО3ВООЯет с J1егкостъю переходить от тшщв, .ха,рах­
теризyeмblX :щачен:иями. к соыJд)'-.пIым ТИIIЭ.м И обратно. Накойen. была ра~ирыта
рФIЬ ТИПОВ данных с раэрешедие.м npпни.Ма:IЪ значение null и nокв:аано. :КЩС: стро­
"и.тh nолъзавательоtЩе пространства .имен.
1
I

rЛАВА 4
Язьш С# 2.0
и объектно-
и

ориентироваННЬIИ
ПОДХОД

В преднщущей FЛa"В'е:Мы рассмотрели ряд базовых конструкций языка С# и


платформы .NEТ. -а также некот.орые типЫ из пространства имен Sуs'tеПL..
3десь :мы углуБИмся в дет<IJIИ проЦеоса ПЬстр6elnШ объектов. Сначала мы рассмо­
ТрИМ 3Н8Мени'1Ъ1е принципы оап. а затем "ВЫЯСНИМ, хан RМ.eНRO реал:изуютсн ин­
RапсулSПJ;ШI. У.аCJJ:едование и полиморфизм в С#. Это обеспечит знания, неоfЖo,пд­
МbIe ,ц:ля того. чтобы строить иерархии пользовательСКИх Iслассов.
В моде обсуждения мы рассмотрим некоторые новЫе КОНС'1'ру1СЦИИ, такие :кав:
своЁcrва ТИПОВ, члeпы TlffiOB ДiЖ RОН'rp.ОЛЯ версий. ИЗОnИPОВанйЫе нлассы и син­
такСИС XML-IФда ДОку1><rе1iтаЦШ1. I1редмав.ilеiП1ая здесь информация будет базой
.ЦЛя оовоения более сло.жных жриемов :построения IiUlaC:COB (с использоВанИем. на­
пример, перетруженных операций. событий И ПШIЬЗ0вательских программ преоб­
раэования). рассматриваеl'i-IblX в сле.z~ующflX главах.
Даже есди вы чувствуете себя впо.ш:'!е комфорvно в рамках объекгно-ориентиро­
Baннoro подхода с друmми языками npограммированин. н настойчиво реRDМeН,цую
вам :вНnМателыlO рассмотреть примеры проrpамм-tl.ога J-юда. предлагаемые вэroЙ
главе. Вы убедитесь. чТО C1t предлагает новые решения для мнотих привычнЫХ
КОНСТРУ1tЦИЙ объеRТно-ори:еитироВЗIШОГО поДХОДа.

Тип клаоса в С#
Если вы имеете опыт создания об"ЬeJ{Т()В :в p~ ка1шго-то другого язьща про~
:rpaммцрования. ТО, несомиеJ-JШ~, знаете 9 роли оцределе:tnlЙ массов. Формально
говоря, юrзсс- это оцредедeнRЫЙ ПОЛЬЗОВЗ:t'елем тид (User-Defined Туре- ПОТ).
который скомпонован из по.цеЙ дщffiых (ИНОrдз Raэъцшемых щ!'~нaмu'1lt?ремеюtы­
ми) и функций (часто вызываемых .меmодWlЩ), возд~.iiствующих на ЭтИ .данные.
Мщ)жество ЦОllей даиных в СОВОRYUности IIреДСТаБ:rщет ~(;ОСТО1IFLИе~ Эlсземnля:ра:
КJЩсса.
204 ч,sIl:ТЬ 11" Я3ыlo: I1Р[Jtр.аjlillМJt[JО8~Нl!.Я с#

::МоЩъ объ.ЕЖГ.8Q-ОРИеатиройЩЦl:,,~ ~эыъ."ОВ 'зашDPчает~ tJ тещ. Ч'VD- С ПОМОЩЬЮ


грyшmpOВЮl ,Дa.JmЫX и фУшЩп'Q~~ОС',feЙ :в ~Д}Щ(Щ ЦQ,(1W(ЦЩ.Тt:ЛbC:ВObI
'11ше МОЖНО с'tpOИТЬ tJВОИ ,,"обётвeнFlliJi;': прorраммны1~ ТЩIЬt rю Qбр~у JiJ JЩдо6Щt'J
nyЧIПИX образцов. co~ црофесtl.юНВJIaмJ:I. ЛреДП~~1 что вы ДО.1IЖШJ CQЗ­
ЩJrТЪ .n:poгpaмм:m.ш (!)('iъek'l:', МQд.eJ.lJ.ilflYЮщиИ тm:tИЧ:н:ОF9 ра.б'O'tffИКд .щщ БW"c:to.,.ерсщШ
.цporpaмм.ы. С Ы.инимyriS.GМ тpetfовани'й:ВЫ МеЖe;IrеСЩДЩЪ!!iЛ.аСt:; EщpLLоуее {работ­
mntJ. подцepmивaю;ЩиЙ,.muш: Д1JIЙ и:м.еШof, ТflRYI1(e1'0 ypmшa зарruщты и 1D (ЧЩ'JtОЩ)l'G
кода)' раБО'J'НШ{a. ,цошmН:WI'А1IЬi-IО Э1'О'I' мае" :можёТ опр.едели-r:ь M~тeд .Qiwев,:орu$. О .
IЮ'rорbIЙ на ~орую ~r увeJmчиВает ВIIШЛ:3.Ту ДiU'iI Д.8..ЩJqго ИВДИВ~'М.а.
ai 1'aRЖ~ мётl!:lД Diisрlау$tа; tэ () I T{OТOPьm. П~Т ДВ11H1!Ie СQСТ(),ИIf.И'.Я, на рие. 4.1
по~а cтpyн;l)-pa 1'1ша E.mpl·Qy.e-s .

~fiI'Ik1'j
n,~y

i" eropID
iI' 1iI1Nг1'l'''~
Ь /Ii'IeIh:d$
• blspЩStiat5
.~

PIq:,4. 1. TI111 класса. Б1t1рl0у~е

Вcnoмнн:re из rnlillы 3, >ПО масСы R о#. МOr.YТ. О.IIpt"дeлmъ l:ooбo«f. "'lИ'CЩ) iUiJн.cmp1J"~
mopйB- м'о сшщиалънЫе MeтQДjloi. :класса. DбесПечйВаЮщие.nOJI:ЫJ@, вЭ.Телю of;l1:!~tml
ItpiЭС'Ю)'Ю возмо.жность .!WЗдaНmI эtt3~ давнШ"о ЮIaсса с . 3aдa:Jmbl:М поведеJЩ1-
БМ. ftaж:ды:й нлаrn.' В Cff И3Н1IЧa.IIЫm обоопечиваетса mЖ!Л!р.~ ~ШIkЬЩ по
УМDмmшю~ RО'I"ор1ilЙ I10 сщределе'.НИЮ .юшОI,'Дii! не ш,оfе.е'Г аргумеюш. В CДO~
}( H01icтpynropy:,. заданному по умOJГ'mIШю. моЖи.э опредeJlИll'Ъ~ое 'ЧйСЛQ пфiь;ю­
ват.e3I'ЬCЮfX KOI-lСТРУИТOf\!ОВ.
дпя наЧ<iЛa мы определим пa.m:y. первую мОАИФпацв;ю R.!l8ot.a Emp.loye,;;; (по
мере Й3,}'ЧJfИiИЯ мат.ерlimла ца:1l'miIЙ гл.авы 'МlOI !!iyдeM .Добавлять в Э'11О'Г .RltШJС Ёl'0выe
фушщиовam.вьre 'ВОЗМОЖ1Ю<..-тиt

/I И~одиое опре.чеп.е_е ЕП.асо.. 'l!hnp1.0yee.


nWlle·s.pao·e Emp1royees
1
рт:ФliС сl.аs'Э RШpl ,;;!у~~
r
I/ Поn~ ;&:I1UImIX.
pri'l/ate st.ring fцlWamе,
р!' i vatё iпt Ш1l.fi'1 и;
p.rlvilte flo~t cw:-rf',a:t' ;
11 Xax~~pw.
PUbllC Employ:ee \) ( f
pu.bl l r:: Щтр''l9уе~(" ~1:rinq' fuilNaro.e, internpID, iloat. currP'ay)
(
this .. fulliNamEi = !\ Jll bl:a.UIe;
Глава 4. Язык. С# 2,0 и05ъектно-ориеfНliФОIJЗНfIЫЙ Лf).!!хад 205
tb.i-s . emND = ernpID.;
tbis.currPay = c.u.crf'ay;

Jj УвеnичеJDCе 2WПJI&'nJ ДЗJSI Д&IЩOХ'О -раб~к •.


pt1blic V'c:HdJ GivеБоrюз (float апюш!t)
1 'СиттРау 1-= _a mount; j
11 техУщев COC~e ~x~a.
public 'fo id bisplaystats-()
1
С!}пsоlе.WritеLin.e.("ИWaJ!D"}" fullNarne);
Ссшsolе.WritеLJ.л~("З/п: {'О' } ", currJ?a.y);
С4)п;,Оlе.W;ritеLiпе- ("КОД: {О} ", e.mpIDI:

Обратй1'е внимание на реализацию ~OНCTpJRТopa по умолчанию (OIJ ОRаэъП!lЩ~Т­


св ПУ-СТЫМ) ДJm 1t.IIacca EmpJ.oyee,

Pt1~lic ~las$ EmplGyee


(

p)lbliC Employee{) {

Пmдобнр Ст+ И Java,. ~СЛИ ~ оnpeдел.е.нии С#-класса задаются дoльsователЬСIЩе


коиcrpУКТGРЫ, 1'0 Rонегруктор.зaдaннblЙ по умолчанию.ОrmcJl)OЧllert1€Я. без предуn­
режденuЙ. Если ~Ы ХQтите пЬ3Вwшть ПОJIbзователю объекта сО3ДЩlатъ экэеМWIДpPI
~erОКJЩсса едедующнм: образом:

5't.atic VGid Mair! !string Г] args)


(
11 1hwOB ХОВС!!.'РУJШlора, за;ца.киоrО по унoдчa!iИIO.
Emplo:ye€ € = i1ew E...ruplGi}lee () ;
}

то дOJ1'lЮIЫ ЯВНО лереопредimИ.ТЬ JiшrСТРУКТор.. ЗaдaшIЫЙ по )!Модчаюпо. ЕCJЩ ЭТQГР


не СДелать, то цри создании зкзeмnл.яpа вашего -ЮIaссд с ПОМОЩЬЮ КQHt;ТPYКTopa

по умолчанию вы :uол.учите оmиБI<y компиляции. так или иначе. сл~дуюnщй метод_


Main () создает цeJII:iIЙ ряд объектов EmplO'jee, используя наш прдъзоватеЛЬ~
ItORcтpy,нTOP с тре:м;я аргументами.

11 С~~а.ИJ4е иескom.кмx O&l;e~OB Elllp1оуе •.


:s tatk void Main (strirlg[) argsj
[
Employee е =ne'W ВmplO~E\€ (".Джо" f в- о, 30000';
ffillljJloy.ee е2;
е2 = n-еVl Ещрlоуее ( "Бет", 81, 5'00 О О) ;
Console.R~adLine() ;
206 Часть 11. Язык программирования С#

Перегрузка методов
Подобно другим объектно-ориентированным языкам, язык С# позволяет типу
n.eрегружать ero методы. IЪворя простыми словами. когда класс имеет несколь­
ко членов с одинаковыми именами, отличаюIЦИXСЯ только числом (или типом) па­
раметров. соответствующий член называют n.eрегружен.ным. В классе Employee
перегруженным является конструктор класса, поскольку предложены два опреде­

ления. которые отличаются только наборами параметров.

public class Employee


{

11 Переrpужеииwе ItOHC~YJtTOplol.
public Employee() { }
public Employee(string fullName, int empID, float currPay) { ... }

Конструкторы. однако, не являются единственными членами, допускающими


перегрузку. Продолжив рассмотрение текущего примера, предположим, что у нас
есть класс Triangle (треугольник), который поддерживает перегруженный метод
Draw (). с его помощью пользователю объекта позволяется выполнить визуализа­
цmo изображений, используя различные входные параметры.

public class Triangle


(
11 ПереrpужеlUПolЙ меТОД Draw () .
public void Graw(int х, int у, int height, int width) ( ... )
public void Draw(float х, float у, float height,
float width) ( ... I
public void Draw(Point upperLeft, Point bottomRight) { ... }
public void Draw(Rect r) { ... }

Если бы в С# не поддерживалась перегрузка методов. вы были бы вынуждены


создать четыре члена с уникальными именами, что, JtaК можете убедиться, весьма
далеко от идеала.

public class Triangle


{
11 Г.лynостъ.. .
public void DrawWithlnts(int х, int у, int height,
int width) { ... }
public void DrawWlthFloats (float х, float у, float height,
float width) { ... }
public void DrawWithPoints(Point upperLeft,
Point bottomRight) { ... }
public void DrawWithRect(Rect r) { ... }

Но не забывайте о том, что при перегрузке члена возвращаемый тип не может


быть независимым. Так, следующий вариант просто недоnyстим.
Глава 4, ЯЗЫК С#2 , О VJ' DБЪ!),КТНО'ОРttенrrироааннык ПОДХоД 20,7
pи"bH~ Giass 1:riangle
[

/I OпmCS.IC;I! Renьзя переrpра'L'Ь 1teWO;DI


11 .ка основе возар~eиr.IX ца"$ИИЙ!
риЫlс float: G€tXO { •.. )
p1Jblic: i[l1: GetX() ~ •.. }

Использование this ДЛЯ возвратНbIX ссылок в С#


ОбраТИте :nвuмaние нз то" ЧТО другой ш>IiCТруктор :класса Employee использует
юtЮ~евое CJlOBO с# this.
11 ЯВqGe' КСПО3l~зоsаиие "this" .цзIЯ разрешеИИJl; ICОИРЖ!l'ов ииеи.
J?1Wlic Emp10yee (Sitring fullNaдliЭ, iлt60ирJD, float cUT;tP:ay)
(
JI ДpUсвaиsаиие :ВxO;цкwx napaмerrpOB дAИИJolN СОС'1'ОJIНИR..
this.!1:J,llName = fi111Narne;
this.empID = emplb:
thiЗ,currРау = currPay;

Это КJiючевое еловоС# ИfЛОЛЬЗУется ТQгдa. Korдa 'Т{Jeбуется она СОСЛаться на


Щ)JJНI и члены mвкущ.его объекта, Прич:яной использования ключевого слова this
"ЭТОМ пОльзовательском KOHcтp~тope ЯВЛJIется стремление избежать конфликта
имен пар.а.'>1етров :и. внутренщ;rx переменных СОСТОЯНИЯ'. Альтернативой :могло бы
бъiТЬ изме:неFlИе имен BGex паращтров.
I /В ОТСУ',1'(:твие !l:ОНф.nИJt'l'& имен "tШз" по,цраз:уиеsае1DCЯ.
pu.G1jc Еrnрl0уее (striag nате l lrrt id, float рауl
{
fullName> = лате;
empID = id;
сurrРэ'У = ра]n

.Б дalЩОМ случае Fie'Г неоБХQДИМОСТИ ЯВНD добавлять црефикс trJts Е именам Ч1Jе­
I1o,n,nepf:jMeНВ:ЫXEm:ployee. потому 'По RОНф.ликт имен y~~e исRJIЮЧен. КоМПИЛНl'ор
м.~eT самостОя'гe.JlЬШ> ВЬ1fIf:Нi>пъ обла(:ти J;tИДl<JМОС'ГИ ИСЦОJTh'Э)'емых членов-uере­
меННl5IХ, и Б ЭТОЙ с'И:!уации trJi s назъmaIO'Г неяен:ь,!М: есди юrас:с ссЫлается на спои
собствеДНЫfl поля данных. и членьу-перемевные (6е& КЗ!RИХ' бы то ни было неодно­
значвmстеЙ). то tЮ.'3 подразумеnaетея. Тcuщм Qбразом. Црeдыцy:щasl лопша кон­
структора фуНкционально идентична с~дующеЙ.

public Employee(string пате, int: id, float ра'У)


t
this "fiJl1Name = Ьате;
th1s . етр П) = icl:
this,currPa'Y = рау;
208 Часть 11 . Язык программирования С#

Замечание. Статические члены типа не могут использовать ключевое слово t hi s в контексте ме­
тода . В этом есть смысл, поскольку статические члены-функции действуют на уровне класса
(а не объекта). На уровне класса нет this!

Передача вызовов конструктора с помощью this


Другим вариантом использования ключевого слова this является таная реали­
зация вызова одним конструктором другого. при которой не возникает избыточ­
ной логики иmщиализации члена. Рассмотрим следующую модификацию класса
Employee.
public class Employee
(

public Employee(string fullName, int empID, float currPay)


(
this.fullName = fullName;
this.empID = еmрIО;
this.currPay = ситтРау;

JI ЕCnI!l ПOJ]~зоаа .... еnь Вызоае .... э ....о .... КонструК ....ор, .... 0
/ / аереда'1'Ь ВЫЗОВ версии с треllR арryмен.... ами.
public Employee(string fullName)
: this(fullName, IDGenerator.GetNewEmpID(), O.OF} ( )

Эта итерация класса Employee определяет два пользовательских конструкто­


ра. и второй из них имеет единственный параметр (имя индивидуума). Однако
для построения полноценного нового Employee вы хотите гарантировать наличие
соответствующего ID и значения зарплаты. Предположим. что у вас есть пользо­
вательский класс (IDGenerator) со статическим методом GetNewEmpID (), тем или
иным образом генерирующим ID нового работника. Собрав множество начальных
параметров, вы передаете запрос создания объекта конструктору с тремя аргумен­
тами.

Если не передавать вызов, то придется добавить в каждый конструктор избы-


точный программный код.

/ / currPay as ....оМа'.I'ИЧесхи npиpasнивае ....с. к О. OF череs значения,


/ / задаИIWе по умопчёUUШ.
public Employee(string fullName}
(
this.fullName = fullName;
this.empID = IDGenerator.GetNewEmpID():

СледУет понимать. что использование ключевого слова this для передачи вызо­
вов конструктора не является обязательным. Однано при использовании этого под­
хода вы получаете более удобное и более краткое определение класса. Фантичесни.
используя этот подход. вы можете упростить свои программистскае задачи.
r Глава 4. ЯЗЫК С# 2.0 и объеКТНО-ОjJм~тир()ааI'lНЫЙ f1I,IAxoA209

ПОСRОЛЬХ)' р~ьная работа делегируется qAnoмy КОНСТРУВТОрУ I~бъrчно это КОВ ­


струнтор. который имеет н.аибo.m.Шее ЧИСЛО параметров) .. а остальные кощ:трукто­
ры: ПРОС1:0 "'rrерекладьпщют ответственность".

Определ'ение открытого интерфейса класса


После соэдаJЩЯ данных внутреннего состояния масса и набора IЩ1;fСТРУКТО­
ров СЛедующим шагом доЛЖIiо быть определение деталей ofl'l1cpы.1тlot?Q интерфейса
класса. ЭТИМ терJtyЦШОМ об9<!на'Яa:lOТ множество членов. 1:lепосредстзе~о доетуп~
ных из объеlf1'1'lQЙ перемецной -gерез о:перацию. обоэначаемую ТОЧКОЙ.
С ТО'q}Ш зрения fi0строе:нил класса откры:тый ИIiтерфt:ЙС фор~р~тте элемен~
ты. которые 06ъя:влены в :классе СИСПО:ЛЬЗ0ванием tillIO"Ч.евого слова ptjblic. Кроме
:j mщей данных и хонструкТ0ров. oт~ интерфейс ЮIаefЩ может иметь множе­
ство других членов, вщлочая слерующИе.

• Merп.oдь!. Jiменовщmые еДИJ-J;ИЦЫ ДСЙQТВИН. моделируюrцие Не1(оторые особен-­


ности поведенИf\ Juщсса.

• СвоШтва,. Трa.zщЦИoюn.J:е ф~ 'lT~ И МDдифИR8ЦИИ данных.


• КонсmWtТЛJ:il.{ПОЛЯ только д.l/Я -чтения, По:лн данн;ых. которые не допускают
измененив IIDСле присваи:зашm им зщчений (см;. ГIlЗВУ3).

3аМf!'IaЩ'Iе. Из ГlО(jЛ6дУю.щегоматериanа Д<'lНI'IОЙ ГЛQвывам станет 11 l.i:но , что влоJl<tlJ./Н%JIВ опреде­


леНИ!!! т~па тоже могут ПОЯS11яrЬСil в рамках ОТlфbIТого интерфейса типа. r< тому .же. ка!; будет
rюк,аэано в главе В. отIфblты1й интерфей'С К,iщсса может подцерживать' и щ,БЫТI'IR,

с учетом тота. qTO наnr. К.IЩСС Щrnрl0у!'.!е Oupеделлет два опфытых метода
(Giv:eHCJnus О 1!1 Displ <.\yStats ()). M;ы имеем ilозможноCТh взайМQдействоваТJ;> с QT-
крытым интерфейсом 7Щ(. lСЗК ПOJC'<\щшо ниже.

11 ВS&JmОД8Йс!rВ.ке о O~~1oI!I!IiIН ИИ'J!ерф8ЙСОМ ·UI&t:ca Eпr,plQуее.


sr.ati с voi cl Mai.ri (strirrg [] args)
I
СопSt>lе. W ritеL i пе(~ * "'Тип Ernployee в цpoцec ~e paoo !t'bl "' *'\n");
En~p~Qyee ~ = new Еmplоу~еl"Д"1l"о,j. $0, ЗО000);
е • Gi"'lеВ'dпuз ( 2 0.0.\ 1
е. D.isp1ay:St.ats n .;
~.lfJye.e Е: 2 ,
е2 = new EJ1j,plQyee ("Бе'l'" I 81., §ОООО);
е2. Gi 1JfеВопus (10.00) ;
е2. Qisp layS'ta ts О:
С (1 1.'\501е . P,s.;>dLin<e () ;

~CJm: БЪШОЛНИТh это приломсение в том его COCTO~, 11 нотором OН('J находитсл
сейчас. вы ДОЛЖНЫ получить вывод. ПОдобный пока:ЗaJЦ;фму яа рис. 4.2.
На этот момеш у иао имеется очеНЬ простой тип класс:'! с м:инималъным откры­
тым интерфdiСОы. П€ред тем как двиra.тьcя дальше ~ более слажным примеРШll,
дЩi.ВЙТе потратим HeROTopoe Вр6МЯ: на обсуЖдeнJlе ПР1ЩЦИl10В объектно-ори.енти­
po:вaa:нoro про.rpаммИр.ования (paCCMOTPeJ-ше 'ГИДа Ещрlоуее мы продomким He).fiIO-
Т'О позже).
21 О Часть 11. Язык программирования С#

Рис. 4.2. Тип класса Еmрl0уее в процессе работы

Принципыобъектно-ориентированного
программирования

Все объеЮ'Но-ориентированные ЯЗЫRИ используют три базовых принциuа объект­


но-ориентированного программирования.

• Ин.капq;ляцuя. как данный ЯЗЫК скрывает внутренние особенности реализа­


ции объекта?

• Наследовuнue. как данный язык обеспечивает ВОЗМОЖНость многократного


ИСIJОЛЬЗОВания программного кода?

• Полим.орфuзм. как данный язык позволяет интерпретировать родственные


объекты унифицированным образом?

Перед тем как начать рассмотрение синтаксических особенностей реализации


каждого ИЗ этих принципов. важно понять базовую роль каждого из НИХ. поэтому
здесь предлагается краткая теоретическая информация по соответствующим во­
просам, просто для того. чтобы исключить разночтения. которые могут возникать
у людей. ощущающих постоянную нехватку времени на теорию из-за жестких сро­
ков окончания их проектов.

Инкапсуляция
Первым принципом ООП является uн.кancуляцuя. По сути. она означает возмож­
ность скрыть средствами языка несущественные детали реализации от пользова­

теля объекта. Предположим. нanpимер. что мы используем масс DatabaseReader.


который имеет два метода. Ореп () и Close () .
/ / DatabaseReader иикапсу.пируе~ сре.цС'1'Ва рабо'l'W с базой .цa.uнwx.
DatabaseReader dbObj = new Databa5eReader();
dbObj.Open(@"C:\Employees.I1)df")i
1/ РаБО'1'а с баsой .ц&ИЮDt •••
dbObj . С 105е () i
Вымышленный класс Databa5eReader инкапсулирует внутреЮlИе возможности
размещения. загрузки. обработки и закрытия файла данных. Пользователи объек­
та приветствуют инкапсуляцию. поскольку этот принциn ООП позволяет упростить
Гn8ВВ 4. ЯЗЫk С# 2.0 1f объеКТНQ-ориентироваННtllй nQДХQД 211
задачи ПР9ГРаммировани.н:. Нет необходимости беcnоиоитьс;в о мноточисленнМх:
стро:ках программного JЮДа. который вЫ:nUЛИЯет рабо1;J' класса Da~ёibaseReader
- зто со~данне9КЗ~IIJШра и отправка
"за' 1о/JШСа:м,и". ВСе. что требуется от вас.
додхоJtRЩИX СC)Q~Щmшй l.вanpимер. "oTh-pыlI файл Eтpl:qyees.mdf, размещенный
:на моем диске C~}.
OднJIМ из ac;:neRТQB ИНШI.rldYJ1SЩIШ mшнетс.я защита дa1ЩEIlX. В идеале данные
СQCТОЯНИЯ объекта дрлж:ны опредМSIТЪСJL 1{ак 11рlLватн.ыв. ~ omxpьut1:Ыe (как *
было в лреДbl,Щ'ЩИX UJtaвax). В этом случае ~внennщй мцр" будет вынужден "сми­
ренно npocиr.ь" право на изменение или чтение СQОТВЩ-СТВyющIOl. значеНи'Й.

НаСl1едоааИJJlе
СлеАУЮЩИМ. ДРИЩЩJOМ, оап ЯБЛЯетсg НI1CЛВдован.ие. озtIaчающ~е способносТЬ
tIЗ'ЬШа обеспе'чить ПОСТроение ,Qпределеmm новых юrассоn на ОСНОЕе определений
существyюD:l:Щ( RJТЗССО:В. В сущности. наследование позволнет расширить возмож­
ности ДОllеДеНИR бааооого ЮIасеа (называемого, таюке родumeщ,скuм классоМ) с
помощью достроени.я поДШIaсса (иааываемого npoщоодны.м IqЩCGQМ или дочернttМ.
It1laCCOM). насл~д~ющего функциональные возм:ожцости pOДWТe.JlЪeKOT'O JtНacca. :На
рис. 4.3 ИJ1JIЮсrpируется отношение поДЧЮIeIOlQСТИ ("l$-а") ДJШ родительских и да­
черНИJC Х1шсеов.

Можно прочптатъ эту диаграмму так: ~ШеСТ~JIhНИК (b,exagon) smляетс:Я фор­


мой (эhаре), Rоторая.f.ШJШ:ет('я объею-ом (object).. , При создацин Ю1эссов, eвaaaнrrъiX
э.тоl:i ФОРМQЙ Itаследовэния. EЬr создаете отношещщ Л9дчиненности между типами.
ОтвQшеН)d,е подчиненности частЬ называется JC,1IйL'сцчесlCU.М lЩ(.":'ледованiшм.
Воыомните ~згла:вы 3, что System.Object ЛВдя:еТCSt~Д~ПiНЫМ бааов:ым мас­
сом: люБQj1 иерархии .NEI: Эдесь юtaCС Shape (форма) расnЩJЩeтОЬjеоt (объект) .
Можно цредположит.t.. 'ЧТО Shape опреАenaет sекоторый 'Набор своИСТ.в. полей. ме­
'!'ОДов,и соб~ТИЙ. :которые будут общими для всех фОРМ, КJ:racc }JexagD:fi (п:rестиу­
r~) раС,ширяет Sha.pe и наследУеТ функциоюшьнъrе воз,мржности, определен­
fIЪ1e в Рамках Shape :в Obj'e'ct.. вдоба:вcm к определен:ищ ЩЮ.IJX ~обствеRlIых членой
(ltaкимJt бы щrи ни бъtли).
В .м:цре ОON есThИ другая форма :многократного ИСЩJЛЬ3QЩшия пporpаммнi')Го
нода-. ЭТ9 модель JIQНaШ'tзации/Д€llеrйрован:иs (также известная. RaК 01'Ноmeние
лонализа:ции. 'Ъаз-а"). Эта форма 'Многократно'Го испоЩ>зования nporpaммнor()
КОДа :ре исnoльt:lует,ся д,IlЯ. создания отношений ~кдасс-подклас;с". Скорее данный
ItЩ1сс МD:Щ:~Т оnpедсliИ1'Ъ член - перемеIiНУЮ дРугого КJIЭСС~ и: (i}·rtrPЫТb часть или все
СВ9и фущщион~е 'ВОЗМОЖНОСТИ для "внешнеГо мира~.

Рис, 4.3. OтI'Iошение "ОДЧl'Iненности Д1IJ'I


родите1U>СIQIIХ И ДО'lврЮ1Х классов
212 Часть 11. Язык программирования С#

Например, если создается модель автомобиля, 1'0 вы можете отобразить тот


факт. что автомобиль "имеет" ("Ьаэ-а") радио. Было бы нелогично пытаться полу­
чить класс Car (abTOh-Jобиль) из Radio (радио) или наоборот. (Радио является авто­
мобилем? Я думаю, нет.) Скорее. есть два независимых класса, работающие вместе,
где клаСС-Iюнтейнер создает и представляет функциональные возможности содер­
жащегося в нем класса.

public class Radio


{
public void Power(bool turnOn)
( Console. Wri teLine ("Radio оп: (О)" r turnOn);}

public class Car


(
/ / Car содер*ИТ ("Ьаз-а") Radio.
private Radio myRadio = new Radio();
public void TurnOnRadio(bool onOff)
{
/I дМSI'а'1' Дnll ВНrrPеинего об'1оеК'1'а.
myRadio.Power(onOff);

Эдесь тип-контейнер (Car) несет ответственность за создание содержащегося


объекта (Radio). Если объект Car "желает" сделать поведение Radio доступным
для экземпляра Car, он должен пополнить свой собственный открытый интерфейс
некоторым набором функций, которые буцут действовать на содержащийся тип.
Заметим, что пользователь объекта не получит никакОй информации о том. что
класс Car использует внутренний объект Radio.
static void Main(string[j args)
(
/ / ВШlОВ 1IНУ'1'рение пере,цае'l'CII Radio.
Car viper = new Car () i
viper.TurnOnRadio(true);

Полиморфизм
Третьим принципом ооп является полиморфизм. Он характеризует способ­
ность языка одинаково интерпретировать родственные объекты. Эта особенность
объектно-ориентированного языка позволяет базовому классу определить множе­
ство членов (формально называемых noлwиорфн.ым. интерфейсом) для всех произ­
водных классов. Полиморфный юrrерфейс типа класса строится с помощью опреде­
ления произвольного числа виртуал.Ы-iblX, или абсmpактl-iЫХ членов. 8ИРТУarIЬНЫЙ
член можн.о изменить (или. говоря более формально, переопределить) в произво­
дном классе, тогда как абстрактный метод должен. переопределяться. Когда про­
иаводные типы переопределяют члены, определенные базовым классом, они, по
существу, переопределяют свой ответ на соответствующий запрос.
Гi13ВЗ 4, :Язык С# 2.0 и об:ьектно-6РI1~НТИРDваНI1IЫЙ подход 213
Чтобы ПРОИ.Jr.iIЮСТРИРовать понлтие ПOJlЩ.'IQрфиам~, снова используем ие.рархию
форм. Предположи:м.. что Масс Shape определил MeтQД Draw (), не имеющий ПАра­
метро:а и не :ВОЗВр8ЩaJoЩИЙ ничего. С учетом 1"ОГО, что визуюmэацИл ДЛЯ каждой
фЬрмы оказывается уникалыщЙ. nOJUtJЩССЫ (т!щ"Ие кав Hexagon :и Circle) MOryт
ш:реопредeшrrь еОO'tВете1:nУ:Юиum метод так,:как это требуется ДJIН них (рис. 4.4).

~I
&rэoв btaм() дroя ООi.rЮ8
Cfrole trображэе1 kW.

8ы:)QI1 Pr8lli() дr1Я оощта


~DТщ-авт
IlJSCT~YГ1Jrn.Hi'IК.

Ри~. 4.4, КJТассWiеский ПОЛ/olМОРфI1ЗМ '

ПОС,JJ;е создания попиморфНОГQ йв:rерфeiiса мощно ИСПЩIЪЗОВВ:ТЬ различные


пре-ДПОЛО)JreВИЯ. касающиеся, програм:много кода. На.пример, если Нех а чаn и
Circl€ Щ!JI.fIЮтслпроизводными от qrЦИОГО общего родителя (SJ:)ape:!). то не:которьtЙ
массцв типов Sh.ape можеТ содержать любой nPОН3ВОi!nIЫЙ масс. Более того, если
Shape оn-редешrет полиморфный Шlтерфейс дл.~ ВС{$ 'прОИЗВ9ДН:ЫХТИnОВ fв дан·
~OM прим:ере э'tо метод Draw (1). то можно предположить. что R:aЖДЫЙ член п TaJ\1QM
Щl.ссиве имеет этл фУНIЩИОНaJIЪные возможности. ПР0аНaJIИзируйте сле,цующий
м:еТ9д Maln (). в котором массиву типов. UРОИ3ВQДIThIX от 6hape, даетсяу:кааание
~и:JyЭЛИЗИРОВа.ть себя (' 11DМОЩЪЮ метода Draw () .

s'tatlc v01id Мilin. (st.ring [] ar-gs)


{
11 СФзд,авие иассиа& эneнеlЩlОI!I, npoиsводных 0'1' Зh~е.
Shape [J J!'.ySl'lapes = Ц~W S)'la.pe [З] ;
lY1ySl'1apes [ О J = .fJew HexagoТJ () ;
П1у3hареs jl J = J1ew ci ~Qle. (.) ;
mYSha;pes L2] = new Нехачеn'Ц ;

11 Двюникеnокасmcву и O'1'OCSpur8.1DU!t !lJIeИeН'1'Ов_


foreach [Shape $ iл myShapes)
s .I)raw О i
Солвоlе.R.eаdLiпе();

На этом наш кра:rкий (и YUРDщеlIПБ1Й) обзор пр~lJОВ ооп 'завершается.


Теперь, имея в запасе т~рию, :мы. "Исследуем некоторые 110дробности и т(Jчный син­
таксис С#. с lIОМОЩЪЮ~ОТОРQro реa.rrизуIOТСЯ каждый на ytffiЗав:вьrx прющиnов,

Первый принцип: сервис инкаПСУЛIIЦИИ С#


ПОlI.fЛ1ilе идщшtymщии о:rpaжaет общее правило. соглас.RО :которому поля дан­
н:ьт об1;>е.кта не д.олжны. бliJ!:rЪ непосредственно дос1)'ПНЫ из открытого интерфей­
са. &ли ЩlJIЬЗОlJзсгель ·Об:ыlliта желает ИЗМе1mтъ СОСТОЯF,lие об'.Ьеl!:та , то он дo.mкси
214 Часть 11. Язык программирования С#

делать это косвенно, с помощью методов чтения (get) и модификации (set). В С#


инкапсуляция Мнавязывается" на уровне синтаксиса с помощью ключевых слов
public. private. protected и protected internal. как было показано в главе 3.
Чтобы проиллюстрировать необходимость инкапсуляции. предположим, что у нас
есть сле.цующее определение класса.

11 Кпасс с о,цнии оCSщедосrюуlUQllll петек.


public class Book
(
pUblic int numberOfPages;

Проблема общедоступных полей данных заключается в том. что такие элемен­


ты не имеют никакой возможности "понять~, является ли их текущее значение
действительным с точки зрения "бизнес-правил" данной системы. Вы знаете, что
верхняя граница диапазона допустимых значений для int в С# очень велика (она
равна 2147483647). поэтому компилятор не запрещает следующий вариант при­
сваивания.

11 М-н-н-да . ..
static void Main(st~ingrJ args)
(
Book miniNovel = new Book();
miniNovel.numberOfPaqes = 30000000;

Здесь нет перехода за границы допустимости для данных целочисленного типа,


но должно быть ясно, что miniNovel ("мини-роман") со значением 30000000 для
numberOfPages (число страниц) является просто невероятным с практической
точки зрения. Как видите, открытые поля не обеспечивают проверку адекватно­
сти данных. Если система предполагает правило. по которому мини-роман должен
содержать от 1 до 200 страниц, будет трудно реализовать это правило программ­
ными средствами. В этой связи открытые поля обычно не находят места на уров­
не оuределений классов, применяемых для решения реальных задач (исключением
являются открытые поля, доступные только для чтения).
инкапсуляция обеспечивает возможность сохранения целостности данных для
состояния объекта. Вместо определения открытых полей (с помощью которых
очень просто прийти к нарушению целостности данных), вашей привычкой долж­
но стать определение частных полей aaHHblX. которые обрабатываются вызываю­
щей стороной косвенно. с использованием одного из двух г.лавных: подходов.

• Определение пары традиционных методов чтения и модификации данных.

• Определение именованного свойства.

Суть любого их этих подходов заключается в том. что хорошо инкапсулирован­


ный класс должен скрыть от "любопытных глаз внешнего мира" необработанные
данные и детали того, как выполняются соответствующие действия. Часто такой
подход называют nрогра..м.мuровwшем "черного ящика". Красота этого подхода в
том. что создатель класса имеет возможность изменить особенности реализации
любого из своих методов. так сназать, "за кулисами·, и нет никакой нужды отме­
нять использование уже существующего программноro кода (при условии, что имя
метода остается прежним).
Гла~а 4, Язык с# 2,0 11 обьеI(ТI-Iо·орие~Пil1рОва~JНЫИ flОДХОД 215

Инкanсудяци'я на ос, нове методов чтения и модификации


Да;ваЙ'те снова вернемся R рассмотрению нашеFО класса Empl .o-yee. Чтобы
"вне1ШШЙ мир~ .мог взаимодействовать с частным поле,м да.нны.х f .u I1N,i3,we. TP~~
днции вел.нт определить средства чтения (.м~oд get) и .модификации (1I.reТOД э.е tJ.
Нanpим~:

11 1'радициORlU1e сре.цСТВ4 Ч!I.'ВНКИ 11 IIOдмфихаQ,ИИ ДJJ. DplllВа!l.'.шt ,цaJUDD[.


риЬНс clas$ EmplDyee
(
pr:tvate string, ful1Name;

/I ЧтаНИв.
рuЪНс stting Ge,t.F'ul.lNama () ,{ return fi:J,l1Name;
J1 МО~фикацик.
paы1cc vэid SеtFullВаще (st:ring п)
j
/1 УДllJ1еlUlе Re~ODYC!t'JQD1x CJtI8OJtOB t!, @, #. $ I %) (
11 аро.ер_а ....~!Ж.ц;шcпI (иnи реrис:тра CМld0J10.')
/I nер.,-ц DpИCJt~ави_.
I'ullNa;me = n;

Конечно, КQМIIWmтapy "все равно", что.вы будете вЫЗывать методъх чтения и


модификации данных lloCJЮЛЪRy GetFullName () и S.etFullName () йнgaдсу.'Urpу­
ют npивцтную отрану () именем fuli"Name> въrб0Р тащц. имен хажетса вполне Щ}Д­
ХО"gЩИМ. ЛогИItЭ. вызова может быть СЛеДУЮЩей.

11 ИСпош.зоаа.ниа cpeAclD ч~/.иодхфижаци:и.


sta t ic vbid Маlп (~t,ring [] args;)
(
Employee р = new 'EmpJ:Qyee (, .;
р . S.tFUj,..lNi!1IIe ("Фре,n Флин-стон"! ;
'C oflsole •Wri t~'Line (ПИмя; работника ~ (О I ". ,р. GetFlillNaJDe ()' )' :
Сопsоlе.RеаdLinе();

Инкапсуляция на основе свойств класса


в ОТJIИLЩе ОТ Трццицианпых Методов чтения }J модифихации. языки .NEТ тн­
ro:relOT It реа.цизации прmщнnа инкanсу.пsщии на 9С.5Рllе нспоЛhЗОIIС1НИЯ своЙсnщ.
которые предстa::вJ1НЮ'Г дoc:rynныe ДJlЯ внещнего 'ПЩlЬ'~овател.я: элементы дaIU:lЬ1X,
ВМ,еета ТIЭГО, чтобы вы.зъmатъ два раэщ,IX ~етодщ (get и se,t ) для 'Lrrения и YCTaнOB~
хн данных СОС1ЮЯнИно6ъеll:та. пользователь получае:г возможность вызвать нечто,
похожее на рбщедос-тynное поле. ЛреДnIШОЖИМ. ЧТо. мы имеем СВОЙЬтво с именем
1D (.Nод).. представлвюще'е ВfrYТ'PеннИ'Й член-перемеяную empID типа Е,mрlбуее .
Синтаксис Bld30B3 В даннОМ елучаедолжеJi выглл:це'lЪ примерно нЯ.

/I Cии'J!ажсио ycwaI!OIJJOI/'Ц'e~ ЗS8чe.JIИИ ID раБО'1!~а.


5tatic void Мairl (~t r i!)'gI ] аЧJ3)
216 Часть 11. Язык программирования С#

Employee р = new Employee();

11 Установха значении.
p.ID = В1;

11 Чтение значении.
Console. Wri teLine (" ID работника: {О) ", р. ID) ;
Console.ReadLine() ;

Свойства типа ~за KaдpOM~ всегда отображаются в "настоящие" методы чтения и


модификации. Поэтому. как разработчик Iwacca. вы имеете возможность реализо­
вать щобую внутренlПOЮ логику. вьшолняемую перед присваиванием соответствую­
щего значения (например. перевод символов в верхний регистр. очистку значения
от недопустимых символов, провеРIi}' принадлежности ЧИСЛового значения диапа­

зону допустимости и т.д.). Ниже демон:стрируется синтаксис С#. использующий.


IipoMe свойства ID. свойство Рау (оплата). которое инкanсулирует поле currPay. а
также свойство Name (имя). которое инкапсулирует данные fullName.
1/ ИиJCаПСУnJЩИR с ПОИОЩЬЮ свойств.
public class Employee
{

private int empID;


private float currPay;
private string fullName;

11 Свойство ДЛR empID.


public int ID
{
get ( return empID;)
set

11 вы иожetl'е проверИ'J!Ъ и, если требуеТСR, иодиФицироватs.


11 поступившее значение перец присваиваниеи.
empID = va1ue;

11 Свойсo:t'ВО ДJIK fullName.


public string Name
{
get {return fullName;}
set {fullName = value;}
11 Свойство ДJIK currPay.
public float Рау
{
get {return currPay;}
set {currPay = value;}
Г~a8a4. Язык С# '2.D ~ обыщно-ориеftтираSЭI4Нtaй ПОД~ОД 21'7
CJliJ>ij:cwвo 'в G# ROмпонуетса из бл01tа чтеюrя и БJЮJ~ моддфищщии (уСТ<WQВКИ)
Эlщчеюm;. Ярлык va~ LJe 11 С# llР~ДСТaшISIет правую сторону оператора npисвацва­
IЩЯ . Соответствующий я:рлыку value тип данных зависит от того. какого сорта
данные этот ярлъrк .npeдс-тавля.ет. .в данном примере СВОЙСТВО ID оперирует с ти­
I;ЮМ ДaнRhIX i:nt. которЫЙ. как вы знаете. отображаетС1\ в System. 1nt32.

11 81 I1pJD!a,I:(JIеllИ'1' sys ten1. In t-3 2 ,


11 ПОЭ'J!OИV "зваo,zе,ИJlвм' ~TCJJ syet8ll1.,Int32.
1

E.mployee е = пе w, Employee ( ) :
е . т = 81;

Чтобы ~o ДORQ3Э.ть. преДПОJlOЖИМ. чтр вы добавИШ!, следУЮЩиЙ проrраммный


:код для уетановки свойства ID.
// Свойотво ~ ешрID.
jюbliс int 1О
j
ge:t ,{ x 'e tllrr! 6П'lрJD; 1
set

Сопsоl е • "'Т! teL1ne ! Пvдluе Я:вU\яе'l"СЯ з:;езе,Мi1ЛЯfЮМ' (О) 1',


vaJ.tle.GetType()) ;
GOJ'Jsol е , Wri te.Une ("ЗнаЧ6rIИе -value, i ,О:} ... , у;аl1.1е'):

ешрI' D = val не;

Въшоmпm npи.ножени:е. вы должны увидеть ~ариант .въwoдa, показa:нв..ы:i;. на


рис. 4.5.

Рис. 4.5. 3на'iеяие vаlце после установки дnя 1D зна.Ч~НИЯ 81

3aMeof8Kмe. СТРОГО ГQ8QРЯ, S1РЛЫК val ие в С# является H~ J(I]ючевым словом. а. CJ(ope~" коНте/(С!Т­
IiblМключевым СЛОВОМ, предOiaВЛЯIФЩИМ НВRВНЫЙ параметР,1ЩТQРЫЙ исполЬЗУеТСя !'IOnepaTQ-
ре пРИG,lЩиеЗНИJ1 в KO~ITeKcтe ме-года. JIIOnOllb/lyeMoro ДЛЯ устажшки значения свойства. Поэrому
вполне доnyотимо имеТh чле~ы-пеlDемеtНШе и 110кз'лыiеe элемеt-tты ДQHHbD( , с именем уа,1 ие.

Сле-дует П'оПИ]l,t;i1Ъ, Ч:ТО свойства (В отщ,ч:щ~ 9т традициоimыX .методов чтения я:


МQДИфЮtации) еще и упрощают ра:бory с nmaми. IЮC-1<:Олъ'ку свОЙства ~пособны "ре­
arировать" на Bнyтp~e операции в С#. 1iш:rpимер. лредпCJlIUЖИМ, ЧТО тип RЛaсса
E'r oployee имеет IЩУТР(Щ1ЦIЙ приватный чден. предстaSлmoJi1;ИЙ значение возраста.
ра:ботшmа. Бот COQтветствущщая МОДИф.t,maщш класса.
218 Часть 11. Язык программирования С#

public class Employee


{

/ / ТехYIЦJfЙ возраст ра6отвиха.


private int empAge;
public Employee(string fullName, int &ge, int empID,
float currPay)

this . ешрAgе = age;

public int Age


{
get{return empAge;)
set{empAge = value; I

public void DisplayStats()


{

Console . Wri teLine (" Возр&ст: {О}" ешрЛgе);

Теперь предположим. что вы создали объект Employee с именем j ое. Вы хотите,


чтобы в день рождения работника значение переменной возраста увеличивалосъ
на единицу. Используя традиционные методы чтения И модифишщии. вы должны
применить. например. следующий прогрaммный код.

Employee joe = new Еmр1оуее();


joe.SetAge(joe.GetAge() + 1);

Но еCJШ инкапсулироватъ empAge. используя ·правильНЫЙ" синтаксис, вы смо­


жете просто написать:

Employee joe = new Employee();


joe.Age++;

Внутреннее представление свойств в С#


Многие программисты (особенно те. которые привыкли использовать С++)
стремятся использовать традиционнъrе префиксы get_ и set_ для методов чте­
ния и модификации (например, get_FullName () и set_ FuIIName ()). Против са­
мого соглашении возражений нет. Однако следует знать. что ·за кадром" свойства
в С# представляются программн:ым кодом CIL. использующим такие же префик­
сы. Например. если открыть компоновочный блок Employees. ехе с помощью
ildasm.exe. вы увидите. что каждое свойство ХХХ на самом деле сводится к с:кры­
ТbIM методам get - ххх () / set- ХХХ () (рис. 4.6).
Глава 4.. Язык С# 2.0 иобъектно-о,рИентированный ПОДХОД 219

.а ' :Str1)Q()
• goUD,tt~
• ~ftImII:~~
• QotJ''e)' \ F\oatЭZС)
.. • ueСSО1:iOlБeщtуNl:rlil:et : 5I:r1n9O

ркс. 4.6, Отр(ijражеf,иеСВ(ЖСТtJ ХХХ В . окрытые методы get _ ХХХ () и set ххх ()
JJреДЦОЛ9ЖИМ теперь., что тИп ЕЛliplоуее иМеет частнъхй член-переме.I:tНy!О с
лмен~м €ropSSN для представления номера социальной страховки работнmщ. Эта
п~ремj:':ННая устанавливаетсв через пара.l\reТP KbНCтpyIC'rOpa. I;J: ]J.11.Я ynpwщеmщ этой
lIерем~ной используется свойство So.cialSecuri t. yWu.rr:be r,
// Добaэnеuе по:q,чеp:telOl иа.ОI'O ПQmIi, npeдC'l'aa~.....ro SS»-I:СоЧ.
public Cl~5S Employee
{

11 Номер социam.аoiil ~axo.1GI (SSN).


pri "i1Ite s tring empSSN;

риЬНс Employe'e (stiring f'ulJ;Name, 1nt. age, L'1\t empID.,


float {~1JTrPay. strinqssn)

рuЫ ic stri~g, SocialSecurltyNumber


j
ge1. r е t1.J П1 empS.s liJ ; I
$et empSSN = valu6; j
~
p'\lt.>l ic void Displa.ySta-r:s ()
{

СQлsоlе . Wri.t~ f ,/ SSN': {О}" eaapSS1f),'

Если бы 'Вы т.a1tЖе опредетmи два метода g~ t soci ,11 SecurityNumDe:r ') и
set _Soci а lSec1Jr:i·tyNumber () , то получили б1>l QOnЩКИ коМIДLJJjщии.
11 СвОЙ;С!L'ВО • С, О'J'Oб~е!L'CJr • .пару ие'1!ОДО8 get._I_et_,
public сlазэ Е:шрlоуее .
{

fI OШИSXA! У.е ОпрЕ:Д8J1&ВIo1 CUIJQI Claой~ох!


p.ublic s tring qe.t _ sосiq.lSесu:ritуNщnbеr () {retи.rn empSSN; J
public 'J"t'>id set _ SОСiа:!JSе.сщ:i tyNumbe;r; \ 5 tring \Тal) I етnрSЗN = val;}
220 Часть 11, Язык проtраммирования С#

Замечание. В библиотеках базовых классов .NET всегда отдается предпочтение свойствам типа
(В сравнении с традиционными методами чтения и модификации). Поэтому, чтобы строить
пользовательские типы, которые хорошо интегрируются с матформой .NEт, следует избегать
использования традиционных методов get и set.

Контекст операторов get и set для свойств


До появления С# 2005 область ВИДИМОСТИ get и set задавалась ИСКlIЮчительно
модификаторами доступа в определении свойства.

/1 Логиха get и set здесь OTxp.wa,


// в соответст8ИИ с определением свойства.
puыlcc string SocialSecurityNumber
{
get return empSSN; )
set empSSN = value;)
}

в некоторых случаях бывает нужно унаэать свои области видимости для мето­
дов get и set. Чтобы сделать это, просто добавьте префикс доступности (в виде
соответствующего ключевого слова) к ключевому слову get или set (при этом об­
ласть видимости без yroчнения будет соответствовать области видимости из опре­
деления свойства).

1/ Поnъзоватe.nи об'И!хта иоryт тольхо получить значение,


11 во производJU18 '1'ИШI MOI'YТ Т&.ItЖе YCTaвOB~ значение.
puыlcc string SocialSecurityNumber
(
get ( return empSSN; )
protected set [ empSSN = value; }

в данном случае логика set для SocialSecurityNumber может вызываться


только данным классом и проuзводнbtМu классами, а поэтому не может быть до­
ступна на УРЩlНе экземпляра объекта.

Свойства, доступные только для чтения,


и свойства, доступные только для записи
При создании типов lЩасса можно создавать свойства. доступные только ДЛЯ
чтения. для этого просто создайте свойство без соответствующего блока set. Точно
так же, если вы хотите иметь свойство. допускающее только запись, опустите блок
get. Для HaIlIerO примера в этом нет необходимости. но вот как можно изменить
свойство SocialSecurityNumber. чтобы оно было ДОGТУПНО только для чтения.
public class Employee
{

/ / Теперь это свойство, доступное тольхо ДЛИ чтении.


public string SocialSecurityNumber { get { return empSSNi } )
Глава 4, ЯЗblК С# 2.0 1<1 объеПНQ-ориеflТlfрО!lаlНJЫЙ nодход 221
При таном измеНении еДИНСТQеJlНЫМ €ПQсобо~ УС11ЦlОв;ки номера СОЦИWIЫi'О~
страховки длярабоТНИ1tа онааываетек УС':(ЗНQВRaЭТО],О н,OM~pa через ,apryмeнг ВОН­
€TpyК'ropa.

Статические свойства
в С# таиже поддерживаются статические cвoЙCmвa. Вспомните из гл"Ввы 3.
что статические члены доc1'yпны 1ia урQJще класса. а Ее Э~ (объента) атп­
го класса. Например. цредположим, что тиц Effiployee оцределяет элемент стати­
ческих данных, предстввляюП:1ИЙ 1-IЭ.Эщm:ие оргсщизa:цmI. 'в IЩ-WРОЙ тpyдoyc>rp6eu
работнив. Можно определИТЬ стат.иче-щсое С:ВОЙСТ~{) [например. уровня шracса) так.
:как покаэан:о :ниже.

iI C'Pa!llJNec~e Qзойс~за: доп:а:вк опериро_а'l'lo со C'1!&'1'JI1IecJUdDr д.K".....~H


p ublic class Employ ee
i
private з ,t-аtiс s t:I?iпg 'cDmpa;ny.Name;
риЫ i 'u &tatic Strin.g С отрапу
{
ge,t --1 .IZ'еtш::l'J
company!l1ame-;
set { compaIJY)1ame = -~al1,!e; },

Стa::rичеснИ'~ свфйства -I,JQПО:ТIЪ~тСfI ТОЧНО ТаК же. КЮ( статические методы.

11 уC!r_OJIJ\!a и чтение НА'9ВaиIOI JCОИПАИИJI,


I J а IФТQ,рой !ЦIУДО~С'1'рОeR:lil Э!t'U р~бо~ииlШ ...
public .gtati;:: .int мain (string [] args)
{
Employ ee .C~any = . "Inter tech, I'r:ad. Dl:nq";
C0!1S01e . Wri tel.ine- ("Эти люди работают в [О] ", Employee. Co,mpany) ,;

1'ащке ~СII0мните из rnавы З. что в е# поддерживаются статические конструк­


торы. ПQЭтаму.еСЛИ вы хотите. чтобы статическое DВОЙСТВО comp.a.nyName все:гда
устанав;швалосъ равным lntertecr. Training, можете .дof!авИ'Г~ в цласс Етрl0уее
ЧЛ(Щ CJ1едУющer'О.JIИДa.

11 О1!а'1'ИЧеСЮ!Й JOОНСЧ!УJC1J!ОР ееЗ мо;цифИК&l1'ороа доступа и 8рZ')'1lleИ'1'O'II.


public e'las$ :Е:'щрlОУj:!ЕО
{

st",tic Employee ()
l
с.ощрапуNаmе '= i' Il'i t ert 'e GI1' TrairJiDg " i
}

Б данном случае МЬ! ничerо не выитрв.>щ в результате добавленИя стarи~,еского


Еонс:трунтора, если учесть, что тот Ji~e реЗУJЦio'Fат МОЖНО было бы ДОСТИЧЬ с ПОМО-
r

222 Часть 11, Язык nрограммирования С#

щью простого присваивания значении члену-переменной companyName. как пока­


занониже.

11 С'r&'ПNесхие С30ЙС'r8а I3;ОmlИlo1 оперироsа'rЪ со С'r&'rИчесDOlИ JI.&I1ИЫNИ \


public class Employee
I
private static string companyName "Intertech Training";

Однако следует вспомнить о том. что статические конструкторы оказываются


очень полезными тогда. когда значения для статических данных становятся из­

вестны только в среде вьшолнения (например. при чтении из базы данных).


В завершение HaIJIero обзора возможностей инкапсуляции следует подчеркнуть ,
что свойства используются с той же целью, что и классическая пара методов чте­
ния/модификации данных. Преимущество свойств зaюnочается в том. что поль­
зователи объекта получают возможность изменять внутренние данные. используя
ДЛЯ этого один именованный элемент.

Второй принцип: поддержка наследования в С#


Теперь. после исследования различных подходов, позволяющих создавать клас­
сь! с хорошей инкапсуляцией, npи:шло время заняться построением семейств свя­
заНIiЫХ классов. как уже ynоминалось, наследование является принципом ООП,
упрощающим многократное использование программного кода. Наследование бы ­
вает двух видов: классическое наследование (отношение подчиненности, "is-a") и
модель локализации/делегирования (отношение локализации. "has-a"). Сначала
мы рассмотрим классическую модель отношении подчиненности.

При создании отношения подчиненности мещду классами вы строите зависи­


мость между типами. Основной идеей классического наследования является то.
что новые классы могут использовать (и, возможно, расширять) функциональные
возможности исходных классов. для примера предположим, что вы хотите исполь­
зовать функциональные возможности класса Employee и создать два новых клас­
са- SalesPerson (продавец) и Manager (менеджер) . Иерархии классов будет вы­
rлядеть примерно так. как ПОRaЗано на рис. 4.7.

Рис. 4.7. Иерархия классов служащих

Из рис. 4.7 можно понять. SalesPerson (npoдавец) является ("is-a") Employee


что
(работником). точно так же. как и Manager (менеджер). В классической модели на­
следования базовые классы (например. Employee) используются для определения

L
ГЛа1!S 4. Язык С# 2.0 и обliеКТНQ-ОРl>1еНТI1РОВднныi\ ПОДХОД 223
Общих харав:геристИ1t. которые б:удутnpщ:ущи .QCeM потомхам. ПoдRJШССЫ (1:fаыри­
мер. SаlеsРеГSО!1 и Manager) расширяют общие фУШЩИОШ1.ЛРные возможности.
добfIВШIЯ специфичеCIQlе элементы Dове.цеюш.
длл нашего· примера М1iI предположим. что класс Мадаgеr раcmиpнeII' ]:;mpl оуеЕ:.
обеспeчившr. 3aIlI1CЪ числа ОПЦИОНОВ. а ltласс Sа1еSРец•.ОП ПQДЦер}ЩIIЩет информа­
цию о числе .продаж. В С# расширение JtlIЭCса задается в оnpеделеиии .{tЩlсса опе­
рациt:и, обознача~(JИ двоеточием (:). так ПOJJY'UUO"fCЯ ПРОИЭВQдные тицы класса в
сл.едующем фрагмеНте npограммноro кода.

11 Добааление двух /З()дмассов в npoС'Ж'р2ЦtС~ нкен Elaployees,


n.атеэрае;е Employees
t
public cla.$s Малаgе-r : ЕшрlQ:yt!е
[
11 Меие.цwер дomUН ~awь числс) ОЩPlOно•.
private UlOD'g щunЬеrОfОрtiопs;
pub1ic 111011g N\J.rnbOpts
{
get retU'In пшnbе,rО:fОрti.онs;
set n.DmЬerOfOptions = value;}

pl.1blic class SаlеЗРеrsоn : ElDployee


I
11 ~poдaвeц до.паев ЗJI&'No чис:.попрояu.
pIivate int numberOfSaleS;
pt1blic int NU!IlbSales
!
g!:!t rе .turл nЩ)1'ЬеrОfSаlеS7 .)
.set nы.rnЬerOMales = value; }

Те.nеръ. когда создано отgоmени:е подчиненности. Sal~s~ersoI'; ~ :Маnа.gеr ав,­


тоМвтИч.е.С"k"И наследуют все открытые (и защищенные) чледы б.аЗОБОГО масса
ЕПlрlQу. ее. Например:

/1 СоздаlUlS IXо~JtПа~а м: .дос~уп ~ ФУJIIЩJ,IOJtaJ:.IЬ- .OSIIOIQI()O~


11 ба,9QВОI'О IШaQса.
static void Main (st.tiпg [] a'rgs)
(
11 Со.давие ЗxJJitмппкр& salesPer80n.
Sales"Person ~ta.n '= new 8.alesPersor, () ;
11 Э!t>fI ЧJ1е1UI .aCJ1~ аоsио.аос~ cia-sо_оЖ'О lШасса lilmployee.
sta.".ID '" 1 00;
= "Stai1",
::,-t.аn.Nа1Dе

11 .9'1'0 оцре.дедево .кзt&ССОК SaJ.esPe,rson.


stan . NumDSales, = 42';
Co,nsale . .ReadLine (\;
т

224 Часть 11. Язык программирования С#

Следует знать, что наследование сохраняет инкапсуляцию. Поэтому произво­


дный масс не может иметь непосредственный доступ к приватным членам. опре­
деленным базовым массом.

Управление созданием базовых классов с помощью base


в настоящий момент SalesPerson и Manager можно СОЗдаТЬ только с помощью
конструктора. заданного по умолчанию. Поэтому предположим. что в тип Manager
добавлен новый конструктор с шестью аргументами. который вызывается так. как
показано ниже.

static void Main(string[] args)


{
11 Предпоnожик, Ч'1'0 ec'l'Ь спедyIOЩИЙ хонструхтор с параиетрами
11 (иня, возраст,
ID, пnата, SSN, чиспо оnционо.) .
Manager chucky = new Manager("Chucky", 35, 92, 100000,
"333-23-2322", 9000);

Если вз:глянутъ на список аргументов. мощно Сразу понять, что большинство из


них должно запоминаться в членах-переменных. определенных базовым массом
Employee. для этого вы могли бы реализовать зтот конструктор так, как предла­
гается ниже.

/1 Еспи не yxasaHO иное, kОИСТРУХТОР подхпасса автоматически вызывает


11 хонструхтор баsовоrо xnасса, задаИИiIЙ по умоnчаllИJD.
public Manager(string fullName, int age, int empID,
float currPay, string ssn, ulong numbOfOpts)

1/ Это наш зnенент данlQ1X.


numberOfOptions = oumbOfOpts;
11 Испоnьзоваиие чnеио., Hacпeдyeнwx от Employee,
1/ ДnЯ' устаиовхи даllJWX СОСТОR'ИИR'.
ID empID;
=
Аче = age;
Name = fullName;
SocialSecurityNurnber ssn;
Рау = currPay;

Строго говоря, это допустимый. но не оптимальный вариант. В С#. если вы не


укажете иное, конструктор базового класса, заданный no умолчанию. вызывает­
ся автоматически до вьшолнения логики тобого пользовательского конструктора
Manager. После этого те.кущая реализ8.ЦИЛ получает доcryп к множеству открытых
свойств базового масса Ешрlоуее. чтобы задать его состояние. Поэтому здесь при
создании производного объекта вы на самом деле "убиваете семь зайцев" (пять на­
следуемых свойств и два вызова конструктора)!
Чтобы оптимизировать создание производного масса. вы должны реализовать
свои конструкторы подмассов так, чтобы явно вызвался подходящий пользова­
тельский конструктор базового класса. а не конструктор. заданный по умолчанию.
Таким образом можно уменьшить число вызовов инициализации наследУемых чле-
r
I
I
Глава 4. I!JblK С# 2.а и об13S1Пно-о.риентираван:ныЙ подход 22.5
НОВ ("чт{) экономит ВрtШН).позвольте для атосо модифицировать ПQльзоватеЛЪСRИЙ
ROВCTPyкtop·

JI На 3'1'0fJi' р4а И"саольsуеи lUDQч~.о. c:.noao С. ''Ъаsе Р ДЛЯ JlIotЗО8а


/ / ПOJ1~о:!!а~е.nьс·хarо ~ОIfСТРУЕтора с: базоlJЫИ ltJIac"COlll.
!iJ,11b-li~ МаТ1эgеr Istxing fliJ.1Naroe, int age, irltе;mрГD.,
f}6at currPay. Strihg $sn T ul(;)',tg nцmhOfOpts)

I
I
; base (fu1J.Nam.e , а.!;е, 8DpID, cu:rrPay r ssn)

numberOfOp1:i О!1В = j"lшnbОfОp:tз;

Здесь tюнс'Рр}"НТО.р был д,)полнен ДОВОЛЫiО запутанными элементами синтак­


сиса. нcrюсредст.венно· после закрывающей скобии списка apryмel-l1'OB конструк­
тора стоит двоеточие, за которым следУет КiIЮчtШое слово С# base. В этой ситуа­
ции .вы явНо вызываете K6H~тpyRTOP (J шпъю аргументами. опр~д.елеНnЫЙ I\ЛассоМ"
Emplоуее •. .избm!ллЯсь от неНУЖНЫХ .ВЫЗОJЮВ в процессе создания до'-!ернеro R.J1acca.
Конструктор Sales'Per sол выг.Jl'ядИ't почти идентиtu-JO.

/IКа. пра!UШQ , ~ nO~XJJaoc .дcmже.н Ц~O JlLt.UI8&\11Ь


/1DОД:ХО.цfIЩИЙ 1ЮКС'1'рУК'1'ор. бitЭОIIОt'о xnаС:са.
pciDlic Sales?erson (5t·ring fullN:ame, i'!1t ач€( ivt empID,
floa·t. curr·Pay, з ·trL'lcg ВБН, iz, t rlо·nШОfSаlесsJ
: Ьаве (fu1!Name , &q&, eJl!PrD, cu:rrPay, ввп)

rJ\Jmt!e·i 'OfS'Iles = n1.in1lJOfSal еЗ.·;


j

Вы должны также энаl'Ь о ТОМ. что ключевое слово Ь ~ 53 е можно ИСnОДЬЗ0ватЬ


Boerдa, когда подН,лассу нужно обеспеЧИ'Т'ь доступ к oтt<pЫTЫM или эащищенвым
членам, определённъгм РОДИТедЪсImМ классом. И:CnОЛhВQваниезтого КJIючевОгО сло­
ва не отраничено ло'rйКoЙ ItOНCTpyктopa. Внanreм следyIOШ€1\II обсуащещщ подимор­
фйЗма ВЫ увидите npимеры, 11 которых тоже ИCIIользуетс.н клюqооое СЛОВQ .!:>з.sе.

Множественные базовые классы


Ibворя () базовЬiX классах. важно Н.е эабhffiать. что II С# каждый KjIa('~ ,щ:щже~
иметь в moчн.ocmu oдuн. неПОС'редс.твенный базовый ЮДlсс. illRИМ образом. неШ!зJ'1
иметь ТИП с двумя или болыIIJiIм Ч1iС110мбазовых КЛаССОв (эта Щi3ъmaется множе­
СТБеНJ:IbIМ наследованием). Вы узидите в ша.ве 7, ЧТО 11 С# дюбому пщ:у ПО3ВОЩiе:rcн
ИМе1;Ъ любое число ди:с::кретныxн~ерфейсов•. Таким образо:t.i масс в G'it может P~1-
.лизовыватъ различные ваРЙа1{l'Ы поведения. избегая npоБJJем. uрисущих Imассиче~
скому rroЦХОдУ. связанному с множеC't:ВеlffiЪ!М »зсдеДОВa.fЩем,. АналОТИЧlIО можно
сконфшурировать обычный интерфейс. жан ПРОИЗВQДНЫЙ ОТ МНQжественных ин­
терфейсов [tм. rлаву 7}.

Хранение семейных тайн: ключевое слово protec.ted


Вы уже знаете. ч''1'о открытые элемеl:lТы tIеnосредСТВe.I-mо доступны oтoncJoцy, а
приваТЮ;Iе элементы недоtтупны ДЛЯ объектов вне клас.са. определяющего ~ТИ эле­
:Мe1i1Ы. ЯЗhIR С#. ааннмщо!ЦИЙ лндирующие {IО3ИЦИИ ер~ди м.\:IОГfЩ других совре­
мeIOu.uc объектных языков, обе.спечивает еще один УРОВeRЬл-ос:ryuа: аа.ЩИЩе,в:нь.Щ.
226 Часть 11. Язык программироваНИR С#

Если базовый класс определяет защищенные данные или зaщuщенные чле­


ны, он может создать набор элементов. которые будут непосредственно доступ­
ны ДЛЯ любото дочернего класса. Например. чтобы позволить дочерним классам
SalesPerson и Manager непосредственный доступ к сектору данцых, определенно­
му классом Employee, обновите оригинальное определение класса Employee таи,
как показано ниже.

/ / З~НlWе даНlWе СОСТОИНИЯ:.


public class Employee
{
1/ Дочерние XJlaccw могут иметь непосреДСТJlенный доступ
11 k этой информации I а полъзоваТeJIИ об'И!хта - нет.
protected string fullName;
protected int empID;
protected float currPay;
protected string empSSN;
protected int empAge;

Удобство определения защищенных членов в базовом классе заключается в том,


что производные типы теперь могут иметь доступ к данным не только через от­

крытые методы и свойства. Недостатком. конечно, является то, что при прямом до­
ступе производного типа к внутрешшм данным родителя вполне вероятно случай­
ное нарушение существующих правил, установлеlпIых в рамках общедоступных
свойств (например, превышение допустимого числа страниц ДЛЯ Цмини-романа") .
Создавая защищеЮ-lЫе члены, вы задаете определенный уровень доверия между
родительским и дочерним классом, поскольку компилятор не сможет обнаружить
нарушения правил, предусмотренных вами для данного типа.

Наионец, следует IIОНИМать, что с точки зрения пользователя объекта защищен­


ные данные воспринимаются, I'о:аи приватные (поскольку пользователь находится
Цза пределами семейства"). Таи. следующий вариант программного кода некоррек­
тен .

static void Main(string[] args)


{
/ / Omибха! ЗащищеНlWe данные недоступlW на уро:ане эхземпnира.
Employee еmр = new Employee();
emp.ешрSSN = "111-11-1111";

Запрет наследования: изолированные классы


Создавая отношения базовых классов и подклассов, вы получаете возможность
использовать поведение существующих типов. Но что делать, когда нужно опреде­
лить класс, не позволяющий получение подклассов? Например, предположим, что
в наше пространство имен добавлен еще один класс, расширяюIЦИЙ уже существу­
ющий тип SalesPerson. На рис. 4.8 показана соответствующая схема.
Глава 4. Я3ЬJК С# 2.0 и оБЪВКТНО-.ОРИe-l-J'пtррваIiIiЫЙ ПОДХОД 227

Рис. 4.8. Расширенная ~~раРХИR служatЩ1Х

Кла~с РТ'SаlеsРееsол JIВляетсд нлассом. npедспшляющим продавца. работаю­


Щm'О на неполную ста:в:ку, и преДПО./JQЖИМ. I-lЗfiример. что щ.r xoт:a:re, чтобы нmщ­
'Кой другой разработ~пш не мог СОздавать цqдклаDСЫ ив PT'Sa.16sP,~roson. (В нщще
RШЩОВ, BaкyIO еще непоmryю ,стщшу NЮЖНО ПQДУЧИТЬ на основе неU0JIНОЙ ставни?)
qтобы не допустить возможностц раuширения !Ща.СС~. используйте ключевое сдов()
С# BE!aled.
1/ Кnacc PТSale~Person не 'скажет бlit'.1'Ь lSазоэш« .rиr&Ccoм.
public sealedclas;s РТЭi31е$РеrSоn ; Sal-е5,J?еt:sоn
[
public РТSаl'е"sРе:rSОn(5triлg fiJIJName, int. age, ifJt en1pJD,
fl,oat C1Jr.rPay, stri!Jg В'БЛ, int. nuшЬОfSаl€l;;)
: b,ase (Еиl lName , .аче, ешрlО, C1JT.J:Pa,y, 8511, mlJnbGf.S'..ales)

11 ЛOГИJt& ЖОВС1l!pУ1Wора _ ••
1
IIДругие чnеЮI, ••
}

ПОСКОЛЪh-У Iша:сс РТ8аlеSР~.tэоn ИЗОЯI-iрован, он не может слуЖить базовым


1tJ13CCOM iПllia1tому другому типу. ПОЭТf)МУ при попытке расширить PT1ia.le6I"er S,QЛ
:вы получите оообщение 06 ошибке IЮМI:rи.ляЦМИ.

/I ОшИб~а ХОМЦJШ5ЩИЙ!
1"!1ЬЬс оlаэs R,eal1yPTSale,sPeTS()l1 : РТSа.1еsРеж:sоn
! ... 1
Наибооее полезным ШlЮчевое СЛОВО seaied ОItaЗЫВаетсн при СОЭДЭI-IIllI авТОНОМ­
НЫХЮIассов утWJИТ. Мас.С Stri.ng. оцределенный в пространстве имен Sу'зtern, на­
цриме~ явно изолирован.

Р1;1Ы i o~ea.Ied сl as'S s,t r ing ~ obj ect,


JСоmр.ат'ЗI}lе, ТСlопеаЬ'lе,
Iconvertiыl,1 IЕt,J:,Jrпеrаi:НЕ {, •. }

Позто'\'Ау 'Вы не сможете со:щать ЦО8ЫЙ ЮJaСС, nPОИЗВйДНЫЙ от 'S Y$tem, 5 t r ing:
/I Снова CШПdбiCа!
public clas5NySt:ring : str.1.t\(j
228 Часть 11. Язык программирования С#

Если вы хотите создать новый класс. использующий функциональные возмож­


ности изолированного Кllacca. единственным вариантом будет отказ от классиче­
ского наследования и использование модели локализации/делегирования (извест­
ной еще как отношение локализации. "has-a").

Модель локализации/делегирования
кm< уже отмечалось в этой главе, наследование можно реализовать двумя спосо­
бамц. Только что мы исследовали классическое отношение подчиненности ("is-a").
Чтобы завершить обсуждение второго принципа ООП. давайте рассмотрим отно­
шение локализации (отношение "has-a". также известное под названием модели
ЛОКQJluзации/делегиРО8Ш-ШЯ). Предположим. что мы создали новый класс, модели­
рующий пакет льгот работника.

/1 ЭТО'J! 'J!ИП будет фуКJщиониро:вать, Itах вложеИНJolЙ класс.


public class BenefitPackage
{
1/ Другие члены, npеДСТaJSЛЯUЦИ8 пахе'J! C'1'paxo_oJC,
1/ иедицинсхого оБСлу*изаниR и Т.д.
public double ComputePayDeduction()
( return 125.0; )

Ясно. что отношение подчиненности ("is-a") между типами BenefitPackage (па­


кет льгот) и Employee (работник) выглядело бы достаточно странно. (Является ли
менеджер пакетом льгот? Вряд ли.) Но должно быть ясно и то. что какая-то связь
между этими типами необходима. Короче, вы должны выразить ту идею. что каж·
дый работник имеет ("Ьаэ-а") пакет льгот. для этого определение класса Бmрlоуее
следует обновить так, как ПОRaЗано ниже.

11 Работиихи теперь ИМeJO'1' льго'l'Ы.


public class Employee
(

11 Содержит объехт BenefitPackage.


protected Benefi tPackage empBenefi ts new BenefitPackage();

Здесь вы успешно создали вложенный объект. Но чтобы открыть функциональ­


RЫe возможности вложенного объекта внешнему миру. требуется делегирование.
Делегирование означает добавление в класс-контейнер таких членов, которые бу­
дут использовать функциональные возможности содержащегося в классе объекта.
Например, можно изменить класс Employee так, чтобы он открывал содержащий­
ся в нем объект empBenefits с помощью некоторого свойства. а ТaIORе позволял
использовать функциональные возможности этого объекта с помощью нового ме­
тода GetBenef i tCos t () .
public class Employee
{
protected BenefitPackage empBenefits = new BenefitPackage();

/1 ОтltpЫ'1'ие нехоторых фунхциональнwx возможностей объехта.


ГЛ'эва 4. ЯаыкС# 2.0 и Dбы!ктно-ориентированный nодщrt 229
pиbl:l.c: double .GetBenefitCost ()
{
r"t-щ::nеmpБеnеf i t.s • Compbl·te l'ayDed"<l'c ti оп О .;

11 Дo~ JC o&aeXl1'}' черes D~!!SoaaTem.CJ[ce а-ойстио.


р1.1ЬНС HE)l'IefitPackage BeHefit:.s
{
get I return eropBe·n ef i ts I }
set{ ~pBerrefi tJS = vallle; )

в след:ующем оБНОВJIен;но~ меТQде Mair ! () обратите внимаНИе нато, -как МQЖRО


взаимодействоваIP с bhy:rPf;\i-mим1ШПdМ ВелеfitSРасkаg*. одреде-ляе~м типом
Employee:•
.зtаtiс void Main (striTig [] a-r'gs)
{
Manl'i .g~r ше 1 ;
тel = new Manager{);
Console.WriteLine (mеl.Ве1щfitа.СDЮРlltеРауDеd:uttiоn(});

СОлs с lе. ReadLine () ;

Влож.енные опредеllеНИ5J типов


Перед тем как рассмотреть за:Ключителъ1iЫЙ прgНЦЩl ООП (пол:щ\'юрфИЗм). да­
Bai1:TC обсудnм текнин;у uрmра:ммироваtIйя. называемую в./IDжекue.м. IТшпов. В С#
МОЖ1IО опредеJlIИТЪ тИIl (переч:енъ . :клаСС. ИJ:-trерфеЙс. cтpyrcrypy или делегат) в пре­
делах области ВE,ДJIМости масса шги c1'p~Ы. при этом _B;1l~ij ("в:нутре:н­
ний l ') тип считается члеfWмсодер~его его ("BH!m1J-lеJ'Q~) класса. с точ:ки зрения
среды ВЪПIолнеНИ\И f::I.Ичем не о:шич:ающи..'ItIСЯ от ЮDбого другоro <JJJенв [полн. СВОИ­
crn<i. метода, события и т.п-). Сю-rtаIССИС. йсnользуемый ДЛЯ вложения типа. Яс­
ключцтелъно пр ост.

pUhli'C c~as.s Out:erCla·ss

11 0'1'~ JIЛO*еНИIIIЙ тип MOIIy.r .НCnЬДЬЗОВ&!t'Ь все.


pl>lb::L ic сlав.э PU1:ili~JnnerCl qSS {}

/I Прква'1'lUoDё .а.пО!Се~ '.IIИИ JlОГУ'11 нcnom.зОВ4'l'Ь To.nъxO ueИbl


1/ ·содержащего ero 1G1i1.CilCёI..
pri~ate сlэ.ss РrivаtеIл.n.еrСlазs {}

Си;нтаl{(~ИС3Дес:ь щ~ен, но rrm:tять. почему вы мотете ЭТО ДeJJать, не так просто.


Чтdбы пpЮim к ПОlЩмнниюзтого подхода. ПОдУМайте lЩД следующим .

• В1IоженnеТ1:ШОВ подобно их RОМПОаицпи ("h.<if:\-a"). за ИСlщючеJ:lllем того. что


вю имеете ~оJщый K-QНТрОЛЬ ИМ доcrynОМ :на уровне внутреннего mщю.. а не
оодержarцеroся оБЪeJ{Цl.

J
230 Часть 11. Язык программирования С#

• Ввиду того, что вложенный тип является членом класса-контейнера. этот тип
может иметь доступ к приватным членам данного класса .
• Часто вложенный тип играет роль вспомогательного элемента для класса­
контейнера, и его использование "внешним миром" не предполагается.

Когда некоторый тип содержит другой тип класса, он может создавать члены­
переменные вложецного типа, точно так же, как для любого другого своего эле­
мента данных. Но если вы пытаетесь использовать вложенный тип из-за границ
внешнего типа, вы ДОЛЖНЫУТО"IНИIЬ область видимости вложенного типа, указав
его абсолютное имя. изучите следующий пример программного кода.

static void Main(string[] args)


(
/I Соэдание и ИСПО'nЬЗО8аиие oTxpы'l'гoo внутреннего класса. Все ОК!
OuterClass.PubliclnnerC1ass inner;
inner = new OuterClass,PubliclnnerC1ass();
/ / Ошибха хомnиnRЦИИ! Нет доступа х npиватноиу классу.
OuterC1ass.PrivatelnnerC1ass inner2;
inner2 = new OuterC1aSS.PrivatelnnerC1ass();

Чтобы использовать этот подход в нашем примере, предположим. что мы ВЯО-


жили Benefi tPackage непосредственно в тип класса Employee.
/ / Вnо*еиие Bene€itPackage.
public clas,s Employee
{

public class BenefitPackage


(
public double ComputePayDeduction()
( return 125. О; )

[Дубина вложения может быть любой. Предположим, что мы хотим создать


перечень BenefitPackageLevel. }'Rазывающий различные уровни льгот. которые
может выбрать работник. Чтобы программно реализовать связь между Employee.
BenefitPackage и BenefitpackageLevel. можно вложить перечень так. как ПОRa­
зано ниже.

// Ешрl0уее содержит Bene€itPackage.


public class Employee
{
// BenefitPackage содержит BenefitPackageLevel.
public class BenefitPackage
{
public double ComputePayDeduction()
{ return 125.0; }
public епит BenefitPackageLevel
(
Глава 4. Я~ЫК с# 2.0 и объеI<Тf\Q ,ориеНТL'tРОВilННtJlЙ nЬдход 231
StandQrd, Gold, Platinum

с учетом ОТНQшеций 6ло~кеШUI обраrrите ВНИМ'ш-ше на ТО. R<Щ ПРИХОДJ:IТСЯ ие:­


пo.m.эоватъ ЭТОТ перече;щ) .
.,,·t a.ti с voi d Мai.n (str iпg [J args)
[
1/ Созд~е перемениаИ BenefitPackageLevel.
'Ещрl оу:ее. :i3E i1зfi tРtJ,.сk;;э.gе .Вen€ fi t.Pack,sgeLev.el
rnyBenefittevel =
Ernployee. ВеПеfi tPac·l'i.GJge .Веnе f i t P.aokageLevel . Plat::ir1JJl'Il1.:

Третий принцип: поддержка полиморфизма вС#


Теперь давайте р~сс:.мотрим :ааwпочи.елъныЙ прm-щЩJ ООN - полиморфИЗМ.

1. НапЬМН:ИМ, чтоба:зQвый власе Ешрlоуее Qпреftt".!!Ил мето.д Gi v~Bonus ~ 1. Ш>ТQРЫЙ


был рeaJlИЗ0Ван: так
j
/I ПредоС'1'aJ!.пение пре1fИЙ раБО'nIМ'И:a.N.
pub1ic c1a.ss· Employ~
{

public void Gi-~еВОnU'6 (float 8ШQuпt)


I currPay += . ашоuп ·t. }

Ввиду того, qт,o ЭТОТ метод ЛВ;1lЯетс.н ОТ1фЬПbl.М. -ВЫ теперь ~eeтe В@ЭМЩКНОСТЬ
выдmъ премии процавцам и менемкерам (UTaJ.bRe ttpОДaJЩам. занm:ым яеnoлный
рабочий день).

з· tаti с 'vюi d Main (s·t:r ing [] args)


{
IJ Времни р"БО'1'IШUII.
Ma.nag·~r Chl.1Cky = o.ew Manager (Л· СЬ-цсkу". $0, 92,
rOQOaO~ ~З33-2З-2З22"r 9000)/
ch'C1C'·kj' . GiveВon~s (3'00) ;
chucky.Display'Stats ();

SаlезРеr'i-Оn f.ra:h = !1.eмr ~аlеs.Реrз(')n (" Frал", 43, 93,


ЗQОО, "932-32-3232",3.1);
fr'811 .Givea·o nus (2"Q{J);
fran . Di spJ..a·y'Stats () ;
СQ!1зоlе. R$B'cI!.ine (1:
}

НедостатОI! д;alШQГО варианта:в ТО,М , ЧТО наследуемый метод GiveBonus () дей­


т:т:вует ОДИliaItQ'1Ю ДЛЯ всех t!oДfUIaCC(l)Ii!, В IЩi'але :премия продавца. Ra~ и. продюща,
рабdтающеr.о на неполную ставку. дол;щна заБш;t}ть от числа uролаж. Возможно.
232 Часть 11, Язык программирования С#

менеджеры должны получать льготы в дополнение к денежному вознаграждению.

В связи с этим возникает интереСНЫЙ вопрос: ~Каким образом родственные объек­


ты MOryт по-разному реагировать на одинаковые запросы?~

КлючеВblе слова virtual и override


Полиморфизм обеспечивает подклассам возможность задать собственную реа­
лизацию методов, определенных базовым классом. Чтобы соответствующим обра­
зом изменить HaIlI проект, мы должны рассмотреть применение ключевых слов С#
virtual и override. Если в базовом классе нужно определить метод, допускающий
переопределение подклассом, то этот метод должен быть виртуальным.

public class Employee


(
/1 GiveBonus () икеет реа.пиsаЦИJD, за.цаииY1D по умonчaиИJO,
1/ но дочерние lU1accw MO:roYT переоnpедenитъ это поэедение.
public v~rtual void G1veBonus(float amount)
{ currPay += amount; }

Чтобы в подклассе переопределить виртуальный метод, используется ключе­


вое слово override, Например. SalesPerson и Manager могут переопределить
GiveBonus () так, :как показано ниже (мы предполагаем, что PTSalesPerson пере­
определяет GiveBonus () примерно так же, как Sa1esPerson) .

p'ublic c1ass SalesPerson : Empl oyee


(
11 ПреИИJl продавца зааисИ'1' 0'3.' числа продаж.
public override void GiveBonus(float amount)
(
int sa1esBonus = О;
if(numberOfSales >= О && numberOfSales <= 100)
sa1esBonus = 1 0 ;
е1зе if(п"t..lmЬеrОfSа1еs >= 101 && nшnbеrОfSаlеs <= 200)
salesBoJ1us 15 ;
else
saJ.esBonu5 20; /1 Все 1 что Isonыlle 200.
Ьазе.GivеВопus (amount * sa1esBonus);

pub1ic class Mal1ager : ЕIпрlоуее


{
// Менеджер в дополнение 1( дене_оку :вознarpаждеНИJO
// получает некоторое ЧИCJIо оnционов.
pl\blic override void GiveBonus (float amount)
{
// Прибавка к зарплате.
base.GiveBonus(amount) ;

.
Глав'84. Язык С# 2.0 и оDъ,еК1tIO-ОРliент.ирмаiНiЬJЙ П(IJ.дхоД 2ЗЗ

11 и пол~еиие Onцl'<!OK08 •••


RandO'l11 I = new Ratld'orn О J
number:Q'fOpticms += (lL11ong) r.N.e'Xt (5.00):

Обратите BH}ima1-1ие на ТО, ЧТо перt:определ:епный ме'ТОд будет 1oJСПОЛЬЗ0вать


поведение. np1ifштое по уМолчанию, eCДfl указать RJIЮчевое СЛОБО 09',Б€. П'Jщ этом
e~ необходимости адова реалиаО;$ЫВатъ догику i;i,,\7e~onus (,), а МОЖJjО СНОва 'Ис­
пользовать (и. возможно, расширить) поведе'ине родитеJI.Ы:.;кого К1Iacca, ПрИНЯТ0С
по умолчанию.

Таюке пре.даоложцм, что Em'plo:yeE\.DisplaySt~ts () БWJ 06~eH виpryaльRO


и переопредмен J,аждым IIОдWIЩ::Сом. чтобы уч~стъ , ~c~o llp9даж (ддя, np()давцон}
и текущее соеТОSIIЩе ОПЦИОНОВ (дщr менеджероц). Теперь, :!{QJ'дa кaж;цьrй ПIЩI:QIасс
MO~eт ПО"С:Qое:му интерпретировать виртуальные MtтOды . каждыиэкаемшшр оБЪeR­
та )Jедет t:еб.я: более Ее3ависимо.

r>ta tic voi,d Маin (5 t.r:J..ng LJ а гgs.)


I
11 Дy-~ смс!tfeИЗ жw~anъJOlX!
МалаJзеr:
chucky = n.ew Manager("Chucky", 5'0, 92,
1: 00600, "333-23-2 322". 9-000):
сhщ:kу. GiveBonus (3 ао) ;
ehиcky. DisplayStats (') ;
SаlеsРеrзCln fran = ae,w SalesPers('Jn (,"Fr'qЛ 11, АЗ. ЧЗ,
ЗОО 'О, <'9з.2~3Z- ;JLЗ2 ", 31},-
f:tar,. GivеВonuз (200) :
frar,. DiSip1aySt,ats. () ;
}

Снова о lO1ючевом олове sealed


Кmbчевое слово seale.d может также примеНЯТЬСil к членам типа. чтобы запре­
тить пере('>пределение:rаких виртуa1Iвных Ч.iIенов в npоизводliых тиnax. это окаэъх­
вается ПОЛ~ЗНЫМ тотда. KOrдa нужно изолировать не весь ltJlа~с. а только несlюдЬКО
его ме:tодов МlШ СВОЙСТВ.
Например. если (по неко1'ОРО'Й причине] классу PTSal е sPers otJ требуется раз­
penш:гь расширение ,tq)УГИМИ Юlассaмlf. НО нужно гарантировать. чтобм ЭТИ клас­
CВl не ,МО2lШшреопределять виртуа:лыный метод G:iv.eBQitl1JS (). можно использовать
следУЮЩИЙ вариант npограммного кода.

/ / Э'1i~ JtJ1ЗСС' NO?IUfO pac1lO!prttъ,


11 ио GiveВоnuз () И8 НО"'1' переопр.е;цеn:.ть,C1II произар,циJiU( lt.Пасс~.
pl:lbli.c o lass .Р1'Sаlеs.Рез::Sоп : Sal'e sPer:s orr
{

рuыic avertide sealed. void Gi \'еВсщu,t' (float ,arnСФТJt)


{

.1
234 Часть 11. ЯЗЫК программирования С#

Абстрактные классы
В данный момент базовый класс Employee скомпонован так, что он может по­
ставлять своим потомкам защищенные члены-перемеЮlые, а также два вирryаль­

ных метода (GiveBonus () и DisplayStats ()), которые MOryт переопределяться про­


извоДНЫМ классом. Все это хорошо, но данный вариант программного .кода имеет
один недостаток: вы можете непосредствеюю создавать экземпляры базового клас­
са Employee.
11 Что же это Знач~?
Employee Х = new Employee();
В данном примере единственной целью базового класса Employee является
определеЮ1:е общих полей и членов для всех подклассов. Вероятно, вы не предпо­
лагали, что кто-то будет непосредственно создавать экземпляры класса, поскольку
тип Employee (работник) является слишком общим. Например, если я приду к вам
и скажу ~Я работаю!", то в ответ я, скорее всего, усльппу вопрос "Кем вы работа­
ете?" (консультантом, инструктором, ассистентом~стратора. редактором,
представителем Белого Дома и т.п.).
Ввиду того, что многие базовые классы оказываются чем-то вроде "небожите­
лей", для нашего примера лучше всего зanpеmwnь возможность непосредственного
создания новых объектов Employee. В С# это можно сделать программными сред­
ствами, используя ключевое слово abstract.

11 Обозначенnе класса Етрl0уее, как абстрактного,


11 запрещает непосредственное создание его экземnляро:в.
aЬstract public class Employee
{ ... }

Если вы теперь попытаетесь создать экземшmр класса Employee, то получите


ошибку компиляции.

11 Ошибха! Нельзя создать экземпляр абстрактного класса.


Employee Х = new Employee();
Превосходно! К этому MOMeHry мы построили очень интересную иерархию слу­
жащих. Мы добавим новые функциональные возможности в это приложение не­
много позже. когда будем рассматривать правила классификации в С#. Иерархия
типов, определенных на данньш момент. показана на рис. 4.9.

Исходный код. Проект Employees размещен в подкаталоге, соответствующем главе 4.

Принудительный полиморфизм: абстрактные методы


Если класс является абстрактным базовым классом, он может определять любое
число абсmракmн.ы'х членов (их аналогами в С++ явдяются "чистые" виртуальные
функции). Абстрактные методы могут использоваться тогда, когда требуется опре­
делить метод без реализации. задЭЮlОЙ по умолчанию. В результате производным
классам придется использовать полиморфизм. поскольку им придется "угочнять" де­
тали абстрактных методов. Здесь сразу же возникает вопрос, зачем это нужно. Чтобы
понять роль абстрактнbIX методов, давайте снова рассмотрим иерархию форм, уже
упоминавшуюся в зтой главе и расширенную так, как показано на рис. 4.10.
Глава 4, Язык С# 2,0 и обьектно-ор~е~,п,!рраанный ПОДХОД 235

..
, ~ ..

~
->JI' NцmbOpls '1! I\Iu!nbsar...
lI\IItf1DCk
If~'"
~ ""'etla>......
Ь
.
lO\ethIdII
~

.. M""aqer '(+ I ОЧ_ . ,


"'~
'" s.ш-р"г"", , (+ 1. . _

Рис, 4.9. Полная иерархия служащИХ

: .т; J):~.",e
'.'1=.: Proportll!l
I 51 1'ot1tlolТ>e
I
kb, ~~$
. ...11,...
... :5С"Р< 1:" l 'Q"<I1 .

1=. мab".>ds
"""'DX....
"о ~""Ф90. ,. , V~

Рмс, 4. 10, ИеР<1РХИЯ форм


236 Часть 11. Язык программирования С#

I{aк и в случае иерархии служащих. лучше запретить пользователю объекта


создавать экземпляры Shape (форма) непосредственно, поскольку соответствую­
щее понятие слишком абстрактно. для этого необходимо определить тип Shape.
как абстрактный класс.

namespace Shapes
l
public abstract c la ss Shape
I
// Форме мо_но назначить понятное ИИR.
protected string petName;

// Конструкторы .
publi c Shape () ( petName = "БезИмени" : )
public Shape[string s) { petName = s;}
/ / Draw () lIиртуanен и может БЫ'l'Ь переоцредеnен.
public virtual vo id Draw()
f
Console.WriteLine("Shape.Draw()"):

publi c string PetName


{
get ret urn petName:}
set petName = value;)

}
// Circle не переоnpедenяет Draw().
p\Jblic c la s·s Ci rc le : Shape
{
public Circle () { 1
publi c Circle(string пате): base(name) { }

// Нехачоп переоnpедеnяет Draw().


public class Hexagon : БЬаре

public Hexagon() I }
public Hexagon(string пате): Ьаsе(паmе) { j
public override void Draw ()
i
Сопsоlе.WritеLiпе("ОТОбражение шестиугольника {О}", p etName);

Обратите внимание на то. что класс Shape опредеJIИЛ виртуальный метод с име­
нем Dr а w (). Вы только что убедились. что подклассы могут переопределять поведе­
ние виртуального метода. используя ключевое слово override (как в случае Itласса
Hexagon). Роль абстрактных методов становится совершенно ясной. если вспом­
нить. что подклассам не обязательно перео:пределять виртуальные методы (как
в случае Circle). Таким образом. если вы создадите экземпляры типов Hexagon
и Ci rcle, то обнаружите. что Hexagon "знает", каК правильно отобразить себя.
Однако Ci rcle в этом случае будет "не на шутку озадачен" (рис. 4.11).
Глава 4, ЯIIЫК С# 2'.0 и объепно-о-риеliтирован"ый n'OAXO,Q 237
fI Об'МIП' Сис1е- не neреоnpe;ЦUflИ рliianиз&ЦИlO Dж:а.w (3 6азовоZ'о маСоа.
static vo.id мain (:stIing[] ,a rgs)
{
!:lexagon hex = пеw Hexagon:( "'Seth'" :
hex..Dra ~ ();

r.irc:le c'ir = n~w CiJ::cle ("Cindy") :


11 M-IoI-х-;ца. испоm.S:felol реamrs&ЦИJO r:lа.аозоro 1IU1a.c.ca.
cir.Dtaw() ;
С~пsqJ;е . R.еаdLiпе () ;

Рис. 4.11. Вир-ryаl1ЫJые методы оореоnpеАелRп. Не обязательно

Яснр, что ЭТО- не идеа.льный: вариант иерархии форм. ЧТОбы заставить каждьШ
nPQИэ.водныЙ ЩЩСС иметь свой собственRh1Й метод Drew (). мощно за,дать Dr:aw () I

как абстракт~ метед класса Sr!ape. т.е Метод. :который вообще не имеет реa.JIй­
ЗIЩJШ,~аданио;й !ТО умолчанию. Заметим, что абстран~е методы могут опреде­
ЛЯТЬСЯ толыш ~ абстрaJtТНI:йX классах. Если вы попытаете(JЬ сделать это Б другом
JtЛaссе, то долУ'~ите ошиб~ .нОМПИЛ.!ЩИИ.

/I SаO'l'авик 'Воех "д:еIJl'ОЖ'" ИН8оп. свое ~~С:lJI'а'Вneние.


public a.b.etraot cla's3 ..5hape
[

II 'l'enер. D~a." () оomtOC'moЮ' аБC'1'pa.JC1J11В1й


II (o6p-аТ:___ нroiааие ~. IJJОЧКУ С 5it.qR'l'ОЙ) •
P\Jbli<; abetr_ct voi>J, Prqw (, ;
. ..~

Учитыва:я это, ВЫ 05язfщыl реали;з!)вать Dгаw () в классе CJ.rcle. Иначе Circle


тожеДОJ1же1-I быть аБСТРaRТftыМ т,ипом, ооозначенным Ю1ючевым СJЮВОМ abstract
'(Ч1'О для данного примера не совс;ем логИЧНо).

/I :В:~ н. 8a,ц&!I!. реа.пиз8ЦИ1О xe!l!oJ\& -D raw (), '1!О zt,Па.сс Circ1:e дomeeв
11 БR'Ro абс~ах'DЮIDI и не дощrсжа'l1Ъ Н~ОСР&ДCIТВ&ИИ3I]D реa.nиsацшо!
рu.ЬНс C'lass Circ le: Shape
~
рubНс Cirr.::le (.) {
publjc Circle (strlng паше): b ase (nюне) I I
II Тепер. Circle ;ЦОmkeН "ПОRИКaIJ1Ь". qJt о~оCSра5~IJ1Ь сеея..
pllblic oVerride 'vpid Draw ,(}
J
CODsole. Wт. iteLlne ("О'тобраЖ~е ()~Р;y':!!iliРСТИ {О )" I pet~a!r!e);
-
238 Часть 11. Язык программировани~ С#

Для иллюстрации упомянутых здесь возможностей полиморфизма рассмотрим


СЛедУЮщий прогрaммНblЙ код.

11 Создание массива различных объехтов Shape.


static void Main(string[] args}
{
Солsоlе.WritеLiпе("***** Забавы с полиморфизмом *****\п");
Shape[] myShapes = (new Hexagon(), new Circle(),
П
new Нехаgоп("Мiсk ), new Circle("Beth"),
r1ew Rexagon("Linda") f;

11 Движение по массиву и отображение об'ЪеХТОВ.


for(int i = О; i < rnyShapes.Length; i++)
myShapes[i] .Draw();
Console.ReadLine();

Соответствующий вывод показан на рис. 4.12.

Рис. 4.12. Забавы с полиморфИЗмом

Здесь метод Ма i n () иллюстрирует полиморфизм в лучшем его проявлении.


Напомним, что при обозначении RЛасса, как абстрактного. вы теряете возмож­
ность непосредствен.ного создания экземтvzяров соответствующего типа. Однако
вы можете хранить ссылки на любой подкласс в абстрактной базовой перемен·
ной. При выполнении итераций по массиву ссылок Shape соответствующий тип
будет определен в среде вьmолнения. После этого будет вызван соответствующий
метод.

Возможность скрывать члены


в С# также обеспечивается логическая противоположность возможности перео­
пределения методов; возможность скрывать члены. Формально говоря, если произ·
водный RЛасс повторно объявляет член, идентичный унаследованному от базового
RЛасса. полученный класс скрывает (или затеняет) соответствующий член роди·
TeJIЪCKOГO класса. На практике эта возможность оказывается наиболее полеэной
тогда. когда приходится создавать UОДRЛассы, созданные другими разработчиками
(например. при использовании купленного пакета программ .NEТ).
Для ИЮIЮстрации предположим. что от своего коллеги (или одноклассника) вы
получили класс ThreeDCircle. который получается из Systern.Object.
public class TI1reeDCircle
[
Глава 4. Slзык С# 2.0 и ебъектно,.ориеН'ТИРDванныЙ ПОДJЩД 239
pUbliC: :\ТоИ Dr:aw()
1

ВЫIIолarаете. ЧТQ Tb.reeDCircl~ QТНосит~ (''1s-а''):к тлпу Circle. поэтому пыта­


.етесъ ПОJJj"ПЛ'Ь ПРОИЗВОДl1Ъ1ЙICJlaСС из сущеСТВУЮЩеГО типа Circl€'.
pubHc class T:hreeDCircle : CJ.:rcle
[
риы1 ic v Old Draw ()
\
Co-n s.vle .:Wr"i t-еLinе !"0тоБР<'l-жеНJ/lе трехмернqй окру...t,iЮСТИ") ;
1

в npрц~ссе КОМI'IИЛ.ЯЦПИ в Visnal Studfo 2005 вы увидите предупреждение, поIta-


3а1ц-roе на рдс . 4.13.
(' Sjщреs.ТnrееDСirсlе.пrаw·()· СНРЫваеТ наследуемыIй член
·Sbapes.Cir,z:le.Dr&-w() ' . 'Чтобы пе.реопределить соответст~ующу:ю реализацию
Д81ЦЦ'Щ\1I "ЧЛеRQМ, ИСПQльзуИ-ге ItлЮчеtюе слово overrid'~ jJн~че испольЗуЙте lш,fO­
чевэе (ЩОВО nf;':w.)

-яIe _~ ,, _.. ...:~ .


:sh~: Тf:JrиeDdr!~. DrllwO-;h/des JnI1etited 5h*<,r~ БО 20
member:'Sh4P6,.c'~<J~.I:i".wO'.. То rmo .... th.
~ tмmber Dve,iide t~ ~,pIe,,,.,.-;tatll>l\;
4CId. ~ """'rii:t.e
щ.....~. Oth",w;s;o add Ihe
","wkщward.

Рис.4АЗ. ой! 'J'hreeDCi-rсlе,Drаlдl () сiФ-Ывает Сirс:lеД1 rаw

ECT~ два варианта решения э-той nробле--МЬL Можно ПРОСТQ изменить :версию
Dra w () Р(.lДwгелН. используя ключевое CJ]OJilO оуе r r 1 de. При та:ком подходе тип
Tr,ree'DCi:rcle :может расш.ир:И"ть возможност.и поведедJ1Я родителя т3к; .как тре­
Руется.
Nщ.rернативой. может быть ИQПDЛЬЗО'ЩЦiие юrюче.вого слова fiew (с членом
Draw () типа Tb.ree-DСirсlе). Э7'О · а:вноеУК~ЭaI-IJ-Jе того. 'N'O ре;uиаацияпроиэвсщна­
го типа должна. cнpъmaтъ neрсnю РОДИтemt (это MO~ 1;IОJ-щдобwtьсн тшда. когда
по.лученные извне программы .NE'Г не. соrnасую:гся с пр.ограммами. уже имеЮIЦИ­
мuтя У вас).

/I Эorот масс раcrщиpяе!l! Circle JIf cltpSIВaВ'1.' _c:.nе.цуею;/Й: ИВ'1'ОД D:r;aw О .


рирliс; сJазs ThreenCircle ! Circ-le
1
/I С1':pN'1!Ь moбую ЗИ811DЩD реam.rsаЦИID D.ra1f О .
Р\,1ЬИс па.., void Draw'(J
{
Сопs:аlе. Wr i tE.birle ("ОТ(')DРа;!(ение ~рехмеfiНОЙ о k"p-уЖftоС;ТИ:" ) ;

}
r
240 Часть 11. Язык программирования С#

Вы можете использовать ключевое слово new с любыми членами, унаследован­


ными от баЗОБОГО класса (с ПОJIJiМИ, КОf{стантами, статическими членами, свой­
ствами и т.д.). Например . предположим, что ThreeDCircle должен скрыть насле­
дуемое поле petName.
public class ThreeDCirc1e : Circ1e
!
new protected string petName;
new public void Draw ()
(
Сопзо1е. Wri teLine ("Отображение трехмерной окружности ") ;

Наконец, следует энать о том, что воэможность испольэовать реализацию ба­


зового класса для скрытого члена тоже не исключается. но в этом случае следует

использовать явное приведение типов (см. следующий раздел). Например:

static void Main(string[] args)


(
ThreeDCirc1e о = new ThreeDCircle() ;
o.Draw(); 11 IWзааааеТСII 'l'hreeDCircle. Draw ()
( (Circ1e) о) . Draw () i 11 ВWaaaaaeTclI Circle.Draw()

ИСХОДНЫЙ КОД. Иерархия Shapes размещается в подкаталоге, соответствующем главе 4.

Правила приведения типов в С#


Пришло время иэучить правила выполнения операций npиведенuя munoв в С#.
Вспомните иерархию Emp l oyee s и тот факт, что наивысшим массом в системе
является System.Object. Поэтому все в вашей программе является объеl\Тами и
может рассматриваться, как объекты. С учетом этого вполне допустимо сохранять
экземпляры любого типа в объектных переменных.

11 Мanager ~ это System.Object.


object frank = new Manager("Frank Zappa", 9, 40000, "111-11-1111", 5);

В системе Еmр1оуеез типы Manager, Sa1esPerson и PTSa1esPer son расширяют


Employee . поэтому можно запомнить любой иэ этих объектов в подходящей ссwше
баэового класса. Так что допустимыми будут и следующие операторы1.

11 Мanag8r - это
Employee.
Еmр1о уее mо опUпi t: = new Manager ("MoonUni t Zappa",
2 , 20000, "1 01-11 -1321", 1);
/1 P'l'SalesPerson - это Sa.lesPerson.
SalesPerson jill = new PTSalesPerson("Ji11",
834, 1 00000 , "111-1 2 -111 9 ", 90);

1
- - - - - - -----------------

Глава 4. Язык С# 2.0 ~ ОбъехТНО-QриеНТf!IJ'О6анный подход 241


Первl!lМ дравилом прео6ра:зованин для типов MaCC(j}B RВ.JIЯетс.я То. "ЧТО котда два
класса свяэа:вы отношением nОД"ЧШIffiIНОсти ("il!l-a~l, Bce~! МОЖНо СQХраии:ть про­
ИЗВ6дrшй тип в ccы;)1Еe- баэовото класса. Формально это называют неявнbtМ {fC@н,­
тneк:cтyanъньu.1}' тtpu.вeдeн:иe.м тшwв, ПЬCRольRy оно ·работает" ТОЛЪRО в рамках за­
ЙОНОв наСЛIЩовави.и.

ТакОЙ подход поэволнет строить очень мощные npограммные :КОНСТРУ:КЦИИ.


lIредuoложим. нanpимер, что у нас есть юrn:сс I.rLeMachin.e (мamинal, который под­
держивает с..'Юдyro!ЦИЙ статичесКИЙ мe:rод. соответствующийуволънeннIO раб\J'I'НИН.a.

pub·lic сlаЭ5 The!Y1<'1c.rjine


r
p.ubJ.i с "ta ti с VQid FireThisPe'I:sQn (E1IIplayee е)
(
// У;цanи!1!Ъ на бнlot даи!АТХ •• _

// Забра~_урабо~а~и ~очкnху ...


J

Мы можем непосредственно. передать этому Meтo,ц,v любой nPОИЗВQДНЫЙкласс


Масса Етрlауе.е, вви,цу :ГОТа, что эти юraСChI связаны О'l'НошенИемподчиненностй
("16-а"].

j I CQXptuцeнue 1II!t'ашо••
TheMachine. FirеТhisРеrЕЮП (mcюпUг.:i:t); // "mo01JUnj,.t." - Э~О !щрlоуе~.
TheMael1il'l.e. Fi reThiBPerS,O!1 (.j il1) ; 1/ "jill" - э~о Sal8sPerson.
в дальнейшем програММ1-IЫЙ код йсполъзует :в ПРОИЗВОДНом '1'ИUе неявное npe-
образовшmе и:а базового дласса (Employee). Но что дела'lЪ. если вы хотите уволить
служащего по имени Frапk Zappa (информация а котором Б настоroций момент
хранится в ССМЛltе System.Object общего ВЩJ;в,)? ЕсJШ передать объект f.rank н:е­
ПосредствейНQ в TheMachine.FireThis'Person 1:) так, как ПОка3аН/) :ЮiЖe:
/1 Manaqer - 9'1'0 оЬj·ебt, во. ..
оЬ] ect fXiiJ,nk = new MaDager ( .. FriiШk Z:appa" ,
:3 , 40 О О О, '1 111- 11-1111 ". 5);

T]-lе!VJdсhirlе _Fire1'hisPe.r50n (frankJ; /1 Ошибж.а!

то вы ПQлучнте щпиБRy 1\~. Причина в том. что' недьзл автоматически И1I­


терпретирОБаn' Syste;m .abject. Щllt объеRТ. яnллющиi:rся произвоДным nепосред­
ствешю от Empl оуее. поскоJIЫty Obj ect не является Emplo,yee. Но Bы можете заме­
ТИТЬ, что объеRТUая ССЫЛlЩ указывает На объeJtт. связ.-'lННЬW: с Бmрlауее. Поэтому
комmщя.тор будет "удовл.еТJюрен • если :вЫ ие:t:Ю.I1ьзуете явноеnptшeдeн11.е тиn.oв.
6

В С# щmое npи~еде.н:ие типов ужазьnrается е помо:щью С'f(об.QК. размещаемых во­


:круг .ИМЩffi типа. ж которому вы хотите nрИЙnr. с последующим указанnем объек­
та, RОТОРЫЙ вы nЫTaeтecь ИСЦОЛЪЗОJiать n н.ачестве исходного. Например:

1/ Приведение общеroо ~ипа Sys'tвm_ Qbject


/I 11: cТporo· '1'ИrtИзозавяону МЗnаgeI.
Mqnager mgr = (t1anаgеt)f.tалk:;
СОПJ501е. Wri teL:-iJ'Jе ("ОЛЦИGЯЫ Фрэ-ю<:а; '{О} ", тч!: . lsТшnbОрtз) ;
242 Часть 11. Язык программирования С#

Если не объявлять специальную переменную для "целевого типа". то можно по­


лучить более IЮМШIRТНЫЙ прогрaммНblЙ код.

11 "Внутристрочное " Я1Iное npИ1lе;цение '1'ипов.


Console. WriteLine ("Опционы Фрэнка: (О)", ((Manager) frank) . NumbOpts) ;

Проблему, связанную с передачей ссылки System.Object методу E'ireThisPerson () ,


можно решить так, как показано ниже.

11 Явное npИ1lе;цение '1'ипа System. Obj ect 11: Entployee.


TheMachine.FireТhisPerson( (EmployeeJfrank);

Замечание. попыкаa приведения объекта к несовместимому типу порождает в среде выполнения


соответствующее исключение. Возможности структурированной обработки исключе~IИЙ будут
рассмотрены в главе 6.

Распознавание типов
Статический метод TheMachine.FireThisPerson () строился так, чтобы он мог
принимать любой тип, nроизводный от Employee, но возникает один вопрос: как
метод Иузнает", накой именно производный ТИП передается методу. Кроме того,
если постynивший параметр имеет тип Employee, то как получить доступ к специ­
фическим членам типов SalesPerson и Manager?
Язык С# обеспечивает три способа определения того, что ссъuша базового клас­
са действительно указывает на производный тип: явное приведение типа (рассмо­
тренное ВЪlШе) , ключевое слово is и ключевое слово аз. Ключевое слово is воз­
врашает логическое значение, указывающее на совместимость ссьшRИ базового
класса с ДЩtНым проиэводным типом. Рассмотрим следующий обновленный метод
E'ireThisPerson().
public class TheMachine
(
public static void FireThisPerson(Emplayee е)
(
if(. is SalesPerson)
{
Сопsоlе.WritеLiпе("Имя уволенного продавца: (О)",
e.GetFullName()};
Console. Wri teLine (" {О) оформил (а) {1) операций ... ",
e.GetFullName(}, ((SalesPerson}e) .NumbSa1es);

if (е i.s Мanager)
{
Console. WriteLine ("Имя уволенного клерка: {О}",
e.GetFu11Name()) ;
Сопsоlе.WritеLiпе("{О) имел (а) (1) опцион(ов) ... ",
e.GetFu11Name(), ((Manager)e) .NumbOpts};
'rf13B8 4. Язык С# 2,0 ~ объектнь-ьриенти,роваflный ПОДХОД 243
эдесыo:[loевоеe CJШ1Щ i$ ИОПО:Т,fьауется ДJIЯ. ТОГ!'), чтобы ДИНaмwJе~щr Qцр~деди'Гь
Тип работшща. Чтобы ПQлrцrть ДОСl1'д к свойствам NvmbSale,5 ИЩI NШfiЬОрts, ВЫ
ДОJDIЩЫ :ИIrПОЛЫЮВ~ТЬ ЦВl.iОе приведени~ типiJв. Альтернативой 1III:Оrnо бы быть ие с
ПOJlызовщще КJIЮЧСЩJГО ,слова ~S для получения СChLJIJ:rИ на произвqдный тип (если
ТИПЫ НРИЭТQМ ОЮЩ(уТСSl н~с;овмеСтимыми:, ccьtJ1Кa пол:учит значение шй :Ц.

SaleBPersO[1 р = :е ае Salesf!e,rs'onj
i f (р ! = r.-1.111)
С::олsоlе.Wrlt'еL.inе ("'Число продаж: {О')" I р.'NШllb5аlеs);

Замечание. ИЗ ~лавЫ 7 вы узнаете, ЧТО такой же nOДXOJJ,. (ЯЗI-IРе nриведен~е ТИt101iI, is 11 as) может
иtmользрватьсяпр'и получении интерфейсных осылок из реШJl.1ЗУЮЩero типа,

Прмведение числовых типов


в З3:JЗерmение нашего обзора оперaциii приведения ТJ,ШОВ 11 С# З~ПIМ, что
цреобразова;ние ЧnСдlЭвых, ТШ10В подчиняетс", цри~ерно таКИl\lf же цр@илам.
Чтобы поместит", ~бол,ьший" ЧИСЛОВОЙ тип в "меныu:lilй~ (напр:m.t.ер:, це.jil'~ число
int n byt~J. следует исподьаоватьнвное лриведеIlilе тиnО'EI, 1tOТopoe 'ЦНформирует
.к,~TO'P о ТОМ, чтО' вы ГОТ0ВЫ принять возмождую пvтерю ДШU-fЬЩ.

11 Еcnи ":К" больше Dpедепьворо SИil~1IИR для:ьyt.е ,1IВрО5W'Иa nО'1'еря:


11 .IIаннух. НА ИЗ 1V!ae~ 9 :в» У5.И~'1'е о "жонorpалируеИЬiX JlCJUDD1;Iеии~" ,
11 с noыoJltы) ICO!t'0P~ но_о ynp~ВI!'Ъ резyn'Ъ'1'&'1'ОИ,
int х = 6"
byte Ь = IЬуи) х;
Когда БJ>l сох,ра:нлете ~еньm:ий" Ч'ИСЛОВQЙ тип в "боJIЪЩ~М:~ (нauример, by·t.e :в
intJ. тип ДЛЯ вас будет лреобр~зован неявно и авто~атичесЮj, т.ак Baf\: здесь иесll
потерь да:ндых.

1/ Приаедеиие '1'ИПа не .бyeoI!СЯ:,


/1 i n t ДОCl'1'8.!rОЧНО ";ве.пиJC" ~ ~а.в8ИWI byte.
byt.e Ь '" 3 о ;
int х = !>;

П'арциanьные типы С#
в С# 2005 вводится новый модификатор типа р а, r t..iq: l, который UQЗВОЩIет
опреде.лятq С#-тип в. несЮ).iIЬЮ!И{ файлах ".CS-. Предыдущие .6ерсии нзыка С# Tpt:-
f)овали, чтобы весь програММJ{ЫЙ RСЩ определения типа содержалс.f.l в пределах
I'ЩНОro фa:йJJа '" .СВ. С учетом того, что сlt-класс ПJЮИ3ВОДСТЩ~ШiОТО УРо:ВЫН может
содержать с@тни строн npoгpaммв:OTOROдa. соответствуцнций фaiЩ мощет OK~aТb­
ел достаточно объеl'V1НЫМ.
В таких слyqаях было бы хорошо иметь возможность разде.тщть реализацию
типа на неCRОЛ;ЫЮ файлQ:В, чтобы отделить nРQГРаммиы:й код. I{Отt}рЫЙ в некото­
ром смысле более важен, от других элементов. Например, Pl'СПОl;rh3J'Я для КJIacca
М~ДИфИRа11@Р part ia 1, можно поместить все открытые ч;пеквr в файл с именем
МуТуре_ РиЫ i с . cs, а приватнъrе IIOЛЯ дан:ных и В0домш"ателъные функции - в
файл NyTy:pe_ Pr±vate. СЭ.
244 Часть 11. Язык программирования С#

// МyClasB_public.cs
паmеэрасе PartialTypes
{
public partial class MyClass
{
/ / KOHCТPYJCTOPW·
public MyClass() { }
/ / OTxpwтыe чnеИil.
public void MemberA ()
public void MemberB()

// МYClasB_Private.cs
паmеэрасе PartialTypes
{
public partial class MyClass
(
/ / ПРИ8аТИilе поп. даниwx.
private string someStringData;
/ / ПРИ8а'1'Иilе 8СПОllоrа'1'e.n:ъИilе чnеИil.
public static void SomeStaticHelper() ( )

Это, в частности. упростит задачу изучения открытого интерфейса ТИIlа для но­
вых членов команды разработчиков. Вместо изучения единого (и большого) файла
С# с целью поиска соответствующих членов, разработчики получают возможность
рассмотреть только открытые члены Конечно, после компиляции этих файлов с
помощью с s с • е хе в результате все равно получается единый унифицированный
тип (рис. 4.14).

ИСХОДНl.IА I(ОД. Проект PartialTypes размещен в подкаталоге, соответствующем главе 4.

!'IANIFEST
• P.г~.lТyptl.

"'.
J!; • Por~ypos.PrDpertles

• .closs pub~c 'U:D 'П51 b.fDr.fleldln~


у SDme5tringD.!!t.' рМ\(к. 5trIГIQ
• .ctor : vold(j
• МетЬ"гА : void()
. • MemberB : vDidO
Q SomeStotlcНelper , voldO
,. . , Рortl.IТvре5.Р,ооrМ1

Рис. 4.14. После компиляции парциальные типы уже не будут парциальными


Глава 4, Язык С# 2.,0 иобъвпно-ор.иентирьgанныЙ подход 245

Зilмечакие, После рассмотрения Windows F1;Irms ~I дSp .NEТ вЫ поймете. что в Visual Si(JdiO 2ЬО5
ключевое слово part l1З 1 используется дня раэделен\oiЯ' t1РОГРЭММl;Iоtо кода, reHep~1pyeMoro
инструментами разрaБQтки. У1СПОflЬ$УЯ этот ПОДХОД. вь! можете сосредО'F0"1ИТЬ'СЯ на ПОlillже
пqДJфДl1ЩИХ решений и не азботитьсяоб aвтIЭМaтwtески генерируемом I1рограмМ!1О>М кеде,

Документирование ИСХОДНОГО
кода в С#с помощью XML
в завершеFЩЕ: этой rnaвы мы pactZМlYI1:!i'Th.I специфичесr<ие для С# лексемы коммен­
тариев. которые DОРОЖДают ДШ<уМентaциro цpoгprow.mOTo Iюда на базе XМL. Если ВЫ
ШYfeете опыт i'Iроrpамми.р<>вания :наязы:ке .Java, то, с'корее всето •. зЫаете об ут:илите
lav'advc_ Используя javadoc, МЩЮIO ЦР~ElраТ'Н'IЪ l'юхо.цн..ь:Ш IЮД Java в соответству­
ющее HTML-npеДСТ81Щe,I-щ:е~ 'Модель ДIiJI~~нтировалия, при:вятая в С# , Оказывается
немного :щIОЙ в том отнощении. что ЦРl'щес;<:; лреобрааования ROмментарШ'в в XМL JШ­
Дf.reтCH LЩООТQЙ IЮМJllИЯТОра .(upи ис;пользoвaюrn ОПЦlilИ Idщ::), а не ОСобой утилигы.
Но поq~ для ДОliументировадия опредr.лений типов используется XМL. а не
НТМL? J)щ:внщr ЦJЭИЧИН;;L в том, ЧТО XМLобеспечивает Ьчеm. IПИJIORие ВОЗМОЖНQ­
ста, Пое1tоль;Io/ XМL отд~JШ!n, определение данных от I1р.еД(:1'эвления 'ЭТИХ .данных.
к JI~жащем;у в ()СШJВf: XМL-KOДY можно применитъ любое XNIL"преобразование. по­
з.вЬлпющее О;fОбразить документацию лротра:Мм:но:го HQДa :в одном из множества
дwтупных форматов [МSDN, НТМL и т.д.).
При документировании С#-ТИIIОВ в формате XМL пёрвой задачей ЛВll.Яflтс,я БЫ­
БОРОДIШI'О из двух в3 риawrов нотации; 'l"pо:И:ной косой черты [1/ /) или прИ3нaRa
:комментария, который начинается КDМбинзцией косоЙ черты и двух звездочен
(/**). а закавчиваетсн- кo:мfjиняцией авсадоЧ1Ш и .коСОЙ черты ('" 1), В nOJ1.E .до:к;у­
ментирующето комментария МDЖно исшiЛъ:юватъ любые XМL-элементы. включая
элементы рекомеццуе:мor{l набора. OIIИсанны:е 11 табл. 4.1.
Таблица 4.1. Элементы XML, pe'IGOMeH)J;YSMbte ДМ1 ИСПО11ЬЭQваНИf1 в комментариях
к программНому коду

ХМL-элемент
ОписаНltе
докvментации

<с> УкэаЫl3ает текст, который ;lJ.OлжеНОТdбражаться "lJJpиФюм ДJlЯ программного щtl,а"


<сФdе> Yl<aabl!Jiim м~roжеСТI!О СТРО,К, к{Порое должно рассмаТриваться, кэк программ,­
нЫй КФд
<ex"J:npl е"> Указывает fIP1'1IiiH!P npoгpaмMI'JOro К{);ЦЭ для QllИOJ:JIВземото элемента
<eKceptiorJ> Докум~тирует I3QЭМОЖНf;,1е ИО1U1Ю"lIЭJ.~ИIl данного 1U1ВОGЭ
<list> BCraВil1f1eT списоК или таблицу в фаЙл докумеmвЦиlll
ОПИChiвает дal-JI1ЫЙ параМ8ТР
<раrзшrеf;> АССОЦ~IiIРУfJ'l данный дзокриптор XMl С rl<lpaMerpoM
<ретrrJssiоп> Документирует ограничения защиты для данного члена
<:t'.2I!\a.rks> СО3Д&IП описаНИе ДЛ~ данного 'IЛЩIВ
<retl!rns> Докумеflfируеf аоавращаемое зttа4еШ18. даНfюrо члена
<э'Е!е> Перекреотная СQI1JЛКЭ ДЛЯ овязанных элеМ8Нтqв ДQкументэ
<эвеalsо> Созщнп раздел "сМ. та~же" 6 ~mlitсаНии'
<sumffi.i;J. гу::> Документирует "по~r;НЯlOщее резюме" ДЛЯ Данного "!леНа
<value> ;lJ.Оi<.уме~щрует ДВ~JI'Юе свойстве
246 Часть 11. Язык nрограммирования С#

в качестве конкретного примера рассмотрим следующее определение типа Car


(автомобиль). в котором следует обратить особое внимание на использование эле­
ментов <summary> И <param>.

III < SUПlll\a.ry>


11/ Это тип Car, и.JIJDOстрирYDЦИЙ
/ / I воsиожиости XМL-дохуиентировaю:rя:.
/1/ </s~ry>
public class Car
[
1/1 <sшnmаrу>
11/ Есть JIИ JШХ В ICploDl!е вашего &Втоиобкnя:?
/ II </summa.ry>
privdte bool ha sSunroof = false;

11/ <summa.ry>
1/1 Этот ХОНСТРУХ'1'ор позвоnя:ет установить наличие JШха.
/ 1/ </SШlll1l.8ry>
/ I I <param name="hasSunroof"> </param>
public Car(bool hasSunroof)
{
this.hasSunroof = hasSunroof;

1// <sшnmary>
1// Этот метод позвоnя:ет отхрыть :nюx.
11/ </summary>
III <param name=" s tate"> </param>
public void OpenSunroof(bool state)
(
if(state == true && hasSunroof == true)
Console.WriteLine("OTKpblBaeM люt<:!");
else
Console. Wri teLine ("Извините, у вас нет люка.") ;

Метод Ма i n () программы также документируется с использованием ХМL-эле­


ментов .

/ / I <SUПlll\a.ry>
/ I / Точха входа пркnо_ения:.
11/ <1 sшnmary>
static void Main(string[] args)
(
Сат с =new Car (true) ;
c.OpenSunroof(true);

Чтобы ка основе комментариев. задающих ХМL-код. сгенерировать соответ­


СТВУЮlЦИЙ файл *.xml. при построении С# -программы с помощью csc.exe исполь­
зуется флаг /doc.

csc /doc:XmlCarDoc.xml * . CS
Гnава 4. Язы.: с# 2.0 и ебьектнр.ориеН-ТИjiJованн""И ПОДХОД 247
в Vjsual Studio 2005 можi-i.о уУ<а3аТЪ имя файла с .xmr.-до~нта:циеЙ, исполь­
зуя вкладку Build оКйа свойСтв (рис. 4.15) .

... , (~iiiOifi;[ bt'~ J _____ __ _ " ..__~

r'~- J ~~'rд~~~О])-'--':i
1~ ,--~~----~-
Pl8tt..m:~~м[A>wСМ . i~;----1 :
.::'==:::=== ~
~ ВЦId 0фIt ~~ -. . ~--"':-_',~"':-==-~_ ,____~ '0 ~o-.- 0- . ~ .'

Г~~.:- Q;tpltpd]; ~~. J 'f


i lЭ<b.u .0 )к ~ ~ ~_
i'J)I!bo
_ _- !"'mЮocCiO'
_,_XI4._ _ __ _ _ J f
! ~~ ~,:j~lI~r [:'t1i , -~~~ Io"' ~«~':?'

1- , . _ ..._ .r.;;- - - :fl ., ~ I


'''''",'"''''\$!' ':"'''--О , ' __
В!!.~~~~~~~~:-~
,,,,,,"',-''''" '''''''' '_'- -с
~
'

I
L::, •.. ~ -",<,- . " : ;';" _. С .~. _. _ _

РИ':J 4.15. Генерирование файла ХМL-до\(уменпщии в Visual Studro 2005

C~BOnЫ форматирования в XML-коАе комментариев


Если <:Jткрытьсгенер:и-рова:fiНЫЙ XML-фaйJI, вы увиди".ге" что алeьre:нтыI будут по­
меrюны такими СИМВОila.мJ;i. как "М-. 1 . ~F~ и Т. П. Т"1ацример:

<membe.r nщtlе="Т: Xml.DocC~,r. Ca,r " >


<summary>
Эт ~ ~П Сах, ИТ'.дЮС'I'рирующ:ий
ЕЬЗМОЖНdСТИ КМL- ДО1СJмеНТJ4Iж)'вания •
<J sшmтlаr~' >
</Ir,erober>
EI табл. 4.2 QПИС:;ЩЫ З1:Ia'ЧеНЩI этих мe>folL

Табяица 4.2. Символы формат~ров~ия XМL

CМM80J1
Onисаtlме
формати:ровat4ИЯ

Е Элемент QбоЗначае.т событие

F Элемент представляет поле

М Элемент предстаапяет метоД (DlUUOча:я крнструкторы 111 перегружеliные


операции)

N Элемент аnредеЛfllПпространстзо имен

Р Эnемент преДСiааляет СВQL1СТе'.i:Пlllпа : (включая 1!111д6КС!!I)

т Элемент hре,цатаВЛЯ61 тИП (например, класс, и нтерФ€йс , CTPYlCТypy.


n.ере4ень, делегат)
248 Часть 11. Язык программирования С#

Трансформация XML-коАа комментариев


Предыдущие версии Visual Studio 2005 (в частности, Visual Studio .NEТ 2003)
предлагали очень полезный инструмент, позволяющий преобразовать файлы с
ХМL-кодом документации в систему НТМL-справки. К сожалению, Visual Studio
2005 не предлагает такой утилиты, оставляя пользователя "один на один" с XМL­
документом. Если вы имеете опыт использования ХМL-трансформаций, то, конеч­
но, способны вручную создать подходящие таблицы стилей.
Более простым вариантом является использование инструментов сторонних
производителей. которые позволяют переводить ХМL-код в самые разные форматы.
Например, приложение NDoc, уже упоминавшееся в главе 2, позволяет генерировать
документацию в нескольких различных форматах. Напомним, что информацию о
приложении NDoc можно найти на страницах http://ndoc.sourceforge . net.

ИСХОДНЫЙ КОД. Проект ХтlDосСаг размещен в подкаталоге, соответствующем главе 4.

Резюме
Если вы изучаете .NEТ, имея опыт работы с любым другим объектно-ориентиро­
ванным языком программи:рования, то материал зтой главы обеспечит сравнение
возможностей используемого вами языка с возможностями языка С#. При отсут­
ствии такого опыта многие представленные в этой главе понятия могут казаться
непривычными. Но это не страшно, поскольку по мере освоения оставшегося ма­
териала книги вы будете иметь возможность закрепить представленные здесь по­
нятия.

Эта глава началась с обсуждения принципов ООП: инкапсуляции, наследова­


ния и полиморфизма. Серsис инкапсуляции можно обеспечить с помощью тради­
ционных методов чтения/модификации, свойств типа или открытых полей. до­
ступных только для чтения. Наследование в С# реализуется еще проще. поскольку
этот язык не имеет для наследования специального ключевого слова, а предлагает

использовать операцию. обозначаемую двоеточием. Наконец, для поддержки по­


лиморфизма в С# предлагается использовать ключевые слова abstract, virtual,
override и new.
..
~----------------------------------~~~~---------------------- -- - - -----------

ГЛАВА 5
цикл существования
объектов

В цред:ыдущей главе мpr потратили достато'ПlО много :времеlШ на 1'0, чтобы на­
Y'QIJ'ься ~троить ПОЛЬ30ВатедЬСRие типы масса в С#'. в ЭТОН 'Гл.аве мы выяс­
НИМ. как среда CLR ynРIШЛЯ~ уже раамешенныIlm объектами с поМощью процеt.са,
:ноторIЩf назr:цвается !'-бор~ой ЩJсорц- Лрограммистам, исrюЛЪ8УЮЩИМ C4l. tUJ при­
ХОДИТСЯ удаЩI1;Ь объ~К'гы из цqмЯТИ "вручвую" (напомним. что в С# вообще нет
КЛючевого слова delete). Об'Ь~'Тbl .NEТ JЩэмещаются в области naмяти. RQТОРая
Н'Ззыва~С_8 .!Jn;pавl1яелwй дuндмuческой памятью. где 31'1'/ об'ЬеJtТы ~ не-который
подходr.пцИЙ момент будУТ автоматически yниqтожены сборщиком мусора.
б

Выяснив ос_вцвн:ые детали npоцесса СборlCИ мусора. вы узнаете. ка'К -взаимо­


действовать 'Со сборщиком мусора. ИСПQJIЬЭУЯ ДJШ этоrо ТИП -класса ЭуStеm . GС .
Наконец. мы раQСмiугрим ~щртуалън;ый метм System.Object . Finalit"E! (') и :шrrер­
фейс IDisp.osable. -которые М(lЖНО испощ,эоваТЬ-Д.1ЦI того. 'iТ06ы СО3Дa1lflГЬ ТйnЬJ~
(lШtlОСТОЯТельн:о освоб(11J(дающие в ~1Й момент своn виутреН1-1ие н.еуnpавля.­
емые pecypcbL И~учив матерnа;тi этой ~аВbl. ВЫ см()жете ПОНЯТЬ, как cpe;n:a CLR
управ.ilяет объектами _NEТ.

Классы) объекты и ссы1ки


Чтобы очертить контуры теМ'м. рассматриваемои 11 ДЮЦiЦЙ rлаве. необходимо
}"Т9q}iИТЬ различия мещду RJIассами, объектами и ССЪUIRaМИ_ J3 npеды,цущей главе
}'те товорилосъ О ТОМ. ЧТО I<ЛаСс - это своеобра3НЫЙ "maблщ('с <щисаиием т(!)го,
E~ экземпляр да:нното пmа должен }lьtrля,цеть и вести себя в IЩIIt{ЯТИ. Кщн;сы
опредеюuотся в файлах. ноторые по сor:лaшеН-ИЮ в С:# имЕ:ю'r расширеI:Ше * _С'В_
Рассмотрим прост.оЙ класс Car (автомоби.т.j. tШpеделенньш в файле Car.es.
pt;blic Ql гн;s Са'!::
t
private ict сдrrSр:
pri "a·t~ s triлg petNarтe;

publlc Car П {)
publi·c C<1ir (S't r irJg пате т int. speed)
1
petName = пате;
---
250 Часть 11. Язык nрограммирования С#

currSp = speed;

public override string ToString()


(
return string.Format("(O) имеет скоро сть (1) км / ч",
petName, curr Sp);

Определив класс, вы можете разместить в памяти любое чисдо соответствую­


щих объеlПОВ, используя ключевое слово С# new. При этом, однако, следует по­
нимать, что ключевое слово new возвращает ссылку на объект в динамической па­
мяти, а не сам реальный объект. Эта переменная со ссылкой запоминается в стеке
ДЛЯ использования в приложении в дальнейшем. Для вызова членов объекта следу­
ет применить к сохраненной ссылке операцию С#, обозначаемую точной.

class Program
(
static void Main(string[] аrgэ)
{
1/ Создае~ся новwй объехт Car в дииаиичесхой памяти.
11 ВОЗJlращается сClol.JПltа на этот об ..ехт (1 refToМyC:ar' ) .
Сат refToMyCar = new Car ("Zippy", 50);
1/ операциJl С#, обозначаеман точкой (.), ИСПOJlЬзуе~СJl
/1 со с:cw.nочноЙ переиениой .ц.пJl BIoV!IOBa членов этого об-.екта.
Console.WriteLine(refToMyCar.ToString());
Console.ReadLine();

На рис. 5 .1 изображена схема, илmoстрирующая взаимосвязь между классами,


объектами и их ссылками .

Динамическая память
Стек

l refToMyCar I
~ Са,!"

Рис. 5.1. Ссылки на объекты в управляемой динамической памяти

Основные сведения о существовании объектов


При построении С# - приложений вы вправе предполагать. что управляемая
динамическая память будет обрабатываться без вашего прямого вмешательства.
"Золотое правШIО" управления памятью . NEТ ЯВЛЯеТСЯ оченъ простым.

• Правило. Следует поместить объект в управляемую динамическую память с


помощью ключевого слова new и забыть об этом .
(лава 5. ЦИКЛ сущеС'ТВDвания объе~t)В 251
СБQРЩИ1( мусфра уничтожит со~да.нвый объект. когда этот объект будет больще
uBRY*eн. Следующий очевидI:Iый вanрос: "Н<ш сборщик :мусора определяет. '.по
объект БОЛЬше не нуже:вт ItратЮIЙ (;г.е. упрощенный) ответ 3aI01ЮЧается. в том.
ЧТО сбоpIЦИБ мусора удаляет объект из динвмичеСIЮЙ памяти тохда, I~Orдa объex:r
с:гавовитсл н.едосrnyrщ,~ длн- все;х. частей nporpa..\IMHOTO кода. nредnо.ilUЖМl\cI. что
Щ~J ИМеете метод. разиещaIOЩиЙ локальный объек:r Car.
public stati{; void MakeACar '(j
(
!! Ecnи myCar RSЛRе'1'СR еДЮlС!J!Веиsой CCJo1JU:o)i на olS'i08~'1' Си,
11 >ро (),б'5е~ J«O-1II' lSJ.rrЪ YJIИ"Itt'OJl(eМ цоc,n:е .оз:врацевиR из нe'1'OjЦa.
С'ат туСаж = new СаУ () ;

Обратите Вl'IЮ<laJnJ~ :на то, что ссылка на объект (myCa:r;) бьта co~дaнa иепо­
среДcnJещIO в методе ,MakeACar () и ~e Uередавалась за пределы области видимо·
сти onределнющеrозту ссъи:ш:у объекта (НИ в виде :возврanфeмоrо значения. ни в
виде дараметров ref/out). Поэтому ЦQ~e завершения рабt>'i'Ы вызванного метоца
ecыnкa myC';:I r СТЩЮВИ1'C,fl недоетутюЙ;. ]ij соответствующий оБЪtlli'Т Сат окззыIа·.
ется КЩtдuдamом для УДa!Ifе;щщ в ~MYC()P~. Однако следует l1OJlИ:М:a:JQ, что БЫ не мо­
ще:rе rарантжровать H~eA.!Jeнвoe удалmmе эт.о);'о. объек:га :из памяти сразу же По
Эа!Верщении работы MqxeACar ( j. в этот момент можноrарантИрОвать тольКО то.
что цря следующе~ сбор~е ~copa в оБщemlыRвой среде ВblПoЛНення (CLR) объект
myCiJ.r можe-r быть~ез оцасенцй уничтожен.
Вы. несомненно, обнаружи.те, что прuграммировмше 111 окружении. обеспечи­
вающ~м автоматичес~о сборку ыусцра. значительно упрощает задачу разработ~
кв ЦJЩлошеню1. Про:rра:м:м:иеты, иcr:roлъзyющие С++. знают о том. что. ес.ilИ в С++
заб~ ~РyчIryЮ удали1~Ь размеIЦеН1-ше Б ДИRaМИЧесной памяти объекты, может
ПРОlJ30Й"'PI' "уте<ща ГЩмнти , На еамом деле ЛИlrnи;!(au;ШI утечек паМяти sшл:яется
П

ОJUiЩМ из C~ЫX ТРУДQВмЕИХ (и неинтересных) аспектов npограм:мироВEiНИЯна язы­


КЩ. 1fPTopble не ЯВЛЯЮП-'Я}'II'PШlJIi'lемыми. Поруч:ив сБQРЩИКУ мусора уничтоокение
oБIьeктов. вы снимаете с себя груз ОТВel'СТБенности за управление .памЛТi;Ю И пере-­
]WflДblBaeTe ето на CLR.

Замеч-анме.. Если вы ИМ99те опыт рil~р~ботки пррграмм G ИСlТOJJьэaвatJиеN СОМ, то знайте, что
объекты' .NEТ Не Н(),[1ДЕ!РЖИI!_3ЮТ С'ЮТ'lик ef.!YTpel-lfl\llХ СОЬ/J1ЩС, ,t)ОЭТОМУ vrчrввляемые объекТЪ/ не
предлаГf}Юl' тзкие M~TOды. как Аф;iRеf () и Releas€ ( ) .

C1L-KOA .для new


КоГда КОМIIИЛнroр С# обнаруживает ключевое ОЛQВО n:ew. он генерирует C!L-HU·
C~ n6wobj в paьmax реализ;щии сооrnе7ствующегамет,ода. Если выполнить
компиляцию tIporpaMMHoro кода текущего npимера и с помощью ildasm.e.x e рас­
смотреть полученный компоновочный блок. то в рамках MeTOЦCl МаkеАсдr (.) БЫ
у:вцците сл.еЦУЮtцКe CIL-оперзтеры .

. method pubiic hide:bysig static -void Ma:ke}IXar () cil managed


{
-
252 Часть 11. Язык программирования С#

/ I Code size 7 (Ох7)


. max st·a c k 1
. l oca ls i nit ( [О] cl ass Simple Finali ze . Car с )
IL 0000 : newobj in s tan c e void SimpleF ina lize.Car:: .cto r()
IL 000 5: s t l o c. O
IL 0 0 06 : re t }
11 en d o f me th.o d Program: :Make ACar
Перед тем как обсудить точные правила, определяющие момент удаления объек­
та из управляемой динамической памяти, давайте выясним роль CIL-инстрYRЦИИ
n ewo b j . Сначала заметим, что управляемая динамическая память Является не про·
сто случайным фрагментом памяти, доступной для среды выполнения. Сборщик
мусора .NET является исключительно аккуратным "дворником" в динамической
памяти - он (при необходимости) даже сжимает пустые блоки памяти с целью
оптимизации. Чтобы упростить задачу сборки мусора, управляемая динамическая
память имеет YRазатель (обычно называемый указателем на следующий объект.
или указателем на новый объект) . .который идентифицирует точное место раз ­
мещения следующего объекта.
Инструкция n ewobj информирует среду CLR о том. что необходимо выполнить
слеДУЮIЦИе главные задачи.

• Вычислить общий объем памяти, необходимой для размещения объеRта


(включая память . необходимую для членов-переменных и базовых классов
типа) .

• Проверить управляемую динамическую память, чтобы гарантировать доста­


точный объем памяти для размещаемого объекта. Если памяти достаточно.
вызывается конструктор типа, и ВЫЗЬUlающей стороне. в конечном счете,
возвращается ссылка на новый объект в памяти , причем адрес этого объекта
будет соответствовать последней позиции указателя на следующий объект.

• Наконец. перед возвращецием ссъmки вызывающей стороне необходимо из­


менить значение YRазателя на следующий объект. чтобы указатель соответ­
ствовал началу свободной области управляемой динамической памяти.

Этот процесс схематически показан на рис. 5.2.

Динамическая память

s ta tic vo i d Ma in( ) 1 cl I С2 1 1
!
Ca r c l = new Car () ; t ) t
Указатель
Ca r с2 = new Ca r () ;
} на следующий объект

Рис . 5.2. Размещение объектов в управляемой динамической памяти

При размещении объектов в приложении пространство управляемой динамиче­


ской памяти может, в конечном счете , заполниться. Если при обработке инструк­
ции n ewob j среда CLR обнаружит. что управляемой динамичеСIЮЙ памяти недо­
статочно для размещения очередного типа. с целью освобождения памяти будет
ГJ1зsа 5. ЦIOO1 Gуще(;ТВО6~НИ!J орьект!)в 253
выnолнещ с(50РКВ мусора. 'ПОЭТОМУ следующее правило еборl$И мусора 'l'анже ока­
зывается очещ, простым.

• Правило. Ес;щ управляемая ДиlIaмRЧесК8.В память !1€Щостаточна ддя рааме­


щения очередного oбъewrа, выполняется сб(!)рна мусора.

В процесое сбор~ мусора сбор~ик Мусора BpeMeRliO ПРИQстанaвщmaет все ali:-


ТИВ1il>Jе ПО'rOJ(И 13 рамках reкущerо процесса, чтобы гаранrnроватъ., "ЧТО .ttpилоЖе~
юre не щщучи.т доcryпa К динамической памяти в npоцессе сБQрIЩ муСора. Тему
потоков ~ рассмотрим BIJIaВe
14. а пока ч'tv ВОСПРЩЦfМaЙ1'е Щ>ТОК, KaR ··нить"
въшолнеНИR в nред~ах ВЪШОЛНЯЮЩ8ЙСЯ программы . Пос.це зanершения цикла
сборlЩ мусора fLРИО(:ТаноШIeННЫМ nOТoRa."d будет раiJрещено D.рОдолжить работу.
К счастью, сборllUiК мусора .NET ХОР01UО оптимиэирован.. И ВЕ!1 вряд ли заметите
~СТНУЮЩIJе ~ОРОТЮlе neрерывы в работе вашего лриложе.:ниЛ.

Роль корней' приnожения


Теперь мы верlreМС.я R вопросу атом, "Как с.борщик .мусора опреJ(миет. !<ОГДа
объект ~бо:пъillе не нужен". Чтобы понять ЭТО, необходимо' paccмoтperrb понятие
Iroрней r!pШЮЖен.Wi. Упрощенно го.воря. ICopeH-Ь- это место в 'n8МЯ:'IИ С0 ССЬ1ЛКой
па объект.ра:зlliещенfIыIй Вдтtaмической цамfi'l1Й. Строго· говоря. корень МОЖет 0Т­
носИться.R любой из следующщ катеJ,"ОРиj:i.

• Сс!blJIКИ на .r:побалъНЬХе объекты (хоти QНИ И не цозволенв1,В С#. проrpaм.'-U:lЫЙ


RQД сп... допускает размещение .rлoбалъдьrx оБЪ~:fP'OВ) .

• ссы/пw .I;Y a иcnоmшyeмьm В настаmций МОМeIП статические объекты и поля.


• Ссылки НЗJIOЩШЬНЫе объекты в пределах данного меТода.

• ССЫЛКИ на Dбъеf('rные параметры. дредаваемые методу.

• ссы.Iпtи на Объект:ы. ожидаюпще фl.1Нl1Лuзa.циu (соo<tветствующее rюнятие QY-


дет описано в ЭТОЙ rла,ве ПQзже).

• Любые реrистры процессоJЩ.. ссьщаю~ся на локальный объеJn


В npоцесее сбор.1tИ' мусора среда въmолнеЩJЯ провериет объекты в управляем.<m
ДИfiамической памяти и определяет. остаются ли они доступнЬ1МИ для ПРИJiIf)Ж~­
вия [иначе говоря. ykopeheI-lНQ1.JIU1)_ ДIJЯ этрго среда Сш строит объеiC:ГW-tЬt'й граф.
изОбражаюIЩIЙ ttаж,цый об'Ьею В динaмu'lt;::СКОЙ пам.ити,. достижnмый для nрилф­
женин. Объекmые rpафы буffYТ ра:ссматрива'Г.ЬСЯ ПОдРобнее при обсуждеwm се­
рИалйЭаци:и объектов (см. таву 17). На д3}JЮdЙ момент вам достаточно знать, ~ITP
объектные трафы используются М.И учета всех достижимых объектов. Следует
sнaTb и: о ТОМ. что сборщик мусора .никотда :не учитывает ОДИН J;,I ТОТ же объе:ь:т
дважды. вследствие чего. в ОТЛИЧiJ!.!е от классического ПОДХQда сом. ~ecь Ije1IОЗ­
никает опасности циклического учеr~ ссылок,
Предположим. что ynpаJ3ляемщr д~амичесхая память содер}RйТ .м:нощесцю
объектов .. имеНа I(Oторых А. В, С, D, Е, F и G. Б процеСсе сборни :мусора Э1'И объ~­
ты (а 1"8I(Же все внутренни~ объектные Ссbl.JПi!И( .которые эти объекты м.oryт содер­
жать) ПРQвеРЮОТСJI на наличие у нщ aкwnmыx КDpriей. После построении ГРЩРil
недОСТИЖИМhJе об:ьекты (1dbl будем цредпояага'm>. ЧТО это об:ьеКТ1>I С.в ") оБDЗl:,lачв­
ю-n;;я. КaJ( мусор .
254 Часть 11. Язык программирования С#

На рис. 5.3 представлен возможный объеI<тный граф для только что описанного
сценария (цаправленные стрелки. связывающие объекты в таком rрафе, можно за­
менить словами ~зависит от" или "требует". - например. "Е зависит от G и косвен­
но от В", 1\ не зависит ни от чего" и т.д.).

Динамическая память

t
Указатель на следующий объект

Рис. 5.3. Объектные графы строятся д1lя выявления объектов, достижимых из корней приложения

Если объекты помечены для уничтожения (в данном случае это С и F- они не


включены в объектный rраФJ, эти объекты удаляются из памяти. В этот момеJП
оставшееся пространство в динамической памяти уплотняется, что в свою очередь
заставляет среду CLR модифицировать множество корней активного приложения,
'-Iтобы они обеспечивали правильные ссылки на точки размещения в памяти (это
делается автоматически и незаметно). Наконец. соответствующим образом изме­
няется указатель на следующий объект. На рис. 5.4 показан результат преобразо­
ваНИЙ.

Динамическая память

А Iв I D I Е IG I
t
Указатель на следующий объект

Рис. 5.4. "Чистая и компактная" динамическая память

Замечание. Строго говоря, сборщик мусора использует два разных фрагмента динамической па­
мяти, один из которых предназначен специально д1lя хранения очень больших объектов. К этой
динамической памяти при сборке мусора система обращается реже, поскольку при размеще­
нии больших объектов возможные потери производительности выше . Тем не менее, вполне до­
пустимо считать "управляемую динамическую память" единым регионом в памяти.
Глава 5. ЦИКЛ GущеСJSDваНl<tя объектов 255

Генерации объектов
Когда среда CLR uытзетея. найти недоступные объекты. зт(') не здачит, что будет
рассмотрев буJo\:.В.CЩЬНО"RВЖДblЙ объem:. размещеННЪ!Й в управляемой Д1Щамич:е~кой
пампти. ОчевИДШ~', "Что это требовало бы слишком МIIоrо Вpt:мени. особен;но в ре­
aJlЬЩill{ (т,е. БолыIх)) ПР:m:JОЖe.E:i:ИЯX.
ЧТОQЬ\ ОПТИМJр:ировать процесс, 1СЗЖ'ДЫЙ объeкr в динзмичес:rщй ПЗМЯn'l цриnи·
еы:вается опреДf.nеннОЙ "генерации". Идея ДОQ1'аточно ПРОС1:а,: чем ДО.IIbШе объект
существует' в ДW':Iамичёсжш IIaМЯТИ, тем более веРОЛТ}JО ТО, что вн дол:щен там '))
оставаться. Н<;lDfJИМер. объект. реализующий М.аiл (), будет находиться в ца.мяти
до тех пор, ПOlса протра.мма не аакоWnnся.. С другой стороны. оБЪeRТЫ. ~OTopыe
HeдmJHO размещены в динамической памЯТИ, ::вероятнее Bcero. станут вскоре недо­
СТИЖИМЫJ14И (н<шример, объекты. созданные в рамках об:na.с'ЦI ВИДИМОj::ТИ ~eтoдa).
При этих предполож~IШlX ШюfщЫЙ обгьент в дm'tами'iеской пам~ можно oтнec~
R oд.нa~ из ['Л(Щ},ЮЩИХ RaТeroрий.

• ThНo~pЦqWl О. Ноцые, 'ГОЛЪ1W что раамещенные объeRТh1. ~OTopыe еще rnmоrдз


ае n:peдJtазнаЧaJ]Н~1? ДДJJ использования в npoцессе сборки мусора.

• Генерация 1, Объекты, IOOТ0рые ~:а~режилиW gднy сборку мусора (т.е. были обо ­
значены Д:n~ испощ.ЗDВания в процессе сборКИ мусора. :но:не бъt.ли УДЩiе:ны
по той цричине. ~O в ДИНaм}f"lеско:И. пам.вт.iI оказалось достаточно Mef.-ra).
• П?иеРUЦWl. 2. Объекты. "пережцвпm.е" нес:щ)лыш сборак мусора.

СборЩИК мусора сначала рассматривает об"еКТБI генерации О. ЕсЛJ.r и резуль­


тате DЬtя:влениSl geнytx:ныx. объек'I.'O:В и 'соответствуюr.цeа чистки своБQДНОЙ памяти
ОJtaЗъшаетея. достаточно. вс.е оставпmeся об~wrы отноСЯтся к генерации 1.. Чтобы
понять, ttalc генерации объектов в:лияют в:а процесс сборки мусора, рассмотрите
рис. 5·.5. r.ne схематически пщ,ааано, I(IЩ HeкOTQPoe множество "выживших~ объек­
тов (А. в и Е) ге1J(~рацииО переводm'СЯ В сцедующую .генерацию после обновленп.а:
остальной части памяти.

... rенepвцtlя:О

Рис..5.5. ОБМКТkJI гeHepaц~ . O, которые ·переЖli1ЛИ" qборку ~COpal перехоДятк генерац\о1И 1

Ес.1JИ ,все объецтh.l ~Hep~ О уже рассмотрены. но памяти :все равно еще Не
ДОСТаТочно, то рассматррщается "достижимоcrь~ объею.'ов teнерадJШ 1и DЫПOЛНJ'.l­
е1'ся еборRЭ! мусора среди ~ТИХ Ьбъeкrnв . "ны1ивпllIе" объекты rtmерации 1 "ПePeXD-

1
r
256 Часть 11 . Язык программирования С#

дЯТ I~ генерации 2. Если сборщик мусора все еще требует дополнительной памяти,
тогда оцениваются объекты генерации 2. Здесь. если объект генерации 2 "выжива­
ет" в проце ссе сборки мусора, то такой объент сохраняет ПРШiадлежность к гене­
рации 2. поскольку это предел для генераций объектов .
Итан. с помощью назначения признака генерации объекта м в динамической
памяти более новые объекты (например. локальные переменные) будут удаляться
быстрее. тогда как старые объекты (такие как. например. объект приложения про­
граммы) будут "беспокоиться" значительно реже.

Тип System.GC
Библиотеки базовых классов предлагают тип класса Sys t e m. GC. который позво­
ляет программно взаимодействовать со сборщиком мусора. используя множество
статических членов указанного класса. Следует заметить. что непосредственно ис­
ПQльзовать этот тип в программном коде приходится очень редко (если приходится
вообще). Чаще всего члены типа Sy ste m.GC используется тогда, когда создаются
типы. использующие неуnравляемые ресурсы. В табл. 5. 1 предлагаются описания
некоторых членов этого масса (подробности можно найти в доиументации .NET
Framework 2 .0 SDK).

Таблица 5.1. "Избранные" члены типа Sy ste m.GC

ЧлеКЬI System.GC Описание

AddMe mo r y Pr ess u re () , Позволяют указать числовое значение, характеризующее


Re moveMemoryPre ssu r e( ) " срочность" вызова процесса сборки мусора . ЭТИ методы
должны изменять уровень ·давления " согласованно (в частно­
сти, удаляемая величина не должна превышать добавленную)

Coll ect () Вынуждает GC выполнить сборку мусора

Col lect l onCount () Возвращает числовое значение, указывающее, сколько раз


" выживала " данная генерация при сборке мусора

GetGene ra t. io n () Возвращает информацию о генерации, ~ которой в HacToR-


щий момент ОТНОСИТСя объект

GetTotalMemory () Возвращает оценку объема памяти (в байтах), выделенной


ДЛЯ управляемой динамической памяти в настоящий момент.
Логический параметр указывает, должен ли вызов ждать на­
чала сборки мусора , чтобы возвратить результат

MaxGeneratio n Возвращает максимум для числа генераций, ПОРдерживае­


мых в системе. В Мiсrоsоft .NEТ 2.0, предполагается суще­
ствование трех генераций (О. 1 и2)
Suррrе s s F iла l i zе ( ) Устанввливает индикатор того, что данный объект не должен
вызывать свой метод Finali ze ()
Wait ForPendi ng Finalizers() Приостанавливает выполнение текущего потока, пока не бу­
дут отработаны все объекты, предусматривающие финализа­
цию . Этот метод обычно вызывается ~Iепосредственно после
вызова GC .Collect ()
РассмоТрите следующиЙ метод Main () , в JtоторамИЛlllOGтрируетсв иcnолыюва­
ние'указанных членов System.GG.
static void Main(st.ringLJ ~rg$)
[
11 ВSiIЗoд аЦевхи Св 68Й'1'a.JC;) .;ЦnR.диiIa.J&:И"«ескОЙ пa:мwrи.
ссЩ'sоlе.Writе:l>iпе ("OЦ~Hxa Qбъе~а ламя',l'~ (В 5аЙТ$iХ'): LOl ",
GC. GеtТоtаlМemoг;;, Cfal,ae) } I
11 Qu.Clie'r ДnJl Мax:Generation начниаВ'rСR с ИуЛII,
IIПQЗФQNY д.пJl у.цобсп& доtSasnиeм 1.
С(Шsоlе. Wr.i"leLine ("~сл:о генерaJ.lИ:Й ДЛЯ ЦaвRПИ ос; '{О} \п,1I I
CGC • МaxGenera tio.n + 1));

Са!' rеГГом.уСаr = n~W Car ("2.ippy", 100.);


Con'Sole. WriteLine (refTQ}jyCar . ТоВt.r:,iлg О) ;

11 IWaQII'ииf1ОРиации о Z'еиерiЩИИ II'ЛR o&ьe:no& :c-еflоМуСаr.


Console .Wr i t€П;,iпе С"г.зне.ра:tJ,Иf.( re fto:r-~уСаr: {О}".
GC. GetGenera tion vreiToMyCalr) ) i
Ссшзаlе. R"шdLinе () ;

Активизация сборки мусора


И'гак. сборIЦИJ( мусора в .NEТ ПрИ3ЩЦ-l управлять памятью за вас, ОДЯщ!",Q s
очень редкщ cдy"la.нx, пере'nl€.JIешIык нnяte.. бывает выгоДЕ:Ю программло aI\ТИВН­
зировать JiЩчало сборЮI мусора. используя lWЯ :i;3TOrO GC. C'ollec::t (1 .
• Перед входом !IРИJ10Женяя в блан программного J('эда. для !(О1"ОРОГО нежела-
1'елыIO, mобь1 еТ9 m,nrолне:НИе прер:ьmало~ возможной сборкой мv~pa.

• после ОRQНЧ:Uш.я размещеъnщ очень болъшоrо чисJIа объектов. :когда вы же­


лаете освобоДйТЬ WlК можно бoл:ыn:е памяти.

Еслд вы сочтете, ЧТО будет ВЫТОДНО выпоJU.IИть сборку мусора:, вы можете явно
'Начать процесс сборни муоора. так, Kfili и(щазано шtЖe.

stati с voi d Мain (в"triлq [] .args.)


{
, ..
1/ Ax-зтвиsация.с60РJtН мусора и
11 ОJICИ'ДaJ(Нe зuершеИИ5! фJlИВЛИ!lации Об'Ъе1:'1'О:В.
t;:c. ~ol1Bct () ;
(ЬС " W'аi:tF'оrРепс!i.ngFi~iаl i zе:t;я () ;

При непосредстяенной активизации сБОРRИ мусора вы должны в:ы:звать


GC.WаitFоrРепd.i:rщFil1.а.lizеJ:'S (). в рамкахэтоrо подхода вы мож.ете быть увере­
ны, ЧТО:ВСе лре.цуаматpnвз:ющие фmiaлизацию оБЪенты обязательно ЫОлу'Та!т воз­
можнос1Ъ выnол:ни'ГЬ вее необхоДИМlJlе завершающие деиствия, прежде чем ваша
пр.Огра....rмa продOJIЖитсвою работу. ~3a :НУJШсшми" GC. 1reit1'оrРепdi.ngFШаliz,еrв ()
-
258 Часть 11 . Язык программирования С#

приостановит вьrnолнение вызывающего "потока" на время процесса сборки мусо­


ра. Это гарантирует. что ваш программный I<ОД не сможет вызвать метод объекта,
уничтожаемого в данный момент.
Методу GC.Collect () можно передать числовое значение, указывающее старей­
шую генерацию. для которой должна быть вьmолнена сборка мусора. Нацример,
если вы желаете сообщить CLR, что следует рассмотреть только объекты генера­
ции О, вы должны напечатать следующее.

static void Main(string[j args)

/ / Расскотре'1'Ъ '1'onъxo объеJt'l'V генерации О.


GC.Collect(O) ;
GC.WaitForPendingFinalizers();

Подобно любой сборн:е мусора, вызов GC.Collect() ПОВЫСИТ статус выживших


генераций . Предположим. например. что наш метод Ма i n () обновлен так. как по­
казано ниже.

static void Main(string[] args)


{
Console.WrlteLine("***** Забавы с System.GC *****\п");

/ / Вывод ИКфОРlO:ации об 06_е дииaюiчесхой памя:'l'И.


Console.WriteLine("OueHKa объема памяти (в байтах}: {О}",
GC.GetTotalMemory(false)) ;

/ / Отсчет ДJlII МaxGeneration начинае'1'СII с нуnя.


Console. Wri teLine ("Число генераций для данной ОС: {О) \п",
(GC.MaxGeneration + 1));

Саг геЕТоМуСаг = пеw Car("Zippy", 100);


Console .WriteLine(refToMyC ar.ToString());

/ / ВЫВОД инфОРlO:ации о генерации ДJIII объекта refToNyCar.


Соп sоlе.WгitеL iпе ("\пГенерация refToMyCar: [О}",
GC.GetGeneration(refToMyCar) );

/ / Со~даиие тысяч объех'1'О. С цепью тес~оsаииll.


object[] ton sOfOb jects = new object[50000];
for (int i = О; i < 50000; i++ )
tonsOfObjects [i] = new object();

/ / Сборха "усора '1'OnЪKO для объек'1'ОВ генерации О.


GC.Collect(O);
GC.WaitForPendingFinalizers();

/ / ВЫвод инфОРIO:&ции О генерации Дnll объекта refТoМyCar.


Сопsоlе.WritеLin.е("Генераuия refToMyCar: {О}",
GC.GetGeneration(refToMyCar)) ;
Глава 5. Цикл сущеС,Т~DМНИЯ обl,еК10а 259
1/ Црове.р,., "DIВ".пи Об>Ъе1СТ ~опеО:fОЬjесtsf9000] .
i f (tDflsO"ftJbjec-ts I 900'0 ]! = щ.йlj

С9Л ·SGl ,~. WL ite Line ("геН.ерац.и," tо~эQfОЬjе:сts О;ЮОО]; {оу",


GC. Gозt GереХ' аti Ьn, ( t ОQsОJЮЬj ects [900 '(') J J. J ;

е 1 ;,.~
Cort s ole. Wr.i teL iТl'e ("Щ;ъекта tonьOfObjects [9(00) уЖе нет.") ;

11 Выво;ц ~CJП~ процедур сборки Мусора д.11я ;reнe~.


·C;eoGSL,le. WГJ.tеI, iг.е 1" '.'nдJ11J r ·!,HI. О .сбор«:а НЬillо.nн.я.nась j '(}l' раз (а). П,
GC . :СС,llесtiолСоunt ('(1) ) ;
СQщю:1Jе.WritеLiпе{"·Д:,ш ген. J С'берка Еы1олнял;;tс'ьь (О} ра.з· (а.),",
GС.• Соll'есtiоrr.соцnt (1»):
СОn50 1 е .Writ~ LJ: r1,е("Дi1Я V:6H. 2 сберка БЫпо.nm)Л<:lСЬ (О ) раз(аJ",
GC. Соl1е0ti'БJl1СQunt (.2) ,) ;
СаП$оlе. RеаdLiпе (') ~

Эдесь. мы намеренно создали очеlГь большой массцв объектов с: целью Tec11ipO-


вания. ~ еледу~I' из в;ывода. no~a;gаlШО1"ОШ pflC..5.6. хотя метод MaiIJ О дМает
всего ОДШ-i явIIый ЗЩIpОС ,на сборку мусора. среда CLR ВЫЩЩНЯЮТ Це.ПЫ:К ряд опе­
раций ~БQРКИ мусора в фонавQМ режиме.

Рис. 5.6. ВзаИМQдействие сосБЩ>ЩИК(J'М мусора CLJI через Sy5tem..GC

к этому момен.ту, я IIaДеюCh, вы Унснили себе некоторые дe~1Цi, 1'асaIOщНесn


цикла существования объектов·, Остаток З1'ой глэв:ыI мы J;IOСВЯТ~ дальнейшему
изучению пр'оц~сса сБОРJц.t :мусора, выяснив, RaJ( МОЖНQ СТРОИТЬ объекты. пред.­
усматривающие фИ8alЦIзаи~. и объекты. npеiЦYсматривающи:е ос:вобожден:ие ре­
сурсов. Следует замет~'IЪ, что оосуждаюЩйЙся Jffiже ПОдход МQщет Бы'гI:i IЮJЮЗен
'l'ОЛЪRО при построении упрa:вл.f.iемы:8: классОВ с подцержкой ВНУТРе;ИИИХ веу:пр::m­
л.я.емых ресурсов,

Исходный 1101;\. Проект SimpleGC рэзмещlЭl'J в ПО;Ц!!iэталоге, соотвеТётеуюЩем главе 5.


--
260 Часть 11. Язык программирования С#

Создание объектов, предусматривающих


финализацию
в главе 3 говорилосъ о том, что главный базовый класс .NEТ, System.Object,
определяет виртуальный метод с именем F i n а 1 i z е () (метод деструктора).
Реализация этого метода, заданная по умолчанию, не делает ничего.

// System.Object
public class Object
(

protected virtual void Finalize() {}

Переопределяя Finalize () в своем пользовательском классе, вы создаете про­


граммную логику "уборки~, необходимую для Вашего типа. Поскольну этот Член
определяется. как protected, непосредственно вызвать метод Finali ze () объек­
та будет невозможно. Метод Finalize () объекта вызывается СборЩИRОМ мусора
перед удалением объекта из памяти (если, конечно, этот метод объектом поддер­
живается) .
Ясно, что обращение к Finalize () происходит и в процессе "естественной"
сборки мусора, и в случае программной активизации сборки мусора с помощью
GC.Collect(). Кроме того, метод деструктора типа будет автоматически Бызван
тогда. когда выгружается из памяти домен пршюженuя. содержащий вьmолняе­
мое приложение. Вы, возможно, знаете , что домены приложений используются для
размещения Быnолняемого компоновочного блока и необходимых для него внеш­
них библиотек программного кода. Если вы еще не знакомы с этим понятием .NEТ.
то всю н:еОбходимую информацию вам предоставит глава 13. Здесь главное то, что
при выrрузке из памяти домена приложения среда CLR автоматически вызывает
деструкторы для каждого из предУсматривающих финализацию объектов. создан­
ных в процессе вьшолнения программ:ы.

Теперь, неЗаБИСИМО от того, что может говорить вам ин IУИЦИЯ разработчика.


следует подчеркнуть, что большинству классов в С# не требуется никакой яв­
ной "уборки". При'-шна проста: если вапш тиIIы используют другие управляемые
объекты, то все, в конечном счете, будет обработано СборЩИRОМ мусора. Создавать
класс, который должен заниматься "уборкой", вам придется только тогда. когда
этот класс будет использовать неуnpавляемы.е ресурсы (например. прямой доступ
к дескрипторам файлов ОС, неynравляемыM базам данных или другим неynравля­
eMыM ресурсам). Вы, наверное, знаете. что нeynравдяемые ресурсы создаются в ре­
зультате npямого вызова АР! операционной системы с помощью Рlпvоkе (Р1аtfопn
[nvocation - обращение к W1атформе) или с помощью некоторых довольно слож­
HhlX сценариев взаимодействия СОМ. С учетом этого возникает следующее прави­
ло сборки мусора .

• ПравШlО. Необходимость переопределения Finalize () может возникать толь­


ко тогда, когда класс С# использует неуправляемые ресурсы посредством
PInvoke или при решении сложных задач взаимодействия с СОМ-объектами
(обычно с применением типа S y stem.Runtime. InteropServices.Marsha1).
Глвва 5, ЦИКЛ существов:ания объектов 261

Замечание. В mаве. З уже отмечaJ10СЬ, что не допускаетс\\ переопред.~nять Fina.l-i-ze·() с типами


CTPYКТYP~. Теперь э;rQ совеРШВ/;IНО :яс.JQ, ПОСКОЛ!jК)' CT'PYkiYPl:iI Явm!Ю)ТСfI типами, характериэуе·
мыии з~ачениI'IМИ. 11 они не размеЩдJQТСII в ДИliамицеской ламйти .

Переопределен~е System. ObJect. Finaliz.eO


в тех р~ДlЩХ случаях. котда создается Git- кла:с('_. использующий неуправлflе~е
ресурсы. нуяmо гара,е:тирова:rъ. что соответствуlOЩан память будет обрабаТЬ1I!аться
прогнозируемым образом. Лредп()J1OЖИМ, что вы создал:и масс MyResQurceWrapper.
использующий неуцра:вля6МЫЙ реС)1>С (КaRИМ бы ЭТQIТ pec.yp~ ни был). 11 вы X()'ЦiTe
переоuредeJlИ1Ъ Fir,a]"ize !) . КеМНОГО странно. но в С# вы не може::ге Д)IН эТого дс­
пользовать КЛЮ~ВОе слово override.
F,J.Oblic cla.ss MyR1i:\s{>I1rCeW2'apper
1:

1/ OIaиClJl/8 :кОКПИJUЩИИ!
pxotec:t'ed. overri!de v oi d Finalize (] { }
}

Чтобы в ПЩIЪэовате.льС;fЮМ тщ:rе юt&t:са переonр.едели:гь метод Finaiize (). въt


дол.жю.r исполъзовать .8 с# другой си;нта.в:сис деСТруктора. напоминающий СИQ'ral,­
сис деструктора 6 С++. ПJЖ"ЩНa В ТОМ. 'Л"Q при обработке деструктора КОМUWIя­
торОМ С# в метод 1"itJalize() автQмаТJ<rЧески добаБ.rrлются необходимые элементы
инфрастрymypы (ч'rO будет .прОД~ОffСТРИ:РOВlШО чуть позже).
ВОТ прим:ер ПОЯЬЗ0ва:rелъ~кого деструктора для M-уRеsо:urсе1fiJrарреr. который
при вызове rенерирует си,:темн.ьтЙ СИI'НaJJ. HCHO~ ЧТО это сделано ПCWnO'!И.тедЬВО
ДЛЯ примера. НaCTO~ деструктор ДЬлжен ТШIЫЮ освобождать неynравЛflеМЫе
ресурсы. а м.е взаимрдейство~тъ с членами дРугих управляемых объектов, по­
CEOJIЫ'\y вы не можете гараигироватъ. что ати объекты будут сущеС'ТВОВilТЬ в тот
момент. когда сборIЦИ1C мусора вызовет ваш метод Fiла 1 i ze .1) .
1/ Переопре.цenевие Sy-stem. Obj-ect. Fina.liz. () с NCnО'nЬ!lОВа:.киек
11 сиктаХС$da деС!rpу~тора.
clas:$ Му):{еэоurреWrарр:е'с
{
-муRеsоurсеwrарреr ()
{
11 ОсзodO*Jiевие KeyцpuтutfIIIiX pecvpc:oв.

(TOJIЪJl:O ДnRпринера.!)

Если рассмотреть этот Дf}СТpyRТОР с ПОМОЩЬЮ' i ldastn.exe. вы увидите. <{ТО КОМ­


ПИ.JlJПОР добав:.IIЯет lJРОГРаммныИ ход :контроля ошибок Проrраммный код BaдIerO
метода Fir11?liz€ () дом~щаеТея в рамки ·б.пmm try. Это делается ДJ1Я ВЫЛ5Il~rn1Н опе­
раТОРОВ. КQТopыe во, Время DЬi:Полне:вин MDIyr crенерироваТh ошибку (что формадь-­
но на$ъцшетс.я UC/QlЮ'l.цmeлы-юЙ еumуачueЙ или исключением), СОО'rве'J'СТВУЮЩИЙ
бло.к fiQally щрантируст. что метод Fi n,ilize () KiI8.Cca БУД~Т' ВblПошtен неаавиQИ.-

1
262 Чаоть 11. Язык программирования С#

мо от исключений, которые МОТУТ возникать в рамках try. Формальности CTPYК'l}'­


рированной обработки исключений будут рассмотрены в следУЮщей главе, а ПQIЩ
что проанализируйте следующее СIL-представление деструктора ДЛЯ С#-класса
MyResourceWrapper.
. method fami1y hidebysig virtua1 instance void
Finalize() cil managed

/ / Code size 13 (Oxd)


.maxstack 1
.try
!
1L 0000: ldc.i4 Ох4е20
11 0005: 1dc.i4 Ох3е8
11 ОООа: саН
void [mscorlib]System.Console: ;Beep(int32, int32)
IL OOOf: пор
Т1 0010: пор
11 0011: 1eave.s IL 001Ь
// end .try
finally
{
11 0013; 1darg. О
IL 0014:
са1! instance void [mscorlib]System.Object: :Finalize()
11 0019: пор
1L 001а: endfina11y
11 end hand1er
11 0ОlЬ: пор
I1 OOlc: ret
/ / end of method MyResourceWrapper: :Fina1ize

При тестировании тИпа MyResourceWrapper вы обнаружите. что завершение


работы приложения сопровождается системным звуковым сигналом, ПОСЕОЛЬКУ
среда CLR автоматичеСRИ вызывает деструкторы объектов при освобождении до­
менов приложениЙ.

static void Main(string[] args)


{
Conso1e.Write1ine("***** ЗабаЕЫ с деструкторами *****\п");
Conso1e. Wri te1ine ("Нажмите клавишу ввода для завершения работы") i
сопsо1е.Wгitе1iле("и вызова Fina1ize() сборщиком мусора");
Сопзо1е. Wri te1ine (" для объектов, предусматр:v.вающих Финализацию.") ;
Conso1e .RеаdLlпе () ;
MyResourceWrapper rw = new MyResourceWrapper () ;

Исходный код. Проект SimpleFinalize размещен в подкаталоге, соответствующем главе 5.


Глава 5. ЦИКЛ с~щ~ствоваI:tИR обl:otyJ(ТDВ 263

Детали процесса финализации


Чтобы не Делать лишней работы, следует помнить о ТОМ, что целью мето­
дэ. Fi па 1 i -ze () nвляется гарантия ОСl;lобожденин неynpавляе.мых ресурсов _тТ­
об~кта при C{)OpRe .мусора . Поато.му ЦР"И СО21Дaшrn. типа. -не исполъэующеrо не­
управля:е.l\1Ыt': элементы (а тa,lшя ситуация 61'ill:3ыиается вполне типичной). от
фиаалиэации будет :мало пользы. На с.амом деле, upи разраБО1'не своих ТШlов :ем
Д9ЛЖНЫ избе.гать использовЩIИ,В м-етода, FLHa.li ze () по ТОИ очень простой прпчи­
не.. ч.ТО финализация: требует в~мени.
При размещlШИИ объекта в управляемой динамичеС1:>ОЙ памяти среда ЦЬШ()Л}Iе­
йия автоматически определяет. цoддep~aeт ли эТот объе:кт тrальЗовательCJ<ИЙ ме­
ТОД F'ina 1.L>! е ( ) . Ееди Уlffi;3ЩIЫ:Ыn метод под.держиваетсн. та объект обозначаеТся.
!шк ·mреб'ующuЙ фиНaJ.Iuзo.:цWJ.. а :ука.за:геЛЬ .на этот объект сохранл:етar во вцутрен­
Ней очереди. котору10 наЗbl}3ают оч~редью фl.LНWlUзuцutL Очередь фина.rn1Эации
npе-]{~яет собой та5дицу. DOJЩ~'рЖШlaемую сборщииом мусо.ра и CDдерж.ащун>
все объекты. для :ЕЮТОРЫХ перед Удалениём иа динамической :ШL1'уЩТИ требуетея фll­
На.J1И3СЩИЯ.

RогЩl с точки зрения сборщина ~c.Dp-a прИХодИт время удалить объект из па­
мяти, проверJtЮТся ~демепты о<шред:и фпнал:изaцш.i, и соответсТItУЮlЦйЙ объект
I(ОПИРуетС'н из ДИW1Мичещюj) памнтй в дiJугую ynpавлнемую структуру. которую
.Называю-r Т'абдицей эяе.мeнmaв, OOemУПНЫх д,'IЯ фWtй:л.uза'Цi!tt. В Зт!J'r Мt'Iм.eHT CQS-
дает.ся отде;rtьный поток. з~д.аче;й которого я:вл.нется вызов метода Firialize () при
следуlOщей сборке .мусореl для каждр.го объекта из таблицы элементов. дoeтyuнъц
ДIIЯ фИЕ!али.зации.. С учетом ЭТОГО ста:новится..асно, что ДJlЯ ОRончатеДЫfQГО уни­
чтоmeния объекта ПО'1'ре6уетс.я Щl}I'. минимум две ЫJЮцедуры сбор~-и мусора.
ТакиМ образом. )}:OTj'f финали~ объек;та и гаравтирует. ЧТо. оБЖ1>Т сможет
освоБЩ:цлъ неуправляемые ресурсы, паевом природе .а.та працедура оК::t.3QIВается
ведетермиючщванной Н, :1\ С,вязи С nроис.ходтцими ·за 1:\Улисами" ДОПQЛlштеЛЬНЫ­
ми I1pоцессами, доcrа:п,}чна меДJJеннОЙ.

Создание объектов, П'редусматривающих


освобождение ресурсов
Поскольку многие веyupавляемы.е ресурсы ЛБЛЯЮТСЯ СТОЦЪ "дратоцеННЬJМи". что
их НyЖnQ' освободить как МQЖnо быстре~. предлarается рассмотреть еще ОДИН noд­
ХОД, исполъзуеМЫI1 цля "уборки· ресурсов об'Ье1СДl, В КR"JeCТB6. альтернативы пере­
определению F1 па l i ze О !(.ласс может реадизоnать иатерфейс IDi зроsa.hQe, !Хато­
РblЙ определает единст-веmrый метод. име1QЩ!1Й имя .D i spose {) .

tHJb'lic 111te r:f'", c.e ]])is.p .o sab'le


{
vo id Ызроае () ;
}

ЕСJП'I ВЫ I:Щ имеете опы,ты прОгра:м:миравания интерфейсов. ТQ в главе 7 вы най­


дете Щ'е иеоБходпмы1~ по.црОБНости. В сущности. интерфейс представляет собой
набор абстрuFГНЫХ чл~нов. которые мoryт поддерживаться KJlaCCQM или структу­
рой. Если BhI р.еалц~~те D<;>ЩIep1ifcкy интерфейса IDiзр0s.а Ы\:;. 1'0 предnолаrается..

,
1
.....

264 Часть 11. Язык программирова~IИЯ С#

что после завершения работы с объектом пользователь объекта должен вручную


вызвать oispose{) до того, как объектная ссылка "уйдет" из области видимости.
При таком подходе ваши объекты смогут выполнить всю необходимую "уборку·
неуправляемых ресурсов без размещения в очереди финализации и беэ ожидания
сборщика мусора, запускающего программную логику финализации класса.

Замечание. Интерфейс IDisposable может подцерживаться и типами структуры, и типами клас­


са (в отличие от переопределения Finalize (), которое годится только для типов класса).

Ниже показан обновленный Imacc MyResourceWrapper, который теперь реали-


зует IDisposable вместо переопределения System.Qbject.Finalize ().
// Реали~ация IDisposable.
public class MyResourceWrapper : IDisposable
(
/ / Пользователь объеХ'1'а должен вызвать этот метод
// перед завершенмем работы с объектом.
public void Dispose()
(
/ / Освобождение неуправлsrеиыx ресурсов.

// Освобождение других содержащиХСR объектов.

Обратите внимание на то, что метод Dispose {) отвечает не тольно за освобожде­


ние неуправляемых ресурсов типа. но и за вызов oi зро зе () для всех других содер­
жащихся в его распоряжении объектов, предполагающих освобождение ресурсов.
Б отличие от Finalize (), обращаться из метода Dispose () к другим управляемым
объектам вполне безопасно. Причина в том. что сборщик мусора не имеет никако­
го представления об интерфейсе IDisposable и никогда не вызывает Dispose ().
Поэтому. когда пользователь объекта вызывает указанный метод, объект все еще
существует в управляемой динамической памяти и имеет доступ ко всем другим
объектам. размещенным в динамической памяти, Логика вызова проста.

public class Program


(
static void Main ()
(
MyResourceWrapper rw new MyResourceWrapper();
rw.Dispose() ;
Console.ReadLine() ;

Конечно, перед попыткой вызвать Dispose () для объекта вы должны прове­


рить, что соответствующий тип поддерживает интерфейс IOisposable. Обычно
информацию об этом вы будете получать из документации .NEТ Frarnework 2.0
SDK, но это можно выяснить и программными средствами, используя ключевые
слова is или аз, пр именение которь1Х обсуждалось в главе 4.
Глаliа 5, ЦИКЛ сущеСТВО!r8НVIЯ а6ъектов265
publi\1:': clas';5J program
(
st""ti с:: void Ma.in ()
[
MyResou:tt;;eWrapper rw = пеw MyF'"esol1IceWt:apper () ;
н (rw.1з Шi$розе.hl€-]
rw. ])isрФ.sе () :
СGлsglе • .Е~еаdLin€ ();

ЭТот при:мер з~таВ.jIЯет ВI[''nGМНИTh еще ОДНО 11рaDИЛ:О раб.оты с типами, предпо­
лагаюЩИМи сборку мусора.

• ПраВtL;'lО, DБЛ1Ш1'ельно ,выьmа'йтеe D i s Р (1 S е () ДJLII moбого созданного вами


оБЪeRта. поддерживающего IIJis:poBBble, Если разраб6ТЧ}ffi класса решил
p-еалиЗО13ать ПО,JIДержку ме'I10да Dl s роае '( )., то' типу. скорее всего, еств что
"убпрат.ь" .

Снова окnючевом слове using в С#


Приабраб:от:rre управляемых объектов, реалиауюИI,ИX l'IНТерфейс Ibl sposBbl.e,
Боолне ТИПИЧНЫМ будет иеполъзовш.mе метОДОВ структурированной обработки ис­
КIIЮчemm (·СМ. главу 6), чтобы гарантировать вызов метода Disp.ese () даже при
ВО3FГишюве:нии иt.1Rлючителъ'ElblX: си:ryaц:ий в среде выпIIн!ши~~

static voi.d MaiH(stririgIJ Cj~·<ts)


i
ИуRеsо.urсеWrарреr rw = пеw MyOResQurceWrapper (1;
try
{
11 Испо.nъ.эо:аааие члене. rw,
}
fiПаllу
l
/ / ])i зро~е () вtJi'vвa$'Wcs acer~a, ес!!!ь ·ошиб!ЕИ Иn~ ~!I!.
rw.. ШБроае () ;

Этот пример npименения технолоllИИ "безопаевого ПРОI'раммирования~ 1IpeJq)a-


,сен, :но реальность тажОва, что лишъ немногие ~аэрабоТ'rnКИ готовы мириться с
:ПерCneRТИВОЙ noмeII(elmfl КаЖдого типа, предполагающего осв@бождeЮrе ресурсов,
в РaмRИблОВ'.а try!catchjfinally толыш для. ТШ'О, чrобы гарантировать вызов ме­
тода DispGs€ () . поэтаму дпя доcJ"ИЖtШИЯ тето же результата в С# предусмотрен на­
lIШoro бlШее удобны:И: синтаксис, реализуемый 'с помощью .шnочевarO СЛова JJsiHg.

static void Hai!! (strlпg [J аrgз)


I
/ / Dispose{) :вкD1:aae!I!CR аЗ'l'ОИ&'.1'ИЧесхн при ЭilXоде !за предem.1
1/ 06.паC!DН .!!Iи.циtdФСomo.r using.
u5ing !(MyF~s.oL1reeVJrapper· IW = 1'1ew MyReSOI.J.fceWrapper'(), J
-
266 Часть 11 . Язык программирования С#

11 ИСПОЛЬЗ'ование объекта rw.

Если с помощью ildasm.exe взглянуть на СIL- код метода Main (). то вы обнару­
жите. что синтаксис using на самом деле разворачивается в логику try/finally
с ожидаемым вызовом Dispose () .

. meth od private hidebysig static void Main(stri ng [] acgs) c il managed


{

. tcy
{

/ / end . try
finally
{

~L 0012: callvirt i nstance void


SimpleFinalize.МyResourceWrapper; ;Dispose()
/ / end r,andl е с

// end of method Prog cam;:Main

Замечание. При попытке применить using к объекту, не реализующему интерфейс IDisposable.


вы получите ошибку компиляции.

Этот синтаксис исключает необходимость применения "ручной укладки" объек­


тов в рамки программной логики try/finally. но. 1\ сожалению, ключевое слово
using в С# является двусмысленным (оно используется для указания пространств
имен и для вызова метода Dispose ()). Тем не менее, ДЛЛ типов .NEТ, предлагающих
интерфейс IDisposable. СИНТal\сическая конструкция using гарантирует автома­
тический вызов метода Dispose () при выходе из соответствующего БЛOl{а.

ИСХОДНЫЙ КОД. Проект SimpleDispose размещен в подкаталоге, соответствующем главе 5.

Создание типов, предусматривающих


освобождение ресурсов и финализацию
I{ этому моменту мы с вами обсудили два различных подхода в построении клас­
сов, способных освобождать свои внутре:нние неуправляемые ресурсы. С одной сто­
роны. можно переопределить System.Object.Finalize (). тогда БЫ будете уверены
в том, что объект непременно освободит ресурсы при сборке мусора, без какого
бы то ни было вмешательства пользователя. С другой стороны. можно реализовать
IDisposable. что обеспечит пользователю возможность освободить ресурсы после
завершения работы с объектом. Однако, если вызывающая сторона "забудет" вы-
Глава 5, Ц~КJII)у'щв~ТВDваНИil абъекюв 267
звать Di.'lp0Se О, неУПР;llщ~ем:ые ресурсы CMOI'Y'J' оставаться в памяти неопреде­
ленно дощгое время.
Вы можете ДОГ8iд"ЫВWГЫ:я.. что есть в~м~)жность RомР:идиvо.вать оба эти ПQДJ{()да
11 ОДНрм onpедеЩJйИИ RлаСса. Такая .Rqмб,И;Наци:я позвО.тщТ ИОПОЛЬЗ0Вать пр~ще­
СТЩ1 Qбеих мqдел:I'Й. Всли Ц9Щ>ЗО:ватель объеЮ'а ~!J? забуi}ет вщ3Вti'IЪ DispOB€ '() • ТО,
С ПРМОЩЬЮ вызова GC .S'uppressFiIla1izt3 О :вы можете ИНфОРМИPQватъ сборищк
~c()pa о ТОМ. что цpoцec~' фИl-I<J,I1И;3aдllИ CJIeдyeI' О1'меFJ;ИТ1!. Ес,з:щ П9дЬзоватeIIЬ объ­
екта 3l1будет вьшвать Di эр~ве () • то t)('h,effT, в J(онеч:н:ом C~~_ п()дв~ргн.ется. про­
ц<;дуре финалиэации при сБОРI{е мусора. Таи ИJIИ :инаLJе, внутреннщ~ в:еудравJШ~~
мые ресурсы абъеRтабу~'Т о(;'в060ждеPlbl. Ниже предлагается D'LJереДl;lОЙ в.ap\iМiT
MyResourpeW1Capper, :в 'I>QTOPOM теперь предусмотрены}>] финализa.цfЩ. и ООВQбо;rц­
дение РОСЛIСОВ.

1/ c,nоЖJU!Й JtОВ'1'ейНер ресуРСОВ.


p1.1bliCC' lаs',э МУRe;Юl1rс:;еWrаРР€l;
I
/I C6~x МYCQP& :аЫЗJilВ,ае'l! эmО!l1 Ne!t'OA З !I1OМ случае, :кo~a
II~onьaoBa~~i> объеХ!l1А забывае~ ~a~ь Di~pose()_
~ MyRescHlГceWrapper ()
,!
11 Ос:вобоqeнPS~ ВНУ'!!Реввих науnp~ peCYPCj)B .
1/ НЕ c.ne,цye':l! 'Вl;IЭ,EЗа'1'Ь Di.spose () ~я:. уnpавnя:eм:wrr: d,C5"&61t'I'OB.

/1 nOnьзоза.'1'em.. об'f,e~Фа. BWJU,lВae!1' з'Z'о'11 не!1'ОД ДJt,B '1'01'0, ч!!"обl,t


11 ха:к IIOЖ:RO БUС'Ррее осэобо,цить peaypGI.
f.'lib1 ic '1'01 с1 Di.spO$Eo!)
{
1/ ОСВОБОИАение аеуцрaвnя:еиwx ресУрСОВ.
I/ выз~ Щ.s:роs'е () д.mI'COiIt~CIi об>ьехшов,
1/ npедусиа-три:аaDЦЮt о.с:зобо-.цениа ресурсов.

1J ЕCJl'И пonъэо:ваoreJIЬ ВЫЭаа.п Dispose () ,


11 то фииaJ'Щ~ не HYJIIНa.
GС.S'uррrеssF'iлаll-zе (tills);

Обратите впи:м::пm,е:На то, что'в метод DiSР0зе () здесь д;О'бавлен вызов


GC.SuppressflnallzeCJ, информирующий ереду СШ о TqM, ЧТ(,J'теперь при сборке
:мусора не требуется вызывать деструктор, ПQСКО.ttьку неупраю.rяемые j.lecypcbl уже
освобожцевы с ПОМОЩЬЮ ПРО1'раммной ЛОГЮ'JJ Dispose ().

Формализованный шаблон освобождениSl ресурсов


ТlJRyщая р~ализацИJJ
MyRe$QlJTCeWrapper pa60-:Пlf!:Т вцодне приеМJre!t;щ. но H~,к9-
торые неДDстатки рна все; Jfte щ:ме~т. Во-первых. 1t8ЖДОМУ ин Mf;l'DAOB F inal i Z i" ( )
и Di.spose () приходитг..;я: очищать ОДНИ и те же неуцравляеr.1ЬТе ресурСы. Это, ир­
Re'UЩ, ведет к дубл:ироввлмIO программного КОЩl, ЧТО ,Усложняет задачу (11'0 под­
держки. ЛУЧIIIе всегО' опредe.JШТЬ приватную БСПО!\10гательную ФУВfЩЮO. ко1"Орая
вызъщ;:uшоьбы lfa,t1l;дым ~З ;ц.:вух ЭТЩХ методо:в.
-
268 Часть 11 . Язык программирования С#

Далее, следует убедиться в том, что метод F i n а 1 i z е () не пытается освобо­


ДИТЬ управляемые объекты. которые должны обрабатываться методом Dispose ().
Нсщонец. желательно гаранТировать. что пользователь объекта сможет многократ­
но вызывать методDispose () без понвления сообщений об ошибке. В настоящий
момент наш метод Dispose () таких гарантий не дает.
Для решенияуказэнных проблемы Мicrosoft определила формализованный ша­
блон для освобождения ресурсов, устанавливающий баланс между устойчивостью
к ошибкам. производительностью и простотой поддержки. Вот окончательная (и
аннотированная) версия MyResourceWrapper. ДЛЯ которой используется этот ~офи­
циалъный" шаблон.

public cla ss MyResourceWrapper : IDisposable


{
11 ИСПОnЬ9уе'l'СЯ: для: 'l'0I'0, чтобы выя:сНИ'1'Ь,
11 выsызался: nи метод
Dispose().
private bool disposed = false;

public void Dispose()


(
11 ВЫЗов нашеrо BCnOMora'1'enЬHoro кетода.
11 Значение .. true ~ ухазывае'l' на '1'0, что
11 ОЧИС'l'lCУ инмциировaзr поnьзова'l'еnь об<l.е1C'l'а.
СlеалtJр (true) ;

11 Запрет финa.JtиЗации.
GC.SuppressFinalize(this);

pri v a t e void С lеалUр(Ь ооl disposing)


{
11 Убеднися:, что ресурсы еще не освобождены.
if (!this.disposed)

11 Еcnи disposing р.ано true, освобоДИ'1'Ь


J1 все уnpаз.nя:еиые ресурсы.
i f (disposing)
(
11 Освобождение уnpaз.nя:еиых ресурсов.
}
11 Освобождение неynp&ИJIЯ:еиых ресурсов.

disposed = true;

-МуRеsоurсеWrарреr()
{
11 Вызов нашего вспомоrа'l'8ЛЬноrо метода.
11 Значение "false" уJCазывает на 'Ро, Ч'l'о
11 ОЧИС'1'ху инициировa.Jt СбоpщиJC мусора.
ОеапОр (false) ;
ГЛЩlЭ 5. Ц~fК.110уществования объектов 269
Обратите ВFIИNaRие на ТО, 'ЧТО T~.цepь МуRеi.IO:ю' се'iJrарреr сщредt'ЛЯет n:pиваТ­
!wйвcIJомогателыfblйм.етод с имен~мСlеаnUр () .. Если для ето ар1"JМfШ1'D. указано
t:cue (ИСТ:иFrn), ЭТО3Iiа'Щ;т, 'ЧТО' сборку му.сора ини .циировал пОJtЪэователъ объе~та.
и тогда ~l ДОЛЖНЫ ОСВОБОДИТЬ и управляемые. :и нeynра.вляе:мы..е pe<;ypcbI. Но ' если
·уборка" ищщииро-вана сВорIЦИ1tOМ мусора" ТО' при вызове eleanUp () следует}'l<a·
эать fa 15 э (лщпъ). чтобы внутре,нн.ме объекты не :ОСБобождалисъ (trос}tО'ЛЪRy мы не
можем rараитироваТJ;>. ~TO QНИ нее еще НaxQДЯТСЯ в памяти). Наконец. пgред вы­
ходом из Сlеi'),Л'LТj:.() 'Ч,IJ,е~-lJере,j'dеннан di s posed Jlогнческого типа устанавливается
равной t'X'U'e, чтобы Dispo5e () МОЖНО было вызывать МНОГОRратяо без ПОЯDЛеНИII
сообщений об OIIJ:Ц6~_

ИСХОДНЫМ 1tOA. Проект Fil)filizаЫеОisраsашlеСlass размещен !i ПQДкаталоге. QоomеТСТJlующем таве 5.

На, этом RЗ.IПе обсуЖдение того. "ав среда CLR управляет объектами с помощью
сБОРIm мусора. завершаетсв. ОСТaJIИСЬ нерассмотре1-IНЫМИ еще ряд, вопр.оеОJi. свя-
3~ с ;ПРОЦ~ССОМ сбор:iШ. мусора (это. 1ШIфимер. сл.абые ссылки и восстановле~
н:ие объектов,), :Но вы теперь име;:етедостаточно ЗНaJ-ШЙ Д.l.Lff т@го. чтобъr продолжить
и<.:1'.ледовmtиё ,Дa:m'I'ОЙ темы ,самостоятельна в удобное для вас врем>!!.

Резюме
Целью этой главы было обсуЖдение лроцеСQ3 (:QQPIW мусора. Бы мргди убед:~I'1'I:o­
СВ. что сБОРЩПН м.,vсора наЧИnает раБOJy TO)~O ТОI1Щ., JtQгда c-rаноm,IТC.fI :невоамО'ж­
НЬ1М выделить Clеобходимый рбрем уцрам.яемоЙ ДЩ'Ш_l'dИЧе"скоj{ ТJ.a.МЯТИ' (ПЛИ ROrдa
из памяти выrp.ужаеТС8 ЦOM~ дадного ир.илож.ещия). Eы Mo~eT~ бытъ уверены в
том, что сбоРJta мусора будет въщолнена в оптимальном pCi{ЦiМe с помощью соОт­
ветствующего алгоритма М1crosoft. использующето генерации о'БЪf\КТОВ, вторПЧ8:Ы€
uотоь."И для фюtализаЦJ{R 05ъе1\ТОВИ области уцравллеМQЙ ДИН<ЦilliчеСRОЙ naм:Rти l
предна.з.начtНliые длл pa.-змещенЩ! болъших об'!,t1R1'0В .
В этоi1 же Гдаве объя:Сlщется. Юlli С домощъю типа' класса $yst~m. GC в.ааи:мо­
.n:еЙствО'ватъ со ~БОРЩИ1{()"1\iI мусО'ра. Это требуется ПРPl С'О3ДЩiИИ ТИПОВ. ПРе>цусма­
-тривающих ф:анэлизацшо :nm;I О'СJЮБОЖJIeRJiе peeyp~O'~. 'JI.щы" Пj?еАУС'матриваю:щие
фt>шализвцюо. upедt:.'Т/ц!л1UO'Г сО'бой ющс,сш с п~реоцределенн;ым виртуалъным ме­
'ТОДОМ S у stещ .'Qbj ect, Fi !).a 1 iz:e .( }, RОJ'ОрЫЙ' (В 6)ЦIJrщемJ дО'лжен , обесIIeЧИ'I'Ь осво­
БОЖДt$:ие н<=ynравля:е.мых. ресурсов. Объекты, пред,усм~тривающие освобождение
ресурсов. п:влmoтся RП,аесами {ИЛИ СТРУR'ryрами;). 13 .котО'рьц реалиауетс,я интер­
фейс lDisposa.ble. В рa:мJЩК этого подхода ЦОJIbЗQl3а')'еJ:IЮ объекта l1редnзгается
открытый мехОД. 1w"I'ор.ы.й долщен быть ВЫЗВaF,I пщц,зоват~лем ДЛЯ ВЫПОJUirени.н
lЖ)"Треннffii "'уборки сразу же. 1ЩК только это пО'требу~я. НакОfJёц, ВЫ уЗ'li:.!'lJШ об
Ц

"О'фициальвом~ форм:amr.зован;вом шаблоне освобождmщя ресурсов. в Ъ"OтapGМ .ком­


БШПIР.УЮТСЯ оба )1I{азЩlНЫХ подхода.
ГЛАВА 6
Структурированная
обработка
u
исключении

Т ема этой rnaвы -. устранение аRОМалий в среде ~QЛНёНИЯ вшuего пр~_­


rpaммнoro .IiЮда С# с I10МОЩhЮ еmpуюnурцрованноu обрабоinlCU uCЮtюче~щu.
Вы УЗНаете о кmoЧеЕЫХ: славах С#.. 1tоторые ПоЭВоляЮТ ре:шать т-3.кие ~ацачJ.f (это
RiJИO~ецыеслава иу. cat_ch, thr:ow. fiщ:\llу), и вьщсните, в чем различие между
исключ-ени.я:ми:системно,о }'РОВНЯ и УРОВПЯ ПРИЛQЖени.я. Данное обсу.щцени€ мож­
но рассматривarгь. как введение в T~ СQ<tдэщ-rя. 110JJЬЗ0ватепьCJmX иск.1IlОЧ€ЕЩЙ. а
также IЩR кpa'Pl!:oe описание средств О'i'лIЩКИ Visua!. Studio 2005. Б основе RO<I'OPЫX:,
по сути. и: леЖlП обработка ИСRЛИJче}П1Й.

j Ода .ошибкам и 11СКJ1ючениям


,! Вне зависимости 01' Toro" что говорит нам Наша (Ч:tСТО преувeлwreндая~ 'самоо-
.1 ценна. абсолютно ИД€'~нЫХ програММИСТОБ не существует. Соадание прогрaм;Il.tН.о­
го обеспечения яв:лле1:сft СЛОЩН;ЫМ делом и поэтому даже самые л:yчдlilе програ:ммы
~IOгy-r иметь nробле.мы. Unш:да ЦРJ-IЧИEiОЙ npоблем. бывает nесоверweнный Про­
граммный 'Код (nапри;меР. выход за границы массива). В других c.nyч.a:лx nробле:ма
ВО:ШИК8.e'I" I!з-за непрamrnЬНЪЦ ,цaJ:ц-i:ыА' ВВОДИМЫХ ПО.iIbЭОВ<ll'еле:м (е:апрцмер. ~В(lД
иМеНИ В пttле Т~JIефоннoro номера). 6CJШ ВО3МDШНОС1Ъ обрабоn<P! l'Щ1;ЩХ дщrnыx :tre
была пре,пусмотрена в JIpЩЩfteющ. I-!езависимо м' причин ВОЗ1iИlCaIOЩИХ проблем.
реаульт.атом: всегДа оказьmаетr;я неПРедУсмотреННQ{} поведение nPИЛОЖeFIJ,tя. Чтрбы
очертить paм:кu п~гo оБСУ".мде;нин струюурйрованной. обработIO.I ДС'UlЮчите.т,п.­
HbIX ситуaI1ИЙ. П03-60льте СФОР:МУJDJРО:вать опрещ:ленин 'Трех термино~. часто не"
пОЛьзуемых при изучении соответствующих аномалий .

• ПРО2juJ.JИ.>иИЬ1е ОШибки. Это, попросту говоря, ошибки протраммuста.


Например. ПР1-t ИСГIOЛЬЗ0вании H3bll\a С++ без управляемых расширеНQЙ. если
вывд-гьb уКазатель NULL или эаБJ>IТЬ Оqj}СТИТЪ ВЫде1ie1rnyЮ память (6 резу.щ,та­
те чerо nPОИСХОР;ИТ "уТeчJ<а" памяти), 1'0 lЮЭНИlfaflТ npоrpа:ммвая оши.бкц .

• ПQЛЬЗЩlameл.ыжuе ошuбlC!J. В ОТ./ШЧИе от црогр2!ММНЫХ ошибок, ПОЛЬЗОБатеJIь­


CКkl' ~ о:ыmб:ки обычно ВОЭlШйmoт Б результате действий объеtt'tа, нспользую-
272 Часть 11. Язык программирования С#

щего приложение. а не в результате действий разработчика. Например. дей­


ствия пользователя, который вводит в текстовый блок строку "неправильного
вида". могут привести к возникновению ошибки. если ваш программный код
не в состоянии обработать такой ввод .

• Исключения. Исключения. или исключительные СИ'I}'ации. обычно проявля­


ются в виде аномалий среды выполнения. и очень трудно. если вообще воз­
можно, полностью учесть их все в процессе программирования приложения.

К возможным исключительtiым ситуациям относятся поIIЫТКИ соединиться с


базой данных, которой больше не существует, открыть поврежденный файл,
контактировать с компьютером, который выключен. Программист. как и ко­
нечные пользователи, имеют очень ограниченные возможности управления

в подобных "исключительных обстоятельствах".

С уч.етом данных определений должно быть ясно. что структурированная об ­


работка исключений в .NEТ предлагает подход, предназначенный для выявления
исключительных ситуаций в среде выполнения. Однако и в случае программных
и пользовательских ошибок, которые ускользнули от вашего внимания, среда CLR
зачастую генерирует соответствующее исключение, идентифИЦИрующее проблему.
Для этого библиотеки базовых массов .NEТ определяют целый ряд исключений, та­
ких как FormatExcept ion. IndexOutOfRangeExcept ion. FileNotFoundException,
ArgumentOutOfRangeException и т.д.
Перед тем как углубиться в детали. давайте определим формальную роль струк­
турироваmюй обработки ИСЮ1Ючений и выясним . чем такая обработка отличается
от тра,дшщонного подхода в деле исправления ошибок.

Замечание. Чтобы сделать при меры программнога кода, используемые в данной книге, как мож­
но более конкретными, в них обрабатываются не все возможные исключения, генерируемые
соответствующими методами из библиотек базовых классов. Приемы, представленные в этой
главе, вы можете исполЬзовать в своих реальных проектах по своему усмотрению.

Роль обработки исключений в .NET


До ПОЯВJlения .NEТ обработка ошибок в операционной системе Windows пред­
ставляла собой весьма запутанную смесь технологий. Многие программисты соз­
давали свою собственную логику обработки ошиБОК. используемую в контексте
приложения. Например, группа разработчиков могла определить набор числовых
констант, представляющих известные сбойные ситуации. и использовать эти кон­
станты в качестве возвращаемых значений метода. Для примера взгляните на сле­
дУЮЩИЙ фрагмент программного кода, созданного на языке С.

/* Типичный _ханизи учета ошибок в С. */


#define Е FILENOTFOUND 1000
int SomeFunction()
(
11 ПреДПОnО:IICИИ, что ВОЗКИJCJ1ё1. CИ'I'уа.циR, в реЗУnlaт&те
11 JCоторой возвращаеТСR cnедущее значекие.
return Е FILENOTFOUND;
ГЛ8!iа 6. СТРУКlуриро.ва~JtlаА Обра:ОIпка I1сключениJ1 273

int retVal = .Б-оmеF.uп сt iоn О;


Н (re t.Val == E:_F'ILE1'HOTFOUND)
pr iIl1Lf' ("l:1e найд е н ' ф.aй.D ••• " ) ;

ТaJ~ОЙ подход следует tIpизнать Дa.JreНИМ от идеа.цьНОГQ, ес.ци учеетъ ТОТ факJ.:, ЧТО
}(oнcтa:rera Е F1LENOTFOUND является mnnЬ1ЩСлОВЪ!М знач.еЫJ1~l, а не lmформаци'.
оIo-Iым агентом. предлалuощим решenие ВО3I-щщпей проблемы. В ИДt:~е хотелось
бы ПОJJY1lатъ название оши.бки, сообщение о ней и дрyryю полезную информaцmo
в ОЦНОМ псщете вполне опреде-.ленноrо вида (и имещш это предда,та;етса в рамках
СТРУН'IYPированного подхода 1\ обрабQТЕе псюцоч.еНИЙ).
В дополнение I{ приемам самого разраБОТЧИRa. Windows; АР! предлагает с~тнй
КОДО'В ощиБОI\. }toTopble поста.влщохс·я в виде 11- de f i па. HRiESQbl\ s, ТaRже в ВИД@
многочисленных вариаций булев.ых знач.ениЙ (bool. Э О'Qi, . VARIANT_B001 и -r:.д.) .
.многие разработЧJiЮi программ на язьше C+~ (а TaJVIre VВб) в ра:мках модеJШ СОМ
1IВHO ИJ.Uf ненвно npименнют оrpа:аиченыый набор стандарт.иых СОМ-интерфейt::о~
(например. ISupportErrotrJnfQ. IЕrrоrlлfо. IСrеаt6Ецor Infо ). чтобы предоста­
вить СОМ-клиеmy ~формацпI(> об ащибках.
Очевццной проб:лемой этОй уже устаревшей схемы .лВ:IIЯ.еТ(:jJ отсутствие ~им­
метрии. каждый из rroдхддов более или менее уклaдъmаеТСJ:l в р~и СВОеЙ &01-1-
жретной ТexElологии.. RоЮtР!':Тl{оrо лзыка И, В03можне. даже в p<1М1ll1 коюсретноrо
проекта. Чтобы подожить конец н~уемцоll4Y буйетву разнообразия, IIJIзтформа
.NEТ предлагает СТiil.НДартную техцодarцю генерироваFJИ~ и выявления ОШИ~(J~
ср(дыI выполнения: стру:ктурирозадную оf5работЕ;' ИСКТIЮчений - ~ОИ (S1ruсtш'€d
~ception Handling - ЭЕН).
nре.имущества предлагаемой технолщ'ии закдючается в ТОМ. что теперь раз­
работч.ики могут использовать в областц обрабопси ошиБОR унифици:ров~
nO,UQД, обдn1Й ДЛЯ все,х лзьщоs. обесцечиваюIЦЮt поддержку .NET Таким оБРС!:З(J~.
способ обработки ошибок в СИ 01taёiьшается С:J-штаксичесии аналогичным Т/щ()Ц' 00-
раБО1"lt€ и.вVВ . NEТ. и в С+" еynpaдlIяеJЩiIМИ расшдренитm. л.ри этом синт;шсис ,
ИGпom,аУ~МЫЙДЛ1J генерироеания и выявле.I-ШЯ ~СR.lJЮчеНИЙ между RОМnОНОВОЧ1Ц>I­
м'» бл'ОЩL:МИ и грщmц~ <;истем . тоже <щззьmаетс,J:t один<щовым (.И это .!{ВЛл~ся
ДРnOЛЩiтедЬЮilМ преимущесnIOМ) .
Еще ОДНИМ npеимущесТВQМ обработ.tm ИОЮIЮчений в .NEТ ЩilJЩетГJl ТО , что l3М:e­
сто передачи ПРОСТGГО ~защифрованного" ЧИСЛGвоrоздач~. иде.l:Ц~фИЦИРУIO'
щего проблещ . И'сЮ1Ючt:НИJII предСТаБJlffi9Т СQбой объеl\Т1!f;. содер:щащие Щ)fl:ЯТНО~
Че1[овеку описание ощиQни. а также цодробnyю"копию
U
содержимого стmщ ~ы­
зовов В момент ВОЗн;иI(нове1ЦП.l. исцлючитеЛЬRОЙ ситуации. ~ тому ,не вы имеете
возможность npедоставить RОF,!еч;80:МУ пользователю ссьщку с !Щресом URL" по но­
торой пощ>зователь можеr ПОлучдтЬ подробную л~формацию о соотвеТСТВy1Qщей
IIpоБJIеме.

Атомы обработки ИCКnЮ"Iений в .NEТ


При создании протра:м.м с; пр.имевением СТРУКТУРИРОВанной обработн;и ис­
l\ЛЮЧевий ц:рeдnолагаетс.Я ИСЩ)ЗIЬЗОВ,атъ сдедующие четыре вэаимосвяэавн~ эле­
:меБ'Т.а:

j
274 Часть 11. Язык программирования С#

• тип класса, который предоставляет подробную информацию о возникшей ис­


ключительной ситуации;

• член, который генерирует. или направляет (throw) вызьmающей стороне ЭК­


земпляр класса, соответствующего исключительной ситуации:

• блок программного кода вызьшающей стороны, в IЮТОРОМ был вызван гене­


рирующий исключение член;

• блок программного :кода вызывающей стороны, в котором выполняется 06-


раБQтка, или ЗQX6ат (catch), данного исключения.

В языке программирования С# предлагаются четыре ключевых слова (try,


catch, throw и finally), с помощью которых генерируются и обрабатываются ис­
ключения. ТИп, представляющий соответствующую прОблему, является классом,
ПРОИЗВОДНым от Systern.Exception (или его потомком). С учетом этого давайте вы­
ясним роль указанного базового класса.

Базовый класс System.Exception


Все ИСWIЮчения, определеШiые на уровне пользователя и системы, в конечном
счете получаются из базового класса System.Exception (который, в свою очередь,
получается из System.Object). Обратите ВНИМaIШе на то, что некоторые из ука­
занных ниже членов виртуальны и поэтому могут переопределяться производны­

митипами.

public class Exception ; ISerializable, Exception


{
public virtual IDictionary Data ( get;
protected Exception(SerializationInfo info,
StreamingContext context);
public Exception(string message, Exception innerException);
public Exception(string message);
public Exception();
public virtual Exception GеtВаsеЕхсерtiоп();
public virtual void GetObjectData(SerializationInfo info,
StreamingContext context);
public ЗузСет.Туре GetType();
protected int HResult { get; set;
public virtual string HelpLink { get; set; )
public System.Exception InnerException { get;
public virtual string Message { get; }
public virtual string Source ( get; set; )
public virtual string StackTrace { get; )
public MethodBase TargetSite { get; }
public override string ToString();

Как видите. многие свойства, определенные в Iслассе System.Exception, до­


ступны только для чтения. Причиной этого является тот простой факт, что про­
изводньrе типы обычно предУсматривают для каждого свойства значение по умол­
чанию (например. для типа IndexOutOfRangeException принитым по умолчанию
сообщением является "Index was outside the bounds of the апау·, т,е. "Выход индек­
са за границы массива") ..
[л&ва 6, СТРУПУРИРО6ВJoilJ<I!\ 06раfiiоп,а ИСКЛЮ'i.ВНI:IЙ 275

Заме'f8t\Ие.8 ,NEТ 2,0 Sys t,e,rn .Except iQn реализует интерфейс Exception, чтобы COOТBBf­
стаУk:Jщие ФУНКЦИОН0:sJ.,ные вММОЖflОСТИ !liыди ДОСl)lflНЫ неУfФ8МЯемому программномУ коду,

в табл. f).l предлагаются mшсания некоторых члеНQВ Sузtеm.!J}(серtlол.

Таблица 6.1. Основмые чJlен&! типа Эуstеm.ЕхсерtiQr,

СвоЙст.во

Data Добавnвно в .NE1 2д. ПреАЛзгеет КОл'l1еI<Ф1Ю пар Id1lочей J.I ЗI'Iг,'Iе~и.й (пред­
стаеSIf:МНУЮ об'\:i6КТQМ , рёализующим IDlctiana!:y), которая обеспе.Ч1l1вает
ДОГJолнительную ПО{lьзоваТВЛbG~Ю информацию Ю' данном ИСlQ'(ючвни,И,
По УМОЛ'lаfl~НQ эта '(QЛi;1еlЩ~JI' ЯВЛl\ется nуотой

fleipLj tJ k Возвращае" I'!дpec U'RL файла спращ~и с ьписан.VJем ошибки вО воех подроб­
н!>стЯl!:

InnerExcept iQI) ДОСТУПl'Ю толы(o д:11f1 4Тения. Может ИQf)QЛI:!ЗОВ~ПbQЯ ДЛЯ полуЧения инфор·
мации о r;rредыдуЩем ИСКЛЮчении 1111"1 ио~лючения)(, ставших причино.Й дан­
Hpro иr;КЛlOчеIoiИЯ . Заnиоь предыдуЩих ~lCl<ЛlOчеflИЙ Qсуществляется путем
пер~д~чи .и~ КОНСТРУКТ0РУ саМОе(} последнего ИGКЛЮЧЕЖUlя

Messa1iJ'€> Доступно тqлько ДЛЯ LfТвния. В.оз. вращает Teti:cтoBoB описзнивдаl'fliОЙ ошиб­
ки. Само сообщеltJllе об ·ощибке задается,. как пара:метр конструцора

S.OU-.f.ce ВОЭВРВJ.Щ!е-т имя компонOIIОЧМ(]J'О БЛQКii. генвРИРУlOщего ИCЖJ1Ю4ение

stac'k'I'.ra'c.e ДЩ:ТУПJЧО только ДНЯ чтения . Содержит mpoкy, идеНТli1ф\Щll1рУЮЩУЮ последоЩL­


TenbHQqfb ЩЗОВQВ" которые лривели]( .иСКЛЮ!.Jительноil Cl<rТ)'3Ц1#1. Ka~ B~ може­
те :Цогадатьс:я саМИJ это свойство окзаываетGR очень полезным д.nя ОТЛaдlG1

1\/jrgetS,i le Доступно только дЛЯ 4ТЭНИЛ, Е30звращает rип М'еt h;о;dБаs е, nредлагаlOЩИЙ
самую разную' инФорМ<:lLtию о мето;ае, который генерирOEUlЛ ИСfWЮЧ6rние
.(То9t.гiпg () будет идеНТИфИЦLllравgrь им~ соответОТВУlQщего метода)

Простейший при мер


Чтобы npодемо;нстрировать Нпольэу~ структурированной обрабОТI~J'! и~.ключе­
юпi, 11}'ЖНО СQздать ТИП, lWторш В llодходmдем еЩJуженИI1 MonreT reJ-Iериров"J,ТЬ
исшпочевие. I1ре:дположпм. что мы -создали I-Wfloe консольное nРИЛОЖСЩIе ~ ~e­
ReM Sim-рJ..еЕхсеэ-рt i!)I1. 'в ROTOpoм опрtrд€>ляютсн .ДВа типа класса.Саr (авТQ,МобlЩЪ)
.и Radiq (радио) . СIЩ.:Заннъre QТ.ношенмеом lIо:калиаации (l1as-a"). 1)щ Radi9 сщреде с
~T QДШI метод, .вщцочающий и RbШЮOчающm'i радио.

publlc c lass Rad-i 'o


j
р1.1Ы i (: v oi d !'urпОп ~ bool ал)
{
HIQn)
СО·Г.ЭQlе .WriteLine ("!'адИ опом ели ... ") ;
еlЗе
'C011s;;:>le. wr:i. teti ое ("И, т.ишина •.• ") :

:J
276 Часть 11. Язык программирования С#

в дополнение к использованию типа R.adio в рамках модели локализации/деле­


гирования, тип Car определяет следующее поведение . Если пользователь объекта
Car превысит предел ДЛЯ скорости (этот предел задается значением соответствую­
щего члена-константы). то " двигате.ilЬ взрывается" и объект Са'!: становится непри­
годным ДЛЯ использования, что выражается в соотвеТСТВУЮЩCi\1: изменении эна ..
чения члена-переменной типа bool с именем carIsDead (автомобиль разрушен).
Кроме того, тип Сас имеет несколько членов-переменных , представляющих теку­
тую скорость И "ласкательное имя", данное автомобилю пользователем, а также
несколько конструкторов. Вот полное определение этого типа (с соответствующи­
ми примечаниями).

public class Сас


[
11 KOHC'l'aн!l'a ,цлJl мах с_уна CJtОРОС'1'И.
public const int maxSpeed = 10 0 ;

11 данные ВНУ'1'реннего СОС!l'ОЯНИЯ.


private int currSpeed;
private string petNamei

1/ РаБО'l'ае'l' JtИ этот автомобиnъ?


private bool carIsDead;

11 в aJlтоиоби.nе ec!l'Jo радио.


private Radi o t h eMusi c Bo x n ew Radi o () ;

11 КОНС'1'рухторы.
public Сас () ()
public Car(string name. iпt c u rrSp)
{
currSpeed = currSp;
petName = пате;

public void CrankTunes(boQl statel


(
11 Запрос делегата ДЛЯ внутреннего объекта.
theMusicBox.TurnOn(sta t e) ;
I
11 Не перегрелся ЛИ aJlтомоби.nь?
public void Accelerate(int deLt a)
{
if (carIsDead)
Console. Wr i teLine (" (О) не работает .. . ". petName );
else

currSpeed += delta;
if (currSpeed > maxSpeed)
(
Console.WriteLine("(O I п ерегрел ся!", petName);
currSpeed О ;
carIsDead = t ru e;
Гпавв' 6. Структурированна~ обработка искmочений 217
еlзе
ton s.ole,WriteLirJ~("=> GшrrSрееd = {[)}", ~UI.tSpeed);

Теперь реа)IfJЗУем Т!:ЩОЙ метод Ма in О • в котором объект Сат превысит задЩ'i"


uyю мatocим:щ:ызую скорость (предСТ"dШ1еНRУIO t«:Iнст;штой maxSpeeo.).
s,t?:t.Jc чэ.id Mairl ~s:t:ri!Jg [] argsJ
!
Соы, о le. Wri t ,'i)Line ,( \, * ** -Создание :и испытание ав'Uомооил'Si .. ** " ~ ,;
Car mУСЭI' = пе.., Сах (nZippy", ?O,J;
myCar. CrarJkTun&s (trbe) ;

f'Ol' (il'lt i =0.; i '" 10; HT~


mуСэ r • Ас.сеl е :rвtoe ~1 О) ;
Сопsоlе. ReadLi:ne () ;

TOIДa мы увидим m:.щод. щщобный .noЮtЗанному на рис. 6.1.

Рис. 6.1. 06ъ.e1(Т Са r в дel~CТВ"IiI.

ГенеРИРОВ8' Rие исключений


Теперь. ~oJ'Дa тип Са!: фующионйрует. про.деМОi<lСТРИРУ~JI<1 ПРОС'геЩuий способ
reнерировaщfН ИСRjIioчевиИ.. Текущая реализация Acc:elerate () в~одит сообще­
ние об Qшuбне, lЦ:>I'дазначение СКОРОС'ГИ объекта Ca.t: с<гановится выше эадаш'(О.гG
npeдe.тra.

МодUф~руем ЭТQТ мeтqд TaIt. чтобы 0}1 генерировал ИСЮnО9.ение при попыт­
ке ИОЛ.ЬЗОВ~ПЩН увeJIИ.'ЧИ'lЪ CROpodn автОМООЮ1fl выше предусмотреННых ero соз­
да,те:лем дределов. Для э'Юго' нужно создать й скоnфигурироватъ ЭIfЗe.мrmяр .к.ласса
System.E.xception. установив значение доступного TO:lIbl\O дЛЯ чт~ния свойс,тва
Mes,s,age: с помощью :КOНCTpyRТopa класса. Чтобы отправить СОQтветстщтющий
ошиб~е оБЪеН'J' вызывающей стороне. используйч:е .ключевое слово С# th.IOW. ВОТ
шm может вь;irmщеть соответСТВУЮЩall JI<10ДИфИJ{8ШШ ~eтoдa Accelerate ().
278 Часть 11. Язык программирования С#

11 Теперь, еспи пользовате,пь увелИЧИТ скорость B~e maxSpeed,


1/ :rенерируется ИСJCJDDчение.
public void Accelerate(int delta)
{
if (carIsDead)
Console.WriteLine("{O} не работает . .. ", petName ) ;
else

currSpeed += delta,
if (currSpeed >= maxSpeed)
(
саrI sDead = true;
currSpeed = О;

11 Иcnользyj!те JtJIJDчевое слоно "throw" 1


11 чтобы генерировать ИСJtJIJDЧение.
throw new Exception(string.Format("{O} пе ре г ре лс я !",
petName) );

else
Console.WriteLine("=> Cur rSpeed (О)", currSpeed);

Прежде чем выяснить, кю~ вызьшающая сторона должна обрабатывать это ис­
ключение, отметим несколько интересных моментов. Во-первых, при генериро­
вании исключения только от вас зависит, что следует считать исключительной
ситуацией и когда должно быть сгенерировано соответствующее исключение.
Здесь мы предполагаем, что при попытке в программе увеличить скорость ав­
томобиля . который уже перестал функционировать, должен генерироваться тип
System.Exception, сообщающий, что метод Accelerate () не может продолжить
свою работу.
В качестве альтернативы можно реализовать такой метод Accelerate (), ко­
торый автоматически выполнит восстановление без генерирования исключения .
Вообще ТОВОрН, исключения должны генерироваться только тогда, когда выявля­
ются экстремальные ситуации (например, не найден необходимый файл, нет воз­
можности соединиться с базой данных и т.п.). Решение о том, что именно должно
вызьшать появление исключений, должно приниматься разработчиком, и вы, Ka:I<
разработчик, должны всегда помнить об этом. для наших примеров мы предпола­
гаем, что увеличение скорости нашего обреченного автомобиля выше допустимого
предела является вполне серьезной причиной для генерирования исключения.

Обработка исключений
Ввиду того. что теперь метод Accelerate () может генерировать исключение,
вызывающая сторона должна быть готова обработать такое исключение. При вы­
зове метода, способного генерировать исключение, вы должны использовать блок
try/catch . Приняв исключение, вы можете вызвать члены типа System.Exception
и прочитать подробную информацию о проблеме. Что вы будете делать с получен-
Глаl1а 6. СТРУКтУРИРf)~анная обраОон.а исключе",ии 279
ННIМИЩ.ШНЫМИ, зависит. в оснощюм:. ОТ вас. Вы можете noмeСТИ'IЪ соответству­
ющую ивформanию в файл отч!:та,эвnи~атъ ее в журнал рег.истраnии событи:й
Wmdo'ws. отправить ее по 'ЭЛfЖТРOIffiОЙ поч1,'~ aдми:ниt'трэ.торУ с:ш:темы иди пока-
3З.ТЬ сообщение с описщшем пробдемы конечному ПО.nьaoвaтeлIO. Здесь мы просто
вЫводим ннформациro в окно кщrСОJIИ.

11 О~раб6,»ц с,Генерироиаиноrо исltJ'IlOчeИИR.


$.t.atic voi:.d Mail1 (эи ing [] args)
{
Солsr::llе. Wri tеLiде (" * ~" Создаяие JI! Исm..ТТ·а"iие а.5~Q.I<dQ.биля .'*" ".) 1
Car ucyCa:t = new Сау ("'z{ppy", 20);
myCar . 'Cr адkТl,щеs (t:rueJ ;
1/ Ijpell.ьnrre2Pfe .цопус!t'ИИOI'О 'максикуна д:II5I СlCороС'Х'И,
11 ....>:1'·015'101 геи:ерирosа'Х'Ъ и;с:~е_
иу

f.or (i·n :t i = (1; i < 1 О.; i 'Н- )


!1'IyCar_AcceleIat:~ (10);

catcn CE::XceptlCme)
{
Сс>л·sоlе .. WriЬеLitrе("\nН'I< ОШиt~)I<:а! .. ·"' * "1;
Co.neole. W.r:iteLiQe ("'Ме'Год: J О} ", е . Tar:getSite) ;
Соn.зо~е_ Wr·l.teLine С'1 с tюбщениеl ~ 01 п, e.l.'1es·sage) I
С;@пsаlе.WritеLinе( "исq.ОЧНИУ.; (О)" ., e.,80urce);

/1 ОШибка обрабО'1'ана, IWПО.JIИ5le-rО51 сле-~ otle~op.


C011sQ.l e. WriteL·i.ne ("\n1<Т" :Выход из· оорqtСii::Jтqика ' lX'Скmr.,чени:Й """");
Сопsоlе.RегdLiПЕ: (') ;

БЛОR t 1: У представляет собой :набор операТОР0В, ~OTopыe MOгr'г генерировать


ие.ключения в ходе их вьm()шIt~НИЯ. Еслй об:наружщшет<;я 0'СКЛЮчение, поток ВЫ­
.поЛНfЯШН npограммы нaIIpaв:Jшется поЩtOд1lЩему бilIОRY CQt~h. С дрym'Й СТОРОЮ;I.
еClШ прогрaмм:нъrn I«Щ в ~aмкax блока try не reнерирует ИCКJIIQЧeI-ШЙ. блдк ca·t ;::h
папносТЫо nропусваеiI:.СЯ. и все прохоЩ:lТ "тихо И СDGRОЙ1'lО". На рис. 6.2 покаЗaI-i
ВЫВОД этой npогрaм:t\'lы.
КаБ Bцдwre. после обработки НСRJ-uoчения nPIЩожение может ПРОДDлжать свою
радо-ту. начиная с оператора. (:дедующего за блоком G;ё;ltch. В некОторых случаях
пскrnoчение может оказаться достаточно серьеэ};JЪЦ>1 Аля ТO~, чтобы прекрати'IЬ
ВЫnОJШение I!рилож~mIя. Но в БО.!Ihлхинстве случаев О~blБ8етсЛ возможным ис­
править 'СИ'I}'aцRЮ в рамках npограммной логи;ки о~ботчика ис:к.лioчениЙ. чтQбы
npиложение продолжило своЮ работу (хоти и с возмощны,:ми ограничениями фytrn­
ЦИОВШIЬВОСТЙ, если. RaIIpИ1\1ер. не удается установИ'1'Ь СQеДи;нение с :Удаленным :Ис­

точником данных).
-
280 Часть 11, Язык программирования С#

Рис. 6.2. Визуализация ошибок в рамках структурированной обработки исключений

Конфигурация состояния исключений


в настоящий момент конфигурация нanrего объекта System.Exception зада­
ется в методе Accelerate (). где устанавливается значение, приписьmаемое свой­
ству Message (через параметр констру.ктора). Но. как следУет из табл. 6.1. класс
E.x c eption предлагает ряд дополнительных членов (TargetSite . StackTrace.
HelpLink и Data). которые могут оказаться полезными:в процессе дальнейшего
анализа возникшей проблемы. Чтобы усовершенствовать наш пример. давайте
рассмотрим содержимое указанных членов.

Свойство TargetSite
Свойство System.Exception.TargetSite позволяет выяснить дополнительную
информацию о методе. генерирующем данное ИСIщючение. Кан поназано в пред·
ыдущем варианте метода Main (). при выводе значения TargetSet демонстриру­
ется возвращаемое значение . цмя и параметры метода. генерирующего данное ис­

ключение. НО TargetSite возвращает не просто строку. а строто типизированн}ЦЙ


объект system.Reflection.MethodBase. Этот тип содержит подробную информа­
цию о методе. породившем проблему, и том массе, который определяет данный ме­
тод. Для иллюстрации обновим предыдущую логику catch так. нан "оказано ниже.

static void Main (st ri ng [] a r gs)


{

11 в дейС'1'1lИ'1'еnькости TarqetSi te возвращает об'Ъе1t'1' MethodВase.


catch(E.xception е)
(
Сопsоlе.WritеLiпе(" \ п1о-** Оши бка! *** ");
Соп sоlе.WritеLiпе(" Имя ЧЛена : {О ) ", e.TargetSite):
Conso le. Wri teLi пе ( " Класс, определяющий метод: {О)",
e.TarqetSite.DeclarinqType ) ;
ГJ1эаа 6. GтрукrУРИРОSЭННQЯ обработка ИСXJ11С!'IЩIИ'й 281
Сол sо lе. Wr i t'еLi я;;> ( "'Тип чл еl-'.а : j 0' /" I
e.TargetSite.мemberТype);
(оrцюlе. i~r :i. t-еL in е ( " 'Сооl5Щ6Ю1 е; {fJ}" I е. Мэs sag'e ) ;
·С опSоlе. Wri t eL,i t(>! 1'" И6ТGЧни:!l' ; j 'О , ", е. Э,риУСЕ!.) ;

Con·s·ole. WritE"Lln e ('' '\0' ~ * Е\ЫХО Ц .из 'сюраеЮТЧИI<:а исключении **~ ") ;
тусаж: . Accelerate (10) ; 11 ~O ие УС:kОРИ'1' авmоиоlSюП.,
Con sole . R,e.a:ctL i ne () ;

На этот раз .вы исполЫ\vете свойство N·eth;oдВа Ё; е . Dec larlngTypE:. Ч'Iобы опре­
делить afiсоillЮ1'Ное Й::МЯ lШасса. (;генерировавmего оmиБRY (В дmшом случае &1'0
м'аса Зiшр1еЕхсерt ion .Ca I ). и СВЬЙС"I'J30 МеrпЬ€- rТуре об1.>еК1'8' Me -r:hо-dВа:sе.. чrобы
Щ{тп~[РОБать тип ПОРОДJ'IВшеr'о исключение члена (й том смысле, своиство
ЭТО или метод). На рис. 6.3 ПОRазан обновлен:ньrilвывод.

Рис.. 6.3. Получеfit1Е! jllliфОРМац~ о целевом объекте

сво.йство StackTrace
CвoikTBO SУ8 tе~I,-. Е х с ер t i.оп . 31:.ack Tr,.'ice llО:з.воля-ет И,JIевтифицироватъ серию
вызовов. которые привели R Т!IскmDчитеJIЬНОЙ СИl'уanии. Вы не ДОЛЖНЫ уетанав '
лива'l'Ь значение SE:ac kTr a c e. ПФt:RОJ1ЬПУ это Д'елас-тсл автоматически в· момент соз­
дания исключения, Для ИJ1ЛЮстраЦЮI uр€дt!олоЖИМ. что мы снова об~ОВИД1И лро­
граммный ~OД cat.ch .
С'дt .:.'h (E;xcept:Ume)
j

ЕГ"ilИ ВЫЛQЛНИ.ТЬ црограммутеперъ. то эы обнаружите. что на RоНСОЛЬ будет' вы­


веден след стека (Для ваш~г(J приложения lюмера cтpo~ й На3Занм папок, НО'rlечнф
же. могут быть ДРУГШIИ).

(; T·eJ;l: : a t S i mp l eEx c:iep t ior . ·r: ar . AJ::ce l .e.rat.e (I l'lt32 С1еl t e )


iл' с : '\шуаРР 5 \ezcept l '~}Л() \.,".а:1: , ':;'8 ': l,i.r.e 65
а:[. Ei<.c;'e pti О ! с3 . i\Vro . t4":1 i n (1
in с; \!'rJ усрр s\€<жсесрi:i [)r1э \арр . С $ ; l J л", 21
282 Часть 11 . ~3ЫK программирования С#

Строка. возвращаемая из StackTrace, сообщает последовательность вызовов,


которые привели к генерированию данного исключения. Обратите внимание на то,
что здесь последняя из возвращенных строк идентифицирует первый вызов в по­
следовательности, а строка с наивысшим номером идентифицирует проблемный
член. Эта информация может быть полезной при отладке приложения, поскольку
вы получаете возможность "пройти" путь до источника ошибки.

Свойство HelpLink
Свойства TargetSite и StackTrace ПОЗволяют получить информацию о данном
исключении программисту. но конечному пользователю зта информация мало что
дает. Бы уже видели, что для получения :информации, понятной обычному польэо­
вателю, можно использовать свойство System. Exception .Message. Б дополнение к
этому свойство HelpLink может указать адрес URLили стандартный файл справки
Windows, содержащий более подробную информацию.
По умолчанию значением свойства HelpLink является пустая строка. Чтобы
присвоить этому свойству некоторое значение. вы должны сделать это перед тем,
как будет сгенерирован ТИП System.Exception. Вот как можно соответствующим
образом изменить метод Car .Accelerate ().
public void Accelerate(int delta)
{
if (ca rIsDead)
Console.WriteLine{~{O) не работает ... ", petName);
else

currSpeed += delta;
if (currSpeed >= maxSpeed)
{
carIsDead = true;
currSpeed = О;
// Чтобы вызвать свойство HelpLink, перед оператором,
// генерир~ объект Exception, создается
/ / ЛОJC8JIЬная переиенная.
Exception ех =
new Exception (string.Format ("{О) перегрелся!", petName»);
ex.HelpLin.k = ''http://www.CarsRUs.com'';
throw ех;

else
Consol e .WriteL ine("=> CurrSpeed = {О}", curr.spe ed);

Логику catch теперь можно изменить. чтобы информация ссылки ВЫВОДИJ1ась


так. как ПОRaЗано ниже .

catch(Exception е)
{

С опsоlе.WritеLiпе("С о ответствующая справ~а: {О)", e.HelpLink);


ГЛЗRf) 6, Сrpуктурирманнвяобра60тка иС:КI1IОЧ8tJИ'Й 283

Свойство Data
Сврйство D.st.a объex:rа &уstеш.Ехсерtiол является новым.в .NEТ 2.0 и Ш!JЗВО;rn­
Ет Доб<щИТЬ D объект иcюnочеН$ Д;ОЦОJЩJiтельную информацию Д!IЯ ПОЛЬЗОЩ'lтеш,I
(шщример. штамп време}iИ иди ЧТQ-1'О цPYГOt;). Свойcnо Data возвращает объшц.
реализующий интерфейс с цмецем П)iсtior,d.rу. опредедеН1:{ЩЙ.з ПРОСТРЩiств{'
имеЕ System.Col1ectibn, РОЛЬ nрограмм.ировamш интерфейсов. кat< ц ПРОС1"рa}j­
стВО ИМеН System ,Соl1 ection. раесма:rрИЩU()ТСЯ :в следУЮщей главе. Сеiiчасже бу­
дет ДОСJ:аточ:но заметить.. что Н:ОJИеRДИЦ сдовэрей ПО3БО.ЩПот создавать МПOOlЩства
Щlач:ений:, :возвращаеМJ>IX IЮ значению ключа. PaccmotpI-J'Ге. щщример. CJlедyIOIIJYЩ
мощrфИКfЩИlO Mt'ТO~ Car .Accelerate О,

рнЬНс voicl AcceleTa.te (int del ta)


{
if (С.i;l,rlSDезd)
C':JC1S01e. Wr:.i tel,ine ('" {О} не рабоъ'ае'l' ..• ~ I реtNаюе);
else

currSpeed += d-el ta;


i f (C)fr.r SP€ied >= 1l1axSpeed)
{
.car IэDеаd = tTLIe; cLIrтSpeed = О;

./ 1 t;!'r'Qбbl В!;.1З1"<:IТЬ СьОЙСТВ() RelpLirJ).;, п.еред ОПЕра'тором"


/1 геRерируюш;им объе1l:'I' ExceptiO!J, 'Создае'L'СЯ
11 J'!@каЛЫ,lая перем:е1-mая.
Except i011 ех =
Ilе\~ ЬxceptioIJ /s't.ril1g. Fo:rmat (" «()} перElJ?Pелся ~", petName)) i
e~ . Не} pLink = "11t, tp; 11 www.CarsRUs.com " ;
1/ .СТО ;l;PIlI по.пъSОВа.~сJI:ИX .цaiКIIJIrX
/1 с описанием ОшибllCИ.
ех.Dаtа.АddС'·Дата и Время П •
зtг it'lg. :e;\;lrrnat (" АвтомоБИЛЬСJJОМа.пся {О 1". Di;lt€'T ime., Now) ) ;
ех.Dаtа.А<'id("IТРИЧИ1'lаl!, "У вас тлmелая fiюга.");
tbra~f ех;

}
else
СБJ1s.о1~. writеLiщо: (IТ=> Curr6peed = {О]", currSpeed);

Чтобы не воэннюIO проблем при (jПР~Цen:ении пар ~юnoч-аначение·. с ПОМОЩЬЮ


дирe'ltrИВы 115ing следует yI(азатъ пространство Юlен System.CDllectiCiri. посlФЛЬ-
10/ в файле. еодерm:аше:м КдЗСС с реализацией метода Ма iп о. мы собираемся И['­
пOJXЬЭОIЩТЬТlШ Dict.:Lonar yEnt:ry.

\Jls,ing S'yslsw. Collection$;

3атем нужно обловить J;IpОf'Vc.LММ:НYЮ J,югииу cat.ch для проверки того. ЧТОElНЗ:<lе­
ние, возвра1Цаемо.е свойством 'QаЦ'. не рi1шво щ;йl (анач~:ние лиll задается ПD умол­
чанию) Посл{'Э'того:мы испольауе~ CBP~CTBa Ееу и \1@1пе тида Diс.ti,щ,аrуЕntrу.
чтобы вывести ПОЛЫlOвцтеЛЬСКIJе ДЩ-UIЫе щ:t 1СОI'JСОЛЬ,
284 Часть 11. Язык программирования С#

catch (Exception е)

/ / По УИОJlчаНИI:I поле ДёUIJВIX ~YCTO, поэ'l'ОМУ провеРllен на null.


Со п sоlе.WritеLiпе(" \ п-> Пользовательские данные:");
i f (e.Data != null)
{
foreach (DictionaryEntry de in e.Data)
СОl1sо1е.WritеLiпе("-> (О); (l)", de.Key, de. Value);

С этими изменениями мы должны получить вывод, показанный на рис. 6.4.

Рис. 6.4. Получение пользовательских данных

Исходный код. Проект SimpleException размещен в подкаталоге, соответствующем главе б .

Исключения системного уровня


(System.SystemException)
Библиотеки базовых к.пассов .NEТ определяют множество классов, производных
от System.Exception. Пространство имен System определяет базовые объекты
ошибок. например ArgumentOutOfRangeExceptiOn. IndexOutOfRangeException.
StackOverflowException и Т.д. Дрyrие пространства иМен определяют ИСКlJю­
чения, отражающие поведение своих элементов (например, System. Drawing.
Printing определяет ИСЮIЮчения. возникающие при печати, System.IO-ИСЮIЮче­
нин ввода-вывода, Systern.Data- исключения, связанные с базами данных и т.д.).
ИСКlJючения, генерируемые общеязыковой средой выполнения (CLR), назьшают
исключениями системного уровня. Эти исключения считаются неустранимыми.
фатальными ошибками. Искmoчения системного уровня получаются непосред­
ственно из базового класса System.SysternException. являющегося произвадным
от System.Exception (который. в свою очередь, получается из System.Object).
public class SystemException : Exception
{
/ / РаЗJlИЧИJdе ХОНСТРУХТОРЫ.
Главэ 6, Стрvпурирова~l1,ая обраб.оТl(а И,С,l(лю~ений 285
Sys tem. $'ystВff1ЬХсерt1 оI1 не добав.тщт нич:erо НОВОГО, :кро­
с }'Ч'етОМ ТОГО, ЧТО ТШI
ме набора .конСтрукторов " у ва(: может ЦОЗНИRН)'ТЬ вопрос, почему SysteffiExcep,tion
ОJ(aзъmаетС:8 на ПFрвом месте,. Thавная uрачинi1- в тр;м, что если лодучеI1НЪ1Й тип
исЮrючення оказывается прОи.ЗВОД1:lЫМ Ot&ystem. , SystemEx-серtiоn. вы можете
утверЖдать. ч:r'О исключение сген~рярован:о (~редо.й В,bIIlDЩJения .NET. а не nро­
гра.ммным кодам въmоЛiЮеМDГО ПРИЛ,сщreння.

Исключени'Я уровня приложения


(Syst'e'm.ApplicationExoeption)
УЧИТрХвая то. чТО все И(';КlJЮчеmш .NE'I' .являЮТСЯ Tlma.-..n1 .класса. МООКН0 созда­
вan. СВОИ собственные ЯСlЩЮчения. учитывающие специфlffiy прйШiжения. ОДnaiФ
~иду ТоГО, ч-rQ базовый :класс Syst..e m.Бу s t еrilЕя (; ер t i Q [! предст~ исюnoче­
NИЯ. генерируе!\>Iые средой CL& Iш()ЛВ'е естествеllllO было бы предполшкить, -что
ПОJlьэоват~дъские IДС]WЮЧ~l-lИI'I J),ОЛЖЕ:Ы выводиться изтйпа E!ys tern . Exception, ЭтО'
Д'е~ствит-е~}:щ ВОЗМО1lЮiО. но пращ:и:ка.Д~шту€т сйои правИJта. по li.oTopым ПОЛЬЗQ­
вательСКF1t': Щ:ЩIlоченив: дучше ВЫВОДИТЬ из типа syS tem .App li caticJtJExc-ерtiол.

p,u blic сыз з Аррl l С'а t i Cfл Е1 хсерt; Lоt; E 1oiC",,&, t i 0i'1
j

Подо,6но SysIem E x c-ерt i оп , 'l'Иll Аррl iСд,t-iоrйж серt i l~Tj не опр~де.'[Яе·г ника­
ких цополнительных членов . :кроме набора ItIlliCTP)'It'YOpOB. С ТОЧJQJ зрения ф'УН;К~
ЦИОRaJll:;НОС-ТИ еД}ffiс-твенной цеm.ю Яу5t еm.Аррl :i с аt.iопЕ А с ~р ti,ОГl должна быть
IЩентифй:ка:ция nСIГоЧНИIW. (устрanим()й)оr:tIИБЮL При обрабОТИ::1;1 иск.щоче-ния, rro-
.л.учеmЮl·О ИЗ Syst-em. A-ррJ.iсаtiоnЕ хсе рtl.ол. ВhI можете nредпо.rrаг<!.тъ, что пр~ "
ЧИtiой m::щвлeJ{йл :исlVD0ч.ения БЫJI ,I1J'юграммный КОД ВblПол:внеиorо пр~ожеНДJl.
а lIе' бибщroтеки оа..з()вых классов .тт.

Создание пользовательских исключений, раз ...


BceTДi! e~TЪ в.оаможностъ r.енери-ровать ЭI\.3емIlШIР Sу:;; t 8 !!1 .Е КЕ; €рtiQЛ. чтобы
сигнализ:ировэть об ошиб.ке врем~lШ выполнения (как llOIЩ'ЗаЕО в нашеN! первом
прцмере). НО часто бъmа.ет выгоднее пос.троить строга тunUЗЩIOВaюще I.ЩСJlЮче­
,НиЕ• .которое пр~достави'i' уникальную информацию . харю"'теризую.ш;ущ данную
ытncре-tную 1Тробл:ему. Пре1trIоложим , например. что мы хQтим создать полыю­
вате.:ru,.С1юе ИСlщючение (с 1L'w:еВБМ Са!" 1з De аdЕ:к с е.рtj [:·л J, ореДС1'авJrя:Ющее ошиб­
JCY :цре-вышедИЯ скорости нашего обреченного автомобилл_ Первым делом эдесь
ДОЛЖЦО быть создание HOвoto :IOIасса из System .Рфрl i~, ati aoEx c ep ti :;;n (па corna-
шению. классы ис:.'tiПО"'lf::Нн])J имеЮl: суффикс "Exception", ЧТО в пtJреводе означает
··~СlWЮченИf: ·).

11 ЭII'О ПОJIЬЗОВS'1'e.nь'СJl:ОЕ! исю-..:ючевие предпаrа8'I,' Q1IИса:.ние


1/ U'1'ОМО6И:na, в.wшeД1l!ilГQ из сшров,.
p'j bliC с:l;эss Carl ,s Dea clE~_ception Аррli ~з t i о!!Е;(еi?-ьt i,~п
'{ }
286 Часть 11. Язык nрограммирования С#

Как и в случае любого другого класса, можно определить пользоватеЛЬСI~ие


члены, которые будут затем использоваться в блоке catcb в рамках програм­
мной логики вызовов. Точно тан же можно IIереопределить любые виртуальные
члены, определенные родительскими Rлассами . Например, можно реализовать
Car IsDeadException, переопределив виртуальное свойство Message.
public class CarIsDeadException : Appli cationException
(
private string messageDetails ;
public CarIsDeadException() { }
public CarIsDeadException(string message)
{
messageDetails = mеввач е ;

/ / Переоnpeдenение своЙс'.1'В& Exception. Message .


public override string Message
{
get
{
return string. Format ("С ооб щение об ошиб ке Car: (О}",
messageDetails);

Здесь тип CarIsDeadException предлагает приватный член (messageDeta ils),


представляющий информацию о теR)'Щем исключении. которая .может быть зада­
на с помощью пользовательского конструктора. Генерировать ошиБIiY с помощью
Acce lerate () очень просто. Здесь следует разместить. сконфигурировать и сгене­
рировать тип CarIsDeadException. а не общий тип System.Exception.
/ / Генерируем поnьsоватenьсхое исключение CarIsDeadException.
pub,l ic void Acc eler'ate (i nt del ta)
(

CarIsDeadEx c eption ех =
new CarIsDeadExcepti o n(string.Format("(Oj перегрелся!",
petName»);
ex.HelpLink = ''http://www.CarsRUs.cOJll'';
ех . Dаtа .Аdd("Дата и время",
striпg.Fо:rrnаt("Автомобиль сломалея {О}", DateTime.Now));
ех . Оа ta . Add ( "Причина", "У вас т яжелая нога. 11) ;
thriDw ех;

Чтобы выполнить явный захват поступившего исключения, блок catch сле1J;Y­


(', :рзменить для захвата конкретного тиuа CarIsDea dExcepti on (однако. с уче­
том того , что System.CarIsDeadExcep t ion является ПОТОМRом System.Exception,
можн о также выполнить захват объекта System.Exception общего вида).
Гl1вва 6., СiРУКТУРКРОВ1.\ННВЯ обрабЬ"OOl ИСllлю'·tеНИЙ 287
static void Маiл(stringlJ argaJ
1
catcb{CarIsDe~ception е)
f
1I О~раБQшка nос~шеrо ~.

'fenepb. послерассмотре}ЩН OCEOBНbliX ЭТЩIО.В процесса (',()здaюrя поJIЬ.З0ватель­


til,их ЙС1tJIючеНИЙ. воаникает вопрос: ltOГДа MO~eт пОтреБOIlат.ьг-в их ооздание? ~
правилО.. пОльэовательСJtИе ИeJQJIюче~ требуе'r~Я создавать талыш TOrдa. когда
ошибка непосредственно СЩlзана е 1шассом. ТIЩt.'РИpyIOщим эту ошибку (например.
пользовательский к:nacc Fi le может Tт~ep~pOBaTЬ ряд исключений. связаfIНЫX с
доступом l' фrolла.м. Щlасс Car может генер.иРОВ6'n' ряд искmочениЙ. ев,язанных
с рабо'tой авТО.щ}биля и т.д.), Тем t:aм:ыM вы обеспеЧиваете вЫЗЪiВаюl:ЦeЙ CТCiрDяе
.возможность Qбработюt AЦIожеетва ИСКЩОЧeIЩЙ на основе "индmщцуальн:ого под­
хода",

Соз.дание ПОnЬЗ0вате'льск.их исключении, два ...


тип: с а rIsDeadExcept lOn переопредеджт С.БоЙстваSyst:em. Exce:pt'ion . Messag е.
чтобы устано:ви.тъ пользоватеЛЪСI~ре сообщение об ОIШiбire.. Однако задачу М~ЖНО
упростить. установив РОДйТeJIЬскре свойство Me'$sage через входной ПВра1\iетр ЕDН­
етруктор,а. В резуль:rате нам не придется .делc;tТЪ НИЧeFО, кроме СJIeд,ующе:ю.

pJJblic с1 q sз саrI"ЭD~аdEхсер t i.оп : fI.ррlir:.аtiШ-lЕХlCерtiоп


{
public Car lsD€:adExceptilDn () {
риы1 icClir IsDеа:сffilюерt LOri (skrH'g J1lj~зs.аgе)
: base(mess,age~{ }

Обратите вцимание на ТО. ЧТО 'теперь мы не определяем СТРОRОВУЮ D.еремеFIFJYЮ


ЦШI npецставле-.l'lИJI t'OобщeIOЫ1 И ,не переопредетrем свойство Mes,&alJj.e. ВместО. зто·
ro lЮНСТРУ1\ТОРУ базрвог!) Ю1асса ПРОСТО. передаerся параметр, В PaмRax TaI<OГO под­
хода полъзоват~ди,й щraсс ИСRJIЮЧеНИSi .Ig1ещrtавляет собой неМJ:J'О1'им БOJl~е,
чем ЦJЩCС с ущща.iIЫIЫМ йме:нем" по.пучен:ныИ из Sуstеm.АррliсаtiОllЕ~'(:ерЦ()п
без КfЩЩ бы то ни был!;> '!iЛенов--перемetпb:iIX (и переonpеделеlЩЙ базрвоrо масса).
Не yдFЩiЦiRЙте<;:Ь. ~CЦil большинство Ba.ImfX пользователъс1tИX ЮI.а.ссов кс1tЛJQче­
ний .(еСЩI :не вее они) будет иметь такой простой вид. во МНРГИХ cлyчa.щr целью еоз­
ДaнI1.H пользоват{'льс.ftQГО. ис.itЛIOЧенин является не фymшиОНaлыIые возможности,
расширяющие возможности баао.вых влассов. а попучеюrе строго ц.мe~вaHн.oгo
muna. идеН:I'JlJф.ицирующеro прираду ВОЗНйl(шей ошибки.

Создание пользовательских "сключений, три!


Ее'ЩI Бы. ХQТ'Ите построить. "педантично To"ll!ыif. пользовательский КЩ1С(; }iC-
RJ1Jоче:ния. то создRННЬJ:Й ,ва:м:и тип должен cooweтCТВOIlRTb лyчIIIИ.М образцам , ис­
пользующим ИCЮIЮЧЩI'ЩЛ .NET, В чаctвости, ваше пользовательское 'ИСlQ]ЮЧel'I:ие
ДОj]ЖНО ПQДЧJЩЯТЪСf{ СЛ~ДУЮЩИМ1'ребованиям:
288 Часть 11. Язык программирования С#

• быть rrроизводным от Exception/ApplicationException;


• обозначаться атрибутом [System.serializable]:
• определять конструнтор. используемый по умолчанию;

• определять конструнтор. устанавливающий наследуемое свойство Message;


• определять конструктор. обрабатывающий Цвнутренние исключения";

• определять конструнтор. выполняющий сериализацию типа.

Пока что глубина ваших знаний .NET не позволяет вам понять роль атрибу­
тов и сериализации объектов. но сейчас зто и не важно. Соответствующие во­
просы будут рассмотрены позже. А в завершение обзора, посвященного вопросам
создания пользовательских исключеций, рассмотрите заключительный вариант
CarIsDeadException.
[Serializable]
public class CarIsDeadException : ApplicationException
{
public CarIsDeadException() { )
public CarIsDeadException(string message) base( message ) ( )
publi c CarIsDeadException(string message,
System.Exception inner) : Ьаэе( message, inner) ( )
protected CarIsDeadE.xception (
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Ser i alization.StreamingContext c ontext)
: Ьазе( info, context ) { }

Пользовательские исключения. соответствующие лучшим образцам програм ­


много кода .NEТ, на самом деле будут отличаться только именами, поэтому вам бу­
дет приятно узнать, что в Visual Studio 2005 предлагается шаблон программного
кода под названием "Exception" (рис. 6.5). с помощью которого автоматически ге­
нерируется новый класс исключения в соответствии с лучшими рекомендациями
.NEТ (шаблоны программного кода обсуждаются в главе 2).

Обработка множеств исключений


в простейшем варианте блок try имеет единственный блок catch. Но на прак­
тике часто вознинает ситуация, когда операторы в рамках блана try способны соз­
давать множество возможных исключений. Например, представьте себе, что ме­
тод Accelerate () дополнительно генерирует определенное библиотекой базовых
классов исключение ArgumentOutOfR.angeException. когда вы передаете методу
недопустимый параметр (мы предполагаем, что недопустимым считается любое
значение, меньшее нуля).

// прежде чем продоJIЖИ'1'Ь, проверим ,цопустимос'1'Ь аргумента.


public v oid Ac c elerate(inl delta)
{
i f (delta < О)
Lhrow new ArgumentOutOfRangeException(
"Скорость должна быть выше нуля!") ;
----------------------------------- ~ -- ~--

Глава 1>. СТРУКТУРИРОВЗliна1'l обработка ~СIQЛIQ'Iею\й 289


" .. : - ..=~ ".
.'С'

~tI ==

j~~ t~,~:!~,t~:_~:;tJE.~~2j:.!.;~Q~' J -..~k~ ~ i~- ~I


·.4_;~ .
'ti1 F7ttttt _ _~&;~~;Жt~~$:t'JI~
.~' ~ 1 i!;J~
~~

='filriil• :~it==='-'------1
~ Itread>
!iilw I
JOolIf
~ inde'W 1
., ~.~

!!tit"ШI4r1&{~{(i'~~f ~_~~_} . ~"~}!~~r~~~~~Ц\;fil .t'1 ~I


Рис. 6.5. Шаблон прorраММНОГQ кода Exceptior;J

Лоrmm c atch должна соотве:тСТSОИ8.ТЪ· каждому тицу ИCЮIIоq~НИЙ.

S'tatic voi.d fvI~ln( s tring[] args}


{

/ I ~~.oa ~ae'1'c.- _O*eC~O ис~.


try
(
f~r(int i = 'О; i < 10; 1++)
myCar. Ar::·c~lera·t~ (1 Щ ;

c.atc'n (CarlsDeadEx(:,epti0:n ·е.)


{
/1 Обработка CarIsDeadExeeption .

,c:at·ch (.Argumen,tO,utOfRangeEKception е)

\
1/ Обработха Argument()qtQfRa'ngeВ,xo~ption.•

Пр~ co~д~ множества РЛОRОВ patch следует учитывать ТО. чт.о сг.енер1'lр.о­
ванное ИСWIЮЧ;е1-IИ~ буд~ об~бОТIi1Щ М ПеРВЫМ подходящим" блокам aatch. Чтобы
поин.rъ, ЧТG l'8Щ)е "пepв.ыij Падх9~" блок catch. добавьте в предыдущий фраг­
ме;нт ПРОСР!!ММflОГО ICO,lG:t «,:ще Одщi блан 9ё;ttch. который будет обрабатывать все ис­
КJ}J9чешц1' JJoqle СаrlsDеаdЕ,хсерti 9Л и АrgumепtОutOfRaл.g еЕхс,ер t.iоn, въiпОЛВЮI
ЩiXВCJ,Т S.y~teJtl.1i1xcept ipn общеro вида.• ка;к ПОКНЗaRО ниже..
290 Часть 11. Язык программирования С#

11 Этот npоrpi!МNIWЙ хо,ц не ХОlШИJJируетс.!


stat,ic void Main (string [] args)

try
{
for(int i = О; i < 10; i++)
myCar.Acce1erate(10) ;

catch(Exception е)
{
/1 Обработх& всех ос~&nьиwx исхпючений?

catch(CarIsDeadException е)

l
11 Обработха CarIsDeadException.

catch(ArgumentOutOfRangeException е)
{
1/ Обрабо'J.'Х& ArgumentOutOfRangeException.

Такая ЛОГИI<а обработки исключений пораждает ошибки КОМШfЛЯЦИИ. Проблема


в том, что первый блок catch может обработать все, что оказывается npоизводным
отSystem. Exception, внлючая типы CarIsDeadException и ArgumentOutOfRange-
Exception. Таким обраэом, оставшиеся два блока catch оказываются просто не­
достижимыми!
Правило, ноторое следует использовать на пра:ктике, ЭaI<лючается в необхо­
димости размещать блоки catch тан:, чтобы первый блок соответствовал самому
"частному" исключению (т.е. самому младшему производному типу в цепочке на­
следования), а последний блок - самому "общему" (т.е. базовому классу данной це­
почки, в данном случае это System.Exception).
Поэтому если вы хотите определить оператор catch, который обработает все
ошибки после CarIsDeadException и ArgurnentOutOfRangeException, вы должны
записать следующее.

// Эorот проrpaюowй хо,ц будет СХОlШlU1ИpOв&н.


static void Main(string[] args)

try
(
for(int i = О; i < 10; i++)
myCar.Acce1erate(10);

catch(CarIsDeadException е)
{
/1 Обработх& CarIsDeadException.
Глава 6', СТРУК'ТУРИРU&анная обрабОТК<I ИС.kЛЮ<lI:1НИЙ 291
cat.ch (АrgQlТlелt.оut,Оf,Rаfl9"еЕхсе,рtiО1! е)
f
/I О~ра,БОТkа ArgumentoutO:fRangeException.

саесь (Ехоерti,:ш е)
{
/J Здесь будУТ обраCSо'l'З.JW все ocwanl.!We ВОSИОII!2Щ8 ИC!CJШЧ8RИJJ r
1/ reиеIрируеlOofE! ottepaTOP~ _piUOC&Х ~.
}

Общие операторы catch


в С# тащке щщдержмваеТСfJ "оt5щиИ~ ' блок c-atch. которыи не получаe'r ящra
объеRТ иaкmочеюur. генерирусмьdi данным членом,

/! Вдох са toh обще:I'O вида.


static void Main lэtriпg t-J args)
{

иу

I
1'orlint i = ' О; i " 1О; i-t+)
myCar.Accelerate(10) i

caJ:,ch

Сопsоlе. Wr i te1;in:e ("СлyiЧJlJЛОСЬ ч~'о--То уЖаСН:l\i>е ••• ·'};

., .
J
Очевидно. 'ЧТо Э1"О не самый щrформа~ныйспосо(j обрабоТЮI исклюЧения. по­
СIЮJIЬКУ здесь вы не им~те ВОЗм6ЩНОСТИ получитьсодержателъную ИНфQрмацию
о ИРОйзonreдшей ошибке (1Jапример, Иl\.fЯ метода. GодерЖИМОе cтeнaВlill3oВOB ИJ1И
пd.JIЬэователъсКое сооБU!ение)_ Тем :не ]4енее, в С# такая КОFlCтpyкцшi вwзможна.

Генерирование вторичных исключений


вы долж1lы знать, ЧТО в рамках логики try имеетC!l вовмфкноCТh генерировать
ИСШlЮчение для вызьmающей cтopoHы. ваходюдейс,я ВЫlDе по цeno-ч:кеВЫЭQВОВВ
стеке вызовов. для этого цросто исnoльзуйте ключеJWti слево throw в рамках блока
-ca'bch. Тем ~'ис.ш.uoчеЩ1е будет напрanлщ{о JIblПJе до цепочке.J1[)1'ИКИ вызовов.
что может быть полезно тогда. догда данный БЛщt catQb не имеет воЗМожно~,тй.
поmrocтью обработать воэюnanyю оШИбtq,

1/ Пepeюt_a,дJoIВ:aIDIе О!(IВете!1'ZSИВОС!1'И.
8'tatic l10id Main (stTing!! аrчs)

1
-
292 Часть 11. Язык программирования С#

try
(
11 Логика ускорения: ааТONоби.n •. ..

catch(CarIsDeadException е)
{
/ / ЧаС!1'ИЧНaJI обработка ОDlИбки и п.ренаправnение.
11 Зде~ перенаправn.еТСII ВХОДНОЙ объект CarIsDeadException.
11 НО МО*НО reHepкpoaa'l'Ь и дрyzoое ИСJCJШ~еиие.
throw е;

Вы должны понимать, что в данном примере программного кода I~онечным по­


лучателем CarIsDeadException является среда CLR. поскольку здесь вторичное
исключение генерируется методом Main (). Поэтому вашему конечному пользова­
телю будет представлено системное диалоговое окно с информацией об ошибке.
Как правило. вторично сгенерированное и частично обработанное исключение
предъявляется вызывающей стороне, IЮТОРая имеет возможность обработать его
более Ыграциозно".

Внутренние исключения
Вы можете догадываться. что вполне возможно генерировать исключения и во
время обработки другого иСКЛЮчения. Например, предположим. что вы обрабаты­
ваете CarIsDeadException в рамках конкретного блока
catch и в процессе обра­
ботки пытаетесь записать след стека в файл ca rErrors.txt на вашем диске С.

catch(CarIsDeadException е)
(
11 ПОШl'1:'ка OT1CpNТЬ фaйn carErrors. txt на диске С.
FileS t ream fs = File.Open(@"C:\carError s.txt", FileMo de.Ope n);

Если указанный файл на диске С не найден. попытка вызова File.Ope n () даст


в результате FileN otFoundExcepti on . Позже:мы рассмотрим пространство имен
System.IO и выясним. как перед открытием файла можно программными сред­
ствами проверить наличие файла на жестком диске (и предотвратить возможность
возникновения исключения). Однако здесь, чтобы сосредоточиться на теме исклю­
чений, :мы предполагаем, что исключение возникло.
Когда во время обработки одного исключения обнаруживается другое исклю­
чение, лучше всего записать новый объект исключения, как Ывнутреннее исклю­
чение" в рамках нового объекта того же типа, что и исходное исключение (язык
сломаеIIIЪ!). Причина, по которой мы должны создавать новый объект исключения,
заключается в том, что единственным способом документирования внутреннего
исключения оказывается использование параметров конструктора. Рассмотрите
следующий фрагмент программного кода.

..
Глава 6. СТР~КТУРИРD.ванна!l обработка ИСlf:ЛЮч~kий 293
catc:h (СjЭ.1.· IsDе..аdEхсерt·iОI1 е)
{
try
{
FileStrearn fS = File.Open/@"C:\c·a ,. rE·rrors.txt", FileJclode.Opt!n);

Ca,t CI1 (E*'cep.t i оп е2)


1
/I rеверирова1lИ& JSCкmoЧенИII I зanиCloПl~ro B()B~ ИCJUlШlаиие
1/ 1'4 соod-.юrе DepBoro JScкniDчевиа.
thr'ow n.ew CarIsDead.E.xcept.J:oo' (8 ,Message, е2):

3aм~;rbTe, что в Д~HHOM с.луЧafl мы передали объе1\Т Filе.NоtFQuл4ЕJI;серtiоn


конструктору CarIsDeadExc.ept.io-п в виде второго n~paмeтpa. С;конфигурuрЬвав
Э:ГР'r наnы;й объеК':Ц ~ НЩIp3ВЛЯeJ.t его' по стеку 'ВЫЗов()В СJ1tЩУlОщему ВЪ~ЗJ9ще­
му Qбъеюу, fI да:щщм случае по отношению н: мeT(jдY Main () .
с учетом тата, ЧТ0 после Main ( ) "следУЮЩИХ вызывающих 05ъентов· для Dб­
работки ИСIUJЮченцJ:! нет, мы снова должны увидеть ДИмorо:вое онна с сообще­
нием {)б ошибке. ,Во мнотом подобно rен.ериро:ванию в1'оричных ис&тючеюrn;, за­
ПИСЬ ВНУТР~IЩИX ИСКJIJPчений обычно ОICa3hIВ·ается полез~й только тогда. ~orдa
выьцщющий объект имеет :возмоЖliостъ ъ."Расиво обработать такое ИСКЛlOч~йИе.
В ЭТОj\,1 случае логина aatch выаывающей cTopoEы может u.СЦОJIЬзовать СВОЙСТВО
InnerExce:pt io.n дщI по.лученИя подрЬбнои информации об объек:ге внутреннего
исклю·щmи.

Блок finally
в рамках try/catch можнотаюке ОJiIРeд.eJlИТЪ1Iео6язат~Нl:itЙ блОК finally.
Зада:ча бnоХа. f1 nаllу - обеспечить безусл:овн.ре Вьщo.щieНИе HeКOToporo набора
операторов программного хоца. н:еза:виеимо "т налич;ия ищи OТCYТCTВ1m RСКЛЮЧ(i!­

mm: (любого типа).. для npимера предпцложим, ЧТО .I;Ibl хотите всегда выключать
радио а:втомоБШIЯ перед ВЫХОДОМ из Ма i n ( 1, независимо от исключений.

static void Маiп(зtri..ng[] ait'gs)


{

Car туСах = Dew Car("Zippy", 20);


myCar.CrankTunes (tr·LJ-е.) ;
t .:r;y
{

catcb iCe.r!s.DeaG!Exception е)
{
/1 ОбрабООМСiL Ca:rrsDe~ception.

datcb (Мrgu:rnеntОutСП~angеЕ.хСi\рtiОD е)
{
/I Odрабduа ugщnentoutОfRJлgdxсерtion .

1
--
294 Часть 11. Slзык программирования С#

catch(Exception е)
(
// Обработка всех остальных искmючеНИЙ.
)
finally
(
11 Э'l!о ВIUlOJIИя:ется: ВС8%'да. НезёUlИСИКО О'l! ИСJC.JUOчеНИЙ.
myCar.CrankTunes(false) ;

Если вы не включите в конструкцию блок finally, то радио не будет выклю­


чаться. когда обнаруживается исключение (что может быть или не быть пробле­
мой), В более реальном сценарии. кorдa приходится освобождать ресурсы объектов.
закрывать файлы. отключаться от баз данцых и т,д,. блок finally может гаранти­
ровать соответствующую "уборку".

Что и чем генерируется


с учетом того. что методы в .NEТ Framework MOryт генерировать любое число
исключений (В зависимости от обстоятельств). логичным кажется следУЮЩИЙ во­
прос: "как узнать. какие именно исключения могут генерироваться тем или иным
методом библиотеки базовых классов?" Ответ прост: это можно ВЫЯСНИТЬ в доку­
ментации .NEТ Framework 2.0 SDK В системе справки для каждого метода указа­
ны и исключения, которые может генерировать данный 'lЛен. В Visua1 Studio 2005
вам предлагается альтернативный, более быстрый вариант: чтобы увидеть список
всех исключений (если таковые имеются), генерируемых данным членом библиоте­
ки базовых классов. достаточно просто задержать указатель мыши на имени члена
в окне программнаго кода (рис. 6.6) .

• ' Program.cs*j . . . '. ~_,~ - х.

lii~&~:~~;;q:Sy;t~';;;' .,',.=;1 [~~~~.~~~~--_:~:_:_,,=--~=_.=~!


::I :J...S'i.nq Sу,!t:.е..YD.Ссllес't.iоnз.Gе-nеri,с; ~.\
:;:L з~ng
1
... SУЗl:eID" Tex:t; ~'~~~"i
--i ·..1~lng Sy:ttem. 10; !:i-~:I

~GI' ~.' аше"расе И;'Ар~ ~!IxiI


'··"'T_H\I'.
:
сlазs Р:r·\~,:;т.:url ·".·,. .~.,·.,.,'.·.I
",1

~ ~T ~lt"ИG ',c~d Main ("txing [ ) 4:rg"} ~I


. c'i ::.1" .OpenTexi[(".5"rneF~le. ~"",П): ,~I
:' I Stт~<OmRoader F~.QponT~xt{.trIng pa1h) 41
': [} CJPenS .., oxisting (fI'f -е mcoded text fIIe fbr readino. i
Exceptiom;, i
5ystem.Ю.tirесroryNoiFoundEхсерtюn
Sуstem.ArgumentNtilExЩ>tюn
System.l.hluthorl2~dд~ception
5vsten.No1SuppOrtedException
System,ArgumentExcepUO(1
Sy$tem.ю.р.thтooLonф<ception
system.!О.FiIeNoIfounс&серtюn

Рис. 6.6. ИдентификацИR исключений, генерируемых данным методом


...---------------------------,--,- ,

Глава 6, Структурмрованная Обработка ИСl(лючеflИИ '295


Тем программистам. IЮторые npиmли в ЦЗ?leниIО .NET. имен опыт работы с
Java. :важно понять. что <Шены типа не подyчцroт множ.естВо мсIt1ПOченш1 из npo-
ТОТШIа (дрyrими слоЩ!МИ . .NEТ не ПОддeIЩ(ИВает' типи.зацию ИСRЛIOчеЮlЙ). Поэтому
вам нет необходимости обраба-гывать а&отрТп6 вее иCJt.лЮченин, генерируемые
данным членам. во MH@ГJilX случаях МОЩНQ обрабо'ГЭТЪ .все ошиБЮl nyтe.м захват::!.
ТШIЪЕО SyS'b,em. ExceptioQ,

sti!-tic v{)id Ма1п (.s tri!)g []аюgs)


{
-try
f
1'i le. Open ("IDontBxist. txt" I FilaМo de. Operi) ;

cat сЬ (E'x'cepti,o n ех')

I
ConsQle. W'r1teLi)~,e (ех. Мessage),

ОДН<Шо если Rеоvхолш>lО обработать конкретные ИCЮIючения у:никаль~ об­


разом. ТО UPИДe'FCJ1 ИСПl!цrь~овать MHo~e:c-твo блоков catch, хан был.опоказано в
Данной главе.

Исключения, оставшиеся 'без обработки


Здесь вы можете сщюСИТЬ. что проиаоfщет- в том случае" если 1Je обработать
ИСlUlЮч.ецие, нanРЗJЩеmroе в ват адрес'? предположим. 'LTO ПРО1]Jам:м,вм ЛОFИlШ
"liiain () 'Увеличивает скорос'ТЪ объеь."Т8 Са:!:" выше МВ1<СИМaJIЪ.ноЯ: екорости в отсут­
ствие црограм~uщй ЛOI'.I(ШИ try/catch. Результат игиори::ров:ания ИСКJIЮчепия про­
граммой будr.т очень мешать Rонечnому ПОЛЬЗ0ватедю. пос:коль~ перед ио гла­
.эами пщmится Дщuюговое окно с информацией о "в.еобработа:mЮl\{ 'иCRЛ1bчеШш~.
Если на маПIF}i~е УСТ'dНовлеНЫИRструмевты отладки .NEТ, появитqr нечто ПDДОб'­
ное тому. что щща~ано на рис. Б.7 (МaIIIйIfa без среДСТ8 O'U[aДIOI дOll;ЖН.'l 6тобрааи:гь
ШЩ./ЮГИЧЕще, не м!Шее на30Йi1Ивое Оi..rю).

!ltP'pp . 1Iбнioi>!- 11811i1ut. п ........._ б!IA~'_D. Пр"",,_


nB ••• ",1МIIII 3ь ~Cf!II1I4 -

PItG. 6.7, Реаул~~т И('НОPl'1РОВ3НИЯ ИСКЛlOчения


296 Часть 11. Язык программирования С#

Исходный код. Проект CuslomException размещен а подкаталоге, соответствующем главе 6.

Отладка необработанных исключений


в Visual Studio 2005
в завершение нашего обсуждения следует заметить. что в Visual Studio 2005
предлагается целый ряд инструментов, которые помогают вьщолнить отладку
npограмм с необработанными пользовательскими исключениями. Снова предпо­
ложим. что мы увеличили скорость объекта Car выше максимума. Если в Vlsual
Studl0 запустить сеанс отладки (используя DebugQStart из меню). то выполнение
программы автоматически npервется в момент генерирования исключения. остав­

шегося без обработки. Более того. появится окно (рис. 6.8). в котором будет отобра­
жаться значение свойства Message.

currSpeed • О;

! "1 Р

TroubIeIt_1O ТIPII
IGei oenёrafhe~ for this ехсерtioП.-- · --_._ - (О ) "

!
I
L--.. _ ____ ._ .. ______ ._ ______~.._.
el",e

-}

Рис. 6.В. Отладка необработанных пользовательских исключений в Visual Studio 2005

Если щелкнуть на ссылке View Detail (По казать подробности). появится дополни­
тельная информация о состоянии объекта (рис. 6.9).

3ама'lанме. Если вы не обработаете исключение, сгенерированное методом из библиотеки базо­


аых классов .NEт, отладчик Visual Sludlo 2005 остановит аыполнение программы на том опера­
торе. который аызвал метод, создающий проблемы.

Резюме
в этой главе мы обсудили роль структурированной обработки исключений.
Когда методу требуется отправить объект ошибки вызывающей стороне. этот метод
соэдает. конфигурирует и посылает специальный тип System.Exception. исполь­
зуя для этого ключевое слово С# throw. Вызывающая сторона может обрабатывать
посryпaюIЦИе исключения с помощью конструкций, в которых используются клю­
чевое слово catch и необязательный блок finally.

....
Глава 6. Структу~ированнз.я о(jре.бот~а иоключений 297

' ~~iO'1 : ~~h • .~i; - - -,


· JВ rCU5ttlmE.C!!!XIOn,c..r~(jf)J i!'Zippj' ' - o""'~~$te4!"j i
. IВ ~~ ~.caiittll<Jlj$ , ~'lstDiI:hcmI"IIl\It~ .
· ~ 'bttp:/).wwI't.C8l'5RLS,com' ~
~ .~~ ~ . "
, ~ Мi:5~ ~ ; ;;''РРУ hos DVet'~I'
· IВ ~PUbIC mllrnt.er.
"Cusl:OIi1EXI:optJoh'
5t~1roa! • al:CUioott~.ar;A=lorit
m5t8tk rraШrs
1J.~ ••" •••.~~OI~ А~.{ttJ~эz)}
t.symn"atflectlo".RUI\tIrmjf (voJd'Аtteleгatо(lr\tЭ21~
АФbJte. s~.f<eI'Iectkn: Мe&.oIWtihtes.
ьме
~~~~~~~-
j~dd Acr"~~
~~~==~
.
oj( J.

Рис. 6.9. nодробност~ OT1I8Д1t101 не(}бр..iбР~ННых П(}Л),·


зоватеЛьСJ(ИХ l<ICIU1ючений I! 'Visша\ Studlo 2005

При создa.mш noльзоsате.i'IЬСКИХ ИСЮП9че;ниi;! B~ сОздаете ТИI.I 'itласса. произва­


ДН:ЫЙОТ Systern. Applicat io.nException. что оз$~ет иcIOlючение. генерируемое
ВЬП10ЛН'яе:мым прооожением. В противополощность ЭТDМУ объе:kТЫ ошибок. полу­
чающиеost 1'13 sузtеm. SystemE'X~eptio!).. цредcт.;tВЛSЮТ :критические (и фатальные)
отибюr. rенерируемые средой CLR. Наконец. ВЭ]10Й главе бьши I1pедcrавлены раз­
личные Шlcrpументы VIsual Studl0 2005. })O-ТОР~~}.f(jЖНQ исполыювать .прЙ отладке
и при создюши ПОJIQЭовательс1tИX ИСКJIl9чеЕЩЙ (В соответсrtmи с ~ обра3~
цами .NEТj.
ГЛАВА 7
Интерфейсы
иколлекции

I В э:юй главе предлагает('.~ p~OMoтpeTb тему nporpам:мированnя на основе ин­


I
_ терфейсов. ч'Го~ы р.а~ШИРIЛЪ.ваши представления об объеl<nю~ор.иентиро­
I
l'
ВJЩНОМ подходе в области разрабоnm прШJQжениЙ.~деьъ вы узнаете.I\ак в ра'М­
ЩIX С# определmoтЩI JJ ре~лизуютов я:нтерфеЙсы. и поймете. 11 чем З8.tcЛЮЧaIOТC:.fl

I
npеИМУЩfXтватипов. ЦQДЦерживaюIЦИx "множесТ'sеНkое поведениеМ. В процеесе
Обсущде,нщr будет рассмотрен:и ряд смежных вопросов-в 'IаC':rНОСТИ. IIQдуч.ение
ин:reрфеЙсныхссъщок. ЦН8;Il реали3аЦ1-Ш и:нтерфеР...сов. а таюке иерарxnй интер­
j фейсов.
! Част,Ь FЛЗВЪil будет посвящена рассмотрению целого рцда интерфейсов. Опреде­
ленных в paм~ ffiИQmrотек базовых ю:raСC(Jв .:NEТ. выl увЦЦИ'rе. что определеНl:fые

t
!
lЩМИ цолъзоватenьсдие типы 'I'О:же МDЖl:lО встраивать в эти предопределенные' ~­

терфецсы, Ч1'QI5ЫQбеецечИ1Ъ noдцержку cnеЦtfалъных функций. таюiX КаЕ КlIони­


poBВFДI.e. переЧИGде.нде и сортщ>овна объектов.
Чтоf5ы uродемоне1iРИРОВВ'IЪ, :&iШ интерфейсы ИСПОЛЬ3}'1Отся а бибдиотеках ба­
ЗQВЬ1Х классов .NEr, в этой rnзве бу.zwг рассмотрено множ~тво встроешIых :uБтер­
фейсов. реали~у~ различными классами коллекций (Arra.yList. Sta.Gk Д т.п.).
опред~енным:и в -nро.странстве имен Буstеm.СОl1есt{о.ns. ИнфQрмацил. пред­
СТaвлe:JПlщ!. ЗД6С,Ь, будет нeoбxqд;има Д1IН понимаюш ма:rери.ала: rлавы 10. в щ:rгoроj:i
рассм~триваютi;:Я обобщения .NEТ и Пространство имен .Collection-s.,Gе.{lе.rlс.

Определение интерфейсов в С#
иано»reI-IИe материала этой главЫ мы начнем с фо,рмалы~ого щ:rределен:ия ТЙIIа
"интерфейс". ИНтерфейс - Это просто имеНОВailll8Я RDллекдия сеМ11нтИ<IеCЮJ свя-
3a.IпIых абоtnpwcrrtн.ьtx ' чтrеi:WВ. Эти члены опредeлmoтсл l';IНТерфейсом Б зависимо­
СТИ от noвeдеНlЩ .которое МОДeJniрует дaнньtй интерфейс. Иитерфейс отражает IЮ­
ведение. которое :может поддерживаться данным )'tЛ8.ссом'ИЛИ стру.турпй.
В рамках СИНТaRCйса С# интерфейс опред~щreтся е по1.!fОЩЪЮ ЮIЮчевого CJl0В3
interfac€. В отЛИ'4йе от дРулах ТЮ10В .NEТ, ;ин-reрфейCJЫ :викогд<!. Не уназывaю:r' б8 J
зoRыйассc (mшюч.ая System,Object) И ,ДЛЯ- ихчne.нОВНИКОIДВ ./ifеушi3ывaIOТся МOJ
дификаторы доступа (пос&o.льt<y все члены интерфейса H~HO считаются oт.кpы~
тыми). во1" пример пользовате.llЬCRОI;O юr:r:ерфеЙса. опреде.цеllllОro на.языке С#.
-
300 Часть 11. Язык программирования С#

11 Э'1'о'1' ИН'1'ерфейс опре,цеJIRе'1' нanичие вершин.


public interface IPointy
(
11 HeRВHo о'1'ХРIol'Ш!Й и аБС'1'рах'1'lDoIЙ.
byte GetNumberOfPoints();

Замечание. По соглашению имена интерфейсов в библиотеках базовых классов .NET имеют пре­
фикс "1" (прописная буква "j" латинского алфавита), При создании пользовательского интер­
фейса рекомендуется придерживаться аl~алогичных правил.

как видите. интерфейс IPointy определяет единственный метод, Однако типы


интерфейса в ,NEТ могут также определять любое число свойств. Например, мож­
но определить интерфейс IPointy, в котором используется ДОС'ryПНое толь:ко для
чтения свойство вместо традиционного метода чтения данных,

11 Реa.nиsаЦИR поведеНИR в виде СВОЙС'1'ва, достynноrо '1'OJIЬXO .ц.пи Ч'1'ения:.


public interface IPointy
{
byte Pointslget; I

Бы должны понимать, что типы интерфейса сами по себе совершенно беспо­


лезны. поскольку они представляют собой всего лишь именованные коллекции аб­
страктных членов, Б этой связи вы не можете размещать типы интерфейса так,
как это делается с классом или структурой,

/1 Соадава'1'Ь IJ.'ИlUI: интерфейса с помощью "new" не допусхае'1'СН.


static void Main(string[] args)
(
lPointy р = new IPointy(); 11 Ошибха хоиnитщии!

Интерфейсы не приносят пользы. если они не реализованы некоторым клас­


сом или структурой. Здесь IPointy является интерфейсом, отражающим "наличие
вершин~. Такое поведение может быть полезным в иерархии форм. построенной
нами в главе 4. Идея очень проста: некоторые :классы в иерархии форм имеют вер­
шины (например, Hexagon - шестиугольник). а другие (например, Circle - круг)
вершин не имеют. Реализовав интерфейс IPointy в Нехачоп и Triangle. вы може­
те предполагать, что оба класса поддерживают общий тип поведения, а позтому и
общее множество членов.

Реализация интерфейсов в С#
Чтобы расширить функциональные возможности класса (или структуры) пу­
тем поддержки типов интерфейса, нужно просто указать в определении клас­
са (или структуры) список соответствующих типов. разделив их запятыми.
НепосредственнPIЙ базовый класс должен быть первым эле.менmoм в списке, сле­
дующим после операции, обозначаемой двоеТОЧl:lем. Когда ТИП класса получается

L
[лов 7. Интерфейоы и ICОJIЛе~ции ,301
Jreпосредственно из System.Object. можно укаэа.тъ ТОЛЬКО список .интерфеЙСов .
Щ)ддерживаемь1Х массам, ПОСКООЬ~ РРИ отсутствии явноro указaJmЩ компилятор

С# получает типы именнр цз $ystem.Obj·ect. ТQЧНО так же. ПОСJЩlIl>ИУ CT,p.YJnYPЫ


Bcerдa Ш!JJ1УЧЭЮТСS uз Sуstеrn.Vа1щв'I'уре (с:м. rлaву 3). моЖ1<Ю ун,аэатъ ТОды50 Щi­
терфейсы: в спиеже , следующем HeдoepeдcтвmJНO nOC.iJe оnpеделени~ (:ТРУ!Р"УРЫ.
Рассмотрите 'следующие пример:ы..

11 ЭoR~ кд&сс: IDml8~CJl n:poи.аоДiDIМ Sy.tem.Obj-есt


11 и р8anиaуе~ O~ lCJIирФВЙс:.
public clas-s SomeClass: ISomelnterfaee
( ... )
11 3'10" .JaIAca ~'''1'С. прош._а=-* ВУ. teш, Obj8ct
11 и pea.mв.Y8'1' a~ lCJI'1'.рф8Йа.
publ i c qlas:s МуСlа.зs : obj ect" ISоmelцtеrfасе
{ ... }
/1 ЭJI"' 1UIa~ Jl&ПJaКlСJl ~ • .a,ц1DDl ПОJПoаоаа~8JUoС.0Ж'О 15••0.01'0 JQf&cca
11 Jt peaтu)"e'1' QДJDI ИИ'1'8р• • Йа •
риыlс-сlаsээ АЛ0thеrСl'аss : МуВаsеСlаsэ, IS:omeIТlte.rface
[ ... }
11 ЭФа C'1'P~a 1dl1UI8'1'C8 прохааФlНОЙ Sу.tsш .VаluеТуре
1'1 Jfp.amrs}'8!f1 .ца.. lCИ'I'ерФdса.
public str-иct SomeStru,c.t : 1 Somelntei:face, IPointy
\ ••• r
вы дощкныnо:НИМатъ. что реализации интерфейса подЧинReТСЛ принципу ~вce
}JJЩ ЩIЧ~l'ОW. ТИп. 'реализующий :интерфейс, не :может оl5еспе~Iвать селективную
поддержку членов этого интерфеЙса. Есди mrreрфейс IPoi n t у опредеЛ)Jет !ЩИН~
ствeщwе свО:Йст,во. то Д1tЯ выбора Jlа:РИaR'I1QВ H~ ОЩ1зко если .реализоваTh mцep­
фd:iс. определяющий , десять qлеиов. то соотретствУЮЩИ-Й тип будет обязан .t(Oнкpe~
~ЦPQВaTЪ все десать аБСТРaI(Т1'IЫX элемеНТОf).
Тщ;: или иначе. вот вам пример реа.тщзaцJoJ,И qбновленной иерархии форм (~ 06-
раз:ите вНйМamiе на. новый тип К.i1ассатriа;лg1е - треугольник).

11 Иеха.gОr;l '1r8IJep. р8&nЮJvеl,ll IPOinty .


pubIic clas·s Hexagon : 5h.ape, IPoinq
(
public Нехаgод(J { )
риь! ic Hexaqon (stz:.iлg пате) : b~5e (name),{ }
public override vo.:i,d Dr~w О
1. Соnsоlе.WritеLinе("Отображение щеС';t'~О:Л~НИj(~ {О] ", .РэtNаmе); J
11 Pea.mcaaцн. IP61.nty.
publiobyte E'oints
{
get ( :teturn 6;
302 Часть 11. Язык программироваНИR С#

1/ НОВЫЙ ПРОИSВОДIWЙ macc Trianqle, полученный из Shape.


public class Triangle : Shape, IPointy
{
public Triangle() { )
public Triangle(string пате) : base(name) { }
public override void Draw()
{ Console. Wri teLine ("Отображение треугольника {О)" PetName); }

11 РеanиS&циR
IPointy.
public byte Points
{
get { return 3;

Теперь каждый класс при необходимости возвратит вызьmающей стороне чис­


ло вершин. Чтобы резюмировать сказанное. рассмотрите диаграмму на рис. 7.1,
которая была получена в Visual Studio 2005 и иллюстрирует совместимые по ин­
терфейсу IPointy классы, используя поnyлярное обозначение интерфейса знаком
~леденца на палочке".

-lii1
,.'~ .. ~ ..
, ,.)

Рис. 7.1. Иерархия форм (теперь с интерфейсами)

Интерфейсы в сравнении с
абстрактными базовыми классами
с учетом знаний, полученных в шаве 4. вы можете спросить, какова причина
въщвижения типов интерфейса на первое место. Ведь в С# позволяется строить аб­
страктные типы класса. содержащие абстраятные методы. И. подобно интерфейсу,
при получении класса из абстрактного базового класса, класс тоже обязан опреде­
лить детали абстрактных метОДОВ (если. конечно , произвоДНый класс не объявля­
ется абстрактным). Однако возможности абстрактных базовых классов выходят
далеко за рамки простого определения группы абстрактных методов. Они могут
определять oTRpblТble. при ватные и защищенные данные состояния. а также любое
число конкретных методов, которые оказываются доступными через подклассы.

Интерфейсы, с другой стороны, - это чистый протон:ол. Интерфейсы никогда


не определяют данные состояния и нwroгда не обеспечивают реализацию методов
(при попытке сделать это вы получите ошибку компиляции).
rлаза 7. ИнтерфвJ.10Ы и коJtЛещии эоз

public interface IAmAВаd:rпtзrfа~е


{
1/ OIIIибжа! Иитерфейс не мo*elJ1 оnpе,цeJPt'.1'Ъ ~aHвve!
i11t m1Int = О;

/I ОШИБJEЗ '1 Допус_UI'J!SИ 'l'b.пьжо абс~'I'JDo1e ч.:nElOl!


I7Did MyMethod ()
f СОЛ'3сlе. Wri teLine (';Фи '! "): ]

Тиды -и:н:герфейёа GказыIаютсаa uолеаньtмй и .а свете того, "{То в С# [и других


язывах .jlrE1l не :IЮДZJ,ерживает('л множест:веНtiое насд~Д!:шащre, а основанный на
интерФейсах протопол позволяет типу п~ддержuвать м.'lOжество Вариантов пове­
деЮIЛ, uзбегая при вто.и npоблем, 80ЗНИКающих при нащrедовшщи от М110же("гВа
оозовhЦ классОв.
Еще родее важно ТО, что .rtрограмм:ирование 1ia основе 1Щ'ГерфейеовобеCn6Ч1mа­
ет альтернативный спасо!) реaJ'lИЗации ПoJl1lморфщ)го, по.ведения . .хотя мноЖество
клаССОЕ\ IlVIИ crpYК1YP) р-езли3}'ет 0ДI-т и тот же .uвтерфеЙс своими ('обетвеяю.tми
С,пособщи, вы имеете 1юзможнЬ,С:ГЬ обращаться со ВС'еМИ типами по одной схеме.
Чуть поз,же БЫ убедитесъ, чтО' ш-rтeрфсйсы ИСRJlЮЧ'ительно DО~Орфны. потому
что ~ и;х Щ)Мn-ЩЬЮ ilОэм.ожностъ демонстрировать иденТИЧНОе поведение получают
типы, ,6е ,связанные кnaс-сиче{:ким наСЛe,z;(ова.нием.

8ы,зовчлен,ов интерфейса на уровне объекта


Теперь. Rщца у вас есть набор типов. подде~ЗIОЩЯХ :miТерфейс Poi-n ty. сле­
дУЮщей 'аадачей О'казывается доступ ~ новым ФУJlRЦИонвльНЫм возможностям.
Самым npоcтым способом обеспечения дос:ryпa R фушщиональным возможностям
данного Интерфейса является неIIосредствeшIblЙ вызов методов на уро.в:не объек·
roв, Например:

statiG void Maiд ('string[] а1.'ЩS)


f
J/ BQ.OB тmeнa P'oints инoreрФei!='!I IPo.i nty.
He:xiag,o,!'I hex '" new tJexagaJ1.' () I
' СОrJ!'юlе . Wri feL-iuе (DВер!l1ИН: j О} ", :tщя. Ро ifl'ts) ;
:C or,,sol е .. ReadLine О;
}'

Этот ПОДХОД npeкpaCHo работает 8 данном 'КОlJRретдОМ СЛУЧае, поскольку вы


:щцете. 'Чi'отиn 1J.exag.on реализует УПQМЯНУТЫЙ интеРф('jЙс . Однсщов других слу­
чаях во время компиляЦИи вы :не сможете определить. юnmе интерфейсы по,nдер­
живаютсл данным типом. ПрeдnолОЖйМ. например. ЧТО у на«;: еС1Ъ J\11lеGИВ из 50 ти­
ПОВ. соответствующих S11ape, НО ТОЛЬКО некоторые ИЗ них поддерживюот IPointy.
ОчtЩИДВО. ч;ro если вы поnьгraетесь ВЫЗВать свойство Points Д11,Я типа. в IЮтором
IPointy не реализован. вы получите ОIJ1ИБR)' IЮМnЩl1ЩИИ. Возникает' следующий
воцрос: ''Как динамически nDЛУЧЙТЪ информацИЮ о mho-ж&'Тре интерфейсов, под­
мрщиваеМЫ» данным типом?"
Выясжить lЮ Бр€М& въmолнеюш, ПQДДерживает ли Дщпц.!Й ТЩl K-D1ШреТl-IЫЙ ин­
терфеЙ:с. МОЖНО. например. с помощью sшного nЫЗQЩi. Если ТИП п€ поддерживает
304 Часть 11 . Язык программирования С#

запрошенный интерфейс, вы получите исключение InvalidCastException. Чтобы


"изящно" обработать эту возможность. используйте сТруктурированную обработl\Y
исключений. например:

static void Main(string(] args)


{

// Воsиоaкwй захва~ искnючеии. InvalidCa8tE~c.ption.


Circle с = пеи Circle("Lisa");
IPointy itfPt;
try
(
itfPt = (IPointy)c;
Console.WriteLine(itfPt.Points);

catch (InvalidCastException е)
{ COnSOle.WriteLine(e.Message);
Сопsоlе.RеаdLiле()i

Итак. можно ИСПОЛЬЗОБать логш\у try/catch и надеяться на удачу. но лучше


еще до вызова членов интерфейса определить. какие интерфейсы подцерживают­
ся. Мы рассмотрим два варианта такой тaктиRИ.

Получение интерфейсных ссылок: ключевое слово as


Второй способ проверить поддержку интерфейса для данного типа предполагает
использование ключевого слова аэ. о котором уже шла речь в nIаБе 4. Если объект
можно интерпретировать. как указанный интерфейс. будет возвращена ссылка на
интерфейс. Если нет- вы получите null.

static void Main(string[J args)


{

// Но_о .пи ин~ерnpетироваon. Ьех2, как IPointy?


Hexagon Ьех2 = new Hexagon (" Peter") ;
IPointy itfPt2 = Ьех2 аэ IPointy;
if(itfPt2 != лull)
Сопsоlе.WritеLiпе("Вершин: !О}", itfPt2.Polnts);
else
Солsоlе. WriteLine ("ОЙ! Вершин не видно ... ");

Обратите внимание на то. что при использовании кmoчевого слова as не возни­


кает необходимости использовать логику try/catch, поскольку в том случае. когда
ссылка оказываетСЯ непустоЙ. вы гарантированно будете иметь действительную
ссылку на интерфейс.

Получение интерфейсных ссылок: ключевое слово is


Можно также проверитъ реализацию интерфейса с помощью ключевого слова
is. Если соответствующий объект не совместим указанным интерфейсом. будет
Г:лава 7, ИнтарфеUrцы Jo\ I(ОЛЛ81Щj.1И 305
воа.ара:щено значение fа:lзе. А еCJЩ ТИЦ совместим с интерфейсом. вы можете сме­
ло вызвать его члены бе~ ИСПQ;rьзоц~ ЛОГИКИ ·tTy/catch.
Для примера предположим, ЧтО мы ИЗМе!НWIИ J,l4ассив ТИПОВ SrJape Т'ц, что те­
перь неИ(Yf()рые его 'UreнъJ, ре.ализуют IJ?oin:ty. ват как с помощью кmoч:еВОГG сло­
ва iэ можно выяснить, какие ИЗ элементов .Ц массиве поддеРЖИВaIQТ ЭтОТ ИJIтер­
фейс.

st.atic void Маiп (string (,] ахчs)

i
., .
S.hap€[] s = { r,ew Hexagon(), r1ew Ci:rcleO, new TLiang~e("JDe"),
Л'еw Circle ("Jо;:Го"')} ;
for (int i = О; i < 6.1engt:h.; i++)
[
11 Н~OJOIИМ, Ч!1'О ба_о'- х,иасс Shape оnpед8.1U18'J! а6С!1".Р&МИIoIЙ
// чаев nraw (), ПОЭ!I'ОИу _са фОРIAI JlОryш Q'1'Обрua_ оеб••
$[iJ .DrawO:
11 х..о с: ••РIllИК8КИ?
i f (6 [i.] is lPointy)
Console. Wr-i.tеLlIlе ("~> Вершин: ! О) ", «( 1 Pointy) s [:L] ) .Po:Lnts) ;
else
CCJTislole.Wr-iteLiпе("-> {О} .!'5ез вершин!"/ B{iJ.PetName);

Соответствующий вЫвод I10ЬСа3ан на рис. 7.2.

Рис. 7.2. Диыамическое обнв.р~еff'иереалиэоваfilныx И~lтерфейсое

Интерфейсы в качестве параметр.ОВ


Поскольtcy интерфейоы лВ,)].moтс.я: полноценныыи ТЮIa.~ .NEТ. вы можете КO:Н~
струировать методы ..I(oтopble будут исш:шьэовать и:нтерфейсы, х!ш параметры. Для
дримера предположим. что мы опредеЛи'il:i' дpyrоИинтер.фейс с именем IDrаwЗD.

1/ ИоДtЦЦ1pуех JlОSИ~ОС:'l'Ь ~бр. . .ни. '1'1IШ8. JI nPOC'l'PaнC'1'88.


public interface ШrаwЗ'D
{
",о10 1!taw3D О i
306 Часть 11. ЯЗЫК программирования С#

Предположим также. что две из наТnИX трех форм (Circle и Нехаgоп) сконфигу-
рированЪJ для поддержки этого нового поведения.

11 Circle поддерживает IDrawЗD.


pub1ic class Circle : Shape, IDrаwЗD
(

public void Draw3D()


( Сопsоlе.WritеLiпе("3D-отображение окружности!");

11 Нехаgопподдеpzивает IPointy и IDrаwЗD.


public class Нехачоп : Shape, IPointy, IDrаwЗD
{

public void Draw3D()


{ Console. WriteLine ( "3D-отображеrще шестиугольника!"); }

На рис. 7.3 покаэана соответствующая обновленная диarрамма классов, полу­


ченная в Visual Studio 2005.

Рис. 7.3. Обновленная иерархия форм

Если определить метод. использующий интерфейс IDrаwЗD в виде параметра,


вы получите возможность передать любой объект, реализующий IDrаwЗD (но если
вы ПОПblтаетесъ передать тип. не поддерживающий нужный интерфейс, будет сге­
нерирована ошибка комrшл.яции). Рассмотрим СЛедУЮщий фрагмент программно­
го кода.

11 С09дание несJCО.пъхих форм.


11 Если это В09МО_О, их О'1'обра:.еиие в трехмерном виде.
public class Рточтат
(
11 Отображение форм, поддеpzивaDЦИX IDrаwЗD.
public static void Drawln3D(IDraw3D itfЗd)
(
Сопsоlе.WritеLiпе("-> Отображение IDraw3D-совместимого типа");
itf3d. Draw3D ();

static void Маiп()


{
Shape[] s = ( new Нехачоп(), new Circle(),
new Triangle("Joe"), new Circle("JoJo"))
Глава 1, ИfЩ!JJфеЙ~Ь1 и коллекции 307
f"ot(int i = О; i .: 5.Lепgth; i ++)
{

1/ МожНо ;ли Оа.оаб:Р4!IИ':I!Ь 11 ЗD-1IJQIе?


if ('8 [11 i s Iдrаw3Щ
Dra wInJ:ti ( ( II;Jr~ w3 D) :;; НJ ) ;

Обратите внимание на то, -чтотреут(щъ,l-~ не отображается. ПО~J\()ЛЪRy он.не


Я&-uюrен IDraW3D-совмест.и:мым (рис. 7.4),

Рис,.7,4, Интерфеисыв I<:а'lедтве парамет.ров

.интерфейсы в качестве возвращ,аемых значений


~':ГерфейсJ:,l МОЖ;НО ИQПользовать и .в качестве возвращаемых значений мето­
дов; КащрИ)'df'j>. можнр .::оздаТh метод. КGrоjlьш берет moбой System.Object. прсг
вернет на совмеCТЩdОClЬ с IPcHinty и возвраЩает ссъшку на иэвле"leннБIЙ интер­
фейр.

11 Эт~ 1Ie'J.'OjI; прозер._ соо~е!1'(:щаие IPointy 51:, ес:.пи это _О8NOIIИO,


11 .QS~Ращаe'l' CCJ01JUtY на ИИ'l'ерфейс.
statiG IРфirltу ЕхtrасtРоiпtуnеS:;J (o:b ject о)
f
if (оi5 IPpintyJ
retu'гrJ (IPoiflt.y) о;
else
retl",'m null;

с этим методом моЖно. В'3Шf.модmIс~оватъ т/щ • .l{а:к предлагаетсЯ виже.


З08 Часть 11. Язык программирования С#

static void Main(string(] args)


(
11 ПОШl'1'ка ИS8JI8Ч.. IPointy иs об'МIК'l'а Car.
Car myCar = new Car();
IPointy itfPt = ExtractpointYn88s(myCar);
if(itfPt 1= null)
Console. Wri teLine ("Объе~т имеет {О} вершин.", i tfPt. Points) i
else
Console. WriteLine ("Этот объект не реализует IPoin ty")
};

Массивы интерфейсных типов


Следует понимать. что один и тот же интерфейс может реализовываться мно­
гими типами, даже если зти типы не находятся в рамках одной иерархии клас­
сов. В результате можно получать очень мощные программные конструкции.
Предположим. например. что мы построили одну иерархию классов для описания
кухонной посуды. а другую - для описания садового инвентаря.
Эти иерархии абсолютно не СВязаны Между собой с точки зрения классического
наследования. но с ними можно обращаться полиморфно. используя программи­
рование на основе интерфейсов. для иллюстрации предположим. что у нас есть
массив объектов. совместимых с IPointy. При условии, что все объекты этого мас­
сива поддерживают один интерфейс. вы можете обходиться с каждым объектом,
как с IРоiпtу-совместнмым объеJ(ТОМ, несмотря на абсолютную несовместимость
иерархий классов.

static void Main(string(] args)


{
11 ЭТОТ массив КО••'1' сод.р.а'1''' '1'Оn"КО 'l'ИПЫ,
11 р.a.nиsуацие ИНТ8РФейс ~Pointy.
IPointy(] myPointyObjects = (new Hexagon(), new Knife(),
new Triangle(), new Fork(), new Pitc hFork()};
for (int i = О; i < myPointyObjects.Length; i++)
Сопsоlе.WritеLiпе("Объект имеет {О) вершин.",
myPointyobjects[i] .Points)i

Замечание. С учетом общеязыковой природы .NEТ ваЖliО подчеркнуть, что можно определить ИН­
терфейс на одном языке (С#), а реализовать его на другом (VB . NEТ). Но чтобы выяснить, как
это сделать, нам потребуется понимание структуры компоновочных блоков .NEт. что является
темой обсуждения главы 11.

Явная реализация интерфейса


в определении IDrаwЗD мы были вынуждены назвать наш единственный метод
DrаwЗD (}. чтобы избежать конфликта с абстрактным методом Draw (), определен­
ным в базовом классе Shape. Такое определение интерфейса вполне допустимо, но
более естественным именем для метода было бы Draw ().
Глава 7. Иt1терфейсы и К()ЛЛ81/ЦИИ 309
/J Изкев.нме ~ с. "Юrаw3D OI на OIDr.,,".
pиb li c in t e-rface ID:r;aw3.D
I

Если ввосптъ 1'акое изм~нение, то потребуется также обновить нашу реализа­


цию Drawln3D () .
ри:ЬНс- stat:i G void DrаlllIпЗD (IDrаwЗD i t fЗdJ
{
Consol е . Wr i teL1I).e ("- > CYrображе.f/ие ту a·w 3 D-сов-ме с тимо'Г о ТИда П) I
i tf3d . Dr.w ( ) ;

ТеIЮРЬ преДJIОЛОЖИМ. что мы .опредеЛИ,j1И но1'\ый класс МП8 [линия). который


получается из абeтpa1rrного класса Sh-ape и ревлизуе'J' IDr.a.wЗD (оба из них т~перь
(i)преде..1JШOТ ОЦИНa1tОВО названные абстра:кгНЫeJ метрды Draw ()).
/I дробn8lillll? 9'1'0 !l1UIИСИ'1' •••
puhlic class Liйе : Shape., IDrаwЭD
l
pu.blic cverr:1.de v-oid D.raw ()
{
Console. Wri1;.eLJne ("'Отображ.ение ЛИНИИ ••• ") ;

Класс Li n.e lщмnилируеТСJli бес;п:рщrятС'т.венно. PaccMOTI»JМ следую1ЦУЮ лопту


Main ().
st.a tic void MailJ (st.r in,g [J args)
{

/I Выso. D:Ж-iLV О .
Lihe ту Line = new Line() I
myLl.ne. Dra,w () ;
11 ВШlОII той . . реa.nи8АЦИИ Dr.'W 1> !
IDraw3D itmrаwЗd= ~IDr<!wЗЩ myLit\e;
itfJ;jга:.W З!:i.D:rвw() ;

с учето-м: того. что вы уже знаете о баЗОВ(i)М классе Shqpe и интерфейсе LDrаwЗD,
это выгля:.цuт так, как ~YДTO вы вызываете два BapJiIaНTa метода :D r aiW .() (один с
объеКТНОI'.O уроВЮI, а дР}'I:ой - е помощью интерфl;,йсвой ccblJlКl'i.). Однако вомnи­
литО}') споообен вызывать oдgy'11 ту же реализацию и с :ЦО~ЩЪю ивтерфейса~ и с
помоIЦ'Ью OOЪeRТНOn ССЪ1Л1Щ. Dое.кольку абетрактный базовый :класс S'hape и:интер­
фейс IDrаwз.D имеJOlГ(i)дшIа:ково внзвaнщ.rе ЧПСНЫ. Это может о:казаться проблемой.
KdrДallЫ ХО'1'ИТе. чтобы метод IDraw3D. Draw () предс'I"'cI1IJIЯЛ тип во в.сеЙ трехмерной
(3D) ~I(pa.c·e·. а не 11 неказистом .двухмерцом npедсташщн:ии переоnpеделенноrо ме ­
тсща Shape.D:r:aw ().
Теперь рас~М01JШМ родетвеl:UiyЮ проблему. Ч1'9 w.,лать.еели вам НуЖНО гаран­
rnpовать. что метоЩ>J. oцpt'J1Meнвьre Дa.JЦIЪЩ m;lт~феЙсрм. БУдУТ .цос:ryпны ТОЛЬКО
310 Часть 11. Язык nрограммирования С#

с ПОМОЩЬЮ интерфейсных ссылок. а не объектных? В настоящий момент члены,


определенные интерфейсом IPQinty. доступны как с ПОМОЩЬЮ объектных ссылок.
так и по ссылке на IPointy.
Ответ на оба вопроса дает ЯВн'ая реалuзaЦUЯ uнmeрфеЙСа. Используя этот под­
ход, вы можете гарантировать. что пользователь объекта сможет получить доступ
1\ методам, определенным данным интерфейсом. только с ПОМОЩЬЮ правильной
интерфейсной ссылки, не допуская при этом конфликта имен .. Для примера рас­
смотрите следующий обновленный масс Line (предполагая. что вы соответствую­
щим образом обновили Hexagon и Circle).
/ / Используя _H}'IO реa.nизацию lII~oдa, 110_0 указа'1'lo
/ / другие реanизации Draw () .
public class Line : Shape, IDraw3D
(
/ / Э'1'о'1' "е'1'ОД "о_о В5IЗва'1'lo только СCКIIкоЙ' на ИН'1'ерфеЙ'с IDrаwЗD.
void IDraw3D.Draw()
{ Console. Wr i teLine ("Отображение 3D-линии ... ") ;

/ / Э'1'0 IIОlКИО вызатьь тоnько на уровне об'Ъе1С'1'а.


public override void Draw{)
{ Console. Wr i teLine ("Отображение линии ... "); }
)

Как видите. при явной реализации члена интерфейса общий шаблон выглядит
так: возвращаемое3начение ИмяИнтерфейса .ИмяМетода (аргументы). Эдесь есть
несколько "подводных камней", о которых следует энать. Прежде всего. не допуска­
ется определять явно реализуемые члены с модификаторами доступа. Например,
следующий синтаксис недопустим.

/ / Нет! Эта недопустиио.


public class Line : Shape, IDraw3D
{
public void IDraw3D.Draw() / / <= Ошиб1Са!
{
Сопsоlе.WritеLinе("Отображение 3D-линии •.. ");

DIавной причиной использования явной реализации метода интерфейса явля­


ется необходимость "привязки" соответствующего метода интерфейса R уровню
интерфейса. Если добавить ключевое слово public. то это будет означать, что дан­
ный метод является членом открытого сектора класса. и "привязка ~ будет отмене­
на. Тогда вызывающая сторона сможет вызывать толыш метод Draw () . определен­
НЫЙ базовым классом Shape на объектном уровне.

// Здесъ вызывается переоnpеделенИЫЙ' ме'1'од Shape.Draw().


Line myLine = new Line{);
myLine.Draw() ;
w.~--------------------------- ----- ,

Глава 7, Интерфейсы и КР/lЛекции 311


1:fтобы вызвать метод D:raw (). определенный с ПОМQЩРЮ IDraw~l). мы ДО.J:Щ(НЫ
ЯВБО получить интёрфейсную CCЬLГfKY. ИCnОJIЬilУ:.I1 щобой из ранее указанных под­
.JЮДОВ. Например:

~O обеспеЧИ'1' 1Ul!J9B И8'1'Q,Цa J:Drа"ЗD. Draw () •


.1 /
tine ffi)'Liле = пеw 1111е ( ),;
IDraw-ЗD i 3d = (I Draw31J) юуLiпе;
iЗd. DtM/' ( ) ;

Разрешение конфликтов имен


.нвмея реализаци:Я' интерфейс'а может ОJщза1'~СН' очень полезноя тогда, JWr-
да реализуются ие-еКQЛЬ:К:О И8.терфеЙсов. содержащих 'И.Д~Бтичные Членм.
Предположим. например. "1"1'0 вы СОЗДaJЩ класс, реализуюЩИ'Й сле.щующие новые
типы интерфеЙta.

11 ~и ИВ'1'еРФ8Йоа, оnpедеnlDl1lЦИе x8'RQды с ОjЦИВaI(ОВЫNИ иие~,


рuэliс i n t е!' f асе ID:r:aw
j
V'o1d' Dra,\l () ;

риЫlс interfac~ rDraWТoPr~nt&E


(
vo'i,d D.ra-w J )' ;

Если вы захотите постр<щ1Ъ .класс с именем Supe rlmage (су':Первэображение).


поддерЖ~аlОЩИЙ базовую визуализацию lIQ.raw). ЗD-ви'эуализацию (rDra:W:?Ii).
а -vакже rt:рВИ(' де,Ч<;lТИ (IDraw'l'oP.rlfltE.r), '1'0 едивствеfШЫМ опособом обеспечить
УНЮЩлънyro реализацию для каждого Мtтодабудет использование явной реализа­
ЦIIЩ ЩiТeрфеП!::il.

11 Не _О~CJI ив ЗЬаре, R.O ."ВBOДJIiТ :lCоафзtmt'Ji ииеll.


Fublic cla.s.s supetT.mage : Ш)rа"r, I:J)rаYI'о1Тin,tеr, IDrа",ЗD
{
"'oi'd rDraw. Dr_w ()
I 1'" Л;;Г1iд<абазовой sизуащrзацiИИ. ... I }
vol d Ш:z::аwТоPrin.tеr.. Draw ()
i 1" Логика печаТ~l. ·" /1
Yoid IDraw3D.~%aw()
{ /.. ЛОР>;lЮ3' ЗD'- БИЗУа.тu'{Зации . ~ i }
}

ИсхОдный код. ПРЬ8Ю Cusfomlnterface размешен в подкаталоге, .сощвеТСТВУl!i)щем гпаве 7.


312 Часть 11, Язык nрограммирования С#

Построение иерархии интерфейсов


Продолжим наше обсуждение вопросов создания пользовательских интерфей­
сов и рассмотрим тему иерархии U/-UТIeрфейсов, Вы знаете, что класс может высту­
пать в роли базового класса ДЛЯ других классов (:которые, в свою очередь. тоже мо­
ryт быть базовыми классами ДЛЯ других классов), но точно так же можно строить
отношения наследования и среди интерфейсов, Как и следует ожидать, интерфейс
на веРIЩПiе иерархии определяет общее поведе!ше. а интерфейсы, находя.щиеся на
более !Шзких уровнях, "уточняют" зто поведение. Для примера рассмотрите следу·
ЮIЦYЮ иерархию интерфейсов.

/I Базовый ИИ'l'ерф8Йс.
public interface IDrawable
{ void Draw (); }
public interface IPrintable IDrawable
{ void Print(); 1
public interface IMetaFileRender IPrintable
{ void Render(}: }

Соответствующая цепочка наследования показана на рис. 7.5.

п:nw.ы.. 11i'
"'bwfкa

БiI Methods

'. Pnгс

(',j Methods
• R~(1d<!r

Рис. 7.5. Иерархия интерфейсов

Теперь. если некоторый класс должен поддерживать все варианты поведения, за­
данные в рамках зтой иерархии интерфейсов. то этот класс должен выводиться из
интерфейса. лежашего в основе иерархии (В данном случае это IMetaFileRender).
Все методы, определенные базовым интерфейсом (или интерфейсами). автомати­
чески переносятся в определение. Например:
Глава 7, Ищерфейсы и ЩЛl1ВКЩ1И 31 3
11 9TO'l' .x.na:cc ПОjЦ.цеpzивае!l'! IDrawable I IPrintable и lМetaFileRende'Z;.
PUbl.l"C cla S$ suреIImэ.gе : IМetaFileRe:nder
(
publi,c void Draw ()
{ 'сопsоiе .Wri t:eLine ("Вд.З0ва.", ndГ1ИКа, визуаЛИЗiЩИЙ. "'); t
pUblic void Prlnt()
1 Сшню.Lе ..Wr i teli rJ8 (1' ВЫВОД :на ПРИН'I'Е:'р. ") 1 }

pUblic void Rеиdеr()


.{ COn$ole, .. W:r:!t teL:Lne ("!ВЫВ'ОД 'в метафаЙ;((J. 11) "

Вот npимер вывода Ii8ЖДОГО интерфейса из э:кзеМILiшра SuperItnage.

j I ИСПQJJЬЗQSаиие ~рФеЙС()з.
5t,cёltic voieJ Main (strirtg [] argfJ)
(
81Jре.tIщаgе .5i = ne.w Superlmage () ;
/I Поnyчеиие IDrawable.
ID:ra'Wable H ..fDraw· = (TDraw~ble lai;
i tfDra'ol .1)У а.. () ;

/I Пqn:учение IIЩ!tai'i.leReпdеr I ЖO'J!Ор!Й ИСПQmoJlУ8Т все Ke!1'O~,


j / опреД&.IJeJUUlе B.blDIe пО цепочхе kW1'ерФеИr:;os.
if (i tfIJraw 1з IMetaFileI}.ender)
'{
IM.etaF'lleJ'l.end€'!: itfMF = (IИеtаFllеRепdег) itfI..'raw:
i HМF . Remd'e r () ;
itfMF.Frint (1;

COТilsole. RеаdLiпе () ;

Интерфейсы с множеством базовых интерфейсов


При поотроении иерархии интерфейсов вполНе, допустимо создавать ИUТер­
фексы, которые оназываются nPОИ3I10,цными от nеСRОЛЫ(ИХ базовых интерфейсов,
Однако напОАШИМ:. что нельзя cтpo:trrъ массы, которые будут ПРОИЭВОД)iliIМИ ОТ ие­
СКOJIЬ,'I{ИХ базовых ЮIЗССОВ. длл примера предnолотим, что В:&l строите набор ин­
терфейсов, :моделирующих повеД'еlше, автомобиля.

p'::ibJlc interface те",!"


( VGid brlve (); )

public interface IUпdеn~аtеrСаr


( void Dlve(:); ]

11 З,десь ИИ'1'ерФ8ЙС _еет ,tIВA базоlWX ииu.ерфейса_


рuЬИIZ: lnterfac€ IJа:mеsВог.dСаr : Ic&:r, 'IUnderwэ~rСаr
{ void '!'\.1rЬоВ1;ю~t (); }

На рис, 7.6 ПOIщэана соответствующая цеЦQ~IRа: Щ;lтерфеЙсов.


-
314 Часть 11 . Язык программирования С#

Рис. 7.6. Общая система типов (CTS) ДОПУ­


скает множественное наследование интер­

фейсных типов

При построении класса. реализующеl'О I J amesBondCar (машина Джеймса


Бонда). вы должны реализовать TurboBoos t (). О! ve () и Dr i ve () .
public class JamesBondCar : IJamesBondCar
{
p ubli c void Drive () { Console . Write1ine ("Ускорение ... П) ; }
public void Dive () { Co n sole.Write1ine ("Погружение ... ");}
public void TurboBoost() { Сопsоlе.Writе1iпе('·Взлет!");)

Этот специализированный автомобиль можно использовать так. как и ожида­


ется.

s~atic void Mai n (s t r ing() args)


{

JаrпеsВопdСаr j new Jаrп еs ВопdСа r () ;


j .Drive () ;
j .TurboBoost() ;
j . о! ve () ;

Реализация интерфейсов в Visual Studio 2005


Программирование на основе интерфейсов - это мощная техника программи­
рования. но процесс реализации интерфейсов может предполагать ввод програм­
много кода вручную в очень больших объемах. Поскольку интерфейсы представ­
ляют собой именованные наборы абстрактных членов. вам придется вводить
программный код для каждого метода интерфейса и каждого класса, поддержива­
ющего соответствующее поведени:е.

Вы не ошибаетесь. если предполагаете. что в Visual Studio 2005 имеются раз­


личные средства автоматизации для решения задач реализации интерфейсов .
Предположим. что нам нужно реализовать интерфейс ICar для нового класса с
именем MiniVan. По завершении ввода имени интерфейса (или при помещении
указателя мыши на имя интерфейса в окне программного кода) вы обнаружите.
что под первой буквой имени появился так назьmаемый ·смарт-тег". При щелч­
ке на нем раскрывается список. предлагающий реализовать интерфейс явно или
неявно (рис. 7.7).
ГлаВ'S 7, И'нтерф.еЙсы 101 I:ОЛJН:ЩИИ 315

_" ,' ~Pac" .1JI'1Ш~ ilU!а~с}\у


EJ

tl
vtЮ 'Нс сlJ!·З>! R~ "l \7;!< r,
(
p\JЬHc- IIlr.-1V",r, (,)

~
tI

Рис. 7.-7. Реализация интерфеЙСОIi !!, ViэмJ Studio 2005

После выбора нужной вам опции Visual stшtiо ~005 QFе~рирует nporpам'МНЫЙ
код ЗarдylliКИ (В рамках соответствующей имен@ванцой области программнОI'О
I\од~J. который ВЫ затем можете ИВМСИИТЪ (обра1'Ите вщuщние на ТО. ЧТО.IlQ умол~
'1:Ia.НJm) реализация ПРeдлaI:ает иcюnоq~mе System. E~c.€ptiQJJ).
n:а11lеэрасе 1FOIсеНiе.rё1,rсhу
r
I pttbli ,c clas!i MipiV<J,n
{
ICait

p u bli с Mi:rIi'Val', ()
r {

I )

!I-re gian ICar Мепibеr;s puiJIic v p id, Drive ( )


{
rtew E2rсерtiой ("Tl'le methocl or Dpera,tl on i s not implementea.") ;

#еПф- еqi оn
}

Теперь. после Gбсуждени.нcnецифщщ построения и реализ8ЦШi пользователь ­


ских иатерфеЙеов. мы досвятим QOтaJ'OК :щавы рассмотрению ВС'ГрОеffi-lbIХ инт~р­
феЙеов. содержmциxСН' в бибщIQТ~ базовых классов .NEт.

Исходи"'" K~ [1роею lFaceHierarc!1y размещен в подкаТSflоге, соответствYJQЩf!'М таве 7.

Создание перечислимыx ТИПОВ


(IEnumerabIe и IEnumerator)
ЧтоБЫ перейти !( идmострanии nроцесса реали:зarщи сущест1'1WUWX интерфей­
сов .NE't нужно въшснитЪ роль IEnumerable и IE'n1.J,me;t:ator. Предnoлoжим. что у
нас есть хласс Garage (rараж). содержащий некоторPIЙ набор тIЩОВ C'ar (см. r.rra-
ву 6), хранимых. в Биде System.Arr ay.
-
316 Часть 11. Язык nрограммирования С#

11 Garage содерхи~ набор объектов Car.


public class Garage
{
private Сат[] carArray;
11 Начальное наполнение об .... naми Car.
public Garage ()
(
carArray = new Сат[4];
carArray(O] = new Car("Rusty", ЗО);
carArray[lj = new Car("Clunker", 55);
carArray[2j = new Car("Zippy", 30);
саrАrrау(З] = new Car("Fred", ЗО);

Было бы удобно выполнить проход по элементам, содержащимся в объенте


Garage, используя ,'конструнцию С# foreach.
11 Это каже~СR разyкиъtм . ..
public class Program
{
static void Main (string (] args)
(
Garage carLot = new Garage();
11 Дпн каждого об'Ъеlt~а Car в колnе1ЩИИ'?
foreach (Car с in carLot)
{
Console. Wr i teLine (" (О) имеет скорость (l) КМ / Ч",
c.PetName, c.CurrSpeed);

Но. нак это ни печально. компилятор сообщит вам, что класс Garage не реа­
лизует метод GetEnumerator (). Этот метод формально определен интерфейсом
IEnumerable. находлщимся в ·недрах" пространства имен System.Collections.
ОбъеRТЫ. поддерживающие соответствующий варианТ поведения. декларируют,
что они MOryт раскрыть содержащиеся в них элементы вызьшающей стороне.

1/ Э~от интерфейс информируе'l' 1I1IЗЫ8~ сторону о тои,


11 что э.пеиен'nI объекта перечиCJ1ИИЫ.
public interface IEnUmerable
{
IEnumerator GetEnumerator();

КaR видите, метод GetEnumerator() должен возвращать ссылку на другой ин­


терфейс- интерфейс с именем System.Collections.IEnumerator. Этот интер­
фейс предлагает инфраструнтуру, которая поэволяет вызывающей стороне вьmол­
IЩть цикл по объектам. содержащимся в IЕпumеrаblе-совместимом и:онтеЙнере.
Глава 7. и.1i1~рфеЙt;ы и J(10лле'КЦии 311
1/ З'l'ОW ИК'l'ер.феЙо РQЗ:ВОmlе9J JWЗloIE'аva;.eй СЩ'O]Iоие
11 B~ &ИутрЕ!ивие эзr8И8WJIR 'lCOB!1I~epa.
p-ublic im:er face IЕтшmе:tаtоr
{
Ьо01 MoveNext ( ) ; .1 / С'двину-ть на ПQВИЦИЮ впеРЕД.
object c\,Hrent { get.;] I J ПрочwrдТЪ (сзойс'I'~О ТОЛЬКО ДДЯ чтеНИ:Я·j •
V'oid. Reset (J ; / / Сд&RН:У'1'Ь ~ .!iаЧELrlр1j:уВ;З позицию.

Чтобы обеспечить поддержку указанных И'.нтерфеЙСов ТШlOМ Garage. можно


пойти по длинному I1yrn реализации каждого !иетада 8ручную. Кон:ечво, ничто не
запрещает 'ушшатъ свои версйи GеtЕnl1rnег'э,tоr (). N.oveNext () C,jrreI1t и. Rезеt (), J

но есть и более простой :путь. ПОСКQЛЬКУ Т1Ш 'S'Iзtеm.Аrrау • .ltaR и Мноrие другие
ТШIЫ. уже реал:иЗОВaIi: в IEnu11Jerable и IEn,11Inerator. вы Moateтe просто делеrиpо­
BalfЪ запрос н System.Array. K~ поназано НЙже.

us:ing SYSt~rC1, .co,llectiorIB:


рu.ЬИс: сlaвБ Ga:rage : IEnшnerа1йе
i
11 ~ ЭуstEЩI.Arrау »2[е есть р~aцюr Iltnumerator!
private Сат [J carA.rray;
public Саа ()
(
~,arArxa1 = new Car['~];
carAtray [О] = t!ew Сах ("FeeFee 11. 200, О);
carAr ,"ау [1} = Cl€JtI Сах ( "С1 Ш'Jkеr ", 9'0 t j));
carArray[21 = пе", Саr(ПZiF'РУ", ЗО, О);
сахАтта:у[3] =п:еwСаr("Frеd", ЗА, О);

pUbl:tc IБЛL1гnеrаtоr GetEIl1.1meratQr ()


{
11 :ВСsаращае'Ж' IEnumera.tor ое'lo8lt'11акасоива.
retutn caTATТ13:~ • Get2Er1lm\er!:!tor () ;
у

Теперь. после I"'ЮДИФЩ'ации ТЮ1R 'Garage, вы можете' 1-IСПQ.!IЬ;JОВ~ТЬ этот тип ц


ItOHC'I'pyкtnnJ. fфrеасh без Qпасений, R тому же. ПОСКОЛЬКУ метод GetEnurnera:t.or ()
oд:pe~. шщ открытый, полъ:щвателъ объента тоже может J;l3аИМодеЙСТВQвать с
типом IЕI1шnеrаtоr.

1/ Иanuаllу work. with IEnuшeсаtor.


IEnllli:Lerator i = ca:cLot ,.GetlCniltner.a tor (J ;
i . Mo·veNex.t () ;
Са.!" roуСах = (Car)i,Current;
Conso1e. Wri teL'i'ne (R' (О} имеет СКОРОСТЬ 11} к'\II/ч",
myCa'J: . PetNam~, туСаУ. Cllrrs'peed) ;
Еслц вы предпочтеТе скрыть ФУFПщионалъные возможно('ТИ IEI'J'u:i11erable на.
~ ~

объектцом уровие. 1'0 сде.а;ует исп(:UIЪЗOВать mшyю реализацию ивтерфеЙса.


318 Часть 11. Язык программирования С#

public IEnumerator IEnumerable.GetEnumerator()


(
11 Возвращает
IEnumera tor об'Ъекта маСCИJilа.
return carArray . GetEnumerator();

Исходный код. Проект СustоmЕлumегаtог размещен 8 подкаталоге, соответствующем главе 7.

Методы итератора в С#
в .NEТ l.x для того, чтобы пользовательские коллекции (такие. как Garage) до­
пускали применение конструкции foreach в операциях, подобных перечислению,
реализация интерфейса IEnumerable (и, как правило, интерфейса IEnumerator)
была обязательной. В С# 2005 предлагается альтернативный вариант построения
типов, позволяющих применение цикла foreach. - с помощью tunepamopoB.
В упрощенной интерпретации итератор является членом, указывающим поря­
ДОЕ возвра1Цения внутренних злементов контейнера при их обработке с помощью
foreach. И хотя метод итератора все равно должен назьmаться GetEnumerator ().
а возвращаемое значение все равно должно иметь тип IEnumerator. при таком
подходе ВЭllI пользовательский класс уже не обязан реализовьmатъ все ожидаемые
интерфейсы.

public class Garage 11 Без реl1JJизации IEnumerator!


(
priva t e Car[] carArray;

/1 Метод итератора.
public IEnumerator GetEnumerator()
r
Eoreach (Car с in carArrayJ
{
yield return с;

Обратите внимание на то. что данная реализация GetEnumerator() oCYJ.ЦecT­


вляет "ПРОХОД" по вложенным элементам. используя внутреннюю логику foreach. и
возвращает объекты Car вызывающей стороне, используя новую синтаксическую
констрyrщию yield return. Ключевое слово yield используется для того. чтобы
указать значение (или значения). возвращаемые конструкции foreach вызываю­
щей стороны . Когда в программе встречается оператор yield return. сохраняет­
ся текyrпая позиция. и именно с зтой позиции выполнение будет продолжено при
следующем вызове итератора.

Когда компилятор С# обнаруживает метод итератора, в рамках области види­


мости соответствующего типа (в данном случае это Garage) динамически генери­
руется вложенный класс. Этот автоматически сгенерированный класс реализует
интерфейсы IEnumerable и IEnumerator и указывает необходимые параметры
членов GеtЕлumеrа tor () . MoveNex t () . Reset () fI Current. Если теперь загрузить
ГЛElва 7. Иliте.рф~Йсы и КОЛJJ8JЩИИ 31.9

данное прилфкение в ildasU\ .• e1!e. то будеrг ВЙДI.J:о. что ВнуТренняя рещrизация


GetRrlUI11eratQ.~ () в объек:rе Garage исnцльэует сгене:рНpGВанн.ый КОМIIИЛЯтором
тип (КО1:'ОpБIЙ В Д<ЩНОМ uримере. .получает l1МЛ <GetEnumerator>d_O) .
. Itlethod publlc hidebyslg iщ;tаl1се .;la.s·s·
[1I1sCOI' lib] .sys tem. Col1ectio1\s .. IЕ:пuше'ха tOI
GеtErj'шuеrаtоr () ci 1 :Irtatlt;l.ged

newobj ii1stance voi.q


СustоII)EIll,.1П\еrаtоtWi tl"tYiel:d. G.aIage.! I <GetEnUl!!lra.t or>d_O ': ~ . ctQr (int32 )
...
I 11 епа of :mettiod Garage:: G€!bl;;:nume:t\:i tOl'

Ясно. что от цреДЛОЖe1l:НОТО здесь олредел:ениn метода итератора мы не по­


ЛУЧIIМ боJIЪШОЙ ло.iIЪзы. ПОСI(О.л:Ьтty FЩЩ тиц Garage изна~щ.!UiНо реализовывал
GelEnt1mer'a·tor (). ссыаи:съ на впутре;нний тщr SY'stem.Ar:ray. НО СШ:IТaRСИС ите­
р;;цора С# .может СЭКОНОМИТЬ немало Вре~liП~ при noст'рЩ~НИИ более ~ЭКЗО1·иц"-€CКИХ·
под.ЬЗОllатеlIЬСКИХ контейнеров (например, ВИRaрных д~peB.ьeB). еде ПрШОДR1"СЯ
вручную реали:зоваn. интерфейсы IЕпumеrаt ·оr :и IЕnuщеrаblе. В любом случае
программЮ>IЙ код вызывающей стороны при вэаимод~етвии с ме.ТОДОМ итератора
с испOlЛЬ3Oванием f:oreach окаЭhПIает~ ОlЩНакав:ым.

static void Main,(:;triDgrJ ar.g s)


{
С·рn'Зоl~. Wri t .e,Line (" *". Забавы
j/ '-' о м~:r'одами ирера'1'ора *,..,,. * \ n ") ;
GarG\Q"e са.!' Lo1;; = n.e w Garage.() ;
foreach (С3.С с in .c arLo·t )
(
Солsоlе. Wri teL.i ri~ (;; {О) _ее1' CR:OPOC'I'b {1} км/ч" I
с. Pe-tNёimе, с. Сш: rSpeed) "
}
СОПЭОlе.ttеаdLiпе():

ИОХOДИWЙ 1(01. ПроеkТ CUstornEhumeratotWithY"teld, размещен 8 подкаталоге., соотвеrсТЗуюЩем гnзвв 7.

Создание клонируемых объектов (ICloneabIe)


Вы, ДОЛЖНО быть, поМНJ,Jте из главы 3. что System.Object определяет член с
имев:ем МеПiЬеrwi.IТеСlрпе (). У.казапаьiй .Метод испо.льзуется для получеви.я nоверх­
носmиou КЮn:Uu объект. Пощ,.эовате;щ объекта не могут вызвать Этот мео'ГОД непо­
f средственно (ПОСRОЛЬКУ он; jJВJ]Rе'(СЯ защюцefI1lым••. но сам объект может вызвать
этОт ме.то.Д п . дроцессе ICIl()ltыроваttuя. Ддя пpt;IМ.ера предположим. что у нас €cть
1 власе е имен:ем Point (Точка).

/I К!1асс Point.
риЬНс ~lёl~s POi11t
I
т

320 Часть 11. Язык программирования С#

publi c int х, У;
public Point(in t х, int у) ( this.x = х; thls. y = у; )
public Point () {)
// Dереоnpедenение Object.ToString().
publ ic ove.rride string ToStr:ing ()
{ retur:n string .Fo rmat("X = {Ol; у = { 11
ОО
, х, У ); }

с учетом того. что вы уже знаете о ссьmоЧНblX типах и типах. характеризуе­


мых значениями (см. главу 3). вы должны понимать, что в результате присваива­
ния одной ссылочной переменной другой получаются две ссьmки. YRазъшающие на
один и тот же объект в памяти. поэтому следующее присваивание дает две ССЫЛЮi
на один и тот же объект Point в ДШIaмической памяти . и модифиRЭ.ЦИИ любой из
этих ссылок будут влиять на этот объект.

static void Main(string[] args)


(
/ / Д8е СCbl11JCИ на один и '1'0'1' . . об'Их'1'!
Point рl = new Point(50, 5 0);
Point р2 = р1;
р2.х = О;
Сопэоlе.WritеLiпе(р1);
Console.Wr iteLine(p2) ;

Чтобы обеспечить пользовательскому типу возможность возвращать копию


этого типа вызывающей стороне. можно реализовать стандартный интерфейс
I Cloneable. Этот интерфейс определяет единственный метод с именем Clone () .
public interface ICloneable
(
obj e <::t Clone () ;

Очевидно , что реализация метода Clone () будет зависеть от объекта. Но базо­


вые функциональные возможности оказываются одинаковыми: это копирование
значений ЧJIенов-переменных в новый экземпляр объекта и возвращение этого
экземпляра пользователю. В качестве иллюстрации рассмотрите следуюIЦYЮ мо­
дификацию класса Po lnt .
// Теперь Point поддеpsивае'1' xnонироаание.
public c l a ss Point : ICloneable
{
public int х, у ;
рuЫlс Point() { )
public Point(int х, int у) ( this.x = х; this.y = у;)

/ / Возвращение хопни данвого обrъeХ'1'а.


public object C'lone ()
{ return new POint(this.x, this.y);
public o v erride string ToString()
{ return string.Format("X = {О}; У = (l}", х, У ); }
iЛава 7. Интерф.~Исы и КВJlлещми 321
с помощью уиаза1IНОГО ПQДХОДщ можно· создавать точные JI ReЗaвIilсимые RОlIИИ.
типа Poin.t. как nО~IШО в CUI~дующ~ фрarменте црогра:ммноп~ 'Кода,

statiG vold' Hain (stIi.o.q [] args,)


{
/ /Обр&'J!И'1le ~, C],OIЦI
(") аоnрaиr;a8'1' o&ъe.x'.r обще1'О "'1'Ипа ..
/1 ДmI пощче_ npoиsаодко1'О flШа ИСПО~8
11 __ 08 ареоl!Sр.з~а.нке.

Р9tлt р3 '" new Poin't (10'0, 100);


POin.t р4 = (Point") рЗ. С1опе () ;
11 ИsК8Jreииel р4 .:К (S!I'O K~ .JII.IPI8НИ'J.' рЗ _х).
р4 , х = О;

/1 e-ооЦ· o&.e~a.
COhsole.W.r iteL·ine (р3);
Console. iilri t:Е;Liле (р.4);

Техущая реализация Point решает все поставленные зцца.чи. во ВЫ MO~ Ije-


МНОТО усовершенствовать процесс. Бвццу того, ЧТО тип PQint не содер1\ШТ пе,ре­
менных ССЫJlочвоrо типа . .можно упростить реализацию метода С1от, () . как до­
казано ЮОКе.

JНl'blic' Clbject Clone (.)


I
/I CxOQJq)DB8WЬ все J1OJX1I Po~nt "noчnlЦDtо".
returD this .Мemhеrwis.еСl'оле (); I

При нamrчии в :Poil1t ЧJJeНов-переменных сСъшочнoro тиtrа метод МеmЬеrwiэSCl{)l1е ()


Сш:пшрует ссыJшиа соответстВующие объекты (т.е. выполнит п.oвв~ ICOnu-
po8й!:U.I.e). ЧТоБЫ обеспечить nOMep>Ккy полнота lЮПИрования объектов. вы ДОJIЖНЫ
вnpоцесее юrонирования создать новый э-кземnляр .цлп яаждой пе.Рe1\fe!ffiОЙ ссы­
лочного типа. СоотВетствующий пример мы сейчас рассмотрим.

Пример кnонирования
Предположим. что клаСQ 'Б'оi ·пt СQде'рЖИТ член ссылочного ·TIJUa. с именем
РQiЛtDеSсri.рtiоп. обеспечивающий поддержку ~панятн()ro" имени объекта Point
и ero.идеFi':rИ:фикационного номера в.виде System.Guid (ecmlY вас нет OIlЬJТв, при­
менешm сом, знайте. что GШD - rлобалъно унивалъный пдентnфикатор - Э'ro
стаТИC'rИчоски уникальное 128-раарвдное значение). Вот соответствующая реали­
за:щш.

11 ЭJl1Q'1' ЖJIасс ОПJ4mJВаe>r !l'o~kY.


pvb-1ic сlаз·з РоiпtDеsс.r;iрtio.n
1:
11 ~~ Д,п,I ~ос'РОЩК ..
ршJliс s t,riHg реtNа:п.е ·;
public Guld :роiпt.IP;

p).1blic PO.lnt;Description ()
J
-
322 Часть 11. Язык программирования С#

this.petName = "Без имени";


pointID = Guid.NewGuid();

При этом для учета НОВЫХ элементов состояния в самом классе Point следует
изменить метод ToString (), а Та1\Же операторы определения и создания ссылоч­
ного типа PointDescription. Чтобы позволить ~внешнему миру· указать имя ДЛЯ
Point. можно Та1\Же модифицировать аргументы. передаваемые перerруженному
конструктору.

public class Point : ICloneable


(
public int к, У:
pub1ic РоiпtDезсriрtiоп desc = new PointDescription();
public Point() ()
public Point (int х, int у)
{
this. к = к;
this. у = у:

public Point(int х, int у, string petname)


(
this.K = х;
this.y = у;
desc.petName = реtпаще:

public object С1опе()


{ return this.МещЬеrwisеСlопе() i
public override string ToString()
{
return string.Format("X = {О}: у = Il}: Имя {2) ; \nID (З)\п",
х, у, desc.petName, desc . pointID):

Не забудьте о том. ЧТО вы еще не обновили метод Clone () . Поэтому при запросе
клшm:рования объекта пользователем с помоЩьЮ данной реализации все равно бу­
дет получена поверхностная ("почленная") копия. для примера предположим, что
мы обновили метод Main () так. как показано ниже.
static void Main(string[] args)
(
Console.WriteLine("***** Забавы с ICloneable *****\п");
Console. WriteLine ("I<лонирован рЗ, новый Point сохранен в р4") ;
Point рЗ = new Point(100, 100, "Jane"};
Point р4 = (Роiпt)рЗ.Сlопе();

Сопsоlе.WritеLiпе("До модификации:");
Сопsоlе.WritеLiпе("рЗ: 10}", рЗ);
Console. Wri teLine ("р4: (О}", р4);
p4.desc.petName = "Мистер Х";
Глава 7. Интэрфейсы и ~f)лпеIЩИИ 323'
р4.х = 9;
CO,D sole.WriteLine ("изыенены- р4.dеsс.p€,tName и р4., .:Х" ') i
·С O l'l-S о 1е • Wr i teL 'I..ne ("ПоСле :МQд:ифИI<:а1(ИИ~ О ,) ;
Солsоiа,Writ;еLiпе '''рЗ . : (О] 1', р3);
.Consol е . Wri teI.ine ( .. р4: (О )~I р4);

На рис. 7.8 nOImЗЭН соответствующий ВЫВОД.

Рмс.7.8. Метод МещЬе r,wiэе'Сlопе О возвращает поверхностную копию объекта

Дщ!ТОГО чтобы метод ClQne {) ~озврщцал- ЦDлные копии :ВНутренних .сСРШО'ЧRЫX


1:'ИПОВ. нущно "'цаУЧИТЬМВОЗВР~Q,еыыйметодом МеmbеrwisеСlопе ()объектyчитъt­
B~'FЬ теJ(yЩее ~ ti1бъ~q. Poi,nt;. (тип SYSt:eIТI.Gu:!.d яв.пнеТСfl СТРYI<турой. та:к что на
самом, деле НО:IЩрущ'I'СН Щ1сл:о~ъ;1~ даНные). ВОТ одна из возможных реализаций

11 МII .ц~ ~ec'1'Ь ШlJJИЧИ8 ч.пеJl& PointDesoription .


pti'b Ii c object Clon.e ()
1
Point !1 e~Poi.nt = !P'bint) this .МеmЬе,rwisеClоn~ ():
FОiп t D~ 6'с:riрtiоп сып:е !) t Dе sс '" new Poin'tDe5 Cx:ip t:i.on () ;
currentDe.'H:.petName = t his. de s c . petName;
lle1llPoint. desc = t"u r re!It.'Desc.;
ret ur n newPoint;

Еелй выполнить ПРИЛОiЖе}ЦIе тe:neръ. ТО.ВЫ увидите (рис . 7.9). ЧТО возвращен­
ный методом Clone () объект Point действительно I<DIrирует .:мlyТpеЮlя.е ССЬJЛоч.­
вые члены-переменные:гипа (обратите внимание на то. что эдесь р3 и р4 Щlltеют
СВ(jИ yникв.;n.'НЫе имена) .
Итак. в том случае. Rогда класс или структура содер)Кит только ' ТНЦЫ. ха­
рю(т.еризуемыe значеюlЯМй. лучше реализовать .метоД CIOlle (.). использующий
MemberwiseCl o.ne (). Однако в том сдучае~ коtда ПOJ1Ъ:ювате,лъсюm ТИП содержит
СCЬL1l0ЧНЫС :rипы. :вы должны создать HOB:t>.Iй тИП. П,PJ1ню,.twoЩИЙ :(Jo вющ:ание Bt:e
ч:ле:ньt-переменные ССЫJ!очн:огоnшa.
-
324 Часть 11. Язык программироаания С#

Рис. 7.9. Здесь получена полная копия объекта

Исходный код. Проект СlопеаЫеРоiпt размещен а ПОДкаталоге, соответствующем главе 7.

Создание сравнимых объектов (IComparabIe)


Интерфейс System.IComparable определяет поведение, поз:воляющее сортиро-
вать объекты по заданному ключу. Вот формальное определение .

/I ЭТОТ иитерфейс ПОЗВОJUlет об'lo8lt'l'Y указа>nt его с.нз ..


/ / С ,црyrиии подобl6lКИ об'1оеItТами.
public interface I Comparable
(
int CompareTo(object о);

Предположим теперь. что класс Сат поддерживает некоторый внутренний


идентификатор (предста:вленный целым числом. хранимым в переменной carID).
значение которого можно устана:вливать с помощью параметра конструктора и
изменять с помощью нового свойства IO. Ниже показана соответствующая моди­
фикация типа Сат.

public class Сат


{

private int carID; public int 1О


(
get return carID; )
set carID = value; }

public Car(string пате, int currSp, int id)


(
currSpeed = currSp;
Гпрва 7. Интерфейсы", I(ОllЛ8КЦUlИ 325
petName = пате;
сатН) = id;

П&дъзоватеJШ объекта могут создать массив типов Сат так.

sta tic vobl Ма in (s tr i ng t] args)


{
1/ Соадаиие мaQCJaI8, orJцit>a ca~ ..
Са !: [] myAutQs '" леw Сат 15 J ;
JI1IY.1l.1.1··tps{O] = new C.arC"Rusty", ВО, 1);
:myAut:01S[l] = n~w Car("Nar:i", 40, 234);
myAutos [2) = Df:JW Сах
( "Vi p e r'i I 4 О. 34);
mY-АutО$ [3] = ре"" .Сат ( "Меl ", 40, 4);
myAu't-оs[4]. = new ~a r ("Chuck y ", 4:0, 5);
J
ВСДQмнtiМ, -ctrоюrэ.сс Sузtеm.Аrrау апр~деляет статичесюШметод S{;n:tO.
Вызl1Ш3 мот метоДДЛЯ массива :встроенных типов (int. sЛо..rt , .str ing и т.д.), мож­
по ОТСОp'rироватъ элементы в массиве в ·ЧИслОвом. или anфавИТ1Jом пор,вдке. по­
С}(О)1ьку вcтpoeнНЬie тш:оо данных рeanизyю:r lСо mратаЫе. Но что ПРQИ3QЙД~ в
ТОМ сдучае. I(отда методУ Sort () будет передан массив типов Сат. щuc цр~;;uю
ниже?

11 S:v,цym nи O!I!соptЖ'ИpOвaиw ном U'1iоItОбшu«?


Ar'Lay. Sort (myAutos) ;

Запустив этот пример. вы обнаружите. что cpeд~ въшо4Н!ЩИВ сгенерирует Ис­


IU1JOчение .Ar.gurnentExCE!p tion о сообщением следY1QЩ~l"О сt'lдеР/КЗНИJl: "Каи мини­
мум ОДЮf объmt'Г должен реализовать lComparable"". Чтобы ПОЗ;ВОДИ"l'Ь сортиров~
Массивов :ВaпIИX ПОЛЪЗ0ва'J'eJIЪСКИХ ТИI1GВ.· 1JЫ д!>~ реализовать J;ComparaDle.
При создании Compar eTo () вш дom.к1Jы решить. Ч')70 .дo.JДКНO лежатъ, в основе соот­
ветствующей операции упорядочения. дiIЛТИIIЭ. Gar оамым црдхрд,цщим ":RaIЦИДа-
1'ОМ· яв-ляется: c.arID.
11 ПО~8До.а.~em.IJОQ'1'Ь Car .мo:aJ.O УПQpRДОчwп.
/1 и.· осво.8- Car.I D.
public Class Cat : lСащрarab~.
{

11 Р811.JIИ$&циа ICCllllpar~],. ••
int IComp.arablce . CompareTP(object obJ}
!
Car~emp = (Car) obj;
if (this. car!D > temp_oar~p)
retlJ.rn ~;
i f (this.c.arIP <t.emp.carID)

e lse
re t-ur.n О;
326 Часть 11. Язык программирования С#

Как видите. в CompareTo () выполняется сравнение посrynившего типа с те­


I<уЩИМ зкземIlЛЯрОМ на основе сравнения значений заданных элементов данных.
Возвращаемое значение CompareTo () указывает, будет ли данный тип меньше,
больше или равен объекту сравнения (см. табл. 7.1).

Таблица 7.1. Возвращаемые значения CompareTO ()

Возвращаемое значение Описание

Любое число , меньшее нуля в данном порядке сортировки текущий экземпляр размещвется
до указанного объекта

Нуль Этот зкземnляр равен указанному объекту

Любое число, болы.uее нуля В данном порядке сортировки текущий экземпляр размещается
после указанного объекта

Теперь, когда тип Car "умеет" сравнивать себя с подобными объектами, вы МО-
жете записать пользовательский программный код следующего вида.

// Проверха интерФейса IComparable.


static void Main (string [] args)
(
// Создание NаССИ8а типо. Car.

// Вwвoд исходного кассива.


Соnэоlе.WritеLiпе("НесортированнЫЙ набор .машин:");
foreach(Car с in myAutos)
Coosole.WriteLioe("(O} (l}", c.ID, c.petName);
/ / Тепер.. сортируем их с ПОNОЩЪ" IComparable.
Array,Sort(myAutos);
// ВЫвод О'1!сортированного иассива.
Console. Wri teLine ("\пУ.rюрядоченнЬ!Й набор Ь4ашин: ") ;
foreach(Car с io myAutos)
Сопsоlе.writеLiпе("{О) (l}", c.ID, c.petName);
Coosole.ReadLine();

На рис. 7.10 показан соответствующий вывод.

Рис. 7.10. Сравнение автомобилей на основе значений 1D

i
Глава 7. Интерфейсы .и коллекции 327

Сортировка по набору криrериев (JComparer)


в этой верCJЩ ТИIlа Car в :качестве щmтep:ия ynoРfЩОЧ:eн:иiН мы ИСП<ЩЬЗOБaJIИ ID ав­
томобиля. В другом случае ДJIЯ сортировки МО3\НО использовать. шшример. petName
f'q'ООбы разместить автомоG:ил:и в шrфавитном порядке Щ названий). НО -ЧТО делать,
если нужно ОТС0ртировать aвroмобили и по значению 10, и по зпачени1О petName?
В этом случае вы должны ИСП0ЛЬ90ватъ другой стандарт.!31?Щ иwгерфсЦс. определен­
I-Jый в рамках прос:гранства ю.'Iеи SY5t~m.Col1eC"ti~ns, - :mn'ePФейс IComparer.
11 'fJmичвr.IИ способ сраввeIIИiI ,цвУ'С сб~ов,
interfac~ ICoмpa:rfЦ"
{
int Соmра,пэ (object .01, Qbject 0:2);

в от:nичие От ICornpa:r.able. интерфейс IComparer обычно реализуют нес ПОМо­


щью типов,. которые npедnьлatае:'ГСН сортировать (В ДШП-1ОМ uлyчае это типы Сат).
а с ПОМОIЦPЮ неКllп'орОГQ набора :ВСIЮМоtательных нлассов. по оД:ному дли каждо~
го nOРЯД1tа сортирomm: (petNarne. ID и т.д.).ТИп Car (автомобиль) уже "3Haeт~. кеш
сравшшать себя. с дpyrими автомоБИЛНМlI на основе внутреннего идентификaro­
ра I.[J. Чтобы uо.3!10ЛИТЪ ПОЛЬ301sателю объеltта отсортировать маСсив ТI'ШОБ СаУ
.по значению petName • .нам nO'I'ребуеТGЯ вспомьгателыIый ЮIасс. реализующий
IGompare.r. Вот ПОДХОЩllц:ий для Э"ГDto программ:НыЙ I{ОД.

11 ~O!t':аСПОИОI"а!1'епЬJWЙ JЦlaoc ИСJlIOnъэуеlrС'l ,чn'l СQРIrИpОВIOI(


JI маоoQа обоъеJt!1'ОВ Саж: ·nо наз:aaвюD.
using S;yst~m. C011ectioI'l&;
public class Pe1:NamaCompaLer ; IСошраre.r
!
publ i с PetNameOoI)1par~.r () { .1
11 про:аерll" liа~вa.н:Юi об'l4JtТОВ.
iпt !()'::.tПtраrеr ..Соrnра.rе(оЬjе·с-t 01, object .02)
'{
Сау tl ~ (Car)ol;
Сд!' t2' = (Сах) 0.2:
r~t'.)rn Strinq.C01llpare (tl.petName, t2 .petNam.e);

ЭТот ВСПОМОГ3"1'ельны* RЛaCС МОЖНО использовать в црограммно:м ~oдe IlOJТЬ­


Зовател.н объехта. Класс System .Array предлагает п~реFружеШ'lhli; метод ЭОТ t О,
один из .вариантов Ш1торdГо ,1J;опуснаст исполъзоввние dбъеега. решшзу'!ощfJГО ин-
терфdiс IСшпрarеr (рис. 7.11). .
static "о10 MiiiI'l (s'triпg[] ЕТЧЗ)
{

I/ ~enеръ COP!2!J:IPYe!li по UМЕ!ИИ.


АТг,ау .Sort (.rпуАutоs, ре.. PetNameCompa;ret О);
/I Выво;ц О!1'сортироваНИоrо массива.
COIlsole. W'ritеLiле ("\:n!{ПС\РQдо.чение по назьанию:·");
foreacb(Car с in. myAut:os)
сог:зоlе.WritеLiгно·("jО} J11", c.ID, c.petr!3.meJ I
-
328 Часть 11. Язык программирования С#

Рис. 7.11. Сортировка автомобилей по названию

Типы, определяющие сортировку.


и пользовательские свойства
Следует отметить. что с помощью пользовательсКИХ статических свойств вы
можете помочь пользователю объекта отсортировать типы Car ПО заданному эле­
менту данных. Предположим. что в класс Car добавлено статическое свойство
SortByPetNarne () . дocтynн:oe только для чтения и возвращающее экземпляр объек­
та, реализующего интерфейс ICornparer (в данном случае это PetNarneCornparer).
11 Здес. обеспечиааетс_ поддержха поnъsователъсхоrо свойства дn_
11 воsвращеим. "правизп.ноrо" интерфейса IComparer.
public class Car : Iсощраrаыle
{

11 С.оЙСтао, возвраЩl'пцее хоипаратор SоrtБyРеtNa.me.


public static IComparer SortByPetName
{ get { r e turn (IComparer}new PetNameComparer(); }

в npoгpaммнoM коде пользователя объекта теперь можно выполнить сортиров­


ку по названию. используя ассоциированное свойство без какого бы то ни было
')'поминания" специального типа класса PetNameComparer:
11 Сортировха по имени становИ'1'CR H8IIНoro проще.
Array.Sort(myAutos, Саr.SоrtБуРеtName}i

ИСХОДНIolЙ КОД. Проект СотрагаЫеСаг размещен в подкаталоге, соответствующем главе 7.


'nщерь вы Д~1. ТЮндМЩ'Ь BfI "'0J.1'J;dЮ Tt), '!Щ}{ wреД.е:!!ЯЮ!J'eЯ и pewщзухrreн
nmы ин'Гfффей~ 'fI() и ТO,:ES Ч€М Щ Щ):~ ~~ yвt::peIO;!l. uнте.pфclieы можно 06~
Wlp]JЩi1'Ъ в J'IlQ.бом. :аЭPJЩ)ВЫR ЛрОСтpaIК"ТS; имен .NEТ, fI S зanеpm~.нщ: этой rлlШЫ
)IЬt P<:tС~I\>IO,!,рЩi{ лрf~ы 'щrтерфеuсо:в ('И Ва..<1€IВЩХ ~CWii) Ш ~cтpaн:c:rвa имен
ЕУ-s'tsщ .cё':.Jll e.dt: iЩ:t:!;.

Инте'рфейсы И3 простран,ствэ
l1м:ея System,.Co:llecti:ons
ВУй'lч€€'me СЩ4(JГQ ~Т:Ц:НОЩ ~paA(O&feт~ 1'ИП s~~'.Al;'J:,gy,.
В Щ~~ :3 бьJp:rд IIOEa,g;uro, ЧТО 'У..л.аJX Sуэt1О'l!! ,Arr,,-'{ npеддаГоЗ.е'l цe.,lТblii рад С!)ОТ'
~.YJOЩ~ »G<},МЩ'RВОС~Й (1'a1fn':1Щa ,И!I{В«;РТНРОWНfiе. ОQРТlilPQЩ<а. очв~п:а 11
tlе~'!<J'IJ[cэ;t~Iе). Но lиаее ,'АУ r ау дм.еет С:В!Ж' о:гр(l.'ЮfЧ~,1-:ЦЩ. :n IlCЦJБОiQее важщ.~:,0~
нs: я;I\ЛJ!!;('WJt Н~OSМOЖflо(,n. ДIm:uтче(:IrOГI:>, I1ерео~дещ~ШI~ pal1Jl4ep0J!l' nPJi!i ДQ­
бщlле--cliЛI 11 У.д:ai1rеt:I,ИП Эn€МtЩТФ'1i.'. Еt::J,Ти ДrnJ ~eJ'U'UI Тl'ЩйВ аеобхщmм БQдее ...~
JCИЙ~' 1'toa:rdhlep, лучщ~ JI:t',До~ов:ать ТJЩы. Onp,t'i,te,zr€il'JВЪte В :ПРЩтpa1IС'fflе ~Щ!!
$У'тtе;щ. С:оl1~,,(З t ±dl'il S(I;IJЩ, .в (Щ:O"fвет(,"1'ЩfИ С Р~К,t;lмеFW;Щ~ r,(I'aвbI l'O, щ1 пrю­
с':tpaщтв& имев ,S:,'[:Stem.~Q;Q11"ctiQn;S .:Gе:пе:zr1:~}.
Лрострm.tМ'):Щ имц:, SystO!;1Тl:,Co1l!ectiQ;us Ф1:l;реде~т ЦfC.зnШi: PfЩ и:нт~феЙ~Dq
[в. ~Ke;тOPыe :И:-9 Н1Юi УlIЩJ' ИUIlQ,льзона:писЪ в lWи:м:ер~ Э'wй ГШUlЫ).. ~ u:pавиn'О.
WIOreR"ЦИliI' ВШtесов pew:mэyет 9ТИ Я1Нl1еptf!eИСJ>l tЦJ~ 'ТOГlil~ 'Ч1'O(j'Ы 'ofreслечиlГ.Е;. ДQC'I)'П
.k cnPt",МY е(Щ~ржцмому, В 'Тз,бд. "1 .2. цр~"СЯ, QnИС;WUЯ ~YOB~ ИJl'терфеЙаQВ.
't:1Di«)СЮЦИООJ}3 ~ .IIЩJIil:elЩRПJ'.t;.

lCol1eC:'UDjl' ОI1Ред;еn.я,sт общие Xl\раlC'rЭj,'IИСt.шttи {T8K~e. кг~ ,9aI,ц~.LJ.te"'1Юё'IЪ o'Jer-


.ч,иIФВ 1'\ цеГ!онек) AfLII tl1lПS l«l/lrr~lЩll1l'!'

T1:bmpa:re.r Пfit'Э.ВО:~Т СI)IaВЫSIilWi,ДВУХ ~e~\JВ

IDiQt:lom..ry П~Е\roI!lе... r;!GЪ6\\1У 'np8Д't'ТВ1!1'rjЪ ero см'It~Ц,ое Q 'IIО~ОЩblO пар


i\Щ,Эft .L4 $1:mч",,>IИ~

lDi,'ф ;'ООаr'!'ZrВ1I1п:е..r'.,.!щr ПереЧ!:fСМG1 flодер1l<иlwtое ТW1i1, [1CЩ1lе'рЖI'I~~rе .lD1CftJ..oD-:ir;г


~'jme~~ы. I3riзsрalJ.JpmИНТ9рфеЙео 1 Е: !.'l,QшеГ.аtGт "для дaHMГ~ o~
r~}jli1f!r~t.o[' ОUШ',fisциаае:r6б!lWЮ l1iодце.DЩ пере!ШC!I'ВНI.tЯ ПОiЦiWlОВ ,с DO~.{I')­
ЩI:IЮf ~·ea'e11.
!l!!а$ЪСQdеРt'о'.r:i"~!еr Щ>~~рЗ1JЩ~Т }jEIl!J"J1iiJ;Ц,;:wв. fJeat1(о<1:ЭУJOЩ~Р(t rnn:o" "спель~ ",aC:~Ill1-
ваеr'Н'JЙ' ;!I~Г.0р.\oIi'М,.\IeJlIJI4J;!Qшrl~~:Я
'l!(~с.:,.щpe:.:r;.е:! (Эlior ~теDфе~с ЯiВляеttJ\ i"I&,IJbfМ " .NEТ 2.0.). 'ООЬед,i4.fМЮ1: ~1ЩIiI­
Q\\aтJьИЫEl IШэмО'JКmJСТ)'{ iСtJлnра T""r и' 1 Eiaml"iCoi:J е !rtJQV-!'a,<! t, \f:Т:o.-'
~D6еG'rIaЧЮЬ[):РmRIенж. 06ъе1l,-;rI)В Л{i) JIIX ~ш-J(.oдEW (еШlIil oOъe&"m31
p'I}Bl'lbJ,1'O [Щi! ДЦJ1Ji&IЫ вmЩР:Щ,\fijt,. 'ОА!o'II~fЩООEi,lе IIf1t1!11-КОДЬ1)

lЪiiя'С ОбеСfl$ивает ElОЗМQ;р;,чость.до:ба:ВFI~И5f" \(ДЗnetil~Я fI! 'ИliДi!I\!VИPQIЩ­


~И:Я зле~,tе'IТоtl З" On~1mе t)бъецus; ,KwmMtt. ТР'Г{1 'С 11Q~ОIЩ>Ю"'!JIеIЩВ
moro IUiТерф~c;J M~MO :s.м~(1ии:rJ.JflВЛ\llе11!':f!l1U" .qi!i.I.I(НЫ'Й 1i11~ml;l"
~ WJ:U\Щ"'~ At)(j~I;IЬ,IM 'l;олы;О p,п~ "Ire'!i1!lЯ, И ИМ~ ЛI'I Df!
ФIilIЩИРOj:l1Jн~rыj;j ];Ш!!МiJ,Р
ЗЗО Часть 11. Язык программирования С#

Многие из ЭТИХ интерфейсов связаны иерархией интерфейсов, в то время как


другие являются автономными единицами. На рис. 7. 12 показана схема взаимос­
вязей между указанными типами (напомним, что один интерфейс может быть про­
изводным от неСкольких интерфейсов) .

......------Dt' =- а
1j) j<;)-_ _ _ _ _- '

.-- -- - ~ -- _.- ..
~ . - - - -- - ---
I=odo!Ptovlder
. ,. !!' I I' ~
I ~

:i';.'!
~ __:.!

Рис. 7.12. Иерархия интерфейсов System. Colle c tions

Интерфейс ICollection
Интерфейс
ICollec tion является простейшим интерфейсом пространства имен
System.Collecti o ns в том смысле, что этот интерфейс определяет поведение,
подцерживаемое любым типом коллекции. По сути, этот интерфейс обеспечивает
узкий набор свойств, которые позволяют определить: а) число элементов в контей­
нере; б) защищенность цепочки контейнера: в) возможность копирования содержи­
мого в тип System.Array. Формэльно ICollection определяется так, Jtaк показано
ниже (обратите внимание на то. что ICollect i on расширяет IEnumerable).
public interfac e ICollection : IEnume rable
{
11 Член IEnumeraыl •. ..
int Count { get; }
b ool IsSync hronized [ get;
object SyncRoo t { get; }
v o id CopyT o (Array array, int index );

Интерфейс IDictionary
Вы, возможно, знаете, что CJЮварЬ- зто коллекция, обеспечивающая поддерж­
ку пар имен и их значений. Например, можно построить пользовательский тип ,
реализующий IDictionary, в котором вы сможете сохранить типы Car (значения)
с возможностью их последующего восстановления по IO ШIИ petName (это примеры
имен). Интерфейс IDictionary определяет свойства Кеуз и Values, а также мето-
Глава 7. Интерфейсы f1 коллеЩI1И ЗЗ1

ды Add{). Reцnove () J)J СЩltаirlS О. Отдельные элементы можцо 1I0JIyЧИТЪ С пОМО~


щыо lЩЦексщгора ТШ1а, Soт форма.пъ;н:ое оцредещ~ние.

рuЫiс interfa.c:e IDiсtз.оnary : lCo11ection, IЕnUПlеrаЫе


[
Ьооl IsFixedSlze I get; I
Ьо01 ISReadOnly { get; }
obj'ect; this t object key 11 ge-t; set; I
ICollection КеуIЗ {get; },
ICol~ection Уаlиев ! get:
void Ад'д
(objec,t key, object value) ;
VQid Clear () i
Ьооl Contain$ (object Кеу.),'
ItJiсtiопаrуЕ11Шl1еrаtоr GеtЕnШl1е.r·аtоr () i
VGi.d Rerno·ve (.obj eot key);
)

Интерфейс ID:ictionaryEnumerator
При внимательНQМ чтении вы моти замеТl11'Ъ. ЧIТО IDiсtiопату.GеtЕhuд\~rаtот 1)
ВQзвращаетэ;кземnляр lDiсtiРDаrуЕщunеrаf..оr:'.Т'ип !Dict.ionaryEnurnerator-
это строго ТЩЩЗ0Вa.ннъr,й. нумератор. рас1DИpШОЩИЙ IEIJUmer:at:or путем добавде­
ниn CJlедУЮщей фую:щио;надьиой возможности,

public iIlter:rn.ce Шiсtiоnar:yEnWII8J:аto:r : lEJ:Jшnеrаtоr


r
11 )Je!l!O,цu n:numeratoJ;:, ..
DlсtiопаrуЕпt.rу Entry ( get;
dbject Кеу { get; }
abj eqt Уа} iJ е { ge.t; }

Обратите В:Нl'maние:на ТО. что IDictiOh-аrуEIшmеrаtоr о.беспечивает возмож­


ность пере'IИсленил элементов словаря с помощью общего свойства Entry. ICn'Topoe
возвращает тип класса System .СаJ.lёсtiопs . DictionaryEntry. Кроме того, вы мо­
жете JlЬПJОJIНИТЪ цш:tJI ПО парам имеН и аначений, JI1СПОЛЪЗУЯ свш1ства K€y{Val'Je'.

Интерфейс IList
ПослеДШIМ из :клюttевых Иftrерфейсов Sуstеm.СоПесtiОJ1S ЯБЛНется I'n-rrерфейс
IList, который обеспечивает ВОЗМОЖnость вставки. Уд8.J"'Iения п индексирования
элемt".RТQв контейнера.

p1iblic interface nist : ICol1ec;tiorl t IEIiumerable


I
bo:ol 1 sFi xed3i ie { че t ; }
Ьооl lsR:eadbnly I get; }
objectthis 1 int iadeK J (cget; set.; }
int Add(objec:t у.аlие) ;
\7oid Clear();
Ьо:оl COTltains (.object value):
int IndexOf(object -17аlие) ;
-
ЗЗ2 Часть 11 . Язык программирования С#

void lnsert(int index, object value);


void Remove(object value);
void RemoveAt(int index);

Классы из пространства имен System.Collections


Еще раз подчеркнем. что интерфейсы остаются бесполезными до тех пор. пока
они не реализованы соответствующим классом или соответствующей структу­
рой. В табл. 7.3 предлагаются описания основных классов из пространства имен
System.Collections вместе с ключевыми интерфейсами. которые этими классами
поддерживаются.

Таблица 7.3. Классы System.Collections

Реализуемые
Класс Описание
интерфайсы

ArrayList Представляет динамически изменяемый по размерам массив IList,


объектов ICollection,
IEnumerable,
ICloneable
Hashtable Представляет коллекцию объектов, идентифицируемых IDictionary,
по числовому ключу. Пользоватеl1ЬСl(ие типы, хранимые в ICollection,
Hashtable, ДОl'1жны обязательно переопределять System. IEnumerable,
Object.GetHashCode() ICloneable
Queue Представляет стандартную очередь FIFO (first-in, first-out- ICollect ion,
первым прибыл, первым обслужен) ICloneable,
IEnumerable
SortedList Подобен словврю, но здесь элементы могут быть также доступ· IDictionary,
ны по позиции (например, по индексу) ICollection,
IEnumerable,
ICloneable
Stack Очередь LIFO (Iаst-iп, first-out - последним прибыл, первым об­ ICollection,
служен), обеспечивающая функциональные возможности стека ICloneable,
IEnumerable

Вдобавок к этим ключевым типам в System.Col;Lections определяются не­


которые менее значительные (в смысле частоты использования) Мигро.ки", такие
как BitArray, CaselnsensitiveComparer и CaselnsensitiveHashCodeProvider.
Кроме того. это пространство имен определяет небольшой набор абстрактных ба­
зовых классов (CollectionBase, ReadOnlyCollectionBase и DictionarYBase). ко­
торые могут использоваться для построения строго типизованных контейнеров.
Экспериментируя с типами System.Collections, вы обнаружите. что все они
Wстремятснn использовать общие функциональные возможности (в зтом И заклю­
чается суть программирования на основе интерфейсов). Поэтому вместо описания
всех членов каждого класса коллекции задачей нашего обсуждения будет демон­
страция возможностей взаимодействия с тремя главными типами коллекций-

___ _ __ _ _ __ _ _ _ _ __ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ __ _ _ _ ь~
Глава 7, Иl:IТерфейсы ~ КОМ:~КЦИ~ ззз

~rI'зуЫ st, Оиеие and' Stack. ОСВО:leШ фymщиональныевозмоЖН'ости .этих-тmmв. вы


без особого труда сможете nPИЙТ11 Ji Ii(ШИМанию и ОСТальных :кJ.1<lCCOB коллекций
(особенно если учесть ЧТО. в фаЙл:ах. СПРЗВJ{И npедлarаетсл исч-qщывaIOЩая доку­
ментация ДЛЯ каждого из типов),

Работа с типом ArrayList


тип ArrayList непременностане:т wrя вас наиболее 1{aC"t1!) иcnользуeмымипомM
пространства имен Systern.Collections, П0СIФлыtу он пооволя.ет динамич-ески
переопр~деля.'Гь размеры содержимого. Для иллюстрации базовых возможностей
ЭТОГО типа nредлarаем вам рассмотреть слeцyющnй l1'pоrpaммнblЙ :коД. D ltoтopoм
.A-rraybl. s't используется для манипуляций с набором объeкrов Car.
{;tati~ yo.id Main i $tripg 1] argsJ
{
l' Со!snаие ArraYL:i.st к Э.anonиевне ИСХОДJWIЩ SЦЧeииJlНИ.
Array1.1st darA1:bl$t = о'еУ
Air:rayL!.st ();
сагArL.iвt.АddR-а.пче(l1еw Ca.r[J i ВЕе"; Car("F'red", ~.O, 10),
new Cat("Mary", 100(50), ne·w Ciif("MB", '19Ф, 11) iJ;
СопsФlе.Wгi teLine (1' \ пЭ;л~м~нтов Б carArList: ! О)",
OatArLiet. Со\ш t ) ;

/I Печв'l'Ь 'l'eJ()rЩИ,I( sкачеlDiЙ' ..


fCJП!ё!сh (Саг с in ca rArLi·s t)
Ооnsоlе.Writ е Liil е("Имя .а.БТОМОбйля: !' Oj ", c.petName);

/I Всфрка ИОВОIfQ · з.neмен't'&..


С~п. sоlе.Wr.! teLirls (1' \n->ДО.6авление А'СНЮГО Са!.");
car,A rL.ist.lr!s'ert (2, l1ew Car("TheNewCat"" О. 12)),;
Cbnlsole. Wr.i teLlrre ("Э.nемем·то1;С в e:ar;U-Liэt .: (О) ''', carA.r:Li.st.Cotl1'tt):
11 nо.пуче~ ~СС;ИВ. об'SeJt!l.'оа ив ArrayI.i8t JI сио_. пе1lа'Шt.
obj€:ct [.] а!; rayDfCa:r 5 = ,c:arArList. 'I'oArray .() ~
fФг(1:Dt i = О ; i < arrayQf Cars,Length; i++J
i
СQЛЭQl:е .WriteLirl!~ (DИМЯ ав'Х'омоБИЛ-Р.: 10) ",
НСаr)аrrауQfСаг э l i l) .р ~ Ц<Iаmе.J .;

~ДE:CЬ для доб3ВJJ~НИЯ В КОЛJleRЦИЮ А.гrауЫзt цаБQра типов Сах ИСП()J]ъзуе'l'С;Я


метод AddR-аngе (:) (lфторыЙ. 110 сути. за..1\<lещrет п-цратиый 'ВЫЗОВ метода Add (»).
Посл~ .ВЪШQда информации О Ч)fсле 9лемедтов в коллекции [и после ЦЩUIa по B~M
эдеМеБ:ТЭl!'I ДДИ получения имен) вызывается. Meтo:D: Ir,sert () . ках В'ИДИ'I'e. Illsert ()
ПО3ВOJШeТ осуще~'ЛЩть BCT~КY НРВОГI!) элеменrа в эaщшнyIQ' поз.ицию 'ц rayList.
Обратите вниманне на ~OB .~eтoдa Т6А): пу (), КOIГOрый воз:вращает Qбщий мас­
СИQ типов 5yste.m.Objec t .н:а. основе содержим.ого оригивa.ru.ноro ArrayLi~t. На
рис . 7. 13 ПQказан ооответствующий вывод.
-
334 Часть 11. Язык программирования С#

Рис. 7.13. Забавы с System.Collections.ArrayList

Работа с типом Queue


ТИп Queue [очередь) - зто контейнер. гарантирующий размещение элементов
по правилу "первым прибыл - первым обслужен". К сожалению. люди сталкива­
ются с очередями повсеместно; очереди в банке . кинотеатре. по утрам к aвTOMary.
продающему кофе. и т.д. При моделировании сценариев. в которых элеме~ты обра­
батываются по правилу очереди. на помощь приходит System.Collections.Queue.
Вдобавок к функциональным возможностям. обеспечиваемым поддерживаемыми
интерфейсами. Queue определяет ряд членов. описанных в табл. 7.4.

Таблица 7.4. Члены типа Queue

Член Описание

Dequeue () Возврвщает объект. находящийся в начале Queu.e. с одновременным его удалением

Enqueue () Добавляет объект в конец Queue


Peek () Возвращает объект, находящийся в начале Queue, без его удаления

Чтобы проиллюстрироватъ возможности этих методов. снова используем нашу


автомобильную тему и построим объект Queue. моделирующий очередь автомоби­
лей перед въездом на мойку. Во-первых. предположим, что у нас есть следУЮЩИЙ
вспомогательный статический метод.

pubiic static void WashCar(Car с)


{
Console. WriteLine ("Моется (О)", с. petNarne) ;

Теперь рассмотрим следУЮЩИЙ программный код.

static void Main(string[] args)

1/ Создание очереди с orpeмя: эnемеитаии.


Queue carWashQ = new Ои еие();
carWashQ. Enqueue (new Car ("Первая", О, 1»;
Глава 7. ИliliерфеЙс.ы и КОlll1еJЩИИ 335
car.WasbQ, E>rrque·\:Ip. (пе"" Сах ("'Вторая н, О, .2) ' ;
ca.rWaSlJQ. ЕllчuеUе (new Сах ("Трет):, я" f I}, З)\ ~

11 ПSIpJI&JI КaJlИНа а ОЧfllредJI.


Cons>Ol е, Wri teL1:ne ("Первой в очереди явл я ется i О} "(
( (C;U) ca:i:"Was·h Q. Peek (J ) .реtNэ.mе);

11 Y~aneвНe асех злеиек~ ка о~ереди.


WashCa r«Car)carWashQ . De queue());
WashCa:r ( '(Сах) c arWasfrQ. Deque'.J.€ () ) ;
WashCar (.Сах) carwa·SllQ, D~gueue () } i

11 nOПU'1'aAQlС" y~~ снова?


try
IWa$hCar ( (Cair) carWashQ. DeqЩН:lе () ) ;
catch (F:~ception е')
( Console.Writ·eL,i ne j " Ошибка: {О}", е д·1еэsач,е ) ~)

3десь в тип оиеuе с помощьщ метода Епчuе.llе о вставлпютCJI ТрИ элемента.


Выаов Рее).: () поэволяет проверить (ВР не УДaЛIпъ) первый :шемен1" в текущем CO~
СТOJUIИИ Ql1eiJe.• И ТaltЩ'd элеменТQМ D данном случае .flВ:Ляетс,я мroпина с имеgем

Первая, H~oнeц. се помощью Dequeue '() элемент из очереди удаляется .и посЫJIa­


етСSi:!Ю ВСЦЩilоrзl''еДЬВ:УЮ фyнкцmo Wi'!Js1:!C;'ar О для обработки. Обратите вни:мшmе
на то. что при попытке У'далеНИJI элемента иа ПУC'l'ои очереди среда выnолиен.ил
rенерирует ИClЩЮче.ние,

Работа с типом Stack


1)m System.Collections .Stack upедставлнет .ROллеI<ЦИЮ. в которой элементы
размещаю"I'Ся. ПО правИЛУ "послеДl«1М npйБЫJI - первым обслужен". 1\:aR и cJleдyer
ожидать, Stack оцределлет ч.леЩ:;J с ИJЧ.e.Нами PL1s·h {} и Рор () (для добавления эле­
'ментов В стек .и удаден.шх их из С1'еIЩJ. В рле;дующем примере стена используется
стандартный nm sуstещ.strin!jJ.
sta'tic void Маin' (stIiлg[] ar:gs)
{

sta:ck strciлgStас k = TJ€W $t.ack О ;


s trihgStack. Pus'h ("Первый") ;
э t xif)gS ·t:.a~k. Рыэп' ( "Вт0Рo.ii1") ;
s tringstack. Рuзh (ПТретий") ;

11 Cko~1'Di: ка tt8ipвwК ЭJl~, 17ДiШRе,к 8it'O н C:X~_ снова.


Can·sole. Write-Li'ne (·'IJерfШЙ. элещ~нт:
I О)" ~ зtr ingStack. Peek. ()) ;
Censole. Wri teLine strinr;g'Stack. !='ор ( i ) .;
("Удадеи {О j ",
Соnэоlе. Writ~Li1).e ("Первый элемен'I': ! О} "т s"tringstack. Pe.e k О) ;
COils'oJ;e .1i9riteLi,ne ! "Удален {О}", э trin.gSta ck, Р с.р (1 ) .;
Cor1801e. Wri't eLl ne ("ДервЬ:\Й элеме,~'l': t О 1 ~t, ~~t r:i ngS..t ack. P~ek () ) j
С(Щ501е. W~i t-еLinе ("Удале'li {О} ", stxinqs't-ack, Рар ());

try
{
console. Wr i teLine ("Первый элемент' : {О}". s t ringStack.. I?eek (j );
ЗЗ6 Часть 11. Язык программирования С#

Сопsоlе.WritеLiпе("Удален {О)", stringStack.Pop(»);

c atch(Exception е)
{ Сопsоlе.WritеLiпе{"ОШИ бка: {О)", е.Мезsаgе);}

Здесь строится стек, содержащий три строковых типа (названных в соответ­


ствии с порядком их вставки). ~ЗаглядываяW в стек, вы видите элемент. находя­
щийся на веРПlШlе стека, поэтому первый вызов Peek () выявляет третью строку.
После серии вызовов Рор () и Peek () стек. в конечном счете, опустошается, и тогда
дополнительный выэов Ре е k ( ) / Рор () nриводит к генерированию системного ис­
ключения.

IIIсходнlolЙ КОД. Проект CollectionTypes размещен в подкаталоге, соответствующем главе 7.

Пространство имен
System.Collections.Specialized
Кроме типов, определенных в простран:стве имен System.Collections. библи­
отеки базовых классов .NEТ предлагают набор более специализированных тm;rOB ,
определенных в пространстве именSystem.Collections. Special ized. Например,
типы StringDictionary и ListDictionary обеспечивают ~стилизованную" реали­
зацию интерфейса IDictionary. Описания основных типов класса из этого про­
странства имен предлагаются в табл. 7.5.

Табnица1.5. Типы пространства имен System.Collections.Specialized

Тип Описание

Col l ectionsUtil Создает коллекции. игнорирующие регистр символов в строках

HybridDictionary Реализует IDi c tionary, используяListDictionary, пока


комекция мала, и переключаясь на Hashtable, Korдa комекция
становится большой

ListDictionary Реализует IDictionary, используя однонаправленный список .


Рекомендуется дпя коллекций, которые содержат не более десятка
элементов

NameValueCollecti on Представляет отсортированную коллекцию связанных ключей и


значений типа String, которые могут быть доступны или ПО ключу,
или по индексу

Str i ngCollection Представляет коллекцию строк

StringDictionary Ревлизует Hashtable с ключом, строго типизированным, как


строка, а не объект

StringEn1Jmerator Поддерживает простой цикл по элементам StringCollection


i Глава 7. интерфейсы и iWлnещии ЗЗ7

Резюме
И:Erreрфе'ЙС ,мo~o tпrределить. как имеf{ОВанную ./{QЛJIelЩИЮ аОСl7'ipаюnн.ых чле­
НОВ. ВJ!Иду' тоro. что jffiтерфilliс не преДJ:Iзтает деталей реализаЦиИ. ан обычно рас-­
сматptmaется. как, ~риант ПQБедeIOfЯ.. воаможноro для двв.нОТО типа. При реализа­
ции о;цноro и 'того же ин:reрфейса .в песJtолыtlц ЮIассах вы llOд:учаете ВО3мОЖ:ноеть
обращаться с Сouтве'ГОТВУК)lЦИМИ ТИШiМJХ о,дЩi'авово ,.~1ГO называется интерфейс­
ным ПО:ЛИ)fорф1;Iэ;мом).
Для определе6"~Я }]о.вых интерфей:~ов В, С# предла"Fается клюЧевое СЛОВО
lnterface. Любой тип может nодде}!)живатьстолько щперфffi:iсОВ. акО;Т1ы(о необ­
ходимо • .цyжFIO талыш ухазать их в сIIИCJte определешш типа. разделяя 3arutт.ыми.

При ЭТОМ можnо' соэдава'l'Ь интерфffiicы. l\OTQPble ои:азьmюотCl1 npоиэводнымn це­


с:хо:nъкиx базовых :интерфейсов.
Помимо возмож:ности создания ПОЛЬЭО8ателъс:ких ИJ:п'ерфейсов. :вы имеете ~­
М{)ЖНость ИСПОJЦI!30вать рнд ст-андартнш ИJ-Iтерфейсрв. определенныхв библи­
отеках ,NEТ (т.е . цредпarаемых каркасом npШIожеНИЙ). Вы можете строить поль­
зовательские 1'ИIJЫ. ре-ализующие эти .встроенные щцерфейсы:. чтобы обеспечить
въntОJПЮЮilе ряда стандартнь1х действий, таких RaКtt1l0нированйе, сорт.иРОВR.а uлп
перечислеflие. liщ<i:Нf€Ц. в ЗТ0И г.лаве БЫ узна,ilИ о JfiiШССах RолЛеlЩИЙ" опреде.11еинЪD(
в простра.нстве цМ~H Sуs t:еm.Соllесtiолs, и изучили ряд общих интерфейсов. ко­
торые ИСЩ~ЛЬЗУЮТСЯ связанными С ROЛЛекциями 'ТИпами.
ГЛАВА 8
Интерфейсы
обратного вызова,
делегаты и события

Д о зтоro момента в нamе-й книrе в каж.цОМ uримере ПРQЛожеIЩЯ щ:щгрцммный­


код Маiп () тем или иным способом направляд запросы соответ~'J;'ВУЮЩИ:М
объектам. Но мы пава что не ра('сма:гр:ивади 1Jозможн.остq обратноГQ обращения
объекта.к. вызывающей стороне. В большинстве программ ТИЩJЧВЫМ ддя обыmтов
~C'reМЫ 01tЭ.ЗЫВа:ет<:я ДВУХСТОРОНННЯ' Rо~ациа. которая реализуется- с помо­

щью ИН'tерфейсов обратного вызова, событий и других программных КQне'1'РУIЩИЙ.


Эта Г.Jlana на:чинается с раСсмотрения тoro, Ш1R с помощью типов -интерфейса p~
J1'ИЭовать функциональные ВО3МОЖНОС'I'I1 обраТ1fОГО ВloIЭОВЦ .
Затем вы узнаете о· типе делегата .NEТ. l-tоторый нвдяеТCJ:\ o~ъe}P"OM, Gбеспечи­
'Бающим типовую безопасность и ~)'1(азывзющимО на метод цли методыl' которые
могут быть вызваны позднее . Но. в отшrчие от традициощюrо у~а;щтелн на фy:im­
цию:в С++. делегаты .NEт IIР~ДСТавJIlПОТ собой объекты, Roтop.ge имеют встроен-
1iY1O подце'р>1ЩуМJ::fогоадресноrо -и аСИВХрОНI{ОГО Bbl30Bf} Mf"l'QДQB. мы; рассмотрим
ас.инхро-нное поведение Т'и.nОВ де.легата позже, npи изучении цространства имен

system.Thre-аdiпg (см. ,Главу 14).


ВыяснИв, ЕаЕ создавать типы дел-еrата и работать с ним:и. МPI с вами pa:tCMO"
т-рим ItЛЮчевое слов-О с# even.t . I{OTopoe nPИЗВa1l0 УЦРОСТИ'J;'Ь И уснорить работу с
тиnaми делегата. f--taRьпец. в этой главе будут paCC~OTpeы:ы новые ВО3МОЖ1ЮСТИ
яЗЫка Ci ~ связанные с испOJП;зoванием .дедегатов, и ео6ытд.. включаЯ анонимные
методы и групповые npеобразовщщSf методов, вы увидите. -что указaнньiй подход
O'Г.IrpblВae'!" более Мроткий ПУТ1. 1\ целеЩйМ объ~там соответствуюtци:x соб.ъtг:ИЙ.

Интерфейсы обратного вызова


При раесмотрении r.Jатериада пре:Дl!lДУЩей tлав.ы,Вы имели .ВО3МОЖ!ЮСТЬ убе­
дifТЪСЛ. что m-ггерфе~icы Grrpс;дещuот поведение. KOTop(Je -Может подперживатьея
сauыми разными ТIЩ.ЭМИ. Кроме щпользова:юш интерфейс(ш .ддя. подце:рящ'И по­
JI'.Иморфиэма.. щ{ можно ИСllQЩ>ЭQ1ЩТЪ И В качеСтве .iИeXанuзМl1 обрaтrtJWzо дЫЗQВЙ.
Соотвt!'I'СТВУЮЩИЙ подход дает об"1!ек:rам возможность осуществлять двухсторон­
ЮJЙ обмец, ИСllо.лыуn обще_е 1'oЦ'fo:aceCTBO 'ЧлeF.iав .
..,....

340 Часть 11, Язык программироваНИR С#

Чтобы показать варианты использования ff.нтерфеЙсов обратного вызова, мы


изменим уже знакомый нам тип Car так, чтобы он мог информировать вызываю­
щую сторону о прй:ближении поломки машины (т.е. о том. что текущая скорость
на 10 кмjч ниже максимальной скорости) и о свершившейся поломке (когда тену­
щая скорость равна или вьnпе максимальной скорости). Способность посылать и
принимать соответствующие события будет реализована с помощью интерфейса,
носящего имя IEngineEvents.
1/ Ии~ерфейс обра~ноrо 8~OBa.
public interface I!ngineEvents
{
void AboutToBlow(string msg);
void Exploded(string msg);

Интерфейсы событий обычно реализуются не объектом, непосредственно ~за­


интересованным· в получении событий, а некоторым вспомогательным объектом,
который называется обьекmом-nрueмн.uком. Отправитель событий (в данном слу­
чае это тип Car) при определенных условиях выполняет для приемника соответ­
ствующие вызовы. Предположим. что класс приемника называется CarEventSink.
и он просто выводцт поступающие сообщения на консоль . Кроме TOro, наш прием­
ник содержит строку. в которой указано его информативное имя.

11 Приемник собbl'RИЙ Car.


public class CarEventSink : IEngineEvents
(
private string пате;
public CarEventSink() { )
public CarEv entSink(string sinkName)
[ пате = sinkName; )
publ i c void AboutToBlow(string msg)
[ Console. WriteLine (11 [О) сообщает: (l)", пате, msg);
public v o id Exploded(string msg)
{Console.WriteLine("{O/ сообщает: {l)", пате, msg);

Теперь. когда у нас есть объект-приемник, реализующий интерфейс событий,


нашей следующей задачей юm.яется передача ссылки на этот приемник в тип Car.
тип Car будет хранить эту ссылку и при необходимости выполнять обратные вы­
зовы приемника. Чтобы тип Car мог получить ссылку на приемник, нужно доба­
вить в тип Car вспомогательный член. который мы назовем Advise (). Точно так
же. если вызывающая сторона пожелает отменить привязку к источнику событий,
она может вызвать дРутой вспомогательный метод типа Car - метод Unadvise ().
Наконец. чтобы позволить вызывающей стороне регистрировать множество npи­
емников событий (с целью групповой адресации) . тип Car поддерживает ArrayList
ДЛЯ представления исходящих соединений.

11 тип Car и ВW8W118nц а я C~OPOHa MOryт C.IIUDla~IoCII


1/ с помощью ин~ерфейса IEngineEvents,
public сlазs Car
{
Гла8~ В. ИнТ!ффейсы обраТflоrа I!ЫЗОВ(i. депеrаТt>1 и [lоБЬIТИЯ 341
/I Вабqp С!В• •"НМJlXnpи~.
АпауListсliеntSinkg = new Ап:а,уLiвt () ;
11 ПРНСО8ДИ$еае 1( ИС!t'Oчнику caCbNo.IOt иди отсоединение 0'1' него.
рцыlсc void АdvlБ<е (IEngineEvents sJ:ok.J
I cl·i en,tSinks .АОО (sj,пk}; )

риЬНс vaid Una:dvlse (lEngi.neEver,ta sink)


i cJ.ientS:inks.Rernbve (si.nk); 1
" .
Чтобы на самом A~,~l~ пocьiЛaть события. l\4I;t! о6l;ЮВИМ метод Car .A-ссеiЕ):rа.-tе ()
'l'aI<. чтобы ОН осущеСТWZmJ ~ц:poxoд" по соедщiениЯJi.1:,}'1ШЗatmъ1М в Arrayl/i,st, и
.при необходимости цыдавалnoдходящее сообщенnе (оРратите ВFtIfМa}IИt': на то, "ЧТО
теперь:в ллассе Car ес1Ъ ч.nев-пе.ремеНl1ая carIsDead лоrnче(f:~arо тиrraдля пред­
ставления СОСТОЩЩR двигателя мamшn.r).

If ПРО!1'ОJtоn CO~ на бasе ИИ!l'ерфе;;са.


сlаsз Car
!

11 Эша. M8~ pa!S~ae'1' юm: .ет'?


Ь'оо;). cai!",l5Dead;
pu.bl.ic void JI-,ссеlеJ;аtе (iht <:)ella)
t
/ / Еc;nи lIIIiшииa. 'CJlol(uacь I I о!1'nPавиwъ соБЫтие EXploded
/ / хаж.дОIllY щm8МИИJtу.
iflcarls1Jead)
'{
f 'o x0ach (IE'1 "lgil1eEventse in clientSiI;1ks)
е. Exploded ( "Иэ:еИИИ.::vе, машин.а GломвлаСh ...... ) ;
J
else

cunSpeed '1-= de.1ta;

1/ Oo.rnpUKa ~ АЬoutжоВlow.
i f (10 = maxSpeed ~ cUirrSpeeq.)
{
foreach ( IEпgitiеЕvеп:ts е in ,С} ±~ntSilJks).
е • Ab01.1tTQВlow ("OCT-,ОрG1IlliO , ! Могу сломатрся.!");

if (currS.peed >= тах5рееd)


саrI ·эDеаd = tлrе;
el$e
COfisole. Wri te!Line (" \ tC1.lrr$pe,e d ={ О J" currSpeedJ;
342 Часть Н. Язык программирования С#

Вот подходящий npограммный код клиента, в котором используется интерфейс


обратного вызова для отслеживания событий Car.

/ / созда_е машиJW и МОJm'J!opиIП' соБыu'ий.


public class CarApp
{
static void Main(string[] args)
f
Console. Wri teLine (" ** * Интерфейсы и КОНТРОЛЬ событий **"") ;
Car cl = new Car("SlugBug", 100, 10);

// Создание объехта-npиемииха.
CarEventSink sink = new CarEventSink();

/ / Передача Car сcы.nхи на npи~.


cl.Advise(sink);

/ / Усхоре_е (ВЫЗlDает настyпnе_е событий) .


for (int i = О; i < 10; i++)
cl.Accelerate(20);

/ / Разрыв с••зи с источвихом собlol'1'ИЙ.


cl.Unadvise(sink};
CQnsole.ReadLine();

На рис. 8. 1 показан конечный результат работы этого основанного на интер­


фейсе протокола событий.

Рис, 8.1. Основанный на интерфейсе протокол событий

Обратите внимание на ТО, что метод Unadvise () позволяет вызывающей сторо­


не селективно ОТКJПOчаться от источника событий. Здесь перед выходом из Main ()
вызывается Unadvise () , хотя, строго говоря, это и не обязательно. Но предполо­
жим. что приложение должно зарегистрировать два приемника. динамически от­

ключить их по очереди в процессе вьmоmiения. а затем продолжить работу.

static void Main(string[] args}


{
Console.WriteLine ("***** Интерфейсы и КОНТРОЛЬ событий Н***");
Car сl = new Car("SlugBug", 100, 10);
Гnзеа 8. интерфейсы Обратног@ БЫЗОIН1, де1lегаты .И события Э4З

11 Ссsдавие двух o&.:l.eX'1'()1!I.


Con sol e ..WriteLine (,1"' ....0.1''* СОЗДалие npИeмI01l1'ОЕ -'''***''') i
'.Ca.rEVen t &1 пk sif.1k= new. CarE:ventSim.k ("Дервш цриемни'К");
СаrЕw.епtSiпk myOthel-Sink == Dew CarE~ntS:irlk ("Вт орой лр.иеМFtИR"):

11 Передача npиемви1l:01!l об_Jtжу сах .


{:Ql'.sole.Wr.it·e1ifje("\n**"'~'" Отара ВlI'а приемнихов Б Car *+*1<*");
ol.Advise(siRk);
cl.Advlse (myQtherSi nk) ;
11 УС1l:0ревие (при Э'1'ОН reи.ерирYElТС~ co~) .
Cens:oJ.e. Wri t eLine ("\11 н"'.," Ус.корени·е "''*'' *'''''') ;
for(lnt i = О; i 1( 10; Н+)
Cjl .Accele.rate{2J) ) ;

11 О'JI~ение перв:оrо npИ:eквmc.!L coCb.rNdr..


СОПЗQlе. W'r i t~Line 'е' \n '* ..... ." ОТ1<::Т!ЮЧШiие пеРВОГ($ приеМНИКd ....... '" ,." )' J
r;;:1 ,UПq\d·visе (sink);

/1 Новое уC1tореиие ('1'enEip!. _"p!IЗ~'1'ОН !1'OJ'1ЪXO myOtherS..ink) .


COn501·e.WrlteL· ine("\11'*"''''·~ (;i;:о.ва ус:кЬрен.и;;;: "'''''''''''');
[о! (int i "" О; i < 1.0; i++) ol.Acc.elerate (20) ;

11 (RJШDЧ:евие B'1'0P0t'O прnехаика соб~.


consoie. wri teLine (11 \.n * * **'* ОТЮ1Юl;!еКИ6 .втор ого цри,е~ый..."lil * .. r-i< '* .. ) ;
I сl . Unad'itise (myOtherSink) ;
I СопеQJ.е.Rе-аdLinе(~;

I ИН'rерфейсы событий могут быть I10ле-.зны и тем. что m-т 'МЦСУТ ИCIJоль:юваться
с люб:ыми ЯЗJ:dКами и любыми 1JШ1'];фQрj..raми (.NE.T. J2EE или КШШМИ-ТО IIными'.'
1I0Д1I.ерживаюЩJiIМИ npоrраммирсшаНие на оонове интерфейСОВ. Однако ~()фйЦИа:ль­
ныК' nP0ТOKQJI событий задает rтa:тформа .NEТ Чтобы понять вву-трeнsюю архи­
тектуру обработки собы;тИЙ. МЫ начНем с обеуждвния роли Tt!lJa делегата.

МtпДМtoIЙ XO~. ПРQект Eventlntertaci:! размещен в подкаталоге. соответствующем главе 8.

Ти'па делегата . NEТ


Перед тем как дать формальное опреД~eJШе де,'Iега-rа .NEТ, Д$айте обсудщ.r со­
ртвеТC1'EJЮЩИе перспeюrмвы . .Б WindGws АР! для создания объектов, н:азьm~м;ых
фуН1CЦl:JЯ.МU обpamнoго Вbl3Oвa. npeдnQЩU'ается использовать YКa3aтeдIi фущщий
(подобные yJCaЗaтeJIНМ С). Ис.цо.ль,зуя. обратный ВЫЗОВ, npoграммисты могут совда-
1Щть ф}'НIЩliи. возвращающие информацИю другим фующилм в прwrожeIOlИ ~ oг:l'>­
вет на их выЗов.

ПроБЛема стандартных фyню:cиji обратйого :въiЗова. noдобищх С. заключается в


том. что такие Функцmt, по cyт~> мздо оршчаюТСВ от простой ССblJ"ЩИ на адрес в
Щiмя1:-и.. В идеале фующии обратяЩ'о I;IЪЦIQВа МОГ.IIИ бы со,цержать допOЛЮIтель,ную
обеGпечивающую безопаСНQСТЬ »НформацkЮ о числе (й тип:ах) параметров и ~08-
вращаемом значениидлл еоот~етСТ{lУЮЩего метода. К сожалению" ДЛЯ традици­
щrных ф}'1ШЦИЙ обратного 8Ы30ва Э!ГD 'Не ТЭ:К. Н, RaК вы можете доr;ада-rъcя. ~eНRo
344 Часть 11. Язык программирования С#

это часто оказывается причиной дефектов. которые проявляются в среде вьrnолне­


ния и которые трудно устранить.

Тем не менее, возможность обратного вызова является полезной. В .NET


Framework обратный вызов поддерживается. и соответствующие функциональ­
ные возможности реализуются с помощью более безопасных методов делегата. что
обеспечивает более совершенный объектно-ориентированный подход. В сущно­
сти, делегат - это обеспечивающий типовую безопасность объект,указывающий
на дрyrой метод или, возможно, несколько методов в приложении, которые могут
быть выэваны с помощью делегата позже. Точнее. тид делегата хранит три следу­
ющих элемента информации:

• имя метода, к которому должен обращаться вызов;

• аргумеюnыметода (если таковые имеются);

• возвращаемое значение метода (если таковое предполатается) .

Замечание. В отличие от указателей функций С(++), делегаты .NEТ могут указывать на статические
методы и на методы экземмяра.

После создания делегата и получения вышеуказанной информации делегат мо­


жет динамически в среде выполнения вызывать методы, на которые он указыва­

ет. Вы убедитесь, что в .NEТ Framework каждый делегат .NEТ (В том числе и ваши
пользовательские делегаты) автоматически наделяется способностью вызывать
свои методы синхронно или асинхронно. Это очень упрощает задачи программи­
рования. ПОСRОЛЫСУ позволяет вызвать метод во вторичном потоке выполнения без
явного создания объекта Thread и управления им вручную. мы рассмотрим асин­
хронное поведение типов делегата в ходе нашего исследования пространства имен

System.Threading в главе 14.

Определение делегата в С#
Чтобы создать делегат в С#, вы должны использовать ключевое слово delegate.
Имя делегата может быть любым. Однако делегат должен соответствовать методу.
на который этот делегат будет указывать. Предположим. например, что нам нужно
создать делегат с именем BinaryOp, который сможет указывать на любой метод,
возвращающий целое число и имеющий целочисленные входные параметры.

1/ Э'J,'о'1' делега'1' lIо*е'1' ухазwaатъ .а oIШбой lIетод,


11 ПРИJDDI&IIIЦИЙ ~a ЦeJПo1X ЗначеНИII
11 м .оа.ращаnЦИЙ цеnое значение.
public delegate int BinaryOp(int х, int у);

При обработке типов делегата компилятор С# автоматически генерирует изоли­


рованный класс, являющийся производным от System.MultlcastDelegate. Этот
класс (вместе с базовым классом System.Delegate) обеспечивает делегату необ­
ходимую инфраструктуру, позволяющую поддерживать списо}( методов. которые
должны быть вызваны позднее. Например. если рассмотреть содержимое делегата
BinaryOp с помощью ildasm . exe, вы увидите элементы, показанные на рис . 8.2.
Глава '8. И~терфе~с\,l оБР;'IТIЮГG Вblзова, делегаты и события 345

~ "'A,NIFEST ~
,. • 5irф1eD.~
~ .~.t",prap_
,. а
, ~ ..:ja$"pUb"c ~Q ~n'i_le:d
~ ~1'fds Г_nrlib~ЯеI1\.м...Jt",.,.ф~
• ,ctol: \':IIld(оЬ1е<t,пж,'«I 1rIt) ,1
• вegл1n1'bk81 Wss,Гmsoortlb1SYstsrn,IAs)'nCRмUlt(II'ItЗ2'{i '
• E~nl'Oko :'i1t32(L'-s, [mscttli:>JS.ystem .!A<Yf'd!IISIA:) ;i:1
• !n~~ : itЭ2!.iJ1tЭ2.iht3ZJ '
,j, 8 S~, Program,

):11110.8.2. Ключев(щ СЛОf\О deleg.a t,e 11 с# предсr.Щ1JЯет ИЗQЛИРО­


ванный Тltщ I'ЩРИ3вО:ДНЫЙ от Буstеm.МI.JНi.:аstDе.lеgаtе

Кш, .вщите, rенерируемьщ аде(:Ь :юraщ; BinaryOp апреДcmяет 'l'риоткрытых ме·


TQДa. Метод IJ1'i,'oke() l\J(),2IЩO НqЗВатгь :rnаВным. ПОСlюm.'ку QН иcnолъзуется: ДЛИ сии­
~н.нoгo вызова методов. поддерживаемых типом 'делегата. и синхронность здесь
означает то, что выз;ьща1ЩЦ!lЯ сторона для продолжеJ:ШН раБОТЬ1 должна ожидаТh
завq>шени:я ~ызо:ва. Б~ъма странным важется тот фа1с"г. что синхроltНЫЙ ,метод.
! nvoke () в С# неЛQдЯ вызвать непосредственно, Ч)"'lЪ позже будет прщ:~емонстри­
ровано, }щи Iпvоkе () вЪ'ra,~вается опооредьвaьtно с помощью соответстВующей
синтаксическом НОНСТРУДЩИИ.
Методы ВеgiдlПVоJ5е О .и Enplrtvoke () обecnечивают возможность асинхронно­
га ВЫ39ва '],~кущ!::то метода и(} ВТDрИЧНОМ Потоке :вы'полВ8НИЯ. Если У вас есть опъt.r
работы с .м,нОГQПОТО"ЧНblМИ ПРИПОжениями. вы должны знать. что ОДНОЙ ИЗ maв­
ных np~ до ж.rroРЪЦ\l.P8.i'работч::и:ии создают втарич.в.ые потоки. явiIЯe'i'CJI вызо.в
:метоДОв. для вЬШОJIНеЩiЯ -которых требуете~ много времен-и. И хотя биолиотeIOl
'базав~ классов . NЕТnpедлarают целое npостранс'rйо имен (Syatcem.Threa,ding).
СП~,ЦИЩIъно предназвачснное для решения задач МИОГОПОТОЧllОГО проtpа.~иро­
щыmя. с по~ощью делегатов СООТВ6Тетвующи:е ф).'НКциональн:ые возможности щ­
П~ОIЩ:n> проще.

Но on<yдa КОМIIИЛнтор KaнaeтW, на.кonpедеШггь методы I nvoke (.) . 'EeglnInvoke ()


и gndln",oke (') 7' Чтобы поНяТЬ суть Процесса. рассмотрИМ пример автоматич-ес1СИ
генерируемого типа класса Bi.Da,~·y'Op (полужирным шрифтом здесь обозначены
элеIYJ:еI:IТЫ. заданные опред~емЫмТШ10М делегата).

s~aied class BinuyOp : Sy s t em-.Мultiсаst.cDеlеgаtе


[
public BinaryOp (obj ect target, uin·t funсti оnАd drеs~э );
.p:ublic v~id lr.Jvoke Cint Ji, int у) ;
pu'b ljc I ,h s.yn,c ResuJ,.t BegirlI!lvoke (int х, i.Dt У:,
As yncCallbaek сЬ, object sta't.e) i
pubiic' i:~t Е;о.dIпv о1<;,е (IA$.yncResult result);
J
346 Часть 11. Язык программирования С#

Во-первых, обратите внимание на то, что параметры и возвращаемое зна­


чение определяемого здесь метода Invoke () соответствуют определению деле­
гата BinaryOp. Первые параметры членов Beginlnvoke () (в данном случае это
два целых числа) тоже соответствуют определению делегата BinaryOp, однако
Beginlnvoke () всегда имеет еще два параметра (типа AsyncCallback и object),
которые используются для асинхронного вызова методов. Наконец, возвращаемое
значение метода Endlnvoke () тоже соответствует исходной декларации делегата,
а единственным параметром метода является объект, реализующий интерфейс
IAsyncResult.
Рассмотрим еще один пример. Предположим, что мы определили тип делега­
та, который позволяет указать на любой метод, возвращающий: строку и имеющий
три входных параметра System.Boolean.
public delegate string MyDelegate(bool а, bool Ь, bool с);

На этот раз автоматически генерируемый класс выглядит так.

sealed class МyDelegate : System.MulticastDelegate


{
public МyDeleqate (object target, 1Jint functionAddress);
public strinq Invoke(bool а, bool Ь, Ьооl С);
public IAsyncResult Beginlnvoke(bool а, bool Ь, Ьооl С,
AsyncCallback сЬ, object state);
public strinq Endlnvoke(IAвyncResult result);

Делегаты могут также ·указьшать" на методы, содержащие любое число параме­


тров out или ref. В качестве примера рассмотрим следующий тип делегата.

public delegate string MyOtherDelegate(out bool а, ref bool Ь, int с);

Сигнатуры методов Invoke () и Beginlnvoke () выглядят так, как и ожидается,


но обратите внимание на метод Endlnvoke () , RОТОрЫЙ теперь включает и все ар­
гументы out/ref. определенные типом делегата.

sealed class МYOtherDelegate ; System.MulticastDelegate


{
public МYOtherDelegate (object target, uint functionAddress) ;
public string Invoke(out bool а, ref bool Ь, int с);
public IAsyncResult ВеgiпIпvоkе (out bool а, ref bool Ь, int с,
AsyncCallback сЬ, object state);
public string Endlnvoke(out bool а, ref bool Ь,
IAsyncResult result);

Итан:, в результате определения делегата в С# компилятор генерирует изолиро­


ванный класс с тремя методами, для которых возвращаемые значения и типы па­
раметров соответствуют декларации делегата. Следующий псевдокод приближенно
описывает соответствующий базовый шаблон.

11 Э~о ~ольхо псевдоход!


public sealed class nМЯДелега~а : System.MulticastDelegate
f
public ИНяДелега!Ра (object target, uint functionAddress);
p-tJЬ:1iJ:: »osIIIp,,~a~.J"an :rnVI!>~~.c.вa~JfEIP"fI'el:
pubH,c :r:l'_sft~:cRe8ult "8e9,in!nv;.liJ>k~ [.!Юапа.PIМ~II1!:rIad(
'Asy~,~-allb'iilc~ .;р, abjest 51tat'e:)',;
vщЫ: k&OS8P:;"Ntоа'Sи.ve\8J(~~а
'E ffd!2!ivi:ok,e 'r_~~,fMi)JItДoQl1rera!1'''', Пill~·,hеRб$ul t Te~'"!) 111 ;

Базовые к.naCCb.1 S'ystem,.,Mul't'icastDelegate


,м SysteJn,. Deleg,ate
Таким Qбр:;шом.,nрй C03Дil..H,aц ~ '" n:омmцhЮ 1ШЮ"lевоГ/J слрв.а ~~le,gi'J,te
В' С# БЫ :ыешш.о об'Ъ'яnляе1't' 'nщ щаctЩ, ~rrЩQЩeroёl! trpоНэвод;ш.1М mr ~ s t. EЩ'I ,
МultJеаэw&iеgаtе. Э:I'O'r JA,"'ЩtС oQlЩие:<Швa.tт crвa:m.« UWl't)МJ:<aк ДРС'Ij"П.Е сrrю..~· с
~д:рfWа..U' 7 " мeт0Д(m. iro'rop~ DQ~~ТСЯo:mngм oЦMeгa;r.a, а 1"~ ~
~~HecRO.1I:bКO ДОП~ .м.eтeAO~ (И' JЩUi пе~ШlЫX оо~аuЩ{). Що!lе~
~ ваавмо,цеЙС:'.t'вв:е iW' C~ Ii!~в.im., Bo'r :шро~~ NOДO 1reКЩ~
члetI~ Буsot...em.Мo'tЙ1:: i(;:'~iS'Шеrеg'.аЦ~..

rseiгlaliz,a:ы1 е J
P!дЫ~ ~ §lI:H~t,~acb t:.la!3s huHi-О.s$'t[).е-]еqRt:е ~ t'le.]egll'J:e
!
li~_
РlЗbНс: >8.2aledomin-i(le Dеlе,qщtэl1 r;eцnш>саt.w.rй,i:;;1r(}i

I /' ~iUIiio1e O~~


publlc s'\rati.c b:o,p,l ,Qpe r; a'~Dr
,== ,tМШ~:L~а:s~DеJ.,еqаt§!, di, мSt1 r.' i(:'аэtD~lеча' t.се dZJ;
рutЙI~ sketl,~ ь.с1ф.l. .ope1:~-tnr
"= 1!Щ.il t:. i.c.a.s.t.DeJ&ga;te G.I'l, Mul 'ti.C\~$tJ)'e.~~~1:e ~~J;

/IП~
pii~~te rntrtr _~nvoo~~i~n~uunt;
~t"i y«teob-1 ~(.,t _ u\;ющ.1r.!L('j:аL:ist i
}I

,Дnш.i.lJнin-~лъвъn: ~Щ:lаЛ'ЫIЫ~ ~~~ёти S)'atertl ,.мЪ l-tiсаS<t Реlе 9асЬе


П~I\'iт· 0'1' reБ(f)ei"О Р(i)~J(Щ"U "'""Ш(::&а, S:У;)jt;~ПJ .J.>е.l~зtе. wт ~.orn;pe~C!
9.1'1'1:nО R.mtcёa"

[S-eriEJ'Н ~'at':J,?" , C)..a.s-slD1t€lуfа·GE; Н:l..а.5 Sln-tеri3~1: 'I' ур~, A.qtO!>Ltal,),]


,rtb5i::лu;;tёlаs& De:;Le-gat~ 7' r{:1..~fieaЬ,le, 1Se:ri.aJ:i:zaIiJle
p;)bJ::):':;:
1,

pU!.blil>?: з.t.аt.i с Dеl,egаtэ с CirnЬj i1e! '(,p~r-arrtlil IIe;}~9'at~I1 ~l.ega\:efi.»;


publit:: $tatic p-elegat,e ~Qm1':rlD,e (;De'1e9iit.-e. a~ D€lega):~ д) ';
I?Щ'Ы ic ]'irt';'11:aJ: De1.ega't e [) 'Get Il'IvQC-а"t iQ;i;)J,d 51;; 11 J
p·abli.e g'l!.аtlю !lele:g:iiltE! RеЩOVI!:'('l1e.1,е.gAtв ·s'CUJ;GS, DеJ.ёgаtе ,'аt!Зе) i
риЬ.Н!::: зt:аtiс шle:qр'~<3 R'E'I'II\;')'v-eAll fooig.galte $olirc~. Рэl:еgаtе voalu.~1 (

/ I .l!LepeюpюlJ• ."""WВ адерlЩ7fИ


Pya1)lJ:J::o s7:a,tic ЬtЧD-l Op1t':t:at.c.r = (P'i!le-~a.t.e~( D'j,lеgйt,е ,tJ,2) t
348 Часть 11. Язык программирования С#

public static bool operator !=( Delegate dl, Delegate d2);

11 Свойства
public Methodlnfo Method { get;
public object Target I get; )

Здесь следует подчеркнуть, что от вас никогда не потребуется непосредственно


получать производные этих базовых классов. и вы можете ограничИТЬ себя исполь­
зованием только членов, указанных в табл. 8. 1.

Таблица 8.1. Избранные члены System.MultcastDelegate И Sys'tem.Delegate

Наследуемый член Описание

Method Свойство, возвращающее тип System.Reflection.Methodlnfo,


который представляет ИНформацию о статическом методе, поддержи­
ваемом делегатом

Target Если доступный для вызова метод определен на уровне объекта (а не


как статический метод), то Target возвратит имя метода, поддержи­
ваемого делегатом. Если возвращенным значением Target оказыва­
ется null, то доступный для вызова метод является статическим
Combine () Статический метод, добавляющий метод в список методов, поддержи­
ваемых делвгатом. В С# этот метод вызывается с помощью перегру­
женной операции +=
GetlnvocationList () Метод, возвращающий массив типов System.Delegate, каждый из
которых представляет конкретный доступный для вызова метод

Remove () Статические ,."етоды, удаляющие метод (или все методы) из списка


RemoveAll () вызовов. В С# метод Remove () можно вызвать опосредованно, ис­
пользуя перегруженную операцию -=

Простейший пример делегата


в начале освоения приемов работы с делегатами у программиста может возни­
кать много вопросов. Поэтому мы начнем с рассмотрения очень простого примера,
в котором используется созданный нами тип делегата BinaryOp. Вот программный
код, который мы собираемся проанализировать.

namespace SimpleDelegate
(
11 Этот деnеrат может указывать на любой метод,
11 QPининааций два це.пых значении
11 и возвраща ....~ цепое значение.
public delegate int ВiпаrуОр(iпt х, int у);

11 Эwот JCJJacc содержит Me'J'OДlol, на :которые


11 будет у:каЗlolВать BinaryOp.
public class SimpleMath
(
public static int Add(int х, int у)
( return х + у; )
Гл~ва 8.. l1f1fерфейь'Ы обратного вызова, делв:гаты и события' 349
pt1blic: static int Subtract (:i,nt х, int у)
! returo х - У; J

r:;lass .PrQgram'
j
static void Mai.n (string 1) args)
I

1/ C~;I:I~ o&wl~'J!a. B,i naryOp,


11 "yxas~UIIII,e%"o n ва Si.iapl8Мa th . мс! Е) •
БinаryGр Ь = new BinaryOp (Siщрlе...r.:fath.~ddJ ;
1/ ВIoJзо. Jdеп:ода Add О с поuoщыD д~.r.'Н.
СQJl8о1е.Writ..еLiпе('fl-О f 10 равно (О}", Ь(10, 10});
C01'!sole. Rea'd Line'(:) ;

Сн:ова ~брати.те 8н:имaIще: на формат д.елегата' 1;11 па ryOp, которьЩ может yкam.т-­
ватъ на .ttюбоЙ ме1'ОД. лринимаюIЩiЙ- ЩJа целых эна-чен;ия; и во3,Вращающий целое
ЭUAчение. ИТaJC. МAI СО3,дЩIИ КJЩСС с вм;ен!Ш SimpleMath, ОIlpfЩедяющий два СТЗ1'И­
чееRИX t.reтoдa. которые (как неожиданно) соотве:тЩ1lуют шаблону, оnpеделеннi:Jму
делегатом БinаrуОр.
Чтобы добавит~ целевсЩ мещд в де.дегат, нуnщо д:рост{) , цереддтъ имя зтo.ro ме­
тода KOHCl'PYКTOPY делеГЗ'l:а. Тогда вы оможете выэ;вan. YJЩЭанНый -ЧЛен с помощью
СИНТ8КСИ',lесJCQЙ IЮ!fС1'рУЦЦии,. подобt!\QЙ прнмому вщову фующии.

11 ,З,цеа. ШI. самом ;цепе .Jo18IirВill!I'1!Cf1 Invoke () !


Console.WriteLine("1(j + 1015 (GI". b(l,O, 10));
ИВа ку:п:nсами~ среда 8bЦJОJIFIения Бызывает creнерировaв:Jiый JroмпилятU})о.м:,ме­
тод Invoke О, ВЫ можете проверить это сами., СCЩf 01JtpoeTe КDМПQНDвОчныИ блОIl 'С
помощью ildasm.exe и посмотрите на пpoгp~ К()Д CIL мето-да Main () .
. metbod p.r l! vate Ilideby's ig stcatj,,= void Maio (st t. in,g[] аrgз) ci1 щanаgеd
{

.local э iri1t (1 О] class SimpleDel-еgаtе. BinaryOp Ь)


ldftп int32 .5impleI,>elegat;e.SirnpleMath: :Add(int:3!1, int;32)

newobj inst-ance vцid SirnpJ.:eDeleqat..e.BinaryOp:: .ctcr (оЬjесt,Пi'!tivе .int)


stloc,O
ldst~ "10 + JO 15 {O'j'''
ldloc.O
тае.14 . э 1 О
ldc.i4.s 1(J
c', Hl virt instance intJ2 S':i.шpl.оеlegаte .a~: ! Invoж. (lnt31 , .L11.t3:2)
г

350 Часть 11. ЯЗblК программирования С#

Напомним, что делегаты .NEТ (в О'l:Jшчие от указателей функций в С) обеспечи­


вают типовую безопасность. Позтому, если вы поПЬ!таетесь передать делегату ме­
тод, ~нe соответствующий шаблону", вы получите сообщение об ошибке компиля­
ЦИИ. Например. предположим, что класс SimpleMath определяет еще один метод.
носящий имя Squa reNuтber () .
public class SimpleMath
(
public static iлt SquareNumber(int а)
{ return а * а; }

С учетом того, что делегат BinaryOp может указывать только на методы. при­
нимающие два целых значения и возвращающие целое значение, следующий про­

I
граммный метод оказывается некорректным и ком:п:илироваться не будет.

/ / Ошибха! Метод не COOТlleTC!1'ByeT шаблону делегата!


BinaryOp Ь = new BinaryOp(SimpleMath.SquareNumber);

Исследование объекта делегата


Добавим в имеющийся пример вспомогатеm>Ную функцию с именем Display-
Delegatelnfo (). Она будет выводить имена методов, поддерживаемых посrynаю­
щим типом, производным от System.Delegate, а также имя класса. определяюще­
го метод. Для этого мы вьmолним цикл по элементам массива System.Delegate.
возвращенного из GetlnvocationLis t (). вызывая свойства Target и Method для
каждого объекта.

static void DisрlауDеlеgаtеlлfо(Dеlеgаtе delObj)


{
/ / Вывод имен xuдoгo из элементо.
// списха вызовов делегата.
foreach (Delegate d in dеlОЬj.GеtlпvосаtiолList(»
{
Console. Wr i teLine ( "Имя метода: ! О) ", d . Мethod) ;
Console. Wr iteLine ( "Имя типа: [О]", d. Target) ;

Если иэменить метод Main () так, чтобы он вызывал этот новый вспомогатель­
ный метод, то вы увидите вывод, покаэанный на рис. 8.3.

Рис. 8.3. Проверка списка вызовов делегата


Глава 8, Интерфеi\.cЫ обратного выэовв~.делегаты 11 соБЫilUl 351
Обратите внимание ва то, что здесъИ1\lЯ типа (siщрlеМаth) свойством rarg~t
~e отобража.ется:. Причина в ТОМ, 'ЧТО йаш делегат в.i!1аrуОр ~eт JJa cтamи­
чeCJtue меТОДЫ" следователъпо. нет объекта. на который нужно ссылатьrnl Но если
изменить методы Add О и Subtract. О так. чтобы aнn перестaзm быn. ста:rи'leetЩ­
ыи. можно СDЗдать экземпляр тйпа SimpleMatb и уназать методы для ЩNзова так
ШIК ПОка3а110 ниже.

sta:tic void Ma:in (s'trlng (]arqs)


I

11 Деле-ro&!D1 .~ NО'%'уФ па"lita&'1'Ъ '.на M~~~ ЭХЗ8JШJUlP8..


S1mpleMath m = new SimpleMaul () ;
BinaryOp' b = hew BinaryOp(m.Add)/
11 'ВiI20,JI ивtориацни 06 обlмХч.'8_
b,tsplayDe~egateInfD(b) ;
Соruз01е.W'ritе1in,е~"\J.110 + 10' равно {Qj", b(l,O. 10);
CpnsQle.ReadLine() ;

Теперь МI:i( ДOJIЖ;Щir увидеть ВЫВОД,noRaзаНFJI>ffi на рис. 8.4.

РМС' , 8.4. Проеерка OAJilCl(a еызовов делегата (новая nOЛЫТК~)

ИСХОДМa.lЙ код. npoeкr SiщрlеDеtеgаtе размещен в подкаталоге, соотеетС'!ВyIOщем таве в'.

Модификация типа Car с учетом делеrатов


ОчеШIДJiD. -что npeдьVJYЩИЙ npимер S.imрlеDеlеgэ'tе был исключwгельно щщю­
стративным. поскольRY нет НИRa1\ИX реальных приtЩН стрОИТц делerаты д.ля цро­

стого сложения двух чисел. Но этот пр:имер раскрывает npинцип:ьr работы с J:ИП,a:­
ми делегата. Д1I.Я построения' более реадьного примера: мы м:рлифиццрущ 1;'ЩJ Ccrr
T8I(, чтоб};! он I10СЬLjJ8Л сообщения Exploded и .AbolJtToB1QW через д:елегаТJiI .NEТ. ~
не через ПО11Ь.зоватеJIЬCКИЙ 1UiТерфейс обратного вызова. Кроме ОТIШ38 ОТ реац:иза­
ЦИИ IEngineEv:ents, :мы должны выnоmmть CJ1еДУlOщu:е шarи:

• определиТь Де.пёгаты AЬoot'l'oBlow и Exploded;


• объкци-n., чдеНЫ-flеj>eменные всех типов делегата в классе Car:
.,..
352 Часть 11 . Язык программирования С#

• создать вспомоrательные функции Car, которые позволят вызывающей сто­


роне указать методы, поддерживаемые членами-переменными делегатов;

• обновить метод Acceler ate (), чтобы иметь возможность в подходящей ситу­
ации обратиться к списку вызовов делегата .

Рассмотрите следующий обновленный класс Сат, в н:отором решены первые три


из указанных задач.

public class Car


{
/ / Опредenение '1'ИПОВ дenе:rаlJ1а.
public delegate void AboutToBlow (str.i ng msg) ;
public delegate void Exploded (string msg) ;
/ / Опредenение чnеков-переменИJJX ДJUI ~Ut,ЦOГO ИЗ 'J1ИПО•.
private AboutToBlow almostDeadList; t
private Exploded ехрl о dеdLiзt;
/ I Добавпекие эnекеНIJ10В • списох вnsоао.
/ / с покощыD .споко:r&IJ1enьИJJX кеlJ10ДО•.
public void OnAboutToBlow(Ab o utToBlow cl i entMethod)
( almostDeadList = clientMethod; }
public v o id OnExploded(Exploded clientMethod)
( explodedList = clientMethod; )

Обратите щrnмание на то. что в этом при мере мы определяем типы делегата
непосредственно в рамках типа Сат. Если исследовать библиотеки базовых клас­
сов. то станет ясно, что определение делегата в рамках типа. с которым он обычно
работает. является вполне типичным. В связи с этим, поскольку компилятор пре­
образует делегат в полное определение класса. мы здесь фактически создаем вло­
женные классы.

Далее обратите внимание на то. что эдесь объявлены члены-переменные (по


одному для l{аждого типа делегата) и вспомогательные функции (OnAboutToBlow ()
и OnExploded ()). Iюторые позволят клиенту добавлять методы в списки вызовов
делегатов. В upинципе эти методы подобны методам Advise () и Unadvise (). ко­
торые были нами созданы в примере с Eventlnterface. Но в данном случае входя­
щим параметром оказывается размещаемый клиентом объект делегата. а не класс.
реализующий конкретный интерфейс.
Эдесь мы должны обновить методAccelerate (). чтобы вызъrвались делегаты. а
не просматривались объекты ArrayList приемников клиента (как это была в при­
мере с EventInterface). Подходящая модификация может выглядеть так.

public void Accelerate{int delta)


{
/ / Если кашина 1 слокanась 1, :rенеРИРУ81J1CR собlol'J1Ие Exploded.
if (carIsDead)
{
if (explo dedList 1= null)
explode dList ("Извините, машина сломалась ... '1) ;
Глава 8. ИнтерФе~см оБDflтнога Bbl'.30an, деl1gгаты и события 353
else

eu.r:rSpeed += del ta;

1/ ВО'1'-:ВОТ сnомаeo:rся:?
if (1О= .rnaxSpeed - currSpeed <1<& д.lmоstDеа.dLis"t ,!= п:иl1)
{
аlrtюst.DеаdLis-t ("ОС'I'Qpожно! Могу СЛОМ2ТЪС:Я I "J ;

11 ПО~i!I все ОК!


i f {currSpeed >= max8peedJ
c.arIsDead = tл~в;
еЬе
'Сопsоlе.WritеLiпе(fl->СurrSрееCl '= [О}", ~tIrrSpeed};

Обратите ВНИМЮ'tИе на 'Ю. 'Что перед вызовом методов. сВJJз:aннblX с >;шенами-пе­


ременными alrnostDeadLis:t и explodedList. их эnачеmm проверя:ютсн на допу­
crгимоC'lЪ. Лричи-на в том. что рМ1\iещеш.1е соо'Тве.тствующих объектов с помощыо
!:Jыэва вспомо:гат.е.'-IЬЮ:i1:8 методов ОпАЬоutТоБ.1.:эw () ИОnЕхрlо.dеd О будет задачей
НЫ3hIВаю:rцeй стороны. Если: ВblЗЬШающая сторона не BЫ30DeТ ЭТИ методы. а мъ! по~
пъпаемся I!ОЛУЧИТЬ список ВБIЗОВQВ делегата. 1'0 будет сгенерировюю йСlWЮчеI:iИе
NuJ'lReferenc:eExceptiop. :и в среде выполнения ВОЭНИННУТ пробдемм (что. J(онечltо
.Же. нежелательно).
Теперь. котда инфрас.тpy1tt'ур3 делегата имее.Т нужный нам ВИД. рассмотрим мо­
дификацию Юlаоса Progra.m.

:;tatic void Иаiп (s:t.rin!g [] a:tgsj


{
соnsоlе.writеLiпе!"* ... .,.."'* Делегаты и: J'tО'ИТРОJIЬ 'соБЫТИЙ .,..~***П);

1/ G&1чиое сов.ц;щие x.n&cca. C~..


Carcl '" пе,. Car (" 51 ugБl1g" 1 1 О О, 1 О) ;
/ I РеЖОИС'1'раЦНR обраClО'1'ч:ro:ов t!обt.1'J!k:йДml ~a Сц,
с 1. ОпАЬО11 tToBlDW (IГiew Сат . АЬо.нЬ,[,oB1Q1rJ (CarAbacutTo.FHow) ) ;
cl.OnExp1.oded(new Car .ExplQd'2d(C.arExplodвdl);
1i УсJtQРJf8МСЯ (при З~ Ж'еверир)/'l!j'1'СЯ: соБЫФНII) .
Ссщsоlе.·Wri teL~ne ("\n"'~" 1<* Ускорение *Н * * ,. i ;
fQ.1"(iПт;. i = О; i < 6; i н )
cl .Acc€,ler-ate (~O, ;
Соп,э<йе" ReadLi:ne () ;
}

'1 Си БУ,Qеw JDLSDa.ТЪ Э'I!И ие9ЮДЫ.


publi,c s.tatic VGid CarAboL1t7cBlow (.s.trlng msg)
{. C.oneole.WxiteLine (msg); }
j,'J\1iblicst",tic: \Toid CarEY.}Dloded (str:!.ng' тзч)
{ С::ОПБоlе. W'ri tесLiле (msg) J }
354 Часть 11. Язык программирования С#

Здесь следует отметить только то, вызьшающая сторона задает значения чле­
нам-переменным делегата с ПQМОlЦЬЮ вспомогательных методов регистрации.

Кроме того, поскольку делегаты AboutToBlow и Exploded вложены в класс Car, при
их размещении следует использовать полные имена (например, Car .AboutToBlow).
как любому конструктору. мы передаем конструктору делегата имя метода, кото­
РЫЙ нужно добавить в список вызовов. В данном случае это два статических члена
классаProgram (если вложить указанные методы в новый класс, это будет очень
похоже на тип CarEventSink из примера Eventlnterface).

Реализация групповых вызовов


Напомним. что делегаты .NET наделены возможностью группового вызова.
Другими словами, объект делегата может поддерживать не один метод . а целый
список доступных ДJШ вызова методов. Когда требуется добавить в объект делегата
несколько методов, используется перегруженная операция +=, а не прямое присва­

ивание. Чтобы разрешить групповой вызов для типа Car. можно обновить методы
OnAboutToBlow () и OnExploded () так, как показано ниже.

public class Car


{
11 Добав.пение эпемеН'J.'а в СnИСО1l: вызоаов.
public void OnAboutToBlow(AboutToBlow clientMethod)
{ almostDeadList += c1ientMethod; I
риЫ ic void OnExpl oded (Exp1 oded clientMet.hod)
{ exp10dedList += clientMethod, }

Теперь вызьшающая сторона может зарегистрировать несколько целевых объек­


тов.

c lass Program

static void Main(string(] args)


(
Car сl = пе. Car("SlugBug", 100, 10);

11 Ре:rис~ёЩИJI множеС'J!Jaа обрабО'J.'ЧИ1l:0В соб~.


c1.0nAboutToB1ow(new Car.AboutToBlow(CarAboutToBlow));
c l.OnAboutToB1ow(new Car.AboutToBlow(CarIsA1mostDoomed));
cl.OnExploded(new Car.Exploded(CarExplOded));

11 Car буде'J.' Bыsывa'J.'Ь Э'1'И методы.


public static void CarAboutToBlow(string msg)
( Conso1e.WriteLine(msg); I
public static void CarIsAlmostDoomed(string msg)
( Сопsоlе.WritеLiпе("Важное сообщение от Car: 10}", msg); }
public static void CarExp1oded(string msg)
( Console.WriteLine(msg); )
Глееа 8. И·\'JтерфеЙr.;ы абратного вызова" делегаты и роlJЫrИН 355
в ЩЮГРiilММl'ЩМ 'КоД"е CIL оперщщ.я += llреQбрв:зуетсп в выЗов статического ме­
тода Del~gate .СоrnЫf1е(.) (мо;ншо бьшо бы вызвать Delegate .СошЬiпе () непосред­
ственно, но ощ:рзцин: += предлагает более простую алшернаишу). Натяните. на­
пример. да CIL-преДt''l'авление метода ОлАЬQutТоВlоw ( )•
. met:fюd public bidebysig instance yoid OnAbout'l'oBlo1f
(сl~·.зs Carf.)~legate.Car/Al)o1,J'tToElow clierltJl1,etbQdj сН m<l,J'Lag€ci

.ma.xstack 8
ldarg. О
dup
ldfld сlаsз 'CarD~lE!gate. Са г/lI..Ь.сщt'l'vIilow CarDelega:te .ca.r: .:i'!lrn{)jзt;DeаdЫst
ldarg.L
call clas8 [msссэ%:liЬ] System.Dalegate
r'm$cor~ib] System,Delega'te: : СФПlbinе t
class [mscorl.ib] Sузtem. Del~gate,
class [mscorli.b]Sy.stem.Peiegate)
са:ЕЙ.сlаS$ Са rDelegate. Саr/.JlliоutТQ'ВlФ.w
sttld сlаэs Сат~lеgаte.СаJ7JА1Юllt'rоВl'0>11 CE!tDelegate.Car:::
.almostD€adList
ret

1Сл:ас(: Delegate определяет та&же статичес:кий метод Remove (J. коrroрый по­
аволит ВЫ3ьtвающcii: стороне динамически удалять элементы из СUИCRа Вhl<JOBOB.
ЛеtRо догадатьСя. ЧТО в c1f разработчики MOryT ДJ1Я этого ИСIlOЛbзоватъ перегру­
Ж~ННУЮ операцию - =. Чroбы предоcтaDИТЬ ВЫ8ывшощей стороне ВО3МОЖl-ЮGТЪ не
привязывюъсл к 060знач:е:ниям AЬou.tToBlow и Exploded. можно Добавить в ntn
Сат след;yroщие вспомогателънъrе методы '(обратите внимание на операцию - =).
:p11l:JHC с:lа$.Э Сат
j
11 'УДaJIeJIИS эnенеит.а И3 оаи:сжа Эl$оэоа.
ppblic \fOicl Rel11OveAJ:H:;JutTc;H31ow (·AbQll"t:ToliНow сliепtМеНюd)
{ аlIlюstDеаdList -= сl ieТJtMethod; I

public V'oid Rr:mtoveEXplodE!d (Exploded с1iедt1i!еthоd)


{ explodedList. -= clie.ntMe-thod: !

3деС'-д С1Ц["гав:сис -= тt»J\e выступает в '~ачес:rве простого СОRращения: ДJЦI' .вызо,


вастатичecRОГО метода Delegat-8.R€mov~ ('). ~цo дока.зьшается сле;цующВJ\d npограм­
МНJ,W ЛОДОМ CIL ДЩi Ч;Гlена RemoveAboutTDBlow () типа Сат,

. mеtJюd рuЫ i·c hi aebysi.g inst аПС8 'vQid R8mоvеАЪOlJtТсШlо.w (


сlаЭ$ 'Саг Deieg.a·t.e . СаХ / Abv.JLitToB low cl.ieI1t~tiltJd) ci 1 шапаgеd

.. тпах:о tack. 8
ldarg.O
dup
1dПd class carDelegate, Ca:r/Abo1Jt1'OlHow CarDelega,t€.. Саг: :iJ.lmos,tDeadLi5t
ldarg.l
356 Часть 11. Язык програММJ.1роваНИR С#

call class [mscorlib]System.Deleqate


[mscorlib]System.Delegate::Remove(
clasB [mscorlib)Syвtem.Deleqate,
clasB [mscorlib]System.Deleqate)
castclass CarDelegate.Car/AboutToBlow
stfld class CarDelegate.Car/AboutToBlow CarDelegate.Car::almostDeadList
ret

Если вызывающая сторона пожелает удалить элемент из списка вызовов деле­


гата, мы должны предоставить тот же объект делегата, который был ранее добав­
лен. Поэтому мы можем прекратить выдачу сообщения Exploded, обновив Main ()
так, как показано ниже.

static void Main(string[] args)


{
Саг cl = new Car("SlugBug", 100, 10);
11 СОхРанение обtъех~а Car. Exploded деn:еrа~а.
Car.Exp1oded d = new Car.Exploded(CarExpioded);
cl.OnExploded(d);

11 Удuение ие~ода CarExploded из cnисха В1oIЗОIIОII.


cl.RemoveExploded(d);

Вывод нашего приложения CarDelegate показан на рис. 8.5.

Рис. 8.5. Приложение CarDelegate за работой

ИСХОА"ЫЙ код. Проект CarDelegate размещен в подкаталоге. соответствующем главе В .

Более совершенный пример делегата


Чтобы продемонстрировать более интересные варианты использования делега­
тов, мы добавим в класс Car пару члеиов-переменных логичеСКQГО типа. Первый
из добавляемых членов предназначен для индикации того. что автомобиль необ­
ходимо помыть (isDirty), а второй будет сообщать о том, что автомобиль требует

I
Глава 8. Интерфей~ы об.Р1l1нога вызова, деяегаты 11 собl:llТ11,R 3-57
замены шин (sIЮll1dR'оt.аtе), 't:JтofiЬL DОЭЕОJIИТЬ ПОJThЗователю, объеъ."ТЗ взаимодей­
етвовать с этими НОВЫМИ данными соСТоmш.я. дл:я Сах опредe.mпoтс.я веобхОЦИМ1i1е
дополп:ителънъте овойства, и обновляется KOнcrpy:ктOp. Вет как BыI'.!llIдw1' соответ­
ствующим образом модифицированный программны:й код.

/ / Обвоanе1UWЙ JUtзсс Car.


рuыlссlаsзз Car
t

11 Не пора .nи' ПОИloШ!ь? Не пора .nи, схешсть IПИВЫ?


private bool isDirty;
pri,v ate bool shоtйdRсtаt~, ;
/1 ДООo,7lJЩ!I!en~ oapaкll!l'1'pЫ ДЛИ установки буneм.tX !lка:чеиий, .
puDlic :Caz: '(striTlg пате, irJt mazSp" int cиT'ГS:p,
Ьоо1 wазhСаr r Ьооl rфt;atеТirеs)

lsDirty ; washCar;
s,hQu.ldР.вtдtе = 1iotat:e'I'iresl

риы1: ,b ool Dirty


j
get { .ret ut'n i.$Uirty; j
aet~ isD:irt..y = v:illJe'; }
)
public bool 'R"Otate
{
get'{ returnshouldRotёite;
set J shoull'dRota:t~ = val и8: J
}

Также преДIIОЛОЖИМ.ЧТО в ТИП Сах ЦJ10~.н :ЩОВЬ1Й дцегат C'a rDelegate.


1/ ДmI Car опредenиетси еще о,ции ;цeпera-:r.
puыlc: class Са!
!

IJ Мo"le'1' выз~а'1Ъ .IШCIQЙ ~:ц, поmnaаwциi( 'Car а виде паpёDleq->а


11 и не 8Qцращ а пцнй ИИ1il:его.
риЫ ic clele:ga:te ".oid ,CarDelega'te CCat с) ;

Эдесь создается делег.ат ~ именем Car:Delegate. тmr C,a,rDeleg'a te nредставля.ет


"некоторую' Функцию"'. принимающую Car в качестве параметра и возвращaIOJЦYR)
пустое значение.

ДелегаТbI в качестве параметроs


Теперь, котдау нас есть новый ТИП Де.lшtата. FШТОрblЙ унaзъmает 00 метОДЫ. по­
луча.ющие Са!' в виде параметра и не воЗIlраЩaJOIфН~ ничеrо. :мъr можем созцава:ть
-
358 Часть 11 . Язык программирования С#

функции, которые принимают этот делегат в виде параметра. для примера предпо­
ложим. что у нас есть новый класс, которому назначено имя Garage (гараж). Этот
тип поддерживает коллекцию типов Сат. содерЖaIЦихся в System.Collections.
ArrayList. При создании ArrayList наполняется типами Сат.

/ / К.nacc Garage хранит список ТИDОВ Car.


Using System.Collections,

public class Garage


(
/ / Создание списка всех машин в I'араже.
ArrayList theCars = пе. ArrayList();
/ / создание .ка-к в I'apuce.
public Garage ()
(
/ / Напомним, что ХОНС'1!ружтор быn обиовлен,
/ / и теперь можно УС'l'&ИовИ'1!Ь зиачении isDirty и shouldRotate.
theCars.Add(new Car("Viper", 100, О, true, fa1se»);
theCars.Add(new Car("Fred", 100, О, false, fa1se));
theCars.Add(new Car("BillyBob". 100, О, false, true»);

Класс Garage будет определять общедоступный метод ProcessCars (). который в


качестве единственного аргумента получит новый тип делегата (Car.CarDelegate).
В Pro.cessCars () каждый объект Сат из КOJшекции будет передаваться в виде па­
раметра ·той функции. на которую указывает" делегат. При этом ProcessCars ()
использует члены Target и Method из System.MulticastDelegat e. чтобы опреде­
лить, на какую из функций делегат УI<азывает в настоящий момент.

/ / К.nacc Garage ииее'l' ме'l'ОД, испоnьs)'DЦИЙ CarDelegate.


using System.Col1ections;

public class Garage


(

// Э'l'от метод попучает Car.CarDelegate _ аиде параметра.


public void ProcessCars(Car.CarDelegate ртос)
(
/ / Худа иаправИ'1!Ь вызов?
Console.WriteLine(""**** Вызывается: {О} *****" ,
proc . Мethod) ;
/ / Вызываетс~ метод экзeкп.nиpа или ст&тичесltИЙ метод?
if(proc.Target != null)
Сопsоlе.WritеLiпе("->Uель: {О} ", proc.Target);
else
Соп sole. Wri teLine (" - > ЦелевЬL''-1 является статический метод") ;
/ / ВЫзов "ух&Заииого" ·метода всех машин по очереди.
foreach (Car с i n theCars)
ГЛilва В. Инте,рфеi1сы Ьбра:~IOГQ МIIзова, де:neгвты и' события 359
Сопsо1-е .Wri teLine 1"\п-~ ОбрабО'I''!{аСa.r'') ;
PIOC (с);
J
}

Кав и в случае lIЮбаго деле;rата, при вызове P:roci?asCi'if s (). мы должны У1са­
~a1Ъ имя метода, который. обработает запрос. Ha:rrOMI-IИМ, что такой метод мщкет
быть или tтaтичеckИМ. ИЛll методом экземпляра, для примера предположим, что
Б пачеc::rnе таното меТdда буДут исnoл:ьзоваться ttJrel-IЫ экземпляра новото класса
BerviceDepartment (отдел те:хни'1ecRоt() обслуживания). :которым назначеНЪ) вме­
па ИasВ'Са!' () и R€Jtatelires ( I ' Обратите внимание на 1'0. '11'.0 ЭТИ д)щ метода uc-
1IОЛЬЗУЮТ новые СВОЙ:ст.ва Rotate и Oirty типа Car.
/I ЗIro'i' JUtacc опредeJIие'!1 Me'1'O~, ItO!l!O~ БV'Д>"» 1w9ыв~сяя
11 ~ОИ Car. carDeIeqate.
pl1blicclass Servi сеПераr.tmелоt
{
public void ·Wasbl~ar (Саь: с)

!
if(c.Dirty")
CO:nSGle. Wr.i t 'e Lirfe ,( "Моем машину") ;
el.se
сопзоlе. Wri te1.ine ("·Зт.а машина уже (юмыта .• '. !'');

publ.i.c vbi.d RctfateTires (Car с)


{
.Н (c.Rotate)
С.Qлёоlе. Wri.teLin~ ("~няем JJJИНЫ") ;
els.e
('шуs аl.е . wri·t.eL iле ("Иенять lНиНI).1 Ы'~ тр ебуется, .. ") ;

Теперь проиллюстрируем В'эаимодействйе между НОВЪ1М1! 'Типам:й С е. r .


CarDeleg.ate, Ga r.age- и Sег;riсеDера rtment, рЭ.ссмотрев их использование Б сле­
дующем фрa:rментЕ пр.огра.ммното KQдa.

/ / Garaqe напр~~ .асе sахзЗ1lt:в Se.rv.iCeDep~tment


11 (иaWNr хорошего механика :всer;ца ПРGбnема .•. )
jQubl1.C сlаsз PrG.graro

st.dtic v"id Mai..n (strlDg 11 args)


1
11 Соз,дaJЩе :t'8pмca. ,
Ga.rage 9 = 1'le:w Ga:r аче () ;

JI СОS,1lа.ии'е owдеnеЮР! оr:SCn)'JI!ИЭ&ИЮI.


Servic.eDeparotme)lt sd = ое .. Se['V'J ceD€>,paJl:·.trr,€jjTL (j;

11 Garage МОе!1' н_ин И иеиие!t шины,


1/ J1~rиpYJ[ СОО'1'З8тсuiВ:iDЦI1fEl п().!:tноиочии ServiceD'e partment ..
9 .PrOCe$SOi2:S (new Car. CarDel egi3t·e{ sd • Wash,cёJг) );
360 Часть 11. Язык программироваНИR С#

g.ProcessCars(new Car.CarDelegate(sd.RotateTires»;
Сопэоlе.RеаdLiпе() ;

На рис. 8.6 ПОRазан соответствующий вывод.

Рис. 8.6. Перекладывание ответственности

Анализ программного кода делегирования


ПредложеlШЫЙ выше метод Main () наЧИНается с создания энземrmяров типов
Garage и ServiceDepartment. Когда вы пишете

/1 ПОМW'l'Ъ все ГPJl!lНile мamинw.


g.ProcessCars(new Car.CarDelegate(sd.WashCar»;
это на самом деле означает: "Добавить указатель на метод ServiceDepartment.
WashCar () R объекту Car.CarDelegate и передать этот объеl<Т в Garage.
ProcessCars () ", Подобно любому автомобильному предприятию в реальном мире,
все заказы передаются:в отдел технического обслуживания (что и объясняет, поче­
му замена масла. обычно требующая 30 минут, занимает целых 2 часа). С учетом
этого ProcessCar s () можно интерпретировать так.

11 CarDeleqate ухазывает на Фунхцию ServiceDepartment,WashCar.


public void Proces sCars(Car. CarDelegat€ proc)
{

L
Fла,вз8, И~IТЭРф!:!ЙСЫ обраТflDГО ВЫЗОВЭ, делегаты и соб!JIТИЯ 361
foreach ( C~T с in thеС~rэ1
proc(cJ; IIproc(o) => Se:r:viCeDSpaztm&nt,WashCaz(c)

ТОЧ1iО тан же. если вы roворите

11 Помеюшъ JIIщ!ы.
g. !?тосеЪ1 sCar s (л€!"w Сат, CarDelegate (зd. Rot.dteTi!l:es) ) ;

ТО Proces,sCars ~) можно интерпретИровать" RaК

/ / CarDeleg&Цухаs:wsз.м ~а.фун!ЩИXI, Seз:viсеDерцtment. l\otateTires .


p-ubl i:c: vo:id Fr{)сe:sзСалs (Са.!" Car!Jelegate ртос)
{
, .'

fO'l'eaGh (.сат с iп th's(ar.s 1


pr0Q j с') ; j / pzoc (с) => Ser:viceDeputment. Rotate'll-res (с)

Исходный код. Проект СагGaгщjе размещав в подкаталоге, с60тветствуroщ€м птаве В.

Ковариантность делегатов
к этому моменту БЫ должны чувствовать себя БOJ,ее ynepetrno при создании м
использовании типов делегата, Перед тем как перейти ,Е иэучению mпrтаксиса со­
БЫnIЙ II C,:ff, мы раССМQТРИМ новую возможность .NEТ 2.0. CRЯэанную с делеrатами
и обозначенную термином кoвa.puaHm.нocmъ. Вы могли обратить внимание на то.
ЧТО все делеrаты. созданные нaJ.\4И ДО сп пор. указывали на методы. воз,йращаю­

щие простые числовые типы данных (ил:и не возвращающие значений :вообще). Но


upeдnоложим. qтO H8..!v:l нужен делеrат, способliый yшtзъmат-ь fla :методы. воавраща-
1OЩ)Iе пользовательс.юm ТИП класса.

1/ О,ареде.nesие де.пerа!1'&, xo~o~ ~ОЗ!l~ УJCа9:uва'1'Ь на об ...е~.1


j / зОз.ращр~е типы Car.
public: delega t:€' Car оЬtаinСаrпеlеg'аtе () :
мы M~teм оnpедeл:wrь Целевой обlЬtЖТ fIllЯ делеrаТа так, I(al( 06ы:чно.

се las's P'[;ogram
1
pl1Dlic delegate Car Ob,tainc:ax'Delegate();

pllbli~ static Са!' Ge'tBasicCa.l' О


{ п~'t. йЛ) !',е", Сат (),. }

st,atle voitJ Maib (:H..r i "9l]args)


r
ОЬt<J.iпСаrDеlеgа 1:е target.A = n:sw ObtainC~rDeI egabe (GetBa5ieC.ar) )
Ctir с ;; t.argt;'t:A () ;
Cons.ole. Reаd1iпе () ;
}
-
362 Часть 11, s:lэык программироваНИR С#

Пока что все выглядит прекрасно. Но что делать, если мы получим новый RЛасс
SportsCar из типа Car и потребуется делегат, который сможет указывать на ме­
тоды, возвращаемые ЭТИМ новым типом I<ласса? До появления .NEТ 2.0 в таном
случае вам пришлось бы определить новый делегат.

1/ НОВЫЙ двneгaT, уха.ывaDЦИЙ на цеnевые об'lo8ХТЫ,


11 аозвраЩ8'IIЦИе тИIПil
SportsCar.
public delegate SportsCar ObtainSportsCarDelegate();

у нас теперь два типа делегата. и мы должны создать по зкземnляру .каждого из


них, чтобы получить типы Car и SportsCar.
c lass Program
{
publ ic delegate Car ObtainCarDelegate();
p ublic delegate SportsCar ObtainSportsCarDelegate();

publi c static Car GetBasicCar()


{ return new Car(); }

public static SportsCar GetSportsCar()


{ return new SportsCar(); }

static void Main(string[] args)


{
ОЬtаiлСагDеlеgаtе targetA =
new ObtainCarDelegate(GetBasicCar);
Car с = targetA();

ObtainSportsCarDelegate targetB =
new ObtainSpo rts CarDelegate(GetSportsCar);
SportsCar зс = targetB();
Console.ReadLine();

По ЗClliонам .классического наследования в идеале лучше иметь один тип деле­


гата, который мог бы указывать на методы. возвращающие либо тип Car. либо тип
SportsCar (в конце концов. тип SportsCar связан с Car отношением наследова­
ния). Koвapиaнmнocmb позволяет реализовать именно такую возможность, Т.е. по­
строить один делегат. способный указывать на методы. возвращающие тицы клас­
са, связанные классическим отношением наследования.

class Pr o gram

1/ Определение делегата, способного возвраща~ь


11 хак
Car, так и SportsCar.
public delegate Car ObtainVehicalDelegate();

public static Car Ge tBasi cCar ( )


{ return new Car (); )

public s tatic SportsCar Ge tSportsC a r()


{ return new SportsCar(); }
1 Гла8~ 8. Интерфейсы обратного sbl~dBa, деneГd1'1>i ~ Cd'быт~~

stat.io !zoid Mein (st:r;i:ng 1 J атчз)


Э63

I
Соnэ~l е. Wri t .e Lin.e (".... * "*" Ко.В'df'иа.н'!'иос.~ь делега.ТQВ *" * 'k*\n 11 ) ;

Obta.inVeb,icalDele:g ate tal'get:A == new


ObtainVehicalDe le'ga te r Gеt:Баsi cC",r) ;
Сатс = tar-gеtА'(} t
11 'l'sl'Ioe ~CJII~аиие воЗМОЖНо вс:ледС'l'ВJ(е JCОВ~С'l'И,
Ob,tainVehicalDeloegate t argetB = па ..
ОЬtаiлvеhiсаlDе l еgаtе(GеtSРQrtsсаrJ I
SportsCar s-c = (SportsCa.r) t argetB (.) ;
Co.p $ole .Readblne ();

Обратите внимание на то. что 'Г'IШ делегата ObtainVehicalDelega~e был оцре­


делен для того, чтобы YI<~BaТb на методы, :возвраща ю Щ1re cтporo типизовaнныif
Car. Однано в условиях ковариантности мы получаем возможность унаЗhIВаТЬ)J
йа методы. возвращающие прОИзводны.е 'I'ИIIЬL Чтобы получить произвоДНЫЙ nщ,
IIyЖfIO просто ВЫПолнить явное прообразовanие.

'Замl!чaни·е. Точна 'так же IWвзрИВН'fi.IOСТЬ обеспечивает возможность создания делегата, кOТQРЫЙ


rтОЗВDJI,И'f указать IiB M!imкecт'8f) MeTQД08, noлучающих ~бьеКТbl . С'8язаННbiе классическим отно­
шением наследоааККR . Более подроБНaJ1 И!1фОРШIЦИЯ имеется II д(,)кументации .NEТ Framework
2.0 SDк.

исходи...... kOД'. Проеn DelegateCovaFlance размещен в ПОдkЭталоге, СОО1'ее'Гствующем г:лаве В .

Со'бblТИЯ В С#
Делегаты оказ-ываются QЧ't:Нb ИErreресн:ыми КGJИетрyщ:wлм:и; с ТОЙ ТОЧJm ЗРeJШЯ ,
ЧТО ойи предостав.ляют возможность реализовать двухсторо;ннее ВЗЩlМОДЩfствие
междУ объект3.Т\.IИ в пaм.wrи. Одшшо. :и вы с 'ЭТИМ сог.п~~;ИТ~Щ" работ~ с делеrnта~
1IШ напрямую преДn'J.!Iarает :ВВ"Д больших по объсМу ша6лOНl{blX фрагментов про­
гpaммнoro кода {anpеделени.е де:л.егата.· объявление членqв-пере,меЩЩdХ, СQЗдание
щ)лъзовзТелЬСкихметОДОВ регис'tpа'ЦИИ и отмены регистрaцJfИ}.
Поскольку возможность обратного вызова оБЪeIcrов дрyrим 06уектом ЯБ.lЩетС'л
очень полезной, в С# п;ре;цл.агаетСn: СПециальное ключевое CJJfJBO ~ve:nt, ПОЗБОЩI'
ющее :мипимиmrpовать неудобства прагрaмNIИста, связанные с неJ;rосредственцым
nрименевием делегатоВ. при обрабоruеюnoчевогослом ~vent кОМПШlЛтор' ~TO'
ма'Гически создает дrJIЯ вас методы регистрации и отмены регистрnцlЩ. '" 'Ii~e
'ЧJIены:~перемепные. неоБХодимые ДIJй вшиcro типа делегата. КЛючеВОе· СЛово €,,"ent
можно наз.вать СИН'1'аксичеСJЮЙ ~Jroнфеткоif'. позволяющей экономить BP~ IlpJf
вводе программного кода.

'З8М8"Iвнме. даже при ИQПольэовании в сit"JWIQЧВВQГО слова event !:!ам все мвiЮ придется ВРУЧ,
ную QпределЯТI:1 связанные с делеrёfТОМ ТI<1[1Ь1'.
364 Часть 11 . Язык программирования С#

Процесс определения события состоит из двух шагов. Во-первых. вы должны


определить делегат, который будет содержать методы, вызываемые при наступле­
нии соО'Гветствующего события. Затем вы объявляете с.обытия (используя ключе­
вое слово С# event) в терминах соответствующего делегата. Определение типа,
способного посылать события, имеет следующий шаблон (записанный здесь в
псевдокоде).

publi c class SenderOfEvents


{
public delegate 80sspЭиаvеиие AssociatedDelegate(apryыeH~);
public event Associ a tedDe l egate ИиRСоБЫТИR;

События типа С а r будут иметь те же имена, что и предыдущие делегаты


(AboutToBlow и Exploded). Новому делегату. с которым будут ассоциироваться со­
бытия, будет назначено имя CarEventHandler. Вот начальНые изменения. вноси­
мpte в определение типа Сат.

p ubli c c la ss Car
(
11 Этот ~eneraT ~аБОТАет в свкэхе с событиими Car
public delegate v oid CarEv e ntHandler(string ms g);
11 Об'Ъ8ХТ Car ко.ет nocыna'1'Ь Э'1'И соБытя•.
p u ыcceve nt CarEven tH an d ler Ex pl o ded;
public event CarEventHandler AboutToBlow;

Отправка событий вызывающей стороне выполняется с помощью простого


указания имени события и всех обязательных параметров. предусмотренных в
определении соответствующего делегата. Вы должны проверить событие на зна­
чение null перед тем, как вызывать набор методов делегата. чтобы гарантировать
регистрацию события вызывающей стороной. С учетом этого предлагается новый
вариант метода Accelerate () для типа Car.
public void Accelerate(int d e lta)
{
11 Еcnи кашина cnонана, rенерируеТСR соБW'1'Ие Exploded.
i f (carIsDead)
{
if (Exploded !=пull)
Ехрlоdеd("Извинит е , машина сломалась ... ");

else

cu r rSpeed += deltai
11 Вот-вот cnокается?
if (10 == maxSpeed - currSpeed
&& Ab o utToBlow != nu ll)
Глаllа 8, Иflте:рф&йt:'ы '(jБРJl;НОГО вызова, деnега1'ы и СDб.ытия 365
Abou,t1QBlQ~1 ("Qс~орожн.о! Мо,гу 'сломаться 1") ;

/ / J;IO,!I!8' в ое ОХ: !
if (cur.rSpeed>= I!laz'SрееЩ
саУI s'Dead ~ true;
~lse
C~mS,Qle. WriteLine (~->ОJ:rrSраеd = ! О}", ,C'U,rrSp,eed} i

МЫ Нaдtmил:и автомобиль dIIособностью посылать два типа ПОЛЬЗ0ват~ских


.собы1I'ИЙ, избавив себя. от .необхqдимоети определить по..ТJЪзcmателъСЮIе фушщц;и
регистраЦии. НеМiЮП"J позже мы npиведем npимер использ(;)ва.в:ия rЩWeI'О RQВЩ'О
автомобиля, но сначала более подробно рассмотрим архитектуру событий.

глубиниый механизм событий


Событие в Ci цр~дставляется двуМя скрытым:и общ~достуцныи: м:етодm.uJ,
DДИН из которых :имеет дрефИRС а4с1_. а другqй- цреф:ИКС I emov:e_. 3,а ЭТ~ пр~
фшtса:ми сл~дует:и.м.я события. Например, событие Exploded трансJШРУется в цa~y
CIL-.м.еТQДОВ с именами add_Exploded () и remove_Exploded{,). Кроме flРЩltЩ~
R методам add_ХУ.х () и rem.o,vE'_XXX О \ определение события па YPOBH~ СП. СIЩ3Ы­
вает даниое событие () соответствующим .делегатом.
БЗrляRИ1'е па СIL-инструкции для .add_ AbolJt'I'cBlQW (), и вы обнаружите Р}XI­
Г.РaмынblЙ ход. почти иден:ruЧН:Ыlr протраммному коду вспомогательного метода
OnAbou.t'ToB10W{) из рассмотреняоJ'О вЬШrе при:мера CarDelegat~ (щ;ратите 1tНИ­
мание hactpOI-ty с вызовам Dеl~gаt€.СоmЬiл€ О) .

. rnethod Р).1ЬН.с l'J.:ldebysig special:naroe insta.nce void


add_AЬoutToBlow (class С аrБ\tе.лts, Сат !CarE'v entHancller "'val uе'-)
cil таnачеа "упсhГсmlz .еd.

,JltdJ't8 tar::k В
lde.rg .0'
lda-:rg.О
ld:f ld class CarE~!]ts .·Ci1l.r;/C.ar,ENe!] t:Нandleт Ga·d v-ents. Са:с: :AbQ!1tToBlow
J.ctarg.1
call сlазэ [lnso<r3.rli1J] S.ystep1. Delegate
[mscorlibj System.Dele_9 ate: :Combine (
cla 53 [m,sco1rl iыl $ys,t6m. Delegate,
class [шзссrliЬ'j ayst'em.Delegat.er
castclass Cii\·rEven ts. СаrJСаrЕV2ТJ,tНаш:!lеr
st.fld с lазэ CarE\r€rnt~s _.Car iС arЕvелtНan,d1еr ' ёаtЕvер ts . С<3,Т; ;.AbQutTQEloW
r e-t

в соотве'}"С'Г.1JИй С ожиданИЯМИ. метод :tепюvе _ AboutTbBlow () неявпо (QDocpeAO~


ванна) вызывает Delegate .Re1rtov~ () и npиб.tmзителыIO соответствует определes­
lЮМ,Y вьnдe ВСIIОМОгатeJ1ЪПОМУ :методу RеJ:tюVеА,Ь.оutТоБlоw 0..
366 Часть 11. Язык программирования С#

.method public hidebysig specia1name iпstалсе void


remov8_AboutToBlow(c1ass CarEvents.Car/CarEVentHandler 'value')
ci1 managed synchronized

.maxstack 8
1darg.O
1darg. О
1df1d c1ass CarEvents.Car/CarEventHand1er CarEvents.Car::AboutToBlow
1darg.l
са11 class [шsсог1iЬ]Sуstеm.Dе1еgаtе
[mscorlib] System. De1egate: : Remove (
class [mscorlib]System.Delegate,
c1ass [mscor1ib]System.Delegate)
castclass CarEvents.Car/CarEventHand1er
stfld c1ass CarEven ts.Car / Car Ev entHandler CarEvents.Car::AboutToBlow
ret

Наконец. программный код CIL, преДСТаБЛЯЮЩИЙ само событие. использует ди­


рективы . addon и . removeon для отображения имен в соответствующие имена вы­
зываемых методов add _ ХХХ () и remove_ ХХХ ( ) .
. event CarEvents. Car / EngineHandler Abou·tToBlow
{
.addon void CarEvents.Car::add AboutToBlow
Ic1ass CarEve nts.Car/CarEngineHand1er)
.removeon void СагЕvепtS.Саг::гешоvе AboutToB1ow
(class CarEvents.Car/CarEngineHandler)

Теперь. I(огда вы знаете, как строить ЮIассы, способные посылать события в С#


(и знаете о том, что соответствующая событиям синтаксичеС1\ЭЯ конструкция­
это просто СОRращение. позволяющее уменьшить объем вводимых с ЮIаБиатуры
данных). мы должны выяснить. нак осуществляется Цприем~ посryпающих собы­
тий с точки зрения вызывающей стороны.

Прием поступающих собblТИЙ


Использование событий в С# позволяет танже упростить регистрацию обра­
ботчиков событий вызывающей стороны. Вместо необходимости уназывать поль­
зовательсние вспомогательные методы. вызывающая сторона просто использу­

ет операции += и -= (ноторые в фоновом режиме "подключают" add_XXX () или


r€ffiOVe ХХХ I )). Если вы хотите регистрировать событие, то следуйте ПОRазанному
ниже шаблону.

11 ОбъеК'1'RaяIIеременн&Я. ИмиСобъrrия +=
I1 new Соотве'1'СТВYDЩИЙДеnега'1'(вwзываеиаRФУНКЦИИ);
Car.EngineHand1er d = new Car.EngineHand1er(CarExplodedEventHandler)
myCar.Exploded += d;

Чтобы отменить привязну н ИСТОЧНИI{)' событий. используйте операцию -=.

...
Iлава 8. Интерфейсы обратноrОВblзова, Аелегаты и cuбl>ПИ!j 367
11 ОIS'J.8кщ~~~. ИИJrСоБJ,Щ1ИR -'= О~Д~er'a'l'а.;
1JlyCar . ExpCLaded -= d;

С УЧ~ТОМ этих сооrrветст:вую1.Ц'иХ ожиданиям шаблонов . вот шы: р;о.л:жеН выrлll­


деть мор;Ифnцироваl-ШъrИ метод Ма {n О. в :КОтором используется СИНТaJ<СИС реги­
СТРаЦЙИ событий С#.

clas$ l?rogram
(
static void blain (St.ring[) a:!."gs)
{
СОП'S<JDlе .• Wз::itеLiпе ("**".** ООDЫТИЯ "1i"~*");
Саг ci =
П
new Саг с э1 ugВug , нJO, 1 О) ;
/ / Ре:I'ИC'ЗIJ?ЗЦЮI обрЗбО'Х'ЧJщQ8 ооБШ!ИЙ.
c1.AbOlJt'roBlow += l1ew Сах: .саrЕ·.... еDtНапdlеr (Ca.rIsAlmostDoamed);
cl.lI..houtToBlow += I"!ew Саг.СагЕvеntJliЭ.ndlеr (Сал:;АЬоцtТоВlоw);
Саг. CarE)\7~tH[;llicller d =о ]')~W Car. CarEve!JtJ-I111),:ilеr (СаrЕхрlоПеd).;
cl. Е:ирlоded += d;

СО:пsоlе .. WГitеLiпе,(,,\й**,,** УС1<юрение ... ",н,*n),­


±Or (ipt i = п; i < 6; i++)
сl.Асоеlетаtе(ЖО) ;
1/ Уда.пеиИе Х8'Х'од& CarExploded
1/ '111.8 С!писха :ut90aoa.
с 1. Expladed -= d;
СtШSQlе.W.titеLinе ("\п..,·**** }'СКОр0rlие *****") i
f6r (int i = ~, i < б; i++1
.:;1 .Accelerate (2 О);
CoM,Ole. ]1.ead'bl 11 е () ;
}

pliblio stati·c void CarAbout'l'o:810'111 (striвg 1llsg)


{ \[;Qщю1е. wrl tеLlле (rog.q); i
pub11~ зtа:tiс vold СаrlsAlmоз.t;Dооюеd (striшg IТH:,g)
i ConsGle.t·klteLin~('ICLitical Mess.age frO!!l Car: {О)", ПlsgI; }
p\iliiic static vo:id CatEx;ploded (sцi11g mэg)
{ еопвеlе. Wri t.eLih~ (JТi::Ч) i }

Исходный КОл. Проект CarEvents размещен в гюдкатаnог~, соответствующем главе 8.

Упрощенная регистрация собьпий в Visual Stu.dio 2005


в Vlsual Studio .l'IEТ 2()ов и Visual Studto 2005 прeдJ1агаетсл домощь в npоцессе
реrиcтращlИ обработчикоп соБЫТИЙ. При вводе += в онне программного кода по·
JIВ.tШется оюю IntellilSense, пре1lЛaLtlЮщее нажать мaвиIIJy <Thb>, чтобы автомати·
Че(.'ttи ввести соответст.вуIOЩИЙ ЭRземnляр ~делегата: (рие, 8.7).
368 Часть 11 . Язык программирования С#
r
r ,,' f! ::~J;;, e 31 '_~.e. x.: "-:::'::" ;.<.З и ,~ 1 .

~:.'5X сl .. nе'Ь' (:..;:tt (r'S lugBug", 100, 10);


cl.AboutToBlo~ +=
rl""-.-::-t.or-·u.----:::E""-nffi-:::-:-end--:;I""---:-(c"""t-_AЬ-:-:-"""'t""T-=авс-:-I_----::>:-('"'"Pr-es-.-=ТАВ,.-t-оinser-~tЯ
.....

Рис. 8.7. Выбор делегата IntelliSense

После нажатия клавИIIIИ <ТаЬ> будет предложено ввести имя генерируемого об­
работчика события (или согласиться ИСПользовать имя. предлагаемое по умолча­
нию), как показано на рис . 8.8.

I rit.e!Line ("~ ~1'I' a"1I "E·~;--ent:.re т...,,,,т,,,,,",,):


I
11 .~ ' " " " ,ыН!'!\ 1 .
п ет,!' С а}. ("SlugБ'ug", 100, 10 ) ;
I 0810v r))e'l1 .\':1 '5.1:. . С ах ~"lеJн::Ji-~\t1 !:j t ~.r

i
!!
Рис. 8.8. Формат целевого объекта делегата IntelliSense

Если снова нажать клавишу <ТаЬ>. вы получите программный код "заглушки" в


нужном формате целевого объекта делегата (обратите внимание на то. что соответ­
СТВУЮЩИЙ метод объявляется статическим, поскольку событие было зарегистриро­
вано с помощью статического метода).

stati c void cl_AboutToBlow(string msg)


\
1/ Add your code!

Эта возможность IntelliSense доступна для всех событий .NEТ из библиотек ба­
зовых классов. Данная особенность интегрированной среды разработки позволяет
разработчику существенно экономить время. поскольку избавляет от необходимо­
сти искать по cnpaв1te .NEТ подходящие делегаты (и выяснять их форматы) для ис­
пользования их с конкретными событиями.

"Разборчивые" события
Есть еще одно усовершенствование. 1tOTopoe можно внести в наш пример с
CarEvents и которое соответствует шаблону событий. peRoMeHдyeMoмy разра­
ботчиками из Мiсrоsоft. При исследовании событий. посьтаемых данным типом
из библиотек базовых классов. вы обнаружите. что первым параметром соответ­
ствующего делегата является System.Objec·t, а вторым- тип, произвоДНый от
System.EventArgs.
Аргумент System.Object представляет ссылy на объе1tт, посьmающий событие
(такой как. например. Car). а второй параметр представляет информацию о соот­
ветствующем событии. Базовый класс System.EventArgs представляет событие и
не передает никакой пользовательской информации.
rfl8вa в, ИН'Fерфе~IGЫ оБР~ТНt}го вызова, делегаты 1.1 СDбlJlТИЯ 369,
public cla5~Eve~L~rgs
(
publi .c sta'tic !еа~щйу S.уstеm.Evел·tАгg·s Eropty;
риыl ic EverrtArg-.s ( ) ;

A.iIR простых ()обы'I'ИЙ вы можете Ц.,рРСТО переда1'Ь экземпляр Eve!ltArgs. Но


если вы хотите передать и nОЛЪЗ0вателъсRИ~ данные, вы ДPJ1mНЫ построить подхо­
дюций. класс . .nPОИЗВQДИЫЙ от E"ventArgs. для нашего uримера мы IIp('ДQОJЮЖИМ.
'l'FO У нас ес1Ъ ЮIасс ' Саr:ЕvелtАrq-з, который со,дерЖИ'r М'рокус сообщением, ОТ­
npавляеiVIЫМ получателю.

pllblic сlазэ 'CacrEventArgs .: EvantArg:s


\
pubJic .teadO.h lY stril1g msg;
j'цbliс Cari!:Ii'entArgs (string message)
{
msg = me'isage;

Теперь мы должны обнови.ть дел~ат СаrЕvешtНаndlеr ТМ(, как nо.каЭЩJ(i) ниже


(события ДОЛЖRЫ оСТаТЬСЯ без изме~еНИЙ).

риыlc С'l-азs Car


i
pu-blic delegat~ v,oid С'а J[ :Е;vепtВаr~dlеr(оЪj'есt senc1er, car!:ventArqs е);

При генерировании r:oбhIТИЙ из м;~qда Ac-celerate () мы теперь должны предо­


ставить с.{:ЫЛ1i."У на те.кущий объеI!'F C~y (е UOМDЩЬЮ ключевого слова сат) ИЭк;3е'М­
шшр нашего типа Caa:E)VentArgs.
p.u blic void Aeeelerate:(i},t delta.)

, {
I J Eomr
if
~a c:.nои~ась. , :r:oеиермрve'l'СR соб2mе ~lll)ded ,
(са J;:! sDea'd)
{
Н( Е:хр 1 oded'! "" !'1I'lJ Н:)
Exploried(this(
леw Cii-хЕvеп tAr9'З (' "Извиниmе f машина .сЛО!ol<lЛdСЪ ••• то ) ) ;

els-e

.. ,
АЬо·ut'ТоБ10.W (thiS,
new CarEver!tA1:'ge ("Ос:rgРОЖЕЬ! МЬГ'У СJlОМЭ<1'!>СЯ! ,j)) ..
370 Часть 11. Язык программирования С#

С точки зрения вызывающей стороны, все. что нам требуется. ,ак это оБНОБ.iIе­
ние обработчиков собьггИЙ. чтобы иметь возможность принять поступающие пара­
метры и получить сообщение через доступное только для чтения поле. Например:

public static void CarAboutToBlow(object sender, CarEventArgs е)


{ Console. Wri teLine (" {О} сообщает: (1)", sender, е. msg); )

Если получатель желает взаимодействовать с объектом. отправившим событие.


следует ВЫПОJПIИТь явное преобразование System.Object. Так. если нужно вьпmю­
чить радио. когда объект Car уже на полпути к своему создателю. можно предло­
жить обработчик событий. который будет выглядеть примерно так.

public static void CarIsAlrnostDoorned(object sender, CarEventArgs е)


{
11 Проото ДЛR гарантии з,цеса пере,ц IWЗОВОИ предлагаеТСII
11 проверка среды ВlШолнеиия:.
if (sender is Car)

Car с = (Car)sender;
c.CrankTunes(false);

Console. Wr i teLine ("Важное сообщение от {О}: (1)", sender, е. msg) ;

Исходный КОД. Проект PrimAndProperCarEvents размещен в подкаталоге. соответствующем главе 8.

Анонимные методы в С#
в завершение этой главы мы рассмотрим некоторые связанные с делегатами И
событиями возможности .NEТ2.0 через призму возможностеЙС# . Для начала об­
ратим внимание на то, что в случае, когда вызывающей стороне требуется осу­
ществлять прием поcrynающих событий. необходимо определить уникальный ме­
тод. отвечающий виду соответствующего делегата.

class SorneCaller

static void Main(string[] args)


[
SorneType t = new SorneType();
t.SorneEvent += new SorneDelegate(MyEventHandler);
}

11 Каж правило, вызывается: тольJtо аб'Ъ8ItТОМ SOIIIeDelegate.


public static void MyEventHandler()
[ ... }

Если немного подумать. то станет ясно. что такие методы. как MyEventHandler () .
редко бывают предназначены для вызова вне вызываемого делегата. А с тоЧI<и зре­
ния продуктивности слишком непривлекательно (хотя и не запрещено) вручную
определять специальные методы. которые вbIЗhIВаютCJl объектом делегата .
Глава В. ИнтерфeJ1:сыоб:ратноуо .8'ЫЗОJ3а, делеrаТhl и ,событи*" 371
чтобы разре.шить эту прvблему, 1'fШеръ позволяется ассоциировать делеl~ат :не­
посредcr:вeШI0 с блоком операторов программн.оI'о кода при реmc-rрации собыnm:.
Фа'рмаlIЫIO таком прогр~tй: код назы:ваетС:Я a.нoНILМН.ым мemoдo;>;l. в качеетве
ил.m:оетраЦИl-1 ба30вогос~нтаксиса рщ::смотрите сле.цующ:ий метод Ма i11 J). :в :КОТО­
ром : посыаемыe 'rИШIМ Car событц;н обрабатываютсв с помощью анонимных ~eTQ­
дов. а не спе:циаль:ных i1Мl;$ОВaшIЪrx проtpамм обработки собыТий.

Clasg Pl-ОgХillТ1
{
st1:\tiC' V"o.id t1a,in (str :ing 1] args)
I
ConS'ole . W.r i teLirl<o' ('" ." ~ /<т Анонимные методы ,~" ..... " \д") ;
Car сl=л~W Car(('SlugBug", Н)О, 10);

1/ l!erис'Iф8ЩIJI обработчИJcа. ёоб~ с: П~


1/ ш.ОIlЮlDDolX мe~;lXOВ.
cl_ АЬс,ut'ГФ'Вlоw' +: delegait'e {
COfis:oJ.e,. Writ,e.Li 11е ("ОХ! EД~1\! GЛИШКОМ быстро! '1),;
j;

~1.ЛЬьut"r.овl0 ,W .f: de:lega't e ('objHG't s ender, 'Са!"ЕУеnt}~:rчs е' J


COnS0'le. WriteLine ('''СОСiбще:ЮiLе, оТ Car ~ -{ О) 11 r е .=9) ;
J;
с1 . Ехрl oc,!ed +: delegcat.E: (,object serjde,r. CarEventArgs е)
C::OJlSole. Wr i t.eline ("ф"'i!гаШiНО.Е' .сс,обtцeJ'i1'fе O'i!' Сат : {О j" r е .msg) :
J;

3амечакке. После завершающеи Фиrwной скq(iкиаНQНшif.tого метода дол~а спедовать точка с


заrrЯТQЙ. !;спи nроnyc:rить ТО4КУ с запятой, будеr полу",ено сообщение 0'6 ошибке j(0МПИЛЯЦИИ:"

Обратите BJ-IИМани:е:на то. что тип Program уже не опреде-лnет :кoнкpeтlJЫe ета­
тические проrpаммы обработки событий. такие :Кан. например. CarAboutToBlo:w ()
а Са:tЕхрl0dэd О . Вм.еето Э'Гого здесь ук,а;JaНЫ безым.яннъte (те, анОtIюmые) мето­
ДЫ, Оripe.Деляемы:е "внутри.rтрочно" В тот момещ lШща вызывающая сторона 06"
рабатывает событие, ИCnQJIQЗУЯ СИНТаЕСИС +=.
БазовВ1Й СИНТCffiCЛС анОfЩМНого методасОЬтвстствует слt+(yЮщему представле-
~ пию в псеВДOJtоде.
"

c'la$s Som~t::qll~r
{
sta.t ic v"id M?lin( s tring [j аrg;з)
{
SomeТype t = пе", SфmеТуре () I
t. S'cmeE'I7E'tnt +=
del e,ga'te (веоб;иэа!1'е.llыwApг)?l8R!1щlle'.lfezojl,~i!I))
{ /'" ,операторы ~ I J;
..,...

372 Чаоть 11. Язык программироваНИR С#

в предыдущем варианте метода Main () сл~дует обратить внимание на то, что


при обработке первого событяя АЬоu tToBlow мы не определяем аргументы, пере­
даваемые делегатом.

cl.AboutToBlow += delegate {
Console. Wr i teLine ("Ох! Едем слишком Сыстро! ") ;
);

Строго говоря, получать входные аргументы, посылаемые конкретным собы­


тием. не обязательно. Но если вы хотите использовать поступающие аргумен­
ты, вы должны указать параметры, прототипы которых заданы типом делегата

(как это сделано во втором случае обработки событий Ab o utToBlow и Exploded).


Например:

cl.AboutToBlow += delegate(object sender, CarEventArqs е) {


Console. WriteLine ("Важное сообщенvtе о т Car: { О)", е .msg) :
};

Доступ к "внешним" переменным


Анонимные методы интересны в том отношении, что ШШ позволяют доступ к
локальным переменным определяющего их метода . Формально говоря, такие пе­
ременные являются "внешними переменными" анонимного метода. ,для примера
предположим. что наш метод Main () определяет локальную целую переменную­
счетчик с именем aboutToBlowCounter. В рамках анонимных методов, обрабаты­
вающих событие AboutToBlow, мы будем увеличивать этот счетчик на 1 и печатать
его значение в конце Main ().

static void Main(string(] args )


{

int aboutToBlowCounter = О;

11 Соs.цание К&IIIИИW.
Car cl = new Car ("SlugBug", 100, 10);
/ / Р.:l:'Ис~&ЦМJII обрабО'1'ЧИJCО:8 событий :8 виде анОНИNНNX кетодов.
c1.AboutToBlow += delegate
{
aboutToBloWCounter++;
Console.WriteLine(" Ox! Едем слvtШК ОМ быс тро!");
};

cl.AboutToBlow += delegate(string msg)


{
aboutToBloWCounter++;
С опsоlе.WritеLiпе("Важное сообщеНvtе о т Car: {О)", msg);
};

Console. Wri teLine ("Событvtе AboutToBlow вызывалось (О) раз (а) ."I
aboutToBlowCounter) ;
Console.ReadLine();
Глава В . .ИнrерфеЙсы оБР('!ТНОJi'J lIbJЗОВЗ, делегаты и СQбыт~я 373
в результате в:ыrlOлнения этогО обиовленн:оrо метода Mai n () завершающий оде­
ратор COI:!$ole.WriteLine () сооБIi])IТ вам о ТОМ, 'WГ{} соБЫТIil.е АЬоutТQЮQW генери­
.ровалось дважды.

ЗаМeitaние. АflOНИМНЫЙ метод не имгет .возможности получить доcrуп k пара метрам :r е f и ou t


определяющего метоЩl,

Групповое преобразование методов вС#


Еще ОДЩ)Й св-язанной с делега:I:д.МJI и' соБЫтя.я:ми возможностью В С# JШJUieТ­
ся Т,Ш На3ЫЩ1.емое Ф!JfI11l)вое преобразован.ue мemoд08. Эта возможаЩ:;rь nOЯВ(\­
ляет рег.щ-:три:ровать "просто" имяобработчив:а соБЫТl11Ii. Чтоб):'! ЦОЯСНИJЪ 3'1'0 ga
npимере, мы СRCiDарассмотрим тип 9i!ТtpleMath. уже рассматриJ:ЩВШИ;ЙС::-,я в этой.
главе Вl;.Iше. но доб:аВ}fМ' в Hero новое ·!;обьrrие. KDТQP01..ry будет .назначено И,'vl.Я
001ТlputationFi nished.
pu:blic c1ass $iтrфl~М"t'h
f
11 Э~есь .мt.1 ке УlPpУ:«Д/1еи с:еб" СОSДАНИ8Н
1/ npоиз.о,фЩ7'О !l.'ипа Systezn. EventArgs .
pU1Jlic delegate v~id Matfu.тe~sa:ge (stri Hg 1115g);
pub1ic еv€лt MathMes::tage Соmр'.:ltаti оА FiПi'S,hе-d;

piJ.b.liC i:ot Аdd(iлt JI., i r,t у)


j
Сощрu tаt i опFiпishеq (,IС.J1·0Ж~!tие БЫfюлн-ено. П) •
rеturл Х + УI
")
pubJ:ic iv.'t suЬtrасЦi:rlt &, i-nt У)
{
Comput.a tio-nF.i.ni she:cf ("Вычитание ВЬПIQлн ено" П),;
:ret:ar:n х - "У;
}

Если не исподьэоватъ синтакйЮ анонимных методов. то мы долЖНы оQраб.О1'аТЬ


сабьпие сь mрц·tаtiопСоrврlеtе 'П;Ш. Шlli пш{Э.зано нище,

cla55 Рrоgr'ёiirп

s.t.atic vbic1 Mai'f! (striL1g [] a..tgs)


(
'S imрl еМэttl щ ~ new Bi:ropleMatb. () ;
ПI,
Computa.ti'onFir!ished +=
roew Simpl eМath . Н'" thМ~sэаg:е (Computat iооFirriэЬе,dНаndlеr) ;
сопэоlе, .WritеL,i:nеj" 1[) -;/- 10 рав.НО {О)", lТ~.Add(10J 10.));
Console. R€i3:e.Line О;
}
зtа.tiс voio соmрut!ltioпFiпishеdEа.nd'1еr (striлg msg)
( C·@ nsal.e.W.riteLine (:m~g); I
374 Часть 11 . Язык программирования С#

Но можно зарегистрировать программу обработки событий для КОlШретного со­


бытия и так. как показано ниже (в остальном прогрaммньtй код изменений не пре­
терпевает).

m.ComputationFinished += ComputationFinishedНandler;

Обратите внимание на то, что мы не создаем непосредственно соответствуюIЦИЙ


тип делегата. а просто указываем метод. который соответствует ожидаемой сигна­
туре делегата (В данном случае это метод. не возвращающий ничего и получающий
один объект типа System.String). Ясно. что компилятор С# при этом должен обе­
спечить типовую безопасность. Если метод ComputationFinishedHandler () не по­
лучает System.String и не возвращает void. то вы получите сообщение об ошибке
компиляции.

Можно и явно конвертировать обработчик события в экзеМIDIЯР соответствую­


щего делегата. Это может оказаться полезным тогда. когда нужно получить соот­
ветствуюIЦИЙ делегат. использующий заранее определенный метод. Например:

// ,ВЕТ 2.0 Аопускае~ npeобразоааиие обрабО~ХQ. СQб~


// в СОQ~.е~С~.у.ащИе Aeneгa~ы.
SimpleMath.MathMessage rnmDelegate =
(SimpleMath.MathMessage)ComputationElnishedHandler;
Console.WriteLine(mmDelegate.Method) ;
Если выполнить этот программный код. то заключительный оператор Console.
WriteLine () напечатает сигнатуру ComputationFinishedHandler. как показано
на рис. 8.9.

Рис. 8.9. Можно извлечь делегат из соответствующего обработчика события

Исходный код. Проект AnonymousMethods размещен в подкаталоге, соответствующем главе 8.

Резюме
в этой главе был рассмотрен ряд подходов, позволяющих реализовать возмож­
ность двухстороннего взаимодействия объектов. Сначала было рассмотрено ис­
пользование интерфейсов обраmного 6Ь!30Щ которые обеспечивают возможность
для объекта А вызывать объект В с помощью общего интерфеЙса. Этот подход не
является специфическим ДЛЯ . NEТ. а может использоваться в любых языках и на
любых платформах. допускающих программирование на основе интерфейсов.

ь
Глава В. Интерфейсы обраТНQГQ ВЫ:ШВ(1, делеГЗП,1 и события 375
Затем бьmо pa:c:cmotpel-iО Ю1юч.евое слово (:::#dele.gate. которое используется
.Д1IЯ н~np,ямоtо построения классов. D,pomtBOДНЫX От Sys,t em.Mul'ti.c8.$tDeleg,a·te.
Как ВЫЯЦIWIDСЪ. делегат цредставлЯe:F собой об'bl:I-а:. :храняЩИй CIIМCO!{ методов. Д{J­
c~ для вы3ва• . При этом Вызовы. jldт'УТ быть ,c.'ИнJCpФ1-ПIblМИ (O.Im RЪЩОЛFШЮТСЯ
с nOМQЩ~ 1\reТoдa lrivoke ( ) ) и.лtt асищронщ.rми (они выttоЛllЯlOТCJlС ПОМОЩЬJQ ме­
ТОДОВ 'Веgi nIЛ.VОkе О R Елd111v(\)}~е О). АсищроннанприродаnmОБ депегата . NEТ
будет р'сссмотрев:а позже.
Ключе:вое слово С# еvепt ОрИ использовании сnшом делеl'ата позволяет ynро­
entтъ ароцесс отправки еообщеI-ШЙ событий "ВЫЗ'ы:&aIOщим объектам. к.ак пщraэы­
вает ГlЩерируe:м::IO::Й CII....kOA. мод~J1ьсоБыщйй .NEТ С:ВОД'ИТ СИ'l)'aциIO 1> CКPЪJТЫMBЫ­
завам 1'WIов.8узtero. Dеlеgаtе/S1stеm.МultiсаstDеlеgзtе . В ЭТОЙ СВn;\lИ кдючевое
СЛОВО С# event оказы:вается :необязателъным и просто ЗЕОНОМИТ' время при щборе
текста npограммы.

Новая Щ)ЗМОЖЕЮсть. UОflВивmasея .Q С# 2005 и получивтая название сцroнам'


КЫХ мerno1JOe. 1ёI0ЗВОЛЯет непосредствеНJiО аС~Dциирава;rь с событием. (неимещ)ван­
lfЫЙ) Q,ЛО!{Dпераroрав протрaммnога I(Qда.Анi:raимны:е M~TOДЫ MoryT Щ'вориро.!Щть
пара:метрl?l. посылаемые событи:ем, и получМ'ь дocтyiI к "nНeutНИ'М переменны:м"
опреде.тщющеtо :метода. fio ааверtuelПIеглавы был рассмотрен уnpоще~fЙ способ
регис1'РaдJ!IИ событий с помощью i?pyf1Ttй6041 npеобрUЗОвaJ-l.UЯ .меlл.одо@.
ГЛАВА 9
Специальные приемы
построения типов

В , этоЙ гда;ве вы расuщpите горизонты вашего понимания яаьш~ е#, рассмо·


тp~ ряд бол~е СЛОЖFЩ"D: [но весЬма полезных) синтаксачеСRИХ КОilСТРYJЩИ'Й.
Сначала мы с ВaJ4И выясним, кал использовать метод ,индексатора. Этот М~ЗМ
1'1 С# ЦО'ЭВQЛяет строить Щ)JIЬЗОЩl;телъские ТИПЫ. обеспечивающие доступ' ~ вну­
тренним ПQДТШIам на осдове еиптаксис,а массивов. НаучивIШIСh строить методы
индекеа:rора. В'Ь] за'J'еМУ~lffiе:Т'е. lШR перегружать ра3J1и"Шые операцl1И (-1'. -.<, > И
т.д,] и $НО и:т~ Н~ЯВНG со;щавать пЬлъзоозтельсние nOДIIporpaммы преобраЗ0Вания
типов (а таюке узвцете. аачем это может nOВа'ДОбитьсн).
ВО второй IЮл(;}вине гл;авы будет рассмотрен небольшой набор ключе)!Ь1Х слов
С#, которые поавощыот ре8.Щl3оваТli БесЬ.М.а интересные каНСТРУКЦИ11. ХОТЯ иеполъ-
3}'Ются не очень часто. Вы уанаете 07'ОМ. КЗЕ С ПОМощью lUIЮчевЫ;Х ело!) chec;ked
и uncnecked црограммно учитывать ус.лCiШIЯ перenолйения и потери значимости.
а также о том, как <;0i3цаетс,Я "пе6езonaсный" прt:lграммный JWнтеlЩТ. обесц:ечи­
вающий ВОЗМШЩJРСТЬ непосредствевнorо управления ссылоЧНЫМИ Т.ЩIaМИ В С#.
3ав~РUIaется:rnaва 06Gуждением роли директив препроцессора С#.

Создание лоnьзоватеnьских индексат, оров


Kalt пр6граммисТbI. мы преRрас:tю знаем, что С помощъю индеДСQВ МUЖlfо ПО~
лучитъ доступ 1, отдельным ЭJreМ6нтам.. содержanщмся в CтaндaPТilOM массИве.

11 OC'i.lUlJJeJQ(E\ иассива цe,JЩX в ..аче.ню1.


int[] mуInц ;= -{ 1О , 9, 100, 432, 98:741;
11 ИСПQШозоваВие операцИи П Д,JUI АОС\rYПа !( ·элеме.и~ак .
f'Qr (iл.t j ' = О; j -(, mУ l n ts.Lелg~h; j++)
С<:юsоlе.WтitеLiпе,("Ин,r:teI«С (О} = (1f '", j, rnylntS(j] );
Эт6Т пpu:гpа:ммньш КОД ни в КОем случае не претеццует на ваВ'ИЗfty. но язык С#
д~eт воЗможноC'JЪ строить пальзоватem.cкие массы и структуры, которые М!ЛjIТ
индeitСИРОИаться iюдоБFЮ стандартным масои:вам. Поэтомусо:всw не УД'И1щт~но.,
ЧТО метод, :который. обеспечивает тав:ой дос-ryп R элементам, называется uнд~кС'й:
mоро:ч.

П еред тем :КаЕ :ПРИСТУП~Ь к со:зданвю СООnJ.e'J'CтвующеЙ .KOHCТP~, мы pac~


СМОТРИМ один пример. ПредположЮ!I. что поддержка метода ИНДе&Са"J;'ора y~e ДО-
бавJmaa JiI ПОlIb3~ 1(<>ллеIЩИЮ Garage. frз-р8Ж'). уже рас.е-м:атривадшу:ю~n Ii!
ГЛ.3IВС 8. ГIpruш'allИзирyй-te СЛGJY10ПIйЙ пример ее ИCII.O.1IЬOOОО!ЩЯ.

11 Ииде1<са~pw' оС$есщ'е"D\JI~ ДQ~П • ззteН6JWам uодоБEii ...со _ _ .


p'иt>l ic. е las:s· P~Qg1f а.т
j
з.t а t:,i'c::' y-p;i..d Wail1 C$,t.!: ir!il.J'I Ja:rgs)
(

1/ Ilpе,JWQЗlС)JIIИН, ЧФО, ~8ge ииее"1': КI!'.J!Q,1( ив.цекоа'llар8 .


,Ga.ra!g:e са :o'LD,t = nе... Эа ;f',ag.e О i

/ /' _~ а r8.рш!! lUDlИИ G- I1ОМФIЦЪ"1D "Щ!МСU!Op.а.


c,arLot! fj 1 = new· сах j" Р',Е>е f'e.e ", 2',оч);
oa'l'L'(·~t!l] = ~e'W C-аJ::! "Сlllt\]i;e ;r ", 9Q):
Co.!:L0tl1!J =. aeW' ~'ar!"~jp~y,j l 30.1'.-

11 Ч'.J!eJIJ;f$ ч ol1'~6p~e Э'n.~И'.l."О8 ~' 110X0l11;Ь8) ror,ц"IЮв.'R'ора.


Ео'.!! I iв t i =0:; :1 < 3; Н·г)

\
\,;Q'nS01i:! ' W.ri te,Li 'le ("Haм~p ма:шины::' \О' r ", i} 1
СGП'$эlе' , Writ.,.,Line ("Наз:!tilние: IO}" , ~r!.(;',t (i j . l"€.tN<:<П1е,) ;
J:QnSO lt! • W:ti teI,.ln,e ( "Мal'l Cl!1!i1a.m и;ая C ~.'.Jp=''!''&.: (О Р ,
c ~r I,ot [ i) ,еuп sр<;;«,~J.:) ,-
СопsGilе.Wri.tеLl!)$'() :

CC1fi'S.ole , RecadLine O,J,

Кщ;t ВJЩите. m.fД~!:arropьr :ведУТ себк вт МНOl'QМ J1едqi}но rIO.m..i8IJBaxeJ:I1;CSOPt КOJI~


Дf:'-J{[WИ. ПQмержи:вающfiЙ шuерфейcЬi IEnшrr€'.r:аtОJ: и: J:ЕПlillLe.l:С :Ые. ОсНOJiНое раз­
Щf~liIе в 'I'OlIiI • ."Lт.o ~мe('.1'1) доступа R со.цер'tКИМоАi}." DtюреДС-ТВQМ 'tИ.IЮВ Inn'.::рфе-:Йсв
вь{ ~Щftе1:е работат~ I!; выу.треннеЙ RO'IfЛekЦИ.e.fl ав':luмабилеЙ . как r: .(]бычнъiм Mёl:C­
~и.-&ОМ".

8дес)Ь .В"'ЗllИ.К<t!:,:Т fJОПР(Ю; "RRксконфmyp'Ир!Ша:гъ kласс (1)tJ1И структуру). "I1'o-


бы обес.тrечитъ доддер.щriу с.о.О,'ГiJетсТВуЮ:щих ФующионаJlыtЪL",- возможностей?'
:»}'r...дe'KcaTOp» C4r цред~Та'ВmretСОf)QЙ; нecIOЭJIЪ.Iю".иCJШЖе:ННОе~ Cвo1o"icTBO. дпя co~цac
~Я ИНДeIfс.ато.рц в caмnп ПрОС'I'ОЙ форме rwиОJIЬ'З'J'fП'СЯ ~aJtQИС tЬiэ[]. ВОТ R"dВ
:t\f:Qже'f' :w.Р'ЛЯдеть пощщц.r.uд;m м(\дифИЮi.tJ;иR типа Ga rage...

11 1!обавдеииеlimil:{еа:сащорз .а <mpе,чеп~ Jt.1I,aC!.ca •


.fmbllc сlаs-Э GЕ!.t;:рч'" : IEn.11~rable /1 ~JU1 ~РЮО ЭJf~'Ni
(
., .
JJ и.CIIQm;,.з~'aJЩе Лr~уШ t ;ЦmI , _о. Car,
pt:'J..va te 1'...rFa'1!L:i. 8 1.:. ~aarray = f!€'W At".. yList о:·

11 ИиДeIt(:атар '!I!O$Зр:ащан !l.'Jф car, ООФ'lt'aе'I'е'118~


Ij '/iJIfCil:lOВOI.q ·ЩlДeJCCV.
FUblic 'C .ur t hig [ int fЮ$)
J.
11 Ar%8yList ~ икее.1,I! ~o&.'l'Op!
Глава 9, СпециалЫllilе nриеМbI по~трое.ния ТИПОJil 379
get { ~еtщ:n (C~!: )ср.rЛrrау [роз]; }
s.et '1саrАпау.Дdd(vаluе);}

ЕсJl:.li не обращать внимания на ключевое слово this. ТО объивлеНйе Jtндеш:а­


ТОра очень noхрже на ,объявление свойства :в С#. Но следУет подчеркнуть, ЧТо ин­
Дl',хсат0ры не обеспечивают иных фуМцИОНiШЬНЫХ возможнос:гей массива. щ>оме
ДО3МQЖНОСТИ испо.iIЬЭОВalШв операции индеNСИРОВaнlШ. Другими словами, пользо­
Щ1r.reлъ об~RТа не может npимeниrь npограММНЫй КОД. подобный следующему.

/1 Иcnоn:ьзуе!1'С!l laOKC'1'BO ~~ayList. СОЩ1t';l He!J! I


GСin'зоlе. Wr: i tebii.Тj~ (i'Ма,шин в наличии; ! О) ", ·o.a'rLot. Count) I

для паддержlЦi I:)ТОЙ фymщиовалъной 1юзмовnrест.и вы ДОЛЖНЫ добавить СlЮе


свойствоСоunt в тип G~rage и'i cooтвeTC'J'.Вeннo, делеraт.

p.tJblic tla~s Gal-age: IEnurner::able


{

1/ ЛОxa.пи.!lацИя/ ~e.nеtoиpоааане '8 дttЙСТJsИn ско.а.


public lf).t CP~Гlt { get ! re.turn. carArray.C-оuлt; j )

Итак. индексаторы'- это еще одна синтаксическая "КОJ:JфеТI~а". пос~оцьfo/ со­


ответствующих функциоI-iа.ilьflых возможностей можно ДОСТИЧЬ и с ПОМОЩЬ'lQ
~оБЫчных" метоДОВ. Наприм~р. есJШ бы ТШI Garage ие поддерживa.vr mщекmтор.
все равно МОЖН:О было бы ПОЗВОЛИТЬ ~внешнему миру~ 8з~действова1:'Ь с вну­
,.;рeнRИМ массивом, исполъзуя ДJШ этоro именованное СВОЙСТВР }ЩИ ТРl:lДИДИонные
МеТоды чтения и модифинaцnи данных (accessorjmutator). Но при ИСПОJIЬЗО8аН.Щ1
ИНдейсаторов UCJЛЫlо.ватe;:u,СЮlе типы 1tOJIJl~IЩИИ ду'-пnе согласуютсд со структурой
библиотек базовых ЮIассов .NEТ.

исхоwfый JЩД. Проект 5impleltldexer размещен в подкат:алоге, соотвеТС16УЮЩем rnа.ве 9.

Вариации индексатора для типа Garage


в своем текущем ВИДе тип Ga t ,age определяет 'инденсатор, который nоЗ'Вол.яет
вызъmающей стороне цдеНТ8.фИЦИРОватъ 1ш)'тренн:це элементы, иецольэуя число­
вое зиаЧeImе. НО ато не щшяется henpe1\ofeHI-IЬ1М тр~ован:ием Me'I'OДoa индексатора.
Предположим. 'lTO щбъектьr Car содержатс,f,\ в sуst. ет"Соllес1;.iоn;з.SР$сiаJizеd.
ListDictionary. а не в A-I'rаYList. Qоско'лыо/ типы Liэttl'1.сti onary J1Q;:JВОЛЯЮТ дО-­
ступ к содержroцимс:я типам с ПОМОЩЬЮ RJIЖ)чевых маркеров (таких- кащ например.
СТРOJш) . .мотно создаТЬ НОВЫЙ ИlfДексатор Garage, подоБНI:.IЙ до~заmюмуниже.

public C1."8S (;.airag~ : IEl'Jumera'ble


(
рr:l.vат.е ListDictie1nary cat:))ict.ionary = пеу; L,i stDict:io19ary ();
11 ЭтО!1' JЦlДeKc<u'0p ВОhраща.!1' СОО'1'ВЕ!ТС!ПIYJCIIP(Й !1'ИП Саж:
11 fIa оонове C'!l'po~O:!loro индеJl:С&.
риЫ1с: Сах: thi$~string namel
380 Часть 11 . Язык программирования С#

get return (Car)carDictionary[name];


set carDictionary[name] = value; }

plJblic int Length { get { rеturл carDictionary . Count ; } }

public IEnumerator GetEnumerator()


[ return carDicti on ary. GetEnumerator();

Вызывающая сторона теперь может взаимодействовать с машинами внутри


так, как показано ниже.

public class Program


(
static void Main(string[] аrgэ)
(
Console .Write Line( " ***** Забавы с индексаторами *****\п");
Garage carLot = new Garage();

// Добавление именованных кашин в :rараж.


carLot["FeeFee" ] = new Car( "FeeFee ", 200, О);
carLot [ " Cl unker "] = new Car("Cl unke r", 90, О);
саrLоt[ "ZiрруЛ ] new Car("Zippy", 30, О) ;
/ / Доступ к Zippy.
Car zippy = carLot [ "Zippy"];
Conso le. Wri t eLine (n[O} едет со скоростью {l} км/ч",
ziрру.РеtNаше, zippy.CurrSpeed);
Console .ReadLine() ;

Индексаторы могут быть и переrpуженными. Так , чтобы позволить вызывающей


стороне доступ к внутренним элементам посредством числового индекса ШlU строко­

вых значений, вы можете определить множество индексаторов ДЛЯ одного типа.

Исходный код. Проект Stringlndexer размещен в подкаталоге, соответствующем главе 9.

Внутреннее представление индексаторов типов


Мы рассмотрели примеры метода ИНдексатора в С#, и пришло время выяснить,
как представляются индексаторы в термш-rах CIL. ЕС.iIИ открыть числовой индекса­
тор типа Garage. то будет ВИДНО, что компилятор С# создает свойство Item. кото­
рое сводится к подходящей паре методов get/ set.
prop e rt y iлstалсе сlаsэ Simplelndexer.Car Item(int3 2)
(
.get instanceclass Simplelndexer. еа т Simplelndexer. Garage: : get Item (intЗ2)
.set instance void Simplelndexer.Garage: : set ltеm(iпtЗ2,
class Simplelndexer . Car)
/ / end о f property Garage; : 1 tem
Гhil'&Э 9. СпециалЬ.Ные tф~.мbl построения ТИПQВ 381
Методы get_ Неш() ~ ?et_ltem l ( ) будутре~ованы аналогично любому дру-
roмy ('Воiiству.NЕт. ЩiIIример:

metbod publi с hidebysig specia1name inзtаnс:~ {;las's , S·iп\рlеlдdехеr. Са!"


get_Item(int32 роз) cil !1)an'iI,ge·d
{
22 (O~16)
,maxstaC'J<; ~
.loo'a ls ir.it ([О] <Class Simplelndexer .Car С'$$1.$000:О'\
п_оаоо: 1darg.O
IL_ о ое}: l<ifld cla'ss [mscor:l.ib]
SуэtеПJ. Collecti стэ .AtrayLis t S:lmpleJ r;)(:iex~r. Ga.r·age: : сауАута:у
IL_QООб: 1ОаУч.l
IL_Q007: c.al1virt instаПG6 obje.G:t [ms:corli.b)
Syst·em. СаllесtiШ\.$. A:r.raiLi$t: : get _Нет (intз2)
:I:Ц_OOOt: castcla&5 SimpleIndexer.Car
IL 0011: stloc. О
1L 0012: br.s IL 0014
11 0014: ldl()c, О
11 001~: I'et
/ / end сЕ L'lJet'hpd 'GarSge: :get_Item

Заключитеnьны'е замечания об индексаторах


Чтобы ЦOJ;J.yЧИ'I'В наcrо.в:щуюэКзOТИRy. вы :можетfl создать шщеисатор. который
ИJ\o1еет МНQЖ~СТВО парамеТров. Предположим. Ч'I'О)' нас есть ПQJlЬЭоватеш.скаяцол­
леI<ЦИ.в:. которая храпит алеыенты в ~YMePНOM массИве. В этом случае вы можете
C08Д~Ъ :метОд индtкоатора, .пОJ:tазанныЙ ниже.

plJblic r::lавз SОIщ,~tодtаi .n~!


{
p.r i vat., in,t[, ) ~'2DiI~t,A:tra;y = l1ew int!10, lQJ;

p .u bli.c int 'this 1:ipt row, iпt с:оlunш]


{ /'" ПРОЧИ'Ji'ать или установит-J. значение '2D-смасси:ва ir /
}

в ЗaIOlючение следует заметить. что индеm:аторы могут оцредеЛЯТЬСIl и ДШi


типа интерфейса .NEТ. 'что обеспе~ает реадИ".:IyЮЩИМ }Щ'терфеЙ~ ' типам возмож­
ность его настро~и. ВОТ пример такого .интерфейса.

IдlbHc i!1terface JEstдb1is·M'UbObjec.ts


-{
11 Э'1'О~' ИИ'1'ерФdс: оnpeдu_Ц'~.хс;:а'l'ОР. :воs:вращ·nu;ий
11 c!S!pOJaC ка осно.. ЧИC;nО:ВОЖ'О _деаса •
зtri:ng this [int il1dex] f get., э:еt, !
}

Пожалуй, Qб ющексаторах С# уще сказано Достa:roчно. Перейдем к рассмотре­


нию еще одного noдхода. ирдощ.зуемого в некоторых (но не во всех) языRax про­
граммирования: .~ это перегруэха операций.
г

382 Часть 11. Язык программирования С#

Перегрузка операций
в С#. кю< и в любом другом языке программирования. есть свой ограниченный
набор лексем. используемых для Вl,шолнения базо:еых операций со встроенными
типами. Так. вы знаете. что операция + применима к двум целым числам и в ре­
зультате дает их сумму.

// Операция + с Ц811ШlИ числами.


int а 100;
int Ь 240;
int с а + Ь; 11 с теперь равно 340

Снова заметим, что это не новость, но вы, наверное, заметили и то, что одна
и та же операция + может применяться R большинству :естроенных типов данных
С#. Рассмотрите. например, следУЮЩИЙ фрагмент программного кода.

/I ОпераЦИR + со строками.
string з1 "He11o";
string э2 = " world!";
3tring з3 = 81 + 82; 11 з3 теперь равно "Hello world!"

По сути, операция + функционирует уникальным образом в зависимости от по­


ставляемых типов данных (в данном случае зто строки или целые числа). Когда
операция + применяется к ЧИСловЫМ типам, результатом является сумма операн­

дов. а когда операция + применлется R строковым типам. результатом будет кон­


катенация строк.

Язык С# обеспечивает возможность построения пользовательских классов и


структур, которые будут по-своему от:еечать на один и тот же набор базовых лек­
сем (таких. как операция +). При этом следует заметить, что можно "перегружать"
не все встроенные операции С#. В табл. 9.1 указаны возможности перегрузки ба­
зовых операций.

Таблица 9.1. Возможности перегрузки операций

Операции С# Возмож.ность перегрузки

+, -, !, -, ++, --, Эти унарные операции допускают перегрузку


true, false
+, -, *, j, %, &, 1, Эти бинарные операции допускают перегрузку
"". «, »
! =, <, >, <=, Операции сравнения допускают перегрузку, 8 С# требуется, чтобы пере-
>= грузка "родственных" операций (т.е. < и>, <= И >=, == И ! =) выполнялась
одновременно

1] Операция 1] не допускает перегрузку. Но, как было показано выше, анало­


гичные перегрузке возможности обеспечивает конструкция индексатора

() ОпераЦИR () не допускает перегрузку. Но, как будет показано ниже, анало­


гичные перегрузке возможности обеспечивают пользовательские методы
преобразования

+=, *=, 1=,


-=1 Onераторные сокращения с Гlрисваиванием сами по себе не допускают
&=, 1=, .1\=,
%=, пере грузку, однако ДЛR них перегруженная форма получается автоматически
«=,»= в резулы'теe перегрузки соответствующей бинарной операции

L
rлава ~. сп'ециальныe приемы nЩ:т.роен,ия типов 383

Перегрузка бинарных операций


-Чтобы ПРОИJ1ЛЮСТРИРОЕаТЪ Процесс перегрузки бинарных операций, paC~9"
трим слецУЮIЦYЮ простую структуру Pbint (точка).

/ / C~ оБJ.tчSu c!rpYJC~a ct.


publi с;; struct Poir1l:.
!
priv~t€ int g, у;
p1iblic Роi.лt
(int xPos f int уРо,э)
(
к = ХРО'Е;
у = '1'1>'05;
)

риЬНе оvец:idе str1ng 'ToString О


{
:tEj,ttrrn $tring.Forma:t(" [JOJ. (l~]", this.:&, th.Lз.у);

Можно. скажем, рассмотреть сдащение типов Р {) i Jl t. МОЩЕ О также вычесть


ОДИН тип Polr:i.'t из дpyroTo., Например. вЬ1 можете 3aдI/JCaть , следующий npoгpaм­
МНЫЙКОД.

11 СпoseВМ8 и В~lUIИе .ц:аух !!'o~eJt..


э't,а,tiс VФi,d Маin(striпgП arqJs)
{
.ccms'Ole .,Wri tetif1e ( (, *** Забавы , с lIерSгр.уженн:ыми олерациями ,* '"\n") ; *
/I СОзда.вие 'AJl7Х IRоче1С.
Point ptbne fiew J?!)int (l(')Q, lЩ}j;
=
Fоiл );. pt ТW,Q
new Point (IJ а, 4 О) ;
=
СОЛS!i:Йе.writеL::i::nе("рtОnе = (О}", ptOne);
'Co:nsole.WriteL.irre("ptTw9 = {О}", pt.Two,J;

// C!Jо.~еwоч.Jt 8 OДIQ" боnJoJllYЮ !IIQЧJqr?


СОПSCilе.'WrjtеLinеIЛрtоnе f p 'tTw,o: {О} ", ptOne + ptTwol/

/I в.rzи..r4SИ& ОAJ,lОЙ то'ПИ "$ .ц:p~ дa.e~ меиь.."., 'l'очку?


Console.Wl-itеLin,е ("рt.ол~ - prtTwo: {О} ", pt.One - pt'I'wo);
Console. &ea.dLine О i

Чтобы nО~ВОЛИ'I'Ь ПО,l1ЫЮвательскому ТИПУ по- своеМу отвечать на встр6еяныe


операцци. в С# преДJlагается ключевое слово ope,rator. которо,е можно lIСПОЛЪ­
:;JOвзтъ только со ~mQ.muuеСJШ.Мl1 метQДRМИ. llерегруженной бинарноЙ операции
(тЮ\ой. Ra.R -+ RiIFJ -) нз, ЩСОД подаются два аргумента, которые имеют тип aI1peAe-
лтощего класса (Е ДaJШQМ примерt: зт'!) Point). кав шшааывет следующий цро­
гp~KOД.

/I Более 'ИН'I'еЛilеЮl'~'алъный' тип P-Dint.


putIit struct Po i~t
1
r
384 Часть 11. Язык программироваНИR С#

1/ переropyжеииаи операции +
public static Point operator + (Point pl, Point р2)
( return new Po int(pl.x t р2.х, рl.у + р2.у);

11 переrop}'Jltеннаи операции
-
p~blic static Point operator - (Point p l , Poi nt р2)
( rеturп new Point(pl.x - р2.х, рl.у - р2.у); f

По логике операция + должна просто возвратить совершенно новый объект


Point, полученный в реэультате суммирования соответствующих полей входных
параметров Point. Поэтому, когда вы пишете ptl + pt 2, можете представлять
себе следующий скрытый вызов статического метода операции +.
11 рЗ = Point. операции+ (pl, р2)
рЗ=рl+р2;

Точно так же

11
р3
рЗ
= pl - р2;
pl -

= Роiпt.операции-
р2 отображается в следующее.

(pl, р2)
I
Операции += И -+
Если вы изучаете С#. уже имея опыт использования С++ , то можете обратить
внимание на отсутствие возможности перегрузки операторных сокращений, вклю­
чающих операцию присваивания (+=, -= И т.д.). Не волнуйтесь, в С# операторные
сокращения с присваиванием моделируются автоматически, если тип предполага·

ет перегрузRy соответствующей бинарной операции. Поэтому, поскольку структура


Point уже использует переГРУЗI~ операций + и -, вы можете записать следующее .
11 Переrpузха бииарИlolX операций автоматически влечет nереropузху
11 операторlWX сокращений с ПРИCllаиваии8М.
static void Main(string[] args)
(
11 Ав'l'оиа'1'КЧеСХaJr nереropузха +=
Point ptThree = new POint(90, 5);
Console. Wr i te Line ("pt Three = (О}", ptThree);
Conso le.WriteLine("ptThree += ptTwo: ( 01", ptThree += ptTwo);

1/ Автоиаnlчесхаи переropузх& -=
Point ptFour = new Point(O, 500);
Console.WriteLine("ptFour = 101", ptFour);
Console.WriteLine("ptFour -= ptThree: (О}", ptFour -= ptThree);

Перегрузка унарных операций


в С# также позволнется перегрузка унарных операций, таких как, например,
++ и --о При перегрузке унарной операции вы тоже должны с помощью ключево­
го слова operator определить статичес1\ИЙ метод. но в данном случае передается
только один параметр, который должен иметь тип. соответствующий определяю-
Глава 9. СпеЦlWlьн.ы.е приемы nOCTPQE!H~'!f ТИПОв 385
щем:у классу ИJUf струюуре. Например. если доБЗ1!И'IЪ в Роiпt сле.цvющие перетру­
женные Щlер-aдI>IИ

public struct Point


I
; ..
11 ДоtSa.nеюса 1 »е J;lОC'RYttшnaqy Point.
pablic: st..зt1с fоiЦ1: opera.tor: +т (Роiлt pl)
! IetuIIl new PoiC\t(pl.xt1, p 'l .ytl); I
IJ ~. 1 0\11 .aoCi~eI'O Point;.
рuЬЦс stat:iic 1'011'1t opera'tor -- ('Point pl'
I Ietarl1 newPoiht (рl . x-l, pl. у-l); }

то вы получите возможность ~a'])ъ или уменьшать на единицу '3Начеmш Х п


у объекта Ро! nt. КaR понаэанр J:lWКe.

st.atic VD'1 d Мa~Ln (.s tring[] асхчэ)

11 ПрКиеиеихlll yвapJl10lX операцкil ++ к -- IC foint.


Cons:ole.WriteLine ("++p tFive = {О}" I ++ptFive);
Сопsоlе' .WriteLiо,е(", ---рtFivе = {О)", --рtFivе);

Перегрузка операций п·роверки


на тождественность

вы можете помнить из материала главы 3, что System.Object.Equal.s () мож­


но переопредeJ1ИТ.b. чтобы сравнение ':tИI]ов ВhII1D1ПШ:IIосъ па: основе значений (а не
ССЫЛОК), ~сли: выереопредemrre Eql~als (') (и свлзашIhIЙ с Ечиаl,5 (j метод :Sуэtеm.
Obj-есt,.GеtfiаshСоdе ()]. то будет очень просто ;щдать перerрузку ацерauяi1 провер­
ки на тождествеlfilость [= = и ! =)_ для lIЛЛlОСТРации мы рассмотрим об110вледнЬ1Й
тип PoiDt.
11 Ькц 'IQIЦPS~" Point sа,дае'Ж', !2!аа.е D~pe:rp,.!txy ОПе.р.!ЩКЙ ==; и !=.
риЬ] ic struct Point
{

pllblic o,V"srride b ,o ol Еquаlз ,(object а)


f
i f {о is Pointi
!
Н( HPQin~)o).X == this.X&&
( (Poir;'t) о) • у == tfLiB.J! )
retuzn tr,u ei

return' ~аlзе;
)
386 Часть 11. Язык nрограммирования С#

public override int GetHashCode{)


( return this.ToString() .GetHashCode();

/ / Здесь поs_оn.е'1'С. перегрУSItа. операций = и ! =.


public static bool operator ==(Point рl, Point р2)
( return pl.Equals(p2); }
public static bool operator !=(Point рl, Point р2)
[ return !pl.Equals(p2); }

Обратите внимание на то. что данная реализация операций == и ! = просто вы­


зывает переопределeнный метод Equals () , который и выполняет основную работу.
С учетом этого вы можете теперь использовать свой класс Point так.

/ / Испо.m.sовави. пере:rpyzеИНJoJX операций проверJCИ на '1'о*деС'1'l!lеНRОС'1'Ь.


static void Main{string[] args)
(

Console.WriteLine{"ptOne == ptTwo l О}", ptOn. = ptТvo) ;


Console. Wri teLine ("ptOne l = ptTwo (О) ", ptOne != ptТvo) ;

как видите. здесь два объеl(та сравниваются с помощью операций = = и ! =, а


не с помоlЦЫO "менее ecтecтвeHHOГO~ вызова Object.Equals (). При исподьзовании
перегрузки операций проверки на тождественность для класса имейте в виду, ЧТО
в С# требуется. чтобы при переопределении оuерации = = обязательно переопреде­
лилась и операция ! = (если вы забудете это сделать. I<ОМnИЛЯТОР вам напомнит).

Перегрузка операций сравнения


Из материала главы 7 вы узнали о том. как реализовать интерфейс IComparable.
чтобы иметь возможность сравнения подобных объектов. В дополнение к этому
для того же класса вы можете использовать перегрузку операций сравнения «. >.
<= И >=). Подобно операциям проверки на тождественность. в С# требуется. чтобы
при перerрузке < выполнялась и перегрузка >. это же касается и операций <= и >=.
Если тип Point использует перегрузку операций сравнения. ПОЛЬЗователь объекта
получает возможцость сравнивать объекты Point так. как DОRaЗано ниже.

I/ Исп~sо.а.иие пере:rpyzеИНJoJX опера.1I;ИЙ < и >.


static void Main(string[) argsJ
{

Console.WriteLine(hptOne < ptTwo {о }", ptOne < ptТvo) ;


Console.WriteLine("ptone > ptTwo 1О}", ptODe > ptтwo) ;

в предположении о том. что интерфейс IComparable реализован. перегрузка


операций сравнения оказывается тривиальной. Вот как может выглядеть обнов­
ленное определение класса.

1/ 'Мо_о сравнив&'1'Ь обtи~ Point с поиощь.l операций сра_неНИJl.


public struct Point : ICompa.rable
Глава 9. tпецltалы188 лркеМtij построеНlltlТМПО8 387

pUblic int Coropare~o(pbject obj)


j
if (.оЬ] ispoint)
t
Роiлt l' = (Point) o!:rJ;
i:f '(this.x > р.х Н tЪis.у > р.у)
ret; tl1:n 1;
if {thi.s.x < р.х &Ь tt'lis.y < р.у)
re.t\.Irn -1:
еlЗeJ
rеturп О.;
}
else
throw new Arg'umen tЕх.серtiОЛ () ;

puЬЦc static рос! operatGr < (Poi.nt pl, Foint р2)


{ :r-et~ (рl. Cbmpa.te~a (р2) <: О J i I

public static bool ope:rator > (Point рl, Poiht p~)


{ re.tЫП1 (р1. C;ompareTo·(p2) ;. О); I

public static bool Gip eratoI <~ (Point p ;l , Point 1"2)


! ret'.1rn fpl.CompareTo('p2.! <= О); )
plibllc stati,c роп! operat'o r >~ (· Рфlпt. p.l, Po.int р21
j return (рl.Сотр.ахе,'1'о{р2) >=о);}

Внутреннее представление
перегруженнbIX операций
ПодОбно любому элемеНТу прох:раммы СИ. переуоружевные операции представ­
.лйlOТёл Cnециа.льными элемен-mми CИИ!raНсиса CIL. Откройте. вanрш.rep, 1rollOIOнo­
ВО!IНЫЙ блок ОvеrlGаdе:dОрз. ехе с помощью ildasm . еХе. ка1( показано на рис. 9.1.
переI'pyЖeННЪfе операЦИИ внутри блоl".а пре;цстaвJI8ЮТСЯ с:крьrrыми методами (Э1'О,
ilaпpНМ6р. ор_ Add'itiQn О. Ьр _ Subt:;act:ion (), ор_ Equality О и т..I1.).
Теперь. если рас.смотрмь CIL--tnlС:ТрYJЩИИ длg метода ор ~ Addition. 'Го Bl)I ()б­
ИВр}!ЖИ'l'e. ЧТО csc. ехе добавл.вет в метод ltiIюч:евое С'ЛОВО specialname .
. ntethod publi~ bid,ebysig epecialname 1/tatia
'valuetype Оvеrl0аdеdОрs.РGiлt
ар Addition (valuetype ОvеrlоаdеdsОрэ ..PDint р1 ,
v$.luetype Ov!'!r loadedOps. Point р2) с11 IТIaлаged

}
388 Часть 11. Язык программирования С#

" :,' I~-,~ I~ ~ г:

• .c!мs value PO.bIiC seqJentlal an<lseded befQIeki


• ~xtends [mscorllb]System .VoIueType
• Implements [msco~lb]Syst.m.!Compor.bIe
ух: prlv4te illtЗ2
" У : priv.te intЗ2
• .ctor : vоid(tnt32,intЗ2)
• Compar"To : IпtЗ2(оЬjесt)
• Equ.ls: bool(object)
• GetHoshCode : i1I:З20
• Т aStrlng : string()
EJ op_AddiJon I valuetypa <М!:rЮ.dedОрs.РOil-.t(v.......
EI opJ>гcremenI: : ya.\Jei:ype OverloooJed()ps.IPmlt(.aI
" op_EQuallty: bool(val!Jetype Oveoo...Jed()ps.IPQI1Ir,\
" opJ;"eaterТhan : booi(va ... type Ov ..rloocledol>S
Et op_Gr.oterThanOrE(JJai: boo~vak!etype Overloa<1e:
EJ opJnc:rement: valuetype O",,~loodedOps.Poir'lt(v.",
EJ ор_!nequ'*у : bool(valuetype Ov"rlo&:Ied()ps.IPo'n
i'.I op_LessThon : bool(valuetype Ove"о.,dedl:)р< . РоirЧ)i!11
ШI DP_lessTh_nOrEqual: bool(valuetype O.....,~o..d.d(JJ"" 11
EJ DP_5ubtr.ctiOn: valuetype Оvеrlо.dеdops . РOJПl:(v<!iWll

Рис. 9.1. В терминах CIL перегруженные операции отображаются в скрытые методы

Итак. шобая операция. допускающая перегруэку. сводится в терминах CIL к


специальному именованному методу. В табл. 9.2 раскрывается соответствие имен
типиЧJiЪД операций С# и методов ClL.

Таблица 9.2. Соответствие имен операций С# и методов CIL


Внутренняя операция С# Представление CIL
ор Decrement ()
++ ор lncrement ()
+ ор Addition ()
ор _ Subtract ion ()

'" ор Multiply()
I ор Division ()
ор Equality()
> ор GreaterThan ()
< ор LеssТhал ()
!= ор Iлеquаlitу()

>= ор GreaterThanOrEqual ()
<= ор LessThanOrEqual ()
ор SuЬtrасtiоnАsэigпmепt()

+= ор АdditiопАsэignmеnt (J
Глава 9, СпеЦИaJIЬJIIЫ& 11риеМbI rтОСТРQВfIИ~ ТИПОВ 389

Использование перегруженных
операций в ЯЗblках, не nОАЦерж:ивающих
перегрузку опер'аций
ПоНИМание 1"01'0, как перe:rpужен:ные щ:rеpaщm предс1.'авлены в программном
ходе CIL инте.ресно не TOJIЬRO с академи:чес.коЙ Т~ОЧКИ зрении. Чтобы осоа.вать
практИчесrcyю UOJIЪЗY этих зиaщm. вспомните о ТОМ,, что ВОЗМОЖНОСТЬ перегрузКй
операций I10,!Щерж:ивается ' ~ всеми язьшами, предназначенными )VJ,Я .NEI'. RaIc,
нanpим:ер. добавить па.ру типов Poi nt в програм:му, соадавнуюna я.зblJUt. не под­
держивающем перегрузку операщШ?
Одним из Подходов 'Являетсн создание "нормальных" oт1cpыты членов. Ю>Торые
будут peIiIaТЬ ту же задачу. что и пере1''Руженнъre опера.1:!;йи. Например. :можно до­
бавИТ:Ь в point методы Add (~ и Subt ract () • которые будУТ ВЫПDJIНЯТЪрабо'I'y. со­
ответствующую операциям +' :и -.
11 ЭХCnО8J!ЩИJ1, с8ll&Вwиtm пtiреrp~ оп.раЦиЙ
11 с I1OJIoЩWO проC'1'lolX .8Иоа-фУJUЩИЙ.
poolic stп)'с't PQint
{
.i ....

11 np.дс'3!aвn".... o~ep~ + с I1D11OЩЫ) Add i )


p'ublic s ,tatic P~oin,t Add (Роiл,'t, pl, PoiIJ1: р2)
( r ,e turn рl -f р2; J

11 ~дс,авJlевие операции - с пока"'" Subtract()


puЬЦc static PGint Subtract (Paint pl, Point р2)
{ raturn р1 - р2; j
J
с '1'анимц модификациями ·nm PQi n t способен дем.ОllС,трировать соответФ'ВУ­
.IOЩИe ФУЩЩ,Щ}нarIЪные ППЭМOO1iliос'fii. 11СПОЛЪ3УЯ любые ПОДХОДЫ, преЩl.8ГRемые в
рамках ДЩЩОГО Я3ЬПШ. Палъзователи С# :могут применять операции 1- и - ЩlП же
вызывать Adq' ( ) /:.subtratt () .
/1 Иcпom.-Sоаа.Ние onep"~ '*' иmr Add () .
СDnЗоlе. W.titeLinE: (npt'On'~ + pt"1wo: \ О) "< ptOne + pt Two) ;
СОПВ0Iе. Wr iteLine ("Poin't. Add (ptOn.e; р1:Тvю): {О) ",
Poi.n,t .Ad.d (ptOne, pJ:;TWo:) ;

11 Иcnо~sоа&1Щ8 Qпераqик - иnи SuЬtract. О .


Console. 'W rlteLine( "ptOne - ptTW.Q : {О} ", ptOr1S' - pt:.Two) :
ConsD,le.WriteLine("Poicnt.Subtract('p j:.ona, pt'l'wo): {О) ",
Роint.SuЬtц 'с.t ('p tOne., ptTwo'»);

Я~bl:КИ. ~ 'Которых 'не допускается перегруэ'Кs. оrlераций, II![oгyт иопощ.зовать


'ТОЛЬКО OTIqJprJ'ble ct~атичесюre методы. В качестве альтернативы МОЖНQ I:Jредло­
)IЩТЬ не[[оср~ствеННЫй ВЫЗОВ cnециалыIыx именованных MeтQДOB, (юзда:ваемых
lЮМIПlДятором:.

Рассмотрим исходный вариант ИЗ1:;lНа rtрограммировaнш:t vв .NEт. Цри построе­


нии КОНСЩIЫЩГО ПРИЛOЖeНИJl VБ .NEТ, ссьшаюЩСЯ'ОCtl на тип РQiлt, вЫ можете до'
бaвJшть wц{ 1;IЫЧИТaТЬ тшIы Роiпt. ИСПОJIЬЗYЯ' ·СпециaJ1ЬНЫе СIL'имена-, папример:

1
--
390 Часть 11. Язык программирования С#

, Предпоnarа8'1'СR, Ч'1'О ДaJUl08 DpИnожение VВ . НЕТ


, _ ••'1' дос'1'УП К '1'ИD)' Point.
Module OverLoadedOpClient
Sub Main ()
Dim pl Аэ Point
рl.х=200
рl.у = 9

Dim р2 As Point
р2.х = 9
р2.у = 9ВЗ

, Не '1'&К красиво, как BWSOB AddPoints () ,


, но 8&'1'0 рабо'1'•• '1'.
Dim bigPoint = Point . op_Addition(pl, р2)
Соnsоlе.WritеLinе("Большая точка (О}", bigPoint)
End Sub
End Module
как видите, языки программирования .NEТ, не предусматривающие пере груз­
ку операций, способны непосредственно вызывать внутренние методы CIL, как
Мобычные
е
методы. Такое решение нельзя назвать слишком "изящным~. но оно ра­
ботает.

Замечание. Текущая версия VB . NEТ (Visual Basic .NEТ 2005) перегрузку операций помержива­
ет. Однако для других (многочисленных) управЛRемых языков, не поддерживающих перегруэку
операций. знание ·специапьных имен" соответствующих методов CIL может оказаться очень
полезным.

Заключительные замечания
о перегрузке операций
Вы могли убедиться в том. что С# обеспечивает возможность построения типов,
по-своему отвечающих на встроенные всем известные операции. Перед тем как пе­
рейти к непосредственной модификации классов для поддержки такого поведения.
вы должны убедиться в том, что для операций. которым вы хотите назначить пере­
грузку. такая перегрузка имеет смысл.

Предположим, например. что вы хотите использовать перегрузку операции


умножения для класса Engine (мотор). Что тогда должно означать умножение двух
объектов Engine? Не понятно. ПерегрУЗRа операций, в общем, оказывается полез­
нои только тогда, когда строятся полезные типы. Строки, точки. прямоугольники И
шестиугольники являются хороlIIИМИ объектами для перегруэки операций. А ЛЮДИ,
менеджеры. автомобили, наушники и бейсбольные кепки - нет. Если перегружен­
ная операция делает более трудным понимание фующиональных: возможностей
типа noльзователем. то лучше перегрузку не ИСПОJIbЭовать. Исполъзуйте указанную
возможность с умом.

Исходный код. Проект OverloadedOps размещен в подкатапоге, соответствующем главе 9.

1,
1

L
1 Глз&а 9. Специальные прием~ rtЩТРОВtlия типов 391

Пользовательские преобразоваН'ИR типов


Рассмотрим T~. БЛИЗIro связанную с перегруэкой: операций: это :nфiIь~ователь­
ские npави.да дреобразовaншt ,типов. В начал~ нашеrQ рассмотреJШЯ мы кратно
обсудим лвдые .и нея:вные upеобрааован:И'Я 'lЙСЛОIЦ!;IX данных и соответствующих
тиnoвRЛaССа.

Преобразования чисел
в случае ВстроЩШЫХЧИСЛ:ОВ"ЫХ ТИПОВ (:sbyte.. int. f10at и 11Д.) явное npeoбразо-
8(l1iUe требуетСЯ тоща. Н.огда вы nы:таетесь.сохранить большее значение в :меньшем

ко~ЙJfере.. ПОСКQJlЬRY при ЭТОМ .ьюжет ПРОИСХОДИТЪ потеря данных, ПQ сути. ·это
способ сказать Щ>мnилятgру приМерно следУЮЩее: аНе бесПOJЮЙCJ:I. 11 знаю, "что де­
лаю!~ С дpyrой СТQРQИЫ, неявное npеобраэован.tю происходит автомат~еciШ, :ког­
да вы .пытаетесь раэместить в тИпе-адресате тип :меныDиx pщiМеров, в результате
чеГо noтери да.:нных 1ю праисходит.

st'a;'tic vDid Мain (!


{,
int а = 123;
lойg Ь = а; 11 llelQК08 Ж!Р8~ра!JОВa.rora
int. alonq
_
int с = (inf:} ь, 1I ЯIuIое ~образо.ааиа шs long. в int

Преобрsзова,кия типов класса


Raк паказа:но в rnаве 4. пшЫ класса могут бъnтъ СВЯ3~ классическим отно­
tщJl'IИеМ"вsследования (OTR.OII1eIfile ~iS-аk). Б этом е;пучае в СИ ДРоцесс преобразо­
вания позволяет cдrmгaться вверх или :ВЮl3 по ш:рархии щщссов. Например, Про­
И3.ВQдJIЬ1Й Ш1аcD всегда можно нemnrо преобра.эоватъ в ба.зощ.Ш тип.. OДl:lalro если
вь,r <!CiXDтите сохранить б.а80ВЫЙ тип K1facCa в произво,1J;НОИ lIеремeюroй. придется
ВЪШOJIНЮЪ явное npeoбра.аование.

11 ;цва са••аи........ !rJШ& JrЛао<:&..


.сlаsз J!ase ( )
'class .Der:i-iТеd ; Ва:зе {!

сlаsз 1?rogram
!
static void Main ()
[
11 B8JDllloe пре;обрааоа&.RU8 Jit!I проиQОрс)roo • б..,оlJlolil_
Вазе wз-Ваs€:Туре:
тyBa~eType = 11е}; De-rived О;

11 .ц.п. соJф4Reв:иJI CSаsоаой QCIo1n1a( • произ.оДlfO!f !!!ИD_


11 СШ8ДУ8'1' 1D1П0JI~ 113&0& ~об~О.аи.ие..
De ti ved .roy.De r i v~dType =(Пе r i ved) туваве.Туре:;
392 Часть 11. Язык программирования С#

Здесь явное преобразование работает благодаря тому, что классы В а sе и


Derived связаны классическим отношением наследования. Но что делать в том
случае, когда вы хотите связать преобразованием два типа класса, принадлежа­
щие разным ueрарxuям.? Если классы не связаны классическим наследованием,
явное преобразование помочь ничем не сможет.
В соответствующем I<Люче рассмотрим типы, характеризуемые значениями.
Предположим, что у нас есть две .NET-СтрУКТУРbl с именами Square (квадрат) и
Rectangle (прямоугольник). Поскольку структуры не могут использовать класси­
ческое наследование, нет и естественного способа взаимного преобразования этих
явно связанных типов (в предположении о том, что такое преобразование имеет
смысл).
Конечно. проблему можно решить с помощью создания в этих в структурах вспо­
могательных методов (например, Rectangle. ToSquare (), но в С# можно создавать
пользовательские подпрограммы преобразования, позволяющие соответствующим
типам по-своему отвечать на операцию (). Так. при праВИЛЬНQЙ конфигурации
типа Square вы получите возможность использовать следУЮЩИЙ синтаксис для
явного преобразования этих типов структуры.

1/ Пре.ращение прякоyrо.пьюпtа в ICBaдpam.


Rectangle rect;
rect. Width = 3;
rect.Height = 10;
Square sq = (Square) rect;

Создание пользовательских
подпрограмм преобразования
в С# есть два ключевых слова. explicit и implicit, предназначенные
для управления тем. как типы должны отвечать на попытки преобразования.
Предположим. что у нас есть следующие определения структур.

public struct Rectangle


{
1/ O'1'xpJIr.DI для: ПРОСТО'l'Ы,
/I НО НИЧТО не мешает ИНlCапсуnиpова'I'Ъ их в виде свойств.
public int width, Heigbt;
public void Draw ()
{ Сопsоlе.WritеLiпе("Отображение прямоугольника.") ;
public override string ToString ()
{
return string.Format(" [Ширина = {О}; Высота {1}]",
Width, Height);

public struct Square


(
public int Length;

L
['пава 9. специaJIьfiыe приемы fIIDстроеНИII ТИПОВ 39Э

риЬНс void Draw ()


I CCQs:ole. 111: l.teLiCle (":О"ТDбражение R5аДрата."): \.
puыlc c оvеп:idе string 'rQS tri n.g ()
! ret \Jrrl gtr i tlg. F orma t ( " [CTopO HВI; { О } ]", Le:ngth.);

/I R.~tan91e (пpsкоyziom.вих) ко_о _-О преОCSpUО8&n'


11 а Squ.are (1I:Baдpa'.1').
рuыic stаЦt: expltci ,t Qperator Square (~ectaIlgle r)
{
Square 5;
э. Length = r. Width;
rеt1JLП 5;
}
}

Обратите внимание на то. что на этот раз ДЛЯ типа F.ec t angle опредеЛ1J;eт<:Jl
Операция явного преобразования. как и при переГРУ3I~е. встрое1ЩЫХ оп~раций. в
С# для ш)Дпр6грамм прео5рааовани.я иопользуется юnaчевае CJ.IOIЮ opera;tor (в 00-
вокynности С .нmОч:евым словом ехрl ici t .или irnpli c::it) :и этц РОДПРЩР8,~ ДОЛЖ­
ны определяТЬСЯ. КВR статические. Входным шtраметром nВляется объe:кr. у.отоpый
вы хотите npeoбрззовать. а возврatЦaемо.е 3Jiaчение - это об'1:Je1a: В который поC1J"
пающий объект ПРевращается.

public sti;ltic explicit ореrаt .Фr Square (1~'ectangle т)


( ... )
Здесь предполагается.. что квадрат (:которыМ .является геометрической фигурей с
Равными СТОРОНI'lldИ) МОЖНО получить I:ta ОСfюве nтpюn.т прямoy:rо.n:ьникa:. Поэтому
вы мomете превратить Rectangle (nPЯМОУТФIbtnШ:). в Square (квадрат).ак.

static void Ma:in (string r] arg.S )


[
Coi150 1e. Wr i tе 1.1ш,;, t " .~ * "*.~ за.C'Jiавы с пр е .образованИями *;; ~ ... \n") ;
11 СОЗдЗ,.lQQ! nP5QlOvгощ. __ s. 10 х 5.
Re c tangle' rec.ti
rect. Widtn = 1'0;
!e..c-t • Beig1'1t = 5;
Сол..s.olе.Wri t€! Linе (".t-есt = {О}", ye ct \;
/1 npеобраЗOllв-ние nPRl(О)l'~$ИJt. в x.a.цpa:ml·0 х 10 .
Square sq = (S·q )l·a re) rect;
Cb r-lsc:i'le. W'iJ:'i tеL iле (" э'! = {V)", s.q);
CO!'!s,b le . F.eac;lLi;ne () ;

Щшериое. от превращени.s npямоytОJI'ЪНИRОВ в квадраты в ра.lЮiЗХ ОДНОГО кон­


текста не слишком .мноro пoпJi:.3ы1~ НО llP_еДI1оложим. что У пас есть фyнIЩИJl. J«)ТO­
рап преДПЬлагает -использование типов SЧ,lаге.

/1 ЭтО'1' кeTO~ ~буе'1' t(СПОnь.!lOJJаинli '1!'Щ1. Sq1Ыre.

,
private static \fold D r-аNSquаrе (Sq.uaf8 sч)

вч. Draw () :
--
394 Часть 11. Язык программирования С#

Используя нашу операцию явного преобразования, мы можем передавать этой


функции типы Square.
static void Main(string(} args)
(

11 Преобразование
Rectanq18 в Square jqmr ВI:IЗО• • иешода.
DrawSquare«Square)rect);

Варианты явного преобразования ДЛИ типа Square


Теперь вы можете явно превращать прямоyroльники в квадраты. но рассмотрим
еще несколько вариантов явного преобразования. Поскольку у квадрата стороны
равны, можно явно преобразовать sуstеm.lпtЗ2 в Square (длина стороны квадра­
та будет равна значению поступающего целого числа). Аналогично можно изме­
нить определение Square, если требуется обеспечить преобразование из Square в
Sуstеm.lпtЗ2. Вот логика соответствующего вызова.

static void Main(string[] args)


{

11 Преобразование Sу_teш. IntЭ2 в Square.


Square sq2 = (Square)90;
Сопsоlе.WritеLiпе("эq2 = {О}", sq2);
11 Ilpeобразоваии8 Square в System. IntЭ2.
int side = (int)sq2;
Сопsоlе.WritеLiпе("Длина стороны sq2 = (О}", side);

А вот как следует обновить определение типа Square.


public struct Square
{

public static explicit operator Square(int sideLength)


{
Square newSq;
newSq.Length = sideLength:
return newSq;

public static explicit operator int (Square s)


{return s.Length;}

Выглядит немного странно, не так ли? Честно говоря, преобразование из


Square в System. IntЗ2 не является интуитивно очевидной (или полезной) опера­
цией. Однако она демонстрирует одну очень важную особенность пользователь­
ских подпрограмм преобразования: компилятору ~Bce равно" из чего и во что вы
преобразуете - важно, чтобы ваш программный код был синтаксически правиль­
ным. Так что, как в случае с перегрузкой операций, только из того, что вы можете

l
Глаза 9. Опециальные приемы построения типов 395
создать QIJ~рi:щmO :ЯВНnrО преобрааова.ниа для дaщroго nmа, совеем не следует, что
вы обя~ ~TO делать. как правило, этоо" подход оказывается паиболее полезным
ТQща, 1(Orдa соэда1ОТen тИпы структуры .NEТ. ПОСIЮльку тахи.е типы не мoryт ис­
ПОЛЬЗОВаТЬ иерархии М3.C€ИЧесного наследования (Дo1Ul КOTQPЫX соответствующие
преобрцовamm реализуютс:я автоматичеСlШj.

Определение под.про,грамм
неявного прео6разования
до мого момента мы с вам;и создц.вали пользовательские операции' явного цре­
образования. НО что МQЖНО СШJЗЦть О следующем l-I.eЯВНOм:преобразозании?

st.8·tic voJ:q Main .(st:r:ing-(] arg-s)


t

11 П01ВlТха 8SПC1JU11R'8 BeJlВBoe !%реобрuова.ви.?


SquаrеsЗ';
S. З . Leng,th = ВЗ-;
Re.ctangle re.ct2 = э3';
")

Itaк вы можете догадатъси г.а:ми, этот прагра.ммн:ы:И Ю::iд ско!\mИ1JИpOВaR не 6удет.


nocltoJIЬКY в нем не предлагается ~оЙ подпрограммы не.явнQГО преобрwsованин
11)18 типа Rectal1g1e. тут нас rroдс:гереrа.ет ·Л(i)вymнa~: в ОДНОМ и том же типе велъ.зл
определять явgые и nemmыe фу:шщии преобразаванйя. не отличающиеся по тnду
возвращаемого значенця ш.rи по В;iборУ парамшрсв. Может пока~ъCR, ч.то это
правило ИВJIяет~ слиппwм огрзнич:явающим, но. не следУет заоhlВaТЬ о ТОМ, что
даже еCJ1П пm оnp~деляет I10oДUporpвмм.r fleявнDгO преобразовallиц, вызывающая
сторона MJ>lМeeт право" ИCnOl1ЪЗQващ. синтакСИI\: явН020 преобраэ.овaнияJ
Запутали~? ЧтоБJЩ црояснить ситуацию~добавим в струюуру Rectang'le ,:юд­
nporpaммy цея:Щ-1ОГО преQбр<!.,3!:JВания, используя ключевое СЛОБО С# implicit
(В следу1ОЩем програ:ммКQМ !((}де предпЬпaraется. что ширина резУЛЬ"rЩ'ующего
Rectangle цолучается с помощью умвOЖe1IИ1l стороны Squa.re на 2)_
рtФliс struct Rec't angle
I

риЬНс stati ii: irnplicit ope.rato·r Rectahg.le (Sgu.a:re з)


(
Rесtзnglе r .;
-z;. Height. = S . _liелgth;

11 lIb«pива ио.о%'О пр-а~сшьвиха раава


1/ у~оеквОЙ" дmcвe C'JIopo_ ж.аадра'l'а.
r.Width = s.Length i- 2;
retu:fn r;
).
J
с тaICИМИ' И'ЗменепkЯМИ .ВЬ1 пщryч:aете ~О~l\oIожностъ npеобрззовыватъ упазЩfflые
типы так.
.,....

396 Часть 11. Язык программирования С#

static void Main(5tring[] arg5)


{

11 Не"ное преобраsоваиие: все ОК!


Square 53;
s3.Length= 83;
Rectangle rect2 = 53:
COn501e.WriteLine("rect2 = {О}", rect21;
DrawSquare (з3) :
11 СиитаJCСИС "но%'о преобраsоваИИR: '.1'088 ОК!
Square 54;
s4.Length = 3;
Rectangle r8ct3 = (Rectangle)54;
Console.WriteLine("rect3 = {О}", rect31;

Снова подчеркнем, что допускается определение подпрограмм и явного, и не­


явного преобразования для одного и того же типа, но только если отличаются их
сигнатуры. Поэтому мы можем обновить Square так, ЩlR показано ниже.

public struct Square


{

1/ Но_о B~loIВa'.1'Ъ хах


11 Square sq2 = (Square)90:
11 и.nи ках
11 Square sq2 = 90;
public 5tatic implicit operator Square(int sideLengthl
{
Square newSq:
newSq.Length = sideLength:
return newSq:
11 ДOJI_О JWSloIВатъся: хах
11 int side = (Square)mySquare;
public static explicit operator int (Square sl
{ return s.Length: )

Внутреннее представление пользовательских


подпрограмм преобразования
как и в случае перегруженных операций, те методы, которые обозначены клю­
чевыми словами implicit или explicit. получают ~специальные имена" в терми­
нах CIL: ар _ Implici t и ар _ Explici t соответственно (рис. 9.2).

l
ГJlава9, Специальные приемы ПОI.>1роеl~ИЯ Т}1ПО9 397

'" ф б\росum""t; ahd'5attinijS'~drew TrC>!I$"'\I'Iy 00t1JmIIntJ\М~ ~(,


• МI:\N 'tFfЯ
.;; . 'CIis~_sЮJ\'I
;'-" .~-.Ior\.,Propet\je<
'1IJ • cшtr.InGon,erSh>n5,Р,оgr<IМ
>f. ~ ~CbnV.mIor!s,R"tlm1gl!!
""~ .• '2'II'ГГ'-I~1

;:
_I"JI~

v!lk.e~~IaI"" 5eol8d b.d'~


'. ,extмds !m$W''']s\'StiIr!I, ~oI<-!I'Т1'Р.
W 51de.щih : P>Jb!it 1~tЭ;:!
• Oraw : voidO
• ТoSt\'U'>;I ~st(lr«)
IJ ф_Е"I'\tt ,~~~ GUst.amcon~.SquaI..),

.i
I!II QP_1rtP~ :'I'~t:Jet~e ~pnvet"',OnS.5~nt13z):

,~~tdnI
.~~'--------'-'----~------------~~
~ . ,,

Рис. 9.2. Представпенив ПОllьзовiпеrlь(jкиII. подпроtpэмм пJjеобраэованиSl в терминэх CIL

lja ~TO:M мы. завершаем обзор В1JЗМО1Кffоc:rей DользовательСRИХ ПQДПРОГРамм


преобравования. Км И.в случае перегруженных операций. C(101:'BeТCTB~ ~иИ­
'l'1Щс,ИС ..я:вJПIетс.я лишь сокращенным вариантом оnpеделенця МНОРЩЛЬНЫХ" члi!­
.h;ob-фуннЦий, и с ЭТОЙ точки зрения 01-1 :не является обnзательным.

ИСХQДИЫИ код. Проект СustоmСолversiопs размещен' 8 подкаталоге. соответствующем глввэ Э,

Ключевые слова С#, предназначенные


ДЛЯ более сложн,ых конструкций
в '~авершение ГJliавы мы расСМотрим ряд .влючевblX слов ct, nPИМe1JelIИеROТQ-
рьц: требует от разра~ несколько болЬшего опьtrа в npоrрaммnровании:
• chE:ck€d.1u'n ch.ecked;
• unвare/stackalloc/fixed/siz8of.

Сначала,мы выясним. Ra1'{ С П@МОЩЪЮ КЩОЧ~ВPIX Слов c/1eck ed It unchecke.d в С#


обеспечивается автома,тич.еCI«>е ВЫЯВJIlцщ.е ус.цо~ИЙ переnoJШенив и потери .значи­
мости при Быполнеwщ арифмептчеСRИХ операциИ.

Ключевое ело'во checked


Вы, несомненно. прекрасно знаете, 'ЧТо любой 'чисщщОй :Тm:J дашtых имеет свl!IИ
строго заданные верхний ц нижний предeлPJ, (значения хоторых можно выяепить
программНЬ1МИ средствами с ПОМОЩЬJQ свuйств M-ёl~V.аluе и MinValue). Лри ВШnол­
ненин арифметических операций с конкретным ТIЩОМ вполне воамоЖВ0 случайное
hepеnomteн:ие блок~ храненЮI даннorо J'ипа (ПОnЫ'шta прИсвоeIm.R типу значении.
которое окаaьmае:rся больше МaI!:СЯМад:ъно ДОПУСJ'mdоrо) или ТlfJn1eря знй.чtш.ocmil
(ПОnЫ"ffiа присвоеFtИЯ значеJjНjJ, l{OTopoe ОIЩзъша/tтсл меньmеминимал:ьно допу-
398 Часть 11. Язык программирования С#

стимоro). Чтобы Мидти в HOry" С CLR. обе эти возможности буl1YТ обозначаться. :как
"переПOJlliение". (и переполнение. и потеря значимости DpJШоДЯт к созданию ТШIа
System.OverflowException. ТИпа System.UnderflowException в библиотеках ба­
зовых классов нет.)
1J1rя npимера предположим. что мы создали два экземпляра типа System.Byte
(тип byte в С#). присвоив им эначеl-IИЯ. не превышающие максимального (255).
При сложении значений этих типов (с условием преобразования результата в тип
byte) хотелось бы предполагать. что результат будет точной суммой соответству­
ЮIЦИX членов.

namespace CheckedUnchecked
{
c1ass Program
{
static void Main(string[] args)
{
11 Пеpenonвевие дmr Sуstеш.Вуtе.
Console.WrlteLine("MaKc. значение для byte равно {О}.",
byte.MaxVa1ue) ;
Console . Wri teLine ("Мин. значение для byte равно {О}.",
byte. MlnVa1ue) ;
byte bl = 100;
byte ь2 = 250;
byte эит = (byte) (ь! + Ь2);
11 Значением ВuDI. ДОJDlalО бlot'l'Ъ 350, но • ..
Conso1e.WriteLine("sum = {О)", sum);
Conso1e.ReadLine{);

Вывод этого приложения покажет, что sum содержит значение 94 (а не ожидае­


мое 350). I1ричина очень проста. Поскольку System.Byte может содержать только
значения. находящиеся между О и 255 (что в итоге составляет 256 значений), sum
будет содержать значение переполнения (350 - 256 = 94). как видите. в отсутствие
специальной коррекции переполнение происходит без генерирования исключений.
Иногда скрытое переполнение не создает никаких проблем. В других случаях соот­
ветствующая потеря данных может быть совершенно неприемлемоЙ.
для обработки переполнений или потери значимости в приложении имеются
две возможности. Первой возможностью является использование программ:ист­
ского опыта и квалификации с тем, чтобы обработать все условия переполнения
вручную. Предполагая, что вы можете найти все условия переполнения в програм­
ме, можно было бы решить проблему, связанную с переПOJlliением в предыдущем
программном коде, как ПОRaЗано ниже.

11 ИСПD.JDa$О8ание int д;п_ аuш, 'Ч'1'!об.. не допус~ переПОnНeвиJl.


byte bl = 100;
byte Ь2 = 250;
int sum = bl + Ь2;
Гпава 9. СпециаnЫlые 'np~eMЫ построения ТИПDВ 399
Конечно, лроблемой э'пд-О подхода звлиетоо то. 1IТo вы - ЧeJIОВех, а значи.'l; цри
всех ВaпntX У:СИЛJ(lЯХ. 'могут остаться ошибки. УСКОЛЪЗRyВшие от вашего B~JJВдa.
Поэтому в С# предлатается ЮIЮ"iевое слово c:hecked. При помещении Qnepaтupa
(или: блока Dпераroров) в рамки контекста lШЮчевого слова check.ed KD~OP
с# генерирует сцецшщъ.ные CIL-инструкции. с ПОМОIIIЬЮ которых .up<>ВерIПOТСН
УСЛОВИfl nереполnеl-ЩЯ, вОэм.ожны.е при ВЪПIOЛnении сложения. умноженщ. ВЫЧИ~
танив или деления ЧИСЛОВЫХ типов даНнЫх. Если происхоДIJТ neреполнеюsе. ,ереда
ВbIПOJЩения генерируе'F 'JtЩI Syst.eIfl.OverflowE.xception. РаССМ0ТРИте следую1ЦУЮ
модифИl«ЩИ1!) проТ'рaммъr.

Glli~S l'rogr=
t
static: voi!i ~in(st.Lingl] args)
I
11 D.epeDQmlIЦJИ& ,щzlll Sy8 tеш. Вyte .
Сощ:юl е. Wri tеLiш~ ( "Ma'l(C. 3}iачение для i:;>yte равнС) ! О •. " ,
byt€.Maxvalue):
byte Ы = 100;
byte 1>2 = 2'SO;
try
t
byte suro = cbecKвd! r.h>yte) (Ь1 .j.; Ь2 J)
Cons,ole.WrJ:te;Lir" e ,! "SU!D = {О) ", sum);
J
c ai cb (Ova dlowEx'.c ,e p:t i 'o:(l е-)
{ СОnSОlе.W:ritеLir:JE'!{е,Меэsаgе);
J

3.де,съ оператор сложения b-l и Ь2 по:мещаетсн в контекст ключевого слова


che.cked. Если 11ы' хотите. чтобы проверка перепoлI:tения. происходила ДЛЯ бnо~
DparpaммHora кода, мО'жно J;lзаимО'деЙСТЩ>13атъс "КПЮ"'rевым словО'м chec.ked так,
нав. noказано ниже .

ну

I
Checkeci
I
byte зum = (byte) (Ы -1- 1;12') ;
Con~oJ:e. Wri teL,i ,r,e (" БШ'!\ = '! о J ", :rum);

j
c'a tch (ОV'~rflоwЕхсерtiо.п е)
{
Consoie. Wr.i teLine {е .. Мезsаgе., ,
}

J3 щобом cnyчае соответсТВуЮщий прогрaммный ROA буд~ проверяться на воэ­


МОЖНQe перепomrение aDТGматически. и eCJI1.I переПOJIИени~ будет оБНару2Иена, тО'
будет сгенерированi:J соответствующее перепО'.зmенl:ПO ИСЮlЮчение .
.,....

400 Часть 11. Язык программирования С#

Проверки переполнения ДЛЯ всего проекта


Если вы создаете приложеJlИе, которое не должно позволять скрытое перепол­
нение ни при каких условиях. будет слишком утомительно указывать ключевое
слово checked для каждой строки программного кода . Б качестве a..1IЬтернативы
компилятор С# предлагает использовать флаг /checked. Когда ЭТОТ флаг активи­
зирован, все арифметические операции будут проверяться на переполнение без
указания ключевого слова checked. Если обнаружится переполнение, вы получите
OverflowException среды выполнения.
Чтобы aRТИВизировать этот флаг в Visual Studl0 2005, откройте стрamщy свойств
проекта и щелкните на кнопке Advanced на вкладке Build. Б появившемся диалого­
вом окне отметьте флажок Check for arithmetic overflow/underflow (Проверять условия
переполненияjпотери значимости ДЛЯ арифметических операций). рис. 9.3.

Ci88'1I --~~ .........-~--,..~~


l8ngUIge - . ~Ы! _ 11
~<:.aqJIIrfrtorRIPQI'ftIO: §ne ~--~-----I1
~ a..1IIimd: CIWfh,v.ldaw
CJ DII notl'llflnra......,..
0UIPUt
~~ ~Iu==~~~==~=- ,
.... ..., ... 11: [~ JI

l' (J( 11 C.aI - I


Рис. 9.3. Активизация проверки переполнения в Visual Studio 2005

Ясно, что эта установка оказывается очень полезной при отладке. После того
t<aК все связанные с переполнением исключения будут из программного кода уда­
лены, флаг /checked для последующей компоновки можно отключить.

Ключевое слово unchecked


в предположении, что вы активизировали проверку переполнения для всего
проекта. как разрешить игнорирование переполнений для тех блоков программно­
го кода, где Ммолчаливая реакцияМ на переполнение вполне приемлема? Поскольку
флаг I checked предполагает проверку всей арифметической логИI<И, в языке С#
предлагается ключевое словоunchecked. которое позволяет отключить генериро­
вание System.OverflowException для KoнкpeтH~ случаев. Правила использова­
ния этого ключевого слова аналогичны правилам использования ключевого слова

checked. и вы можете указать для него один оператор или блок операторов, на­
пример :

11 д... _cmr фпаro 1ch_cked ахtrИ8Иsироаан,


11 :tIJ!O'l' б.ltОJC не roeН_РИРУ_'l' исlC.lDDЧ_1IЮI а сред_ DШO.ItИ_IIИJI.
unchecked
Гла~а 9. СnециаЛЫlые nриеМbI построения ти.пов 401

byte surn = (byteJ (Ь1 + Ь2) ;


Con-s.ole. Wri teLine (01 SUЩ'1 ·=- (а J ", .3urn);
}

nQДВOДfl ИТОГИ обсуждения кttючевых <:ЛОВ С# checked и Щlсhесk!3d. снова noд­


черsвем. что по умолчанию среда ВЫIIOЛ1re1ШЛ .NEТ иг:uoрир~ УСПОВИЯ пеpenол­
ЩНЩf, возникаюЩие при ИСnOJIЬЗOваиии арифметичеm<ИX опi:фациИ. Если вы хо­
тите селеКТИiUЮ контрОЛИровать условия пер~щолн~ния ми QтД~ операторов,
исдользyit>re КJIЮчевое слово checJ~ed. Если нужно контрOJЩРЩЩТЬ ошИбки пере­
цошreнип во всем ЦРиложеНИй. укажите фnar I C'heCked, ffiщонец, можноисtюлъзо­
.mпь юпочевое ruюво unсЪес ked. если у вас есть бn:oк ПРОГр~.\,.D,ЦiOfO !Юда,. ДЛЯ.IЮТO­
рр,ГО· переполвение приеwrемо (и поэтому оно не ДОЛЖНО генерировать иcкmoчение
:в Среде ВЬ1IIDЛНения).

ИСХОpJflllЙ 1<0.11.. П'роеtn' CheckedUnchecked размещен в подкаталоге, СОО1'Ветоrвующем гJ1aвe 9.

Работ-а с типами указателя


Из rna.вы 3 вы узнали. что платформа .NEТ опреД(:JIНfir две тавные 'КаТегории
данв:ык: типы. характеризуемые з-начениями., и типы, ХЗ:р'~еризуемые саатлками
[cm>rло'чнъtе типЫ). ОфlaI<о. сцраведлиЩlСТИ ради. следует сВазаТь. что имеется и
Tpe1'ЫI :кm'eгория: это irU.U1Ы указателя. Для роооты с т.ипами j1ЮlЗaТtШВ пpeддarа­
ЮТСЯ спеЦИальные операции 11: RIIЮчевые слова, с IЮМDЩЫQ КQ'l'ОРЫХ-МОЖНО "обой­
ти" СХему ynpавленив пам.атью CLR и "nзить ynpа1Щение 11 свои руни" (табл. 9.3).

Таблмца 9.3. Операци':! 101 lCЛючевые· СПQ'ВЗ С# W1я работы с указаreлями

Оп_раЦИII или
Описание
19IJQЧ81О8 ·cnoвo

* ИСПQ)1bзyетоя· дr1Я СО3ДаfoOOl лервмежtbltt уКаЗ8теля (т.е. nepeMel'lHO.::l. предСта&­


'мАющей непоорвдствеяно адРесуемую точку s памяtИJ. Ка" и в С(++), тот же
знак используется ДЛЯ операции ра.эымвнования укaэiпeля (т.е. дnя операции,
k!ОТОРая возвратит значение, раэмещенwое 110 адресу. укаэatil'lому onepaНAGM)

ИслолЬэуеТGR ДЛ!! получения адреса переменноL1 в памяти·


Иопqльзуется для Aocтyna к ПОЛЯМ типа. пред~вленнЬ/м указателем (He~ea.
опаСtlая версия операции,. Qбозначаемой в С# точкоЙ)
п Операция JJ (з ttебазОl1асном контексте) позволяет Wiдексировать элемetn;
на kОТОРЫЙ укtlЗЫllает переменная указателя, (Обратите sнИма!1l'ie· на ЗJoiШ10-
fИЮ между пвременной УК8.З8теJJЯ И операц~ [) в С/Н).)
++, -- в нet5еЗОЛВСRОМ контексте к тип8М jlkазател>l могут nримвl1ЯТЬСЯ onepaLUU'l
flриращения 1'1 ОТРllщатеЛЫiОТО приращенкil

+•. - В небезоГlSСI-lOМ 'I(O~l:reKCTe к типам УК;8ззrеля могут .примвн,яться оперзulofН


сложеli~я и вычитания

~;::! ~=, <, >, В l:IеБВЗопамом KO~J'eI(Cre 1( tипам указателR могут ЛРИМ8IiRТbСЯ оflВрации
<=, .. > сраВНaI01яи проеерки на ТО)IЩественмос-ть

s,tackalloc 8 неt!езопасном Iфнте~ МОЖНО использоsать КЛlOче90е слово


s tackal1oc, чтобы размещать массивы С# 8 стеке
В небезопасно", контексте можно испqлЬ301lатъ ключевое слово fi){ed, вре­
менно фйксирующее flеремеliНУЮ Q теМ. 'поt)ы МОЖНD БЫЛD майти ее адрес
402 Часть 11. Язык программирования С#

Перед рассмотрением деталей позвольте заметить. что необходимость в исполь­


зовании ТШIов указателя возникает очеnь редко. если она вознmcает вообще. Хотя
С# и позволяет ~спуститься" на уровень манипуляций с указателями. следует по­
нимать. что среда вьшолнения .NEТ не имеет никакого представления о ваших на­
мерениях. Поэтому если вы ошибетесь в направлении указателя. то за последствия
будете отвечать сами. Если учитывать это. то когда же на самом деле возникает
необходимость использования типов указателя? Есть две стандартные сиryации.

• Вы стремитесь оптимизировать работу определенных частей своего прило­


женин путем непосредственной манипуляции памятью вне пределов управ­
лешmСLR .

• Вы хотите использовать методы С-библиотеки *.dll или СОМ-сервера. тре­


бующие ввода указателей в виде параметров.

Если вы решите использовать указанную возможность языка С#. необходимо


и;нформировать с s с. ехе об этих намерениях. указав разрешение для проекта под·
держивать "небезопасный щюгрaммный код". Чтобы сделать это с командной стро­
ки КОМIlИЛЯТора С# (СБс.ехе). просто укажите в качестве аргумента флаг /unsafe.
В Visua1 Studio 2005 вы должны перейти на страницу свойств проекта и активи­
зировать ОIЩИЮ Allow Unsafe Code (Разрешать использование небезопасного про­
граммного кода) на вкладке Build (рис. 9.4).

I~ Coriipatlon: 1Actlve (Debug) 111


J
I Referenc:e PlIths
I Вuilde.......tS
I

I =~
! =-___,
ВtiId 1

1 W~LМ:
~W~:
1I 11
~~-----------~
-J
L I_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

i
Рис. 9.4. Разрешение небезопасного программного кода Visual Studio 2005

Ключевое слово unsafe


В следующих примерах предполагается. что вы имеете опыт работы с указа­
телями в С(++). Если это не так. не слишком отчаивайтесь. Подчеркнем еще раз.
что создание небезопасного программного кода не является ТИПи<lJIOЙ задачей в
большинстве приложений . NEт. Если вы хотите работать с указателями в С#. то
должны специально объявить блок программноro кода "небезопасным" • используя
ДЛЯ этого ключевое слово unsafe (как вы можете догадаться сами. весь програм­
мный код. не обозначенный ключевым unsafe. автоматически сЧитается "безопас­
ным").
l'

Глава 9. СпеЦИ~JlblIые приемы поtтроения типов 403


unsaf.
(

Кро.м;е объявления. контекста небезоnз.сного npOrpaMMR(i)ro' кода, вы можете


строить "flебеаonас.ные-" структуры. Ю1аСС1>I. ч;)цщы ТИЩ)В \11 пара.метры. Вот йе­
СЩ)W'!ftо' примеров, на :которые c.i1eцyeт обратить внимани~

11 ВCfI э'Фа C'l'pY~yPa. .аnJlешCJil 1~е6е8ОПасв.оЙ 1


IJ и кожи ЦФЩlЫlоаа>,R;СВ !t'om.xo • неlSеаопасвом xo~.XC9:'9.
p.u blic unгaf'e st:ruct Npde
I
puыcc int Value.;
pDblic N'o .d e" Lef't )
ptiblic N9de* R.!i.ght;
.,
"

11 Э'l'a. cwpр:тура .а;nIl8.,;.си 6esоnaсвой, КО 'IJIeJUI' Node* - не'!'.


1/ C'1'pOI'O I'08оря, поцу~ доc.wуп х !Va1ue! иаа"8 :кеCS.sопасио:rо
11 JI:OBo:r81tC'I'a: коаио, ах 'Lef't' и 'R.igh t' - _И.
рuЫ1с struct Node
{
pиblic iг.t Value:;

11 ]с З'1!'JOl э;nемев'1'8Х мацо ПОП~ ДO~ '1'оШtХО


11 а вебe:sonасаом JCOвwez.CT8 t
pub,l ic uns_fe Node* Le-rt:
public un$afe N~de* Right;

МетDДЫ (нан статические, так и уровня ЭI~еМIIДЯ:Р~) тоже можно обозначать,


Щl1( небезОцасные. Предположим. например . вы знаете, что некот()рый статиче­
СКИЙ м.~oд использует лomкy ~ТeJIеЙ. Чтобы гарадтироваrъ ЦЫЗОВ дан.нorо ме­
тода Т9JiJ>Жо в Небеаопа.сн'ОМ 1(оНТ!ШСте, можно OI1pеде.iпrr.Ц метЭД так, Ra1t noхазанD
l:ШЩе.

unaa.fe publicstat i c voi,d .SomeUг.safeCodeO


(
1/ оперaжrори ·дли: раБО!91 Q VХ8.аа'1'83UINИ.

В такой J«IНфю:урaщm. требует,~я, чтобы ВЫЗываЮщая сторона обр.ащалаоъ к


SоmеЮnЗ.:>.fеСоdе{) TaIC.

stf!tic v'o id Ma.i n (S!triи(] [] args)


{
unsafe

$omeQnsafeCooe () ;
404 Часть 11. Язык программирования С#

Если же не обязательно, чтобы вызывающая сторона делала вызов в небез­


опасном контексте, то можно не указывать ключевое слово un 8 а f е в методе
SomeUnsafeCode () и записать следУЮщее:

public static void SomeUnsafeCode()


{
unsafe

11 Onepa~pK дик работк с ухааа~еnRМИ.

ЧТО должно упростить вызов:

static void Main(string[) argsJ


{
SomeUnsafeCode();

Работа с операциями *и&


После создания небезопасного контекста вы можете строить уназатели на тИIIЫ
с помощью операции * и получать адреса заданных указателей с помощью опера­
ции &. В С# операция * применяется только к соответствующему типу, а не КаЕ
префикс ко всем именам переменных уи:азателя. Например, в следующем фраг­
менте программнога кода объявляются две переменные типа i nt * (указатель на
целое).

11 Нет! В СIФ это нехоррехтно!


int *pi, *pj;
11 Да! Эта в сIФ npuи.nloно.
int*pi, pj;

Рассмотрим следующий пример.

ипэаЕе

int mylnt;
11 Оnpeдеnени. ухазате.JUI типа int
11 и nPИСВ&И8ание ему адреса myIn t.
int* ptrToMylnt ~ &mylnt;

11 ПРИСВ&И8ание 8начеиив myInt


11 с помоЩloЮ РlUWllенов&НИJI yxa_aoren •.
*ptrToMyInt = 123;
11 Печатlo с~атис'1'ИХИ.
Сопsоlе.WritеLiпе("Значение mylnt (О)", myInt);
Console.WriteLine("Aдpec mylnt {О:Х)", (int)&ptrToMylnt);
1
Глава 9, Сl1ецИ~nЬНbjе приеМI>I ПООТР'ОЕ!НИЯ Т}1ПОВ 405
Небеэопасная (и безопас"ая) фptlЩИR Swap
~неч:но. oбыrвлеии:е указателей на ЛО,RaЛЬные п~ремеННЬi.e с ПОМОЩЬЮ просто­
то приеваивa:iШЯ им значений (нак в предыдущем цримере) никогда не требуется,
Чтобы привести более полезный пример небезопасного программноro J«j,uEl. пред­
по,лрЖИМ. что вы хотите создать фyнtщЮO оБIl4е:ца. llСПОЛЬЗУЯ: арифметику указа­
Т(Jlей.

uлэаfе public 5t~t i c voi ci Uпsаfе'$wар (int'" i , iлt* j')


{
iлt temp' = "i:
"i *j;
*j = temp';

O<tem похоже на О. нета:к ,Лl'I? Однако с учетом знamПl. полyqerщых ~3 славы Э,


вы должны знать, что можrю записать слецуюiЦyЮ безопасную версию алгоритма
обмена, исп0JIЬ3уя: КЛIQ.чевое слово СИ ref.
pl1blic statlc vo i d SafeS'w ap (ref lnt i, re~ iJ1t j)
{
int tеПlp = i;
i = J:
j = 1:еItlp;
1
ФyнRЩiональные возможности RВЖДОГО ИЗ этих метеДОВ Щ~Н'lичны. и это еще
раз ПQДт~рждае'Т, что работа нanpa:мyIO с укaзa:rелями в С# требуется peД1lo. Ниже
покаеана лorикa вызва.

stQ.tic void Маiп(з't:riпg[J arg'S)


I
COfiSole. Wrl teLine (" * .. ;. ВьtэQВ ме'1'сда с небеэопасным КfЩОJ,f 11 " " " ) ;

11 ЗвачeВиR ДЛ. обнев:а.


iлt i = 1 ,0 ,
j = 20;

1/ 'Беаоаа~' об~' sва~.нм.ми.


Сопвоlе. W:c i teLione ("\в'" ........ " БеЗ0i'1асный обмен ,,*" '" *") ,.
СоiJэоlе.
W:r i'tel.ine (" З:начеJ-f-ИЯ до оСме;НQ: i = {О], j = О}", i, j);
SafeSwap (ге! i., ref j) ,;
СQл_sоlе.WrltеL.:Lnе("ЗнаЧения после обмена: i = {Ol, j = o. j ", i , j);

1/ 'Вебеаоп&OJa.lЙ' обке. sк.ч....-ми.


conso1e , WriteLine (" \п .... '* ** Небезапасный обмен 1\'***"''') ;
Соnsоlе. 'Ихl t 'e l,irJe ("Значения IIO ,оБМe:rlQ.: i '" (0), j = {1} ,. f i J j);
l:Insa'fe { Uns'afeSwcitp (&i, &j): }
СОПS:Qlе.WritеLinе("3.нацеf,lИfl ПQС1.lе {)омена: i = (О ) , j =ilJ", i , j ');
C'Onsol€ . ReadI.ine О ;
406 Часть 11. Язык программирования С#

Доступ к полям через указатели (операция -»


Теперь предnолоЖйМ. что у нас определена структура Point и мъl хотим объя­
вить указатель на тип Point. как и в С(++). ддя вызова методов или получения дос­
тупа к полям типа указателя необходимо использовать операцию доступа к полю
указателя (-». Как уже ynоминалось в табл. 9.3, это небезопаснаи версия стан­
дартной (безопасной) операции, обозначаемой точкой (.). Фактически. используя
операцию разыменования указатели (*). МОЖJIо снять косвенность указатели, что­
бы (снова) вернуться к применению нотации. обозначаемой точкой. Рассмотрите
следующий программный код.

struct Point
(
public int х;
public int у;
public override string ToString()
{ return string.Format("((OI, (11)", х, у); )

static void Main(string[] args)


{
11 Дос'2!УП IC членам через УIC&за'2!eJIИ.
unsafe

Point point;
Point* р = &point;
р->х = 100;
р->у = 200;
Сопsоlе.WritеLiпе(р->ТоStriпg(» ;
}
11 ДОС'l'УIJ IC членам через раЗJolИеНО8Jo18ание yJC&за'2!e.nеЙ.
unsafe

Point роiпt;
Point* р = &роiпt:
(*р).х = 100;
(*р) . у = 200;
Сопsоlе.WritеLiпе( (*р) .ToString(»;

Ключевое слово stackalloc


в небезопасном контексте может понадобитьси объявление локальной перемен­
ной, размещаемой непосредственно в памяти стека вызовов (и таким образом не
подлежащей UутилизацииП при сборке мусора .NEТJ. Чтобы сделать такое объявле­
ние, в С# предлагается ключевое слово stackalloc, ЯВЛИlощееся С#-эквивалентом
функции _а110са из библиотеI<И времени выполнения С. Вот простой пример.

uпsаfе

char* р = stackalloc char[256];


for (int k = О; k < 256; k++)
p[k] = (char) k;
Глава 9. Специ-альные приемы поmpоеlil4А ТИПОВ 407
Фиксация типа с помощью ключе,ого сnо.а tixed
Как покавьщает npедыду,щвй пример. змачу 'р~еЩения элемента 11 П'амяти
в рамках небеэоnaCJЮrо :контекС1!З можно упрОСтить с помощью' ключевого слова
stackalloc. В с:илу самой npир<)Ды ЭТQif ()п~рации соответствующая::naмятъ очи­
щается. IUUC 1'oJ1:ы(.o ПРОIiIСХОДИТ воаврат ~ метода ра;iМf!щеНШI (поC1tOJIbКj память
ВbIбирается из стека). Но рассмотрим более сло~ npимер. В процессе Qбсуж­
ДeiЦШ операции -> вы ~далн хаРЗRтерlI~У~ значением тип. Роint.ПодоБНО
1JCe"М ТШIЗМ. характеризу~ энач~; щмде/l.eНН:ая Для. вегопамять в cTe1re
освобожn:аетсясразу же после nсчезновеЮЦI RОJГГe1tCТЭ выполнения. Теперь. ДЛЯ
npимера, npедцоложим. ЧтО ТJШ ~оiлt t1ыл О1феделен как ссы.JtoчнtI1 тип.

/ / <= Теперъ э!t'О' xnасс!

p"o.Jb1ic int х:
p~.blic int У;
publio o:verride э ·tring TpStribg: ()
{ returD эtring.Fс·rm.;it(" (\О}, !1n", х, у); }

Вы хорошо эваете о ТОМ. что ес:.ли вызывающая с.тОРlща объв~ет перемен­


.ную типа Polnt. ДЛЯ нее вьщеляетсв динамическая IIaМВТЬC. ЯВJUIЮIЦafI;С.8 объ.еwro'М
ДlШ сборки мусора. Тогда возШlКает запрос: что npОИЗQйдет, ес1IИ небезопа~~
ко}пекст полытается "Взаимодействовать с соответствующим объек';I'ОМ (ИJ,!Д лю­
бым другим объектом в ДliНaМИческой mwв.ти)? ПОС1(()ЛЪJg" сборка ~eopa мрж~т
начаться в любой :момент, предcrавъте себе всю боле.знt":ННостъ доступа 1t члеЩ1М
Point. когда идет очистка динамической пзмящ. Терретич:ееки ВО;3МРЖI:IО , что не­
безопасный контекст будет пыraться взаимодeiiСТlЮ.Рать с ЧJ1ено~, RОТОРЫЙ боль­
ше не доступен или занимает НQБ0е положение В.ЩfIЩj\,fическ.ОЙ ПЗМJiТИ, ~пережив"
очередную ген:ерацию чистки (и ЭТО. Qчевидно. НВЛЛe'f('Л r,tpоблемой).
ЧтоБЫ блокировать перемеgиую ССЬЩОЧ:НОl'О типа В пal\.l.Яти И~ небезо,паеного
.контекста. в с# прeдлar:aется юnoчевое СЛОВО fixed . Оператор fiхеdустaщmли­
:ваe<r указаТедЬ на управляемый тип и ·занРeIШя:ет~ пере:мецную ЩJ. IIp~ выnол­
пения оператора. Без ltl!Ючевого слова ftxed в ~нении УIЩЗ.a1'е.л~ н;ауправ­
Л1lемы:е переме8НЫе было бы мало ~ела. IJОСКОЛЪКУ в реЗУJд,rlP'е сРор;ки мусора.
такие nepeмemn.te могут neремещат:ьс.я аеиредсказуемым обравам. (H~ самом деле
ХОМlIИJlЯТор С# :вообще не ПОЗВОЛИТ установить уназателъ на уцра.вляе~ пере'
Мепную. если в операторе не ИСПо.дЬ3Уетс.в юиочевое славо ti xed.)
Так что если вы сшщадите Т:РШ Foint (щчас переоцределенащ, как класс):и
З.ахоти:те взаимодействовать с ero ТJlII~вами.. то ДОЛЖНЫ записат.ь CJ1~дующий про­
ГРaммв:blЙ код [:иначе воэ.пикнет @шиБЩJ. комrщляци.и).

цrtsii;fe p ub.Ii c static void Maio ()


j
Point I't = new PQint () ;
pt,x = 5:
pt.y = &;
11 ФиJCCaц1bI pt, 'JIJ!о'бw не допус~ пере........ виа
11 юцs УДllJleRIUiI при сборже ~copa.
~izad (int .. р = &р!..)О
408 Часть 11. Язык программирования С#

{
11 ПерекениёUI int* испom.зуеТСII зД'есь.
}
11 Теперь pt не зафИICСИРО8аиа и lIо_ет б»т1o убрана
11 сБОРЩИКОII мусора.
Console.WriteLine ("Значение Роiпt: {О}", pt);

в сущности, ключевое слово fixed позволяет строить операторы. занрепляю­


щие ссылочную переменную в памяти, чтобы ее адрес оставался постоянным на
время выполнения оператора. для гарантии безопасности обязательно фиксируйте
ссыJlки при взаимодействии со ссьmочными типами из небезопасного контекста
программного кода.

Ключевое слово sizeof


в заключение обсуждения вопросов. связанных с небезопасным контекстом в
С#. рассмотрим ключевое слово siz.eof. кан и в С(++). ключевое слово С# sizeof
используется для того. чтобы выяснить размер в байтах типа. характеризуемого
значениями (но не ссылочного типа). и это ключевое слово может использоваться
только в рамках небезопасного контекста. Очевидно. что указанная возможность
может оказаться полезной при взаимодействии снеуправляемыми API, созданны­
ми на базе С. Использовать ее очень просто.

unsafe

Сопsоlе. WriteLine ("Длина short равна (О}.", sizeof (short) );


COr)sole. WritеLiпе ("Длина int равна (О}.", sizeof (iлt) ) ;
Сопsоlе.WritеLiпе("Длина 10пg равна (О}.". Sizеоf(10пg))i

Поскольку sizeof может оценить число байтов ДЛЯ любого элемента, произво­
дного от System.ValueType, можно получать размеры пользовательских структур.

Допустим. мы определили следующую структуру.

struct MyValueType
{
public short э;
public int i;
public 10ng 1;

Тогда ее размеры можно выяснить тан.

unsafe
{
Console. Wri teLine ("Длина
short равна (О}.", sizeof (short) ) ;
Сопsо1е.WritеLiпе("Длина int равна (О}.", Siz8of(int));
Conso1e. Wri teLine ("Длина long равна (О}.". sizeof (long) ) ;
Сопsо1е.WritеLiле("Длина MyValueType равна (о}.",
sizeof(MyVa1ueType));

ИСХОДНl.IЙ КОД. Проект UnsafeCode размещен в подкаталоге, соответствующем таве 9.


Глава.9. Сmщиальные приемы построения типов 409

Директивы препроцессора С#
Подобцо многим дрyrим яаli!ltaМ йз семейства С. 11 С# IЩЦдер~щотся Ра3ЛИЧ.
ные сим:вoлы. по:mол.monще влиять на процесс комnищщии. Перед рассмотрением
директив препроцеесора С# оогласуем соответствующую терМИНОJ,lЩ'JDO. ТерМИН
·директива прenроцессора С#" не ВI1олне точен. Фавтически этот тepмw- ИСПQJIЬ­
зуeтcJ'J тo.цыto J1)IЯ согласованности с ЛЗЫI!Э.МИ npограммиравакия: С. и С-н-. ~ С.#
нет CYJДелЬного щага препроцессара. ДИреltТИВЫ преnpо.цессора :в С# Яв.wnG'N~ со­
crcuщDЙ частью npоц~сеа лексического ана.ilяаа ltОМlIИJIЯТОРа.
Так или ~ИБаче. СИНТЮ<СИС диреRТИВ препро:qесСора С# очень .похож 'На сцнтак­
сие сортветс:myJOЩИХ диреlt1'ИВ OCTaJIhНЫX члеltов семейства С в ТОМ,· ЧТР эm Д1'I­
ре'R:ТИВЫ всегда имеют префlWс, обозначенный знаНОМ «диез" (#). -в таб.л.9.4 ори­
сан,Ы некоторые из наибоЛ!~е часто используемых диреН<rив (подробности можно
Щt.Й1'И в документации _NEТ Framework 2.0 ЭОК).
Таблица 9.4. ТИПИllные директивЫ nреПРО4ессора С#

Директивы

JI'.re.g:Lon., #endregion ИОПОDЬЭУЮТСfl Д!lЯ оБО3f18Чения раэделO!J С'Т'ЯГli\ваемоl"О и.СХО~НОГО f<.oда


#define, #undef ИCf1011Ь3УЩСБ.для ОI1РеделеНИf! и отмен\>! оrтреДe1fеНИЯ симВолов
УСЛ0_ВНОЙ компиляцlill1

1Н Е, #eJ.if. #e l se~ 14спол6зуютс:я ДЛЯ условного ПРОilусkЗ. разделов и'сходноrp кода {НЗ
.ffendif 'OCffOBe указанных СUМIЮЛО8 компиляции}

Раздел" п' рограммного кода


ВоЗМQЖF:rО, QДНОЙ из сам;ых ПОЩ~3НВIX .дире:ктив препроцеосора smляются Jl.rеgi.оп
и *endre;gi!.on. Используя ~ТИ признаки. пы Yl'8зываете блок программного кода.
который можно <;кр.ыть от щюсмотра И иде!lТИфИЦИРОВЮ'Ь йнфор..'1\ШРУЮЩИМ тек­
croвым MapKepo~. ИСllОДЬЗ0ванме разделов программноro кода :может упрОСТИть
обалуЖlma:Eme бощ.:ищх файлов *'. C:S. Можно, :аапример. co~aTЪ ОДИН раздел длн
IroRструкторов типа. другой - для свойств .Iii .. Д.

cla's~ Car
(
priv ate st rlng pe tName;
priv:ate ifJ t Ctlr.r gp i
#reqion Соns truc.t :;ors
рмН 'с Паr ()
{ .. - }
риыic C<J,r Ca r (l.n,: c-u:t rSр , s t :ring petlЫame )
( ... .1
#enliregion
'#:r:8gion Рrореrti8З
public iц t Sp.eed
i ... }
public' string Name
! . -. J
ftещ:1regiоn
}
41 О ЧаСТЬ 11. Язык программирования С#

При помещении YI\aзателя мыши на маркер CBepнyroгo раздела вы получите сни­


мок программного кода. спрятанного за соответствующим названием (рис. 9.5).

pr.l.v.. "" ~~ri:ng ~t;N_:1


private ~nt currSp;

l' ,

Рис. 9.5. Разделы программного кода за работой

Условная компиляция
Другой пакет директив npепроцессора (Hf. #elif. #else. #endif) позволяет
выполнить КОМIIИЛ.ЯЦИIO блока программного кода по условию. базируясь на npед­
варительно 3эдaнных: символах. Классическим вариантом использования этих ди­
ректив ЯВJIяется идентификация блока программного кода. который .компилирует­
сл только npи отладке (а не npи окончательной компоновке).

class Program
(
static void Main(string[] args)
I
/ / Эorоor прогрaммJDolЙ ход :ВiШo.JlИR8'1'СIII '1'оn.хо при О'1'nадочной
/ / ХОНПИnIllЦИИ ПРО8Хor&.
Jlif DEBUG
Console. Wri teLine ("Каталог приложения: (О}",
Environment.CurrentDirectory);
Console. Wri teLine ("Блок: (О}",
Environment.MachineName);
Console.WriteLine("OC: (О}",
Environment.OSVersion);
Сопsоlе.WritеLiпе("Версия .NET: {О}",
Environment.Version);
#endif

Здесь выполняется проверка на символ DEBUG. Если он присутствует. выводится


ряд данных состояния. для чего используются соответствующие статические члены

класса System.Environment. Если символ DEBUG не обнаружен. то црограммный


код. размещенный между #if и # endif, комnилироваться не будет и в результиру­
ющий компоновочный блок не войдет. Т.е. будет фактически проигнорирован.
Глава 9. СnециаЛЫiые npиемы ПЩ:l:poSния П1nОВ 411
ПО умолчанию VJsua1 $tudJo 2005 всегда оnpeдeлнет CЙМЩJ:Л DEB1JG. однаш:> такое
ПDведение можно отменить :ny'Ii'eM сннrия отметки фл3.$tШ Define DmUG сопstалt
(Определить :кoнeтamy DEBUG) па ВЮlэдке Вшld (Сборка). раэ.М:ещеннойна страв:и­
Ц~ Pro,pertles (Св0йства) вашего npоекта. В прeдnоложен.пи о том. -.rro этоТ обычно
:генерируемый СНМВOJI DЕЕЮG отКлючен, можНо OIIpед~ этот символ для каждого
фа:йлав отде:лъности, нсполъауя.ЦИре«тИву npenpоцессора Jtdвfiле.

#define DEBU.G
usi rщ sуэ t:em;
П3Ш6sрасе Preproces18or
1
clagg ProcessМe

g'ta'tia '1010. Main !string]] args')


{
/I Прогрll.NJlИNЙ КОА, по,цоБИiiIЙ похasаиноиу ВioJIIIВ. , •

'Замечание. Директивы #denne в фаЙле С программным КОДОМ с# должны быт,ь указанbl до всех
остщlbl-lых.

Можн'о таюке определять СВОИ собстэеиные сJ-Jмволы прenpоцессора. Предпо­


ложим, например. что у нас есть класс С#, который должен kомшши:роватьсн не­
МНОГО иначе в рамках дистрибутива Моno .NEТ (см. rnaвyl). Исполь~ #de Hп€:,
МОЖНО оnpеделить символ MONO j3UlLD для каждоrо файла.

#defipe QЕВ1Щ
#defiR6 MONO BV1LD

llsiHg System;

петеБрасе Preproee'Ssor
I
cla,ss Рт'очrаm
[
stat;lc vQ·ipMa'in (string[J агчв)
{
tif МЩЮ ВЩLD
Console. Wri teLi'&€ ("Ко=rВ:ЦИА ДI1Я Мод",) ~ ") I
#'else
СаП1;юl е, wri teLine ("КОМI1ИЛ$ЩИЛ ДЛЯ l{lСГОЭ,Qft. • ЫЕТ") ;
#endif

Чroбы: .создать сш.mо.it. прнмеIiИМblЙ ДЛfl всего проекта. И(.'ПОЛЬ3уИте 'Т'еRСТОВЫЙ


блок Сопditiоna! соmрНаtlопsуmЬоls (Символы ус.iЮВНОЙ ROМIIИJISЦИйJ, размещенный
на ймадке BuiJcI (Сбориа) страницы свойСтв npоекта (рис. 9.6).
412 Часть 11. Язык nрограммирования С#

GSW!1II ' -'----,.io'~-,,_._-- . -


-' ~' - -'- --_",,",:- .. - --- --- -~ --. ,------·~4:

J lIUid Events
CardtINI:O~~i"" ~-=~~_~- __ -------.J 11 I
~ Deht1EllUG ~
0DehlR6CE-
г. :: ---'-c--- -~--
. ~
~CpU .~I

.. . .

Рис. 9.6. Определение символа препроцессора для применения в рамках всего проеkТа

Резюме
Целью этой главы является более глубокое изучение возможностей языка про­
граммирования С#. IЛава началась с обсуждения ряда достаточно сложных кон­
струкций программирования (методов индексатора. перегруженных операций и
пользовательских подпрограмм преобразования). Затем был рассмотрен небольшой
набор не слишком широко известных ключевых слов (таких. как sizeof. checked.
unsafe и т.д.). обсуждение которых естественно ПРИВeJ-IO к рассмотрению вопросов
непосредственной работы с типами указателя. При исследовании типов указателя
было показано, что в подавляющем Болыlшнтвеe приложений С# для использова­
ния типов указателя нет никакой необходимости.
ГЛАВА 10
Обобщения

С ПОJIВJIением .NEТ 2,0 язык проrраммировl:U!Wi С# стал по;цдерживать новую


. UOЗJv.[ож:но(.'тъ
обоб~нЩL>IЩ.
c1'8 rConunon '!УРе System ~ общая СИС1'm4а nшoп). назuattнyю
(generiC$). Уnрощernю говоря. обобщеmю обеспечивают проrра:мми.~
сту возможность определеl:ЩЯ "заПОЛНИТt.щеЙ" (формально .называемЬ!Х rшрQJИ~
mpaм;uwtnaj дляа:ргументов методов и оnpеделенFJЙ ТИПОIi. ~OTOpl!1e будут Roнкpe­
т.ЦЗщюваны во вpeМ/i вызова обо6щецного метода !!Щи.цри создании обобщеНflOl'О
ТИII8..

С ЦеЛЬЮ ИЛЛЮСТРЦЦИU этой новой возмощности язы:wa :мы начнем .главу с рас­
~отре:иия пространства :имеJ-l SуstеЛl.СоllесtiОnS .. GелеriС. PaCCMoтpe~ приме­
ры nOilJДсржJ{И обобщендй:в би5лиQтеRaX базо:въrx кдассов.в ОСТЭд,ьной части этой
rnавщ мы JЮIЩТаемСFI ВblSIСнить, KQR строить СВОИ собствtШные Qбобщеmm. членОВ.
RlIЗссов. C'I'JJY"R!ТYP. интерфейсов, и дeд~aTOB.

Снова о создании объектных образов,


восстановлении з,начений и System. Object
Чтобы ПОНЯ'rЪ. в чем эаюпочаютсн преимущества испольЗЬвания обобщеНJI:Й,
следует ВЬ1нсни1Ъ. f<aRИе проблемы во3llИКают у npограммиста без их иС!.-roльзо-­
ванин. Вы должны1 пОмюггь из главы З. что платформа .NEJТ подцерживает авто­
маrrиЧеCI!Qе преобразО13анне объектов стен:а и динамической памяти с ПОМО!ЦЫО
операций создания объектнorо образа и восстановления из объектного образа. На
п~рвEdЙ ВЗГЛЯД. эта ВОЭМО'nщоcrь кажБТСя не слишком полезной я имеющей, скорее.
теоретический. чем nPaICТичесний интерес. Но на саМОм ДeJfе возмоЖtIOСТЪ созда­
нщr объектного j)браза и восстановлени.я: из объейтноrо образа o'lfeНb полезна тем,
что она ПоЗвOJ,Яет интерпретировать все. Щll{ Sys-tеm.ОЬjесt, оставив все заботы а
распредеVIеuии памяти на усмотрение CLR.
Чтобы рассмотреть особerЩQСТИ процесса создания объектного образа. предпо­
ложим, что ММ создали System .C:ollectiO!1s . Array1i,g-t дnя хранения ~ИСлОВЫХ
(т.е. размещаемых з стеке) данных. Нац(!lJ\'nШМ. <по все члены Al-rауLiзt обладают
прототипами wm получeRИя И возвращения типов Syste1Тi.Object. Но :вместо тФГО,
чтобы 3acraБMTb программнета вручную BKnaд:blВaТЪ размещенное 11 СТбl1tе цеJIOе
число в соответст1;lJЮ~'IO оБЪектную обало'Щ.V. среда вьUюm-tеЮ1Я делает это авто­
матически с Щ}МоЩЬЮ операцци С03Дal-ll1Я об'Ье}tТНorо образа,
г

414 Часть 11 . Язык программирования С#

static void Main(string[) args)


{
11 При пер.дач • .цаивwx ч.певу, треБYIЦ8IIY о&.ех'1', д.п.
11 хараХ'1'еризуемых _наче_яни IJ!ИПОВ lUI'1'оиа'1'Ичесхи со_дае'1'С.
/1 об'Ъ8Х'1'1DIЙ обра_.
ArrayL i st mylnts = new ArrayList();
mylnts.Add(lO);
Сопs о lе.RеаdLiле()i

Чтобы восстановить значение из объекта ArrayList, используя индексатор


типа, вы должны превратить размещенный в динамической памяти объект в раз­
мещенное в стеке целое число. ИСПО1lЪЗуя операцию преобраэования.

static void Main(string[] args)

11 ЗнаЧeJШ8 ВОССШ&lla&JJИIIа.'1'С•... и снова С'1'&ИОВИ'1'с. о&.8х'1'о"!


Сопsоlе.WritеLiле("Значение вашего int: {О)",
(int)mylnts [О]);
Console.ReadLine() ;

дли представления операции соэдания объектного образа в терминах CIL ком­


пилятор С# испOJIЪЗyет блок Ьох. Точно так же операция восстановления из объект­
ного образа преобразуется в СIL-блок unbox. Вот соответствующий СIL-код p,1IЯ по­
казcumого выше метода Main () (этот код можно увидеть с помощью ildasm.exe) .
. method private hidebysig static void Main(string[) args) cil mалаgеd
I

Ьох [msсоrli.Ь]sуstem.IntЗ2
callvirt instance iпtЗ2 [mscorlib]
System.Collections.ArrayList: :Add(object)
рор

ldstr "Значение вашего int: {О f"


ldloc.O
ldc. i4. О
callvirt instance object [mscorlib)
Sуstеm.СОllесtiопs.АrrауList::gеt_Itеm(iпtЗ2)
unbox [mscorlib] Syst_. IntЭ2
ldind.14
Ьох [mscorlib] System. IпtЗ2
call void [mscorlib)System.Console::WriteLine(string, object)

Обратите внимание на то. что перед обращением к ArrayList.Add () разме­


щенное в стеке значение Sузtеm.IпtЗ2 преобразуется' в объеRТ, чтобы передать
требуемый System,Object. Также заметьте, что при чтении из АrrауLiзt с помо ­
щью индексатора типа (что отображается в скрытый метод get Item ()) объект
Sy st e m.O b je c t восстанавливается в Sузtеm.IпtЗ2 только для того, чтобы снова
стать объектным образом при передаче методу Сопзоlе . WritеLiпе ().

t
Глава 10. Обобщени!! 415

Проблемы создания' объектных


..,
образов
и восстановnения значении
Операции соз.цания объе~тных о&раЭQВ 1:I1Iqсстаповлеяия из = эначений
Qчень удобнw с ТОЧJGI зрении программиста • .ар ТЩQP;Й упрощенный подход при об­
мене элементами cTelЦl и ДИНal\lUfLIес}Wй Iщ.м.нТl-J им~ет Ьвоn .специфичесние пр6-
б,а~мы дроизво.дите.лъности И не rapaнтnpYe1' ТИПоВОЙ 6еэопа~ОL-ти. Чwбы ЛОRЯТЬ
цробл~ IIpOизводителыIOети, ращ:м.отрим Слf'дующие nШrи. :которые приходится
.eьmолиять при создании объе~ТlЩГQ Qбi>JlЗ~ JJ восст;шtшлеmш значен:ия обычного
целого числ-а.

1. HOВblJ1 объеlCТ JIyЖ.НО разме.СТИ1'J;,t в управляемой динамичес1,fОЙ r,ЩJ\UrYИ.

2. ЗнЗ'чение разме~ в стеКе Да1ЩЫХ нужно зamIсать в соответствующее


место в памяти.

З. При восстановлеНИи значения. сохраненного в объекте, размещенном 11 ди­


Нам:и'ЧесБО:И: ШlМ.Я'1'"Ц, это значение нy»mo cIlOBa вернуть 11 СТек.
4. Н<ЩСДОЛЪЗУемыИ объект 11 ynp~емой ,цина.мическоЙ памяти (В КOIще :КОН­
цов) дощкенбытъ yнIPiТOЖeН сборщи:ком мусора.

В нашей сиryации метод M.a in () с точки зренив. производиrreлънос'ТИ не иМ:€ет


ВИRaJmX проблем. но соОтветст.вyJOЩИе проблемы появятся. когца ArrayL:1st будет
содер)В3ть тысячи целых значений, если вашей npограмме придется регулярно их
обрабатьmатъ.
Теперь рассмотрим проблему отсутствии тиnов.ой безопасноёТИ в отношении
операции во.сстановления значений из объектного образа. Вы знаете, что ДUIH
:восСтановленИя вначени.а в раМюiх CШlтаксиса С# испБЛЫJyетсл оператор преоб­
разОВaншr. Но :каким буJ'(ет ато преобразование - успешным или неудачным, -
вылcRи"тх толыro В среде ВЫ1iOл.нtZнiIsL При поnъtI"ке восстановить значение в не­
пp::r8ИЛЫIЫЙ тип даиных. вы получите lпvаl idCastException.

s.ta:.tic iVoid Main (st:(ing [) args)


I

1/ ОА:! Ис~.1IIИ -реке _ _ OIЦIеlDUl!


C0nso·, le •.Wri teLine ("Значение ваше:го int J I О' ]",
(ShOI't) myln t2 ["01 ) j

Co'n sole .ReadLine (') ;


)

в идеальной СИ-ryэции: коыrnшятoр С# должен решать проблемы lIeROppeIOНЫX


операций восст8.НOШIeни.я иэ объe:ктяorо Wpaза во время компиляции, а не Е среде
вЫn@ЛНеыия. В связи с ЭТИМ. в дейсmвl1meJtыю идеа.пъноЙ СИIYации. можно было
бы сохрапитъ типы. характеризуемые значениями. в ъюнгеЙReре. который не тре­
бовал бы преобраэования в объект. В ,NEТ 2.0 обобщения даЮТ р·еmени:е именно
эrИx пробл.ем. Однако перед тем как углуб1'tТЬСЯ В детали испоJiЬзования обобще­
НИй, давайте посмотрим. КaIC програм:мис'I"Ы пытлисьь бороться с эТИМИ' npобllема­
ми в .NEТ l.x. с помощью cтporo ТИI11IЗО.вamtt.IX холлеКn;ИЙ.
416 Часть 11, Язык программироваНИR С#

Типовая безопасность и строго типизованные коллекции


в мире .NEТ, существовавшем до появления версии 2.0, программисты попы­
тались решить проблемы типовой безопасности с помощью построения пользо­
вательских строго типизованных коллекций. Для npимера предположим, что вы
хотите создать пользовательскую коллекцию. которая сможет содержать только

объекты типа Person (персона).

public class Person


{
11 Опр8дen8НЫ O'1'Iq)~ дп. ПРОС'.t'о'1'lol.
public int currAge:
public string fName, lName;
public Person() {I
public Person (string f'irstName, string lastName, int age)
{
currAge = age;
fName firstName;
lName = lastName:

public override string ToString()


I
return striпg.Fоrтаt("Возраст {Ol, {l} равен {21",
lName, fName, currAge);

Чтобы построить коллеIi:ЦИЮ персон, можно определить член-переменную


System.Collections.ArrayList в рамках класса PeopleCollection и настроить
все члены на работу со строго типизованными объектами Person. а не с общими
объектами System.Object.
public class PeopleCollection : IEnumerable
{
private ArrayList arPeople = new ArrayList();
public PeopleCollection() {}
11 Ореобраsоваиие дп,. JiIo1Slo1Ващей стороны.
public Person GetPerson(int pos)
{ return (Person)arPeople[pos]; )
11 вст_ха '1'onыto '1'ИDов P8rson.
public void AddPerson(Person р)
( arPeople.Add(p); }
public void ClearPeople()
{ arPeople.Clear(); }
public int Count
{ get { return arPeople.Count; I f
11 Ооддерака foreach НУМ8Р&'1'ора.
IEnumerator IEnumerable.GetEnumerator()
{ return arPeople.GetEnumerator(); }
,
I
Глава 100 Обобщения 411
о Т8I(ИМИ оцределении:ми NШО:В вы будете уверены в тшшвой безщтаоности, по­
('КQJIId(Y' теперь ко:мmшятор 0# сможет распоэ:нат:ь JПOбую ЦОПЫ1'1(}' ВСl'авпи непод­
xoдmцero типа.

static void Main'(string [] CI:r=gs)


{
Сощ;оlе. Wri teLine ("**'*'" * С1J,stОПI Person Coll€'ction ***"" -1< \n"):
РеорlеСфllесtiоп 1!1yPeople = new: РеэрlеСоllесtiоп {);
туРеорlе. P,d'dPerSOIl (new Person ('''Нomех'' , ~Simpsou", 4'0));
шуl1еорlе. AddPerson (new Рerви) ('''Ма,[че'', n Б i.mрs оп 11 , 3'8} 1;
roYPeople.AddPersQn (:new Person (J'Li$a~, "simpson", 9»;
,туРеор,1 е. AddPersorl {l1ЩW P.erson ("Bart". 11 Sirnpson ", 7)) I
myPeople. Add!?erson (new Рехвоп ("Magg-le" I ., !Hmpson" I :2)):
11 Эlt'о прИа8де'1' k O1IIИ6хе ltоlШИmЩJo«!
щуРеорIе. A,ddPerso!) (new Car () ) I
foreacЪ (РехэЬп рin IIIYPBople)
Console.WriteLine(p);
Со!Н;оlе .l\e~dLine () ;

ХОТЯ по.ilЬ30вательские :КО.iJГ.teIЩИИ и гараНТИРУЮJ' типовую беэопаснос'l'Ъ, при


ИСПdЛЬЗОВании этого подхода прихо~ится со:щэва-n. новые (и почти идентичньrе)
iJOЛЬЗОвa'ТeJThCюre ROЛЛекциид.1JЯ Ka~KДOГO из соответствующих ТИПОВ. поэтому еCJIИ
вам п:она..цобится п:ол:ьзователъскаа: коллеRЦJL<l, IЮТорая ДОJlЖна работать только
с массами, ям:moщимися произвоДНblМИ бэзовorо кда.сса Car (автомобиль). вам
придется построить очень похожий тип.

public сlазs. CarCollet:tion: IEnwnerable


{
priva:te 1\;rrayList arCars = ,new A.rrayList (") ;
риЬНс La.r:CollectioD () ( J

11 Прео~раао. ___ ~ ВКЗ10lllащей C'.1'OPOJDt.


pUblic Car GetCar(in± pos)
( ret.urn (Car) arCal:S [pOs] i }

11 Вставка wGDWto 'It'ИПOa Car.


public void AddCar (Саг С)
! cll'Car 1> ,Add Се); "

public. VQ~d Clea.r Cars ()


{ arC,aI5,Clear(}; J
publio iflt Count
{ getl return arCars. СоицЦ 1 }
11 Поддероа. foreach ~pa'1!opa •
.IЕ1nшnегаtог
IEnumera'ple. GеtЕпчrnегаt;ог ()
{ return arCars. GеtЕпurnе:tаtюг (); }

Бы:. навеРEJOе,ЗЩle1.'е ЩI CB!)er(I еООствешщго· Q.IIЪЦ'Э. ЧТО цраце"с создЭЕИЯ мно­


ж~стаа строго ТИJ;Щ30Вaщ:rыx RОЛЛI':КЦИЙ дJlЯ учетfl. разщiчны.х цщо:в' явллетС.я
r

418 Часть 11. Язык программирования С#

не только трудоемким, но просто кошмарным для последующего обслуживания.


ОбобщеЮIЫе КОЛЛeIЩИИ позволmoт Отложить указание спецификации содержаще­
гося типа до времени создания. Пока что не слишком беспокойтесь о синтаксиче­
ских деталях. Рассмотрите следУЮЩИЙ программный код, в котором используется
обобщенный класс с именем System.Collections.Generic.List<> для создания
двух контейнерных объектов, обеспечивающих типовую безопасность.

static void Main(string[] args)


{
/ / ИCUOJIЪsование обобщенно%'О 'l!ИПа List ТOJ1ЬJCо Дnll Person.
List<Person> morePeople = new List<Person>(};
morePeople.Add(new Person(});

/ / Иcuо.nьsование обобщенного типа List тоnьхо Дn. Car.


List<Car> moreCars = new List<Car>();
11 о.иБJCа JCОИПИnIlЦИИ!
moreCars.Add(new Person(»;

Проблемы создания объеКТНblХ образов


и строго типизованные коллекции

Строго типизованные коллекции можно найти в библиотеках базовых классов


.NEТ, и это очень полезные программные конструкции. Однако эти пользователь­
ские контейнеры мало помогают в решении проблем создания объектньrx образов.
Даже если вы создадите пользовательскую коллекцию с именем IntCollection,
предназначенную для работы только с типами даннъхх Sуstеrn.lпtЗ2, вам при­
дется создать объект некоторого типа для хранения самих данных (System.Array,
System.Collections.ArrayList и т. п.).

public class IntCollection : IEnumerable


{
private ArrayList arlnts = new ArrayList();
public IntCollection() { }
/ / ВОСС'1!аиовnе_е sначенюr Дnll ВIoIЗuва.щей С'1!ороиы.
public int Getlnt(int pos) ( return (int)arlnts[pos];
/ / ОаеРёЩИR соsд8.НИR об'li8JC'1!RОГО образа!
public void Addlnt(int i)
( arlnts.Add(i); }
public void Clearlnts()
I arlnts.Clear(); f

public int Count


{ get { return arlnts. Counti } }
IEnumerator IEnumerable.GetEnumerator()
{ return arlnts.GetEnumerator(); }
1 Глава 10. Обобщения 41'9
Вне зав,ilСJD/lОСТИ оттОго, канОЙ 'I'Иil :въt выберете .цnв XJ>attения целых чисел
(Syst~.Array. Syste.m.CoJ.lectiOn6.AIIayList И т:п.). вы не сможете 'ИЗбавиться
00: р.роб~мы .NEТ 1.1. связавно~ с создаиием объеатншх образов. Anpyднo дога­
дэ,1'I:!C9.. чтозд~ снова на помощь приходят ,обобщёния. В сл.eдyIOщем фрагМe:ttre
цpoгpa1dМJloгo кода тип System.Cellect ions .Gene!: ic. List<> используется для
создания ~QtI'ТeЙнера целых~еЛ. не имеющm"O проблем СОЗДIIЮIЯ объекmыx 'об­
ра:зо~ и восстаповления. значениИ при вставке и получении ТИПОВ харахтеризуе­
мыxиач~• .

s t.atic void мain '( st:ring [) args)


{
/1 S-s cos~ __ О&мucшIlОРО ОбрU.'
Lis"t<.int> .mylnts = ne,w- List<lnt> () ;
!П.уI~1t~ . Ad'd (5) ;

11 в.. 80CCS<_O.8JItnDOl .sвaЧ81ОО1!


irtt i = royIn'ts [ О i ~

ПРQСТО в качестВе подтверждения рассмотрите следующий CIL-KOA для; зrn)l'O


щ:тода Маiи () (,обратwre ВНИмaime.на отсутствие в нем К3RИХ бы то ни был,о бяо­
щ)в Ьох И UnЬОХ},

.meth'0Q private hidebysig static V'oid Мain(stringI1 argsJ cil manag:ed


I
.entr:ypoint
/ / Code sit~ 24 (Ох18}
.ща)'s!;.асk 2
.locals init (10] сlаsз
LЩSСQr1iЬ1 System_
. Col1ecHo,n s .Generic.List ' l<int32> J;!IyIntB,
(1) irJtЗ'2 i)
н_оаоо: ЛQР
IL_OOOl: pewDbj iлstа~се void class
[msc":clib] System. Collec't ions. Generic. List . l<fnt.n>: : .ctpr О
IL 00'0 6.: stioc.O
IL_QD07; ldl.oc. О
I1 0008: ldc. 14 • S
11,_0009: callvirt in.stan:ce void сlаз,э
[.m.эСО'I:lib ]'SySDem •.Callecticms. Generlc. L:ist' 1 <lпtЗ2>: : Add ( ! О)'
ILOOOe: П0Р
IL оо-ы: ld1Dc. О
IL 0010: ldc.i4.0
11,_0011 ~ callvirt irlstance! а cla',ss
[mscorlib] System.. Col1-есtiOп.s. Generic .List' 1 <iJ'1't-З2>: :get _I_tem! in·t 32)
11 ,0 016: stl'Oc.l
tr; 0017': ret
} f I e!~д of method I'r:og:llam: :Мюn
Теперь, .ког,ца вы имеете nyчmее представление о роли обобщeюrii в .NEТ2.0~
мы С 'ВаМИ ГОТОВЫ углубиться '6 детали. Дли начa.na. мы формально paCC-м'ОIJ)JЩ цро­
crpaнc1'ВO имен Syst'em.Callec't ions .Gener:lc.

1
-
420 Часть 11. s:lзык программирования С#

ИСХОДIlЫЙ КОД. Проект CustomNonGenericCollection размещен в подкаталоге, соответствующем


главе 10.

Пространство имен System.Collections.Generic


ОбобщеННЬJе ТИПЬJ присутствуют во МНОГИХ библиотеках базовЬJХ классов
.NEТ2.0. но пространство имен System.Collections.Generic буквально наполне­
но ими (что вполне соответствует его названию). Подобно своему ~родственнику~
без обобщений (System.Collections), пространство имен System.Collections.
Generic содержит множество типов класса и интерфейса. что позволяет вклады­
вать элементЬJ в caмЬJe разные контейнеры. Совсем не удивительно. что обобщен­
ные интерфейсы имитируют соответствующие необобщенные типы из простран­
ства имен System.Collections.
• ICollection<T>
• IComparer<T>
• IDictionary<K, V>
• IEnumerable<T>
• IEnumerator<T>
• IList<T>

Замечание. По соглашению для обобщенных типов их замещаемые параметры обозначаются бук­


вами верхнего регистра. И хотя здесь допустимо использовать любые буквы (или слова), обыч­
но используют Т ДЛЯ обозначения типов, К - для ключей, а V- для значений.

в пространстве имен System.Collections.Generic определяется и ряд классов.


реализующих многие из этих ключевых интерфейсов. В табл. 10.1 представлены
описания базовых типов класса из этого пространства имен. реализуемые ими ин­
терфейсЬJ и соответствующие типы из пространства имен System.Collections.
В пространстве имен System.Collections.Generic также определяется це­
лый ряд ·вспомогательнЬJX~ классов и структур для работы с конкретными кон­
тейнерами. Например , тип LinkedListNode<T> представляет узел в обобщенном
LinkedList<T>, исключение KeyNotFoundException возникает при попытке до­
С'I}'Па к элементу контейнера снесуществующим КJПочом и т.д.
Как видно из табл. 10. 1, многие обобщенные классы коллекции имеют необоб­
щеШIЫе аналоги в пространстве имен System.Collections (иногда даже с одина­
ковыми именами). В главе 7 было показано, как работать с такими необобщенны­
ми типами, поэтому дальше не предполагается рассматривать все их обобщенные
"дубликаты~. мы рассмотрим только List<T>, чтобы проиллюстрироватъ приемы
использования обобщений. Если вам нужны подробности о других элементах про­
странства имен System.Collections.Generic. обратитесь к документации .NEТ
Frarnework 2.0.
1 Глака 10. Обобщения. 421
Т8блИIi&810.1. Классы System.Col1ections.Generic
Н'еобобщеинWi aнanor
Qбc)бщeннwА klf8CC Оп"санме
• System.Collectlons
CQlle.ctian<T" Collect iOn8q·s e 'База дпя обобщенной коллекции

Comparer<r> С ompa.tех В.ыПОJ1няе-r сравнение двух обс~­


щеfll'lЫ;( объектов

Di~tion,ary<K, v> HaSl'ltable Обобщенная комеlЩИЯ пар имеН>


и значений

blst<T> ArrayList СmlСОI( элементое С, динаМИ"Iе­


сI<И i1зменяемыми размерами

Queue<T~ ОбобщеЮiЭЯ реaJ1ИЗЗЦИЯ списка


FlFO (ДИСЦИПJ:1ина обсnУЖИВ<UiиЯ
тиrтэ"сatередь·')

Sorte.d'Dictibnary<K, У> SortedL:lst Обобщенная реаяизация со­


РТИРОliJаН~loro набора т1~p иМен и
З/iд'lений

Stack Обобщенная реализаЦия списка


UFO (ДМСЦИТlJ1ина обслуживания
типа ·стек" '
Linkecl'bls.t <T> Обобщенная реализация АВУС­
ВЯЗНОГО ОПИСk8

Readl)n1YCOllectiOrI<T> ReadOnlyCollectionBase ОБQБщеГlная' реал~аЦ\olq набора


З119МеНТ08 ТОЛЬКо для 'IТВIiИЯ

Тип Llst<T>
подобно неОбобщенным классам, обобщенцые· lPlaccpr Явллioтс:я объектами,
размещаемыми» ДИIJзмической памити. ПОЭ'Fо~ дди ~ сЛедует испальзt>вa::rь
neW со всеми йеобходю4blМИ ар.ryментам:и конструктора. Кроме того. вы должны
укзза1'Ь ТИIIЫ:. замещающие naра:ме1'\РЫ. оnpедеШЩJЩlе обобщеFШЫМ типом. ТaIl.
ДЛJI Systero.CollectiQ!1s.. GeneriC:. L± st<Y.> требуется указать одно значение, за­
дающее вид элемента, с иоторьrм: будет фymщир}щроватъ Ыв t <Т> . например, что·
бы соз-дать три объеltТВ List<> A1lR хранеJiШ[ Це!Щ1Х .:щсел, 1 объектов Эроr. t эСаг и,
объеКТОВ Регsоп. B~ ДOJDIЩJi[ записать следующеt.

Etatic vpiti Main (stxing r 1 args)


t
11 'Соад••'1'с.Ц.st АП.• ЖР _ _ цemrx IilИСan.
Li,st<i i!t;> mylnts = new List <lnt> () ;

11 со."il.М'а8 Li.8t дп. ~.~ обlи1c'1'ОВ Spox:t8Ca1:.


tist <SportsCar> myE:a:rs = new List<Spor tsca.r> (1 <
/I Coa~""'CJI Li.t , дm1 .xpa1I8JIJIJt o~o. Pers6n.
L i st<Реra;ол> myPeople = new r,ist<J?erso,n::. О :
)
422 Часть 11. Язык nрограммировЮiИЯ С#

в этот момент вы можете DОШ:lтересоваться, что же на самом деле становит­


ся значением заполнителя. Открыв окно определения программного кода в Visual
Studio 2005 (см. rлаву 2). вы увидите. что везде в определении типа List<T> ис­
пользуется заполнитель Т. Ниже показана часть соответствующего листинга (об­
ратите внимание на элементы, выделенные полужирным шрифтом).

I/ Час~ JlИсmинга д.п:. типа List<Т>.


namespace System.Co11ections.Generic
(
public c1ass List <T> :
IList<T> I 1Co11 ectiO!1<T> , IEnumerable<T>,
IList, 1Co11ection, IEnumerable
!

public void Add(T item);


public IList<T> AsReadOn1y();
public int BinarySearch(T item);
public boo1 Contains(T item) i
public void СоруТо (Т [] а rray) ;
public int Find1ndex(System.Predicate<T> match):
public Т FindLast (System. Predicate<T> match):
public boo1 Remove (Т item) ;
public int RemoveA11(System.Predicate <T> match);
public Т [] ТоАпау () ;
public bool TrueForAl1(System.Predicate<T> match);
public Т this[int index] I get; set; )

Когда вы создаете тип List<T> и указываете для него SportsCar, это эквива­
лентно следующему определенюо типа List<T>.
паmеэрасе System.Co11ections.Generic
{
publiC c1ass List<SportsCar> :
IList<SportsCar>, ICol1ection<SportsCar> , IEnumerable<SportsCar> ,
1List, 1Co11ection, IEnumerable
{

public void Add(SportsCar item);


public IList <SportsCar> AsReadOn1y():
public int BinarySearch(SportsCar item):
public bool Contains(SportsCar item);
public void CopyTo(SportsCar[] array);
public int Findlndex(System.Predicate<SportsCar> match);
public SportsCar FindLast (System. Predicate<SportsCar> match.);
public boo1 Remove(SportsCar item);
public int RemoveA1l(System.Predicate<SportsCar> match);
public SportsCar [] ToArray();
public bool TrueForAll(System.Predicate <SportsCar> match);
public SportsCar this[int index] { get; set; }
Гnзв.а 10. обобщения 423
Конечно. 1<огда ):1Ы создаe-re обобщенный List<'l''>.. нелъза:.сказа.ть. что :компи·
.JIJIТOp букВaлblIо создает совершешrо ВQВУЮ рецл:из.ацию типа List<T>. Он обра­
щается TOJIЬКQ к теМ членам обобщенноro· 't'ИЩi. КОТОрые ВЪt вызываете фa.f(ТИче­
СКИ. Чтобыпоясшлъ ЭТО. цре.дnолО.1НИМ. ЧТОБЫ используете List<'I'> Д1tЯ объе1аО):1
SportsCar так.
st.at ic '\101d Маiц {s'tri:ng []a.rgS)
(
/I IIp01aeFU, Liat, сюдepw-.Х'О о&.ека Sport8СаХ8.
I..ist<Sроrt.sба. r> my.CaI:S = new List<;Sp.ortsCar> ()';
myCars ,·Add (new S'portsCar () ) ;
соrцюlе.. Writ,еL..iпе -(,'Уоих l.ist contains 10). " .,
myCa..rs.• C01Jnt) ;
}

Если с ПОМOЩblQ i:ldasm.exe проверить геиерируеМhТЙ СIL-код. обнаружатся


ФIедуТ()tцие подставовки .

•methQd pr':ivate hid.ehysig static void Main (string [] args) c;:i];man!'lg<;!d


-{
. entr1Poin·t
.maxstack 2
.lp.cals init ([О) class (II!St:orlib]
Syst,em,C911 eot:ions,Ge.пeric.'List'l'
<c.lass Spor1:8C'a.t> ro.yCars \
ne.wobj ins·tano:::e v()id ~lass [,ros'corlib]
Systero.C:;olleotions .Gелеtiс. 'List '1'
<class SpOruCar>~:' . .ct.or ()
stloc _О ldl0C. О
n~wo:bj instance void ColleetiO.tlG.e nerics, S:роrt5Саг: : , O<tqr ()
callvirt instanc:e void <;:la$s [rnзСОl-liЬ]
Systero .Ccllectio!1s . Ge'n e·ric. 'List' 1 '
<clas.s SportsCar>: : Add: (! О)
пор

lds'tJ! "Уои!" LiS't contains· jO} item(5)."


ldlыс. О call vi1!L instance int3,z с;1аs"э
[mscorlib) system.Co1):eccti"ns.Generic. 'List ' l '
<class SportsCa~>:;get_CoUilt ()
Ьох [msoorlib JSystem. IлtЗ2
с.аll vold [mscorlДop) SУЭЕет, Console ~ : W.r:iteLine (str ing, ooj ect)
л"'р
ret

Теп~ръ. ПOOJ.1е изучения процеctа 'Использовании Qообщениых тищJВ из бjmлио­


теJ(баэовЬ!IX шmСcGв. ·в оставmейся чacтrt главы :мы paccмoтpnм ВQПРОСЫ создaюiя
обоощенн:ых методов. тmюв и tюллеIЩИЙ.
424 Часть 11. Язык программирования С#

Создание обобщенных методов


Чтобы научиться интегрировать обобщения в npоекты, мы начнем с простого
примера обычной подпрограммы СВОIIИНГа. Целью этого примера является постро­
ение метода обмена, RОТОРЫЙ сможет работать с любыми типами данных (характе­
ризуемыми значениями или ссылками), используя для этого один параметр типа.
В силу самой природы алгоритмов свопинrа входные параметры будут посылаться
по СCЫЛRе (с помощью ключевого слова С# refJ. Вот соответствующая полная ре­
ализация.

/ / Э'1'О'1' ие'1'ОД переС'1'UПRет тоб_ два эпеиеИ'1'а,


/ / опредenеlUUlе параи8'1'рОИ типа <Т>.
8tatic void Swap<T> (ref Т а, ref Т Ь)
{
Сопэоlе. Wri teLine ("Методу Swap () передано {О)" I

typeof(T»,
т ternp;

temp = а;
а = Ь;
Ь = temp;

Обратите внимание на то. что обобщенный метод определяется с помощью


указания параметра типа, размещаемого после имени метода, но перед списком

параметров. Здесь вы заявляете, что метод Swap () может работать с любыми дву­
мя параметрами типа <Т>. просто для информации вы выводите имя типа соот­
ветствующего заменителя на консоль с помощью оператора С# typeof(). Теперь
рассмотрите следующий метод Main (), в котором происходит обмен между цело­
численными и строковыми типами.

5tatic void Main(5tring[] arg5)


(
COn501e,WriteLine("***** Забавы с обобщениями *"***\п");

// Обмен и_ду ,цвуии ЦemDIИ.


int а = 10, Ь = 90;
СОП501е.WritеLiпе("До обмена: {О}, (1}", а, Ь);
Swap<int> (ref а, ref Ь):
СОП501е.Wri teLine ("После обмена: {О}, (1}", а, Ь);
COn501e.WriteLine();
// Обмен ие-.цу ,цвуии строхами.
string 51 = "Не110", 82 = "There";
СОП501е.WritеLiпе("До обмена: {О} (1}!", 51, 82);
Swap<string> (ref 51, ref 52);
СОП501е.WritеLiпе("После обмена: (О) (1) !", 81, 52),
COn501e.ReadLine();
r
·i

Глава 10. Обобщени.я 425'

Пропускпараметровтипа
I1ри вызове обобщенньц .методов. подQб~ 5wap<T>. у насесть возМОЖНость не
указъmarrъ параметр типа, lЮ ' Т~iЮ .в 1'ОМ с.лygaе, :коща обобщенный метод требу­
ет ygааания аРГУМСlIТОВ, ПОСIiОЛЪКУ 1'Orдa RОМЛWlЯТOР может ~в:ы.вснить" тип этих
аргументов па основе BBOДttмЫX паРa1loiетров. Например., можно neреставитъ два
'J'ИПа: Sуэtе1'!1.Вооlеа:n ТШ;:.

/ I ~'1'op dудет пpeдnопаrаlJ!Ь system. Вoolea.n.


Ьооl ы1 = true, Ь2 = fi'ilse;
СопsФlе.w:ritеLiщ~(II'др обмена: ·IO}t {1}", t,l, Ь 2 );
SW&p (:ref' ьЗ: I ref' Ь2) ;
СQnэоlе.WritеLinе("ПQСЩе обмена: (О), Il}'n, Ы, Ь2);

Но если, дапример, у вас есть обобЩe.IOn:iIЙ метод с именем DisplayBa.seClaSs<T>.


m: имеюIIЩЙ' ВXPДНЪJX лараме'rpОБ, ШlR ' П:оказаНо' ниже:

static "о1а DisplayBaseClass<T> ()


{
Con.s ole. Writ,e:Line ("Базо!3.ЫМ 'КЛа'С,СQМ 1[)} являетс>! 1 11}."',
typeof (Т). tП?SОf (·T').Ba.seType) ;

то пр:й nызове этоrо метода IIЫ ДОJ]',iJЩЫ указать параметрnша.

$tat.:i:c: void. Маlп(вt.ri.nЧ[] a:rgs)


{

// Еc.nx меТОД не иие" mt.pa-.ТPD_,


11 необходимо ух&Sil.'1'Ъ Пil.РPl8тр !NШ&.
Ь'LsрlауВа$sс: Lass чn't> (.J ;
DisplayB,aseCla,s s<,.st.ring:> () ;

1/ ODIибu. ХОНJ:lXnIlЦИИ!
J/ He'R aa.puc8'J1P08? ТОzjДз. .дomcеи бva .!tапо~f;ЩIo!
DisplayВa_eClass() i

Рис. 10.1. Обо(jщеКtiые методы в де'йствld\o1


426 Часть 11. Язык программирования С#

в данном случае обобщенные методы Swap<T> и DisplayBaseClass<T> были


определены в рамках объекта приложения (т.е. в рамках типа. определяющего
методMain (»). Если вы предпочтете определить эти члены в новом типе класса
(MyHelperClass). то должны записать следующее.
public class MyHelperC1ass
1
public static void Swap<T> (ref Т а, ref Т Ь)
(
Console. Wri teLine ("Методу Swap () передано {О}".
typeof(T));
т temp:

temp = а;
а '" Ь;
Ь = temp;

public static void DisplayBaseClass<T>()


{
сопsоlе.writеLiпе("Базовым классом (О) является: (l}.",
typeof(T), typeof(T) . ВаэеТуре) ;

Обратите внимание на то, что тип MyHelperClass сам по себе не SlВJIЯется обоб­
щенным. но определяет два обобщенных метода. Так или иначе, теперь. когда ме­
тоды Swap<T> и DisplayBaseClass<T> находятся в контексте нового типа класса.
при вызове их членов придется указать имя типа.

MyHelperClass.Swap<int>(ref а, ref Ь):

Наконец, обобщенные методы не обязаны быть статическими. Если бы Swap<T>


И DisplayBaseClass<T> были методами УРО8НЯ экземпляра. нужно было бы просто
создать ЭКЗемпляр MyHelperClass И вызвать их ИЗ объектной переменноЙ.

MyHelperClass с = new MyHelperClass() i


С. Swap<int> (ref а, ref Ь) ;

Создание обобщенных структур (и классов)


Теперь. когда вы понимаете, как определять и вызывать обобщенные методы,
давайте рассмотрим построение обобщенных структур (процедура построения
обобщенных классов оказывается аналогичной). Предположим. что мы построили
гибкую структуру Point, поддерживающую один параметр типа, который пред­
ставляет единицу хранения координат (х. у). Вызывающая сторона может созда­
вать типы Point<T> так.

/ / Point с иcnоа.80аави... int.


Point<int> р = new Point<int>(lO, 10);
/ / Point с иcnoa.80аани... double.
Point<double> р2 = new Point<double>(5.4, 3.3);
~
1 -----'--
,
,
Глава iО . Q60бщеНМR 427
ВОТ полное (jnpеделение Poimt<T>. ffеоБХQДИМ!)~:Нам для да.щ.цеЙШего aн~aa.

11 ОБО~_a:R ~1t!1'YPII :f?o.i,.nt.


риЫ а с slIuc t Foint <'T>
{
/ / Об.об,щевныа ~aRНМfl.
p.ri vate Т xPG's ,; p ,t i vai:e Т УРОЗ;

1/ OCSоdщeюadi x~c~yцop.
p ti'bl ic Po.±nt (Т' xVal r ' т yVal)
{
Х РО!'! = хУа .!:
YPeJS = yval;

1/ ОбобщеauЩe C80itC'1'В&.
pfiblj..c 'L' Х
{
.get { r~t\J;Ln .кРов: J
set { к?о:з ;= val~; J

public т )."
!
get { return yPos; }
se't ( уРО8 = vi:I ;Lue; .}

publi c Qve l" r i de striD9 Tostring (')


(
retUJ::nst ;!:ing.Fo.r:mat("[{OJ, fl}J", xPos r yPoS);

11 nepeyc!I!a!!io.~a ~~ей' со !lВа..eIIИJIКИ napaxe>rpa !1'ИП8.,


/I прив.'DIИИ ПО укo.n.&1IИКI.
publi c void Rё s E:tРоint (')
(
ХР,О'В = defa,u lt!T);
УРО5 = d'efaul t (Т) ;

Ключевое ело'вО default в обобщенном программнам коде


кэи видите, Point<T> щщодъзует параметр типа в оnpеделепии полей ,цBн'Iiых;
аргументов конструктора и е ()ЦР~~tЩИЯХ СВОЙСТВ'. 9бр~тите внимание на 'ГО', что
в~оба:ВОJ\ в переопределению TQSt:,ring, ~ обобщеIi.1U:Щ тип
Polnt<T> ьпределнет
МёТОД ResetPoi1it (), в I<OTOpOM испольауетел НОВЫЙ (щнтакСис .

1/ 1Cmoче,аое c.в~a~ I defaul Ь' а С, 200.5 Яали~CJI пер&:t'pY*еlUDlК.


/I При IIСП·Оm.зо.авии t1 обобщев:IODot: ОВО ~JtCI~IiИ.пSl8S! ~avaнкe,
Ij пар&.иe!I!p& !l!Ji(Па. прИИJOtаехое J;IO ~p.n.aюaD.
p1,1bl i c v o i d ResetPo 'ic.>.1:, ()
428 Часть 11. Язык программирования С#

xPos = default(T);
yPos = default(T);

в С# 2005 юпочевое слово default получило два значения. Кроме использова­


ния в конструнции switch, оно может использоваться для установки параметрам
типа значений, принятых по умолчанию. И это, очевидно, полезно, поскольку
обобщенный тип ничего заранее не знает о фактических замещающих значени­
ях и поэтому не может с безопасностью предполагать о том. каким должно быть
значение по умолчанию. Значения по умолчанию для параметра типа ЯВЛЯЮТСЯ
следующими.

• для числовых значений значением по умолчанию является О.

• для ссылочных типов значением по умолчанию является null.


• Поля струнтуры устанавливаются равными О (для типов, характеризуемых
значениями) или null (для ссылочных типов).

Для Point<T> вы можете непосредствецно установить кРоэ и УРОЭ равными


О. поскольку вполне безопасно предполагать, что вызывающая сторона будет по­
ставлять только числовые данные. Однако с помощью синтаксиса default(T) вы
можете сделать обобщецный ТШI более rиб.ким. В любом случае вы теперь можете
использовать методы Point<T> так.

static void Main(string[j args)


(
Console.WriteLine("***** Забавы с обобщениями *****\п");

// Point с испоn~аО8ание" int.


Point<int> р = new Po int<int>(10, 10);
Console.WriteLine("p . ToString()={O}", p.ToString());
р. ResetPoin t () ;
Console.WriteLine("p.ToString()={O}", p.ToString());
Console.WriteLine();
/ / Po.int с испоnъаО8аниек double.
Point <double> р2 = new Point<double> (5.4, 3.3);
Console. WriteLine ("р2 . ToString () = {О) 11, р2. ToString () ) ;
p2.ResetPoint();
Console. Wri teLine ("р2 . ToStriI1g () = (О) ", р2. ToString () ) ;
Console.WriteLine();
// Обмен двух Point.
Point<int> pointA = new Point<int>(50, 40);
Point<int> pointB = new Point<int>(543, 1);
Co nsole.WriteLine( "До обмена: (О}, {1}", pointA, pointB);
Swap<Point<int»( r ef pointA, ref pointB);
сопsоlе.WritеLiпе("После обмена: {О}, (1}", pointA, pointB);
Console.ReadLine();

Соответствующий вывод показан на рис. 10.2.


Fлава 10. ОбобщеН~А 429

Р"с. 10.2. ИСП(lЛЬЗОfil!iние ' обобщеНliоrо -r:иna Роlл t

Исхо.цнloIМ kl!lA. Проект Sil11pleGeflencs разм~щен в подкаталоге. COOT8B,-СТ8УlOщем rn~ве 10~

Создание пользовательских
обобщенных колnекций
Итак,. пространство имен Sy5tem.CQ11e'c tions.Generic предлагает множество
ТИПОВ, ПОЗВОЛЯЮЩИХ сщ!даватъ эффективные i«JнтеЙнеры. удовлетворяющие тре­
бованиям типовой безопасности. С учетом мноЖ€Cтва .ДОC'JYIIНЫX вариwnvв очеНЬ
вe.пИka веРОЯТIЮСТЬ того, -что в .NEТ2.0 У вас вообще не :воз:ютне:г :неоБХодимщ:ти
в ПОСТроеНИИ' пользователъоких lЩПОВ ноллекции. Тем не менее. чтобы по!tЭ.затъ.
как строится обобщецl'IЫЙ контеЩt~р, Jlшnей следyiOщеи задачей будет соэдание
обобхце~ого К:JJncca Rоллtпщи:и, который мы назовем CarCollect :i.on<T>.
ПодобlЮ созцанному выше необобщенному тиny СатСоЦ.е'сt ion, щ1ш новьш
вариант будет использовать, уже существующий тип КОЛЛeJЩИИ д.'IЯ хранещtя сво­
их элеМентов '(В' данном случае это L,i st<». Будет реа.тtи:зова:на. и подцержка дик­
лаfаr.еасЬ путем реализацlЩ Qбобщeшroго интерфейса IEnumerable<>. Ьбра'l"ИТt'
внимание на то. тrтo 1 Е n \l'Щ е r д ble < > расttm'ряет неоБОБщенный .w;rrерфеЙt1
IEnumerable. поэтому ~омmrnятор ожи.цаеrr. что вы реализуете две l'tерсЩ'l м:етuда
GеtЕдщnеra.tоr ( ). еот K~ может ВbJ'гЛffДеть соответствующая МQJWфИItaЦmI.

puЬHc cla8:3 Car.colleC't-i .оn<Т> : тЕдuIIlе, rа,ыl<т>>


[
priv-a.te List<T> ахСасэ = new List<'I'> () ;
p.u.blic Т Get.car (in,t ров)
I returл arCars [ров]; }

Pиblic void AddCqr (Т с)


.f a:rCa.rs .,J!Jdd (с); }
puыl ic vo:td Clea'tCars ()
(arC~r •. Clear(): }
ptlblic iHt Count
{ get { r-еturI1 arCars. Cbtlnt I .1 }
430 Часть 11. Язык программироваНИА С#

11 IEn\Dll8rable<Т> раCllИp•• ~
IEDUIII8rabl., ПОs'1'ОКУ
11 117880 peamuao. .o.n. об•••pc.r
G8tzn\Dll8ra tor () .
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{ return аrСаrз.GеtЕпumеrаtоr(); }
IEnumerator IEnumerable.GetEnumerator()
{ return arCars.GetEnumerator()j )

этот обновленный ТЮI CarCollection<T> можно использовать ТЗR.


statlc void Main (string [] args)
{
Console.WriteLine("* Пользовательская обобщенная коллекция *\п");

11 Соsдаии. кonиeхции O~K~O. Car.


CarCollection<Car> myCars = new CarCollection<Car>();
myCars.AddCar(new Car("Rusty", 20));
myCars.AddCar(new Car("Zippy", 90»;
foreach (Car с in myCars)
{
Console.WriteLine("PetName: {О), Speed: {l}",
c.PetName, c . Speed);

Console.ReadLine();
}

Здесь создается тип CarCollection<T>. который должен содержать только


тшIы Car. Снова заметим. что того же резу;шraта можно достичь и с помощью не­
посредствеиного использования ТШIа List<T>. ThaBным преимуществом данного
подхода является то. что теперь вы можете добавлять в CarCollectlon уникаль­
ные методы. делегирующие запросы к внутреfшему типу List<T>.

Установка ограничений ДЛЯ параметров


типа с помощью where
в настоЯlЦИЙ момент I<JIaCC CarCollection<T> привлекает нас только откры­
тыми методами с уникальными именами. Кроме того, пользователь объекта мо­
жет создать экземпляр CarCollection<T> и указать практически любоЙ параметр
типа.

11 Это CИJIIJ:IаКСИЧ.СIDI корреn.о, но 8Щ'.JIJIДJI'1' r


11 по ХРАЙНей и.ре, ~aввo ...
CarCollection<int> mylnts = new CarCollection<int>();
mylnts.AddCar(5);
mylnts.AddCar(ll);
Чтобы про иллюстрировать дрyryю форму типичного непредусмотренного ис­
пользования объекта. предположим. что вы создали два новых класса - SportsCar
(спортивная машина) и MiniVan (минивэн), - которые являются npоизводными от
Car.
I
"
гЛава. 10. обобщемия 431
publiccla:ss $PQrtsCal' : Сат
{
public S:portsCar (stl:incg р, iпt з)
: Ьаsе (р, 5) {/
11 ДОI!10Jl~ • •'J!OAМ ,цв. sportscar.
J
pu,blic ' сlаsз Мiпi'/аn : Car
{
p.ub1ic МiniVan (string р, irlt э)
~ basetp, s) {}
11 Дo~нвe и_ор ,див Xin:i.Van.
)

в Соответствии с законами наследованИfl, в RОЛ:Ш~}ЩИ;Ю CarCollection.<T>. соз-


данную с парам~тр6м ТИПа Car. :моаЮfQ добавлsrь-и ТЩlbl MiniVan и SportsCa;r.
/ I CaxCQ1!ec:tiQn<Car> ..оаеш XP&RJfIrЬ .mDC$о'Й тип,арои.нодКiIЙ Q!J! Ca;r;.
CiHCol1ection<C,ar:> туСатв = new С:аrС'<йlесtjоn<Саr> () i
myInts .Add.Car (ne'W Mi'l:i,.'7an! .iparnily Truck,st.er", 55) >;
rnyln.ts . AddCaT (new 5po'r tsCal: (7I' Сп.r5h,е r", 4 О) ) ;
Это си.нтаксическ;и корре.ктво, но что делать, еС.11И BДPyr ПОНад<rlбится добавить в
Сат С оllесН оn<т> врвыйоткрblТЫЙ метод. например, с именем E'rirltJ;'etNarne ()?
Такаязэдача lCaЖ.ется простой- ДОCJ:'a:roчно получить доступ К ЛОщroд$IЩему Э1Ie'­
мeRryИЗ Lis't<T> ~ вызвать евой.СТВd J?etName.
1/ Ошибка!
/ / System. Об'Ъ8~'l' ..е ,ииеem С.ОЙС'1'2lа о ииевеи i>etNUIS,
public v oid' Pri ntPe't.t>'!ame( i nt PQS)
1
CDnSble. Wr i teLine (arG,ar's [розJ • PetNa!f1e) ;

OДнaRo в таком виде программный код скомnилиро:ван не будет~ lIocRольку ИС'­


тюrная суть <'1'> еще не известна. и вы не можете С у,веренностьюутверждать. что
:какой-то Мемент типа List<'1'> будет }{Меть СВОЙСТВО PetNarn'8'. Когда napaмe'l:p
тt!ItIЗ не имеет никаких ограничений (как в Д8,ШlОМ случае), обобщенны:И тип на­
зывается свободным (unbound). 110 идее пара.~етры своБОДlЩro 'ТШIа ДOJDШiЫ ИМеть
толыю члены Syst em,Object (Korrop.Qte. очевидно. не lЩ~ют еВОИС7Вf1 PetlJam~).
Вы можете попытаться ~обм.ануть~ kOмдroUIТOP I;Jy'J'eM np~образования элемен­
та, ВО;НlраЩe:юrorо:и:з М'е.тQДЭ.. :индексатора List<T::>. в строго типиз0вa.mIый объект
Са т, чтобы затем выЗвать PetName ВОl3вращеНFj:GГО об'l?е,КТ<!-

1/ OJкиБЖ<L!
/ / Нe.nьз. пpesр8'1'М'п> .шип • Т" • ' Cair' !
public void priotPetr:Iame (ir1t ров.)
{
Con sole,. Wri teLine (( (Car) arCars [роз1) .petName) ;'

Но э'ГО тоже liе ~ОМDroшруется. ПflC:;~олъку коМIiИляТOр не знает значения пара­


метра типа <Т > н не может гарC\Н'ГJfРО~ЩТЬ. ЧТО прообразован:йе БУдетэакоlЦIblМ.
432 Часть /1, Язык программирования С#

для решения именно таких npoблем обобщения .NEТ могут опционально опре­
деляться с ограничениями, для чего используется ключевое слово where. В .NEТ 2.0
обобщения могут иметь ограничения. описанные Б табл. 10.2.
Таблица 10.2. Возможные ограничения обобщений ДЛЯ пара метров типа

Ограничение обобщения Описание

where Т struct Параметр типа <Т> должен иметь в цепочк.е наследования


Systern.ValueType
where Т class Параметр типа <Т> не должен иметь в цепочке наследования
System.ValueType (т.е. <Т> должен быть ссылочным типом)

where Т new () Параметр типа <Т> должен иметь конструктор, заданный по


умолчанию. Это полезно тогда, KOrдa обобщенный тип дол­
жвн создать экземпляр параметра типа, а вы не имеете ясных
предположений о формате пользовательских конструкторов.
Заметьте. что это ограничение должно быть последним е спи­
ске ограничений, если у типа их несколько

where Т БазовыйКласс Параметр типа <Т> должен быть проиэводным класса, указан­
ного параметром БазовыйКласс

where Т Интерфейс ПарамеТр типа <Т> должен реалиэовывать интерфейс, указан­


ный параметром Интерфейс

При наложении ограничений с помощью ключевого слова where список ограни­


чений размещается после имени базового класса обобщенного типа и списка ин­
терфейсов. В качестве конкретных примеров рассмотрите следующие ограничения
обобщенного .класса MyGenericClass.
11 ВJtо.8КИJo18 Эn8118Н'DI дOJIJICНII ИII8..,. ХОНСТРУХ'1'ОР,
/1 .аданный по умоnч&НИID.
puЫic class MyGenericClass<T> where Т : new()
( ... )

/I в.nO.8ННЫ8 Эn8118Н'DI oЦOn:.нl.l бbl'1'lo xn&Ссамн, реamrsv'мМIIИ IDrawab18


1/ IC ПОДД8Р:lКИllUIIIr;ИIIIC хонструтор, s.д&IUIIIЙ по УМOnЧaJOaD.
public сlаээ MyGenericClass<T> where Т : class, IDrawable, new()
{ ... }
/ / МyGen8ricCla8s ПОnУЧ.8'1'С. иs МуВаа8 и Р8anиsу.~ ISOlll8Inter~aC8,
11 • B'nO.8ннwe эnеи8Н'D1 дo.mlClUl бlol'1'1o ctp)'X'1'YP--.
public class MyGenericClass<T> : MyBase, ISomelnterface
where Т : struct
{ ... }
При построении обобщенного типа. в котором указано несколько парамеТРОБ
типа. БЫ можете указать уникальный набор ограничений для каждого из таких па­
раметров.

/1 <к> дo.mкeн ин...,. XOBCТP)'X'l'OP, s.,.аJUВIЙ ПО YМOn'laJlll1D,


11 • <Т> дотк8И Р8IШИSО8la.~Ъ О~JCPЫ'1'ЫЙ JIJI'1'8ptейс IСошрarаЬ18.
public class MyGenericClass<K, Т> where К : new()
where Т : IComparable<T>
{ ... }

_t
r.naB8 1О , Обобщения' 433
Ес.пи вы. хотите изменить nm CarCollection <:Т> так. Ч'tобы D него МОЖ}IO было
.поместить только ПРОИЭВQдm!Iе от Car. вы можете звписать следующее.

pцblic' class CarCollect~on<T> : !Enumerable<T> where ~ I C~r


{

public void PrintPetName (iпt ров)


(
11 noeкcn.lCy ~8p. 8С8 SnlUl88R1 доmaAt tI~ •• с. .Й~ сц,
11 ~ой.С11'ао ?et:N8JМ lI08Iio 8Ч11W8а~ В8ПосредC!R88ВКО.
CODsole .WriteLine {ахСахо; [posl . РеtNаще) :

)'

При TaIЦIX огрmmчен:иях на CarC-оllесtiоn<Т> реализация J?rini:1?etName О


станоВИ'I'CЯ о-чень простой. lюcRoлыty теперь· RОМПИЛJ1'rОР можетпредnoл~ать, что
<Т> RВЛ.Яет$ црои.asодным 'от Car. Более тoro. если указанный пc>JIЬЗoвателем па­
рзметртиrЩ ]{~ совместим с Car. будет Сl'(Щерирована ОПП'lбка компиляции.
1/ о.мбn .ОIlli.':lWnIЩИИ!
carCol1ect..±on<int> m.yInts = ne-w CarCol1ection<in't> О •
вы долтньr пониматъ, чro обобщениыe методы тоже моryтиtтользова']1Ь Ю1Юче­
'Вое сдова where. Нщrpимер. e~ НiYЖflО гарантировать. -чтоdы: методУ Swap ()~ СОЗ­
дaщlом:у 13' этой rnaвe выше. пере.щwались 1'ОЛЪR0 тИПЫ. производные от Sy;stem..
ValueType. изменю'е CBO~ npoгрaммIO»Й !ЮД так.

11 З'.I!O'R К8'1'ОД a8p8C!1!"~ maes.- ТИШI, ~~~y........a •• ~.


s'tatic v~id. Swap<T::> (ref т а, ref Т Ь) wheJ:e Т : struct
I

СЛедует также понимать то.. ЧТQ при таком orpаничeнuи метод Swap() уже не
смОжет щ:реставля.ть СТРОRO~ые тиIIы (nOC.1<OJ1bКY они S'IВJIЯЮТся ссылочнЫмИ).

Отсутствие поддержки ограничений


при ИСnОnЬЗ0вании операциА
При создании обобщеuных методов ДЛЯ 'Вас монсет оJtaэаться СЮРПРИЗQМ цо­
RВJIеиие о~бок !ФМПИJIJ!тора.к()гда с парамe>rpа'ми 'I'ИПa й:cnom.зуются ОпераЦии
С# (+, ~.*, =: ~'I,д.). Например. и уверен. вы сочли бы полезными плассы Add ().
$up'tract (). Мцl'ЦрJ..у(о) и Divide (), способные рабamrъ .с обобщеннЫми типами.
1/ 0IaIб.. ~ОJIIDf.IJIЩIUI!
11 в.па..а. nP1fМ8~ оперlЩИИ Jt D8p&118lJ!P'" lJIJШаl
public c,l,a:ss BasicMathl(',:!,>
(
pdblic т Add (Т argJ..T arg2)
{ ret:u·:rlil argl т' ar<J2,; I
риЬИс Т SuЬtrac't (Т aIgl, Т arg2)
{ Tetu:rn argl - arg2: }
pu.blic ! Multiply П щrgl, Т a:rg2)
434 Часть /1, Язык программироваНИR С#

{ return argl * arg2; }


public Т Divide (Т argl, Т aIg2)
( return argl / arg2; )

как ни печально, этот класс BasicMatrl<T> не компилируется. Это может пока­


заться большим ограничением, но не следует забывать , что обобщения явллются
обобщенuямu. Конечно, тип SуstеlrL.IпtЗ2 может прекрасно работать с бинарными
операциями С#. Однако, если, например, <Т> будет пользовательским классом или
типом CTPYKrypbI, компилятор не сможет сделать никаких предположений о харак­
тере перегруженных операций +. -. * и 1. В идеале С# должен бьm бы позволять
обобщенному типу ограничения с использованием операций. например. так.

1/ Топъко для ИJIJII)Corpации!


1/ Э'l'О'l' nPOЖ'ракмный код не явnяе'l'СЯ допус'1'ИИШl • с# 2. О .
public class BasicMath<T> where Т : operat o r +. operator -
operator *, operator I

public т Add (1 асчl. Т arg2)


{ return argl + arg2; }
public Т Subtract(T argl, 1 arg2)
{ return асчl - arg2; }
public Т Multiply (T argl, Т асч2)
{ return argl * arg2; }
public Т Divide (Т argl, Т arg2)
{ return argl I arg2; }

Увы. ограничения обобщенных типов при использовании операций в С# 2005


не поддерживаются.

ИСХОДНЫЙ код. Проект CustomGenerioCollection размещен в подкаталоге, соответствующем главе 10.

Создание обобщенных базовых классов


Перед рассмотрением обобщенных интерфейсов следует указать на то, ЧТО
обобщенные классы могут быть базовыми для других классов и МО$ таким обра­
зом определять любое число виртуальных и абстрактных методов. Однако произ­
водные типы должны подчиняться определенным правилам, вытекающим из при­

роды обобщенной абстракции. Во-первых. если обобщенный класс расширяется


необобщенным, то производный класс должен конкретизировать параметр типа.

// Предпоnожим, Ч'l'о создан поnьзова'l'еnъский


1/ обобЩеНЮIЙ JCJ1acc сnис:ка.
public class MyList <T>
(
private Li st<T> listOfData = new List<T>();
}
11 КОВlCpе'1'иые '1'И1ПI доnжиw у:каза'1'Ъ параиеorp o:t'ИПа,
11 еcnи они поnучaJO'1'СЯ: из обобщеивоrо базовоrо кnacca.
public class MyStringList : MyList<string >
{}
т

Глава 10. О&Общенкя4З5

Кроме тоro. если обобщеннЫй ба8ОВьШ класс определяет обобщенные виp:ryanъ­


ные .или абстраюные методы. производный тип должен перeonpеделитъэти oБQБ­
ЩeШfbl'е мeтo:цы. mшольау.я ROнкретиаирОВaнFIЫЙ uaраметр типа.

/I О&lоcS. .JUa.JЙ JC.ttaec с: ВIIp~ЮI"JDA[ Кe!l'O~OK.


puPlic olass м.уLiэt<'r>
{
priv<'!te List<T> liэtОfDаtа = I)ew J..i~t<T> ();
puЫic vi.rtl1al void PrintList (Т data) I !
1
public сlВ.5З .мУSt::rinqJ..iэt : MyLis't<st.ring>
{
IJ в nPOИЗ80ДКIIX хе'1'ОДа. ~o sамаКИ'.II'Ь ПАраме'1'р типа,
1/ испom.sу8llOlЙ .. РОДW1!4ШIoCJI:0IC xnaеое.
pt:ibl,i.c оvепidе VQid FrintList (зtzing data) ( }

Еc.nи пропзводный nm 'Тоте тщиетen обобщенным. дочерний ЮIасс может (оп­


nиовалы'lO) ИСПO.:J1Ъзова1Ъ заменитель типа з С80ем onредедении. Одама зйaй're.
что любые ограничения. разм.ещеlJНЫe в базовом КЩlссе. должны )rчитьmaтъся· И
nPОИЗlЮдным тиno.м. Нanpимер:

/I О.бра!I'И'AI _1DWiUIИ8 t тtШ8рЪ ад!К=Ъ икеВ'l'Q. Огр&1DR8ВИ8,


11 'J!fIe~e XOHCТP)'X'l'Op по YМOn.8.JdaI.
pubIlc с1азз Mybist<.'T> Where т : new()
{
private L'ist<T> list.OfData = new List<T>O;
p\iblic virtual vqi:dР:п:intList (Т d'ata) r }

/ / ПРО"80ДRlolЙ '.пш AOm&eB ~a'1'Jo оrpaюtЧ8IDPi бааоаоro.


p.ublic class МуReаdОnlУ'Liэt<Т> : MyLiS1t<'I'> where Т : j)ew ()
[
p!lblic override ".. oid PriPtList (Т da:t.a) f }

ЕCJШ вы тOлыtо не планируете построить евою сОбс'С,I3~.щfY1O бибiщотеку обобще­


НИй. вероатностъ ТОГО, что вам придется строить иера;рхии оощ;щею:Iых юшсСов,
wазываеТC.II npактичесШi нулевой. Тем не MeHe~, вы теueр:ь ЗН;iете. что язык С#
обеспечивает поддерЖку наследовании обрбtцев.Шi.

Создание обобщенных интерфейсов


вы уже вид~ли при paccmotpeJ-ПШ пространства .имен System~Collections.
Gе n е r i с. что. обобщенные инте.рфеЙсы в С# тахте допустимы (например.
IEllumerable<T». Вы. }(ЩlечlW, можете оп:редemпь €БОИ собственные вбобщенвые
интерфейсы (как с оrранич-ешurми. так и б,еа ограничений), Предположим. что
нужно определить интерфейс. ROТОРЬТ:Й сможет ВЫПйJIнвть бинарНЬi:е опеpaшm €
парЮ4етрами О,бобщенноrа типа.
436 Часть 11. ЯЗЫК программирования С#

public interface IB1naryOperat1on8<T>


(
т Add(T argl, Т arg2);
т Subtract(T arg1, Т arg2);
т Multiply(T arg1, Т arg2);
т Divide(T argl, Т arg2);

Известно. что интерфейсы остаются почти бесполезными. пока они не реализо­


ваны некоторым классом иди струюурой. При реализации обобщенного интерфей­
са поддеpжJШaЮЩИЙ его тип указывает тип запOJШИТeJlR.

public class BasicMath : IBinaryaperation8<1nt>


(
public int Add(int arg1, int arg2)
( rеturл arg1 + arg2; )
public int Subtract (int argl, int arg2)
{ rеturл argl - arg2; }
public 1nt Multiply(int argl, int arg2)
{ return argl • arg2; }
public 1nt Divide(int argl, int arg2)
{ return argl / arg2; }

После этого вы можете использовать BasicMath. как и ожидали.

static void Мain(string[] аrgз)


(
Солsоlе.writеLiпе("·**** Обобщенные интерфейсы *****\п");
BasicMath m - new BasicMath();
Console.WriteLine("l + 1 - (О}", m.Add(l, 1»;
Console.ReadLine();

Если вместо зтого требуется создать класс BasicMath. действующий на числа


с WIавающим десятичным разделителем. можно конкретизировать параметр типа

так.

public class ВазiсМаth : IBinaryoperations<douыl>>


{
public doubl. Add (doubl. argl, doubl. arg2)
{ return argl + arg2; }

Исходнwй код. Проект Genericlnterface размещен в подкаталоге, соответствующем главе 10.


Глава 10. о.бобщвн.ия 437

Создание о· бобщенных дел·егатов


Нa1taнeц. что:не :м:e~ важно•. NEТ2.0 позВOJПJет ОDредеJЩТЬ обобще.иm.lе тиnы1
дencraтa. Предположим. например. что требуется О~дe.тIИ:lЪ делeraщ )Ioторый Сма­
жет 8ъtзыватъ любой MerQA. воовращающИЙ' void и црщnnщющиИодин аРгуМент.
ЕсЛи apryмe:нт может МeiВ.ИТЬC'Я. :ЭТО можно учесть с ио!wщыo па:раМетра тИПа. Для
примера рассмотрим следУЮЩИЙ npограм;мный КОД (обратите 1ШИМamtе яа ТО. что
целевые объеКТЫ делегата реrистрируются вак с lДIМЩЦЪЮ "трaди::циamroго~ сИН'­
таксиса делегата. таи Й с помоЩЪю rрYIJПОВОГО цреобрilЗОВания ыe':l'Oдa).

nатезрасе Gener.tcDel~ga ,te


j
11Э'1'о'1' оCSо6ще~ .1Ie.JJEЦ'a~ .mre!II JЦ1SИ.'По JШ6ой И8'1'ОА.
Ilао ••раща-.cic vo1d и ~ о;циВ п&ра-.8.'1'р.
public deleg,ate void MyGe.ner.lc.Delegate<l'> (Т tП'q) ;
class Prograrn
{
stati~ void Main (string[] аr;gэ)
f

IIР&rИС'1'ра" цuи с пока. .., ., ~OIUlOI"O '


11 mпtm&хахс& д._ежоа'1'~.
МуGепеri r.Юеlеg.аtе<stri-ng> st~Target =
new MYGenericPelega·te<.strl,ng> (striлgТа.хgеt) ;
э trTarg:et (."ВеН9.торые строковые да:IOl.ые");

/1 РеrиС'1'р&ЦJПt цепи с 11011011l;I01II


11 xopyttDo.ot'o пр80брааоа8JЩJI М8!1'o~a..
МуGеn~riсDеlеяаtе<illt> int Target = lIJ.tTarget;
intTar g et (9);
CQnsole.ReaOLine()I

Btatic vO'i d S.tring-Тarget (striлg arg:)


I
C;o.nsc,le . W:c i te-Lirle ( "aтg' в aepXHe~ реГИС'l'ре: [О J 11 ,

arg.TGUppert)) ;

static void IntTarqet (iлt arg)


{
'Console. WriteLine ("++arg: ! О) ". ++arg} I
}
J

Обратите J,t~e на то. что MyGenericDele9'ate <T> оп~Д!:цяет оди;н щipа­


Ме1]) 'l"'.ИПа, предt;:тавляющИй: ap.ryмeнт. отправляемый целевому объекту делегата.
Uри СОЗДIЩИИ э:кзeмnляpа это:rо· типа требуется lюю<.ретизЩ)оватъ значeJШ,e llа­
раметра типа. а также .имя мe-roдa. вызывeblоrоo дел~гатом. Так., если Щd yRaж~те
строповый ТИД, то отправите целевому MeтoЦV C'lpOKOBoe зщчеЩ<lе.
438 Часть 11 . Язык программирования С#

// Соз~ание эхзeмnnяра МyGenericDelegate<T>


11 СО зна~ени8М string ДJJJI параметра типа.
MyGe n er i cDelega t e<string> s t r Ta rg e t =
n e w МуGепеri сDеlеg аt е<s tr i лg > ( Str iлgТаrg еt ) ;
s trTarget ("Н е к о т орые стро ко вые данные") :

с учетом формата объекта strTarget метод Str i n g Targ e t() должен теперь по ­
лучить в качестве параметра одну строку,

st a ti c void Str in gT ar g e t( str in g arg)


(
Co n so le. Wri t e Li ne (" a rg в в ерх нем регистре : ( О} ", arg . To Upper () ) ;

Имитация обобщенных делегатов в . NET 1.1


Как видите. обобщенные делегаты предлагают более гибкий подход для указа­
ния вызываемых методов . В р амках ,NET 1.1 аналогичного результата можно до­
стичь с помощью базового Sys tem ,Object.
publ ic delegate void MyDel e gate(ob j ect arg) ;

Это позволяет посылать любой тип данных целевому объекту делегата . но при
этом не гарантируется типовая безопасность. а кроме того, остаются нерешенными
проблемы создания объ ектных образов . Предположим . например. что мы создали
дв а экземпляра My De l eg ate , которые указывают на один и тот же метод MyTa r get .
Обр атите внимание на про блемы с оздания объектных образов и восстановления
значений, а также на отсутствие гарантий типовой безоп асности.

c l ass Program
(
static void Ма i л t st r iпg[] args)

/ / Регистрация цепи с помощы!)


/ / • традиционного' синтахсиса дenегата .
MyDe l egate d = n e w My De l ega t e( MyTarget ) ;
d ("Д о п олните л ьные с тр оковые да н ные " ) ;

// Регистрация цепи с помощью


/1 группового npеобразования метода.
MyDelegate д2 = My Ta r ge t;
д2(9); // Пробnеиа объектного образа.

11 B_~ отсутствия типовой безопасности NW дOrolCНlol


/1 опредenить соответствупциЙ' тип до npеобразовании.
st ati c v o i d MyT ёlrg et ( o bjec t a rg)
{
if (arg is il1t)
{
iпt i = (i n t) arg ; /1 Проблема восс~ано.nеНИJl значения,
Глава 10. ЬБDБщеН,ИR 439
CQPSO 1 е. Wri teLj, nе ("++ar(j: {О}" I ++} j ;

i~\arg 18 stxinq)
!
эtr-inч 5 = (st:c i rlg) a·rg,
'СопsоЪе ,Wrll t~Line r " а,хв liJ :верхнем реrистре: IQ} н •
э.1'оtJрреr(»);

ltor,DJi вы ПОСblJJзете целеJЮМY объеКТу тип, характеризуемый -значением. это


'ЗlЩчение l1«>пе~ же) "упаковывается" в объекти ·распаковывается· после еroпо­
луч-ения Дe.1ieвым методом. Точно также. 'nОСКОЛЪ:КУПОС'Ij'Пающий параметр может
оыть всем 'Чем утодно. БЫ ДОЛЖНЫ динамичесКи проверить ОООТlfетствующий 1'Иn
п~редтем. RaIc:вЫnOЛ1IИТЬ npеобразование. С помощью обобщенныхелегатовB вы
можt!те получить всю необходимую rибаость без -npоблем".

Несколько сnов о вложенных .делегатах


3авер1IIИМ Э'1У rnа:ву рас-см:отрением еще ОДIЮГ() аспекта обобщешгых делегатов.
ВliIзщете. 'ЧТо делегаты мory:г быть "вложены "в тип ЮIЗ:Cс-а.. что должно означать
ТeQiYЮ ассоциацию МI".ждУ' этими ДS.YМiI Ссылочными типами. Ecmt тип-контейнер
дp~ этом оющывается обо'бщенныllo•• вл"ожeн:tiIый ДeJJег,и ' МОЖет ИСПОЛЬЗОваТЬ .Б СВО­
ем OI]ред~ении параметры типа.

11 Вn088JU1i.te оdобщ8JUGre ,JIeneж<а<moI КO%')"J! 1D18'l'Io ;ЦOC~. " uapll.lUl'Rpu.


11 оdоlЩeввОЖ10 !t'JШ~хоа!l!~а.
public clas5 NyLi.st<1'>
{
private List<T> listOfDa~a = new List<T>()i
public deleq,a t·e void LiatDelega.te{I arg);

"СХОДНЫЙ 1toP.. Проект 'Oeneric08regate размещен 8 АQДI<аталоге. соотвеТОТВУlOщеt,1 mэве 1О.

Резюм'е
ОбоCiщeиnя можно Qбoспавamю считать главным из усо~ершенс:г,вnвШJИЙ. пред­

I
ложещпФ( в С#2005. Как вы .могщ! убедитьсп. обобщеwlЫЙ ~лемент ПOЗВQляет
yщuш.ть WзanщmителИ (т.е • .I1ара..четры типа), .которые КЩЩР~ИЭffРУЮТСЯ в ыo:мtнт
создa:н:иJJ типа (или вызова, в случае ~бщеища: методов). llQ сугп. обоl$щemm
дaюrr решеви<: npoблем объеК1'ВЫХ образов :и типовой беэоЩl(;ПОСТИ, yc-тrожкящщИк
разработку пpQтрамм: в среде .NEТ 1, 1.
Чаще нс.его вы будете просто ИСЦО.IJ,ьзрватъ Обобщснцые типы. дредла~аемые
БИБл:иотецами базовых классов .NET• .но МОЖ,ИО С9ЗДав~ть и свои собственfIыe
обобщенные типы. При создании обобщеннь:цс ТИnQВ "Мшщ!О указЗ'FЬ лЮбое чпмо
ограничендji. чтоБыI ПОВЫСИТЬ уровень типовой бе~оIIВСНОСТ1l! П' гарантировать. что
соотве:гс:mующие опера::цид будут ВЩПОЛНSJТЬСН С '"иэвес~ 11е:личинами~.
ЧАСТЬ 111
Программирование
к, омпонов ,Qчных

бл'оков .NEТ

в этой части "'.~


Глава 11. Компоновочные блоки .NEТ
Глава 12. Отображение ТИПОв, Дйнамич,еское связывание
и ПРQграммироваliие с помощью' аТРLllбyrов

Гnава 1З. ПроцеССЫ 1 домены приложений, контексты и хосты CLR


Глава 14. Создани,е МНОГОПОТQЧНЫХ приложений
Гnава 15. CJL и роль динамических 'компоновочных БЛОf(ОВ
r
I

Г'ЛАВА 11
компоновочныIe
блоки .NET

Л fu.rn:e
юбое из прwюжeRИЙ. описаянъп в э-rой Кl:iиrе в предыдуЩИХ десяти rлaвa:x..
. об.ычвым: -'aвтoHoмным' :приложением. вел прorpаммнаи lЮI'ИШl KO'J'O~
роro целином содержалась в ОДНОМ ныnоnняm-юм qmйле (... ехе J. Однако ()Д1lCIP~ из
плавных задач платфо'Р1ИЫ .NEТ является ~Ш020кра:т.н.ое ucnОJl.ЬЗOlЮНue npdгpaм­
МЫD20 кода. 1IpИ катером при.11ОЖения могут ИCnОЛЪЗfilвать типы. содержащиеся
в разnичныx ВН6ШНИХ ltOМllОНавочных блоках (называемыхтaшke б.иблиотека..l\lИ.
программного Бода). Цмью этой :rлавы будет lIодробное рассмотрение вопросов
сОзданин.. инсталляции И 1Шliфш:урaциn }юмпоновочfLых блmtQВ .NEт.
Сначала мы рас.смотрим pa.aлиtшя меш,цу {)ДНОМО~JIЫШМИ и мнагомо.цульl-iыми
IЮМnQffiЩОЧНЫМИ бло:к.a.м-n, ата.кже мещrw '~ПРИВатНыМИ" й "общедоступными'" ком·
поноШ>чНымИ бланаМИ. Затем мы ВЫНСl:IИМ.• вак среда въшолнения .NEТ onределя­
ет I1араметры размещt)НИg IШМlюlюво-ЧНОго блока. и lIоnытаеl>lСЯ IЮRЯТЬ РQJlЬdЛС
(Glnba1 Аsвешы1y СасЬе - rnобальный. ШШ1 комlIонolючных бло&ов), фaйJIОВ КО1tфи­
гурац1!.И пр1:uюжения (файлы 1/ .ссmПgJ, поJIИТИКi;J nyб.JIИIШЦии .кcmшоновочНЫХ б,Jl:О­
КОВ п ПРОC'Jpанства имен Sуstеш.СопПgurаtiоп.

Роль компоновочных 'блоков .НЕТ


. . -

При.'IожеItиn .l'IlEТ строятся t1yl'eM СВЯЗЫ5~ прОnЗВО.lIb1iОГО чиела К'1\IМ,llOНО'


ВQЧJ-LЫХ бжжов. С ТО.чюr эрепиn упрощенного подхода JOOМnOIJОБО"-mъili бn:о.в: Щ:1лне.т­
Сп' двои"{ЕЪiМ фa:й.r-том. ВЮIЮчaIOЩIOvI ('вое оuиеание. снабжеЯНЫ1\1lIомером :версии
и 1I0дLtерживae:мьtм средой CLR (Common Language Ru:ntbne - общеязы,ll:OВЩI среда
SbllID.11НeIi.i:'t1I), Несмотря на тот фаК't. Ч'r1) ДОМПОНОВОЧ!iЬJ(' fiлOЮl .МЕТ имеют такие
же раеширенив: (* .ехе иди * .dll). A<3...R}f дРУТие двоичные файлы Win32 [ВIWlочая;
все etr(e ИСlIо~емые сервеР!;I СОМ1 по сути:. КОМnОНОВО'llrыеБJIOi{И .NEТ имеют е
НИМ:U очень мало общего. Поэто.му Д;J:Щ lIЗЧЩIа мы рассмотрим нен;оторые пpeu:мy­
щеет:ва. обеспечиваемые форматом КQМIIОнщЮЧnОrо бnО1ta.

Расширенные возможнОСти многократного


ИСПОnЬЗ0вания прогр,аммного кода

При посТроении консольных приложений в предъЩущих главах :моша ПО:WЭ'-


3iUhСЛ. ЧТО В создаваемом вами вьmолняемо:м lюмпшlDвочном блоке содержат-
444 Часть 111. Программирование компоновочных блоков .NET

сл все функциональные возможности соответствующего приложения. На самом


же деле ващи приложения использовали множество типов, предлагаемых всегда

доступной библиотекой программного кода .NEТ. mscorlib.dll (напомним. что


компилятор С# ссылается на mscorlib.dll автоматически). а также библиотекой
Sys tem. Windows. Forms. dll.
Вы. наверное. знаете. что библиотека npoграммного к:ода (также называемая
библиотекой к:лассов) представляет собой файл * .dll. содержащий тшIы. дос1}'П­
ные для использования внешними npиложениями. При создании выполняемого
КОМПОновочного блока используются компоновочные блоки. предлагаемые систе­
мой, а также пользовательские компоновочные блоки. При этом файл библиоте­
ки npограммного кода не обязательно имеет вид * .dll, поскольку выполняемый
компоновочный блок может использовать и типы. определенные во внешнем вы­
полняемом файле. В зтой связи файл *. ехе тоже можно считать "библиотекой про­
граммного кода n .

Замечание. До появления Visual Studio 2005 единственную возможность сослвться на выполняе­


мую библиотеку программного кода обеспечивan флаг /reference компилятора С# . Но те­
перь ссылаться на КОМПОl10вочные блоки * .ехе позволяет и диanоговое окно Add Reference
(Добавление ссылки) в ViSUBI Studio 2005.

Независимо от того. как упакована библиотека программного кода, платфор­


ма .NEТ позволяет использовать типы в независимой от языка форме. Например,
можно создать библиотеку программного кода в С# и использовать Э'I)' библиотеку
в любом другом языке программировa.Rи.я .NEТ. При этом можно не только созда­
вать зкземnляры типов в рамках других языков. но и получить производные таких

типов. Базовый класс, определенный в С#. можно расширить с помощью класса,


созданного в Уlэизl Basic .NE'f. Интерфейсы. определенные в Раэсзl.NEТ. могут ре­
ализовываться структурами, определeIПlblМИ в С#. Смысл в том, что при разделе­
нии единого и монолитного выполняемого программного кода на множество ком­

поновочных блоков .NEТ вы получаете язbtКово-нейтральную форму программного


кода. пршодного для многократного использования .

Установка четких границ типов


Из главы 3 вы узнали о формальных понятиях. лежaIЦИX в основе любого про­
странства имен .NEТ. Напомним, что абсолютное имя типа строится путем до­
бавления префикса пространства имен (например, System) к имени типа (напри­
мер. Console). Однако. строго говоря. компоновочный блок, содержащий данный
тип, задает параметры дальнейшей идентификации типа. Например, если у вас
есть два компоновочных блока с разными названиями (скажем, MyCars.dll И
YourCars.dll), которые определяют пространство имен (CarLibrary), содержа­
щее класс
SportsCar, то эти классы во Мвселенной" .NEТ будУТ считаться разными
типами.

Управление версиями
Компоновочным блокам .NEТ назначается состоящий из четырех частей число­
вой идентификатор версии, имеющий ВИД <главный номер веpcuu>.<до11l)ЛИшneль-
Глава 11. KOMI10КOIOIIHbl8 бnОkИ .NEТ 445
ющ номер веРCUU>.<fФJlЩР КOМnOНQ8I!iU>.<нo.мep 8Цpuaнma> (еоли вы не укажете
mщo ИДeJЦВф:икатор версии с ПОМОЩЬЮ СВОШТJЩ [AssemblNVersfOnl, Jtом:поко­
вочный БJ;IрИ RВТO),ЩТJ.IЧеmm п~лучит иденти.фикаТQР :!'epc~ 0.0.0.0), этот ИДeR­
ти:ф~тор в сово,кyIIQоСти С }ЦЮБR~атeJlЬИЬ[М ЭНCNeН.ue,м О~ьРПОгО '1CJIЮЧa позво­
l1IIeT Мl'JщКеству верCJ.IЙ ОЮlc.:JI'О 11 тoro ~e ;КОМПОНОВОЧНОГО б:J:юRa сосуществовать на
одной и той ~e :м.a:rшnrе в ПОЛНОЙ raР1l!lОНИ:И. Rомпоновочные' блщщ. обеспечива­
ющие иифQР;Мацщо об отхр:ытом КJIIOче, нвзьrвaютСR crrrpot:O u..мeкoвaнны:ми. КaI<
будет пока,зано В ЭТQЙ rnаве пещве. при наличии етрогр ЗaдaJnlОro ltМeюt <1реда CLR
enособва rарантировйть-, ч;го ПО 3ацроСу' ВЫзывaIoщег.а клиента БУА~ загружена
имен~1O та версия КOМIJЩЮВОЧИОrQ ~oц, ~ра:я: требуетсВ".

Самооп"са.ние
КОМПОНОВОЧНБIе ~J:lОКИ считают~ еДИНИЦаМИ е чаСТИЧИЬ1И ·самСХДIИЩЦЦlем, по­
CIюлысу В них содерЖИТQR информаци~ о внещних:: ко.шОН{i)ВОЧНЫ:Х: блOl(U. необхо­
димых для правИJIЪВЩ'О ФYJlКДИOl;lИpовани,я ЩJМЦО~ОВОЧНОro блока. Тц что если
вameЪiIY ROМJIOHOВO"!nroмy блоку J"реб~SуstеIII.Wi.ndоwз.FQrmэ .dl1 и System.
Drawing .dll, то информация О них бу;Цtt ЗaJЩ$Шl в М41iuфecm ltомnolЮВОЧНОro
блока. Вспомните из главы] . что мmщфест- ЭТQ ~JlOК метаданных. ОlШсывающих
сам хампОНОВО'ЧНЫЙ блок [ИМя,версJЩ, ~форМaцЩI О ввешl'lЩ( компоновочных
блоках 'и :I:A.).
Кроме данных манифеста. коМпоновочный блок Co.цe~ fIofeтa,д3rниы,' оnиoы­
вь>щие cтpyIaYpy :каждого содержащerocя типа (имена членов, peaЩlЗyемые и;н­
терфеЙсы. базовые В'Лв.ссы. lWиcтpyкroры И :I:A.)' И посхо.льиу ROМДЩЮI!OЧНЫЙ б.лqк
,ц:окумеКТI.fPYё'!:СЯ настолыro "кpa:mlОречиво~ • среда CLR 'НЕ ооpa.щйemcя ~ реестру
системыWin32 дла m.шсifения размeIЦeншJ компоновочного блока (что IiрiUЩИ­
:mtaJ1ЬiЮ O'l:7IИЧa.eТСЯ от предшu;aвmeЙCSI ранее МIcrosoft модели npo1'JЩММИровamш
СОМ). из Э1'ой rлзвы вы узнаете. 'iТO среда СLR.йcncшьsyет совершенно новую.сему
получения информации о размещении внешних БИБJпroтек ЩЮrpaiММJlom кода.,

Средства конфигурации
КОМIЮновочвые блОКИ МожНо йнс'I'Э.1lЛ&lpo8a'I'ъ как ·npиватнъте
w
ИЩI 'kaJ( "06-
ще,цоСТymiЫе". Приватвые коМJiОI!ОВОЧНhlе блохи размещаются в том Ж~ вата­
лоre (шm. возможно. подкаталоrе). что и ИСПОiЛЬ3уЕщее их IфИЛОЖeвJre-юmеwc;
Общ~дО'cтyпньre комnoнО'вочньre CSлeки. RaIIpO'I'.иВ. явлmoтcн библиотеками. дрсту11-
ными для мнО'rиx прuложений. и т.ахие :КОlimоковочные блоки)1стaнaвmmaI01'CН в
специалЬНЫЙ каталог, имеюtt~IЙ специальное название - zл.oбальftый 1CЭШ1CDМ11О­
ItOВОЧНbIX БЛOlWв (оле).
Неэависим.о от вида ШIC'1'a11JUIЦIiИ хомпоновo'чных б:локов. вы можете со~аваn.
для них XМL-фвйлы: конфихурацшI. С ПОМОIЩ>Ю этих файлОВ можнО' ДI!I.ТЪ среде СШ
-укааание" с!) том. где C11e/IYeт исхать коМIIЬНОЙОЧные БЛOЮL.какую версию СООТ­
ветствующегО' КОМПОНОВQ'ЧНОro блокs. СЛедУет' aarpyэиrъ щ!я КоНкрenroro 1UIИeИ'J'a,
I< lШКON}' каrrалory на ЛОRaJIЬИОЙ машине. в вашей локальной сети или по RЗJI:ом:у
эцданиому адресу URL в Web CJle,цyeт обратиться. Более подРОБНую информацию о'
XML-фаШах КОНфигурации вы ПОJIyЧйте в Дa.лi>Н.eЙШем при изучении материала
ЭТОЙ rдaвы.
446 Часть 111. ПрограММlotрование компоновочных блоков .NET

Формат компоновочного блока .НЕТ


Теперь. когда вы знаете о некоторых преимуществах. обеспечиваемых компоно­
вочными блоками .NEТ, давайте немного сместим акценты и попытаемся понять
то. как устроены компоновочные блоки. С точки зрения внутренней структуры,
компоновочный блок .NEТ (*.dll или *.ехе) состоит из следующих элементов.

• Заголовок WinЭ2

• Заголовок CLR
• СIL-код

• Метаданные типа

• Манифест КОМIIоновочноI'О блока

• Необязательные встроенные ресурсы

Первые два элемента (зaroловки WinЗ2 и CLR) - это блоки данных. которыми
вы можете обычно пренебречъ. так что в отношении этих заголовков здесь пред­
лarается только самая общая информация. С учетом этого мы и рассмотрим все
указанные элементы по очереди.

Заголовок WiпЗ2
Заголовок WinЗ2 декларирует. что компоновочный блок может загРуЖаться и
управляться средствами операционных систем семейства Windows. Данные этоro
заголовка также идентифицируют тип приложения (консольное. с графическим
интерфейсом или библиотека программного I<ода *.dll). Чтобы увидеть инфор­
мацию заголовка Мп32 комnоновочноI'О блока. откройте компоновочный блок
.NEТ с помощью утилиты dumpbin.exe (в ОlЩе командной строки .NEТ Framework
2.0 SDR) с флагом /headers. На рис. 11.1 показана часть информации заголовка
WinЭ2 для компоновочного блока CarLibrary.dll. который вы построите в этой
главе немного позже.

Заголовок CLR
3агОЛОВО1< CLR - зто блок данных. который должны поддерживать все фай­
лы .NEТ (и действительно поддерживают. благодаря компилятору С#), чтобы сре­
да CLR имела возможность обрабатывать их. ПО сути. зтот заголовок. определяет
множество флагов. позволяющих среде выполнения выяснить структуру данного
управляемого файла . Например. существуют флаги. позволяющие идентифlЩИJЮ­
вать размещение метаданных и ресурсов в файле. выяснить версию среды выпол­
нения. для которой создавался компоновочный блок, значеЮJе (необязательного)
OTKPblTOro ключа ИТ.д. Если с dumpbin.exe использовать флаг /clrheader. вы по­
лучите внутреннюю информацию заголовка CLR для данного КОМIIоновочноro бло­
ка .NEТ. как показано на рис . 11.2.
Заголовок CLR компоновочного блока представляется неупрawшемой структурой
С-типа (IMAGE _ COR20 _ HEADER). определенной в файле С-заголовка corhdr.h.
ГIl'llва 11, КОМnОЩ)1;Iочные 51'10l(И .N~T 447

Рис. 11.1. Информация загопов!щ WiriЗ:1lШмпо~j(i)80LJНего Qпока

Рис.11.:2. Иlfформация заголовка GLR КОМПОНОВОЧr](JГО блока


448 Часть 111. Программирование компоновочных блоков .NET

для заинтересованных читателей предлarаем ознакомиться с видом структуры,


о которой здесь идет речь.

11 С'1'рухтура aa:E'OJJ08Xa CLR 2. О •


typedef struct IМAGE_COR20_HEADER
{
11 ВеРСИИ .&%'Одо.ха.
ULONG сЬ;
USHORT MajorRuntimeVersion;
USHORT MinorRuntimeVersion;

11 Таб.mщa CКl80J10. и иа.~и. . инфориацик.


IМAGE ОАТА DIRECTORY MetaData;
ULONG Flags;
ULONG EntryPointToken;

11 иифopиaцlOl С.UII8aJ1ИJ1.
IМAGE DATA DIRECTORY ResQurces;
IМAGE ОАТА DIRECTORY StrongNameSignature;

11 C,aкдa~ _ _ фоpR8дИJI a,цpecaц,al и С8JUlI8aJ1ИJ1.

IМAGE DATA_DIRECTORY CodeManagerTablej


IМAGE ОАТА DIRECTORY VTableFixups;
IМAGE ОАТА DIRECTORY ЕхроrtАddrеssТаblеJumрз;

11 ИнфОpи&цml пр81toиnиnиpо.аиноrо обрааа (,om.хо ДJUI


11 .нутреннего нспоm.ЗО8&НИJ1 - обку.n•• ,cs)
IМAGE_DATA_DIRECTORY ManagedNativeHeader;
IМAGE_COR20_HEADER;

Снова обращаем ваше внимание на то. что вам. как разработчику .NEТ-прило­
жеНИЙ. не придется иметь дело с информацией заголовков Win32 и CLR (за исклю­
чением того случая. когда вы захотите построить новый управляемый компилятор).
Вам достаточно пониматъ, что каащый компоновочный блок .NEТ обязательно со­
держит эти данные. используемые средой выполнения .NEТ и операцИОЮiОЙ си­
стемой Win32.

Программный код CIL, метаданные типа


и манифест компоновочного блока
в своей базе компоновочный блок содержит программный код CIL. который.
как вы помните. является промежугочным языком . не зависящим от ШIaТфоРМЫ
и процессора. В среде выполнения внутренний СIL-код компилируется Ина лету'
(с помоIЦЪЮ JIТ-компилятора [just-in-time сошрПег - оперативный компилятор)) в
специфические для данной платформы и данного процессора инструкции. В рам­
ках такого подхода компоновочные блоки .NEТ действительно могут выполняться
в условиях самого широкого разнообразия архитектур. устройств и операционных
систем. Вы можете вполне обойтись и без понимания особенностей языка про­
граммирования CIL. но, тем не менее, в главе 15 предлагается краткое введение в
синтаксис и семантику CIL.
Компоновочный блок также содержит метаданные. полностью описывающие
форматы содержащихся в компоновочном блоке типов. а также форматы внеIШIИX
Глава 11. КОМПОНОlIо~ные бмки .NET 449
nmОв. на JЮТО~~ сеылаe'1"C1,f ДaIЩЫЙ Щ>МIЩВОВО~ ~л:еR;. среда щ.пщJ!R'eшцI .NEТ
ифp.JIЬЩ7ет Э':rИ мет3ДЩIFIbl'е lJlIЯ нахождения '1"ИПDВ.p.r их ЧЛеПо.вl В бmщрнем файле.
длsr р~ени.я типов в :rmмнти лудвлеаноro ВБIВOва методов. Детали формата мета­
ДаЩЩ1Х .NEТбудут изу"lа'I'.ВОЯ 11 т:лоое 12 ПРИ рассмотрении сервисрв ОтОбрщКeRИЯ.
Кроме TQr0,. КQ'МIЮНtiвочный блок дОЛЖeI-J содержать ассоци:ирО1IанныИ мmш­
фе(:m (та:кж~ 1'!:азываемЫй мeтaдaннЬLМи ко.w:tО1ЮВОЧного блока). Манифест до.
н)'ментирует Jice модули даивОI'О ЕОМПОИО1l0m:ю']ю БЛORа.задает версию КQМПОНО­
вочного блока. а также прeдnaгает инфdрмацщо обо всех. внешюw RОМПQНОВОЧНБIX:
бло:н:ах. на ItOTopble сСылается даннЫЙ RОJ\ШОНОВОЧНЫ:Й блок (вот.nич:ие от библиотев
СОМ, :в;отQрыIe не предлагают способа донумевтированнн внешних зависимостей).
В npоцесееиэуче:вия маЩйaJiа этой главы вы по.Имете. ЧТD среда СШ ИН'I'енсивно
использует маиифест коипоновочнот'О бло:каnpИ определении внеmниx CCЫJlOК.

Заме-.анне. К Зтйму момеЮ'у, наверное, уже не нужно повторять, Что Mi'I просмотра профам­
Мl'IOro кода CIL КОМПОНОSО'I~JOГО б1l0К.ц, метаданны){ Т'ИПОВ или м;аНИ~ОТfJ М'QЖНО иrJПеnb3GВ~П.
ildasffi,ex8. Я. предilолarаю, Что вы будетечвсто испOJtЬЗОВ<lТЬ ildasm.ex€ при изучении
примеров ЭТОй тазы.,

~ неоБяз8теnьныe pecypcbl компоновочного блока


, Наконец, КОМДОНОВОЧНЫЙ БЛ(~к .NEТ МОжe'l' содер:iImТЪ любой набор ВС1роещ~
ресурсов,тaIOiX нан, например, щlнl'ограммы приложения, графl'Iчещme файлы.
ЭвyROвые фрагменты или 'габдlЩЫ CTPOJ~. П:rJатформа .NEТ обeqцеч:m!ает цод.цержку
аorlYJТlQl1lВуЮЩUX IOOМТWЦОВОЧНЬ1Хбл:ок:ов, liQTOPbl~ нt: СОДеptlЩ'"l' uичero. li"PQMe лока­
ли30ва,нных ресурсов. Это может :ионадобдт;ЬСЯ ТQl'да, Щ)rда "f1ЭебуетCS;{п~дос:гаml'1Ъ
ресурсы H~ разНI:!1X языltах (английском. немещщм и т.д.) uри СО3ЩI.ЦИИ: прщра.м­
много обесцечеНJШ, исполъsy~ого в рааны:,;, странах. Тема создauи.н {ЮПУТСТВУЮ­
щих lщмlюнQвочнык 'РJЮКОВ BDlXOAW:l' за р;rм:ки этой Ю-IИГ~. ПО цри цзучeюrn GD]i.~ :в
юаве 20 вы узнаете, lШН встроИ1J'Ъ ресурсы приложеRи.п в, IФмtr6ЕОБОЧНЫЙ блок.

Одно",одуnьные и мноrОМОДУnЬ*iые компоновочные блоки


Щ):мпоно,воЧНЬJ.i; БJIО.к МОЖНО СJCомпо,н:овать из однрf'(} или Н~('К09IЬКИ'X мoдgllEu.
МодУЩ> - эт(} прОсто обоfuща:lOщ.mтермШ-I для обозначения Д:ВОИЧflыX фшmов .NEт.
В БОЛЫIIинстве случаев КОМIIоновоЧJ-JЪ!Й бло.к компdвуетсн из одного мЬдулн. В та­
нам случае наблюдается взаимно однозначное соОтветС'ТНИе междУ (логичесItИМ)
RОМПОНОВачн:ым блоком и лежащим :в ero основе (физичеlЛGIl\ll) ДВОИЧНЫМ фaйJ1()м
(отсюда и nаЯВ,iШется термин одноМDдУ:7lbНЫЙ [Ссм,пОНОiЮчн.ыЙ блок),
Одномодулъные компоновочные блоки содержат все необходимые элементы
(информация, заголовка. npограммный КОД Сп.., метаданные ТИПОВ, манИфест и
ТptWyeмъie реС)'рСЫ:) ВQ.дщIМ narteтe " •.ахе или ".dll. на рис. ] 1.3 nокааана кОМПО­
зиционная схема ОДНОМОдУЛЬНОГО ItdмпоНОВОЧНОТО 'блока.
Мно;гомодУЛВНЫЙ КОМПоНоВочный блок, напротив, .является ШLбором _.NEТ"'ФЗЙ­
лов ;>; .dl.l, кохор:ме ивстaшrиpyются как одна lLOI:'Ическа:а еn;инm~а и ROR:I'pOлиру­
ЮТСН :ио единому иде.нтификaroру версии. Формально ОДИН из ю'их фaй.troв ".dll
йазыва:ется neрвuЧНbIМ модулем. он щдеpжй:r :мш;.uфecrn lCомnoн.DВОЧJЮго б1lOЮI (а
ташвенеоБХОДИМЫЙ.npограммный ход CIL, метаданные. информацию ЗЗI'(')ЛI'lвRa и
опциональные ре{)урсы). Манифест первичного М<ЩVЛН содержит записи о- каждом
из CШiзанных файлов ...'dl.l. 0'1 IЮ'ГОрblК. 00 за:в:ИСИт.
450 Часть 111. Программирование компоновочных блоков .NET

Одномодульный
компоковQЧtfbl1i блок

CarLibrary. dll

Манифест

Метаданные типов

СIL-код

Необязательные ресурсы

Рис. 11.3. Одномодульный компоновочный блок

По соглашению о выборе имен вторичные модули в многомодульном компоно­


вочном блоке имеют расширение *.netmodule. однако зто не является непремен­
ным требованием CLR. Вторичные файлы *. netmodule также содержат СIL-код и
метадШПIЫе ТШJов, а также мwшфесm уровня модуля. в котором просто записана
информация о внешних компоновочных блоках. необходимых для данного кон­
кретного модуля.

lЛавное преимущество построения многомодулъных компоновочных блоков за­


ключается в том, что они обеспе.чивают очень зффективный способ загрузки со­
держимого. Предположим. rIапример. что у нас есть машина, которая ссылается
на удаленный многомодульный компоновочный блок, состоящий из трех модулей,
причем первичный модуль установлен на машине клиента. Если клиент потребует
тип из вторичного удаленного файла *. netmodule. среда CLR загрузит двоичный
въmолняемый файл на локальную машину по требованию в специальное место, на­
зьшаемое кэшем загрузlClL Если каждый файл *. netmodule имеет объем lМбай-х: я
уверен. вы поймете. в чем здесь преимущество.
Другим преимуществом многомодульных компоновочных блоков является то.
что для НИХ можно создавать модули на разных языках программирования .NEТ
(что очень Удобно в больших корпорациях. где разныIe подразделения могут отда­
вать предпочтение разным языкам .NEТ). После компиляции отдельных модулей
эти модули можно логически "связать" в один компоновочный блок, используя, на­
пример. такой инструмент. как компоновщик (al.exe).
В любом случае следует понимать. что модули. которые образуют многомодуль­
ный компоновочный блок. не связаны непосредственно в ОДИН (больший) файл.
Скорее. многомодульные компоновочные блоки связаны только логически инфор­
мацией. содержащейся в манифесте первичного модуля . На рис. 11.4 показана схе­
ма многомодульного компоновочного блока. состоящего из трех модулей. каждый
из которых написан на своем языке программирования .NEТ.
r rJlasa 11. КОМПОНОJlOllные б"Оk~ .NET 451

МноrОf)lоду.nъИl:;IМ компоновочнъ.1Й 6no~

СSЬажрСиLib .dl.l

Манифест
(сс.ь!лки на дpyrиe
С8Я3аI'f"'ые файТ1Ы)
МеraдвНJiЫВ' lWЮВ

Метаданные типов

ClL-IФА

CIL-k(jД

tucalCa.tLib . natllOdul.

Метадамные ТИЛ08'
соmралуLоqo.b:mр

Рис. 1'1.4. Первичный модуnь заn'Иоывает ИНформациiO о в:rОРИЧНI\IХ МОДУ!lЯК е МЩ(И­


феСт компоновочного блока

R этому MOMeltry вы ($1 надеюсь) уже лучще nонимае-re вцутреннюю СТРУК1УрУ


ДВОИЧНОro файла .NEт. Теперь ·лоrpУЗИМСЯ· в обсуждение подроБRОC"l"eЙ ЦРQцеt:;Чi
построения и выбора 1tоифJttypации библиотек проrpaю.mого кода,

Создание и использование одномодульных


компоновочных блоков
Чтобы шu.L1ЦIИРРВ~ТЪ процесс понима.:вия IWМnО1ЮВQЧНЫХ блоков .NEТ, мы с
.вамнсоэд.адим одномu,цулъный комnОновоЧfIЫ'Й блОК" .dll (с именем CarLibra:ry),
содержaщl~Й 1;fе', больmой набор OТRpЫТhIX типов. Чтобы построить библиотеку
rrрщ-раммного <КОДа в VjsUш StudJo 2005. выберите рабочуЮ область Class иЬгагу
(Бm>JПlOтеIЩ классо.в) в oJdle соэдания npoектОВ (рис. 11.5).
Процесс раэраООтIЩ щцneй библиотеки мы начнем с создании абстрактноl'O ба­
зового класса Car .(~томоБИль). опредеmumцеrQ РЯД защищенных членов-данных.
.ДОC'I}'IIНЪЩ ~ез jJО.lI1!эователъсвие 'своЙСТ.Ва.
452 Часть 111. Программирование компоновочных блоков ,NET

WюСооtrоl Console
Lbгaтy Дpp!oca1ion

\'iindows Empt:'/ Ргоэес! SQl Server Pod:.t Ре Pocket РС


н
j: Project 2003 Др ... 2О03а ..
Ij
!
;'1
~
ц'ol .~
,.
;~':J1
~ :3
)';'.,;0

I1 Pod:et ре роа.! РС Рошtрс Smartphone s"",r1phoпe


I1 2003 Со,,!,.. 2003 CQf!$... 2003 Em... 2003 Дрр,.. 2003 о" ...
' •.7d . = " ,. , "._ " ~ '~' '~"" ",7,~'7,,,c,-~c- ,o,'O"'O - "'7~,"'= =cc","~=.,==~:,=_",,",=c"''''"

--=
~ P'~ Ь a""tn;! ~ 10 use f1 o~ appIicatJons

rСarLЮг.гу __ - _-~_~~~ - -:=~_==----==~-~=-


[C-:~_ode -.~~ -_-=~ ~_ -~~ -_
= _~ ~_= _ _ __~~]
-~-~-_~=-=_:=:_~_"' Вrowse .. ,

[~~~<;" -_-::~~-"'-~- - - _ о. о СЛ~II! ~ fur SaUtiOn

О дdd 10 soura! Conunl: d.t1ilbaSe 'SSТranng'

еж 11 car1re

Рис. 11.5. Соэдание би-бли-отеки- программного кода С#

Этот класс имеет ОДИН абстрактный метод TurboBoost (). в котором использует­
ся ПОЛЬЗ0вателыжий перечень (EngineState). представляющий текущее состояние
двигателя автомобиля,

us ing System:
namespace CarLibrary
{
// Представляет состояние двиrателя.
public епит EngineState
{ engineAlive, engineDead
/ / Абсwpахтный базовый lUIacc в данной иерархии.
public abstract class Car
{
protected string petNamei
protected short currSpeed;
protected short maxSpeedi
protected ЕлgiпеStаtе egnState = EngineState.engineAlivei
public abstract void TurboBoost();
public Car () { )
public Car (string паше, short тах, short curr)
{
petName = пате: maxSpeed = тах: currSpeed = curr:
r ГllаВ8 11. КОМnОflОSОЧliые БJ101(~ .NEТ 453
publlc зtr.i119 r~tNatne
{
get { retu~n petName: }
$et { ре' tЛате = value; '~
}

"P'lb.lic short Cur:rSpeed


(
get .{ re turл сих I"Speed; I
set aurrSpeed = v:аlщ~; }

риЫ ic s,h,ort MaxSpeed


( get { return тахЭрееd: } }
puь·Hc Engi neSti'tti:! Ещ;ril1еStаtе
{ ge.t ( retuтn egnВt.at.e; ) I

Теш;rn=o преДПОJIOЩШ;J. что у Еас есть два npiIМЫX "нас.i1едника.~ ТШ1а Cat. имеца
1wтopых MiniVan (мщmвЭН) и SP01':'tsCa:r [CIiOртивный автомобиль}. каждый из них
ПQДХО~ образом lJер~пр~ет аБСтрактный метод ТurЬоВ<юst () .

и9 ing з,уэ t.em;


usfnч system . .win.dow's . Forms:

ьатеsрас'е CarLibrary
;
public clas·s SpQrtsCar : CdI'
(
pUbJ.ic sports:Car О {
p\1blic SportsCar (st.ring паше, sJ:юrt ~, short С11П')
: ha-se (11aт€-, так, CUYr J i ]
puы1!c Gverl:ide ",oio TurboBoo's t ()
{

pubUc с,1аээ MiniVan : Car


{
publi с Mini'Van () 1
publia Мini',Van (s'tring name,short тах, short curr)
: ba:.se ( пате, JТ,a:x, curr), { }
public oveEJ;ide void Тщ:ЬоЕоо.s·t ()
{
11 XioIиJtsи с 'DУРБОВ&,JЩy]tDJf ВО'Р8'1!ИIIIЬ В'" чаО'l'O!
egТl.s·tate = Епgih81П:а .tе. engin.eD,eacJ.;
MegBageBo~. :Sb.aw (" Заоните в д.В' ТОСёрвис j" ,
"Н~ШИ!1а: СJ10Мё1ла· сь .•• ;")-:
454 Часть 111. Программирование компоновочных блоков .NET

Обратите внимание на то, что каждый из ПОДI(Лассов реализует TurboBoost ()


с помощью класса MessageBox, определенного в компоновочном блоке System.
Windows.Forms.dll. Чтобы наш компоновочный блок мог использовать типы, опре­
деленные в рамках этого внешнего компоновочного блока. дпн проекта CarLibrary
нужно указать ссылку на соответствующий ДВОИЧНЫЙ файл в диалоговом окне
Add Reference (Добавление ссылки). доступном в Visual Studio 2005 при выборе
РrojесtФАdd Reference из меню (рис. 11.6).

~YP;--t~~~._______. __~,__
CIImPanO!nt _ _ , RUnIime Раlh

S_.T;;;;~~ Ci "" - ' ·2::ii:З600.0 ·-v2,О ..-I060i 'C;WJoo


Sysl!!m. Web." 2.0.3600.0 .2.0.40607 C:\W1I'O
system.Web.МObiIe.dII 2,,0.3600.0 У2.0.4О6О7 С:\\'IIЮI
System.~.ReguWExpr~s..,ns.<1I 2.0.3600.0 "2.0.4060] С:\W!Ю
Sysl!!m.web,$erv1C8.dII 2.0.3600.0 ,,2.о.4ОБО7 C:\WII'()(

Sysl!!m.XmI." 2.0.3600.0 v2.0.40607 C:\WII'()(


vjscor." 2,0.. 3600.0 ,2.0,40607 C:\WlNDI
vN..-рСodd>rovide1" .<1 2.0.3600.0 _2.0.40607 C~!N)(
I>'jsj>c. .. <.а. 3600. О У2.0.4О6О7 C:\WII'()(
vj$Ib." М.3600.0 .2.0.40607
vjshw 2.0.3600.0 ,,2.0.'!0607
VJSs"p! m Ь." 2.0.3600.0 у2.О.40607
vjSvw"" .... 2..0.3600.0 "2.0.40607

еж 11 CВnaI

Рис. 11.6. Здесь добавляются ссылки на внеШI~ие компоновочные блоки. NEТ

Очень важно понимать. что в списке компоновочных блоков диалогового окна


Add Reference могут быть представлены не все компоновочные блоки .NEТ, имею­
щиеся: на вашей машине. Диалоговое окно Add Refeгence не отображает созданные
вами пользовательские I(омпоновочные блоки и не отображает компоновочные бло­
ки. размещенные в аАС. Это диалоговое окно предлагает список общих компоно­
вочных блоков. на отображение которых запрограммирована система Visual Studlo
2005. При построении приложения:. для которого требуется компоновочный блок, не
представленный в списке диалогового окна Add Reference, вам придется перейти на
вкладку Bгowse (Просмотр) и вручную найти необходимый файл *.dll или *.ехе.

Замечание. Можно сделать так, чтобы пользовательские компоновочные блоки тоже появлялись
в списке диалогового окна Add Reference, если установить их копии в папку С: \Program
Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies, но большого
смысла в зтом нет. На вкладке Recent (Недавние ссылки) предлагается список компоновочных
блоков, на которые вы недавно ссылались.

Анализ манифеста
Перед тем как использовать CarLibrary.dll в приложении-клиенте, давайте
выясним, иэ чего СКОМIIонована библиотека программного кода. Предположив.
что наш проект уже скомпилирован, загрузим CarLib['ary.dll в ildasm.exe
(рис. 11.7).
! Dluэ "/1. KOMnaHaBO'ltlbl~ б:nОI(И ,NEТ 455

Рис. 11.7. Библиотека CarLibrary.dll в окне ildasm, exe


Теперь О'FRpойте мш-шфест файла Ci;irLi'p r a~y.(ill д:вой:ным щелчком .НЗ mш­
ТQгpaммe MANIFEST, в первом блоке прогрШwfЩfОГ() Ж1да манкфе!i'ra указьtвaю1'сn
внешние и{)мnон.овочные 6локи.<веобходИМЪ1~ ~'Q9Т1!етствующему IЮ:МпововО'чному
блоку Д,l1Я правюn.ного ФУНRIЩО.аированин. ]щ щ.i: пОмНите; Car Library.dll не­
пользуОО' типы J:tз mstorlib.dll и System ,Wi ndqws.F.orтs . dll. и оба эm файла бу­
ЦV'I''YKaaaны в СПИDне маsифеста с пQмoщыf;'J лещ:е'Мы . a5sembly ex't ern внеПllШХ
связей КОМПоновочного БЛОlса .

. assembly ex~r1) mscorlib


{
. pub1i c keytoken = (В7 7А 5С :.;!З 19 34 ЕО 8'9 )
• 'Jer f ; .O: О : О

. assembly extern Systero. Windows. ГооГПlli


{
. р<ubliсkеуtоkед = (В7 i 'A 5С 56 19 3,4 Е' О 89 )
_ver 2: О : О : О,

Здесь каждый блок, азsеmblу extern C1IзбжeFJ директи;вil:rirи .publ ick.-eytoken


и ,ver. Инстру1ЩИЯ .publ1ckey tokeI} yшtзыветсяя ТОЛЬJЮ тогда. коtда компоновоч­
ный БJЮR имеет cmpoгIjЮ форму имени (подроБН0СТИ будУТ IJPИВедены в Этой главе
позже). Ле:kCeма .ver обоаначает (I<онеЧНQ же) ЧJilСЛОВQU' идептифщ<З.тор :версии.
После списка каталогизаци!'l внешних ссылок вы Оf)наружпте ряд л~ксеМ
.<tustom. идентифицирующих атриБутыlроння КРМПQНОВОЧНО1Ю блока. Провер:ив
файл АэsеmЫуIлfо.сэ. созданный в V'isIla1 Studlo 2005.:вы обнаружите. что эти
атрибуты представл:JПЩ' ТaRYЮ информацию {) КОМПОНОВОЧНОМ блоке. как назва­
НИе компании. торговая марJCЭ. и ~Д, (все сеответствmщие поля и данный MO~
мент пусты). Вшаве]4 атрибуты будУТ рассмаТРИВ<ЦЪС,iI1 подробно. поэтому noка
что не обращайте па :них большого ВIDt~НИЯ. ОднЗJtО следует знать. Что атри­
бyтm JotЗ lI.ssemblYlnf.o .cs добавJ1ЛIGТ ~ манифест ряд JIe}tceM .cu.st0 m.. например..
(Аэ s е1l\ыутit1е] .
, aiSse.mbl у 'CarLibIiHY
{
456 Часть 111. Программирование компоновочных блоков .NEТ

Systero.Reflection .Ass8lllblyТit1eAttribute: : . ctor (string) ( 01 00 00 00 00 )


.hash algorithm ОхОООО8004
.ver 1:0:454:30104

.module CarLibrary.dll
Наконец. вы можете заметить. что лексема.assernbly используется для обозна­
чения понятного имени компоновочного блока (Car1ibrary). в то время как лексема
.module указывает имя самого модуля (Car1ibrary.dll). Лексема .ver определяет
номер версии. назначенный для компоновочного БЛОRa В соответствии с атрибутом
[AssemblyVersion] из Assemblylnfo.cs. Подробнее об управлении версиями ком­
поновочного блока будет говориться в зтой главе позже. а сейчас необходимо заме­
тить. что групповой символ *в атрибуте [AssernblyVersion] информирует Vlsual
Studio 2005 о необходимости в процессе компиляции выполнить приращение для
идентификатора версии в отношении номеров компоновки и варианта.

Анализ CIL-КОАа
Напомним. что КОМпоновочный блок не содержит специфических ДЛЯ плат­
формы инструкций. а СQдержит независимый от платформы СIL-код. Когда среда
выполнения .NEТ загружает компоновочный блок в память. этот СIL-код I<ОМПИ­
лируется (с помоIцыо JIТ-компилятора) в инструкции, понятные для данной плат­
формы. Если выполнить двойной щелчок на строке метода TurboBoost() класса
SportsCar. с помощью ildasm.exe откроется новое окно. в котором будут ПОRaза­
ныI ClL-инструкци:и .

. method public hidebysig virtua1 instance void


TurboEoost() cil roanaged

// Code size 17 (Ох1l)


,roaxstack 2
IL 0000: ldstr "Rarorning speed!"
I1 0005: ldstr "Faster 15 better ... "
IL ОООа: call valuetype [System.Windows.ForrnsJ
System.Windows.Forms.D1alogResult [System.Windows .Forms]
System.Windows.Forros.MessageBox: :Show(string, string)
IL ОООЕ: рор
11 0010: ret
// end of method SportsCar::TurboBoost
Обратите внимание на то. что для идентификации метода. определенного ти­
пом Sport!,!Car. используется лексема
.rnethod. Члены-переменные. определенные
типом. обозначаются лексемой .field. Напомним. что класс Са! определяет набор
защищенных данных. например. таких как currSpeed .

. field faroily int16 currSpeed


Свойства обозначены лексемой .property. Этот СIL- код описывает открытое
свойство CurrSpeed (заметьте. что характеристики read /w rite свойства обозна­
чаются лексемами . get и . set).

L
r Глава '11, КОМnО~ОВОЧflые '6110КИ .NET 457
.praperty instahoe ltJt16 C;urr8peed ()
(
.get iпstапс€ int16 СаrLiЬr:а.rу.саr::gеt_Сш;r$рееd()
.set inst &dc:e Vb.!.d CarL:ibraxy .Car : :·set-:Сш: rSр е е d (iаt16)
.} I ! eI1d о! p ;r: op ert y Сау; 1 Curr'Spe e d

АналМ3 меrаДЭИtJЬIХ типов


Наноцед, если вы сейчас нажмете Rомбйнацию IWавиnx <сtrl+М>. ilda·,s.rrc.sxe
отобразит' MfТaдaнныe . ДЛЯ RaЖ.ДОI'О Йi3 типов, имеюЩ;ИХСЛ в кQМnQНОВО'П-ЮМ блоке
C~ rLibriil"y.dll (рис. 11. 8).

!/cOpeНil8le : . Сilr!,.illrЗ"9. dl1. i!;


""'.Iр : PI3I1l1CBC&-DIВ9-1fJI82-В8DА-IAD4Cf:С69Е57} 3 .,

G18b~
__ ;...'_ _ _ :0-.funetions
_ _ ___________ ________ ~------- _ ____ _ __________ _

- ..... ~--- --- - ----- - -----------.--- -",.. -~---- - --------

--~
. - - ----------=---------_._--------- -----.. . ------~-

'rgp~fHilPК' ~ CarLibYilry. Сзf' ! 021.Р111:1)


Fla!!" : [1'1I1i1icI [RotoLayDU"t] [Class] IRbstril( .
Еlltrщfs : 011'110" ПIJРtRi.'fl Sustеп.ОЬ·1i1l:t Jt ,
~f~i;~"'~'J~~~ ~·~~ll!;-'.:!:.."oo~~~~;~1 ~.

Рис. 11.8. Мета.цанные ДЛЯ типов из Ca r:Library.dll

Теперь, после TOI'O как.мы с вамц эаглднули внутрь RОМПО~ОВОЧНОl'О БJIока


Ca.r.Lib.rary.dl l,. мы можем праступить к шэстроtЩИЮ np.иложеЩ!Й .I<J1ИNJТа .

ИСХОДНЫЙ ход. Проект Ca(Lfbraгy размещен в 1l0дкатало~е. соотвеТОТilующем Qщве 11 .

Создание ПРИI10жеНИJl-клиеН'та в С#
БВJiД)1 TOfO.• '11'(1) все IЩIЬ1 'c :arLibra r y 6ЫJЦ1 ~)бъявлены с КJlЮ"Чt:lвЫМ словом
publ i >::; . дрytие. EOMnOI-ЮВОЧnыe блоки C1Jocofurы их исnо:rrЫlOвать. Напомним. что
·вы можете также объявлнть типы С ~е.цОЛЬ.З0Ванием клЮчевого Cl10ва С# il1t.e :rnai
(именно этот режl(М доступа в с# исuо.щ:,3YW'РJ по умолча:в:и:ю, ноrда вы определл­
ете тип без указания- public) . В~реНI;I;ие тиды мотут использоваться ТОЛЬRО тем
кампоно:вочным блоlЮl\:i. в котором ОЩ1 .оцределены. Внешние кпиеНТli1 не моryr:ни
вJ'Щеть . ни создавa:rъ внутреnниe :гипы ~ОМЩ)~ОВОЧНЫХ блCOOJВ.

3вмечВНИе. Сеrодня .NET 2.0 npeдnar:a~T ВОЗМОЖНОСТЬ УJ<азэть "друж:еJJlрбные" комf10новочныe


БЛОJ(И, ХОТОРЫ8 пqзврляют II1DПОЛЬ3fJ:вaTh свои 8нутреliние типы заданным I<ОМПОНОВ~~lblЦ бло­
каМ, ПоДробности мож~1O найти В' разделе 1I0к.умеfmщиw .NEТ Fmm!3woтk 2.0 SDKc -описани.ем
Iфасса lц1:еrлаls\'isiы.е'1'оАtt:riЬu tе .
458 Часть 111. Программирование компоновочных блоков .NET

Для использованИя открытых типов CarLibrary создайте новый проею' кон­


сольного приложения С# (CSharpCarClient). После этого добавьте ссылку на
CarLibrary.dll на вкладке 8гowse диалогового окна Add Refeгence (если вы ском­
пилировали CarLibrary.dll в Visual Studio 2005. ваш компоновочный блок будет
размещен в подкаталоге \Bin\Debug папки проекта CarLibrary). После щелчка
на кнопке ОК. Visual Studio 2005 поместит копию CarLibrary.dll в папку \Bin\
Debug папки проекта CSharpCarClient (рис. 11.9).

о ~ ~O . .r:t .}-) Se.ch !~ ~·1 rmJ·


JQ.~:~~~.;;dS;~~i;~;;;~1 f] Go
M"'".. Nortm ДniJv~щ ~ •
.F~~~_----8-iS-~t- х ~ I L:1
~ ь lЖ1 '*,М; cart..b'мy.". CSharp(a".
ODebu!1

Рис. 11.9. Visual Studlo 2005 копирует приватные комnоно80чныe блоки в каталог клиента

с этого момента вы можете компоновать приложеШ1:е-клиент с использованием


внешних типов. Модифицируйте свой исходный С#-файл так.

using System;
11 Не забу;ць~е 'ис:по.пъзова~ь' простравс~о имен CarLibrary!
using CarLibrary:
патезрасе CSharpCarClient
{
public class CarClient
(
static void Main(string[] args)
(
11 Создание спортивной кашины.
SportsCar viper = new SportsCar("Viper". 240. 40);
viper.TurboBoost();
11 Соадание JlИНИВэна.
Mini Van mv = new Mini Van ( ) ;
mv.TurboBoost();
Console.ReadLine();

Этот программный код очень похож на программный код приложениЙ. созда­


вавшихся нами ранее. Единственным отличием является то, что теперь приложе­
ние-клиент С# использует типы. определенные в отдельном пользовательском ком­
поновочном блоке. Запустите зту программу. И вы увидите на своем экране целый
ряд окон сообщений.
глава 11 . I':DMnOHOE\QI(flbIe БПОI(И .NET 459
Иf:.ХМt\,IIIЙ IОА. Пр0'8КТ CSl1arpCarClient разМещен в подкаталоr.е, соатветCТSую.щ8М главе 11.

Создание ПР'ИЛОJКения-клие' нта в Visual Basic .НЕТ


ЧТобы прсщемонстрировать НЗЫКОВУIo незаВИСИМDСТЬ lIJIатфОРМl1l .NEТ, соз­
дадим ;црутое Itо:нсольное npиложение (Vb.NetCar·Client) на Э1l()Т раэ с помощью
Visual Вaslc.NEТ (рис. 11.10).. Создай npоещ. yшuките ссыкунаa Car1.ibrary.d1]. с
помощью диалorовоro OКIХа Add Reference_

;:..
!-"- D_
----'tjpesi
:.;,_.~.~_ _ _ _ ~,i"
~ --=-
.-..-.,- - -- ._ ~. _ __ ._ __-~~ ___
tij ...... с,.
iiii@the'~

I
'* '.........
11 'iI5WIJ#'
Jjj''IIsuaI'c++
.'~
.
WlrldOws
~,.,. g;J
C:lbSh!br...-у
.]ау.'
>мndo"",
i3.
·Wtbc..,,~
11
. ~ OIt1@ ~'IJIII'S ~ C""trOl ubr!Or)' Lluy

.ф]
!
I
I~
\'Jhjo""5
' Y~
~r I'rt>ject
~
gQ..~ .;er
ijj.
'..,
~tPC Podoe!PC

ii
~ 2Oi:Jз "'•.. :2OOJQ...

.r
11
_toc
jS]
Pod;etK
l~jJ: ~
1; PO<I<I!tPC Scм tpllOl'!!. ~~
' . .__ rooзd.._
----'J.~ :100 ;1 r;;a,t - 2O~ COns :.. ~Eт ... ЮОЗ}W•• •
~il
No";t'ar~;~~~, ~- ~==~=
.~ .. \"'~ ~-
..

~ ~ ~-='-=-=--=~--=I,
~, 1c;'tI~ _ _ _ R!1 '!-tt---..-.--"
~J"OON~~'" 1'~~""'ы!'ц J О QmI' g._, fD: ~
DAдdID~Canir8~ . . . . . .

( ок I( с.о4

Рис. 11.10. Соэдание KOJ-ЮОnЬНОГО nриложенмя Visoal Вазl\) .NEТ

Как и в С#. в Vlsual Basic , NEТ требуется указать ' С:ПИСQR ~cex цростр~'I.lrств
ямеи. ксполъзуеМ»IX в текущем файле. Но в Visua1 Bask .N~ГдJlЯ этС!IV цр~длата­
ется использовать КЛIO"lевое СдОво I.щрогts, а не юrючевое слово lJ s in,g, как в С#.
С учe'roМ этого добавЬТе след,y1QЩИЙ оператор lmport;s 11 фай:т! ЦРQГРаммн:ого пода
Modulel.vh.
Imports ~~rLibrary

Мod ule Mo dulel

Sub Mai l'l()

End S'tЩ

tnd Modu l e
460 Часть 111. Программирование компоновочных блоков .NEТ

Обратите внимание на то, что метод Main () определен в рамках типа


Module
Уlэиal Basic .NEТ (который не имеет ничего общего с файлами *.netmodule много­
модульных компоновочных блоков). В Visual Basic .NEТ Module используется про­
сто для обозначения определения изолированного класса. содержащего только
статические методы. Чтобы сделать зто угверждение более понитным, вот аналог
соответствующей конструкции в С#.

/ / 'Мodu~e' в VВ . NEТ - 9'1'0 ПРОС'1'О иsо.пирован.ный кnacc,


/ / СОД8Р*uuШ С'1'а'1'ИЧ8схие ие'1'О.цu.
public sealed class Modulel
{
public static void Main ()
{
)

Так или иначе, чтобы использовать типы MiniVan и SportsCa.r в рамках син·
таксиса Visual ВаЮс .NEт, измените метод Main () так, как предлагается ниже.
ЗиЬ Main ()
Console.WriteLine(~***** Забавы с Visual Basic .NET ****-")
Dim myMiniVan As New MiniVan()
myMiniVan.TurboBoost()
Dim mySportsCar As New SportsCar()
mySportsCar.TurboBoQst()
Console.ReadLine()
End ЗиЬ

После компиляции и вьшолнения приложения вы снова увидите соответствую­


щий набор окон с сообщениями.

Межьязыковое пере крестное наследование


Весьма привлекательной возможностью .NET является меЖЪЯЗЫICовое пере·
крестное наследование. Для примера давайте создадим новый класс Visual Basic
.NEТ, который будет ПРОИЗВОднЫМ от SportsCar (напомним, что послеДl-ШЙ был
создан в С#). Сначала добавим файл нового класса с именем PerformanceCar.vb в
имеющееся приложение VisuaJ Basic .NEТ (с помощью выбора Project~Дdd Class из
меню). Обновим исходное определение класса путем получения производного типа
изSportsCar. используя ключевое СЛово Inherits. Кроме того. переопределим аб·
страктный метод ТUrboBoQst () , используя для этого ключевое слово Overrides.

Imports CarLibrary
, Э'1'О'1' VВ-'1'ИП RВJIJI8'1'CJl произво,цнwм С'-тиnа SportsCar.
Public Class PerformanceCar
Inherits SportsCar
Public Overrides Sub TurboBoost()
Console.WriteLine ("От нуля до 100 за какие-то 4,8 секунды ... ")
End ЗиЬ
End Class
Глава 11, KOMn0!10BIJ4Hыe блокИ .NET 461
Чтобы проверить работу НОIЮГО типа -класса" обно.ви:rе метод Mai n (1 МОдУЛя
так.

s iJ.b Маiп 1'}

Dim- drеашf.аr Ав Ne·w Ре.! f.оDnалсеСаr ()

, Нас.пе;цуемо& сзоttОI]iВО.
dreamCar •.?etName = "Над k"
t:1.re·a mCar . Тшr.Ь0Вооst ()
СЬп.g.jlе. Rе.а.ШЙп€' ()
Err,j$ub

Обратите B~!= пата, что объект ·dreamC.ar способен Bы3ьtвaть лю.бые от­
крытые члепы (н:ац:римf'Р. свойство JЭеtNсаше) по цепочке 'НаслеДОn;;iНИ:Ц,аесмcrrpл
ua 1'0. что базовьЩ клас~ определен I-la сОвершенно другом языке и ВДРУ1'ойбибли­
отеке прогрэ;ммното КQда.

ИсходНЫА ~. Проект VbNetCarCllent Р~М8щен В, J;lор.кэтМlоге, соотвеfDТ8ующем rлаElе ~ 1.

Создание и использование многомодульныx


kОМПОНОВОЧНЫХ блоков
. .

Теаерь, ногда:вы научились CTPO~TЬ )I использовать ОДНОМDдУлыше ко~шоно-


1Iочные БПОJlli. рассмотрим: :rтPO.цeco создания .:мнотомодУЛЪНЪtx. КОМПОНОБО<i'НЫX
блоков. НапОМ1IИJI,I, что МНQГОМОДУЛЬJ;lЫИ RUМnОНOlЮчн:ыИ блок- ЭТО просто Ha~
бор связa.IQIJ:;IX модулей, 'Щ{сталлируемых: :как, цельная e,цtпm:ца и контрOJlИРУ~
по единому номеру версии. H~ M01tieн'r создания этой юrnги в V1suaJ S{udio 2005
не предЛаГал.ся щаб.IIОВ IIРое~та длЯ' МНDГОМОдУЛЪНОГО' КDмпоновочноrо блока С#.
поэтому ддя построения Т8:Щ)ГО UPQeRTa вам придется исnOлыitова1'Ь :КОМПИ:Л8ТОР
RОМCUЩНОЙ СТРОRИ (cs!:.e~e),
для лримера м;ы с 'вами построим МВОГОМОдУЛЪНЫЙ компоновочный б.'IОВ: С на­
званием AirVehicles (~транспорт). Первичный Модуль (аirvеh.iсlеэ.dll) бу­
дет соде.ржаn. один ти,п КJIa1O:C!3. Hel.icCJpter (вертолет). Соответсогвующий манифест
(Тaщltе содерщащи:йся.в airvehicles .dll) каталогизирует доIlоJIните.1Iъный файл
* ,леtщQdul~ с И)\1ене.мufо .IJ~trnoc\·ule • .которЫЙ будет содержать другой тИП клас­
са, назътающийсн, конечно же. , Ufo (НЛО). Эти два типа класса физически содер­
жаТС8 В QТделъщ.rx двричных файлах. но мы сгрynnиpуем их Б ОДНОМ пространстве
щ.rен, назваНном AirVehic1es. Наконец. оба класса БУдУТ созданы с ЦОМP1IU>Ю С#
(JJlОЦI вы.есJ!И хотите, мож~е использовать иравные лаыки).
ДJlяна;'Ю-IJа от.кройт!= просroй теRcтовblЙ редактор (например, БлоlWОТ] » С03-
.дайте слецующее ОПР~Деление Jшаtса tJfo, сол-раltИВ затем ето в файле с ИМенем
Ufo.C$.
us.ijl.g SуstелL;

name.zp,ace JИrVе.:"iсlесs
!
462 Часть 111. Программирование компоновочных блоков .NET

public class Ufo


{
pUblic void AbductHuffi3n()
{
Сопsоlе.WritеLiпе("Сопротивление бесп о лезно");

Чтобы скомпилировать этот класс в .NET-моДУль. перейдите в папку. содер­


жащую ufo.cs. и введите следующую команду компилятору С# (опция module
флага /target "информирует" csc.exe о том, что необходимо построить файл
*.netmodule. а не *.dll или *.ехе) .
csc.exe /t:module ufo.cs
Если теперь заглянуть в папку. содержащую файл ufo.cs. вы должны увидеть
новый файл с иыенем ufo .netmodule (проверьте!) . После этого создайте НОВЫЙ
файл с именем helicopter. cs. содержащий следующее определение класса.
u$ing System;
namespace AirVehicles
{
public class Helicopter
{
public void TakeOff ()
{
Console. Wri teLine ("Вертолет на взлет!") ;

Поскольку название airvehicles.dll было зарезервировано для первичного


модуля нaпrего многомодульного компоновочного блока. вам придется компилиро­
вать helicopter.cs СИСП0ЛЬ30ВаниемOIЩИЙ /t:liЬrаrуи / out:. Чтобы поместить
запись о двоичном объекте ufo.netmodule в манифест компоновочного блока. вы
должны также указать флаг /addmodule. Все это делает следующая команда.

csc /t:library /аddшodulе:ufо.пеtmоdulе /out:airvehicles.dll helicopter. cs


К этому моменту ваш каталог должен содержать первичный модуль
airvehicles. dll. а также вторичный ufo. netmodule.

Анализ файла ufo.netmodule


Теперь с помощью ildasm.exe откройте ufo . netmodule. Вы убедитесь. что
*. netmodule содержит манифест уровня.модуля. однако его единственной целью
является укаэание списка всех внешних компоновочных блоков. на которые есть
ссылки в соответствующем программном коде . Поскольку н:ласс Ufo. по суги. вы·
полняет только ВЫзов Cons ole.WriteLine (). вы обнаружите следующее.

ь
]
Гмвв11, КОМГJOIiОВОЧliые б-JJОКИ .NEТ 463
.assembly ext:ern mscoriib
(
.рuЫiсkеУtоип = (В7 7А SC 56 19 34 ЕО 89 )
· ver 2; О : О : О
}
.module пiо. пеtпюdul.е

Анализ файла airvehicles·.dll


Теперь с помощью ildasm .еке oтxpofiTe первпчный МdдулЬ <11 irvehicles.dll
и рассмотрите манифест уровнн КОllЩ.ОНОщ;JЧl:Юго блока. Вы YB~, что лексемы
. fi1e д(щу.ментирyюt ассоциироваЮlые модули многtiМО,юуЛЫiОГО :КOМlЮНОВОЧНОFО
блока (в данном случае ufо.леtmоdulе}. Лещ;емы: .olass e.xterp ИсПOnЬ3)'1QТСЯ
для указamm имев внеIIIНИX ТИПОВ .из вторишюго модуJIя (,Uf.o]. иа . IWторые ~t'U9Т­
ся сc:.ы.JЩИ .

• a,ssemblyexte;rh rrlscorlib
1
.рubliсkеуiшkеп =. (В7 /Л 5С 51:! 1934 E1'J 89 )
· veI 2.: О : О; о

..азБеты у ail'vehi с 1 е;13·


{

.hash algorithm OxOOOP300~


· v€r '0: 0: .0 : О
}
.. file ufo. netmodu1.
, ..
• clBSS extern publi(" AirVehj.c.les.tifo
{
.fi:te .u:fo,.netmod~le
• с1аЭ.Б Q:tta2000002

Снова :подчеркнем. что м.шшфест RОМnQЕОВОЧНОТО блока ·mЩяt:ТС'Я еДJP'iСТВeJi­


НБrМ объектом. СВЯ:3ЫВа:ю~ airveh,iQles.dll iiI u·'fo.netme>dule. 'YIщзaнньre два
бинарНых ФаЙЛа Fle содержа)'CR в OДI;lOM, бо.nъшем -1'. dll.

Использование многомодуn'''НОГО компоновочного блока


Полъзо:ваreлей мноrомодульного Кl;1МPоновоч:воrо блока не ДОЛЖI,щ забm'lfl'Ь ТО.
что КОМIIОНОВО'ПlЪ1Й БЛQК. Па который щщ ссылаются. состоит И3 неQtОЛЬЩП мо­
дулей. Чтобы пояснить ситуацию. мы построим НОВ.6е приложешre-ltJIJfент Vlsua1
B!ls1c.1IIEТ с lЮмандной стрОlЩ. Сшщайте ноВbIЙ файл Client .vb, содержащий при~
lIеденпое ниже onределенце. Сохраните его в ТQМ месте, где находится JJaD1 много­
МОдулыIЫЙ компоновочный блОI(.
464 Часть 111. Программирование компоновочных блоков ,NET

Imports AirVehicles
Module Modulel
ВиЬ Main ()
Dim h As New AirVehicles.Helicopter()
h. TakeOff ()

I Э'Х'о sarpуsи'Х' *. netmodule по ~ебо.аJUq).


Dim u As New UFO ()
и. AbductHuman ()

Console.ReadLine()
End ВиЬ
End Module

Чтобы скомпилировать этот выполняемый компоновочный блок с командной


строки. используйте компилятор командной строки V1sua1 Bas1c .NEТ vbc.exe со
следующим набором команд .

vbc /r:airvehicles.dll *.vb


Обратите внимание. на то. что при ссылке на многомодульный компоновоч­
ный блок компилятору нужно указать только имя первичного модуля (файлы
*.netmodule загружаются ПО зanросу программного кода клиенrа). В самих файлах
*.netmodules нет индивидуального номера версии, и они не MOryт непосредствен­
но загружаться средой CLR. Файл *. netmodule может загружаться только первич­
ным модулем (например. файлом. содержащим манифест компоновочного блока).

Замечание. В Visual Studio 2005 позволяется ссылаться и на многомодульные компоновочные бло­


ки. Используйте диалоговое окно Add Reference и выберите первичный модулЬ. В результате
будут скопированы и все связанные файлы *. netmodu le.

к этому моменту вы должны чувствовать себя вполне уверенно при построе­


нии :как одномодульных' так и многомодульных компоновочных блоков. Конечно.
можно утверждать, что с вероятностью 99.99 % ваши компоновочные блоки будут
одномодульными. Однако и многомодульные компоновочные блоки MOryт оказать­
ся полезными, если вы захотите разделить большие по объему двоичные файлы на
менее объемные модульные единицы (это может пригодиться для сценариев уда­
ленной загрузки). Следующим нашим шагом будет формализация понятия приват­
пого компоновочного блока.

Исходный КОД. Проект MultifileAssembIy размещен в подкаталоге, соответствующем главе 11.

Приватные компоновочные блоки


Компоновочные блоки, которые создав~lЛись вами в этой главе до сих пор. ин­
сталлировались. как пpuвa.mныe lCомnoНО6ОЧНые блоlCU. Приватные компоновочные
блоки должны размещаться в том же каталоге, что и приложение-клиент (такой
каталог называется lCamaлoгом npuлoжеиwi) или в его подкаталоге. Напомним. что
результатом добавления ссылки на CarLibrary.dll при построении npиложений
CSharpCarClient.exe и VbNetCarClient.exe в Visua1 Studl0 2005 было копирова­
ние CarLibrary.dll в каталог приложеНИЯ-КiIиента.
1
j ГМВ8 il. KOMnOHOBDLff+ble БJ1Ol(~ . Nп 465
Когда праЕрамма--JCлиент испальзует 'I'Ипы • .определенные в этам внеш­
~e:м :КОМnОНDвачно:м: блоке. среда CLR праста загружает лаRалыtую жопию
Ca'rLibrary.d.ll. ВВиду того. чТО среда въrnолне:йИЯ .NEТ 1:le испО'лъ~ует реестр си­
стемы при IJОшше КОМШШОВOЧlIЫx блоков, вы можете переместить :камnоновочные
блоки CSh.a:rpCarC1i.ent.exe (или IJbIiletCarCllent.e~e] BMeore C'CarLibrary.dll в
другое меСТО на своей машине и успешно заnyсти:гыIpJложсши0;;
Де:ивсТa.m1ЯЦИЯ (а также тираиеиравание) прИ,iIOжения, исnолъзующеrе ИСКдIО­
чителън.о iIpНватн.ысItоМПОНОВО"lные блоки. не -требует .ос.обых УСИЛИЙ: нужно пра­
c'ro удалить [и.ли СЕОnир.овать) пanнynpИложенин. В отличие от COM-npиnожеНИЙ.
:щесь не нужно беспокоиться о десятках i4Qсйротевшшt~ .параметров. разделов и
ветвей реестра. Но· еще более важно то, что удал:ение прцватных l(ОМ:ПОНЩЮtffiЫX.
блОКОВ 'frn:Кaк lЮ lI.IШЯет на рабаТОСПОСОбность других приложеНИЙ. установленных
-на .мАшине,

ИдентификациSl приватных компоновочных блоков


Полный идентификатьр приватното' компоновоч}{ага БЛОI1:а састаит из. uоня.т­
Rato имени RОМIlО1ЮВDчно:rо блоха п ч:иеJ10Вorо номера ero версии. которые ДОJIЖ­
ны: быть записаны в манифест RОМПОНОВОЧНОГО блака. Пон.яrтшое UМя (friend1y
пате) - зто просто имя МодУJJЯ. содержащеroманифест lЮмпоновОчноro бдона..без
файлового расПJJфеНИJl. Так. есJlИ вы проверите манифест компоновочного блока
CarLibrary.sl1. •. то обнаружите -raм следующее (ВЗIШI. версия буде:г. скарее :всего.
другой) .

. аsзетЫу CarLibrary
{

.ver 1:0:1);54:30104

Rви,цу ИЗОJШPованнойприроды npивa.rnoго компоновочного блоха. имеет смысл


то. что С'реда CLR не исп:Ользует номер .версии при выяснении "!Idecтa размещения
такот.о ю>мпоновочнО:го блока. Прtщполагается. ЧТО . ДЛЯ npива'I'НЬ1X KOМ:nOНOBOtffiЫX
бщжов не обязательно ВЬШО.JIВJПЬ проверicy версии. поскольку ПРИЛОЖепие~к.;шен'r
являeтcR ер;инственН:ым Dp:иложением, "знаюЩИм" .об их сущесТВовании.. Поэтому
BIlOJIН'e вероятно, что на одной ыaшntiе БУдет много коIПiЙ одного If 1'01'0 же при­
ватнохо .кОМПОНCllIоЧНОТQ БJюка в разНЫх каталогах тtpиложеНИЙ.

Процесс ЗОНДИРОВ3НИJI
Среда вьm01IНemiЯ.NEТ ВЫJJсняiп место размещения приватного komnohoboq--
ноro блока с помоЩЬю технологиИ эондuрОвания.. 1I10тары.е на самоМ д.еле оказыва­
ют.ся намного менее агреосивными. чемХВЖетсв:из НаэвэЮtя. Зоцдщювание пр.ед­
('та:вляет Саба'Й процесс o:rображен:ив зanpосаввешвего компоновочного блоха :в
соответствующее :место раз:мещенJW запрошенного двоИчного файла. Запрос на
эarpyзку RОМn()НQВОЧRОГО блока мажет бшть либо Н€.яsнbVI(, лйбо ЯВf(blМ.. НеЯВfIЫЙ
запрос :выполняется тогда. когда среда СIЯ исполъзует манифест для выяснения
места расположения 'К(}мnонавочвога блока. ОI1ределенноrо с помощью. лексемы
.assem!t:>ly extern.
466 Часть 111. Программирование компоновочных блоков . NEТ

11 ВеRВJWЙ 8апрос 8&rpУ8ХИ .


. _88embly extern C_rL1br_ry
{ ... }

Явный запрос загрузки происходит при ис{юльзовании в программе метода


Load () или LoadFrom () типа Зуstеm.Rеflесtiоп .АээеmЫу. обычно с целью дина ­
мического связывания и динамического вызова членов запрашиваемого типа . Мы
рассмотрим эти темы позже в главе 12, а сейчас только npиведем пример явного
запроса загрузки в следУЮщем фрагменте npограммноro кода.

11 ЯвВIIЙ 8апрос 8_%'рУ8ХИ.


Assembly asm = Assembl y. Load("CarLibrary");
в любом из этих случаев среда CLR извлекает понятное имя :компоновочного
блока и начинает зондирование :каталога приложеНИЯ-lUIИента в поисках файла с
именем CarLibrary.dll. Если этот файл не обнаружен. делается попытка найти
вьmолняемый компоновочный блок с тем же понятным именем (CarLibrary.exe).
Если ни одното из указанных файлов в каталоге приложения не обнаруживается.
среда въmолнения прекращает попытки и генерирует исключение FileNotFound.

3амечанма. Если запрошенного компоновочного блока в каталоге приложения-клиента нет, среда


CLR пытается про верить подкаталог клиента с именем, соответствующим понятному имени за­
прошенного компоновочного блока (скажем, C:\MyClient\CarLibrary). Если запрошен ­
ный компоновочный блок обнаружится в таком подкаталоге, среда CLR загрузит найденный
компоновочный блок в память.

Конфигурация приватных компоновочных блоков


Конечно. можно инсталлировать .NЕТ-приложение с помощью простого ко­
m1рования всех требуемых компоновочных блоков в одну папку на жестком дис­
ке пользователя, но вы. скорее всего. предпочтете определить ряд подкаталогов

для группировки взаимосвязанного содержимого. Предположим, например, что


у вас есть каталог приложения С :\МУАрр, содержащий CSharpCarClient.exe .
В этом каталоге может быть подкаталог с именем MyLibraries, который с одержит
CarLibrary.dll.
Несмотря на предполагаемую связь мещду ЭТИМИ двумя :каталогами, среда CLR
не будет зондировать подкаталог MyLibraries, если вы не создадите файл конфи­
гурации с соответствующим требованием. Файлы конфигурации состоят из XМL­
элементов. позволяющих влиять на процесс зондирования. "По закону" файлы
конфигурации должны иметь то же имя, что и соответствующе приложение. но
иметь расширение *. config, и должны размещаться в :каталоге приложения-кли­
ента. Так. если вы хотите создать файл конфигурации для CSharpCarCl i en t .e xe.
он должен называться CSha r pCarClient.exe.config.
для иллюстрации создайте новый каталог на вашем диске С, назвав его МуАрр
(например . с помощью Windows Exploгer). Затем скопируйте CSharpCarClient .e xe
и CarLibrary.dll в зтот новый каталог и запустите программу на выполнение с
помощью двойного щелчка на ее файле. Ваша программа должна выполниться
успешно (вспомните о том. что компоновочные блоки не требуют регистрации!) .
Теперь создайте в С:\МуАрр подкаталог, выбрав для него название MyLibra r ies
(рис . 11.11). и переместите в него CarLibrary.dll.
,_ ~'JiQ ._ ' , I'tt~

PI.JC. 11. , И. Теперь 'C~I-r:b i b-rar.r. dll рз3Ме!.Ц3етс,1I' .IHWAI<8.Тa:JtQгe }})"i.,iЪ!" .:1.1," Щ

I10nьrrmiтech ВblIIO:ШtR'l1Ъ n~· ~ЩJa.В:&.QАУ 11'.11'0, чтq.с:piЩA Ci.R не<СМоЖеТ


яайт.и "CarI..1htaly" freJIосре.деТn€l-'цЮ::S Ifm'<lЛЩ"С пp'IYlQ~m" вы i1Wt}"ЧШr1!l н.оо-бр.а­
бооiQШroe исюпочевиеF-ilsliJОtF~und{фЩiJJ H~~).
Чтvбьr 1lЫПp'И'ifflTh С;ИТУlU).юа.• COSA'3ii:Te Н!llIЩ фW'f:л 1ШН.ф~ИiI евы. rp-
Са rGl:!.9":t1t ,е-Х'Е!. соnfigи'с_щр~~ его 'ц )J!,~-e., ~о.д-ержащ~ ПpшlОЖ'еН'Я,е
СБhахрСаr'Сliе,l'I,\:. ~~e (It д.ана.ож ~ 'э'r0 l'Ш.IЩ С; \ 1i!yAp.p'. От~соадaшп.m
файл и BB~ !& неro CiiJещющиЦ ~Д В T()"l:lf~r ~CЦ\). l'Щk rn:utilЭ3Нв lJИЖ~ ISr.!litG
XМL romнМ'(>я 'Ч}'ВС1"М1'е.Iд>НЫМ ~ реrи~ БIМВOJ)'ЩI'I.

<[-i':Ii!iП;Г± 9tt а 1:.iQ.c"jC>


<mitJottme->
~a;s<эerobl f~iцс;! :iДI'g ~tnl:n~= "JJ=; з.-С;Ь'?flJ;:!S '-m,i CJ::O'5-1:!ft -~Оlll;: 6t~ln . \т 1 и >
<p1r0blJI.g р;riViit.e1l,~ -th"""И}"Llьrai'iез··j>
<.' s:ssemblyJj-i !.'Icip g>
<.,' ~tJ nct i ше;>
/'..) =;Лi~\l:r,аtи'n>
фаimьt "'.corttJ.g .NE1' вceI71~ ШlчинаЮТatхорН~lJЫМ Э'-w-!\tCЦ"11Ш' <:.;;:;cm;tiч,шr~'tfurt>.
~ В .него ал~ '::rI:in'tln1e?- МQЖC-т со,д~ЖE(I'l, e.t'letYfe1iЦ' ~~[уВ.iЩJ il ;J')fj;> о
iwторыИ, l> соо'ю очерl!.щb. МШВeJr Q()держать .вооженщ.IЙ Щlf:мев"1' <-р:rсФ'i ЩIjI ~. Дi!W'
д-otimore ЩШМ~ вanбо.лее важ'ным: .roшлетС'я- -атри6yr p;;:i-V3WРа:~Ъ. ;:[Шl)iJЮ:abl-I:y ,,(1}ц'
:ИСПQfIhgyется для )'J(в:эаЮ1Я ~'МЛоrolt:р -м&т<щохе ПРи;IJОЖ~~я. rдr, Cp)'C-Ф;/l GL'R
ДI!IJI.Ж:Шl. ООУЩ~ зощr,npoвazmе.
Обра:~ оеобое ВНJiШIJНйe на тв. 'Ч'I'PЭ.'leМe:RТ ,qp\t"ooirJg:> це }"КЩJывае1:, .mюoй
.к.nм1lQНD'ВО'ШI:aЙ блон размЩfkeJ'CЯ:'8 СQотве"~-I'tYJ.Oщем llt;)дщц'а\'1ЦVС. JJоэrroмy вы
ш 1itillIte1'C 'Сka'аш!'Ь. Ч'Тd~ "'Ca~:c.ib1:ar)' раз~lещ~Т'("л П 1l0дц,м'ШIIiJ~ :lfу1.iЬХ,q'гlr;,--s. а
&:thIJt i 1 э -1'1 1l0ДЕtaт.Umге '6in", ЭЛемен-t' <рТ;ОЬ:1 ГJ.g;;. np~гa даетереде "'Иir~ CLR
~'lЩlit1O~ ПРИ notfuкe зanpошеН110rь 1{'ОМЩ>1ЮБQ'tЩIд'о 'б,ф)It~ :l(Iа;.ueц9Вfiт'h" укава:в­
ш.t~ ~~ ПО)ta не oб1iaружв.тся: хюрnoе еОll1'щд<tmrе.
468 Часть 111. Программироваflие КОМПОНОВОЧflЫХ блоков ,NET

Замечание. Атрибут pr iva tePa th нельзя использовать для указаflИЯ I~И абсолютного (С: \
Палка \Подпапка), ни относительного ( •• \ \ОднаПапка \ \ДругаяПапка) пути! Если вы
хотите указать каталог вне пределов каталога приложения-клиента, вам придется использовать

другой ХМL-злемент - злемент <codeBase> (дополнительные подробности об этом элемен­


те будут приведены в зтой же главе немного позже).

Чтобы у--казать с помоlЦЬЮ атрибута privatePat11 множество подкаталогов, ис­


пользуйте список значений. разделенных точками с запятой. В данный момент у
вас в этом нет никакой необходимости. но вот вам пример, в котором CLR дается
указание проверить подкаталоги клиента MyLibraries и MyLibraries\Tests.
<probing privatePath="MyLibraries; MyLibraries\ Tests" />
После создания CSharpCarClient.exe_config выполните приложение-кли­
ент с помощью двойного щелчка на выполняемом файле в программе Проводник
Windows. Вы должны обнаружить, что теперь CSharpCarClient.exe выполняет­
ся без проблем (если это не так, проверьте введенные данные на отсутствие опе­
чаток).
Затем, с целью проверки. измените (произвольным образом) имя файла кон­
фигурации и попытайтесь выпошшть программу еще раз. Приложение-клиент
должно выдать отказ. Вспомните о том, что файл ".config должен иметь префикс.
соответствующий имени приложеНИЯ-Шlиента. В качестве последней проверки
откройте свой файл конфигурации для редактирования и переllишиrе любой из
XМL-элементов символами верхнего регистра. После сохранения файла выполне­
ние вашего клиента должно стать невозможным (поскольку язык XМL является
чувствительным к регистру символов).

Файлы конфигурации и Visual Studio 2005


Вы. конечно. можете всегда создавать ХМL-файлы конфигурации вручную с
помощью своего любимого текстового редактора. но Visua1 Studio 2005 позволяет
создать файл конфигурации в процессе построения программы-клиента. для при­
мера загрузите в VisuaI Studio 2005 проект CSharpCarClient и добавьте в него но­
вый элемент Аррllсаtiоп Configuration File (Файл конфигурации приложенил). выбрав
Project q Add New Item из меню. Перед тем как щелкнуть на кнопке ОК. обратите
внимание на то. что файл получит название App.config (не переименовывайте
его!). Если после зтого заглянуть в окно Solution Explorer (Обозреватель решений).
то вы увидите. что в текущий проект добавлен файл App.config (см. рис. 11.12J.

liiI W!J CSbarpC~


~ Praper_
l1J tfiI R.ef<!ronces
"~M 1М
'($1 AssernЫyrnm.cs
'ii) с« дpp.cs

Рис. 11.12. Файл Арр. config в Visual Studio 2005


Глава 11. KOMtrDHOBO'lHlJle бnо~и .NEТ 469
ПОc:l'Lе этото вы сможете ввести все необходимые xML-элемент-ы для соз­
даваемшо .клиеН'l:а. И здесь следУет отметить К0е- что деЙетви:rе.IlЬИD ЦHTepe~­
ное. ltажДЪiЙ раз при БОМnИШЩllИ проеRта Visua1 Studio 2005 будет aBTOMaT~­
чеСКИ копировать дaнНhIe App ..config В каталог \ Bin\De-Ьuq, назца"fая HO~
ими С учетом соответс>rвухощего ~оrnашеlIИЯ о назначении имен (в.anри:мер. In,ш
СSЬа.rpСю::Cl iепt.ехе •.сопfig). ОДВaI(О :ЭТО происходит только "* том случае, I<Orдa
файл Rонфшурацш-t называется App.config. При ЭТОМ вам придетcn пом~рживать
TOnЪКo Арр .confi.g . а Vi,sua1 Studfo 2005 щрантирует. ЧТО ка1'а.ЛОТ приложения бу­
дет содержа1:·Ь самые последние и самые ПOJUlЪre данные (даже если В:Ы. например ,
переименуете свой щ>оект).

Утилита конфигурации ..NEТ Framework 2.0


СОЗДaJUJе файлов ". QOJ1fig .вручную Ife нв.л:яетCSiCЛИ1IllЮМ большой пр06демОЙ.
00, тем не менее, .NEf Framework 2.0 SDK предлагает Щicrpумещ, КОТQРЫЙ ПQЭЩ>­
J1Яет строить XМL-файлы конфигурации в рщ.пшх l"РафщчеСRQГО ИRтерф~са пощ,~
зователя. Утилиту Мicrоsоft .NEТ FТащewoгk 2,0 COnflgura:ti011 МОЖНQ найти ~ IЩII­
не Администрирование. размещенной в панe.nи управЛt.'НИН Windows. Запустив этот
инструмeнr.:ВЫ увидите ряд оrщнй 1tонфиrypаци.и (рnс. 11.13).

Рис. 11,.1З. Утиnита ICОНфИI)Ip8Ц"И .NEГ Fтamework 2.0 Сопfiguгаtlоп

Чтобы построить файл *'. Qonfig :клиента с ПDМоЩЬW этой ут:t-IJШТЫ, первым
шагQМ должно бъiтъ добавление того приложения.. которО'е будет ~онфигуриро­
ваться. дпя этоro 1ЦеЛЧНОМ правой кнопки :мыши ОТRpОЙ're K()нтeXC'I:Нl)e' мета УДJЩ
AppТlcations (IIриложеНИII) R выберите JIYШI.'Т Add Щобавюъ). В пщшившемся диало­
ГО1ЮМ Diilie 'Вы можеtne обнаружить придоже1Iйе lIЛЯ lЮНфurypадм при услоlЩИ.
что она вьшолнялосЪ ранее с помЬ'ЩЫО програ:ммы ЛРОВОД1IИК Windows. &ди это не
'IЩ(, ще.лкните на кнопке Other (Другие) 1'1 зайдите в nanкy nPОГJ>Щ>[JI{Pнwиента, ко­
торую вы.хоо:и:re конфигурйрова:ть. для даиноro npимера сле.цует ':в:ыlра'Iъ r.rpиложе­
ние 'l7bbletCar:Cl ient .ехе. созданное:в этой главе рзнее (поищnте его в rum:кe Birl).
После этого вы ДОЛЖНЫ увидеть НОВЫЙ дочерний УЗeJl. MI\ пощхзавО.Щ1 рис. 11,14.
Если щеЛk1:JYТЪ правой lU-юпкой МЫII1I'i на уме VbNetCarClienl и. QRТивизировать
пункт контекстНого · меню Своистаз', тО внизу шmНИВЦIerося ДШlлогового ок;на !~ !.
увидите :нжстовое поле, где можно ввести '3RaЧf;fШЯ, которые будут ПРИПИСЮJ L.J
атрибуту privatePath. Просто ДЛЯ провеРRИ введще имя подкат;щща Test Dir'
(рис. 11 .15).
470 Часть 111. Программирование компоновочных блоков .NET

Console Root
в·. ,NEТ Frome_k2,O ConflgJr.tion
EI ~ М)' CompUter
," Дs$eтb!y Cache Loсаtюп: C:\Documents апd Settings\Andrew Тnoе!sеп\му
'. с@) Configured AS$OmbllM Documents\My 600ks\C# and the ,[ЧЕТ P!atform 3rd
,- В Remotinli Se1'V1Ce5 Ed\Code\Ch_ll
Ш !iфI Runtime 5e<:urity РоМе)' Code\VbNetCarClient\ VЬNеtСагСliепt\Ып\DеЬШ;J\ VbNetCarClient, еяе
В ~ App!ications
вое'·!
:' .
М'
Assembly Dependencies
This аррliсаtюп сап have its own configured assembIies and
rBmoting s8rvic8s, These settings only affect this опе
.Сф Confio;lured Assemblie. application, Use the tasks below to get started,
В Renю~ПQ ServiCe5

Рис. 11.14. Подготовка к изменению конфигурации VbNetCarClient.exe

Рис. 11.15. Указание приватного пути зондирования


в рамках графического интерфейса

После щелчка на КНОПJre ОК вы можете посмотреть в каталог VbNetCarClient\


Debug и убедиться. что в типовой файл *.config (который VisuaJ Studio 2005 соз­
дает д.л.я программ vв .NEТ) был добавлен нужный элемент <probing>.

Замечание. Как вы можете догадаться сами, ХМL-содержимое, сгенерированное утилитой конфи­


гурации .NEТ Framework 2.0, можно скопировать в файл App.config Visua! Studio 2005 для
дальнейшего редактирования. Позволяя инструментам автоматизации генерировать начальное
содержимое, вы, очевидно, уменьшаете для себя объем вводимого с клавиаryры текста.
Глава j 1, КОМJ1OНОIЮ'lные блоки, ЫЕТ 471

общедостynныe компоновочные блоки


Теперь, :когда вы nO'fII'JМaете., НЗR I!II-IСтaшmpоватъ и ROНфигурировать npиватные
пОмnонов()чные блоки. МЬ1 с вами можеМ·nPИС'ryIШтьR рассмотрению роли оощеОО­
cm!JТlЮ:ilX KoM11DJ-[oвочных БJЮ1ctJВ. Подобно приватному компоновочному блоку, 06-
щедостy:mrыИ КОМIIi:mовdчнБIЙ блан предОТaвJШет собой набор ТИIi:ОD и (возможно)
р.есурсов. Самой ОЧ0ЮЩНGЙ особенностью omцeдoCJY11НЫX компоновочных блоков.
в отличие от npиватНБIX, яв.лнет(ш ТО. что OДEI~ и та же IWnИН оffiцедос'ryIПЮГО КОМ­
ШIНОВОЧНОГО блока может ИСПОЛЪ30В8.ТhоJ1 разными npилоЖffiJИ.ИМ'И.
Так. многие npиложения в ЭТОЙJШИI'е указывают CCЫJlКy :Н'а System.Wlndc,w$.
FОI:rпs .dll. Но есJПt~lamянутъ в каталоги этихnрm:lOженИЙ. вы не обнаруЖJJ.­
те·там np:иватные копииэтото кОМпоновочного блока ~NEI: Причина в ТОМ,ЧТО
System, Wiпdоw s. Fиms . dll инсталлируется. КаЕ общедостy:unы:й компоново~:mый
блОК, Очевидно. что. именно такОЙ ПОДХОД нужно использовать при создании би­
блпотeюI кла(1СОВ, ИCIЮльзуемой на уровне всей м:amины.
Общедостy:nный 1ЮМIIановочн:ъrii блок не ИlfCталлируетсн в каталог использую­
щеro ета npиложеяия Общедоступные ЕОмnоновочные блоки УСТaliаВJIИВaiOТся В
GAC (Global Assen1ыly еаеЬе - L'лобалЬный кэш I<OМIIОНовочных блоJюв) .. Каталог
оле Явлне7'СЛ ПОДКaтaJIо:гом с именем ii.ssembly в корневOJ!tl каталоге Windows (на­
пример, с:\wiпclоwз\авzеюыl),' нак поШiа3Во на рис. ]] .16.

I>OЗfSf1'f JJ dSO<!За
:bDзfsi-1fа~
ЬоЗf5f7F1Jd5!J/IЗa МSJL
.Ьщflif1Щd5lia3;l f'lSlL

=
Рис. 11.• 1'6. I1lОбальный кэш компоноаочны)( БJl10КОS

Замечание. Выполняемые компоновочные блш:tи (* • .ехе) уствнавirивать в каталог аде нельзя.


общедосl'}'J1I'1ыI1 КОМТ10tЮ80ЧНЫМИ блоками могут быть ТОЛЬКО блоки. имеющие ВИД *.0,11,

Строгая форма имени


Перед установкой компоновочного блоца в GAC Вы дpщJtНЫ назначить комшщо­
во'пюМj' блоку строгое I.LМЯ, :которое уника.д::ьm.rм образом и:денТJIфищrрует И;:IДа~
тeшIданноrо двоичного объекта .NE1'. При этом 4Ледует ПЩf,Иматъ. ЧТО ')rздатedlем~
мощеТ быть 11 аrдещ,ный ирограммиСТ, и подразделение D p~ отдCJ1ЪЩ)Й ком­
цании. и ОТДедьНая компания целиком.

Н некоторомсмысле cтpo;roe ими является cobpeme;I-ЩЫМ .NЕТ"Эl'.вивалентом


cxeМPI GU:ID-идеIЦИфИКации СОМ. Ес.пи вы имеете ОПЦJТ работы с СОМ. ВСПОМ­
НlIТё' О том, что Аррт (ццентифика:тор пршroшemщ) - это GШD !GlobalJy LТnique
472 Часть 111. Программирование компоновочных блоков .NET

IDentifter - глобальный уникальный идентификатор). характеризующий конкрет­


ное COM-приложеJ-Iие. В отличие от GUID-ЗJ-Iачений в СОМ (которые являются
ничем иным. как 128-разрндными числами), строгие имена создаются на основе
двух связанных криптографических ключей (называемых открытым ключом и се­
кpemн.blМ 1CЛЮЧDм). Поэтому строгие имена оказываются гораздо более стойкими в
отношении искаженцй и должны быть ближе к уникальности, чем простые GUID-
значения.

Формально строгое имя компонуется из набора связанных данных, в болыпин­


стве своем задаваемых следующими атрибутами уровня компоновочного блока.

• Понятное имя компоновочного блока (которое, напоминаем, является именем


компоновочного блока без расширения файла)

• Номер версии компоновочного блока (назначаемый с помощью атрибута


[AssemblyVersion) )
• Значение открытого ключа (назначаемое с помощью атрибута [AssernblyKeyFile))
• Необязательное значение идентификатора культуры, используемого для ло­
:кализации (назначаемое с помощью атрибута IAssemblyCulture))
• Встроенная цифровая подпись, создаваемая с помощью хеширования всего
содержимого компоновочного блока с использованием значения секретного
ключа

Чтобы создать строгое имя для компоновочного блока, вашим первым шагом
должно быть генерирование пары юпочей (открытого и секретного) с помощью ути­
литы sn.exe .NEf Framework 2 .0 SDK (что мы с вами сделаем чуть позже). Утилита
sn.ex.e генерирует файл. обычно с расширением *.snk (Strollg Name Кеу- ключ
строгого имени). который содержит данные двух разных. но математически свя­
занных ключей (зто так называемые "открытый" и "секретный" ключи). Если ком­
пилятор С# получит информацию о месте нахождения файла *. sn k, то во время
компиляции значение открытого ключа будет записано в манифест создаваемого
компоновочного блока с помощью лексемы .publickey.
Компилятор С# также сгенерирует хеmиpованный код на основе всего содер­
жимого компоновочного блока (ClL-кода. метаданных и т.д.) . вы должны знать из
главы З . что хешupoВQ.Ю-LЫЙ код представляет собой числовое значение, уникальным
образом характеризующее вводимые данные. Так. при изменении любой части ком­
поновочного блока (даже одного-единственного символа строкового литерала) .NEТ­
комnилятoр генерирует уже другой хешированный код. этот хеmиpованный код ком­
бинируется с данными секретного ключа из файла *. s n k для получения цифровой
подписи, встраиваемой в СLR-заголовок компоновочного блона. Процесс создания
строго именованного кОМПоновочного блока схематически показан на рис. 11.17.
След,ует понимать, что данные секретното ключа нигде в манифесте представ­
лены не будут - они используется только при создании цифровой подписи содер­
жимого компоновочного блока (в совокупности с генерируемым хешированным
кодом). Напомним. , что основной целью применения криптографии на основе ОТ­
:крытото и секретного ключей является гарантия тото, что во ·вселенноЙ" .NEТ ни­
какая пара компаний. подразделений или ИНДИВИдУУМов не получит одинаковых
идентифи:каторов. Так или иначе, по завершении процесса создания строгого име­
ни компоновочный БЛОR можно будет установить в структуру аАС.
Глэsа 11, KOMnoHoBO'IHble блоки ,~EТ 473

MeTaдaftfibIe типов

CIL-JroА

'Рис. 11, 17. В процессе ко,МПИШЩИИ на о,сно,ве QТ!фЫ'ГОГО и C8KpeT1i0rO ключей генерируerо~ циф.
ро,вая t"1OiL\ПИСЬ, KOTopaSj затем встраll1ваеrся е I<ОМПОНОВОЧI-tЫЙ блок '

ЗJlм~.. анме. Строгие ИМ8/i1а d6~спеч~ваlOТ И Щ1РЕЩеленную с.теnень защиты от п'Отенщtат НЫХ, нз·
РУШlIГТелей, пытзющихся модифицировCfrb 'СQдерЖJal'Aое KOMrlOHo,BOLj~JOr(1 блоКiL С учетом Зтаго
в рамках .NEТ CJЦи,аетея целес;ообразfJЫМ сФЗдавать строг!>!'! им~ ДJ1Я каЖДОПJ комп'()новочно­
го блока, а I~e только ,цля тех коМпоновочнЫХ блоков, которые предна3~laчены ДЛя установки в
CТPYIC1YJilY GAC

Создание строгого имени ДЛSl CarLibrary.dll


давайте продемонс:rpируем весь процесс ваэначе'EtИН строгого :имени компоно­
вочному 6локу CarLi.1Jra.i"y. созднншхму в этоЙ rnаве ВЫIiJe. Оrnpойте соответству­
юпnШ прое'Ь."Т 11 'Той Cpeдfl разрабетки. RОТЬрУЮ :вЫ предпочитаете ИСПОльзовать.
Первым делОМН}'ЖiliО сгенерИровать необходимые дaFI1JЫe ключей с ПОМОЩЫО yтJf­
литы $n.exe. ЭТQТ.инструмент имеет множество оIiЦИ"Й "Rома:ндной' стро1'tИ. ff() нам
сейчас шшвдобитсн ТОЛЬКО флат -К, Е:ОТОРIdЙ дает команду rенерировать новый
файл, содержап:(ИИ информацию отнрытого иооJtpё'I'tlQГQ IШIOчей. СdЭдайте новую
ruuшy МуТезtКеуРсCJ,i r на своем диске С и перейдите в нее :в оюrf: JroМАНЦНОЙ c-Ipo-
ни .NEТ. После З"l'"ого. чтобы сгенерировать файл MyTesТ_F.'eyPair.SDk. введите иrе­
,ttyIOщу1О ком:андУ.

S!1 -К NyTeStKeyp"i 1;: • ~nk

Terтepъ. ПО.JJY"ЩВ дaщп.rе своеro юпоча, с~общитекомroIЛНТОРУС# о том, rдt: раа­


мещаетен файл MyTestI<eyPair. snk. Обратите. вНИМЩiие :на то, что при создании
рабочего прострэнства ДЩl JПобоro HOBoro проекта С# в Vi5ual Studio 2005 один да
ИСJШДНЫХ фвй;лов проеR'1:'а получает имя ,)\s,semblylnfo. са (он размещается в рам­
как узла Properfies в окне Solutiof1 Explorer). Этот файл соде:рж:uт рцд свойств, описы­
ващщих КDМnОНОВDЧЩ.>IЙ блок Атрибут AssernbJyKeyFile уровня кОмrIОНОВQчноrо
блока можно ИСDШIЪ30IШТЪ дЛЯ информировamm КGМn1ЩЯТора о месте расположе­
ния файла *.sn:k. просто yщrnmте цутъ :& виде стропового параметра, например:

[а.аэетЫ У,; ASBe:mbl y'K€yF"ile (@ "е ~ \MyTest.:ReyJ?.a.L:r \МуIеst.кеуРair .6n~")]


474 Часть 111. Программирование компоновочных блоков .NEТ

Поскольку одной из составляющих строгого имени общеДОC'I}'lIНого КОМПоновоч­


ного блона является идентификатор версии. мы укажем его и д.ня CarLibrary.dll.
В файле Assemblylnfo.cs найдите другое свойство. имя которого AssemblyVersion.
Изначалъно его значением является 1. О. *.

[assembly: Assешыlvеrsiопn ("1. О. *") J

Напомним. что цденТИфИRaтор версии .NEТ компонуется из четырех числовых


значений. По умолчанию Vlsual Studio 2005 автоматически будет выполнять при­
ращение номеров компоновки и варианта (что обозначается групповым символом
М*") при каждой коМIIИЛЯЦИИ. Чтобы установить фиксированное значение иденти­
фикатора версии для компоновочного блока. замените групповой символ коннрет­
ными значениями номера компоновки и варианта.

/ / Форма'J! иоиера версии:


/ / <:roJliUlНlolЙ>. <ДОПOJlНИ'1'е.пJoIlWЙ> • <1СОJdПОROIIха> • <вариаи'l!>
/ / Дпн: х--.цо:roо ЭJlеиеR'l!а ДОnYС'J!ИМЫ змчеНИR O'l! О ДО 65535.
[assembl у: AssemblyVersion ("1. О. О. О") ]
Теперь компилятор С# имеет всю информацию, необходимую для генерирова­
ния данных строгого имени (поскольку вы не указали уникального значения для
параметра локализации с помощью атрибута [AssemblyCulture]. будУТ "унаследо­
ваны" текущие параметры локализации вашей машины). Выполните компиляцию
библиотеки программного кода CarLibrary ildasm.exe откройте ее
и с помощью
.publickey. с по­
манифест. Вы увидите, что в нем теперь используется лексема
мощью которой документируется информация открытого ключа. а с помощью .ver
представлен номер версии. указанный атрибутом [AssemblyVersion] (рис . 11.18).

r MANJfI S Т : _ ii !5 1 'Х I

.custo. instance yoid [ascorlib]Syste•. Runt!


.publickey - ( •• 2_ •••• __ 8 ••••• 9~ ••
•• 24 •• •• 52 53 41 31 00 84
3.3 81 61 02 98 ОС 3Е lJ5 26 А8
03 35 1С 3В 8~ D~ 12 •• 2Е 2.
3С 02 18 71 13 Е1 Е3 Е1 59 О9
СС 00 69 6В 9С 11 43 А3 FE CD
68 61 3В D2 11 С9 19 9А FA 90
2_ А9 F6 89 11 АВ с_ 5А 58 18
-н А6 70 С3 АВ АЕ 36 F1 С1 DF
Е4 41 DF 9F F8 24 16 S9 FD 3F
.hashalgorith~ Iхl.II8.И4
.uer 1:0:1:'
)
.RDdull! CarLibrary.dl.l

Рис. 11.18. Строго именованный компоновочный


блок записывает открытый ключ в манифест

Назначение строгого имени в Visual Studio 2005


Перед тем как установить CarLibrary.dll в структуру GAC. заметим. что Visual
Studio 2005 позволяет указать место расположения файла *. s n k на странице
Pгopeгties (Свойства) проекта (в Visual Stud.io 2005 такой подход оказывается более
rлава 11. КО:~ПОНDв:.Очные блоки . NEТ 415
npедпuчтителъным. П()СКОЛЬ1Су' np:и: ИСПОJ1ЬЗОВrotuи атрибута IА;;sещы1I'i:еуFii l~J
генерируется предупреждение ХOМI1Иля.;гора). Выберите вкладку $ignlnQ (ПОДПИ~Щ)
и. }\Казав путь к ф3Й.r'1)'1< • sпk. ycтaнoвuтe флажОR Slgл ~he assembly (Подписать КОМ­

.
ПDlЮВDЧПЫ:Й блок). КЗ1( nO:к.аэано на рис. 11.19.

)(

! , - - -----1 0Si;,tthe~
1:ey~

0lJ58 а ... ffo!


Ic:\МJtТesttevP<itI,Мo.·1est\:':eyP« .....

Рмс:.11.19. ИнФормация о файл!! * . snk на страни~е CВO~ТВ npоекта

Установка и удалеttие общед,оступuых


компоновочныx блоков
Ваюпочи::rельным шагом оу,дет ~Ш:IOЩW.. (теперь уже строго именованноЩ б:и­
БJп.Jотеки CarLi.t nary.cl.ll в струптуру
.
GAC. ыроще
-
всего установить общедоступ-
цый КОМПQЦОВОчньЩ блОК в структуру GAG. п~тащив 'фaiШ1ЮМПОНО1lOЧIiОГО блона
С помоЩЬЮ мыши .ц пarIlО' С: \Windows \assembly в uporpaмМe Пров.одник Windows
(это очень удобно при teO'nJ:POBaJ-.IИl!).
Кроме ;'Iтoro •. NEr FramewQrk 2.0 SDI(. цpeд;naгaeT S'тилиту командной стро­
ки g-аСll t .i 1. ex€, цоторан поэ,щщяет ILр(l~МатрИйатъ иизмешпъ содержимое GЛC.
ВтаБЛ.11.1 mщa$lН..bI не)tОТОРыеоIЩИИ ~clltil.eKe (:используйте флаг N. чтобы
увидеn. все оrщии).

Таблица 11,1. опции gacutil.ex.e

опцм. Описание

/1 Устанавливает OTPOF{) именоваН~lыrr J(ОМПОI10ВО\jНь/'й блох в tтpYК1YPY GдO

/и Удаляет к0мnрново'1ныи блох ~ЗсТРУк1УРЫ GAC


/1 Отображает компоновочные БJlOКИ (МЛИ конкретный КОМnОНOIiOчНblЙ бnок) в стрУ!С'Уре GдC

Испо..тц.зу-я любой из уха.зш1НЫX по,/:QЮДОВ, установите CarLibr:a:ry. dll .в струк­


'IYPY G:AC. Лосле этого вы должны увидеTh. -ЧТQ ваша бибдиотека в C'l'pyКтype npй­
cyrCТBYeт и yrшТQIВает~ (рис;. 11.20).
476 Часть 111. Программирование КОМПОНОВОЧНblХ блоков .NET

Рис. 11.20. Строго именованная общедоступная библиотека


CarLibrary (веРСИR 1.0.0.0)

Замечание. При щелчке правой кнопкой мыши на пиктограмме любого компОновочного блока от­
крывается контекстное меню, с помощью которого можно открыть страницу свойств компоно­
вочного блока или деинсталлировать соответствующую версию (это эквивалентно использова­
ния флага /и с утилитой gacutil.exe).

Отложенная подпись
При создании своих собственных компоновочных блоков .NEТ вы можете назна­
чать строгие имена. используя свой персональный файл *. snk. Но ваша компания
или подразделение могут отказать вам в доступе к главному файлу *. snk. ВвидУ ис­
ключительной важности файла. содержащего открытый и секретный КЛЮЧИ, этому
удивляться не слецует. но это. очевидно. является проблемой. поскольку у вас (как
у разработчика) будет часто возникать необходимость установки компоновочных
блоков в структуру GAC с целью тестирования. Чтобы позволить такое тестирова­
ние без предоставления настшuцего файла *. snk. вы можете использовать метод
оmложен.нОЙ n.oдnиcи в случае с файлом CarLibrary.dll в использовании такого
подхода нет никакой необходимости. но мы все же предоставцм краткое описание
соответствующей процедуры.
Процедура отложенной подписи начинается правомочным mщом. имеющим до­
cryn к файлу *. snk. с извлечения из зтого файла значения открытого ключа. Для
этого используется ЗП.ехе с опцией -р. позволяющей создать новый файл. содер­
ж~ значение открытого ключа.

вп -р myKey.snk testPublicKey.snk
Файл testPublicKey.snk можно предоставить всем разработчикам для созда­
ния и проверки cTporo именованных компоновочных блоков. Чтобы сообщить ком­
пилятору С# о том, ч.то соответствующий компоновочный блок должен использо­
вать процедуру отложенной подписи, разработчик должен установить для атрибута
AssemblyDelaySign значение
true (ИСТШlа). а также указать файл с псевДОКЛЮЧОМ.
как параметр атрибута AssemblyKeyFile. Ниже показаны строки. которые следУет
ввести в файл AssemblyInfo.cs проекта.

[assembly: ASSemblyDelaySign(true)]
[assembly: AssemblYKeyFile(@"C: \ MyKey\testPublicKey.snk)]
Глава 11. KOMnQrlot\o<IHble ·БЛОJ(101 . NEТ 477
Замечание.• Прlll использовании V/sual StudiO 2005 те же атрИбуты МЩКНО создать "ВИЗУШ1blЮ · , ис­
ПQПЬЗУЯ 'ВО;3МО~fIOСТИ, предлагаемые на СТJ!ЩНИ'Qе с:sой'ста провкта .

После разрешев:и.я 01:/J·i)жеFIRОИ ПОДПИСИ ДllЯ lЮМfIОН.QВОч.ного блока следующим


шатом .fLВmreтсв: оТКЛючение продесса проверЮf ЦQДШIСЦ. КО1'Орый для цомщщавоч·
ных блО1ЮВ. устаноВЛ.енвых 13 GAC. l3ЫI10Л1-1ЯеТСЯ авТОМ~'fИ'iеCJЩ. Чтобы ПРОЦУСТ.t-ITh
процесс проверки падrrи:си па текущей м.ашине. укажкrе (дл;н sn.exe) ОЕЩИЮ -vr.

ПО заве'РЩ~ЮU1 тестиJЮ'Ваии~ КQмпоноrючный блок МОЖН'О отnрa.llИ'rЬ уполно­


моченному объекту. имеющему доступ к настоящему файлу с oткpът.rым и секрет­
ным t(]Щ}чами.. Ч1'обы обеспечить Д8Qичному файлу НЩ.'ТОm.цyIО цифровую под,:писъ.
Эдесь СIЩI:Щ необходимое реntеШ1е обеспеч'Ива.ет .S:i'J • ехе. -на этот раз при исполъэо­
ВaIOIИ флarа -r.
SIl.exe- - I му}'uJsепiыl.dJ.lG:\ м укеу\mукеу.snkk

Чтобы :в зIOtЛючение активизировать процесс проверirn ПQ.ДIIИ'си, ПрJP.~~ется


флar -vu.
S!! . e.)ote -viJ Myll,65embly. dll

КояеЧНQ, ~ЛИ вы (или ваша :компания) создаете Но:мrtоновочные блоки таЛЬJlЮ


.для 'BJ-tyтPецн['1'О ИСПОЛЪ30БаЮm. JЩМ npоцедура отл_ожеI-!I-ЮЙ ПОДПИСИ может НИROГ­
mt не понадо6иТЬCJI. Но еСЛИ вы СОЗЦ41.ете JfО:МnОНО!lочные блохи .NE1· АЛИ ВНenIЮiX
nOJ'pебителеЙ. возможность ОТДОЖеНН(}Й подписи позволяет обеспечить безuпас­
нрстъ с раз}~FIыми ограничwипми для. .всек 3aЮ:JтересошuUi'ЫX СТОРО'Н.

Использование общед,оступных
КОМПОНОВОЧНЫХ блоков
при построеmrn. приложеНИЙ. использующих общедостynные ItOМIЮНtПlочны:е
блоки. е,динствеШIое о.rnич'ие .от случая использОвании прйватного КОМПОRОВОЧНО'
ro б:JIока заключается в том. каи вы ССЫлае1'есь на соответствуюшую fiИб.JlШ)теКу 11
V16дa1 Studio 20.0.5. фaI(тически с точки зрения И'спользуемоl'О ине-трумента IIИКa ­
кой рааницы нет (ВЫ Псе равно исполъ..~у:ете диалОГОlюе окно Add Refe.rence). Важно
ПОIШТb то.. что это ДИЭJiоговое окно не rюЗООЛUIN вам сослаться на NОМIIОНfJВОчн.ыИ
блок путем просмо·rpа пanжиаssеtnы1у,' ПОПЫТIili сдел8.Тьэто будут бесполезны­
ми - возмOJIWОC'tь СОС)1атьси на выделенный lЮМIIОНОВОЧНЫЙ блоu пр~доставдена
це будет (рис. 11.21).
Вместо зтого на вкладке Browse нужно перейти в каталог \B::Ln\Debu:g ()ршu~
~!lы.ыыwгоo npоекта (рис. 11.22).
478 Часть 111. Программировани.е компоновочных блоков .NET

2,0,0.0
I , ОДО

8,0.0,0
I О,2. З600. О
10.2.3600.0
., In . ? , ~ffi!l . n

Рис. 11.21. Не правильно! Visual Studio 2005 не позволяет со­


слаться на общедоступный компоновочный блок путем пере­
хода в папку assembly

Рио. 11.22. Правильно! В Visual Studio 2005 для ссылки на


общедоступный компоновочный блок нужно перейти в каталог
\Bin \ Debug соответствующего проекта
1 Глава 11. J<О'м.ПФНОВQLIНl:it,е 6лоltи •NEТ 479'
Учитьша.я; этот (раЗДIJaЖЭЮТnИЙ) факт. создайте новое консольное ПРИЛОЖ~JЩе
С#с именем Sr.laredCarLibCli.erit и проверьте возможность ЩШCWьЗОВa::щI,fJ CI!O~
nmОВ.

L1sing CarLibrary;

name.sp~oe Shar:edCe:rL±bCl i 8'JJt


{
Clil5S' Program
{
static void Main (stri ng [] args:)
j
Spa rtsC.ar с = new Spprt.sCar () ;
с '. Тш;ЬоВо оst !) ;
солsсlе .ReadLi ne () ;

После IЮМПилJЩИ.Й прИJJЮжеНИЯ-.IЩИента. в программе ПРОБодаю.: Windows пе­


рейдите в Jtаталог. содержащий файл Shаr еdСаr1i ЬСliелt.. е хе, и убед.итесь втом.
'ПО V.isuaJ Studio 2005 не C1COnupoвan.a Car LibrarY.cl H в каталог прил.оЖеш1Я-КЛИ­
епт.а. При ccbl.Jiкe на каМnOlЮВо<tJiЫЙ блок, манифест которего СQдеР1ЮП значе:нnе
.pu1>lic~ey. vlsua1 Studio 2005 преДПOJ1RГае'I; что строго имеНОВа1:-llif.IЙ Н:ОМПОНО­
воЧJtъrЙ блок. -вероятнее всеro. устаномен в структуре GЛС. и IЮэтому Не "У'ГРУЖ­
дaeг~ себя RОПИРОВанием ДВОИЧllого фqЙiла.
В , iшчестве !tpЗ.ткого даМeч3lntя: ~ на ТО, ЧТQ мОЖНО "-засnW:ИТь " Visu.a1 Studio
20Ь5скопиро.вать оБЩедоcтynный: IwмnоfIовочI-lыi::i блок В каталоt клиента. для ЭТОГО
нужно выбрать компоновочный блок из У~Щ Refer'ences в шmе S~lu;tjQn Explorer. -а за­
тем . в окне I'roperties (рие, 11.23) установить для свойства Сору Local (КDnИровать в
локалЪНJ"10 папку) апа~e:IOfе Tri..le (истина) !'iМCCTO значения. Fal зе (Jtожь).

Р"с. 11.23. С ПОМОЩЬЮ св()йства Сору Lbc~l MO;t(-


но ~зас{s.вIo1ТЬ· системУ вьrnОIlНИТt. копиjJаван~е Ul'porD
именоаанН'ой библиотеки' программного кода

'i
1
480 Часть 111. Программирование компоновочных блоков ,NET

Анализ манифеста SharedCarLibClient
Напомним. что при генерировании строгого имени компоновочного блока в
манифест компоновочного блока записывается значение открытого ключа. Точно
так же. когда клиент ссьmается на строго именованный компоновочный блок. в его
манифест записывается ~конденсированное~ хешированное значение открытого
ключа. обозначенное лексемой .publickey. Если с IIОМОЩЬЮ ildasm.exe открыть
манифест SharedCarL;i.bClient.exe. вы увидите там следующее .
. assemb1y extern CarLibrary
I
.publickeytoken = (21 9Е F3 80 С9 34 8А 38)
.ver 1:0:0:0

Если сравнить код открытого ключа. записанный в МЭ1Шфесте клиента со зна­


чением для открытого ключа. IIоказанным структурой GAC. вы обнаружите IIОЛ­
ное совпадение. Напомним. что открытый ключ является одной из составляющих
строгого имени, идентифицирующего компоновочный блок С учетом этого среда
CLR загрузит только версию 1.0.0.0 компоновочного блока CarLibrary. открытый
КЛЮЧ которого имеет хешированное значение 219ЕFЗ80С9348АЗ8. Если среда CLR
не найдет компоновочный блок, имеющий такое описание в рамках GAC (и не най- ~
дет приватного комnоновочцого блока с именем CarLibrary в каталоге клиента).
то будет сгенерировано исключение FileNotFound (файл не найден).

Исходный код, Проект ShагеdСагLiЬС/iепt размещен а подкаталоге, соответствующем главе 11 .

Конфигурация общедоступных
компоновочных блоков
Подобно приватным компоновочным блокам. открытый компоновочный блок
можно конфигурировать с помощью файла *. config клиента. Конечно, ввиду того,
что открытые компоновочные блоки находятся по известному адресу (в структуре
GAC) , для них не указывается элемент <privatePath>. как это делается для при­
ватных компоновочных блоков (хотя, еCJШ клиент использует как общедоступные.
так и приватные компоновочные блоки. элемент <privatePath> в файле *.config
может присутствовать).
Файлы конфигурации приложения можно использовать в совокупности с обще­
доступными компоновочными блоками для того, чтобы дать указание среде CLR
выполнить привязку к другойверсии конкретного компоновочного блока, Т.е. что­
бы обойти значение, записанное в манифест клиента. Это может понадобитъся по
целому ряду причин. Например, представьте себе, что вами бъmа выпущена версия
1.0.0.0 компоновочного блока, а через некоторое время вы обнаружили в ней де­
фект. Одной из возможностей исправления ситуации может быть перекомпоновка
приложения-клиента, чтобы оно ссъmалось на новую версию компоновочного бло­
ка (скажем. 1.1.0.0), свободную от дефекта, и переустановка обновленного клиента
и новой библиотеки на всех соответствующих машинах.
Другим вариантом является поставка новой библиотеки программного кода с
файлом *.config, который автоматически даст среде выполнения инструкцию по
Глава 11. КДМПОFlО;80ЧНl>lе рлеkИ .NET 481
орnвяэ.ке R JlОВОЙ версии (своБQдно;0: от еоответ(;'твующего деф~а). После уствлов­
ки новой верcиu библиотеди В струщуру GA.С оригина,льНЫЙ КЛJJe:RТ сможет ра60-
тать без ПQВТQPНОЙ JЮ!vЦnI.,iJ~ и переуста:новки-, а вы не буде'Fе опасат&сн эа свою
рецутаЦИЮ.

Бот еще одна ари:мер. Bbf npеДlIОЖИЛИ нервую версщо (1.0.0.0) КоМnQIJОВОЧНОГО
бllIока. ~ободного от вCJIКИХ дефектов. но I10сле МСС:ЯДS-ДВ)'Х его щ<сuлyатfщии ЕЫ
i добавиди в КQМДОНОВОчtIЬЩ t'lЛО15 новые фymщooНqл::ыlые ~ЩМQЖНости. ПО:i\ВOЛНlO­

j щие п.ереЙХи R .версии 2.0.0. Q. Очевидно, СУЩе!ствующ~е дрщщ~ещI.Я ЩIИf:ЩТ~. HO~


'ropble БыIи СНОМIlwrиpовэ.ны В условиях сущеСТВОJ3ани.fl тоцъщ.l версЩf 1.. 0.0.0. ве
Щ;tеют никакрrо "предс-r;l1щеНИfl о новых типах, тщ( что Щ ба;ювblЙ IфQграммный
ЩЩ ~Ja эти новые тиды не ССЪJЛаетс~.

Предполагается, что новые приложеБи;Er-шщенты будут И()ПОЛЫlOвать новые


фymщцов:алъныев03МОЖJЮс,ти версии 2.0,0.0. В par..цt:~ ПЩl.гуформы .NEТ выиме­
ете возможвдС'.rь отправить версию 2.0. О. О JoЩ целевые ~ы, -ЧТОбы эта новая
версия работала вмеСТе с бол~е·{."ТароЙО версией 1.0.0.0. Если необходимо. 0'1-
щест:вУlOщие к.rшенты могут ддна.м:и;чески перенацравлятъся на загрузку версии

2.0.0.0 (чтобы ПОlJY'ЩТЪ ДQ.CТYD нее И:ОВЩМ:, более ео.вершенным возмt>жност:ям) с


ПОМОЩЪ1Q фaiIJШRОНфИГУРaщm придоже:нд.ц, тqже без пов.торной коМIIИ1rJЩИИ И
переустановки приложеНИД-КЩIеfiта.

Фиксацм~ ·в·е'рсии общедоступного компоновочного блока


Чтобf:i[ прказатъ, :как осущеС"qIляетс;я дцнамич:есltая nРИВЯЗRа- R Rою<ретной
версии Qбщедостуruюго fЮМПОIЩВОЧНОГО ~лока. откррй:ге программу ПРОВОДfIИК
Windows u СКQIщруй1'е reI<ущую ае.реию CarLibraJrY (! .О.О.О) в друтой: подкаталог
(здесь ;::tJЩ него выбрано fra~ание ·Версия ]") корнещ)й ЩUIКЯ проекта. Ч'I'о6ы за­
фихс:ироватъ эrгy версию (рис. 1 J .24).

Рис. 11.24. Фиксация текущей версии саЗ:LiЬrаrу.dll

Соэдание общедоступного компоновочного


блока версии 2.0·.0.0
Теперь обновите свой проект CarLlbr а r у. добавив 11 него оаределе:ние нового
перечпи Ml1SlCMeclia. QП'ределяющего четыре вощ..юЖнъvt музы~альных устрой·
ства.
-
482 Часть 111. Программирование компоновочных блоков .NET

/I СодерЖИ'l' иифор~аЦИID об источнике музыки,


public епuт MusicMedia
{
musicCd,
musicTape,
musicRadio,
musiсМрЗ

Также добавьте для типа Car новый открытый метод. который позволит вызы­
вающей стороне включить один из имеющихея проигрьmателеЙ.

public abstract cl a ss Ca r
{

public vo id Tur nOnRadi o (boo l musicOfL, Mu s ic ~1 ed i a rnm)


(
if(mUSlcOn)
M6ssageBox.S how (st rirщ . Format ("ШУМ (О}", mm));
el se
MessageBo x. Show (" И ТИ ШИН$ , •• " ) ;

Измените конструнторы класса Ca r , чтобы отображалось окно MessageB ox . в


котором подтверждается использование CarLibr ary именно версии 2.0.0.0.
public abstra ct c l ass Car
(

publi c Са.!: ()
(
Me ss ageBox. Show( " Ca r 2 .0 .0.0 ");

publi c Car (string пате, s hort ш ах, s hor:t c1Jrr)


{
MessageBox. Show ("Car 2.0.0.0");
p etN ame = пате ; шахSрееd = тах; CUL r Speed спrr;

Наконец, до начала новой компиляции не забудьте изменить з начение версии


этого компоновочного блока на 2.0.0.0 с ПОМОIЦЪю изменения значения. передава­
емого атрибуту IAs semblyVers iOl1 ].
// CarLibrary версии 2.0.0.0 (теперь с музыкой!).
[assembly: АззетЫ yVers ion (" 2 . О. О . 011) J

Если вы теперь заглянете в папку \ Bin\Deb ug проев:та. то увидите, что там


присутствует новая версия компоновочного блока (2.0 .0.0). в то время как версия
1.0.0.0 в полной безопасности хранится в подкаталоге Верси я 1. Установите этот
новый компоновочный блок в папку GAC в соотв етствии с ИНСТРУН:ЦИЯМИ. предло-
r Глава 11. KOt4nOHOBO'lH'ble блоки ,NET 483
жеЩIЫМИ в ·этоЙ шщве ВhШIе, Обрат.и:те вНИмание на то, чте теперь вы будете иметь
цве версии ОДНОГО и ТОГО же RОМ:ПШlовочноtо блока (рис. 11.25).

Рис. 11,25. Параллелыное выполнение

Если теперь в протрамме ПровоДНИ1\ Windows выпО.1):нить имеюшуюс.я црограм­


му ShаrеdСаrLibСliЕшt.ехе с помощью ДВойноrо щелч;ка на ее IIИктогра:м:ме, вы
не YвuO'uJТre окно с сооfuцеШiем ~Сэr 2.0;0.0", поскоJU>1!у сФОтветствующий. манифеСт
сшщиалыro запрашивает версию 1.0.0,0. Та1( t<зJ( же Тогда: датъ·уцзэа.ни:е Gре:де CLR
о ТОМ;, "ЧТE;lбы среда вьшtJJlНЙЛа npив.язку н версии 2.0'.О.0? Я рад. что Bl!.I об этом
СЦР8lIIИВаете ,

ДИнамическая привязка к конкретной


версии КОМПОНОJlОЧНОГО блока·
дл.я ТОГО чтобы c:p~a CLR загружала ·оощедос J"уuный ROМnОНQВОЧПЫЙ блок опре­
деленной версии. О'J;.JU-IЧНОЙ от тОй версии. которая указана в манифес:ге ROмnorю­
}ЮЧJЮГО блшпа:. СЛедУет создать файл ... с.(шf--i-g с элементом <depenaentA$sembly>
впутри. Брамка.х этщ-вэле.м:ента нуЖно задать элемеlfi <aSS9ffi'b lyldent 1 ty>., нато­
рый укажет поняmое имя ROМnавовочного блока из соотв.етствующerо мщmфес:та
1:I:.1JJJecrra (В нашем rrримере .это CarLib·:r:ary) 1'1, возможно. :необяззтельноес :шач:ение
атрибута си lt u re [ему ~ОЖЕЮ ыj}эначи:ть пустую СТРOl'У. а :мошно ВОdБЩе опустить ,
если предnолarаетсн использовать параметры. прецусмотренные для данной M~­
ШИНЫ по у:мо.,цчанию) . Кроме 'l'oro. в рамках элемента <dерепdеIi .tАssеmblу.::. сле­
дye'l' задать э,пемент <b·indi n.gRedit·eGt> •. Y-юlЭЫИЗТОЩиИ версию, RОТОРЩt задана
Б маниф~те в нn.cmоящuЙ момент (атрибyt oldVersi on ). и версию из С1'рYh'1JlPШ
Gле, к.uторую щгжнЬ '3'ЗJ'ружать вместо версии. указанной в ма:нифе('те (атрибут
деw'J'еrsWп).
В I.ШТaJIQге приложени.в Sba.redCarLib'Client создайте новый фаЙ.)l Rонфигура­
ци;и Sb-аrВd(:аrЫЬGli алt..ехе. с опfig и пемесmте в нет:о спедующиеXМL-да.нные.
Конечно. ЭJЩчение вашего ОDфliIтоfО Ю1Юча будет отличэ.тьсл от T~Д·O. которое со­
держится в показанно:м RйЖе примере программн:ого :кода, но это ·з:н:aqенще Bl:iI мо­

жете выясди'п) путем ПРОCМD'1'ра манифеста ltПИента с помощыo ildasrn. ехе шrn: в
структуре GA(::.

<ccmfigu:ration;>
-< r:цI1. t irr,1? >
<аsэerobl уВiпdiпg- xmlns="urn: sсheUti1з-m.iсrщ,о ft -(')от: asm. V] ">
<depen den tA$sembly,>

1
484 Часть 111. Программирование компоновочных блоков .NEТ

<assemblYldentity name="CarLibrary"
рubliсКеуТоkеn="191еЬf55б5беОа43"
culture=""/>
<bindingF.edirect oldVersion= "1.0.0.0"
newVersion= "2.0.0.0"/>
</dependentAssembly>
</аssеmblуБiпding>
</runtime>
</configuration>
Снова выполните программу SharedCarLibClient.exe. Вы должны увидеть со­
общение о том. что загружена версия 2.0.0.0. Если же для атрибута newVersion
вы укажете значение 1.0.0.0 (шщпростоудалите файл *.confi9). будет загружена
версия 1.0.0.0. поскольку среда CLR найдет в манифесте клиента ytШзание о том,
что необходимо использовать версию 1.0.0.0.
В файле конфигурации клиента может присутствовать несколько элемен­
тов <dependentAssembly>. В нашем случае никапой необходимости в этом нет,
но предположим. что манифест SharedCarLibClient.exe ссылается также на
общедоступный компоновочный блок MathLibrary версии 2.5.0.0. Если вы за­
хотите перенаправить клиент на использование MathLibrary версии 3.0.0.0
(вдобавок к использованию CarLibrary версии 2.0.0.0). то в этом случае файл
SharedCarLibClient.exe.config должен выглядеть тап.
<configuration>
<runtime>
<аssеmblуБiпding .xmlns="urn:schemas-microsoft-corn:asrn.vl">
<dependentAssembly>
<assemblyldentity name="CarLibrary"
рubliсКеуТоkеП="191еЬf55б5беОа4З"
culture=""/>
<bindingRedirect oldVersion= "1.0.0.0"
newversion= "2.0.0.0"/>
</dependentAssembly>
<dependentAssemb1y>
<assemblyIdentity narne="MathLibrary"
рubliсКеутоkеn="191еЬf55656еОа4З"
culture="" />
<bindingRedirect oldversion= "2.5.0.0"
newVersion= "3.0.0.0"1>
</dependentAssembly>
</аssеmblуБiпdiпg>
</runtime>
</configuration>

Снова об утилите конфигурации. NEТ Framework 2.0


Вы вправе надеяться. что должна быть какая-то возможность генерирования
файлов *. с о n f i9 общедоступных компоновочных блоков с помощью средств гра­
фического интерфейса утилиты .NEТ Framework 2.0 Conflguration. Подобно по­
строению файла *.config ДЛЯ приватных компоновочных блоков, первый шагом
rt1aeC1 i 1, Ком!\оtiО8QЧН6IВ блоки .NET 485
эдесь JПtЛЯ'етt.я CCЫJLК'a на соотве1'СТВУЮЩИ'Й файл *. ехе" дла. Jl;oтoporo ВЪПlOЯ~
ннется :жонфигураций. Для примера удашrrе TOJlb~O что созданный вами файл
5h·aJr~dCarLibClient.exe. ' confi'9. Теперь в ОННе утилпrы .f';lET Fraд1ewor:k 2.0
CvtJfiguratJotl дрбавьте сCЬ1ЛRy на SharedCa.t-L1ЬСl ient. ехе, щелкнув правой ююп~
Roii :мьmm в cТP0'.Reyaнa Applicatlons (Приложения). Затем р~ройте ПИJtТ0FpЩl:fМy
(+) и выберите пqдузел Cdnfiguгed АssеП1ЬНеs (Снонфmyp:ироnщц-ще компоновочные
бщжи). После этого Щf."..'lJCНИ're на ссъuше Conflgure an AssembIy [Сщ)NфliIГ.YPироватъ
:КОМПОНОВОЧНЫЙ блок) в правой части оЮIa УТИJlЙТЬr.
Вы УВИДИ'Iе дивлoroвoe ошro. котороел~вам СoздaIЪ <tЛeмeнt' «depend~tAssemыl>>
с помо:щыо ряда элементов графического интерфейса. Сначала с помощью кноцки
neреlШючателя вЫберите Choose an аssеmЫу from the Ilst of а~sеmыiээ thir;> applJcation
USBS (Выбрать RОМП()lIОВОЧНblЙ блок из CIU'lCItl1 I<ОМnОНОВОЧНprx блORОВ, испо~е~
мых данным приложением). Ч'ТО. па cyrи:, означае'l' требование ПО1Щзать маиифе~
Затем щemшитe на кнопке Choose, A13sembly (Выбрать кoмJIoRовочный бщщ) .
Полвивmееar диалоговое окно O'I'обраэи}' не TOJ,(bКO компоновочные блQIЩ. $Но
УlCазйJIШ,Ю в манифесте 'клиента. :но И ROMn01-lOВОЧRЪt~ БЛ(!J1Щ, на KOТOPЫ~ укЩ'!анные
ROМnОНОВoчНъie блоки ссылаются. Дrtя: нашего ЦP:{olМepa выберите ,Car1ibrary. После
щелчка на E1-IОПRe Flnish (IbTOBO) будет ]10каэана страница своЙств ,ДЛИ ВI!1(iраяпо­
roобъекта маНифеста клиеIПa. Ta..'d. ИСПoJIhgyя ВОЭМQЖНОСТИ ВКТIaДКИ 8inding Роliсу
(hолИТИlra ПРЮШЗКИ ресурсов). вЫ сМожете сгенерировать <dере.п.tlеntАs,sеmЫу>.
На вюшдке 8indlng Роliсу вы M~eтe установить значендя атри;бута oldVersion
(уиажите 1.0.0.0) в T~KCТOBOM поле Requested Ver:slon (3anpоше:ю·IЩJ: Bep~) и атри­
бута nеwVеr5itш (2.0.0.0) текстовом ПОде New Version (Hoвa:Ji вероЩl). После ввода
УКа:ЗаННЫХ параметров. вы обнаружите P.JедующИЙ qШЙЛКОliфЩ-УРа.щщ, сгенерц­
рова.нныЙ' ДJIЯ вас СйстемоЙ.

<?хтl V'еrэiоn="l.О"?>
<coniiguration>
<ru.n:!:.:lme>
«as'serЦJlyBi ading xmlns=e"arn: sсhеma,s-пtl' стозоft-соm: ' а..sm. vl ";;>
<dе,репdепtАs,эеmЫ у >
<аз эеrnbl у1 denti ty name,=j,j ca;r't ,ibriiry"
ррЫ i cY..€yT,o ke n= "191 еЬ f 55 6 5 5е О а 43" /">
<publish,e .rp,o licy apply"""" yss " 1:-
<bipdingRemil'eCt оld\7еrэiоn;="1. О. О. О" new"'ersi drl=" 2 .,0. о .0" 1>
</dерелdeпtАЭsеmblу>
<) assemblyBindinq>
</ y'u ,n time>
</соnfigur'аti"Оп>

Анаnиз внутренней структуры ·ОАС,


Итак. все раБОТ<lет. Теперь д.авайте посмотрим на ВflJТPеннюЮ' 'CТpyltтуру GAC.
При ПРQсмотре .nаmш GAC в программе ПРОIЩЦНШ<; Windows вы видите ряд IIШt­
TOrpa.\tМ. изображаЮlЦЦХ :каждый И~ общедос;ту.пных IЩМIlОl-ТОНОЧIThIX блоков всех
имеющихея аерсИЙ.Эта rрафичесщm оболочка обtюпе'ilИвается СОМ"с.ервером
shfusion .dl.l. Но., Ш1К вы можете подозр~ать. за этими ПИК'I'ограммами должна
скрьша1'ЬС:fl еЛQЖllaН (КОТЛ и ВПОШ1С логwшая) <::rpyw.rypa Rа.талarов.

1
486 Часть 111. Программирование компоновочных блоков .NET

Чтобы понять. что на самом деле представляет собой структура GAC, откройте
окно командной строки и перейдите в I{аталог assernbly.
cd c:\windbws\assembly

Выберите в команцной строке команду dir. В этом каталоге, среди прочего, вы


обнаружите папку с названием GAC MISL (рис. 11.26).

Рис. 11.26. СКРЫТЫЙ подкаталог GAC MS IL

Перейдите в каталог GAC MSIL и снова выберите команду dir. Теперь вы


увидите СПИСQl{ ПОДI{аталогов. которые имеют в точности тю{ие же имеНа. как и

пиктограммы. отображаемые сервером shfusion.dll. Перейдите в подкаталог


CarLibrary и снова выберите номанду dir (рис. 11.27).

Рис. 11.27. ВНУТРИ скрытого подкаталога CarLibrary

Как видите, в структуре GAC для каждой версии общедоступного компоно­


вочного блока создается свой подкаталог. имя которого выбирается по правилу
<веРСИ.s:JКомrlОновочногоБш:жа> кодОткрытогоКлюча. Если нз текущего каталога
1 rl'I2IBa 11, КОМГf0НDВО'lные БЛ!)Ки ,N ЕТ 487
вю перейдете в каталог CarLib:rary версии 1.0,0 ..0, то обпаружйте rnм юmmo С(ЮТ­
ООТcrвyЮЩей библиотека ПРОfpш.шного &ода (рас, 11.2S).

Рис. 11.28. Смотрите! Внутренняя Jroп\.iя GдС библиотеки CatLibrary.dll!


ЛfЩ установке cтporo и:менованв.orо ROмпоно.вочноro бпоЩl. в cтpy.wrypy GAG опе­
рщирн:нзя система. расширяет струнтуру ,путем создания cnециальнOl'О II!'1дКaТaJ1O­

гц В ~:истемн.о.м катал6]'f' assembly. При таком подходе среда СШ моще'гисnuльзо­


ВаТЬ разные :версии но.мпоновоч.ных блоков. избегав ~онфликтов. 1\'отОрые иначе
МЩ'JШ бы lЮЭНИRатъ из-за НaJIИ'IИН файлов" • dl1 с одинаковЬThЩ нщшfllmйМИ,

Файлы политики публикации


КОМПОНОВОЧНЫХ блоков
СледуюЩИЙ вопрос. It(i)ТОРI;JЙ мы долЖНы рассмотреть в pшmах обсуждения воз­
можностеЙ 1C00фиryрации, это роль llO.!IUЛIШCU ЛfJБЛLl:JClЩtш коМПОНовочных бло:ttов.
Бы только что убедил.ись. что с помощью фaf<IЛОВ ... _С@nПЧ можно ВЫПОJIНlIТЬ дри­
~ 1< .EtонкретНОЙ версии общедос;т.yrrвОJ!О ROМПоновоЧ1-юtо блоха в обход версии,
указанной в манифест,е KJ-,rnента. Все это просто .прекрасно. но npедстав.вте себе ,
что вЫ ЯВJlЯетесъ здминистрq.трром, которому .придется пере.конфиrypаровRТЬ :все
npил:ожениg щщ:ента на дан:еой, машине ТаЕ. чтоб.ы эти приложеlПiН ,исn.oльэQ8ЩlИ
:коМПоновоЧНЫЙ .бл()!{ CarbibTary.dll версии 2 ,0.0.0. ВвидУ прикятоro соглащeww
ДI'IЛ имен фa:йдQв конфшуращщ. :вам П'ридеТСR мно.тократно ко.tщpоватъ оДНО JII то
же ХМIFСОД~РЖИМQе 110 множество мест (.еще и: IIp€:Ддолагаетс.II , что вы ДОЛЖнЫ
знать, где находятся вс'е ф1щJJы.• исI10льзую.щие CarLibrary1). ОчевИДНО, 'tJТO длн
администратора это будет дро(.'ТО кошмаром.
Поли1'1J1(a ауБJIИ:~аци~ поэволле"г ·издате.7lЮ~ данного Компоновочного бдо.ка
(вам. nатему подразделению, ЩШ.lеЙ ;компании или друтому KDВRpeTHOМY постав­
lЦЙh'У) предложить бшrарf:IYЮ версию файла ". со ах ig. которая устанавJЩJщ,е:гcsr
в структуру GAC BM~CTe с nовейшей J;lеропей соответствующего КОМДОНО:ВОЧflоrо
блока. Дреимущество 11iIJюrо под~ода в ТОМ. что тогда отпадает необходимость ~
наличии специальных файлов, ... . ,config в каталогах приложений 1ШИента.. Среда
CLR чИтает тевущий манифест и ПЫТae'L'СЯ найти запрошенную версию в CTpyg-
488 Часть 111. Программирование компоновочных блоков .NET

туре GЛС. Но если при этом среда CLR обнаруживает файл политики публикации.
читаются встроенные в этот файл ХМL-данные и выполняется соответствующее
перенаправление на уровне GAe.
Файлы политики публикации создаются средствами командной строки с помо­
щью .NEТ-УТИJШТЫ al.exe (это peДВRTOp связей компоновочного блока). Этот ин­
струмент имеет очень много опций. но для построения файла политики публика­
ции потребуются указать только следующие данные:

• информацию о размещении файла *.config или *.xml. содержащего ин­


струкции перенаправления;

• имя файла. задающего новые параметры политики публикации;

• информацию о размещении файла *.snk. используемого ДЛЯ созданИЯ под­


писи файла политики публикации:

• номера версии, назначаемой создаваемому файлу политики публикации.

Чтобы построить файл политики публикации, контролирующий библиотеку


CarLibrary.dll. нужно использовать следующую команду.

al /link: CarLibraryPolicy.xml /out:policy.l.O.CarLibrary.dll /keyf;


C:\MyKey\myKey.snk /v:l.O.O.O
Здесь XМL-содержимое вwпoчено в файл с именем CarLibraryPolicy.xml. Имя
выходного файла, которое должно иметь формат роliсу.<главны.Й(ио:мер версиu}>.
<доnoл.н.wneльиый(ио:мер веpcuu».JCонфuгyрupуемыйКoмnoиовочн.ы.йБлок), указы­
вается с помощью флага /out. Обратите также внимание на то, что имя файла,
содержащего значения открытого и секретного ключей, тоже должно быть пред­
ставлено, но с помощью опции /keyf. (Поскольку файлы политики nyбликации яв­
ЛЯЮТСЯ общедоступными, они должны быть строго нмен01Iанными.)
В результате использования al.exe вы получите новый компоновочный блок,
который можно разместить в структуре GAC для того, чтобы. не используя O'IДeJIЬ­
ные файлы конфигурации для каждого приложения. "заставить" все клиенты ис­
пользовать CarLibrary.dl1 версии 2.0.0.0.

Игнорирование файла политики публикации


Теперь предположим, что вы (Как администратор системы) установили файл
политики публикации (и новую, более позднюю версию компоновочного блока) на
машине клиента. как обычно и случается. девять из десяти соответствующих при­
ложен ий перешли к использованию версии 2.0.0.0 без всяких ошибок. Однако в
одном из приложений l(JIИента при доступе к CarLibrary.dll версии 2.0.0.0 воз­
никли проблемы (мы с вами знаем. что создать программное обеспечение, которое
будет демонстрировать 100%-ную обратную совместимость. практически невоз­
можно).
В TВROM случае можно построить файл конфшурации для данного "проблемно­
го" клиента с инструкциями. которые позволят среде CLR uгнopupoвamb установ­
ленные в GAC файлы политики пyбmiкaции. При зтом другие npиложения клиента,
которые могут использовать новый компоновочный блок .NEТ, с помощью уста­
новленного файла политики публикации будут перенаправлены на новый компо­
новочный блок. Чтобы отключить политику публикации для отдельного клиента,
Глава 11 . КО,МПОНОВОЧffЫ& блоки .NET 489
создайте файл'" .ool1f.ig (с подходmцим имене1\«). D котором В Р.ам&Ш элемента
<:puЬUsherpolicy> ус.'ТaRОвпте lJllЯ атрибута apply эпачение по . после етого оредэ,
CLR будет зarpужать комnоновоtmblЙ БJlOК той версии • .котора.и указана в манифе­
сте клиента.

<c.Qnf:i,gurfitiol'J.>
<iruntiroe>
<<1 ss erobl УВindiлg хщlЕS"'"U'rр: SСь'еmiis-miсrоs,оft-сщn: авт. 'Jl ":>
<рublisЬ.r~о'Цс:у apply="no" 1>
<!а.sэеinblуВiпcting>
<./ runtime>
<!СОl1 fi ЧI:j:rаtiол>

Элемент <codeBase>
Файлы :конфиrypации приложениn могут также указать базовый проrрaю.tнblЙ
код. С , помощъю элемента <codeBase> МOЖJ;IО дать инструкцшо среде CLR ИCIЩ'IЪ
зависимые RОА-шо:новочные БЛОЮI в yt<aзанных:меС'1'аХ (mmример. в общеi1 Ce'reJJoj{
naпRе или в ло:ка.iIЬНQМ каталоtе вне хата.1l0га ПРWJРжeниJ{ J(]ЦIeнтa).

Замечание. , Если значение! присвоеНl:lое EI рамкахЭJ1емента <codeBa$€!>. УkaЗывае1 на уда·


ленную маши/'!У. КОМПОJ,fОIlОЧНЫЙ блок буд~ загружегl Г\О требованию в спеЦИ8ЛьfrЫЙ каталог
структуры GAC, имеющий специаЛЬtiое f1sзвание - кэш загрузки, Увидеть содержимое кэЦla
:!агрузки МОЖНО с помощыо gacutil.exe, указав npJoI запуске этой УТИЛИТЫ' Of1ЦМЮ /ldl.

СуЧe:rом того, что вы уже знаете об установке комnоиовочных. блОКОВIJ'GAC. бу­


дет я.СИО'. что кoмnоIюво'1ныe блOlШ. зarpyжaемые С'поМО1ЦЬЮ эm:меJПa <codeBas~>,
далжНЬ1 быть строгэ именовaинъtми , (В КО1Ще КОНЦОВ. как асе ю:mч:е ~eдa СШ смог­
ла бы устанОВИТЬ уда.ленные КОМПОIiОВQчны:е блоки в струь.-туру аАС?).

3ам8ЧJ1J018. Строго говоря, зламеtn <соdеБаsе> МОЖНО использовать .. для зонд.ирования


КОМПОIiOSОЧН!3Ж блоков, KOTDpble lie ЯвляЮТQЯ строп') имеJotDEI~ННblМИ. Однако в Т8/СОМ QJ1учае
адРес kOмrюНQВОЧНОГQ 'бnокf.I должен З8,tl,ават ..ся относитеЛ!НiО KaTa:,noгa приложени" ICIIИlIнта
(В ЭТОМ ОТtfоwеRИИАа~IНЫЙ , элемент преД1lатае'Г f)олее широкие возможНости, чем элемент
<privatePa:th».

Создайте, .кOHCMbHO~ npиложение с им:евfI,М CQdeBa$~Client. ус'ГЭ.Новиre ДДJI


него ССЫЛК)'"НQ Ca:z:Library.dll версlЩ 2.Ь.О.о ИJlЗМените ИСХОДНЫЙ файл так.
usiлg Carl.ibrary;
!iamespace Code&l.seC1ient.
.\
еlаsз Prog1i"am,
i
~ta'tic V'oid Mai.n (string [J a.rgB)
{
tons'oie. 'W\::i teltine ( ........ '" *-* Забавы с C:od~I!ase * '* "'**") ;
SPG>'I 'tsCa,r с = new .sportsC.ar () i
Console. Wri 1:eLi:ne ("Созда&аCnОРТV!ВНая машина ,< ") ;
490 Часть 111. Программирование компоновочных блоков .NEТ

Conso~e.ReadLine();

Поскольку библиотека CarLibrary.dll бьmа установлена в структуру GAC, вы


уже можете выполнить программу. Но для демонстрации применения элемента
<codeBase> соэдайте новую папку на своем диске С (например, папку C:\MyAsms)
и поместите в эту папку копиюCarLibrary.dll версии 2.0.0.0.
Теперь добавьте в проект CodeBaseClient файл App.config (в соответствии с
инструкциями, предложенными в этой главе выше) и добавьте в этот файл следу­
ющее ХМL-содержимое (не забьmайте о том, что вanrе значение .publickeytoken
будет другим, и вы можете выяснить его в структуре GAC).
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.vl">
<dependentAssembly>
<assemblyIdenti ty name="SharedAs sernb ly"
publicKeyTOken="191ebf55656eOa43" />
<codeBase version="2.0.0.0"
href="file: / / /С: \МyAsms\CarLibrary. dll" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configura tion>
Кш~ видите, элемент <codeBasE:> вложен в элемент <as semblyIdentity>, ис­
пользующий атрибуты пате и publicKeyToken для указания понятного имени
компоновочного блока и соответствующего кода OTI<PblTOrO ключа. Сам элемент
<codeBase> указывает версию и (е помощью атрибута href) адрес загружаемого
I{ОМПОНОВОЧНОГО блока. Если вы удалите CarLibrary.dl1 версии 2.0.0.0 из струк­
'УУРы GAC, этот клиент все равно будет выполняться успешно, поскольку среда CLR
сможет найти внешний компоновочный блок в C:\MyAsms.
Однако если вы удалите каталог MyAsms со своей мanrины, то клиент работать
не сможет. Очевидно, что элементы <codeBase> (если таковые присутствуют) име­
ют преимущество по сравнению с проверкой GAC.

3аМ8Ч8ние. Если размещать КОМПОНОВО'iные блоки в случайных местах на машине, велика вероят­
ность того, что у вас, в конце концов, аозникнет необходимость воссоздания реестра системы
(из-за соответствующих проблем DLL). поскольку при перемещении или переименова.нии па­
пок, содержащих выполняемые ДВОИ'il-tые файлы приложений, имеющиеся связи будут нару­
шаться. В связи с этим используйте <co deEase> с осторожностью.

Элемент <codeBase> может оказаться полезным при ccьmкax на компоновоч­


ные блоки. размещенные в сети на удаленной машине . Предположим. что у нас
есть доступ к папке, размещенной по адресу http://www .lntertechTraining.com.
В таком случае для загрузки удаленного файла *. dll в кэш загрузки GAC на ло­
кальной машине следует изменить элемент <codeBase> так.
<codeBase version="2.0.0.0" href="http://www.IntertechTraining.com/
Assemblies/CarLibrary.dll" />
Гла!!а 1 1, .l(j)МПОНОВОУ]iЬН! блоки , NП 491

ИСХQДIfIaIЙ !I"A, Пр,оект СоtJе8.эsеСllеilt раз~еЩilН в nО'дкаталоге , соответствующем l1taвe 11,

Пространство им' ен System.Configur'ation


ДО ЭТОГО времени вее файлы '" .c-c,hf 19, nокаЗaн1lЫ:е "Б ЭТОЙ rnаве. C(j)CTO~ И~ И3~
lJecт.f:JЬUl; XML- :ЩСNIевтов. по которым среда CLR выясняла адреса BHe~ к-омпо­
НОВОЧДЫХ блоков, Вдобавок .R ЭТИМ элем,еНТaJVJ файл У.онфигурации к'гшеща может
содqJЖэ.ть и специа,льиые данные npилож~ния, :не им.еющщ: никаКОrQ t)1'f'НQшt.1'lИЯ R
устаншще CШIзей, (. 'yilt<TQМ {'Ю1ЭЗИНdIО становится ЯСНо. uоче;му BNEТ Framework
иerrО,дЬзуетсл npострансТ1Ю имен. 1ш'Торое I103DолЯ,ет считывать ДafЦ{Ыe файла коН­
фигурации юrиента uрограМ1\ШЫМ И среДстнаМ11 ,
ПространсТВО ИМEl{ ,$ 31 st€II1 . С О!1 f i iJll r-a t i ОГ) опрсn;еJJnет неб'О'льщой набор ТИПОВ,
КОl'оры(:' можно И:DПОЛl::>зовать для чтения nОJ1ьзоваТeJ1ЬСItИX УСТalЮВОК из файла
• .eoJ').f i g юrиента. Эти лользо:ватеm;спие устанОВRИ д'олжны задаватьGЯ в ~онтеисте
ЭЛ1':I\rlr..нта < з.р рS еt t i Ii1 :,тЕУ > . Элемент <.app.Set t j пф, :> М оже-r Сli)держать ПРОИ3ВОJThНое
"IИСДО элем ещ()в <ad,d ? оп:ределmоDlJ'lX пары нлючcii и зuачений" ROTOP111~ MOryr
щшле;!Щться црограммиыми средствами,

Предполо:щи!м, " ПО 'у нас есть фаЙл. t.c о п f i g ДЛИ tr.онсолыюго пр~ложения
Арр С ол f igR:e.ad e '[" A~p . в ltOТOPOM 'определяется СТРОIЩ СЩl3И С ба';30Й: дaH;HЬJX и ука­
затель на данные t i lТi€s T'o S.ayHe l l o ,

<се)!'!,! 1 чи r а t i cm >
<а.ррS'еt.tinqз>
<a d d k ", у= " а'рр С' 0 f1'St 1: "
iI а 1 ц е = "
"'"' C" ; ~ 1:=1 b L':,al tJ(~з.'t i ul,d= ' s ? " ; :pw'eI= I ' ,; da. ta'b a s <e=Ca. r~ " 1>
<ad,d key= n :citrтe ~ :r o9;;1yHe l 1 o " >,,, 11)f, =" B " 1 ;-
</appSett.ings>
<i с о пf l Ч И ! i:1 tio\'1 ;.

Чтение этих зваче,аий A!IfТ и'С{}ользl'maнйя 'Приложением :кл.I1ента ОcyIЦе<:ТВЛЯет­


ел простым 1ЗJ:;JЗОВОМ M~Oдa ЭКЗF.~пiляра Get V:al~le О 'Гиnа sуs tеm, Соn:figцr,аtiол .
AppSett:in g'$Rea der, KaR показывает сл едующий прI1мер програмfvUЮго кода. пер­
вый пара1\>Iетр C
;et,J,a l u e () задает имя п.mоча 'в фалл!';' w, con f ig. а ~:rорой napaм~Tp
представляет СООIГЩ'т<;твующий тип ключа [получаемым в С# в результате приме­
неmш операции type of),
class Program

эt а tiС"- ,го Иi мзlrr ( 5 ~; I'ifJ1E r 1 argsl


I
11 Соз;цавие среДс!liSачw-еаи. ~ nOJlучеJЩе сozpохи соединевюr.
Арр5.е.t tiГJч sRе а i,;iе r 0 1:" = I,e';'; AppSe!. t:.i l19'$'J~ eadf;!r ();
'CorJ:sole ,'W'r i teLLo E 1ar . Ce tVli!l и е ( "аррС Ь nЯt r", t ypeo f с$trirщ ) ') 1;
/ / Попучение т,wcп. nOB!t'op~ приае'РС'l'IIИJi[ и вwnолИеиие.
int п ш:il±:' О f Т :imе $ = { i ГJ 't ) а r . Gе tvз1 L1'8 ( "til1'1€'з ТоSауНе llа" ,
t-ypeof (iI'-t 1 ) ;
[с,. т ( i n t i = О ; i < гюrtl.ЬО f Т imе s; i" " )
CO Ds ,o l e , Nx' i te:Li rte (";10 l" ) ;
,Cbn:i1 Q 1!О, , R",i'J Ш, i 112 () ;
492 Часть 111. Программирование компоновочных блоков ,NET

1Ип масса AppSet tingsReader не задает способа записи специальных данных


приложения в файл *.config. На первый взгляд это может показаться ограниче­
нием. но на самом деле это вполне логично. Сама идея создания файла *.config
ЗalUIючается в том, чтобы он содержал доступные только для чтения данные, кото­
рые должны поМОчь среде CLR (а также типу AppSettingsReader) правильно уста­
новить приложение на соответствующей машине.

Замечание. В ходе нашего обсуждения ADO.NEТ (см. главу 22) ВЫ узнаете об элементе кон­
фигурации < с оп о е с t i о n St r i ngs> и одрутих Тli1IlЭХ пространства имен Sу s t е m.
Configuration. Эти элементы, появившиеся в .NEТ 2.0. предлагают стандаРТНI>!Й метод
обработки строк соединений.

Исходный код. Проект AppConfigReaderApp размещен в подкаталоге, соответствующем главе 11.

Файл конфигурации машины


Файлы конфигурации, которые мы с вами рассмотрели в этой главе, имеют
одно общее свойство: они относятся к КOFшретному приложению (вот почему они
имеют то же имя. что и соответствующее приложение). Но каждая uоддерживаю­
щая . NEТ машина имеет еще и файл. имеющий имя machine.config. который со­
держит множество параметров конфигурации ДЛЯ управления работой всей плат­
формы .NEТ (многие из этих параметров не имеют ничего общего с разрешением
ссьшок на внешние компоновочные блоки).
Платформа .NEТ использует файл *.config для каждой своей версии, установ­
ленной на локальной машине . Файл machine.config для .NEТ2.0 можно найти в
каталоге C:\WINDOWS\Microsoft .NET\Framework\v2 .O.50727\CONFIG (номер вашей
версии может быть другим). Открыв указанный файл, вы увидите множество XМL­
элементов, задающих установки ASP.NET, различные параметры безопасности,
поддержку отладки и Т.д. Но если вы захотите добавить в файл machine.config
(с помощью элеменrа <appSettings» установки для приложений, применимые в
рамках всей машины, вы можете сделать и ЭТО .
Этот файл можно редактировать непосредственно. используя программу
Блокнот. но следует иметь в виду, что при некорректном изменении этого файла
вы можете нарушить рабоry среды выполнения. Ошибки в этом сценарии могут
иметь гораздо более серьезные последствия, чем ошибки в файле ".config прило­
женин, поскольку ошибки XМL в файле конфигурации приложения влияют только
на даlпюе приложение. в то время как неправильный ХМL-код в файле machine.
config может вообще блокировать работу конкретной версии .NEТ.
Глава 11. К(Jмпано.вОЧ~lые блоки .NEТ 493

Общая схема связей компо.новочных блоков


Теперь . .коща МhI евами ИзyчиJШ подробности 1'01'0, :как среда CLR осуществ.ляет
поиск запрошенных lCQМПоновоЧНh!X блоко.в. вcnомиите о. Т(!)1d. ЧТ9 простое на 00-
~roм деле дОЛЖНо. быть простым. МНоrие (~1Щ .и.е все) ваши .NEТ-приложевии будут
иметь ВИ:Ц ГрупПЫ npнваТИЫХ . 1(омпоново.ЧlIЬ,IX бl1о.ROВ.• разм~ в оДНОМ ката­
логе. В 1'ЗRОМ cnyчае ДОстаточно цросто скопирова.тъсоответCТБYfOЩУЮ"Пanкy 1Уда.
куДа требуется, и начать йЬШОлнеиие клиента.
Однако, вЫ уже вццели, что в цроцессе· рцрешения С!iИзей среда СШ вЫпоЛ­
няет проверку на наличие файлов }(()нфигурации нлищrщ ~ ЦOJlИlИКИ :пубjIИвaции
КОМПI'JНОВОЧ~IX б:ЛОRОВ. В ICaЧеетве общеii ~eмы ~, ~оторый проходит среда
СI.Rдри разрешении В}lеmниx ссылщ( КOIЩЮНlJВОЧНОГО б:;:rщщ.• преДl"IВI'ается. схема.
показанНа.я ка рис. 11.29.

Заnpoo ImtrЮfКlllO'lНOf'Q
блока кnиеtl'ioм ,

ПОМOII
jфм~ноro бnolЦl
в катaлore nриложения I.jDU""~a
и вобycnoмeННЬfХ f-'-
"1..1-
......
-,
nCЩКIПiЛDги

1. [ИмяФаМВJ.dll
2. [имIlФaЙll8..ее
Да:
mporo .мме~IОванНblЙ Успех

Проварk8фаЙ1lOlk!>Нфюурациlt
~ IIЫбор seРСИИ.
прiюрит&ты ПOl'lИТИl!И
(от выошегО /( H~~MYj:
1. q>aйл -.config JII1ИВIIТiI
2. Фaii1 norn.m.1КИ публиki'lЦИИ
З. Фом machlne.con~g

Нет

Рис.Н.Ц. Метод разрешения ССЫЛOJ< компоновочного fiлока а среде CL~


494 Часть 111_Программирование компоновочных блоков .NET

Резюме
Эта глава посвящена тому. как среда CLR разрешает ссылн:и на внешние ком­
поновочные блоки. IЛава начинается с рассмотрения содержимого компоновочно­
го блока: заголовка, метаданных. манифеста и СIL-Rода . Затем рассматриваются
создание одномодульных и многомодульных компоновочных блон:ов. а также не­
скольких приложений клиента (на разных языках).
Бы смогли убедиться в том. что компоновочные блоки бывают приватными и
общедос'ry"ПНЫМИ. Приватные компоновочные блоки копируются в подкаталог кли­
ента. Общедос-ryпные компоновочные блоки устанавливаются в глобальный кэш
компоновочных блоков (GAC). и при этом они должны быть строго именованными .
Наконец. вы узнали о том. что приватные и общедоступные компоновочные блоки
можно конфигурировать, используя ХМL-файлы конфиrypации клиента или, как
альтернативу, файл политики публикации.
ГЛАВА 12
Отображение типов,
динамическое
свсязыIаниеe

и программирование

с помощью атрибутов

К ах nекаЗaJ;IO в предыдущеJl rла.ве. lЮМПОНОElочные б}JOКИ lIВляются базовы­


м:п i;meme,JT1'am-и уt'таы.оВlfИ "в среде .NEТ. С помощью ЮiтеrpИрGвaRНОГО обо­
зревателя ·об'}~н:тов в Vls.Lшl Studio 2.005 можно рассмотреть открытые типы тех
lЩМП!).IЩDО'ПIЫХ б.IШНОВ. :на KOтQpыe ссыл;ае<гсн проект. Вн:еl1..mие средсnщ. такие
как ildasD1'.e.xe. позволяют увидеть соот,ветствующий СIL-код. :метаданПhIе ТШ1ов
и еодеpщm.,1<Х: м~феста иомповЬвочного блока любого· бинарноrо файла .NEТ.
ВдоБЦВОR R ;;tТИМ вщrм,ОjiЩОстям., доt''т)'пным во время проектироВанИl1 HOМnOf:!o­
В0ЧНОro блоЩl. .NET. вы можете ПQДУЧИТЪ ту же· информацию npeгpaММHЬ1,МtL сред­
ствами. ИСДОЛЪ:iУЯ объекты пространства имен Sу 5.tem. Re fl ее t ion.. 8 связи с этим
мы ВЫJЯСНИМ роль отображения ТИn{IВ и неоБХОДИМ'6СТ'Ь ИСПОЛЬЗования Mernдa:~rnыx

.NEТ·
В оставщеЙСfl чаL"ГИ:maвы рассматривается ряд тесно связa.юtbIX ВОПРО.СОIl . ОТ­
l-IOС'ЯЩДХGЯ н: возможностям сеРВ:И:СОD отображения. Напр:им:ер. вы узнаете о ТОМ,
lta..f{ .NEТ-клиент .мошет ИCnDЛЬ30ВЗ:Т:Ь динампчесиую ЗМрузъ.'У и д1ll-Iа.>4ПЧеCi(ое· CВЯi-
3Ц1Вa.J:il;Ie ДЛЯ aR'ТИВ"ИЭ3ЦЙИ тишiв . .о наторь!.'" у I{ШJеПТfI нет поJЩОЙ инфор:шщии на
этале l\ОМnИЛЯЦИИ. ВЫ TaJ.Cd«' узнаете. К/;Ш: r ПОМОЩЬЮ СИс.тем:ны:х и 11WIЬз()ВЗтель­
скик атрибутов можно· добавить в :компонtшочнщй бло.R _NEf ПОЛЬЗ0ва:гедьсние ме­
таданные. ч.тобы :продемонстрировать пе:рсneЕТИВЫ применения зтЮt ($ дерв&щ'
В:ЗТЛЯД I,1злиnще специальных) ВОЗМОjЮ{()стеЙ. глава ззверIllИТС.fI пр:имером постро­
ения нес.RОЛЬКИХ ~BCTpHnвae:мыx~ объектов. I{OTOpbre вы сможете дРба.в;ить в рас­
.ширяемое приложеНJН:~ WlпdоWsF{:)Frrl.
r
496 Часть 111. Программирование компоновочных блоков. NET

Метаданные типов
Возможность полного описания типов (классов, интерфейсов, CTPYRТYP. переч­
ней и делегатов) с помощью метаданных является главной особенностью платфор­
мы .NEТ. Многие .NЕТ-технологии, такие как сериализация объектов. удаленное
взаимодействие .NEТ и Web-сервисы XМL, требуют, чтобы среда выполнения имела
возможность выяснить форматы используемых типов. Возможности межЪЯЗЫRово­
го взаимодействия, поддержка компилятора и возможности IпtеШSeпsе среды раз­
работки тоже зависят от конкретного описания типов.
Важность метаданных очевидна и, возможно. именно поэтому они не являются
новой идеей. предложенной в рамках .NEТ Framework. Технологии Java. CORВA и
СОМ уже использовали аналогичные понятия. Например, для описания типов. со­
держащихся в серверах СОМ. используются библиотеки СОМ-типов (по сути, они
представляют собой просто скомпилированный IDL-код). Как и СОМ, библиотеки
программного кода .NET также поддерживают метаданные типов. Конечно, ме­
таданные .NEТ синтаксически совершенно не похожи на IDL (lnterface Deftn1tlon
Language - язык описания интерфейсов, используется в СОМ-технологиях для
спецификации интерфейсов объектов СОМ). Напомним. что просматривать мета­
данные типов компоновочного блока поэволяет утилита ildasm.exe (см. rлаву 1).
Если вы откроете с помощью ildasm.exe любой компоновочный блок *.dl,l или
* . е хе, созданный вами в процесс е изучения материала этой книги (например,
CarLibrary.dll), и нажмете :комбинацию клавиш <Ctrl+M>. то увидите соответ­
ствующие метаданные (рис. 12.1).
Как видите. ildasrn.exe отображает метаданные .NЕТ-тила очень подробно
(двоичный формат оказывается гораздо более компактным). Если бы здесь потре­
бовалось привести описание метаданных компоновочного блока CarLibrary.dll
целиком. оно бы заняло несколько страниц. Это бьmо бы лишней тратой вашего
времени (и бумаги тоже). так что давайте рассмотрим метаданные только IUIIOче­
вых типов ИЗ компоновочного блока CarLibrary.dll.

TypeDI!'F 11 (D2IIII02) . :д'

TypD~fНa~~~ carL1b~i~y.Ca~ (12111112)


Flags : [Publ1c] [AutoLayout] [Class] [Abstract] [Qns1Cl.
Ext~nds : 018а8аа1 [Typ~R~f] Syst~~.Object
r1l!ld 111 (81181181101)

Fleld Нате: p~tНaтe (DIIOODDU1)


Flags [Family] (001188884)
CallCnuntn~ [FIELD]
F1~ld typ~: St~ing

Field 12 (~IIIID2)

Field нате: cu~rSpl!ed f.-180112j ~I


о:~ ~_~:~~-,",:~~п.~~..,~_:~~~~~~",~-;%.:~~~~~~:i
,,",,
~;

Рис. 12.1. Просмотр метадЗННЬJХ компоновочного блока


1 Глам 12. Отображение типов, диflам.,<tеское связЫEtаНИ8 .. , 497

Анализ мета.данных перечня EngineState


КажДЫЙ тип. оnpедеnенный в .l:ЮМ:ПОНО.!ЮЧJЮМ f5ло.1Се, обознаЧеН ма.р:кером
Uтурепе] -~ п" (где ТуреПе! - это. СОRращеJUJе от tlJP8 cJеftпitiоп. что. в переВDД~
означает определение I:1ЩТlq) . .iScJIи QЩJс;ьm~еМ1Щ тип ИСnО11Ьзует тип, определен­
ный В рамках друто.го fЮМnОНОI\OЧНОТО блщ-ш .NEТ. то ДJLЯ ССЫЛ1\I-r -на такОЙ ТШI ИС­
ИОJIЬзуется "'!ypeRef #r!" (!\де T}rpeREf- это СОJ-Cращедие от [уре rt;/ererн::e. в пере­
воде CCbl:JU(:Cl на тип). ЕсJТИ хотите. TypeRef МОЖНQ' С"ЦfП.lТЬ ук.аЗf,.Iтелем на полное
ощределение метздэвныxoQтве'н.''твующегоo тида. По существу. метаданные ,.NIE"f"
npедставщпотсобой 1Щ:lо.жество. 1'а6JШЦ. -!Шноодисы:вающю; все определения ТШ10В
(TypeDef) и все ТИIIЫ, НЦ которые имеются СсЫШШ (Type-Ref}. Все это. м;о:JЮJо уви­
деть в Olше црDсмотра МеТ;;щанных ilda:"Snt.exe.
В случаеCarLibrary.dll ОДНО из о.Шl'~aщrй TypeDef в Mel'aдaнRых соответству­
ет перечню Ca.rbib.ra ry.E.n.g;L!J e5ta t-e .Су ~ЦC ~OMep Iур.ере:(: мо.жет быть дpyrn:м:; ну­
м~рация TypeD.ef ооответствуЕ'Т поря:д.J-'У. в lЮТОРо.м .компилятор С# обрабатывает
соо.тветС1"вующие типы),

ТypeDef *1
-------------~----~-------~-------~---~----------~---

'PypDefNpffie : CarLibrary, EngineState, (020 [} 00 0;1)


Flags [Public] [Alit0LaYQutJ IClas'~J ISea.iBdj [АnзiСl.аэs] (!JЙОGDIQ.l)
Ex1;ends 01 ОЩ,ОО 1 [Typ€!Ref ] 13yste.rr•.. Enfu'r,

F.ield #2
----;;;...-- - -.- -- ---- ---- --- - -.--;.... ...... _----'._- -.:-.---- -..:...~- --"--'--'--'---
Fie}G Ыате: engi.neAlive ~()400()002)
Flagi5: [Plililic:j [Staticj СИtеrаl] IИ"S.Dеfа!Jlt] (u.О[}{1В115б)
DeflH alue: \14)
T
О
СаllСпVТ!tn: 1FIELD]
rield type : Vаllдеtl-ёJSЭ СаrI,iЬrаrу.ЕtJgif1еStщtе
.. .
MeTxaTypDefName используется ДЛЯ вмени типа. МеТJ{З метаданных Extends
используется ДЛЯ указания базового класса .данноrо типа .NET '(В дs.нн(\м случае
это т.и:п Sуstеш.ЕшШl. обозначе:НН.1>rЙ Kai( Typ€Ref). Каждое поле перечня обозначе­
но метной "'F'ield #п". ДJ1Я примера здi1iсь представлены только метаданные П()JIЯ
Enginest,,-t..e. eng ineAl i v.e.

Анализ MeT8A8HHblX типа Car


Вот часть дампа тИПа Саь которая иллюстрирует сJ1едУющее:

• способ опре~еления полей, 'в терминах ме1"::Ща:нных ,NEr;


• цредставление методо.в в мстаданньш .NEТ;

.. отОбражен:ие свойства типа в пару СПециальных члеuов·фуюпЩ:Й.


498 Часть 111. Программирование компоновочных блоков .NET

TypeDef #3

TypDefName: CarLibrary. Car (02000004)


Flags : [Publicj [Auto1ayout] [Class] [Abstract]
[AnsiClass] (00100081)
Extends : 01000002 [TypeRef] System.Object
Field #1

Field Name: petName (04000008)


F1ags : [Family] (00000004)
CallCnvntn: [FIE1D]
Field type: String

Method #1

MethodName: .ctor (06000001)


Flags : [Publ ic] [HideByS ig! [ReuseSlot] [ Speci а lName J
[RTSpecialName] [.ctor] (00001886)
RVA : ОхООО02050
ImplFlags : [I1] [Managed] (00000000)
CallCnvntn: [DЕЕАИ1Т]
hasThis
ReturnType; Void
No argllments.

Property #1

Prop.Name: PetName (17000001)


Flags : [попе] (00000000)
CallCnvntn: [PROPERTYj
hasThis
ReturnType: Striпg
No arguments.
DefltValue:
Setter (06000004) set PetName
Getter : (06000003) get_PetNaдIe
О Others

Прежде всего, отметьте то, что метаданные класса Car указывают базовый класс
типа и включают различные флаги, использова.Бlпиеся ЕОНСТРУКТОРОМ типа при его
создании (ТaRие ЕаЕ [publicJ, [abstract] и т.п.). Методы (например. ЕОНСТРУЕтОР
класса Сау) описаны с учетом их имени, параметров и возвращаемого значения.
НаЕонец, обратите внимание на то, что свойства представляются внутренними ме­
тодами get /set с использованием меТОЕ
Setter/Getter метадro-iнbIХ.NEт. Как
и следует ожидать, производныетипы Car (это SportsCar и MiniVan) описываются
аналогично.
Гмва 12. Отоб:ражение ТИПDВ , динаМИlfеrжое СВЯIJblВЗ-Ю1е . .. 499

Анализ TypeBef
Напомним. "<по Метад.анные ROМnQН"QВОЧllorо t)лоШl ОllИQlJ,ВаЮт не :голыш множе-­
С'ФО ввутреJ,IНИX типов (Car" EnglneSt~te И 1:Д.). 1Ю и внешние nmы. на которые CCЫ J
JЩ~СЯ данный lЮМnCllвовочm.IЙ блод. Нanpиъlер. поскwn.нy ~a:z;Libra.ry. dll апреде­
~ два neречнп, n описании upисутC1'Rуer блок TypeRef ДItЯ тИПа System .EnlJТn.
тyp8Refll (010оаОО1)
- .... ~----_ . __-:-._-_._ -------------_ .... _-----------------------~

'I'оkе-л: QXiOlO{/O'OOl
Reso1 uti:OnSc;ope: Ох2ЗООО(J"O, 1
TypeRef:N.a,me: SysteJD.EnUIR
МemЬerRef #1
- -----------------_....._----------------- -- ------------~---

Мепt.bвr: (ОаО:ОО О От) т 6St.ririg :


CallCnvntn: [D~F-AULT ]
hазТhis
Ret~znтype~ String
NQ argцment,s.

ПредСТ8'вnен.ие метаданных компоновочного блока


OНUO Мe'rМaщIЫX ildаsm.ще позволяет :rаюке просмотреть Meт~a:нныecaмo­
го 1!ОМI!ОНQВО'П{РГО бдО1Шj ДJЦJJ обозначенииlЮТОРЫХ ИСПОiiIЪЗYе'Тся метка А,5э~m:blу,
Следующий фрarмент ЛИСТШIГa по1tазывает. что информация, предc:raJmfШная в
таблице АэзетЫу. аиалоrи'ffl3. $формации получаемой в окне i ld'asm .ене че­
рез ШlI(тограм1>1У MA~IfgST (и это совсем не удивительно). ВО1' часть манифеста
CarLibrary.dll Свеpcиu- 2ДО.().

AsНlllbly
- --- ------------------~
. --- ----- ---------------~-
Token~Ox20(J00001
йате ~ Ca,r1.i·b rar'i
PuЬHc 1<еу : 00 24: ао 00 04 ВО 0.0 00 11 JI .... д.

Ra.sn Algorithro , охооооеОО4


мa.jor Version: 021.0000'0 002
Мin<ц:vl:!rsion: охо.оооооро
Build Nu'm beI: o~ooaboooo
Reviaion NumЬer= 0:н;ооаОООО()
Locale: ·< null '>
Flags : (SideBySideCow.pstible] (ооооро.оо)

Представление ссылок на дрyrие компонавочнwе бnо.ки


Вдобавок к :метке ЛS5еmЫу и набору :меток TypeDef и TypeReff метадЗШIЫе .NEТ
ИСЦ~ метки ~AgsemblyRef4tn". чтобы обозначить внешнщ~ вом:поновочные
блоцв. Например~ :ПОСROлысу CarLibrary.dll и.спользует np] Messa.geBox, в oltНe
М~'Faдшm:ыx вы. обнаружите метку Assernb.1yRef длn Systero.• W.i.l'I.do'W-s. Form.s.
500 Часть 111. Программирование компоновочных блоков .NET

A888lllblyR8f 12

Token: Ох23000002
Public Кеу or Token: Ь7 7а 5с 56 19 34 еО 89
Name: System.Windows.Forms
Version: 2.0.3600.0
Major Version: ОхОООООО02
Minor Version: ОхОООООООО
Build Number: ОхОООООе10
Revision Number: ОхОООООООО
Locale: <null>
HashValue Blob:
Flags: [попе] (00000000)

Представление строковых литералов


в заключение нашего обсуждения метаданных .NEТ укажем на то, что все стро­
ковые литералы базового программного кода представлmoТС8 в окне метаданных
ildasm.exe под знаком метки User Strings,:кaн показано ниже l •
User Strings
70000001 (11 ) L"Car 2.0.0.0"
70000019 ( 11) L"Jamming {О}"
70000031 (13) L"Quiet time ... "
7000004d (14 } L"Ramming speed!"
7000006Ь (19) L"Faster is better.
70000093 (16 ) L"Time to call ААА"
700000Ь5 (16 ) L"Your car is dead"
700000d7 ( 9) L"Be quiet "
700000еЬ ( 2) L" ! 1."
Пока что не СЛИШКОМ беспокойтесь о точном синтаксисе каждого элемента ме­
таданных .NEт. Более важно то. что метаданные .NEТ дают очень подробное опи­
сание всех типов. определенных внутри базового кода. и всех данных, на которые
в этом базовом коде имеются ссылки.
Теперь у вас должен возникнуть следующий вопрос: если вообще нужно что­
mo знать о метаданных, та как использовать эту информацию в npwwженuяx?
Чтобы получить ответ. давайте рассмотрим такое понятие. как сервисы отображе­
ния .NEт. А вопрос О пользе предлагаемых ниже подХодов мы оставим открытым до
рассмотрения соответствующих примеров в конце этой главы. Поэтому наберитесь
терпения.

Замачание. В окне Metalnfo утилиты i 1 d а s m • е х е вы обнаружите также РАД меток


CustomAttribute, которые используются АЛА обозначения атрибутов, примененных в ба­
З080М программном коде. Роль атрибутов .NEТ мы обсудим в этой главе немного позже.

1 Символы кириллицы В пользовательских строках программа ildasm.e)(e просто игнориру­


ет и в окне метаданных не отображает. - Прuмeч. ред.
Глава '12, Оitrбра.жеtн{е rипов . ди~амичеСj[ое СЕ!Я3Ы~l'Iие... 501

Отображение ТИП'ОВ В .NEТ


В 'терминах .NEГ оrnюбрl1Жеi-Uie обозначает процесс выяснения параметров типа
срмой выполнения. Иепo.ttъaуя сервйсы отображеmm. ту же инфОРМЭЦЩD ме та­
Д~ТX, .которая Qтобрюкаетсн {; помощрю i lda~·m. ехе, вы можете получить npo-
грам;мно. Нanpимер. с ПOJllOII(ЬЮ отображения МО1Щ{О полyчпrь список всех тЮlОВ,
сод~р~ся В данном RОМIlоновочнам б;ТЮJ{{: (И-"'rи в файле" .ле tmО d1J.lе]. ВJUIЮЧая
MeтoД}J., по.тш, сноИ:ства и события, ОiJ:pеде.1ТЕ:lшые данным ТИII~М. МOЖНQ также ди­
намичесЮ"l ВЫЯCНnТЬ,• .RaкОй наб!1р интерфейсов 110дцер:atnвается данцым 1tJ1з'СС0М
(или сТрjl1trypойJ, .вьшснитъ параметры метода и .,llрутие ана.ttоrиЧН:ые 110дробности
(базовые классы, информа:цию пространства имен, цанньх!" малифеста и т.д.).
подобно moбому друГому просгтранетву имен, SJlS't"_E'!]1. .RefleC tion содержит ряд
свяsанных ТИllQВ . В табл . 12.1ПРИВОДи'ТNf СПИСОI< элементов зтоrО иространстlЩ
имев, Q HOТOPъui:. вам следУет знать.

Таблица 12.1. Некоторые элементы пространсl'В'Q имен Sys.tern. Re f le-r:ti~)Jl

Тип ОnИСl!l'Iие

Этот клас!:) [вме(;[е t м'НDжесtвом с!iilзанныx типов) npeдnвraeT рsЩ методов ,


пriэвоi1Яющихэarpужат.Ь, исследовать и обрабатЫвать компоновQч/,lый МО)(

А,ме!!!Ъ1 yNatne Класс, позволяющий ВtiIЯСНЮ!, много 4'и'сr.rежыe hO)J;робf10СТИ, кэсающиеСI1


идентификации КОМПОfН)ВQЧНi)ГО блока (информаilИЮ .о версии. I1араметры
лcrжалvщации и Т.д.,)

Eveni:lnra Клеое , содержащий информацию об указанном событии

rie1d.Intc Класс, содер:жащи'й инфОРМацию об указан-нам попе

меJпЬеrII1fо АбстраК:ГНt>IЙ БВзооtilЙ класс, определя!ош.ий общи~е ~арактеристиlcИ пащще­


ния ДЛЯ ТИЩ'!! ~vei1t,I о fo', fiеldIпf,о, J-1еthоdIr, fc И p'yopert.yl l'j Ее

Met hodInfp КItacё, содержащий инФормацию об УIcli!ЗЭIiНОfi.1 меТОДе

Мос!и lе Класс" лозвоliя'ющиИ получl>ll'Ь доступ 1<. y~agafJ't'lOMY модулю МI:/ОГОМО.дульно­


го KOmnQJ-JОВО<jНого б.1ТUк:а

1?аrаmеtЕ!J;"Iлfо. Класс, содержащий liНфармацию об указанt,lоМ г,apaM~Tpe

:propertylnfD Клас{), СDД~Ржащий инфармаuию об Уl<~занном СВОЙС18~

ЧтоБЪJ ДОНIcr'Ъ, как использовать пространство имен System.Refl.ect.ion для


ЧТeRИfJ метадЩЩЫХ .NEТ праг]')юVlмн:ы:мn средства.'ЧИ. мы с вами доласны сначcurэ
03'НакоМIol'ТЬСЯ с 'ВОВМQЖНОQТЮIIИ RЛасса S'yste!:Fl. Ту'ре .

Кn:acc System. Туре


Класс System.T'yp.e определяет ряд 'ЧЛенов, которые MOryт использоваться IIJIЯ
чтеНИll MeTaд;ЦUIЫl!; типа, :и юю:гие из ЭТИХ членов возвращаю'Г ТИПЫ ~r-з пра "

~Tp~CTBa име.н Sузtеm.R-еflесtiОО. Например, ТИП Тур€;.ЭеtМ-еthGd.l>()ВQ~нt


щает массив ТИПЩJ Me>thodInfo, тип Type.GetFie.~d.s () возвращае.т массив 1"1'
FieldIr,fo ~ Т.Д. ПОJIНblЙ набор I)'I1RpытblX членов Sys t e;m.Type очень 13елю<
табл. ],2.2 uредлагаетея н:ебoorьшоИ; -с:гrи:со:н наиболее важных: из .них [под,р, · '
QПЩ'ШlИН МО~U-lO найТИ в документации .NET F.amework 2.0 SDKJ.
--
502 Часть 111, Программирование компоновочных блоков, NEТ

Таблица 12.2. Избранные члены System.Type

Тип Описание

IsAbstract Эти свойства (наряду с другими аналогичными) позволяют


IsArray выяснить ряд основных характеристик соответствующего
IsClass объекта Туре (например, является ли этот объект абстракт­
IsCOMObject HblM методом, массивом, вложенным классом и Т.д.)
IsEnum
IsGепеriсТуреDеfiпitiол
IsGenericParameter
IsInterface
IsPrimitive
IsNestedPrivate
IsNestedPublic
IsSealed
IsValueType
GetConstructors() Эти методы (наряду с другими аналогичными) позволяют по­
GetEvents () лучить массив, представляющий все элементы соответству­
GetFields () ющего вида (интерфейсы, методы, свойства и Т.п,), Каждый
Getlnterfaces () метод возвращает свой массив (например, GetField$ ()
GetMemЬers () fJозвращает массивFieldInfo, GetMethods () возвра­
GetMethods () щает массив MethodInfo и т,д.). Каждый из этих методов
GetNestedTypes(~ имеет также форму единственного числа (GetMethod (),
GetProperties () GetProperty() и т,Д,), которая поэволяет извлечь один кон­
кретный элемент по имени, а не все связанные ЭJ1ементы

FindMembers () Возвращает массив типов MembecInfo на основе заданных


критериев поиска

GetType () Статический метод, возвращающий экземпляр Туре по за­


данному строковому имени

InvokeMember () Позволяет выполнить динамическу!О привязку к заданному


элементу

Получение Туре с помощью System.Object.GetTypeO


Экземпляр класса Туре можно получить множеством способов. Нельзя только
непосредственно создать объект Туре, используя для этого ключевое слово new, по­
сколькукласс Туре является абстрактным. Чтобы привести пример одной из допу­
Стимых возможностей. напомним, что System .ОЬ] ect определяет метод GetType (),
который возвращает экземпляр класса Туре, представляющий метаданные соот­
ветствующего объекта.

/ / Получение информации 'rИIJа с помощью экземпляра SportsCar.


SportsCar sc = new SportsCar();
Туре t = sc.Get~ype();

Очевидно, что этот ПОдход будет оправдан только в том случае, когда вы име­
ете информацию о соответствующем типе (в данном случае это тип Sportscar)
во время компиляциn. При этом становится ясно, что такие инструменты, как
ildasrr.. exe. не MoryT получать информацию о типах путем непосредственно вы­
Зова System.Object.GetType (), поскольку ildasm.exe не компилируется вместе с
пользовательскими компоновочными блоками.
[ЛIJ,ва 12. Отоfipажеl1ие n1ПОI\, дина"ичеСJtQе евяэыва~ие.. . 503

Получение Туре с помощьюSуstеm.Туре., GеtТуре()


Более гибкий ПОДХОД обеспеч-иваетси 'ИСпользование.м статическоroчлetlа
GetType! J I(JIRCCa Sуэс.ет.Туре с указанием абсomomоrо име1Ш соответствующе­
ro типа в виде строки. I1pи и.СII0JIЬ30вании Tд.R'OГO подхода Ддц :и.зsлеЧСЮIЯ мета­
данных уже не требуется информаЦJ:fЯ о ТШIе во время }(оМIlИJlJЩИИ. по~оJIЫtY
Туре. Get'l'ype () иcnользует экземпляр "вездесуще:го~ System.String.
Метод Type.Get'I'ype () перегружен. ЧТФбы можно было yJ<aзаТ,Ьдва шtраметра
пша Вооl еа.n. ОДИИ из :которых контролирует неоБXQ~ОСТJ;. rенеpиpo1laIUШ ис­
КIПOченшt. оогда тип не найден, а дpyrой - Нt".об"одимость ,игворироввmm: реги­
стра символов в стро.ке. В качестве примера рассмотрите еледующий 4ФarмепТ
проrpаммноro кода.

11 ПОЛУЧfU1И8 ~орка.щас '1?Ш& С ПОIIClЩbl) ",~oi{l" Тyp~. G8t!rypeO


!/ (118 Х'8Веprqюaa>no XCJQW"'eНR8, .c.nИ SportsCar не JlЩ_Н,
/1 к JЦlНО}1ИРОJtа'1!$о P8I"НC'1'.P QИlDO.IIiоа) •
1уре 't = "Тj>"'Ре. G!lt'1'ype C'~CarLibrary. SportSCar", false, 'E.rueJ J

~ этом цримере обратите вютманке па ТО, что в cтpORe. }tOTOPyIO, вы передае­


'те в GеЦ'уре () • :ничего не говоРИТйI о кOМnOновоЧ'НОМ' блО&е, содержащем данный
ТЩ!", Б этом случде предполагается, Ч'I'O соответствующИЙ ТШI определен в рамках
RQМПОВОВОЧНОГО блока, :выIлн.немаго в настоящий момен:х: Если же вы хотите по­
лучи) ь метвдщtные ДJIН'Ji'ИП8 из внешвero, npиватноrо КОМПОно:ВОчногtJ, блока. то
crpOJЮВШ naраметр должен иметь фермат абсшпотного :ИМени ТИпа. за ROTOPbIМ
через запятую доlDtШо следовать nORЛТНое имя компоновочного блока. содержаще­
J:O 'Э'F()1,'ТШ1.
1/ ПQ1iYЧ8ВИ8 КВфopиaфOl orиaа JQ .ilelllНe:J;IQ 1I:оипоноао"<ЦОХ'о бnOll:&.
Туре t = nul1;
t '= Туре. GetЖуре ("CarLiЬ.cary. SportsCar I CarLibii'aty") i

СЛедует 1:аюие :шать о ТОМ. 'l'I'O В строке. передаваемой 'МеТОДУ Get Туре ( ) • может
.npш:утствоват.ь знак "rurюc" (+). ИСIIользуемыйдля обозначения вложеJ-tЖ>2Oтuna.
Предположим, ЧТ() мы хотим получить информaщlЮ для перечня (S'руОрtiол s' j •
.БJIOЖeНifоro в Класс JamesBondCar. для этого мы доJ!жны написать следующее.

11 Po.JlY1il.... инФоptИЦJP( 'ИШ4 ~ 8,JI0*8_0JЮ пер_ч~


/ I • P""'!UI _ _'t'OC:. ХОIIDоВО80ЧJlOZOО б.nоха.
"Туре t =
Туре .GetType ("CarLibrary. Ja.mes.E!ondCar+SpyOptions") ;

ПОЛY'lенме Туре с ПОМОЩЬЮ typeof()


1:IaJЦ>Heц. МQЖнр получить информацию типа с :Щ,lМQЩЬЮ (шерtЩИИ С# type 0.f.

/I П~ ~ с lШIIQЩ,Jd) qpeof.
Туре t = ~o~(SpDrtsCat):

Подо6но методу Type.GetType О . Qперa.циJt typeot ОRазывает~ ;полезной тем,


'что прй ее ИСПQJIbЗOвании нет необходим~ сначала саздаватъ зкэеМЛJIЯР объеu­
та.ЧТО,БЬ): а.атем И3ЩJе~Iq из негр информацию тmщ. Но ириэтом ваш бавовЫЙ' ~oд
все равно должен иметь ИНФОР!I<UЩЩQ о типе во время RDМПИЛЯЦJJП.
...

504 Часть 111. Программирование компоновочных блоков .NEТ

Создание пользовательского ПРИЛО)l(ения


для просмотра метаданных

Чтобы очертить общие контуры процесса отображения (а также привести при­


мер использования System. Туре). мы создадим консольное приложение, которое
назовем MyTypeViewer. Эта программа будет отображать подробную информацию
о методах, свойствах. полях и поддерживаемых интерфейсах (и дрyryю информа­
цию) для любого типа из MyTypeViewer. а также из mscorlib.dll (напомним. что
все приложения .NEТ автоматически получают доступ к этой базовой библиотеке
классов).

Отображение методов
Мы модифицируем класс Program. чтобы определить ряд статических методов.
IЩЖДЫй из которых будет иметь один параметр System. Туре и возвращать void.
Начнем с метода ListMethods (). который (как вы можете доraдаться сами) печатает
имена всех методов. определешlЫX указанным на входе ТИПоМ. При ЭТОМ заметим, что
Type.GetMethods () возвращает массив типов System.Reflection.MethodInfo.
// Отображение _ен методо. типа.
public static void ListMethods(Type t)
(
Console.WriteLine("***** Методы *****");
MethodInfo [] mi = t. GetMethods () ;
foreach(MethodInfo m in mi)
Сопsоlе.WritеLiпе("->{О}", m.Name)i
Console.WriteLine("") ;

Здесь с помощью свойства MethodInfo.Name просто печатается имя метода. КaJt


и следует предполагать, MethodInfo имеет много других членов, которые позво­
ляют выяснить, является ли метод статическим. виртуальным или абстрактным.
Кроме того, тип Methodlnfo ПОЗволяет получить возвращаемое значеНflе метода и
множество его параметров. Реализацию ListMethods () мы с вами проанализиру­
ем чуть позже.

Отображение попей и свойств


Реализация Li s tFi е lds () будет аналогичной. Единственным отличием будет
вызов Type.GetFields (). а р~ультирующим маСсивом будет Fieldlnfo. для про­
стоты мы печатаем только имена полей.

11 Отображение ииен попей типа.


public static void ListFields(Type t)
(
Console.WriteLine("***** Поля *****");
Fieldlnfo[] fi = t.GetFields();
foreach (FieldInfo field in fi)
Сопsоlе.WritеLiпе("->{О}", field.Name);
Console.WriteLine("") ;
Глава 12. Отображение типов. ДинаМllчеr.жов' СВlIзывание... 505
Ло:tикa отображения свойств типааналогИчна.

11 ОТображение инеи сиойсwа WИQа.


public sta,t.±c void ListProp$ (туре t)
{
COn'sol.e.Wr.it~Line (»~ *kH СБQ"ЙС'тв:а *.,***П);
Propertylnfo [] p.i = 'l. GetProperties (J 1
foreacn (Pr:apertYIn:f.o propiJll pi)
c.onQo.le.WriteLine (Н-О> (О}''', prop.NameJ.:
Cbflsole. WюitеL~ (П'П);

Отображение реалиэоваННЬIХ интерфейсов


Теперь построим метод Li.s,tInterfac·es С) • который будет печатать имена ИН·
'reрфеЙсов. поддерживаf'.мьJX указанным на входе типом. Единсnенным за~жи"
вaJOЩММ ВRИМa1-IП8 моментом едесь является ВЫЭОJl Getrnterfaces О. В08враща­
ЮЩИЙ массив System.Types. Это ЛОГИ'IПJО. поскалы<у J:Штерфейсы тоже являются
типами.

{I OlJ!o~p"'lOIe P8a,m!808llRИJIX ИИ~Ф.ЙС08.


public sta·tic void L1вtlлt.еr:f'.асе.s (Typ~ t j
I
Co.n.sole. W:r::itеL:iие С .. "',, **-- Ин'I'ерф.eйCtIc, ,.. ..... * '" ") ;
Туре [] ifaoes =t. GetIn terface$ () ;
foreach (Ту.ре i in Насев}
СОFlSоlе.WritеLiЛ8 (11-> I О) 11, i .. Name) f

Отображение вспомоrатеnьной ИНформации


Наконец. мы рассмотрим еще один ВСПОМQraтem.ВЫЙ:м:етод. который будет ото­
бражать р.азличные статистические хаРaR1'еристиЮJ типа (romв.етси ли тип 0606-
1ЦeotIНЫМ • .кaJ(ои ТИП для н.еro вв,т-щется базовым. изолирован ли он.и: т.д.).

11 ~обр~С8 дon_ ПОnВOlПI xap!lXВi1,


public static void L:!.stvа:r{О\lзStаts (Туре t)
I
CCllSole.. W.ritеИl'lе (""''' * ** Б-=ОN.ОГa:'rsльная: ин.формацив: .. ., ***") ;
Сопвоlе. Wri teLine (UБаэо1выИ :класс: ! 01", t. заа.Турtl) ;
Con.sole. wri teLine (,''ЭТО аБQтра1<'1'JЩff 'jj'ип? (О} 11, t .~a~BtraC!t:);
Oonsole. Write.Line ("Эт,-, ИЗQ,пированный тип? {'О} ", t. IsSealed) ;
солsоiе. Wri teLine ("Это Qб"l5щеннblЙ orИrI? ! G} ",
t.IBGenericтypeDef~nition);
Сопзоlе "WriteLine (-"Это ТИП класса? (.О} "., t. IaClasa) ;
Consyle. Wri teLine С"") ;
506 Часть 111. Программирование компоновочных блоков .NEТ

Реализация Main()
Метод Main () класса Program запрашивает у пользователя абсолютное имя
типа. После получения СТРOItoвых данных они передаются методу Туре .GetType ().
а извлеченный объект System.Type отправляется каждому из вспомогательных
методов. это повторяется до тех пор. пока пользователь не нажмет клавишу <Q>.
чтобы завершить выполнение приложения.

// ЗдеСIt необхоДИIIО yкaaao.rъ npoС!l.'pi1ИC'r80 ииен отобрааеВИJl.


using System;
using System.Reflection;

static void Маiп(вtriлg[] args)


(
Console.WriteLine("***** Добро пожаловать в MyTypeViewer! *****");
string typeName = .... ;
bool userIsDone = false;
do
(
Сопsоlе.WritеLiпе("\пВведите имя типа");
Console. Wri te ("или нажмите Q для выхода из приложения: '' ');
/ / Пonуче_. ииени типа.
typeName = Console.ReadLine();
/I .ела.т JtИ ПOJПoзова'1'8.IПo аавеp!llИ9o работу npиnо.еВИJl?
if (typeName.ToUppe:r() = "Q")
f
userIsDone = true;
break;

/ / ПОШl'1'lCа отобрuce_. типа.


try
(
Туре t = Туре. Get туре (typeName) ;
Console.WriteLine(····);
ListVariousStats(t);
ListFields(t);
ListProps (t);
ListMethods(t);
Liэtlпtеrfасеs(t);

c.atch

Сопsоlе.WritеLiпе("Извините, указанный тип не найден");

while (!userlsDone);
f)Jasa 12. ОТОбражение тиnы3..• динаlAи,4еСI(08 овязывзtlИ'е . " 507
к этому MOMeQ.:IY U}щлошеuие mytype'Jiewe-r . e.)<:~ уже t'OТOВO )VlA Tect'OBoro запу­
ска. Запустите это прилощеиие и введите. следующие аБсolIютныe имена (помните
() том .. что rrpи исполъзуемом здесь варианте вьtзова Туре . G.etTyp е (} строки имен

ОRaз;ываются qувствдrелънь\'МR х реrистру сймволо&).

• Syst E;j 11!.• Intз;2


• Sys,t E!m. С оэllе ctioГLS. Аз: rayLis t

, Sysi:em. Thlreadin§ :Th.read


• sуstе m .\Лоid
• systeIfL.JO.BinaryWriter
• SyStem .Math
• Systero.. Oof1t5Qle
• l"1yTypevie wer.Progra,-m
На рис. 12.2 показала информация ДЛЯ <'J1YЧ<Щ. СООТDеТСТВJЮЩего выбору типа
Sysb'zm .Yiд:lh.

~ис:.12.2. ОТОQраже1-1ие sуяtеД\.Маth

Отображение параметров и возвращаемых


значений методов
Итак. все работает.Теrreръ немного усоверше11сТВУ~М нame: ПРИЛОЖею!:е. В ч:ас'I­
HOc:m. моднфицируем вспомогательную ФУНКЦИЮ L,i stMe·t h·o ds.(). чтобы ~олуч-атъ
не ТОЛЬКО , имя метода. но· и B03Bparцaeмoe значение, а также щодные паРаметры .
Дан решения I1мейно таких задач тиn Nethodlnfo предЛага~т СВОЙСТВО RetuIrJType
.! ме1'Ьд GetPat amete:cs О.
508 Часть 111, Программирование компоновочных блоков ,NET

в следующем фрагменте программного кода обратите внимание на то, что стро­


I{a, содержащая тип и имя каждого иэ параметров. строится с помощью вложенно­

го цикла f or: each.


ри Ь} i c stati c void ListMe t110ds (Туре t)

Cons ole.Wr: it eLine("***"" Метод", ~""~* ");


Met n'odlnfo[ '1 гпi = t.GetMethods();
Eo re ach (Methodl nfo m i n mi)
{
11 Получение возвращаемого значения:.
s·t ring retVal = m .Rеt urпТур е. FullN агпе;
st ring par aml n fo = "(";

// Получение параметров.
f or ea ch (Раrаmеtеrlлfо p i iл m.GetParameters())
1
paramlnfo t= s trin g.Fo r mat("{ O) { l} " ,
pi .Pa ramet erType, pi.Name);

paraml n Eo += ")";
1/ Отображение основных хара~теристи~ метода.
Сопsоlе.WritеLiпе("->{Оf {l} (2)", retVal, m.Name, paramlnfo);

~'JI1so1 e. WriteLine ("");

Г<:с ли вьmолнить это обновленное nриложение теперь, методы соответствующе­


го типа будут описаны более подробно. Для примера на рис. 12.3 ПОRaЭаны мета­
данные методов для типа System.Globalization.GregorianCalendar.

Рис. 12.З. Подробное описание методов System. Glob alization.Gregor ianCalendar

Весьма увлекательно. не так ли? Ясно, что пространство имен System. Reflection
и Iwacc S у s·t ет. Т У Р е позволяют отображать многие другие характеристики типа. а
не только те. которые в настоящий момент реализованы в MyTyp eViewer. Вы впра­
ве надеяться на то. что можно будет исследовать события типа. выяснить., какие
Гnав:а 12. Отображение ТИПОВ , ДИНIШИЧt!ское СВl1зываl1lofе .. . 509
интq>фeйс~ реализоР8НЫ JДlНo. полyчwrъ СПИСОК ОБОБщенных параметров для зз­
Д3Нf!}ilY. ч:ленов ицровер~ МJlQжеСТIЮ ДРУIИХ.хар3RТe'pИСТИR.
Но и jI нынеIЩIем СВоем ВИД-е ваш обозреватель объе.Itтов уже :КQе~что умеет.
'DJЯf\Юх1М его огранич:enием. RЩ'lе~mо же. является то, что у вас нет нишпсой воз­
МQЖНОL"ТИ отРбража~ ОбъeRJ'Ы . размещенные вне дап:нOJ'о IWМПOНОВОЧНОГО блока
(MyTypeVlewer) щm :вcerд~ дрC':l"}'11lmГО IПsСоrliЬ·.dll. В CВJl3И с этим остается от­
RpытымщIJpрс:: ~~ строШ1> приложения. которые могут эarpyжать (и отобра­
жать) КОМ:ПОН9вочцые бдОЕИ. о 1С0Т9рых нет информациИ во время .номnиляции?"'

JJсхоllНWЙ сод' Проект MyTypeViewer размещен в подкirщлоre.• соответствующем главе 12.

Динамически эаrружаемые
компоновочные блоки
Из преДыдУЩей главы B~ узnали о 'ТОМ' !(аБ Среда CLR :Щ:;rIОJ):ьзуеll ИI:Iформа­
цию манифеста RОМШЩQВОЧВОГО блока при 3f.ЩЦ1:fРОВQНИИ КО~ОНОВОЧВЩХ: f)ЩЖОВ
пр внеIIIflИМ ССЦ1Щ(аМ. Все ЭJ'O. конечно. ХОРОIuo, RO во ....щогнх случаях бывает не­
dбxQД}ЦI4O Нна лету'" загрузить КО:МЛDНОВОЧ:НI;dЙ б:щ:щ проrраммными средствgми. а
записей о соответствующем RDМПОНОВО~ОМ бдGК~ ~. ~anwфесtt ает. Форма:лъно
зarРУЗJCa' ~неIIЦП{X KOМnOHOB·D~н.ьm блонов ЦО зацросу FIаэьmается дWtClМtlЧeCJCoЙ
эагрузкоt1
В p~цx System. Rеflесt,iОnQлределяетCJ{ Jf,JIащ:. ИМ~ КОТСРОl'О Assembly.
Иcnольау.я:этот тип. можно дщraМ,IiчеClQi затруэ~ любой JtомnоиовоЧНblЙ блок.
а та.юке вынсни.тъ ето СЩЩС1;'Ба. ИСJ;Ющ.зуя ТЩ1 АэsеrпЫу. можно дина:мичеC1tИ за­
гружать npиватные и общеДОС'1j'IlRЫ6 КQ~ОНQвочньте МОМ. размещemше в лю­
бом месте системы. Класс А,Б- БетЫу предлarает методы (В частности. Load () и
1,oadFrom (»). ПО~ВОJl:яющие проrраммными срещства:ми получать информацию.
ая:8JJ;DIWDJyJQ той. щrгорая ~дерщитcJ;l в файле *. Gon.fig юmента...
,Ддв ЦРИl'<lера испОльзования диnвмичeCRoЙ зarpувки СОЭД8Йreновое КО~OПЫJ'ое
при.п~Ю,Jе с им-енe:t\of Е~tеrnаlА5sеmblуReflес1:.Фl:. Вanrей задаЧёЙ JlВmien:я по­
строе;ъше· !оо(етода Main () • запрamивa:ioщего попятное имя RQМn01IОВОЧНОГО бло'Ка
Д1Ш .щп:lаМ}iче(:;к.оЙаатрузки. Ccьmкa. Аsвещblу будет передана вcnомоraтельному
меТ9ДУ Displ ауТуреэ ,() . :который просто напечатает имена всех .классов, интер­
фей~в, r;тp)'WIYp. перечней 1iI делегатоВсоответc:myющего компоновочного блока.
f-leo(}1ЩЦИМblЙ программный :код ВЫРJU1ДИТДОВОЛЫlо просто.

using Systemi
1I1sing System.Relfl!?ction;
using SysteI!L.IO; /1 Да. сШре~еа8нkR FileNotFounclExceptlon.
nаmе-эрасе Ex t ethalAsseД1blyR:eflector
(
c.lass. Pro.gram
{
static voi d Diврlау'I' урезlлАЗ1Il . fАsS:еmhlу а sщJ
{
Сопsоl е . Wri teLine- ( "\л" ...", ТИПЫ :КОМ:ПОНО'ВО 'ЧНGГО блока ,Н'" ") ;
:C onsole. Wri teI,irle ("-> {О)", asiIi. Fl1l1Name) j
r

510 Часть 111. Программирование КОМПОНОВОЧНЫХ блоков .NET

Туре[] types = asrn.GetTypes();


foreach (Туре t in types)
Сопэоlе.WritеLiпе("Тиn: (О}", t);
Console.WriteLine("");

static void Main(string[] args)


(
Console.WriteLine("* ** Обзор внешних компоновочных блоков ***");
string asrnNarne = " " ;
bool userIsDone = false;
Assernbly аэm = null;
do
[
Сопsоlе.WritеLiпе("\пВведите имя компоновочного блока");
С опsоlе.Writе("или нажмите Q для выхода из лриложения: ");
11 ПоnучеНИ$ имени ~оипоиоаочиоrо бnо~а.
asmNarne = Console.ReadLine();
// Ьnает .nи аonъзоаатe.n. зааElрllih>nO работ)' арипо. .ви.?
if (asmNarne. ToUpper () == "Q")
{
usеrIэDопе = true;
break;

/1 ПОПlol'1'ка заrp)'з~ коипоиоао'IJDIЙ бnО1l:.


try
{
аэm = Assembly.Load(asmName);
DisрlауТуреsIпАsm(аэrn);

catch
(
С опsоlе . WritеLiпе("Извините, компоновочный блок не найден.");
}
whi l e (!userIsDone);

Обратите внимание на то, что статическому методУ Assernbly.Load () передает­


ся только понятное имя компоновочного блока. который вы хотите загрузить в па­
МЯТЬ. Поэтому, чтобы получить отображение CarLibrary.dll с помощью этой про­
граммы. аеред ее выполнением нужно скопировать двоичный файл CarLibrary.dll
в каталог \ Bi n \ Oebug приложения ExternalAssemblyReflector. После ЭТого вы­
вод npограммы будет аналогичен покаэанному на рис. 12.4.
I Гнав!! 12. 010рраJl\(\ние ТИПОВ, ДИf~амичесК{)е СГ:jязыва~IИВ... 511

Рис. 12.4. Отображение 8rf8шнаго К0l1offiаНОВО~f-JОГО ,блока Carl,wtary

ЗамеЧaflИ·В. Чтобы ПР~1лажеАие ExternalP.asemblyReflector БЬiдо более гиБКИМ. Сl1еАУ.еТ


загружать ВNешI'1И~ ~омпоново'lНЫЙ' бло~ с ГlОмОЩЫО Аss<?mblу.LоаdFl"ОЦ1 (). а ~Je с помо­
щью ]l,s-sembly.l,'()ad (). Тогда вы сможете УКа3'8ТЬДЛR соответствующего KOMnoHoB0'4Ho1'1!I
бщжв абооrнотиый пyrь (напрммер, С: \ Му.Арр\МуАет .а 11),

Исходный ход. Про8J(Т ExtemaIAssemb!yRef!ectoT роомещеы в п"Одкатап[)ге. соответству'lOщем главе 12.

Отображение Q,бщедоступных
компоновочных блоков
Raя ВЬТ можете ,щогаДЬШа'JЪСЯ, :Мt;ТОД Assembly. Load () является пере:гружеННБIМ.
Один :иа вариантов метода АssащЬ1у.Lоаd CJ ПDзвоМет уканать ЗНН"!еНИе параме­
'траои1 t\JJ'"e (для щщanи3ОВЩПIЫХ liомп()новочных блоков). а также номер версии 'и
значение OTy~pЫTOГO .ключ;а (для общедос'lУПНЫК КОМnОНОВОЧFJhEl!: БЛORОВ).
B~cь цабор эл~ментов. идентифицирующих номпаНЬRОЧ:Fiъtй блан, 'Пазътвают'
дш.;rureЙны:м [или опюбражае.NН:iLМ) LUlwн.ем. ПО CBOf."JV!Y формату дисплейное имя
представляет собой CTPORY пар lwеБ М значений, разделенныхзan.ятыми, ноторм
начинается с ПОRЯТНОro имени RD:rvшонmющюго бiJJ{JV.а и продолжается необнза:гепь­
ными о:аределени.ями (они могут унааывапюя в любом ПОР.f.IДR'Cl. ВОТ нан выг:щ(ит
соответствующий mаfl..,"Iш-t (В скобках УЕазаНbl необffilатe.JIЫJblf: элементы).

Нате !, Cul tw.-e = к.од_я.ЗЫК8 ) ~, Version =


l' naBl'iЫfl_ н оме]:; _д1)ПОJlliИ'I'~h~ ~tыИ~ !'!Омер. н Шllер _ fiПМГ)СОf1<ЭВЮ1 • H~~мep_::I3ариuн 'J' а)
("pI:IыlcKeyтoken== к-од_ ОТ крытого_ к:n:юча}

3начtние РuЫiсКеуТ6kеn=lШll в 12'Грсш:е. опреДеЛSTh)'iцей дисп.ч:еЙНО,е ll.1\.iH. ука­


зывает на то. что при связьrвaний: про:верка ctрьгоrо :имени не требуется и его на­
личие у комшЛ:lовочного блока не оБЯЗа1"еЛЬЕD. Значение Colture="" сообщает о
Нl':оБХ:одимости испсmьзований значения .кода Лока.г1Изации. прИНИ1'QrQ F-E!. машине
по умолчан-Ию, НaIIpИl\.'l.ер:
r
512 Часть 111. Программирование компоновочных блоков .NET

// Загрузка CarLibrary версии 1 . 0.982.23972 с кодом локализации,


1/ испоnьзуемым по умоnчанию.
Assembly а = Assembly.Load(
@"CarLibrary,Ver:sion=1.0.982.23972,PublicKeyToken=null,Culture=""");

Пространство имен Systern.Reflection предлагает также тип AssernblyName.


который позволяет представить указанную выше информационаую строку в объект­
ной переменной. Обычно этот Iшасс используется вместе с System.Version.
нвляющимся объектным контейнером для номера версии компоновочного
блока. Создав дисплейное имя, вы можете передать его пере груженному методУ
Ass ernbly.Load ().
/ / Использование АзsemblуNamе ДЛИ оnpеделеRИИ диcпnейиоI'О _еRИ.
AssemblyName asmNarne ;
asmName = Jl.8v} AssemblyName () ;
a smName .Name = "CarLibr ary ";
Versi o n- v = new V еr siо п{"1 . О.982.2З9 7 2");
asmName.Ver:si on = V;
Assembly а = As s embly.Load(asmNaroe);

Чтобы загрузить общедоступный компоновочный блок из GAC. параметр


~,'3 e mbly .L oad () должен указать значение publickeytoken. Предположим. на­
' .' I ; ,тмер. что вы хотите загрузить компоновочный блок System.Windows.Forms .dll
. 'jJСИИ 2.0.0.0. предлагаемый библиотеками базовых классов .NEТ. Поскольку чис­
ло типов в этом компоновочном блоке очень велико. следующее приложение выво­
дит имена только первых 20 типов.

USill g System ;
u si ng Sys t em.Refle c ti o n;
using System .IO;

!1amespace Sha r edAsmReflector


(
p u bli c c l ass SharedAsmReflectoI
I
priva te static void Displ ayInfo(Assembly а)
[
Cons o1 e.Wri teLine ("***** Инфо рмаuия о комп о н о вочн ом блоке *****");
Сопs оl е.W ri tеLiпе("3агруж ен из GAC? (О}",
a . Global AssemblyCache) ;
Сопsоlе . Wri teLine ("Имя : {О I ", а. G:etName () . Name) ;
Сопsоlе .W r: it еLiпе ("В ерсия : {О} ",
a . GetN ame( ) .Versi o n);
Соп sо lе.W r i tе Liпе("Кул ьтур а : [О}",
a . Ge t Name() .Culturelnfo.DisplayN ame);

Туре[] types = a.GetTypes();


fo r: (i nt i = О ; i < 20; Н+)
Солsо l е .Wr i tеLiпе("Тил: (О}", type s [i]); }

s t ati c v o i d Мairl (string [] args)

Con so le. Writ e Line ( " ***** О т о бражени е О бще дос тупных КБ ***** \ п"):
Глава 12.0тобраЖ8ние 'I!ПO-S, динамичео~ое СВЯ3~!lа..~е. ..513

11 3itrpyaJta 5Ylltem. ю.ПdоYl!l . i'ox:JU.. dll на GA.C.


s. t:r:iл.g dlsplayName = r11l11;
.displayName = "SJ"stem. Windows .."F'ormв," +
"Vers'i (1)=2 . О . о •.0," ±
11 Pub~.icKey!I'ok en =b'7 7E,15c5619;J4eO ~9" ~
@"'Culture=""" ;
Assembl у а'SЩ = AS5embl У .Load (,di$pla~N~) ;
Di splay ln.f-э (.d>S'ПJ) i
CDnsole.Rea,dLine'(J;

ИсJtOДuwl1 код.. ПР.оект SЛагedАsm~еflесtог размещен 8 'Т10дкаталоге, соотве1fствУ!Оl.I1еNl\"лаве 12.

Чуде<:но! К этому моменту нamero Qбсуждени.f,f B1jl ДОЩJUJЫ ДОНЯТЬ, кан U'споль·
зоватъ некоторые базовые элементы из npщ:тр<Щства ~ЩJ $ystem. ~.e П-есti0П для
чтения метадaRНБIX' ROМПОRО:ВЬ'П'iОто' блока во время ВЫДОJUIЩ;.IЩJ. 3дес;ь Я ГОТЩI
npи;з~ть. ЧТО. несмотря на ·фшстор красоты" ПРедщ.ц'аемого ПQд;1ЦJда. вам IЮ РОДУ
своей деят.елъности вряд ли придется строить ПОJ1Ьзовател:ьсКЩI навщ-аторы o~.к­
ТОВ. НО не следУет 'забывать о том. чrо сер'Висы отображения ЯIЦl.J1ЮТСiJJ ос:аовой
цe:n:аго ряда друтих. ОЧень широко ИGПОЛЬЗУe1'dЫX. noдXOAOBB ПРОГ~РОЩЩИИ.
включая и дШiaмuчecx:ое связыван:ue.

Дина.мическое связывание
"Уцрощеl'n-ro г.ойоря, дшta.мuчесiOOe' св..чзывnн.ue, и.iШ дWf.йМUЧР..ская' nPUВЯЗЩ­
ЗТО подхрд. с ПОМОЩЬЮ которого МОЖНо создавать э~эемпллpьi заданного пша и
~:Ы9:ьtвaTЪ :WX ЧдfЩbl 'fI среде 8ЪЩОЛlleIOШ :в условиях, когда во В'реМЯ КОМlIИЛЯЦИИ о

тшrе еще дичero не известно. При построеН1d}1 ПРЮJOжения, ИСПQльзующего дина­


мичес~ое CВШIЫЩlН}!Iе д onIоШении цеиоторщо типа из внешнerо КОМfiоноllO'ЧНОro

блока нет смы:ела Y~Th ccъrm-.-y на такой lWьшоно.вочныЙ блок. Поэтому


манифест выз~ающей стороны и не содержит прямой CCЪ'IJIКJI.
На .дщпroм этапе обсуждения 3Rачение динам:ического связывания может ка­
заться вам непон.ятны•. Если вы имеете ВОЭМOЖliость использовать "статическую
nPИJ»lак1' J( тицу (например, установить ссыл~ на комnoново'IНЫЙ блок и nOМe­
C~ 'nш в пaмsrrъ, используя ~чев()е СЛОВО С# new). то в этом cJIy'!ae так инут­
НО' (:Дe.тIaтb. Статическое с:вязывание nОзвоJШет оБН.фужйТЬ ощибки уже ВО' время
КОМПИДЯДИИ. а не во вРеМя ВЫnОJIНени.я npограммы. O)i.НaКO динамическое связы­
вание OKaaывaeT~ очень взжвым для пос:троеlUШ !IpUJIожеJ:lИЙ:. обшщающих более
гибкими возмоЖFIQCТЯМИ расши~НШI.

Класс System.ActiYator
Кпасе Sуstеm.Асtivаtщ:: обеcnе'ЩВает возможностъ реализации процесса дн­
намичеCRоij прИВ8ЗКИ в .NEТ. Кроме метОДРВ. унаедедовзщrы:x: 01' System.bbj.ect.
сам класс Act i ',.;;1 tor определяет очень неБОЛЬШ~lvщ(')жество ЧЛЩJОВ. МБGгие ИЗ ко­
торых ОТНОСЯТСЯ R CpeдCT.Qaм удалениого взаим:оде'ЙСТВШI .NEТ (ем. гл:аву 18).. для
514 Часть 111, Программирование КОМПОНОВОЧНЫХ блоков ,NEТ

нашего примера нам понадобится только метод Activator.Createlnstance (). ко­


торый используется ДЛЯ создания экземпляра типа в рамках динамической при­
вязки.

Этот метод имеет множество перегруженных вариаций, что обеспечивает ему


исключительную гибкость. Самая простая вариация члена Createlnstance ()
должна получить на вход объект Туре. описывающий элемент, который вы хоти­
те динамически раэместить. Создайте новое приложение с именем LateBinding и
модифицируйте его метод Main () так, как показано ниже (не забудьте поместить
копию CarLibrary.dll в каталоr \Bin\Debug проекта).

// Дииaalичeсхое соsдавие типа.


public class Program
{
static void Main(string[] args)
{
1/ ПОШol'l'ха sагруsит. .nохапъиyJD хоmao CarLibrary.
Assembly а = null;
try
{
а = Assembly. Load ("CarLibrary") ;

catch(FileNotFoundException е)
(
Console.WriteLine(e.Message) ;
Console.ReadLine() ;
return;
)

11 По.nучевие метаданных типа Мin! van .


Туре mini Van = а. GetType ("CarLibrary. Mini Van") ;
11 Динаиичесхое соsдание Мini уan.
object оЬ] = Activator.createlnstance(miniVan);

Обратите внимание на то, что метод Activator.Createlnstance () возврaIЦaет


обобщенный System.Object. а не строго типизированный MiniVan. Поэтому если
к переменной obj вы примените операцию. обозначаемую точкой, то не увидите
никаких членов типа MiniVan. На первый взгляд, можно предположить, что эта
проблема решается с помощью явного преобразования ТШIа, ко ведь программа не
имеет никаких указаний на то. что MiniVan должен иметь при выборе какие-либо
преИМJIЦества.

Весь СМЫСЛ динамической привизки заключается в соэдaнIO{ экземпляров объек­


тов, для которых нет статической информации. Но как при этом МОЖI;IО вызвать
методы объекта MiniVan. сохраненного в переменной System.Object? Ответ: с по­
мощью отображений.

Вызов методов без параметров


Предположим. что нам нужно вызвать метод TurboBoost () типа MiniVan,
Вы помните, что этот метод приводит двигатель в "абсолютно нерабачее" СоСТО-
f rna&a 12. Оrображеfll.1е. Т11110В, динамическое связывани.е.,. 515
вине и генериру~т появление lШфОРМацйошIOГО БЛОRа еообщенl'lЯ. Первым на­
шим .ш:arо:м ЦQЛЖИО быть получение· типа 'Меthо.dIпfо для метоДр 'I'1irboBoo.st О
с ПОМОЩЬЮ Туре .GetM-еthоd,). Получив Met'hodInfo. мы сможем вызвать
ИiпiVап.1'utЬовооst () с помощыо Inv:oke (). ДЛ:.А MethodlIJfo.Invoke (J необходимо
y&a3a'IЪ все lIapaмeтpы~ ноторые должны быть переданы методу, представ.деЮliJМy
с ПОМОЩЬЮ М.еtrюdInfо. эти napaмe'I"pbl npедставляются масс:ивом: System.Obj ect
(ПОCRОЛЪn-У метод может .иметь любое ЧИC)IО па,раметров любorо тиna).
Нат метод 'r1.Jtt,oBoost () йе имеет nap.aмeTpoB. поэтому для него указыва­
ется ый 1 (В данном случае это и означает отсу'гствие naраметров у метода).
Модnфицир)'йте MeтQД, Mai1'1 () ТЗR,

static vo.ict Мain (st:n:ing [] args')


(

11 По.пуЧение 'it'Иn& МiniVan.


'J,'ype miniVan =(1. GetType ("·CarLibrary .ЩпiVад,j ) ;

/I ДИJJ~е~.ое c:~aД.цDfe }(,1ni Van .


object obj = ActiVatOT. CreateIns1tance (mi'Di lJan) ;

11 nOnучение хиФорхaцюr о ТU~boBoost,


NеthщiInft? mi = miniVa·n. GetMethoc1 ("Т1ЛЬ9ВФ;;Jst").;
/ I ВRЗ08 JlE!'l'O~a. (1 null' озиачае'1l 6'11су'11~ие паране'1'p01l) •
mi . Invoke tobj. диН);

ПоCJl~ Э110ТО вы сможете mще>зр~тъ сообщение. подобное покаэанно:м;у на


рис. ]2.5. .

P.,.c.12.S. Вызов методз в условия)! ди.намИ'IElЩОЙ IlРИВЯЗКИ

Вызов методов с п'араметрами


Чтобы ПQкаэать rrpиыер .ДИНRМИ'iескоrо вызова метода, имеlОщио napaмe~
тры. 'предположим, "'LTO тип МiпЛ7ал о'пределне'J метод. который 1I'8:эывается
TellChildТoBeQuiet () ..
11 Уски:ремне аоD:IЩМИ. , •
рIЩНе v()id Tel.lChildToHeQuiet fst.~j"pg kidName, int sham~Intensity~
i
for iint i =0 ; i о( ~ЬаmеJ;л:tелsitу; Н+)
МеssаgеБС'1Х. Sho.w ("Потише, {О j ! ! " .• Jt.idNarrre).;
-
516 Часть 111. Программирование КОМПОfiОВОЧНЫХ блоков .NET

Метод TellChildToBeQuiet () (приказать ребенку успокоиться) имеет два пара­


метра : строковое представление имени ребенка и целое число. отражающее сте­
пень вашего раздражения. При использовании динамического связывания пара­
метры упаковываются в массив объектов System.Object. для вызова этого нового
метода добавьте в свой метод Main () следующий программ:ный код.

11 ДиВlUCИЧес!СИЙ JВlЗО. Me'J/OA& с пара.ме'1'ра.ии.


object[] paramArray = new object[2];
pararnArray [О] = "Фред"; / / Имя ребенка.
paramArray[l] = 4; // Степень досады.
mi = miniVan . GetMethod("TellChildToBeQuiet");
mi . lnvoke(obj, paramArray);
Выполнив эту программу. вы сможете увидеть четыре блока сообщений. отра­
жающих намерение пристыитьь юного Фреда. Надеюсь , что к этому моменту на­
шего обсуждения вы уже можете видеть взаимосвязь между отображением. дина­
мической загрузкой и динамическим связыванием. Для вас еще может оставаться
неяс~IЫМ ответ на вопрос, когда следует использовать указанный подход в прило­
жениях. Завершающий раздел этой главы должен пропить на ЭТо свет. но пOlШ что
следующей темой нашего рассмотрения будет исследование роли атрибутов .NEт.

Исходный КОД. Проект LateBinding размещен в подкаталоге, соответствующем Г11аве 12.

Программирование с помощью атрибутов


Как сказано в начале этой главы. одной из задач компилятора .NEТ является
генерирование метаданных для всех определяемых типов и ДЛЯ типов, на которые

имеются ссылки. Кроме этих стандартных метаданных, содержэщих:ся в каждом


компоновочном блоке, платформа .NEТ дает программисту возможность встроить
в компоновочный блок дополнительные метаданные, используя аmpuбуmы. В сущ­
ности, атрибутьr представляют собой аннотации программного кода. которые мо­
гут применяться к заданному типу (классу, интерфейсу, структуре и т.л.) . члену
(свойству. методу и т.п.). компоновочному блоку или модулю .
Идея аннотирования программного кода с помощью атрибутов не нова.
Множество встроенных атрибутов предлагает СОМ IDL (Interface Definition
Language - язык описания интерфейсов), что поэволяет разработчику описывать
тШIЫ СОМ-сервера. Однано атрибуты СОМ представляют собой. по сути, лишь на­
бор ключевых слов. Если перед разработчиком СОМ возникает задача соэдания
пользовательских атрибугов. то эта задача оказьmается вполне разрешимой. но
ссылаться на такой атрибут в программном коде придется с помощью 128-разряд­
ного номера (GUlD), а это, в лучшем случае, слишком обременительно.
В отличие от атрибутов СОМ ЮL (которые, напомним. являются просто клю­
чевыми словами), атрибуты . NEТ являются типами класса, расширяющими аб­
страктный баЭОВblЙкласс System.Attribute. При исследовании пространств имен
.NEТ вы можете обнаружить множество встроенных атрибугов. которые можно ис­
пользовать в приложениях. К тому же вы можете строить свои пользовательские
атрибуты, чтобы затем корректировать поведение своих типов с помощью созда­
ния новых типов, производных от Attribute.
Глвза 12. Отображение T\1i10B. Дt,1намичес~ое связывание." 517
Сзtедует понимать. что при ИСПОЛЬЗовании атрибутов В' црограммно:м коде ВЩ)­
ж~нные ваМИ метэданные остаются, по Cynl. бесполезными до тех ПQР. пока дру­
.гой фрarмент программного :кода не исnоль'ЭуеТ явно' отображеuuе COQТBeTCTвyIO­
шей информ~ций. До ЭТoro мет~аниые. В:fюжецн:ые вю,m В ИQМПОНОВОЧНblЙ блок.
просто ш.норируютс.я.

Потребители атрибутов
КaR вы можете:цогадатьсs . в комПЛекте с .NEТ Framework 2.0 SDK поставляется
мноЖеСтво)"rlbШт. предпазначeнн:ьtx Д1IЯ работы е ра3.1IИчны:ми атрибутами, Даже
компиmrтар С# (еэс .ехе) зanpогрiiММИpеван lIа проверку определенных атрибутов
.Б процессе RОМiIИJlЯЦИИ. Например. если :компилятор С# обиаруживает атрибут
iСLSСФm:pilаnt)., он азТО1\4атпч:есК!й прОВернет соответствующий мементна совм:е­
етимоотъ всех ето конструкций с СLБ. Если же RОМПИJtfJтОР С# обндружИ'l' элемещ
с атрибутом 1Ob$Olete 1. в окне сообщений об ЩI!ибках Visua1 StudJo 2005 ИОЯВИТ­
ся CDо.Тilетствующее предупреждение.

Вдобавок и инструментам разрабо"Гки:, :Многие мето'ДЫ: из бибЛИО'1·евба.эо­


вых КI'1aCCOB .NEТ 'Гоже запрlilrраммировaны на работу с l(ОН~Ре'ГНЫМИ атрибyra­
МИ. Например. если вы хоти:ге СО2l.'Ранить состояние объекта в файле, необходи­
мо ухаза1'Ь ДJIJI класса атрибут ['S eria 1 i zable] . KOIдa метоД Serial ize (.) масса
ВiпаryFо:rmаttэr оБЮlРУЖИDа~т указюшое свойсnю, объек;г ~томатвчески coxpa~
АЯетСJl в файл в комшштном двоичном формате.
Cpeдii CLR тaкжt контролирует нattиЧИе ()пределеll~ атрибутов. ВоЭМО:НЩО.
caмьrn известным:из атр:wбутов .N'EТ ЯВJIиетси rWebMethod]. Если вы ХОТИ1"е от·
жрьrrь метод ДЛ.'I запросов НТI'P и автоматичеСКI1 кодировать ВQЗвращаемое :цtаче­
ние M~дa в формат XМL. просто укажите атрибут [WebMethodJ ДJ1Я э:rorо МетФда.
и ВСЮ рупumую работу среда CLR выполнит сама. Кроме разработки Web-серви­
сов. В1'рибугы Bwь."НЫ ДЛИ системы безопаснос."Ти .NE'Г, слоя операциД удале:нного
доступа. вэзим()действил COMj.NEГ 11 т.Д.
Наконец. МОЖНо странт}, приложения . :которые . наряду (' атрибутами библиотек
базовых классов .NEт. будут отображать DОJ1ЬЗQвa:rеJIЬeJЩe атрибуты, Такой' пОДХОД
поаволяет создавать' наборы ulUIЮчевых слов", llOlLRTВЫX толыщзаqa;ВНОl\ty ~оже~
ству компоновочных блоков.

Применение встроенных атрИбутов С#


ltaБ ynOМirna1IОСЪ выше, библиотеlta. баз01lliIX массов .NEТ npедлarает цe.rIЫЙ ряд
атрибyroв.иа pa~HЫX пространств имен. В табл. 12.3 ПРI-ШОДИ'I'СЯ 1Щро:пщй список
HeKOТo)}bl:X из ТаЕ.ИХ атрибугов (и, J{онечно же. даленР H~ всех).
что.Бы привести npимер прим:екеН1rя атрибуто:з в СИ • .преДПОложим, Ч'110 :нзм
нужно построить kЛaсс Motorcycle (МDТОЦИR.д). доnyCl«OOЩИЙ сериализацию в дво­
лчиом -фDрмате. для 3TOl'O мы долж1iы npO'cтo добaвnn. атрибуг [Seriali zablel в
Dпределение класса. Если при Эl'OМ какое-то поле при сеpиaзnIЗaЦИИ сахранятьс.и
не ДОЛЖНО. то к нему можн:о npимени'l'Ь атрибут jNonSerial±zedJ.
1/ ~Оl1' масС И~ coxp~ ка ДК~~.
[Seri&l.i.zable]
public clas5 Motorcycle
(
..
518 Часть 111. Программирование компоновочных блоков .NET

/I Во это оме cOXPaJUeIoCJI .е дo.mкнo.


[NonSeri,al i,zed]
float weightOfCurrentPassengers;
/I CneдYDЦКe оonк COXP&НJIII'1'CК.
Ьооl hasRadioSystem;
Ьооl hasHeadSet;
Ьооl hasSissyBar;

Таблица 12.3. Малая часть встроенных атрибутов

Аtpибyr Описание

[CLSCompliant] Требует от злемента строгого соответствия правилам CLS (Соттоп


L.anguage Specification - общеязыковые спецификации). Напомним,
что соотввтствующие СLS-спецификациям типы гарантированно могут
использоваться во всех языках программирования .NEТ
[Dlllmport] Позволяет программному коду .NEТ вызывать библиотвки программно­
го кода С· или С++ (которые нв являются управляемыми), включая API
(Application Programmlng Interface - программный интерфейс приложе­
ния) операционной системы. Заметьте, что [DI1Import] не исполь­
зуется при взаимодействии с программным обеспечением СОМ
[Obsolete] Обозначает устаревший тип или член. При попытКе использовать такой
элемент программист получит предупреждение компилятора с соот­
ветствующим описанием ошибки
[Serializable] Обозначает возможность сериализации класса или структуры
[NonSer ialized] Указывает, что данное поле класса или структуры не должно сохра­
няться в процессе серивлизации

[WebMethod] Обозначает доступность метода для вызова через запросы нттр и


требует от среды CLR сохранения возвращавмого значения метода в
формате XML (подробности можно найти в главе 25)

заМ.чаН .... Указанный атрибут применяеТСА только к элементу, непосредственно следующему


за атрибутом . Например , единственным не сохраняемым полем класса Motorcycle будет
weightOfCurrentPassengers. Остальные поля при сериализации сохраняются, посколь­
ку весь класс аннотирован атрибутом [serializable].

Пока что не беспокойтесь о сути самого процесса сериализации объекта (под­


роБНости этого процесса будут рассмотрены в главе 17). Обратите внимание только
на то. что при использовэдии атрибута его имя должно Зaк1IЮчаться в квадратные
скобки.
После компиляции этого класса можете проверить его метаданные с помоIЦЬЮ
ildasm.exe. Соответствующие атрибуты будут обозначены метками serializable
и notserialized (рис. 12.6).
как вы можете догадаться сами, один элемент может иметь много атрибутов.
Предположим, что у нас есть тип класса С# (HorseAndBuggy), обозначенный как
serializable, но теперь он считается устаревшим.
1 Гnавг12. От{)браJКение тиn~в, ДИН1ЩИ'f&q1(ОеСВЯ~Ы8ание... 51 9

• MAbllFFSТ

" '. A1'A:rlbUtt,tIQrUb(lor~


lIi • AЦ1~.!ry,P!~8S
., • дti>ibI)~c.t.t1rary.Мotarcyd&

" НeedseI : prlv..t.. bOOI


,j1 " ...
V t-.osRЮIoЭystom:):JI'IWJt!j bOOI
... ~ ,. priV<ltвЬoQ
.., ..eiQhtQtСl)'теПР~5" ~..re ~ ~
• .dor : voI(J()

Рис. 12.6. Отображение атрибутов в ОК}lе ildаЗrn.ех:е

Чтобы примеЮNЬ множеСТIЩ атрибутов к ОДВQМ)lэлемен':.\У, исцщrьз)'ЙТе список


эвач.енйЙ. разделеШlЫX з1UlЯтымu.

jSe:I;"iali".able,
Obsolete ("Клас('; устарел, ИG riО.льэуй"те другой траяспорт! ") j
рuЬНс сlаЗ5 HQrsel\n,(iВuggy
I
11 ...

13 каЧес1'Ве альтернативы.• чтобщ ДРЦМf;:НИТЪ несltОЛЪRО aтp1iбyтов к одвому эле-


.мешу. МОЖНQ ПРОСтО ухазать их пр цорядку' (результат будет -reм же).

tSerializable].
.l Obsole'te ! "YJ!6CC ус'Гдре,р I ;исп~дь:s!ТЙте другоЙт.раисГlОРТ!") ]
puD.J.ic class Hor sеАпdВuggy
{
11 . . .

Параhfеrры конструктора ДmI атрибутов


м:ы ВИДИМ, что атриб.VТ [.Qbsolete] моЖет приьrимa-rъ.неtrro похожее на лараметр
.к<Ш~ра. Если вы посмотрите наформалыroе OIф(>;Целеии.е атрибут [Obsolet.e]
ВОlЩе QnpедeJli:НИЙ IIpограммноro :кода Visшй StudIo 2005. то увцците. что данный
"ЮJасс д~йствmельно предлarает конструктор. шшуч:ающий System.S,t ring.

pl1blic sealed €lass ОЬsоlе1еАtt>:iЬцtе : Systel'!\.At;tribute


I
public 1.001 lsEr:ror { get; }
public string MeSSdg", t get;
pu.ыlcc Qbs.oleteJ\ttribute (вtiriпg rre:s.sage, .Ьо.оl errDr);
ptlЬH.c OЬsoleteAttriЬute (string m.essage) :
pUblic O:bsQle'teoAHribute (};
--
520 Часть 111. Программирование компоновочных блоков .NET

Когда вы указываете параметры конструктора для атрибута, атрибут не раз­


мещается в памяти до тех пор, пока эти параметры не отобразятся друтим типом
или внешним программным средством. Строки, определенные на уровне атрибута,
просто запоминаются в компоновочном блоке, нак часть метаданных.

Атрибут Obsolete в действии


Теперь, когда класс HorseAndBuggy обоэначен как устаревший, при размеще­
ния экземпляра этого типа вы должны увидеть соответствующую CTpOr<y в сообще­
I-ШИ, появившемся В окне со списком ошибок Visual Studio 2005 (рис. 12.7).

2 'AttrI>u~...y.НcrRAncl!uggy· is ~\J!, This


do!I.. iS ~\J!, _ III'1Otne- _~"

Рис. 12.7. Атрибуты в действии

В данном случае "другим фрагментом программного обеспечения", отображаю­


щим атрибут [ObsoleteJ, является компилятор С#.

Сокращенное представление атрибутов в С#


При внимательном изучении материала этой главы вы могли заметить, что
фантическим именем класса атрибута [Obsolete] является не Obsolete, а
ObsoleteAttribute. По соглашению для имен все атрибуты .NEТ (и пользователь­
ские атрибуты в том числе) должны в конце имени получить суффикс Attribute.
Однако, чтобы упростить процедуру применения атрибутов, в яэыке С# не требу­
ется, чтобы вы обязательно добавляли зтот суффикс. Поэтому следующий вариант
определения типа HorseAndBuggy будет идентичен предыдущему (при этом только
потребуется ввести немного больше символов).

[SerializableAttribute]
[ObsoleteAttribute ("кдасс устарел, используйте другой транспорт!") ]
public class HorseAndВuggy
{
/ / ...

Это упрощение предлагается самим языком С#, и следует подчеркнуть, ЧТО эту
особенность поддерживают не все языки .NEТ. Так или иначе, к этому моменту на­
шего обсуждения вы должны понимать следующие основные особенности, касаю­
щиеся атрибутов .NEТ.

• Атрибуты являются классами, производными от System.Attribute.


• Информация атрибутов добавляется в метаданные.

• Атрибуты будут бесполезны до тех пор, пока другой агент не отобразит их.

• Атрибуты в С# при меняются с использованием квадратных скобок.


1 Гла~а 12. От~Р8женl't{! Т\-1ПО8, A~aMI4'tecj(;oe СВАз~ва~ие". 521
Теперь мы рассмотJЩМ ТО, как можно СТрОИТЬ свои собственные ПcJльзова'I'е.jlЪ·
с:кие атрибуты и ПQ.iIЪЭQJщтельcЮlе npогра:ммы. отображающие вctpоениые мета·
д.анные·.

С,оздание пользовательских атрибутов


ПервЫМ шагом npацесса. n:остроeнuя ПОJ)Ъ3оватеЛрСIФГIi), атри(lyта ЯЦJЩе;гс~ соз­
дание НОВОГОЮIQсса. npоизво.zщого от Systsm.Attril>ute. В npодолже.ние ~TOМU­
билюIои темы. используемой взтой .книrе, мы создадим новую библ;ио~~ в.nac­
сОВ С# с именем: AttributedCarLibrat-у, соответс.'!rDYIQЩИЙ RОМIJЩiОВОЧНЫЙ БЛок
определит группу траисnoртньtx средств{о:предел~Юf1l иеК(i)ТОРЫХ :из БЩ. мы УЖе
увидели выше). и при их ОIJИсаНйli будет ИСЦ.ОЛЬ30DЩJ: ПОДЬ80ватeJIb~ !oiТpИБJт
VеhiсlеDеsсriрtiолАttriЬutе.

1/ Дщц.ЗО:8а:oмm.CJЩЯ а'l1Щб~.
public s ·e a.led. сlаэs \iet',icleDescriptionAittribute S"Y1;;'Cem.~·f...trib1Jte
{
p-r i Уа ·!:,.е ;>tri ng msgDa'1:a;
publi.c Vebi.cl€Desi:riptionAttribute ('str lng оезсТ iptlon')
j msgData = dе.эсriрtion;}
public ve1'tiCleDsscriptionAttribute CJ { }'
publ.ic. string Des criptior!
r
qet{ retur.n msg9a·!:.a; 1
set { msgDeta = value·; }-
)

RaR· :видите. VеЫс lеDеsсt:iрtiО!iAtt.riьutеПО)JДеРЖFIВает.пр~ва:гнуювнутрен­


ВЮJQ строку (mBglJata), значение :которой можно установнхъ с ПQМощъю ПОJJЬ30В8-
телъс:кого конструктора. а изменв:ть- с помощью свойства типа (Descript;ion).
Кроме тoro, что Э'Г{}Т класс является ПРОИЗВОДНЫМ 0"1' s'ystem.Attribu-tе. ~ oцp~
:деление БИЧем особенным больше не отличается.

3амечаl:lИе. С точки зрения беЗl!Jn8СНОСТИ рекоменАУется. 4Тобы !3се поль:~оватеЛI)СКlllе аТРИQУТЫ


.NEТ создзвались, ltaк ИЭО:l:lированные lU1aoc~.

Применен~е пользовательских атрибутов


Посл~ прлуч~ния VehicleDescriptionAtt:tiЬute из System.A:ttribut.e вы М:о­
же-;ге снаб~атъ с;ООи тpaIitпoР'I'ИЫе C~ДCTвa тaкm.пr анно'Х'зциами.. какими поже­
лает.е·.

11 На~Rа.еиие опиСIUIИR: С D())IOЩI.J:I 'ИNeJlО8аиио~ саОЙQ'1'JI& I •

IBeria1izable,
Veh!сl_D8.cr.iptlаnЩеsсriрti.оn = "Мой сияюЩий Харлей"))
522 Часть 111. Программирование компоновочных блоков .NEТ

public class Motorcycle


{
// '"

[SerializableAttribute]
[ObsoleteAt tribute (" Класс устарел, используйте другой транспорт!") ,
VehicleDescription ("Старая серая кляча, она уже совсем не та ... ") ]
public class HorseAndBuggy
{
// ...

[VehicleDescription ("Большое, тяжелое, но высокотехнологичное ав':Го")]


public class Winnebago
(
// ...

Обратите внимание на то. что описани.е класса Motorcycle здесь указано с


помощью нового элемента синтаксиса. называемого именованны.М своЙсmвом.
В конструкторе первого атрибута [VehicleDescription] соответствующее значе­
ние System.String устанавливается с помощью пары Uимя-значение". при ото­
бражении этого атрибута внешним агентом соответствующее значение передается
свойству Description (синтаксис именованного свойства здесь коррентен только в
том случае. когда атрибут предлагает перезаписываемое свойство .NEТ). В проти­
IЮПОЛОЖНОСТЬ этому тицы HorseAndBuggy и Winnebago не используют синтаксис
именованного свойства. а просто передают строковые данные в пользовательC}tий
конструктор.

После компиляции компоновочного блока AttributedCarLibrary можно ис­


ПОЛЬЗ0вать i 1 dasm. е хе. чтобы увидеть метаданные с описанием добавленного
типа. Так. на рис. 12.8 показано встроенное описание ТИпа Winnebago.

) ~ ( 11 •• 21 ~1 2. 16 65 12 19 21 6С 6F 6Е 61 2С 21 11 •• 'А uery 10пg,


13 6& 6F 11 21 62 15 1~ 21 66 65 61 7~ 15 72 65 11 s10w but Feature
21 12 69 63 68 21 61 15 1_ 6F .1 •• ) 11 rich autn ••

Рис. 12,8. Встроенные данные описания транспортного средства

Ограничение использования атрибута


По умолчанию ПОЛЬЗ0вательские атрибуты могут применяться к любой части
программного кода (К методам. классам. свойствам и т.д.). Поэтому. если только
зто имеет смысл. можно ИСП0ЛЬ30ватъ VehiCleDescription для определения (сре­
ди прочего) метОДОВ. свойств или полей.
Глава 12, Отображенме 'fИПО8, днttSМНlfеЩ(Qе связывание... 523
(Vе.hiСlеDеsс;riрti tю ("Е10льшре, тяжелое, но высокQте:кн.ологичиое авто")' ]
ptJblic class Win-nebago
j
[VеhiсJ,.еDеsсr iр:t.iЩ1 ("Мой МОЩНЫЙ СD-пле*ер")]
риЬН;: v o id РlауМu.эiс фооl Оп)
j

13 иеitотор.ых сдучаях. это 'ОRазываетс.я иМенно тем. что nYЖ}IO. Но В дрyrщ


случаях, БЫJ3ает ~HO создать пользО'вательсюm атрибy-L КОТОРЫЙ JtOJDICeн пр~­
меняться только к оnpеделеFIНЫМ алемент.ам npограммного нода . Если вы хьтите
оrpaничить ROнтенс';!' ЦРlIмененил иольаователъСIШГО атрибута. ТО' при onpeДeJIении
ЩlльзО'ватещ.скоro а,тр_ибута нужно np:имeнип. атрибут IР.ttriЬutеОзаgе). Атрибут
L~tt r ibu tetJs ag е] по~воJiJIет указать .любую .RОМбинацию· значений (СВRЗанных:
операцией оа) из щ~речRя Attribu,t €'Iarge.t s.
11 ЭwО'1' 'П8р&Чеиъ. задает 210!UlOМИlilе цueвыe 8Jl8,чeIIItJI ДпJII &~БV'Ж'iI.
public еnшn AttrlЬ·ute1'argets
{
Ыl, As sembly , _c iass, СОП$ tructor.
Pe1egi2te, Enum, Еvепt, Field,
Inte r face, Мethod, .M adul.e , Parame1;er.
Property, Rеtщ:nVаluе, StrUli:t

Кроме того , [JI,C'!: \:-riыJеtJsаgе)) llОЗВол.я:ет ()IЩИоналы~о ycтaнoВ-U'Г-b именОJ)8.Н­


ное 'СВОЙCТJJО (AllowMultiple), IЮТОрое указывае1: может ли атр:йбут примeюrrьcя
R одному И тому же элементу многократно. TO'-nIО ТаЕ же с помощью именрВаннoro
ОВ!1Йства Inherited а1-рибут [Attribl:1teOs.age 1 позволяет~, должен ли СО'Э~
даваемый атр.JФут наследоваться праИЗ1ЮДНbIМИ мае<:ами.
Чтоб~ аmибyr; [Vеh'iС1еDеS·сriрti.опl могnpимеНЯ1ЪСя к классу}ЩИ структуре
ТOJIЫtO 9ДИ;Н раз [11 rnютветствующее значение не нacJIед()8ЙЛО'СЬ IJpОИЗВОДНЫМИ ти­
пами), ~ОНЩО ~змеmcrъ еnpeдe:neние Vehicl.eDesoxiptlonAttribute тaR.
// на Э'1'О~ раз AJlII &IUIотацюr il8JIIin'o ао.т.ао_а'1'8JПoС!КО%'О &'1'риб1l"&
J/ _ I\Ictt~~YeN а!l1Щб)",:
A1:trihu1:et1aaqe.
[лttriЬutеIJsаgе (AttriDvit~Targets . Сlа.э s I A·tt;tibL1teTargets. Str!.1ct.
AlloWМul tiple = faisl;'J, InherH,ed = false)' 1
publi с cla:s.s V·eh.icleDes·criptionAt'tr i Ьute : S-y's'tem. At tribute
I

Теперь если ~азрабо'l'ЧИК.IЮпытаетс.я npимеНйТЬ атрйбут [Veh±cleDescriptionJ


R Ч,I;))1)"-ЛJ1бо. щ>оме класса ИJIИ структуры. будет сгенерированО' сообщение об
ошибке JЮмп:ишщии.

Соа.Т.~ше~ прив~чкой ДРllJКIiJ) стап, явное УlCЗЗВние флагов применеNИЯ ДЛ!j люБОГQ с:оадавае­
м.ого вами пользователЬского аТРlilбута. поскольку не все языки лporР<I.Ммирования , NEТ при­
ветЩI;IYЮТ использование атр.iIIбутов, не имеющих квалификационных указанииl
-
524 Часть 111. Программирование компоновочных блоков .NET

Атрибуты уровня компоновочного


блока (и уровня модуля)
Можно также задать применение атрибутов ко всем типам в рамках данного
модуля или всех модулей в рамках данного компоновочного блока, если, соответ­
ственно, использовать признаки [module:] или [аББеmЫу:]. Предположим, что
нам нужно гарантировать, чтобы каждый открытый тип, определенный в нашем
компоновочном блоке, был СLS-допустимым. Для этого В любой из файлов исхо­
дного кода С# нужно добавить следующую строку (заметьте, что атрибуты уровня
компоновочного блока должны быть указаны за пределами контекста определения
пространства имен).

11 Требование CLS-совмес'l'ИИОСТИ д;lIJI всех OTKpW'1'ЫX типов


11 в ~aКROK компоновочном блоке.
[assembly:System.CLSCompliantAttribute(true)J

Если теперь добавить фрагмент программного кода. который выходит за преде-


лы специфmтции СLБ (например, элемент данных без ЗНaI<а)

11 Тип»
ulonq ве СQглаСуа)ТСJl с CLS.
public class Winnebago
I
public ulonq notCompliant;

то будет сгенерирована ошибка КОМПИЛЯЦИИ.

Файл AssembIylnfo.cs в Visual Studio 2005


По умолчанию Visual Studio 2005 генерирует файл с именем Assemblylnfo.cs
(рис. 12.9).

l'ii
s
$oIu1ion 'A~"'y' (1 project)
i!I AttJtЬutedC...ut..."..,

Iij
'iJI F'rope1les
~E_ ;
:iiI R~..,.,.ces.гes><
i.:!I ReМOI1Ce<
'i1 Classl.cs

Рис. 12.9. Файл Assemblylnfo.c$

Этот файл является удобным местом для хранения атрибутов, которые должны
применяться на уровне .компоновочноrо блока. В табл. 12.4 приводится список не­
которых атрибутов уровня компоновочного блока, о которых вам следует знать.

Исходный код. Проект AttributedCarUbrary размещен в подкаталоге, соответствующем главе 12.


Гла,а 12. Отображение ТИПОВ, динами~еское СВfjЭЫВВflие... 525
Таблица 12.4. Некоторые атрибуты уровня компоновочного БJ1(ЖЭ

Атрибут Описание

AssemblyitompanyAttribute Содержит оБЩУIQ ИI-lФОРМВЦ\'lЮ О t<омпаНJ'l'И


AssemblyCopyricyhtAttri.bute СОД~Р*ИТ информацию об ав.торских правах J1B flPOДYIO
МИ IФМПОН'овочныlil блОК

АзsеmblуСulturеА.ttriЬutе дает l'IJ1фОрмвцию о· rтapaMeтPax S10lCализацl'lИ иЛИ язы­


ках, по.одерживаемы)( КОМnОliОВОЧНЫМ БЛОI«)М '

AssemblyDescriptionAttribl.lt..e· Содержщ ОГlисание продухта иmI модулей, из koYOlJblX


СОС'Томт компоновочыый блок
A.3se.r nblyKeYFileAttribute Указывает имя ' фе"~11а, содержащего парУ JЧ]ючвй, иanonь­
:эуемых ДПЯ' создания по.а.ПИОИ!(ОМПОНО80ЧНОГО·,бnо~

Asaem~lyOperatingSystemAttriЬ1rt:e Обеспечивает информацию о Там,. на f1Фддержку каКОЙ


операЦИQН~ОЙ системы рассчитан КОМПОНОIIОЧf!Ый (\1101<
АsэеmЫуРrосеssоrАttriЬutе Обеспечивает инфорцациlO о TQM, на nqДD.ep*кy IФ.kОГО
процессора раССЧ.ИТЭfj КОМПОНО80ЧНk!lиблок

MsemblyProdl.lctAttr:Lhute Обеспечивает информацию о npQQ.YKTe


1\s$emblyТr,adsmarxAttribute ОбвdAеЧИll8етиkформацию а Topr080~ марке
AasemblyVer..sionAt t'! ibute Указывает информацию веРСИ)oj I(()МПОНОаочнorо блока. 8
форМ1П8 <главныiЦiОnОЛНVlreЛЬНЫЙ.l(Oмnоновкв~saрнанr>

Отображение атриБУТО8 при


статическом связывании
l\aR уже ynрмщrnлооь в ЭТОЙ главе. атрибуты БYfIYТ беcnOJXeЗШ4 ДО тех пор, п.mш.
неIl:О'J'ОРЫЙ фрuмеНlI' npог·рaммtroга обесцечеЮIR не .вЬЩОЛИИТ их отобр·ажение.
После ВЫЯВДlЩИнатрибута соответствующий фрагмент прorра,мм:ного Qбеоneч-eюi.Я
м:оже'J' выбрать. прдХодящее действие. k.a1<и само ПРИЛQжение. <rrOТ "фр;rгмeнт npо­
rpaммнoгo обе<:печенин· МО3ШТ ДЛИ ~s:anения : по.дьэователъскоro ~бута ИCПDлъ­
зоватъста.ТIJqеСfGО.е или динамическое связывliие,' дли ctb-тичеЩtого связывания
:xpet;iyeтcн, -.uобы прильжеiIШНt1lиент g момен1У I-Cомnи.тrлци;и уже и.м~о определе­
ние 'СООТ1lетс~ующего атрибута fв нашем cщytiаеэТО атрибут VеhiсlеDe~'Сilрtlоп­
Attripute). КомndиовочКЫй блок AttributedC;;irLibr,ary ОП.l?~еmreТПQЛЪз0ва­
те.оJЬCRИЙ a~ КaI< ~ый ЮIасс. поэтому ста'ГИ'iеокое свяэрцuцmе в данном
с:луча:е будет вa.илyчumм выбором.
для i;I1lIЦOСТрации проЦесса от()браженин ПОJlЬЗOвательскщ ат~бутов СЩlдaiiтe
новое консольное приложение С# с именем VehicleD~sCoI;iptionAttrib.uteReader.
Эатем .установите ввем ссылку на компоновочный блок AttributedCarLibrary.
Нановед, noместите в исходиый файл *. сз следую11IШi npoгp~ код.
11 ~epb." no;n••o ••~eJOOC A~Y'!'O. прх ~~.CXO» с..8Dаани.
us'ing Systemi
uэing Attribut~dCaxLibtary;

рсиЫlс сlа:sз Progt<it!\


{
-
526 Часть 111. Программирование компоновочных блоков .NET

static void Main(string[] args)


(
11 Пo.n:yч8НИ8 Туре .цJIJI ПР8ДС'l'u.пеНИII Winnebaqo.
Туре t = typeof(Wlnnebago);
11 По.пучени8 а'1!риБУ'1'О. 1finnebaqo.
object[J customAtts = t.GetCustomAttri butes(false);
11 Печа'РЬ описании.
Console.WriteLine("*** Значение VehicleDe s criptio nAttrlbute *** \ п");
foreach(VehlcleDescriptionAttribute v in customAtts)
Сопsоlе . WritеLiпе("-> [О} \ п", v . Description) ;
Console.ReadLine();

как следует из самого названия, метод Type.GetCustomAttributes () возвраща­


ет массив объектов. представляющих все атрибуты. примененные тому к члену. ко­
торый представлен с помощью Туре (логический параметр этого метода указывает.
следует ли расширить поиск на всю цепочку наследования). После получения спи­
ска атрибутов выполняется цикл по всем классам VehicleDescriptionAttribute
с выводом на печать значения. полученного свойством Description.

ИСХОДНЫЙ КОД. Проект VehicleDescriptionAttributeReader размещен в подкаталоге, соответствую­


щем главе 12.

Отображение атрибутов при


динамическом связывании

в предыдущем примере ИСПОЛЬЗ0Валось статичеСl<ое связывание и печатались


данные описания для типаWi nnebago. это было возможно благодаря тому. что тип
класса VehicleDescriptionAttribute был определен. как открытый член компо­
новочного блока AttributedCarLibrary. Но для отображения атрибутов можно
также использовать динамическую загрузку и динамическое связывание.

Создайте новый консольный проект (VehicleDescriptionAttributeReaderLat


eBinding) и скопируйте AttributedCarLibrar y .dll в каталог \Bin\Debug этого
проекта . Затем обновите метод Main () так. как предлагается ниже.

using Sуstеm.Rеflесtiол;

namespace Ve.h icleDescriptionAttributeReaderLateBinding


{
class Program
{
static void Main (string [] args)
(
Console.WriteLin e ("*** Описания TpaH~n opTHЫX средств ***\п");

11 ЗаrpУЗltа .пОlta.JUoВОЙ .копии


AttributedCarLibrary.
Assembly asm "" Assembly. Load (" AttributedCarLibrary") ;
Глава 12. Отображ.еН"6 тмnоа. Дl1i'1liмиче.СКОEl связывание... 527
JI Пo.uyqeим. ивфорu~ i1!Иn. ~ VlihiclaDeacriptionAttxibute.
Туре vehi cleDesc ;
аат. GetTyPe (":At triЬutеdёаr:LiЬrаrу. Veh.i (3:leDe·scr.ip~iOllAtt:ribute") ;

11 П«мучевие JCИФори~ ~ ЯШI а.ОЙс_а De_cription.


РrQР~ItуI>Л'fQ
proplDesc =
vehicleDesc. GetProperty (11 oesc:r.iption") ;
IJ Пcmyqеии. &.ce~ 11'МDO& ,цaJЩоl'О хoxnовоаочиоI'O БDока.
Туре [] types = а'зm, Get Туре s () ;

11 Пony1i8RJ1е Vehic1eDe.cdpUonAttribute ~.. Jt8X,Ц01'O иш ••


foreach (Туре t ~n types)
-(
object[] 'objs =t.GetCt15tomAttributes ·( vebicleDese. fаlзе);

1/ l1!1'ераЦIiIИ ПО Veh.icleDesc,riptionAttribute и печ&-.n


11 ОElИсавий: с ДИВII_есхих C8J1ln18aкxeM.
foreach (ohject Q ih obj's)
!
C·onso-lе.WriteLinе("-> (О!; {l}\n",
t. Name, prjJpDes.c., Gе·tVаlце (О, , nиll»;

, }

CO.nsole . ReadLine () :
·1

i
Если вы вlщматеды11о ~адизировIPIИ все примеры этой главы, Т() лисnщг этого
метода Main () долж~}]бъжn. jJ,ЛJllJас (более или менее) поНЯ'I1lhlМ. ЕДИНСТ8еюlblМ
заслуживающим вuиман~ Моментом ~дeeь лвлвется использование метода

Pxopertylnfo.GetValue () длв дocтyna 1.< свойсТву, на рис. 12.10 ПОRaЗ8Н CQ()'I'Вe'Г­


ствуюnt;ий ВЫВОД.

Исходнwi К<Щ. Проект VehiclвDescnptionAttr1buteFJeadel'lateBinding размещен- а поДкаталоге. ОООТ­


ввтcrВ'ующем тц.ве 12,

Рис. 12.10. ОТ·ображеНt1е а-трибутов nplo1 динамическОм 'С8ЯзывеfiИи


.....

528 Часть 111. Программирование компоновочных блоков .NET

Перспективыотображения, статического
и динамического связывания
и пользовательских атрибутов
Даже после множества примеров применения соответствующих подходов вам
может быть не ясно. когда же следует использовать отображение. динамическую
загрузку. динамическое связывание и пользовательские атрибуты в программах.
Строго говоря. эти вопросы (которые увлекательны сами цо себе) можно ОТНеСТи.
скорее. к теоретической стороне программирования (что можно считать как до­
стоинством. так и недостатком. в зависимости от точки зрения). Чтобы спроеци­
ровать эти вопросы на реальность. нам нужен реальный пример. Представьте себе
на минуту. что вы работаете в команде программистов. созданной Д1Iя разработки
приложения. удовлетворяющего следующему требованию:

• продукт должен допускать расширение путем подключения дополнительных

программных средств. разработанных сторонними производителями.

Но что именно подразумевается под требованием допускать расширение?


Рассмотрим Visual Studl0 2005. При разработке этого приложения в нем были
предусмотрены различные "гнезда" для подилючения к IDE пользовательских мо­
дулей других производителей программного обеспечения. Ясно. что команда раз­
работчиков Уlзиal Studl0 2005 не имела при этом возможности установить ссылки
на внешние компоновочные блоки .NEТ, которые зта команда не разрабатывала
(так что о статическом связывании не могло быть и речи) . Поэтому закономерен
вопрос: как именно это приложение смогло предложить необходимые гнезда под­
мюченил?

• Во-первых. расширяемое приложение должно предлагать некоторый входной


механизм, который мог бы позволить пользователю указать модуль для под­
J(JIlOчения (это может быть. например. диалоговое окно или опция командной
строки). При этом требуется дина.м.uчeская загрузка.

• Во-вторых. расширяемое приложение должно выяснить, обладает ли модуль


подходящими функциональными возможностями (например. набором необ­
ходимых интерфейсов). чтобы этот модуль можно было добавить в окруже­
ние. При этом требуется оmoбражени.е.

• Наконец. расШИряемое приложение должно получить ссылку на требуемую


инфраструк'IYPУ (например. типы интерфейса) и вызвать члены. необходи­
мые для запуска соответствующих функций. при этом часто требуется дина­
мическое связывание.

Простыми словами. если расширяемое приложение было эапрограммировано с


yt-Iетом возможности запроса конкретных интерфейсов. оно сможет в среде выпол­
нения проверить. активизирован ли соответствующий тип. Если такая проверка
завершится успешно, запрошенный тип сможет поддерживать дополнительные
интерфейсы, обеспечивающие полиморфную CTPYI<ТYPY функциональных возмож­
ностей. Именно этот подход был применен командой Visual Studl0 2005, и этот под­
ход оказывается не столь сложным для использования. кан вы можете ожидать.

1
I Глава 12. Ото.браженке типов, Дl1наМИllеС'кое СВЯЗ'ЫIISНИ8... 529
!
Создание расширяемого приnожения
в следуюЩ}pt рЩJД~Дах мы с вами npоанализируем пример. ПЛJIЮС"l])JipУЮЩИЙ
процесс создания раcmиpЯе~РГО п:риложени.s WindowS Fbrms. которое можао бу­
дет расширять за счет фувкц~оННЛЪНbIJi. возможностей внешних комповово"IНblX
блОJ«>JJ. сам процесс :црограымирОIJiUfИЯ цpи.noжеНИ:Й WIndows Fonns мы здесь об­
суждать не будем (этому будут IIрсвящены тлавш t9, 20 и 21). Есms вьr не имеете
опьца создания ПРИJJOжеЩ[Й W1I1dov.'S FoI'IDS. просто 1WЗЬ'МИт.е предлагаемый tmже
npоrраммный код в lсачеетве обраэцаи следУЙТе соотвегствующим ШIСТРУКЦИИМ
(или постройте COO"J)BeTC'U5Y19m;yIO ~онсолъную влътернаrивy).
Мы преДПOlIагае~, ЧТО НЩnе расширяемое npИЛQЖ,ение До.lIЖНО иметь С'ле,цую­
щце KoмIIoнQвo"!flые блов:и.

• Соmmопsлs.рраые'rуре, S. dll, l{омцщroвочныИ бло~ 'содерЖащиЙ onpeделения


тЩJОВ, которые дСЩЖНЬ1 реализовыватьЩl ~ ПОДЮllOчаемым расшире­

нием. поскольку Jla них будет сСЬшаТЬ!:!~ раСШиряемое IIJ1ИЛожение Windows


Fbrms.
• CSharpSnapIn.dll. Расширение.сомаююе на л3ыкe С# и вспальзующеё
ТPIПЬJ СоmmОnSпарраblеТуреэ.dll.

• VbNetSnapTn.dll. Расширение. созда:нное на Qэыке Vlsual Bas1c .NEГ Jf гс­


пользующее типы CommonSnappableType5.dll.
• MyP.lug<1JablePl.pp.-ехе. ПрИложение WIndows Forшз, фyJПщиОНa.JJЬНые воамож­
HOC11l gOTOpOro МОЖНО распmрить с помощью подмючаемых модулей. Это
приложенне будет ИCnOJlЪ3ОВатъ динамическую загруз&}'. отображение и ди­
вамическое сВЯЗЫВание дlIЯ дивамичeclCOГО выя:нeиия' фу81ЩИOIf8ЛЬЩ>IX ВQЗ­
можиocre5t кOМnOНОВОЧIIЫX БJJохов. о которых ранее не l.5ьшo НИЧеГО известно.

Создание CommonSnappabIeТypes.dll
'('Щр~OJ) нamейзадачей ИВJIяется создание комnоиовочнorо MOIta, содержащего
типы. :которые должен ИСDОJIВ8ОВМ'Ь ЕSЖЦЫЙ подкmoчаемый MOд.YJIЬ, чтобы о(\еспе­
'чи'гь B03:МO~CТЪ его подкшочении.1t нашему прИЛdжению WJndowsFbrms. Проект
биб;цютеrщ: массов CommonSflappableT~'peB определяет Сjlедующие два типа.

r.amespace СОПТЛIопsпа:рраЫеТуреs
J
puыic interfa'ce IАррF1шсtiопаlitу
{
void Dolt ()1

p~ttributeOsage (Attrib\lt.eTa:rgets. ctla'Ss) ]


puЬ.lic sealeq class Со.."1lрапуIлfоАt ,tiibutе Sys·tem. Attribute
{
private sr..riлq companyName:
pr1vate string .сощрanуUzl;
pub1ic Co1tlpanylnfoAtt:t'ibute () {}
pub11a $tring ~aтe
!
-
530 Часть 111, Программирование компоновочных блоков ,NET

get return СОПLрапуNаrnеi )


set cornpanyNarne = value; }

public string Url


(
get return cornpanyUrl;
set companyUrl = value; )

Интерфейс IAppFunctionality обеспечивает полиморфные возможности для


всех ПОДJUПOчаемых модулей, которые может принять наше расширяемое прило­
жение Windows Forms. Наш пример является ИСКlIючительно иллюстративным,
поэтому здесь интерфейс предлarает единственный метод, Dolt () . в реальности
это может быть интерфейс (или набор интерфейсов), позволяющиД ПОДКIIЮчаемо­
му объеR'IY сгенерировать программный код сценария , поместить nиктограмму в
о'Кно инструментов или интегрироваться в главное меню npиложения.

тип CompanylnfoAt t:cibu te является пользовательским атрибутом, который бу­


дет применяться к любому типу класса, размещаемому в контеЙИере. Исходя из
определения этого класса, можно утверждать, что (Companylnfo] позволяет раз­
работчику расширения сообщить информацию о происхождении ПОдКlIючаемого
компонента.

Создание подключаемого компонента в С#


Теперь нужно создать ТИП, реализующий интерфейс IAppFunctionality.
Снова, чтобы сосредоточиться на npоцессе создания расширяемого приложения.
здесь предполагается создание самого простого типа. Мы построим библиотеку
программного кода С# с именем CSharpSnapln, которая определит тип класса с
именем CSharpModule, Поскольку этот класс должен использовать типы, опреде­
ленные в CommonSnappableTypes, нам придется установить ссылку на соответ­
ствующий двоичный файл (а также на System.Windows.Forms.dll, чтобы выво­
дить необходимые сообщения). С учетом сказанного предлагается использовать
следУЮЩИЙ прогрaммный код.

usi ng System;
u sing CommonSnappableTypes ;
using System.Windows.Forms;
n ame space CShar'pSnapIn
(
[CompanyInfo (Name = .. Intertech Training",
Orl = .. www.intertechtraining.com .. ) ]
public class TheCsharpModule : IAppFunctionality
(
void IAppFun ctionality.Do It()
{
MessageBox. Show ("Вы только ЧТО п о дключили блок ct! .. ) :
ГJlaв!! 12. Отобр;жеНJ.!е типов, ДИН!JМИ4~СlCое связываНие.,. 531
Обратит·е В.}.Jимание Щ!. TQ. ч'то· :здесь используется ЯВная реализация интер­
фейса IAp:pFul1ctionality. Это не Обнза.тельно,:но иден в ТОМ. ЧТО еДИНC'rВенной.
частью системы, КDтор",й ПОЩlДобится RеIlOсредотвенное Взаимодействие с этим
типом интерфейса. Я:В,JUlе1'СЯ н:m:ю расширяемое npиложешrе Windows.

Создание подключаемого компонента BVisual Basic . НЕТ


Теперь. чтобы ИМД1'И]>ОВЭ,11;. СТОрОНПero прошmодwrеля. преддачи:rающi;,ГО ис­
JIОЛЬЗОJЩТЬ не С#. а Visчal B~si~ .NEТ, соэдадим.в ViS'ua1]3asic .NEГ новую би6лио­
l'еку прЩ'рам;много КDд;a ('l1bNetSnapln) •. l't01'opaН будет ссъmаТЪСЯ на те же ваеumие
1tОJlшоиово'<Щые блоки. ~TO и CSharpSnapIn.. ПРОI'раммншй 1tOд (снова) будет пред.­
Намеренво очеE,lЬ прост!:J.М.

Iropo;rts SysteIII. Wiri.d ows •. Fоrmэ


Imports сDrnniQпs.nарраыlтуреss
<Соmра,пуlnfо(Namе:="'GhЧСkу' s Softwax.e". tJz:l; ="\O'ww. Ch~cJ<:ySo·ft. com i .) >
I?uhlic Cl.ass VbNetSnapln
Irnpiernents 1AppFunctio,ns'lity
t'ubJ. i~ 5иЬ ОоI t (, Irnplеmелts COnunon$nёiPpcmleTypes.
lАррFunctiошйi ty. Dolt
МезsаgеЕ)ОХ. s.how ('''Вю ТОД.ЬКО Ч'Г0 nО.lЦQ:lКJЧИJ:IИ б.nе:к VB .NET!")
};;nd SIib
E.ud Class

Ib»ори'IЪ здесь (юобенно не о чем. Однако обратите Бииr.,m:J-ше }Щ ТО. что СИНТЗК.­
сие применепии атрибугов в Vlзuа1 Basfc .NEТ прeдnолa:rает ИСIJрльзование Уl":ЛО­
вых «». а не 1:tBaдp8.'rиых ([1) сJ:tOбсж.
Соэдание расширяе·мо,ro приnожения Windows FQrms
3aюnoчитеЛЫ:ffilМ 'тагом будет соэдани€ Dрилож~я Wfndows Jfurms, ROторое
позволит пользователю выбрать подlt'Jоочаемый блов с помощью стацдартното
диалCi1'.овоrа ОКНQ ОТКРЫТИИ файла Windows. Создав Щ)вое ПРИ!}J(~.жевие Windows
R>rms (е именем MyExte.ndabl.eAppl. добавЪ1,'е ССЫЛ!\у на КОМПOl:ювочнъm блок
СОI!шюпSпарраblеТуреs .dll. но не усmt:lfЩВJ/.щюi1me ссъmщ на БИБJЩртщm про­
rpaммнoro хода СБ'hаrрSnарIп ..dll и VbNetSn:apln.dll. По~ о тОм, ЧТО це..ThЮ
ооа,цзвив этого прп.ilОЖенйЯ является ДеМонстрация динамичеС~ОI·О· связывани.яи

отображении при ВЫЯСlIении возможности ПОДК1lЮчеlЦf.R неЗЩlИеим:ър!: двоичных


б.1шкав, созданных с"l"ороJ.п:rиыи проиаводитeщIМИ.
Снова ПQдчеркнем. чro здесь не pa€CMarrp~TCJI дет<ШFl прl:щесса ПQtТрОев:и.я
приложеНИй W1ndoWВ ·F bnns. Тем не менее, предполагается. что· вы ЦQмес.'тите КОМ­
поJ.1евт MenuSt];'ip 11 окно ФОРМЫ иonpед~е с его пвмоЩbl9 меfIЮ Сервис.ROТО­
рое будет содержать ед;инcrrвешшй nyшcr Подключаемый MOAY.fIQ (рис. 12.11).
эта форма Windows должна также содержатq тип ЫstЬщ (:которому здесь I:i:a-
зваченоими lstLoadedS.naplns). и:сnользуeмый дли отображении ИМеН поДКJ'UOЧa­
емых моцулеЙ. за.-pyжaeмblX пользователем. На рис. 12.12 лоиаз;ш ОR<mчaТe.п:bliliIЙ
В1Щ графИЧeeRОГО шtгepфейса лриложения, о KOТOPOM~eT речь.
532 Часть 111. Программирование компоновочНых блоков .NET

Рис. 12.11. Исходный графический интерфейо MyExtendableApp

Рис. 12.12. Окончательный вид графического интерфейса MyExtendableApp


Программный код для обработки выбора Сервис~ Подключаемый модуль из
меню (этот программный код можно создать с помощью двойного щелчка на пун­
кте меню в окне проектирования формы). отображает диалоговое окно Открытие
файла и читает путь к выбранному файлу. Соответствующая строка пути затем
посылается вспомогательной функции LoadExternalModule () ДЛЯ обработки.
Метод возвращает false (ложь), если он не обнаруживает :класс, реализующий
IAppFunct ional i ty.
Т'лава 12. Отобрвжение, ТИПОВ, Д~lilамичес~ое связывание".. 533
p.rivate v oid snарlrtМоdulеТооlS,tri~Мen\JItеП\_Click (db-j ect.; sendeE, Eventllrgs е,)
{
1/ ПО-ll80ШIВ!I.' DQJDoSОaADl&JШ IUiIбра'lI ХОЮ1QIiО80ЧНUЙ Одох, д,п,. sа~~.lOI.
OpenFileDialog dlg = ne:w openFl1eDia'log, () ;
н Idly. SnowDiaIog () = Dia10gRes,u 1t. ОК)

i f (LCad.E1xternalModul€ (dlg. FileNam'e,) == fаlэе)


МеsзаgеВох , S,!юw ("Нет реализации IАррFunсtiола l i t.y! n) ;

lYIe'rОД LoadExt.ernalModule 'O реnЩ~QЗ1е7JWЮiЦИе задаЧЙ.


• Дlmаыиче.ски эarружает ROМЛОНОВO'ПtЫЙ блОК В лащтъ.,

• Вh'lШ:няет! содержит JIИНOМIIОНОnOЧНblЙ блоктиn. рeaщrЗУЮD.J;ИЙ' !Appf'vncti-


o nality

Если тип. реали3yIОЩЩi IA:ppFunction.alitj', обнаружен, Щ:il!Jывается метод


Do'It О I И абсолютное имя ТШIадобавляется в Lis.tbox (заметьте. Ч"rр ДИRЛ for дол­
жен ВЬШOJIНИТЪ проход по всемnma1l.1 ИОМnОНОВOЧlJOГО блока. 'чтQбы учесть :воэмож­
НОС1Ъ наличия в ОДНОМ номпоновочном блоне неcJroлы'{их :м.о;цул~ р~сшИрения).

рriчаtе Ьооl Lоа , dEхtеrлаiМоd1Jlе (string path)


{
ЬЬОl f CundSnapln = t ,a lse;
IApp~nctionalit.y it.fAppE'x;
1/ ДивaluAесхав еажруsха 1ПICSраиа0IЮ ЖОNDОИО.ОUЩ'О б.JlOJ:а.
As,sembly th-еSnaр1. пАsm = Ass:eДtb1y. L0adErom (,p ath.i "
1/ Поцvчеиие _са 'l'JЩQ8 Jl:OIШOВО.ОЧRarо б.пожа_
Туре [] theType$ = theSrlaplnAsm. Ge ,tTyp~s () ;
1./ ПСЖСJl: типа с реamc81ЩНе1'l ~F'unctiona1i.ty.
{or (in1;. .1 = .0; i < t1heTypes. Lеngtrц .1.++)
(
Туре t = t "heTypea [,i] .Getlnterface .( i'Il:\ppFun<ztionality");
н (t != null}
{
f oundSnp'; pln = ~ruei

/ / ДlцsахнчаСIC08 СВ-»!8'ааи. ~. C:O.,IIaJDIJI '1'Юi&..


'0bject о =
tf-,.е-Зnарlr~sщ.Сrе-аtеj:п~, tар('е (.th,eTyp-е$ [i) .FullName);

!./ Вм~o. DoIt () _реа 1ПR't1рфейс.


i tfApp,F x ~ а а:5 IAppFv.nc'tioC1aH tYJ
i t-fАррF:к..Dоlt О ;
lstLоаdеdSларlЛS . Itеmэ .Add (tl1eTypes (i) . FullName) .;

return fоuлdSnарlП;
)
.....

534 Часть 111. Программирование компоновочных блоков .NET

Теперь вы можете выполнить свое приложение . При выборе компоновочных


блоков CSharpSnapln.dl l и VbNetSnapln.dll вы должны увидеть соответствующее
сообщение. На рис. 12. 13 показан один из возможных вариантов ВЬШОJШения .

Рис. 12.13. ПОДКЛlO4ение внешних компоновочных блоков

Завершающей задачей будет отображение метаданных , соответствующих


атрибуту [Companylnfo ]. Для этого просто обновите Lo adExternalModule (). что­
бы перед выходом из контекста i f вызьmалась новая вспомогательная функция
DisplayCompanyData (). Эта функция имеет один параметр типа System.Type.
private Ьооl LoadE x te r nal Modu l e (string path)
(

i f (t != Bu ll )
{

11 Отображение информации о Jtоипании.


Displa ycomp a nyData ( t he Types[ i ]) ;

r e turn fоuлdSларlП;

Для поступающего на вход типа просто отобразите атрибут [Companylnfo ] .


pri va t e vo i d Di s pl a yCompa nyData (Ty pe t)
(
11 Получение данных [CompanyInfo].
object [] c u s t omAt t s = t. GetCus t omAttribu tes (false);

1/ Вызод данных.
f o r e ach (CompanyInfo Att r ibute с in customAtts)
(
Me s sageBox.Sh ow( c . Url,
s t r iBg. Format ( "Д о п о лнит е льные сведени ... о (О} ищите по адресуП,
c .Name)) ;

Превосходно! На этом рассмотрение примера завершается. Я надеюсь, что :к


э тому моменту нашего обсуждения вы осознаете, что подходы, представленные в
этой главе. могут найти пр именение и на праКТИI(е, а не только при разработке
средств построения прогр амм.
Глава: 11. ОТоt)ражеНИЕ!ТИПОВ, ДltнаМi<tчеС Кtое свяэыва'ние .. . 535
И.СХОАН~ \СОД. прqграммныи код ТlР~110ж:емий CommonSnappal1leTypes, CSharpSnapln, VbNetSnapln
и MyEXtentiabIeApp раэмеLЦе/>l в l)одкаТ$10ге., соответс.твующем шаце i2,

Резюме
Сервис Dтображения оказывается весьма И11тересl'JЫМ асцектом прстроен;ия
надежного окружения при иcnользоваltИЯ объектно-орие1tТИРоваиного подхо­
,да, 8 среде .NEТ 'Ключевыми элементами сервиса отображенИJt -Яlmяются тшт
Syst.em .туре 1'1 пространство имен Sу s t..ещ.Rеf.lесtiод. Отобр<Щ\:енне upедставляет
собой ПРоЦесС выяснения ОсноВНЫ'х харaкrеристик и ВОЭМОЖJIосте,Й типа в среде
ВЫJЮлнеН!ИН.
ДинамичеСкое СВЯЭЫВaJ-IИ:е предполагает СОЗдaRИе типа и вызов ето 'ЦI~JЮВ при
отсутcrвия априорной юtформаций о IЮI-пq>етны.х. им;енах соответствующц.х ~­
.вов. Как показывает пример создаваемого в ЭТОЦ rлаве расщиря.емрго ПРЦJlOже­
.н:ия. ато очень мощн:ая техпика. которая МОЖIП ИСЦОЛЬЗ0ваться JЩR созщш'елями

.инструментов разработки приложerom. ТЩ( и 'ШJлъзователЯМд ЭiJ'ИК JЩCтрументов.


В rnaвe БЫла также рассмотрена родь програм:мировани.я с ПОМОЩl>JO а1'Р!1бy-FО:В.
:.при ДОбавлсН1IИ атрибутов в определешrн 'I'.fШ(')В д.ООalЩнетсн соотвеrcтвующаЯ .ин­
формадия в метаданные IЮМIlоно.вочноrо б;rIORa.
ГЛАВА 13
Процессы,
v
доменыI приложении,

контексты и ХОСТЬ! CLR

В цредJ>IдYIЦШ(Д»)'Х, главах; мырасем:QТрелц шanж. воторые предпри:нимаю'rCJ'J в


ереде' CLR :ПРЦ.ВЫЯСНЩЦlИl1ар.аметров размещemш ВНenIНШi; 1ЮМIIоновочных
блонов, а. также рОЛЬ метада.нных .NEТ. Б этой главе мы рассмотрим подробности
того, Щ1Х CLR обрабатывает XOМnOBOBo~ БJJ.ОК. И nClТIщтаемся ПQНЯТЬ вз~ос­
ВflзьмеждУ процессами. доменами npиложеmn11i! r~ОJ;IТеI(СТами объектов,
В сущн:ости.. QОJlteНЫГipШ!ожен.UЙ (или .AppD€lmaln) представляю'r собой логи­
"чески е лодра:щелы в paМI<~ даЮJоrо процесса. содерщащие наБQРЫ связанных
RОМЛоНОВО~ БJlОRОВ .N,Eт. Вы УИlЩИте, что домеl!Ы npиложеНИЙ, разделяются
Roн'гeRстными rpани:цами, которые :и:сnoлъayIQТCJI ДIШ rpymшpОDRrИ подобных .NEТ~
объеI(ТО.в. С использованием ПОНII.тия RQh-reJtстасреда CLR получает возможность
тарантировать. 'Что объеRТЫ с о€обыми требова.иднми к среде выполщши,я будут
обрабaтьmв:rьc.a Надл~ащим обра.эом.
Обладая пониманием того, RaR сре-Д3. CLR обрабатывает компоново~ БJJ.о~
мы: с вами сможем выяснить, 'что' такое х'ОС1'ИIЦ' CLR КaR уже ГОВОРИЛОСЬ в DlЩ.ВС 1,
сама среда CLR представляется. (по крайней мере. отчасти) файлом mscoree.dJl.
Пр~ 3\шуСке .БыIJJIняемогQ nОМдоновочиого блока файл mscoree.. dll загрушаеТf,:Я
автоматически. но. КaR BЪJ сможете убедиться. в фОНОВОМ режиме :ори ЭТОМ вIЩjJQл­
НЛe'l'М Ц6,1ЩЙ ряд шa:rов, скрытых: от ГJ1аз nОЛЬЗЩlатeJ,In.

Выполн'ение традиционных процессов WinЗ2


Пон.йтие "npoцecc" существовало 1:1 тreрационв:ьuc системах WIndows ЩlДOЛI'о ДО
lЮвШ1еmtв: платформы .NEт. Упрощенно говори, терМиН проЦеСС ИCIЮЛЬзуетсн ДJIЯ
обозначения множества ре~СQII (ТaRИX. RaК. :внешние библио1"ffim программног()
кода. и первИЧEJ:ШЙ nОТСШ) и вщцеля:емой пам.яти. необходимых,zr.rr.a работы прило­
жешш. для ШiiЖДОJ'О зarруженНоrо в naмнть фзй.па *. ехе операцИОНная сист~ма
создает отделЬНЫЙ и иэо.nи:poванJ.IЫЙ npoцесс:. исшmъзуемый в 'течение всего "жиз­
неННого цикла~ еоответетВующего Dp:шrожевив.Врeзym.т·ате тшroй ИЗClЛЛЦИИ при­
Шlmеииit повышается надеЖНОСТЬ и устоi:lчшsость среды ВЫпQлнения. пос1WДЬ1!у
0ТRaЗ 1:1 :ней oднoro npoцесса не в.лияет на фушщион:ирование другого.
538 Часть 111, Программирование компоновочных блоков ,NET

Каждому процессу Win32 назначается УНИIщльный идентификатор PID (Process


ID - идентификатор процесса). и процесс, при необходимости. может независи­
мо загружаться или выгружаться операционной системой (или программными
средствами с помощью вызовов Win32 API), Вы. возможно. знаете. что на ВRЛадке
Windows (которое можно активизировать нажати­
Процессы в окне Диспетчер задаl.J
ем комбинации клавиш <Ctrl+Shift+Ese» можно увидеть информацию о процессах.
выполняющихся на машине, включая информацию РШ и имя образа (рис. 13.1).

Замечание. Если столбец РID в окне Диспетчер задач Windows не отображается, выберите в
этом окне команду Видr:::> Выбрать столбцы ... из меню и в открывшемся после этого окне уста­
новите флажок ИдентИфикатор процесса (РID),

explorer ,ехе CНlEF


WINWORD.EXE CНlEF 00
svchost ,ехе 5У5ТЕМ 00
cak,exe CНJEF 00
IPodSerVice,ex. 5ysтEМ 00
alg,",," 1400 LOCдL SERVICE 00
поtepad,е-хе lЭ2'1 CНIEF 00
gearsec.exe 964 5V5ТEМ 00
spoolsv,exe 848 5VSТEМ 00
svchost,exe 820 LOCдL 5ERVlCE 00
s.."chost,exe 78'1 NEТWORK SERVICE 00
.vchO$t.exe 740 SY5ТEМ 00
svchost.exe 700 NETWORK SERVICE 00
5vd"last .ехе 652 SY5ТEМ 00
taskmg, ,ехе 4% CНlEF 00
Isass.exe '192 SVSТEM 00
'176 SY5ТEМ 00
4ЗiC SY5ТЕМ
408 SVSТEМ

Рис. 13.1. Диспетчер задач Windows

Обзор потоков
Каждый процесс Win32 имеет один главный "поток·. выполняющий ф)'НIiЦИИ
точки входа в приложение. В следУЮщей главе будет выяснено. как создавать до­
полнительные потоки и соответствующий программный код. применяя возможно­
сти пространства имен System.Thr:eading. но пока что для освещения вопросов.
представленных здесь. нам нужно выполнить определенную вспомогательную ра­

боту. Во-первых. заметим. что nОnЮК- это "нить" выполнения в рамках данного
процесса. Первый поток. созданный точкой входа процесса. называется neРВllЧН:Ы,м
nоnюком. Приложения Win32 с графическим интерфейсом ПОДЬ30вателя определя-
[лава 13. Процессы, домеНЫ rтРИJ10жеIiИ", ""Оljте.ксты И lCо.ст.ы ClR 539
ЮТ В качестве точки входа приложеНЮJ метод WinMa in (), I\щrсоm.ные пршюжemm
ДJIЯ 'ЭWОЙ цели ИOllo.nъауют метод Main Л ' Процессы. соетОIlЩИ.е из ОДНОГ() первично­
го пОТока.. БУдУт n011'lOкоуctnоUЧuвымu. DОС1<ОЛЬКУ Б них В ~amдbIЙ момент' времени
талыш один .nото!( может по.JIYЧИ'ТЪ доступ J{ даиным приложе.~. Однзи@ одно­
поточный процесс (особенно есJtИ он оснрван На графичесЮi)М интерфеikе) ча ~т о
бывает "с:клонен:" не Отвечать пользователю rrри вьmо:днении ДОТРАОМ до("т~точв о
Сло-ЖНblX. действий (напрJIМtф. связанных с печатью БОJThЩОГО теНСТОБОГQ фqЩ;;t,
зan;yтaнным;й В:ЫЧ:ИС.J1ениями или ПОnЫТR8.МJ1 соедиnитJ>CЯ с удаленmцм ce-piВ epoM ),
'Учитывав .9Т1'I rtotel-ЩИальны~ недостаТItи однопоточныx приложений, W'in32
API позволяет первИ"IНЫМ потокам пор~mдатъ допо.дцителЪНР1е вторичные потоки
('1:'d'Rже называемые рабачuмu ГlOтaKaA1U). используя. нщrpимер, такую удоб~ую
фymщи.юW1n32 APi. как CreateThread (!. Каждьrй ПОТОR tпервичнъ1Й шrn вторич­
ный) в таком процесс е стеновитсn унивальным элементом ВЬПJQЛsенJiЯ и по~ ает
дpeтyn JЮ всем Oтn.-pы'ГЫМ эдеме:нтэм JIаБНЫХ на УCJ.IОБИЛХ .lЮнкуреяции-

Вы можете сами догадаться. что ра.арабоТЧИЮI создают доfюrmите1Iъны:e поток~.


КfШ правило, дм того, чтобы программа :моrлз. быстрее peaNIpOBaTb fla де.и;ствИJI
пользователи. Мнorопоточные процессы обеспечивают '»';'ЩI@ЗИЮ ТОГО. чтр :цсе де.j.)­
tтвия Ш:JПоЛНЯютсл приБЛШЗИ1'"е.iIЬНО за ОДНО и то же время. Наnpиыер. nPИДOJit~­
ние может порождать рабочий поток ДЛЯ ВЪ1ПQлнеJ:iиЛ д.еЙствИЙ. треБY1Q~ м:IJoF<;l
времени (снова вcrшмlIИМ о печати большого файла), Как только СОЗДaIi:ньЩ вто­
рИчн:ый:nОТОК начинает свою работу, rлавН:ЫЙ поток снова Получает JЮЗМОЖI:;IDСТЬ
отвечать н'а lЮJIЬ30ватe.nьcюm. ввод. <rТo ПООВО)IЯет процессу в целФм blO'I'е,нциa,IIЬ.tJО
обеспечm:ъ лучшую npаИЗвdДИтельно.С1Ъ. :Хотя в реалЪJ-JOСТЦ маго може1' и не цро­
ИЗОШ}!; 1ЮП0JlЪ30вание CJIИ1IIEОМ большого чи.сла ПОТОКОВ в РЦМХах одного дроцесса
может улудш.umь проиаводи-rелъноеть . ПОСКО!lЬИ;У np01leccopy приде1'СЯ ЧQС:ГО пере­
Itinoчаться МеЖДУ активНЫМИ потOIШМИ (а это требует немало времени).
В реaJD:i1IOCти всегда СЛедУет учиты:ватъто, что мноroПDТОЧ}10СТЬ ЯВJI.Нет(щ. по·
~ • .ил.n:IOэией,. обеспечиваемой оперctцИОНl-lОО СИС'1'емОЙ. Матицы, OC,f-IОВaJoшые
:на oднnм npоцеесоре. в действительно.сти lIe способны обрабаты)ваь неско.лЬJ{9 [10-
токов оДНовр.еменно, 8меото этого. системы ·с oд:нnм лроцессором выдеяют ftaJК­
даму потоку свою часть зремени (что :называют KBaWnOOOHueM ,времени) н,а основе
уровн.н приоритета данного потои . Коrда R.aiUI:r вре:мени потока . зананчщваетс.я,
текущий NOТNК Приостанавливается. 'lТOбы свою задачу МОТ ББПIОJЩИ'rЪ Друг9Й П()­
то:к. Чтобы hOТOK "помнил", ЧТо происходило перед тем. КtШ ШIТО'К был временно
I ·ОТОдВинут в сторону·. ЮlЖДЫЙ поток ПО:.iIyЧctет ВОЗмОЖНОСТq эацис~ть пео{5ходимые
данные в блок ТLS (Тhread Loca] storage·- локальная цамять П{)ТОlta), };I кв:ждому
I
llD'1'OКJ обеспечивается отдельный crеж вызовов. щш шжазfUЮ на рие. 13.2.
I, ECJiН тема потоков ДJI8 ·вас нова, не слитком беспо:койтесь о Де'Т.aл:ms::. }fa этот
момент достаточно запомнить только то. -ЧТО ПОТQК ЯБдяеТСiI УНЩ(алЬной hнnтыо ,'
вЫПОлненИя. в рамках процесса 'Win32, КаЩДЫЙ процесс Юdеет оеРJш.ч:ныЙ поток
(ооздаваемый точкой входа Б .приложе1lИе) и может содержать дополнительные по­
токи, которые создаются Dpограммными средствами .

Заме.чание. Ноаые nроцеССQРЫI, Intel имеюrос06еННость, наЭЫlOаеМуlO 1'ехflОЛОГlAей НТ (!iyper-


Threading Тесhnфlоgу - гиnеРПО10коваR технология), которая nОЗВОЛi'lет OAl:loMY процессору
npJII опредм&нных услов~RХ обрабаТ!:ilваrь мнuжвств@ ПОТОIЩ8 Q,I1HOBj!)eмe/llIiQ. ПодРобности
описаАI-1R этой теХНОJlОГИИ МОЖJ,IС найти пр адресу h t tp ; / /'vlW W , i n t е 1 . со·ш I 1. ti f Q I
Ьуреrth.rеэQiflg ,
540 Часть 111. Программирование компоновочных блоков .NET

Поток Д Поток В

Рис. 13.2. Взаимосвязь процесса и потоков WiпЗ2

Взаимодействие с процессами
в рамках плаТфОРМbI .NET
Хотя процессы и потоки сами по себе не являются чем-то НОВЫМ, способы вза­
имодействия с этими примитивами В рамках lDIатформы .NEТ существенно изме­
нены (К лучшему) . Чтобы успешно пройти путь К пониманию npиемов построения
КОМПОНОВОЧНЫХ блоков с поддержкой множества ПОТОКОВ (см. главу 14), мы начнем
с обсуждения возможностей взаимОДt"..йствия с процессами на основе использова­
ния библиотек базовых классов .NEТ.
Пространство имен System,Diagnostics определяет ряд ТИПОВ, позволяющих
программное взаимодействие с процессамИ, а таroке типов , связанных с диагно­
стшtой системы (например, с журналом реrистрации системных событий и счет­
чиками производителъности). В этой главе мы рассмотрим ТОЛЬКО те связанные с
процессами типы, которые определены в табл. 13.1.

Таблица 13.1. Избранные члены пространства имен Sys t em.Diagnostics


Типы Syatem.Dlagnostlcs
Описание
Дn. поддержки процессов

Pr ocess Класс Process обеспечивает доступ к локальным и удален­


ным процеосам, а также позволяет программно запускать и
останавливать процессы

ProcessModule Этот тип представляет модуль (* . dll или *.ехе), за­


груженный в рамках KOHKpeTHoro процесса . При этом тип
Proce ssModule может представлять. лю60Й модуль - мо­
дулЬ СОМ, модуль .NEТ или традиционный двоичный файл С

ProcessModuleCollec tion Предлагает строго типизованную коллекцию объектов


ProcessModule
ProcessSta r tlnfo Указываат множество значений, используемых при запуске
процесса с помощью метода Pro cess . Start ()
rJ1:1i1!l '1·3,_Процеосы, домены ПРИcn0ЖВНИЙ, KOHTBKCTI:II И хочты CLR 541
ОкоНчание табл. 13. 1

Т"п ... System.Diagnostlcs


AlliI поддержки прCЩ8l:lСО8

Предстasлsвт поток 8 рамках Д3ttнoгo прЬцесса. ТИП


Рrосезs'.i'hrеаcd исnоп.ьзуеТОR, ,II)'1Я диагностики МНОЖec11lа
horOICOS n:poцесса. а lie дщI тощ '1т.обы nорождать НОВЫе 110-
i'ОКИ выполнени!! в рамкахдаНIiОГD nPОЦе.&са

РrосеssТhrеаdСоllесtiсщ Предлагает строго типизованную К'Омеl(ЦИЮ обьеюrов


ProcessThread

тип Systero .Diаgnозtiс·s. Process по.эво;щеr проанaJЦfзировать npодессы. ВЫ­


потшемые на ,данной М,amипе (д.ОКaJ1ЬНОЙ иml удалеuноjt). Класс Proces.s. дред­
JIaгает также чJ1еНbI, которые позволяют эадус;катъ и останавливать процеесы
програм:мными с~(:'Твами. УCJ'анавливать УРОВНИ приоригета и пал;учать список
зхтивных ПОТОКОВ иjяJtц зarрyare:nныx MOAYдe~. ВЪJЩJЛRfI~ В рамках данноrо
процееса. В та.бд.. 1З.2: прeдлarае:гся список некО1,"ОРЩ (:ио не всеХ) членов System.
Diagnpstles. Рrосезs.

Таблмца 13.2. Из6РШiные li:neHbI тила Process


Чnвн

EiК it Coae CBOJj~Bq, содержащ~значеНие, tcQTopoe 'указывается nроцесоом


при завершении его работы. для IlQl!Y'rвния GТOгозначения необходи­
Мо обрабОТаТЬ . CQбЫТ.llfе Exi te,.d (l1РИ aCKl-rхронном уведомлении) или
вызваТI> метод WaitFo:J:E,x it () (пр)!' СИltхрОННОМУ86Д()МJ'ании)

E:xi t Time Свойство, содержащее штамп I!pElMetH1, СООТJi8ТСТ8У1ОЩИЙ nl3BKpa-


щению раБОТы лроцесса (и представпеtlНЫЙ тип'Ш.1 DateTime)
Handle Сl;IOйстrщ, возвращающее десКРЮll'ОР. назначенный процессу опе­
РaL\ИlЖl'lой система!:;
RaлdlеQсцпt СвоЙство" возвращающее ЧИ. СJ10 Д8еКРИПТ{JРОВ, ОТ1фЫТIiIX. D130.цееоом
rQ Свойство, содержаще~ идеt-tтификаrор rтpoцecea (PID) ДЛЯ Д~HHOГO
llPoЦвcca

Свойство, t:C,lдержnщее иМя компьютера. на котором RblПОЛlUlется


данный nРОЦ..8СО
м.цnМоdulе Свойотво, получающее тип Рr о сеsэМоdulе, который представляет
maв~ый МОАУльданного nроцесса
M.;liQWiJldo\-,тitlе Свойство NainWil1dtJw'!':l:.tle получает заголоsоll. тавного окна
МаiriWi:ndQwБа!}dlе процесса (еOJU1 процесс lie ~MeeT главного OKtla, буд~т ВОзвращеl'lа
пуста" строка).. Свойство MainWindowHandle полyqаm деокриnmр
(Пl3едстзвленl'IЫЙ ТWlOM Sузtеm.ll'l'tРtr) соответствующего оКНа.
Если I1роцесе, не и-мsет глеlШОГО окна, типу l.ntl'tr:: присваивается
значенl-\в Sуз.tеro.тпtРtr. z'ero
MOOt:lles Свойство, обеспео:.ивеющее ,доступ к стррго типизованно" JЩ:n.riВКЦИИ
ProcessМoduleC.ollect i on , прмставЛ}:Iющеи множества моду·
лей(*,dl1 или ;r .еке), загруженных в рамках reкyщега npоцессз
Рriоr i tУВОО5tЕлаЬlеd Это саойство указывает, AOJIiICHe ли операционная система временно
'iOкорять В'ыполнение процесса, когда ег.о тваное оJOfО гroлучает
фокус ввода

1
r
542 Часть 111. Программирование компоновочных блоков .NET

Окончание табл. 13.2


Член Описание

PriorityClass Свойство, позволяющее прочитать или изменить данные базового


приоритета соответствующего процесса

Pr:ocessName Свойство, содержащее имя процесса (которое, как вы можете до­


гадаться, соответствует имени приложения)

Responding Значение этого свойства указывает, должен ли ПОflьзовательский


интерфейс процесса реагировать на действия пользователя
StartTime Свойство с иНформацией о времени, соответствующем старту дан­
ного процесса (эта информация представлена типом DateTime)
Thr:eads Свойство, получающее набор потоков, выполняющихся в рамках дан­
НОго процесса (представляется массивом типов ProcessThread)
CloseMainWindow () Метод, завершающий процесс с пользовательским интерфейсом пу­
тем отправки соответствующего сообщения о закрытии главного окна
GetCurrentProcess(} Статический метод, возвращающий тип Process, используемый
для представления процесса, активного в настоящий момент

GetProcesses () Статический метод, возвращающий массив компонентов Process.


выполняющихся на данной машине
Kill () Метод, немедленно прекращающий выполнение соответствующего
процесса

Start () Метод, начинающий выполнение процесса

Список выполняемых процессов


fi
Чтобы привести пример обработки типов Process. предположим, что у нас есть
консольное ПРИJ10жение С# Pr:ocessManipulator. которое определяет сле.uуюIЦИЙ
вспомогательный статический метод.

public static void ListAIIRunningProcesses()


{
1/ Попучение списка процессов, ВlШоnИR8IIiIX на ~аниой кашине.
Process[] runningProcs = Process.GetPr:ocesses("."};
11 Печаmъ значения PID и имени к8ждо:rо процесса.
foreach(Pr:ocess р in runningProcs)
{
string 1nfo = striпg.Fоrmаt("-> PID: (О}\tИмя: {l}",
p.Id, р. ProcessName) ;
Console.WriteLine(info):

Console.WriteLine("************************************\п"};

Обратите внимание на то, что статический метод Process .GetProcesses (} воз­


вращает массив типов Process, представллющих процессы, запущенные на вы­

полнение на целевой машине [используемая здесь точка обозначает локальный


компьютер).
Глава 1З. Проц~с(;ы, доJлены прило.жвниИ, KOHTeJ(CTbI и хостЫ CLt:1 543
После ПОJJYЧ.еНn1l масс.ив~ ТИПОВ Ртосе,в g , МОЖНО иСПОЛЬЗQвать любой из ЧJ1енов.
npив~денЮill!: в mбл. 13.2, Здесь просто отображается значение PID и иМя RС!ЖдОГО
пз ПроЦессэв. В npедпрлощеI;IИИ 9 том, что.вы обн:овили метод, M.ain () ДЛ,~ ~.Ь130ва
1istAll'3Junning!i'rocesse s (.) , в ~УЛЬ1'ате ВЬШО.IШения соответСТП)'lощей програм~
мы вы должны увццеть нечто rЩДDбвое ПDI:I:азанн:ому на рй:с . 13.3.

Рис. 13.3. Пере"ень запущеюit>IХ nрОЦеосов

Чтение даННЬIXКОl:Iкретного процесса


в ДОПОЛНeJm'е.к полному списку всех з.aпytценнь1Х на дq:нной М,ашЩfе ЦРОЦёССОВ,
статичес.кий метод Рr ос еэв . Getpr.o ce s sБуId О цозволяе'т nрочwгатъ дЩЩЬ1е d'Ще:пъ-­
НОТО npоцесса ПО ето 3mtчен.ию PID. Если зanРО.QИТЪ :цфс.туп 'к дроцессу :tщ несуще­
ствующему аначeIiИЮ PID. будет сгенерировано }Д'RJцOчение AIqu me ntEJtc ep tion.
Тшt. -чтобы поnyчитъобъ&tт РгО, се ББ. пр,едcraвленньrй аначемием РЮ. равным 9В7 ,
МОЖНО .написать следУ!ьще~.

11 Ecтi процесс.. CI PID=~8" JSe'.L', !l'0, c:p~a JWnо.пвенкя


/ Iс::реsерирует coo0п5eT.o~yuцee исJiC.moЧеНИе.
sta,t ic v Did М-аln lstr:ing [Ja,r\1~)
{
Pxoces s tbePra.(5: ;
try
{

1
т

544 Часть 111. Программирование компоновочных блоков .NET

theProc ~ Process,GetProcessById(987);

catch / / Общий б.пох catch д1IH ПРОС'l'оo:rw.


{
Console. Wri teLine ("-> Извините, некорректное значение P1D! П} ;

Список множества потоков процесса


1Иn класса Process обеспечивает и способ программного получения множества
всех потоков, используемых данным потоком в настоЯIЦИЙ момент. Множество по­
токов представляется строго типизованной коллекцией ProcessThreadCollection,
которая содержит соответствующий набор отдельных типов processThread. Для
примера предположим, что в наше текущее приложение была добавлена следУЮ­
щая вспомогательная статическая фym~.

public static void EnumThreadsForPid(int p1D}


{
Process theProc;
try
(
theProc = Process.GetProcessByld(pID);

catch

Сопsоlе.WritеLiпе{"-> Извините, некорректное значение


Console.WriteLine("·~·**~·***·***·***·***·***·***·*****\п");
PID!"}; ,
return;

11 lhIвoд; мкформации д1IЯ ItaJКд;OI'O ПО'1'оха YJ:asaннol'o процесса..


console. Wri teLine ("Это потоки, выполняемые в рамках j О) ",
theProc.ProcessName};
ProcessThreadCollection theThreads = theProc.Threads;
foreach(ProcessThread pt in theThreads)
{
string info =
striпg.Fоrmаt("-> 1D: {О}\tВремя запуска {l}\tПриоритет (2)",
pt.1d, pt,StartTime.ToShortTimeString(), pt.PriorityLevel);
Console.WriteLine(info);

Console.WriteLine("************************************\п");

как видите, свойство Threads типа System.Diagnostics.Process обеспечива­


ет доступ к классу ProcessThreadCollection. Здесь Д1Iя каждого потока в рамках
указанного клиентом процесса выводится назначенный потоку идентификатор ID.
время запуска и приоритет. Обновите метод Main () программы Д1IЯ запроса у поль­
зователи значения PID процесса так, как ПОRaЗано ниже.
[ЛЗ!lЗ 13. Процессы, домекы nРИЛQ:«ений, ~OHTeI(CTЫ и хосты CLR 545
.si:ёltic void Main (:stri.ng [] args)
{

// Запрос РШ 'f1 ,tro.Jn,sq8&'1'е.пв и alilJlooЦ спиока &ко1о_IфХ :nO~QJi:оз.


СОДЗОlе.Writе1inе("*"·"* В:аеДИ'Г08 З!'I:ачен.Ие' .Р1]) nроцесс.а "Н''''*'''''');
CQ1\·501e. W.rite ("БID: ");
string pID ~ Cc>n'sol.e .,ReadLirJe ();
int theProc:ID' = iJ:Jt.ra,.!'Ose (plD);
Er'JI.l(!lThro;:.a :dsFoT,pid ,(tiJ.eProcIP) ;
Cdi15 ole .RеаdLiщ:~ () ;

в ре3)'лътате ВЫIIO.)nIения обновлe1ЦiОЙ прогрro,n..п;l вы должны llOЛJ'Df'1"Ь ВЫВОД,


)ЩДоtжый ПОК8.ЗflШtому на рис. 13.4.

Рис,,13.4. ПеРs'l.el'lЬ потоков Ei рамкВх ВЫПОЛ~lЯемЬг'а лроцесса

Кром:е члеНьв Id. St.artTiine и PrlorityL,evel. тип ProcessTh.r:e,ad имеет и дру­


гие ti:Лены. которые мmyt представлять интерес. H~$Ol'opыe И~ тaIODt '<ЦIeHOB при­
ведены в табл. 13.3..
Таблица 13.3. 110,l1;борка члеНDВ типа ProcessThread

Чпек Описанке
Bas.e Priori ty ~итает значение бваового ПРИОРl'Iтеrа nOi:dKa
С,пr rэГ!.,t Pr w ri t y Читает ЗНl;iчеltие l'е куще ГО' flриорит~та пот()~
ld Читвет УJ1Иl(альный и.tJ;енrификатор потока
ldealProc.essbr Задает tlре.щю"ггителЫ-IЫЙ Flроцеосор Дд'Я вl>IIJDлнения данного потока
Рriфrl t:iLeve 1 Читает JAЛИ задает YDOl;lfillib пРМРИ1'ета для ;ЩЩНОГО Г1{)1ока
Рrос:еS,ЭQr1l.ftini ty Задает процессор6l, на которых может ВЫПОЛf;!ЯТЬСЯ асоойиирое8НН ЫЙ
поток

41111зат ;ЩР~С S.nамяти .для ФУНКЦИИ, kоторая выэ~.валаСь операцион­


НОЙ Сklотемой дn!! запуска данного П'(Jl:ОJ(a
StartTi.me Llит-ает инфОрМ8.ЦИ19 О' времени запуска данного пото~ оrтераUИОННt:lи
оистемоiii
Тh rе~, dstз т. е Читает ИНФQРмацию о теКУЩем ооото~ии потока
ТQtаlРrо сез'sо;гТiще Читает общую оценку времени" в течение !шгороrо ,да~ный пo'fО~ ж;·
пользовал n'РQцвссqр

WaitRea~on Читает JIIt'(формацию о ПРИ'lине. пр которой nOTOJl. находится в О>I{Ид8.нии


546 Часть 111. Программирование компоновочных блоков .NET

Перед тем как двиrаться дальше. следует заметить, что тип ProcessThread не
я.вляется. тем элементом, который можно использовать для создания. остановки
или ликвидации потоков в рамках платформы .NEТ. тип ProcessThread является
средством получения диагностической информации об активных потоках WinЗ2 в
рамках ВhlПолняющихся процессоа. То, как строить МНОГО поточные приложения с
помощью пространства имен System.Threading. мы с вами выясним в главе 14.

Информация о наборе модулей процесса


Теперь выясним, как въшолнить цикл по всем модулям. загруженным В рам­
ках данного процесса. Напомним. что модУЛЬ - это общее название. используе­
мое для обозначения *.dll (или *.ехе). При доступе к РrосеssМоd111еСоllесtiол
с помощью свойства Process .Module вы получаете переченъ всех модулей. задей­
ствованных в рамках соответствующего процесса - модулей .NEТ. модулей СОм и
традицио1ШЫХ библиотек С. Рассмотрите следующую вспомогательную функцию.
которая перечислит модули конкретного процесса, заданного с помощью PID.
public static void EnumМodsForP1d (int pID}
{
Process theProc;
try
(
theProc Process.GetProcessById(pID) ;

catch

Console. Wri teL1ne (" -> Извините, некорректное значение PID!");


Console.WriteLine("************************************\п");
return;

Сопsоlе.WritеLiпе("Загруженные модули ДЛЯ (О} :",


theProc.ProcessNarne):
try
(
ProcessModuleCollection theMods = theProc.Мodules;
foreach(ProcessModule рm 1п theMods)
{
str1ng info = string.Format("-> Имя МОДУЛЯ: (О}",
pm.ModuleName) ;
Console.Wr1teL1ne(info) ;

Console.WriteLine("************************************\п"):

catch

Сопsоlе.WritеLiпе("МодулеЙ не обнаружено!");
Глава 13. Процессы . домены 11РиложеНI:1И , KOHTe·K.~ТbI и JtOCTbl CLR 547
Чтобы увидеть IIРИJ\«ер :ВО:jЫф~ОГО B:ЫВOД~ прегpёlММЫ, давайте прОверим 33-
трущенные "N:ОЦ)!'ЛИ ДJШ лроцесеа, ~I:,ЦIОЛНRеМОFО "в рамках рассматриваемого здесь
JtOHCOJ1bl30ro rqmложеI!ЩI Process]'1a,nip,J1.a tor. для этого запустите npилОжение.
вьmс.щrте ;:sнаЧСЮiе РШ. СОО1'В~ТGТвующее. PrQge~sManipulatQr. e~e.· и передайте
это ЭЮlif;lе-Flliе методу Е ourrtr>1о q;sForp id () [не забудьте соответствующим обрююм Dб­
навить метод. Mai [j О). ВЫ, навернре, УДИЩ'll]1ееъ. увидев .весь СПИ<:ОК мод.УЛей *, dll.
иоторые иеtlользуюте,ц для ТЩtОГО цростого RЮНГ'ьЛЪНОro пр:иложенил (а t·l. dll,
mfc4.2u .dll. o1e-a.ut32.dll и "LЦ.). на рис. ЦJ~5 IIО:казан результa'I запуска.

Рис. 13.5. nepe-Ie~rl? Э'G!l'руженных модулей в РG\МКах ВЫПОЛНЯIQ'


щегося l1pot:J.eQCB

Начало и остановка прОЦессов с помощью


программных средств
в заверwенйе этого раздеД8 Z..n,J расс:м:отримметоды $t !;3 rt () и Ki 11 () т.ида
Sys·tem.Diagnostic.s.Proc,ess. По именам ЗТЩ методов БЫ мол{ете ДQГМатъся,
ЧТО щiи обеспечивают.. соответетвеНЦО, npогрa.ммt:ц;IЙ запуск и программное Эц­
:Вершение процес:са. Рассмотрите. напрдмер • .вСIIомо:rател:рныЙ статический метод
s·tа.rtAnсlкillРrосеЗ$!) .
plilbli~ stо,з·ti:с void Вt.а.rtAndКillР.rо'сеs"З О
!
11 Запусж Internet Explorer.
Process i.ePr.oc = Proce5S. St:.al·t ("IExplS.re. ехе" ,
"w.WW. int еrtесnt ..с аiл-ing- . I: om u·) ;
console. Wr i t€ (." --;- J~ажмите <EJnter>, Ч1'аб~ заi!lершить {О f ••• " ,
ieFroc. p:r (J()еsв'Ыа'IILЕ) ;
С d пsо·l.е . HeddLi пе () ;
548 Часть 111. Программирование компоновочных блоков .NET

// Завершение npоцесса iexplorer.exe.


try
{
ieProc.Kill ();

catch ( ) 11 Если по.nъsова'l'е.nъ уже sавершиn процесс . ..

Статический метод Process.Start() является перегруженным. Как минимум,


вы должны уназать имя процесса. который следует запустить (например. Мiсrosоft
Intemet Explorer). В этом примере используется вариация метода Start () . позволя­
ющего указать любые дополнительные аргументы. передаваемые точuе входа про­
граммы (т.е. методу Main () ).
Метод Sta rt (), кроме того, позволяет передать тип System. Diagnostics.
ProcessStartlnfo. чтобы yr<азать дополнительную информацию о том, как дол­
жен стартовать данный процесс. Вот формальное определение ProcessStartlnfo
(подробности можно найти в до:кумен:тации .NEТ F'ramework 2.0 SDK).
public sealed сlаэs Sуэtеm.Diаgпоэtiсэ.РrосеssstartInfо :
object

public ProcessStartInfo();
public ProcessStartlnfo(string fileNarne);
public ProcessStartlnfo(string fileNarne, эtriпg arguments);
public string Arguments ( get; set; J
public bool CreateNoWindow { get; set; }
public StringDictionary EnvironmentVariables { get;
public bool ErrorDialog { get; set; }
public IntPtr ErrorDialogParentHandle {get; set;
public string FileNarne { get; set; )
public bool P,edirectSt.andardError ( get; set;
public bool P,edirectStandardInput { get; set;
public bool RedirectStandardOutput { get; set; }
public bool UseShellExecute ( get; set; )
public string Verb { get: set; }
public string[J Verbs ( get; )
p1.lbl ic ProcessWindowSty 1е Wi rldowStyle { get; set; }
public string WorkingDirectory ( get; set; )
public virtual Ьооl Equals(object obj);
public virtual int GetHashCode();
public Туре GetType();
public virtual string ToString() i

Независимо от того. какую версию метода Process. Start () вы вызовете. будет


возвращена ссылка на новый активизированный процесс. Чтобы завершить вы­
полнение процесса, просто вызовите метод Kill () уровня экземпляра.

Исходный код. Проект ProcessManipulator размещен в подкаталоге, соответствующем главе 13.


ГлаВ6 1З. ПроцеССbl, ДО~,&I1Ы nРIIIЛ.!i)*ВI1ИЙ, 1(00'1тексты 1'1 XOCTЫI CLR 549

Домены приложений .НЕТ


Теперь. ногда БЫ nO:IOfМaете роль npоцессов Wfn3Z и ВОЗМОЖНО'стей БЭaJJМодdt­
ст.вин с ltИМИ средствами управляем.ого програММНОГQ нода. давайте расс.мотрим
понятие домена ПРИЛОjf(eНИЯ: .NEт. По npавпдам платформы .NEТ Rомцоновоч.ыые
блохи не размещаются в рамках процесса НбпосредeтвefIНО (нан это бщтIO В тради­
ЦИОННЫХ ,ЦРИЛ.оженШIx Wln32). Вместо зтого выполняемый файл .NEТ пом~ща~
в обособ.tteнНЫЙJ10гическоF1 раздеJ1 процесса:. называемый дo.мmtOM nРШI.OJI(',енW1
(сокращенНО AppDo:rnain). Бы увидите, ЧТО один. процесс можетсодеРJltaТ~ NЦЮжс­
ство Доменов npиложения.. хаящый из которых буде:г обслуживать QВ0~ ВhШол:вяе­
м.ый файл .NEт. Такое .!tОПО.1IНИТeJ1ЬНое раэдеJЮНИе традиционного процессщ WЦ132
дает оnpеделеШIЫе преимущества. и HeROТOpыe из них ymза:нь,l UИЖ!!!.

• Домены .при.ложения Я8.ПЯЮТСS:l ЮIЮчевhIМ аспеКl:ОМ щезввиCЩ\oJQЙ от ОС при­


роды платформы .NEт, поскольку такое ЛОГИ~lеCJ\ое деление абстращруется
от того. :как именно ОС представляет' зarpУЖе1-ШЫЙ Быпол1:Jяемый объект.

• ДоМены прилоmения ЯВЛЯЮIСЯ существенно менее ресурсое:мкими в Отноше­


нии времени проце(.'Сора и памяти. чем весь процесс в nело.м. Поэтому среда
CLR способна зarpyжат:ь и выгружать домеНЫ npи.1IOжеНИЙ намного быстрее.
чем формВ:7IbныИ ·працесс.

• ДомеЮ:iI цриложения ()б~спе"ffiВают лyчm:и:й уровень ИЗО:Jl!ЯЦИИ дтr загружен­


ного црилож~я. Есди ОДИН ДОМен I1pИ1l()жения в рамках процеоса "Tepnwг
неудачу".. Qсщдьные домены приложешrii:могут продолжать фун.jЩИОНИРО­
вать.

11<1 приведе:нцorо списка следует. Ч:ГООДИН процесс можer содержа:гъ любое. чис­
ло до:м!що~ ЦР,Wlожения. кажДЫЙ.из IШторых лолнЬctЫD изолирован от дрyrи:х до­
менов ПРЩlDжения в рамках данного працесса (а тю,же любого другото процесса),
с учетом; 'этого QЛедует ПОfIИ1l.1аТЪ.ЧТо прИhoженй:е. Вbll10лнлющееся в одном домене
r;rpиложещrя, не может пo.nyчить данные (В чаСТНОСТИ. значеииJi глобмьНblX пере­
меfШЫХ JfЛИ с:;.татнчес:ких полей) дpyt'oгo домена ПРИЛО1Кенияина Llе, :Как с ПОМОЩЬЮ
протоI:ЮJIa удаленного взаимодействия .NEТ (кОТОРЫЙ'МЪ1 pacc.мoтpKl\l1 В главе 18).
ХОТИ одцн процесс и МО'жеmnршш'lЪ .МНожество ДOM€HOB npИЛd)!reния.Т€Щ бы­
~ёJ,eT не всегда. I{aк минимум, процесо ОС буДет содержать то, что обычно наз1Ц8Э­
ют дq:М.СfЮМ Г!рШJOжен.Шl. соааанным Jio !jМDЛtft1Н.шо. Этот специалышй домен np.и­
JJЩft~,нщr автоматически создается средой CLR во время :JanyCl(a процесс~.
После этого CLR ,создает ДOD'ОJIJШтелъпые домены прлложения по мере необхо­
Дl'iМрСт,и ., Если тю'I'peбуетс.я (хот.l1 ото и маловероятно). вы можете программно соз­
дa,в~тъ домены npиложенин: всредевlШIОЛНения в ,рамках БЬШ(jJI}1ЯемQГQ npaцe~ca.

~спользуя crатически.е MeToДbt масса Syst:em .A;ppD.omain. Э1'ОТ :класс ОRаЭJ>IВается


также пол:еэным ДJI.Н осу:Ществлени.н низgОУРОВНeIlОГО ш>нтрOllЯ доменоз .upиложе­

НИI'I. Основные члеНы этого kЛаоса описаны.в т.абл. 13..4.


Н.роме 'тог'о, тrm
AppDQma.i1:J определяет небольшои набор событий. соответr.;::r::вую­
щих рa.зJШ'ЩЬ1М МOMeн:raм ЦШUIа cyrцеamовmrия домена ЦРИJIожения (табл. 13.5).

1
--
550 Часть 111. Программирование КОМПОНОВО'IНЫХ блоков .NET

Табл ... ца 13.4. Основные члены класса AppDomain

Член Описание

CreateDomain () Статический метод, с помощью которого создается новый домен


приложения в данном процессе. Среда CLR сама создает новые
домены приложения по мере необходимости, поэтому вероятность
того, что вам понадобится вызывать этот член, близка к нулю
GetCurrentThreadld{) Статический метод, возвращающий ID активного потока в данном
домене приложения

Unload () Еще один статический метод, позволяющий выгрузить указанный


домен приложения для данного процесса

BaseDirectory Свойство, возвращающее базовый каталог, используемый при поиске


зависимых компоновочных блоков

Createlnstance() Метод, создающий экземпляр указанного типа, определенного в ука­


занном файле КОМПОl10ВОЧНОГО блока

ExecuteAssembly () Метод, выполняющий компоновочный блок в рамках домена прило­


жения, заданного именем файла

GetAssemblies () Метод, который LIИтает список компоновочныx блоков .NEТ, загру­


женных в данном домене приложения (двоичные файлы СОМ и С
игнорируются)

Load() Метод, используемый для динамической загрузки компоновочного


блока в рамках данного домена приложения

Таблица 13.5. События типа AppDomain

Событие Описание

AssemblyLoad Возникает при загрузке компоновочного блока

AssemblyResolve Возникает, когда не удается идентифицировать компоновочный блок

DomainUnload Возникает перед началом выгрузки домена приложения


ProcessExit Возникает для домена приложеНJIIЯ, созданного по умолчанию, когда
завершается родительский процесс этого домена

ResourceResolve Возникает, когда не удается идентифицировать ресурс


TypeResolve Возникает, когда не удвется идентифицировать тип

UnhandledException Возникает, когда остается без обработки сгенерированное исключение

Списокдоменовприложенияпроцесса
Для примера программного взаимодействия с доменами приложеfJИЙ .NET
предположим. что у fJac есть новое КОfJсольное приложение С# с имеfJем
AppDomainManipulator. в рамках которого определяется статичес}(ий метод
F'rintAIIAssembliesrnAppDomain (). Этот вспомогатеЛЬfJЫЙ метод использует
АррDоmаiп.GеtАssеmbliеs (). чтобы получить список всех двоичных файлов .NEТ.
выполняющихся в рамках данного домена приложения.

Соответствующий список представляется массивом типов System.Reflection.


А s s е mЬ
ly. поэтому fJеобходимо использовать пространство имеfJ S у s t е m.
Reflection (см. главу 12). Получив массив КОМПОfJОВОЧНЫХ блоков, вы ВЫпОЛfJяете
ЦИRЛ по элементам массива и печатаете пон.я:тное имя и версию каждого МОДуля.
[Лjj:1JtI j З. IlроцёсОЫ, доменЫ прилож-еНI1й., i(ObleKCTbl н ·xocTы. СLЯ 551
рьЬНс з·tаti с vt·id P:r-intАIIА. g ·$еmbliеsТГiАррDоmаiD (ЛРР[ЮJiwin a.q)
{
Assen1bly[] lОii' ОеdА.sэеffib liеs = ad.'3etAs.s sntb.iies();
Со:!I'te$сЙе.-WritеLinе ·("*~1.· 1<омпон.-о.вОЧ1'ШIе· б1l0КИ' в paMl\CaX {О'! ~""\П" I

ad.F:riendlyNam!;) ;
f·oreacll (lis semb1 у а in 1 oadedt\ssem1H i ез)
{
Co:t1so1.e. Wri teL.i.ne , .. -::;-i'O J 11, а. C;l2tNa1!(8 () . Name) ;
.ммя.:
Сол·sоlе .• Wri teLir:re("-> Версия·; (\)} \0", а.G>:>tNаше (i.VersiOl1-);

Теперь обновим метод M~io О. чтобы перед :)iЫЗ0.ВОМ PrintAllAsse.m bliesI[,)A


ppDo.main () поJIY'IИТЬ ссылку на теКJ1Щ'IЙ домен при.ТIOжения, 'ИСДQДЬ3уя с~ойство
AppOQ1'rlain_C'u t r~n,
Чтобы сДелать' прим:ер ~~ цкrересНЫМ" мещд Ма:!, n () OТJ<рывЩ':Т щщо сообщ~
Щ\Л WIndows For.rng (д.лдэтого·'С~R должна ззгрузwrъ lЩМЩШОВОЧfjые блоIШ
System.Win'dows.Fbrms.dll. System.DraiWing .dl1 и SуэtеJТI.dll, так ЧТО не эабудь­
те уcraRОВИТЬ C-ChIДIW на ЭТИ :KOMn0!-10ВI)ЧЛЬ1.е '()ЛОJi(И и еоотв<:теТ'веl'Ц'Ю ~з~ещcrь fЩ­
'БQР операторов us illg).

static VQid MaiГo(5triflg[) a.rg:s)


\
Соп.Sr>lе .Writеьiл.е (""**'** Ч~Д'есное :О'риj]о:жеgи~ A::ppD01'IJ.д. in ~"*"'*'\n");

1/ Ч'1'8вие и:вф!)pld<ЩИИ' PR !1'eJtyщe.l'O AppDozaain.


AppD~mail1 defa!lltl'.D= AppDomain. current.DOma.ih;
meS.Sag-еВох. S·how.( "ПРИ)'iет " '-j ~
РriпtАllAsЗЕ·mb.liеsIIlАррD=adrJ (.defaul tA.D):

СОГlsQ·l-е. Rea.d'I,illE: () ;

На рис. 13.6 ПО1'(~Зан СОOТIlетству.fPпnЩ ЕЬЩОД (номера версий у вас мотут быть
дрyrими).

РИс. 13,6. ГlереЧВf-IЬ КОМПОНОВОЧНЫХ блоков в рамках текущего домена П:Р14ЛQ)кеНIi1~


-
552 Часть 111. Программирование компоновочны)( блоков .NET

Программное создание новых доменов приnожения


Напомним. что один процесс может содержать множество доменов приложения.
И хотя вам в программном коде вряд ли понадобится вручную создавать домены
приложения. вы имеете возможность сделать это с помощью статического мето­

да CreateDomain (). Нетрудно догадаться, что метод AppDomain.CreateDomain ()


перегружен. Вы. по крайней мере. должны указать понятно е имя нового домена
приложения. как показано ниже.

static void Main(string[] args)

// Соsдаиие H08oro AppDomain 8 рамхах ~ехущеrо процесса.


AppDomain anotherAD = AppDomain. Crea teDomain (" SecondAppDomain") ;
PrintAIIAssembliesInAppDomain(anotherAD) ;
Console.ReadLine() ;

Если выполнить приложение теперь (рис. 13.7). вы увидите. что компоновочные


блоки System.Windo\",s.Forms.dll. System.Drawing.dll и System.dll будут загру­
жены только в рамках домена приложения. созданного по умолчанию. Это может
показаться нелогичным для тех. кто имеете опыт программирования с ИСПОЛЬЗG­

ванием традиционных подходов Win32 (скорее,• .оба домена приложения должны


иметь доступ к одному и тому же множеству компоновочных блоков). Напомним,
однако. о том. что компоновочный блок загружается в рамки домена nрuложенuя.
а не в рамки непосредственно самого npоцесса.

Рис. 13.7. Один процесс с двумя доменами приложения


ГЛqва 1З . Процессы, дрмены nриложен.иЙ, KOHHJKCrbl и ХDСТЫ OlR 553
Дал~. о~ратите IlнимаJ'Iие на ТО.' что домек приложения Sе:СОndЛ-ррD .оmаin
а,втоматически получает свою собственнУю копИЮ III S С О r 1 i Ь . d 11. ПОСi\10ЛЬ-·
ну ~TOT 1I:лю-чевой lюМпОНОВОЧflЫЙ блок автоматиqеС~~1 Эla.гружасется средой
CLR для каждого ДOM~ приложенИЯ. Это порождает следующий вопрос: ~Kaк
МОЖНО программно зarpузит.ъ КО:МПОfЮD.ОЧНЫИ БЛ:О1( в домен nРИJ10жения?"
O:rвeT: с помощью меТоДа.АррD'оrnа i Tl. Laad () (или. альтернативно. с помо­
щью АррDоmаiп.ехесutеАВSЕ!пLblу ()]. в предпол:ож~1J:ИИ. что вы СКОШ4РОВали
CarLibra:ry.dll в катал:оr приложеmm. АррОо,шаinМаttiрulаtQr.€хе. вы можете
За1рузить CarL,iOrary. dll в домен приложенин Secon d-АррDоmа-.iл. так.

stati o:: v oid Main(,s t ringl] a:rgs)


(

11 Зa.rpуsха CarLibrary .dll • ИОJП!Й AppDomain.


J;ppDdIilain атю'йtе:r-l\D =
J\,i?pD,jma.in. Creat eDomain (" 5е ссюqАрр·Dоmаi!1") ;
an:oth~rAl.J. Load ("'r;a:rLibrary") ;
PrintA1IAssemblieglnAppDomain(anotherA![I) ;
С спзЫе . Rea:dLi.\le- () ;

Чтобы аакреnить понимание взаимосвязей междУ процессами. дом.еНЮdИ npй­


лОЖёния и коМIiОНОВОЧНЪ1МИ б,Т(оRaЫИ. рассмотрите р'Ис . 13',8. на :котором npед.­
етавле'Па диаграмма внутр,еннето устройства только ЧlО построенноrо проц~сс~
AppDomainМanipl1lat01:.exe.

АррDОШ&ibИaniРUlаtоr,ехе

j mSGorlib. dl1 ", ,. m$C,otlib. dll

' ~j
:. =========~I
5ystern, dll ,
j,.::=:==========~
1: ;u"ЦЬr-аJ;,у ,dll

System. WIТ! dоw s. F01;ms .,dH

,,--_Арр_'.__l)_o_ma_i_-n_МIffi_i_р_l.й_а_t_Qr_,_е_х_е--' '1

Программная выгрузка доменов п'рипожени'я


Важпо понnмать. что среда CLR не ПОЗIlОляет BJ:ilrPY$aTh отдельные компоно­
вочные блQIЩ .NET ОднCU(O. исщо.льзуя метод AppDoma iл.IJnlр~d ()" вы можете И3-
бир.атещщо :выгрузить домен приложен~я из <)б~емлЮlЩе:го npоцесса, При .эТОМ до­
мен придоще:ния :выгрузит по очереди каждый Щ)МД0Щ)ВОЧ,ный блOIi.
554 Часть 111. Программирование компоновочных блоков .NET

Напомним. что тип AppDomain определяет набор событий. одним из которых


является DomainUnload. Это событие генерируется тогда. когда домен прило­
женил (не являющийся доменом, созданным по умолчанию) выгружается из со­
держащего зтот домен процесса. Другим заслуживающим внимания событием
является событие ProcessExit. которое генерируется при выгрузке из процесса
домена, создаваемого по умолчанию (что. очевидно. влечет за собой завершение
всего процесса). Так. если вы хотите программно выгрузить anotherAD из процес­
са .~ppDomainManipulator .ехе и получить извещение о том. что соответствующий
домен приложения закрыт, можете использовать следующую программную логику

событий.

static void Main(string[] args)

/1 ПрИВliSХ8 IC соб5I'rИКI DomainUnload.


anotherAD . DomainUnload +=
new EventHandler(anotherAD DomainUnload);
11 Теперь ВЪП"руз ха anotherAD.
AppDomain.Unload(anotherAD) ;

Обратите внимание на то. что событие DomainUnload работает в паре с деле­


гатом System.EventHandler, поэтому формат anotherAD_DomainUnload () требует
следующих аргументов.

public static void аnоthеrАD_DоmаinUлlоаd (object sелdеr, EventArgs е)

Console. WriteLine ("** *** Выгрузка anotherAD! * ** Н\п");

Если вы хотите получить извещение при выгрузке домена приложения, соз­


дан:ного по умолчанию, измените метод Main () так. чтобы обработать событие
ProcessEvent. соответствующее домену приложения по умолчанию:

static void Main(string[] args)

AppDomain defaultAD = AppDomain.CurrentDomain;


defaultAD.ProcessExit +=new EventHandler(defaultAD_ProcessExit);

и определите подходящий обработчии событий.

private static void defaultAD_ProcessExit(object sender, EventArgs е)


(
Console.WriteLine("***** Выгрузка defaultAD! *****\П");

Исходный код. Проект AppDomainManipulator размещен в подкаталоге, соотвеТСТВУiOщем главе 13.


Гл~ва 13', ПроцеОСbl j AOMEtHbl' ПРW10же»мй, коюексты и )«(i)CTtIICLR 555

Границы контекста объекта


Итак . вы мotли убедиться. что домены ПРИЛQжения - эrrо логические разделы
I! рамкroc ЛРЬЦj::-сса. npедназначеlШЫе ДJlЯ sarрузки ~ОМШНfОllОЧiniIX блоков . NEТ,
ДQме;н nриложения. в свою очередь. МQЖНО делить дальше на Еонтеш~тные облЗС'Г1J
GO своими границами. В СуЩНОСТИ~ контекст .NEТ обеcnечивает домену I1pП)Iоже­
l.llЩ возможность ооздать ~отделъну1О комнату" для дaшroГ0 объекта.
ИСI1@ЛЬ~Я Еонгекст. среда CLR может гa.paнTHpoDaТЬ, Ч'То объекты. которые вы­
ДllИГaIOт спiщиaльнвIе требования 11 среде -выполнения. будУТ обработaIfЫ нqдле»щ­
щим образом и:в нужном порядке, ПОСIЮЛЫty Д1IЯ Э'I'ОТо Срlща использует перецат
Ii\ЫЗОВОВ, IIeJ?есекающих rрalIШJY контекста. Слой перехва'l'а позволяет среде CLR
I«эрректир.овать тену:щйе :вызовы. .метОДС!В в соответствии с ltoI-lтI%стно-зцвисимы­

ми УСТВROВRа:МЛ данного объекта. НапрJirМф, если ВЫ оnредеЛИ1:'е тип ЮI;lсса С'#,


требующий автома.тИчtСЕЬЙ ПQддеря.."КИ МНожества П0ТОКО:В (иcnользуя атрибут
[Synchr on i.z atHHJ)) , :I'O среда CLR при еАО размеЩе.вии соадаст "СИНХРОНЩlИро­
ванный KO'El1"eRcт".
Точно так же. как процесс оnpедеЛJiет создаваемый по умолча:нюо ЩJме:н пIш­
ложения. важдый домен приложениg имеет создаваемый [10 уМолчанию ыо;нтекст.
этот создаваемый по умолчанию контекс'Г (на lЮТОрЫЙ HHo~a ССЫЛaI()ТСЯ.• JЩК на
К{)Ю'ТIl?JCCirI! О. llОС1ЮЛЪКУ он всеГДil оказъmаетсSI первым Еонтек<;том.создаваемым в
домене 1IpШIожеЮ1f!) применяется ДЛЯ тех объектов .NEТ. .KOl'OPbH~ не Щ\!fеют ни­
IШКИХ особыХ или уникальных конrекС'Гнш требовatmй. B1;I1 ВПРЩlе ожидать. что
подавляющее большинство оБЪе1>;ТI)В .:NEТ должно загружа'Гься 11 лонтекст O . KoFД<!
среда CLR определя:ет НОВЫЙ ol5ъeIrt имеющий спеЦйЮIЬНЫе требовэния. 0О3ДaI01'CR
новые rflЗШJЦЫ кон'ГеКС'1'З В рамках объемлющего домена ПjnIJlожеIЩЯ . На рис. 13.9
IЮl:tазана схема взаимодействия Щ'Юцесса. доменов прндожеция:и контекстов.

ПРоцесс .НЕТ

Defau1 tAppDOIIIiIin AppD0IIIai1'l1 1, АррDoшain2

~ !f~ !tnmIiШr

I Гt0У~1O ] I nO ·.Умo:nч8Ю'll&
I I по yмr:ifP'I8НИЮ ]
~1 .ICoнr~1
I I 1 I
Кoктeкcr~ ] КotmIкct2
I I I
РиО', 13.9, ПРОЦООСЫ, AOMt)HЫ ПРИ11ОжеtiJl1R и границы контекСта

Контехстно- независимые и контекстн'о-связанные типы


типы . NEт, ноторые не предъявляют 1:IИКЮtЩ crrециальных контекстных· требо­
ваний. наэ.ывC!lОТСЯ: fCQНЛIе/Q':mшгl-lеЭQдUСШИblМtl ()б'Ь<:~a,м1:I. 9'm объеl\ты доступны
из любого :места в PaмIiax соответствующего дом.еJ;Jа приложе:Ния, беа:кaxmr: бы то

1
--
556 Часть 111, Программирование компоновочных блоков .NET

ни было особых требований к среде выполнения. Построение KOFltekctho-незави­


симых объектов не требует больших усилий. поскольку ничего специального делать
не приходится (в частности. не требуется наделять тип контекстными атрибутами
или получать производные базового класса System.ContextBoundObject).
11 КОИ'l'ехстио-иеsа.иCИlO!Й об'Ъе1t'1' sагружае'l'СR 8 ХОИ'l'&ХС'l' О.
public class SportsCar{}
С другой стороны. объекты. которые требуют контекстного размещения. назы­
ВaIОТСЯ kohmeKCJ7UiO-СВЯ3анными объектами, и они дол;жны быть производными от
базового класса System.ContextBoundObject. Этот базовый класс закрепляет тот
факт. что соответствующий объеI<Т сможет правильно функционировать только в
рамках контекста. в котором он был создан. С учетом роли контекста .NEТ должно
быть ясно, что в случае., когда контекстно-связанный объеI<Т оказьmается в несоот­
ветствующем контексте. в любой момент могут возникнуть проблемы.
Вдобавок к необходимости получения типа из System.ContextBoundObject,
контекстно-связанный тип будет наделен рядом специальных атрибутов .NEТ, на­
зываемых контекстными атрибутами (что вполне логично). Все контекстные
атрибуты получаются из базового класса. System.Runtime.Remoting.Contexts.
ContextAttribute:
public class System.Runtime.Remoting.Contexts.ContextAttribute
Attribute, IContextAttribute, IContextProperty

public ContextAttribute(string пате);


рuыlсc string Name ( virtual get; )
public object Typeld { virtual get; }
public virtual bool Equals(object о);
public virtual void Freeze(
Sуstеm.Ruпtimе.Rеmоtiлg.Сопtехts.Сопtехt newContext);
public virtual int GetHashCode();
public virtual void GetPropertiesForNewContext (
System.Runtime.Remoting.Activation.IConstructionCallMessage
ctorMsg) ;
public Туре GetType(); public virtual bool IsContextOK(
Sуstеm.Ruлtimе.Rеrnоtiпg.Сопtехts.Сопtехt ctx,
System.Runtime.Remoting.Activation.IConstructionCallMessage
ctorMsg) :
public virtual Ьооl IsDefaultAttribute();
public virtual bool IsNewContextOK(
System.Runtime.Remoting.Contexts.Context newCtx);
public virtual bool Match(object obj);
public virtual string ToString();

Поскольку класс ContextAttribute не является изолированным. вполне воз­


можно строить свои собственные пользовательские контекстные атрибуты (для
зтого следует получить класс, произвоДНый от ContextAttribute. и переопреде­
лить неоБХОдJiМые виртуальные методы). После зтого вы сможете создать пользо­
вательский программный код, отвечающий на контекстные установки.

...
Глава 1З. ПроцеССJ>1, домены nр.ИЛDжениЙ, контексты и косты CLR 55.7
3аМ81f8НИ8. В ЗТО~ ICI1Иге 101& ра.СQматриваютоя П0дро6~JОСТИ I:оздан'ия ЬОJlЬЗователI:ICКИ)( ",онтеКСТОII
обьвlCТОВ, но если вы заинтересованы узнать 'об этом БОЛI:iШВ, nРО'lmайте Kl'J)I1ry App/ied .NEТ
Aitr/butes (АрrеSЭI 2003),

Определение контеКСТНО"СВJlЗ8ННЫХ объектов


Чтобы олр~д~литъ класс (SportsCarTS), автоматически подцерживаюIЦJilЙ'пото­
новую безопасность. без добавлеюш в него СЛОЖНой ,IIOгиlЩ синхронизЩцtи ПОТОR8
при реализации членов. следует ваять ,·Объект. производный от Сопtе~t:В()1J1пdОЬj-е.сt,
И применитъ 'атриrJyr [.syn·ch,roi~j,zat ion]. KW{ показано ниже.
\lsing ,sуэ tem. RuntiJ1'.e. R:emOting. c.dnt-ext.!>;
11 Э'l'O'1I xoa'1'8KC'1'HO-оа • • •1IIWЙ 'Т,JШ БУЦ8'1' s&rpУ'К.И'1l0Па.хо
1/ в СМВХРОNJf_rq>О8аиаОIl('.I! . •. llИоrОПО'1'о1oiItоllC) ~QИ'1'е~с'1'••
[Synchtoniz& t,ion]
public: с:lаs:э SpPr'UICarT:$ ~ СоntехtвоuлdОЬj ect
{}

1)щыI С ~rрибутом [Sync:rironization] загружаютен:в ROHTeRccre сохране­


НИll Ш'IТI)RОВ. С учетО'М специалъН1.1Х контекстуаЛЫ;lЫХ требований типа .'клаСС.а
MyТhreadSaf~Obj.ect npедСТШll:il:е себе те пр~блемы. Koтop.fЦe ДОЛЖНЫ воЭifiilКну-гь,
если размещен:ный объе1tТ перевести из синхрониэированнРТО l\:онте:кста в Hectm-
ХРОНИЗИР0Ван:выИ. Объект в,цру! перестанет быть защищ~в отношении ЛОТО­
,иов и nр~раТИтс.нв noтенциальноrо нарушитеД8 целостности данных. ПОСRОЛЬky
другие ПРТоRИ могут llЫтзтъСя взаимодеtiC'rво:ват~ с этцм, ссылочным объектом (1'е­
пер:ь уже не СОХРa:IiЯЮIЦИм ПСlТ(жи) . Для raрантии того, ЧтО сре-.да СIЛ не переме­
аrит об1>еjtТЬi SportsCa tТЗ за рамки СИНХРО.IOlЗИРОВaEl-101'9 контекста, Достаточн.CJ
взяrrь ol$"pe~t; npаизвоДНЫй ОТ ContextBouodObjqct.

Проверка I(OHTeKCTa объекта


Из т-ех ПРlЩожеНRЙ.. :которые вы построите сами, оченъ немНогие могут' потре­
бовать. программнэrо вваимОДеЙ'СТВИfI с .контекстом. 1'Щ вот Qaм прамер дли. иллю­
с.трации црдхода, о котором идет речь. Создайте :врвое ~онсолъное nPИЛОЖение С
именем С0пt~хtМа&iрu1аtоr. Это приложеllие будe:rопре)l.f'.JIIiТЪ ОДИН ЖJНте-кст.но­
неэавJicимый класс (SportsCar-) и ~ДИН контексшо-сЩJЗаняый (:SpcrtsCar1'S).
-ueing Sуэtеm.R1.lЛti1riе.Rеmьt iп q.С о ntехts; /1 ДmI 'l'КП& Context,.
using SY$tem .'Ш\.rеаdiпg I J/ д.rnr It'J!lПА '1'hread.·
/1 ~ SPO%'t8Cu к. мке8Т сП8ци.:зu.JDIX ~()~.xe!l!~ lJP8е08iU1Ий
// и е~и .аЖ'р~ • paмkaX ~01J'1'eJCCtT&, C:a~дa.a. .o%'o доменок
11 lI1pJInО_1ЩJI по VXО'n'l&ItИID.
pцbl ic 'C~a,B .a SportsCa!:
(
puplic SportsCar()
(
11 Чnюсе и~фоpll&цmJ н Ul8o,ц ·иден'n!Фmcа.'.I!QР" ~ОИIНКО'1'а.
Con.t-ext ctx = l'h;rea.d. Сu:r:rепtСФ!1t~хt';

1
558 Часть 111. Программирование компоновочных блоков .NET

Console .W ~iteLine("[OI объект в контексте (ljЦ,


this.T oString(), ctx .ContextID);
foreach(IContextProperty itfCtxProp in ctx.ContextProperties)
Console. Wri teLine (Ц - > Свойство контекста: (О 1",
itfCtxProp.Name);

11 Тип SportsCarTS 'L'pебует заrpузlCИ


/1 в синхронизированном хонтехсте.
[Syn chronization]
public class SportsCarTS : ContextBoundObject
(
public SportsCarTS()
t;
{
I1 Чтение информации и JlЫВОД идеитифихатора хоитеХС'1'а.
Context ctx = Thread.CurrentContext;
Console.WriteLine("{O} объек т в контексте (11",
thls.ToString(), ctx.ContextID);
foreach(IContextProperty itfCtxProp in ctx.ContextProperties)
I
Сопsоlе.WritеLiпе("-> Свойство контекста:
itfCtxProp.Name);
(О)",

I
Обратите внимание на то. что каждый конструктор получает тип Context от
текущего потока выполнения через статическое свойство Thread.CurrentContext.
Используя объект Context , вы можете распечатать информацию о границах кон­
текста , например. значение ID контенста или значения дескрипторов . получеюIЫX
через Context.ContextProperties. Это свойство возвращает объект, реализую­
щий интерфейс IContextProperty, который обеспечивает доступ к дескрипторам
с помощью свОйства Name. Теперь обновите метод Main (), чтобы разместить по эк­
земпляру каждого из типов класса.

static void Main(strl ng[] args)


(
Console.Write1ine("*** Чудесное контекстное прил ожение ***\п");

/1 При создании объех'1'JoI будут отображать инфОРИ;ЩЮОХОН'1'ехста.


Spor tsCar sport = new SportsCar() ;
Console .Wr i teLi ne ();
SportsCar sport2 = new SportsCar();
Console .WriteLi ne () ;
SportsCarTS synchroSport new SportsCarTS(I;
Console .ReadLine();

По мере создания объектов конструкгоры классов отображают раздичные эле­


менты информации о контексте (рис. 13.10).

r
Глава"IЗ. nроцессы, домены прилож~ний, КОfJТfЩПbl Jd XO(JТЫ CLR 559

Рис. 13.1 Q. Исследование контекста объекта

для JI\JП"сеа SportsCar не бьmукаэ2Н a-I'РИбyr IЮНТe:J<CТа. поэтому ореда CLR раз­
мещает spo.rt sport2 в кoнreKёTe О (т.е. в KoнтeКC"J:e. СQздашюм цо умолчанию).
tI
ОДНaIW объект Sport; sCarTS аатружаетс:я. в овои уншщ)lыIыIe КОIn'екстуадЫfше гра­
НИДЫ (:КOTOPhIМ назначается и,центифИftатор 1). hОСКОЛЬку для этоrо ИОWГeRСТНQ­
СВНЗaIOlоготида был указан атрибут ['Sупсhrоni,zаUоn].

ИСХQДtlЫЙ код. Лроект СоntеitМапJрulаtог :размещен В подкаталоге, соответствуЮЩеМ таве 1З.

Еще несколько слов о процессах,


доменах припожения и контекстах
к этому MQМe.uтy JiЫ ДОJlЖЩd лучше IЩНИМать, как среда CLR обрабатывает КОМ­
nOНQВОЧНidе блОIЩ .NEТ. ВОТ ЩJ ЧтО следует обрсцитъ nнимauие.

• процесс .NE'Т может содержать 'QДI.цI иn;и несколько доменов прлложения.


ltaждый домен приложения МОЖ~Т прщшть любое число свяЗа1lllЫX RОМПО­
ПОБОЧНЫХ блоков .N"EТ и незавцси:N1O загр~ться. и ЕЫГJ)YЯШТЬСЯ средой CLR
(vши npоrpа.ммистом с ПО.1'dощью типа System. AppDo,ma.in).
• Любой .домен Тipй.ложенйя СОСТОИТ из ОдЦРГО ЩIИ Нeckолъкихконте.Н:СТОВ.
Ислолъвуя KOHтeKCTbi. среда CLR може,т поместит!'> объект со ЦооециЭ1rьНЫМИ
треБОБанилми~ в логича:liИй. }(ОRтейнер. чтобы гарajrтировать выпо:лнение
ЭТЮ!'. требований в ·среде выnощeюm.

Если upеДЫ,ЦуЩее обсуждение кажется Вам сJIИllШОМ сложным И далеким от


прВRТИRИ. не ВОJIНYЙтесъ. По больпщй чаcrи среда 'Въщолнекия .NEТ автоматически
разрешает вопросы npоцеССОБ. AOMeiiOB .цриложеНИЙ.и контекстов. не требуя ваше­
то ВМешaI'!Шъtтва.Тем не :менее предста:вдеННШJ: з.десJ;o информация обесhечивает
"твердУЮ ОСНоВу" д;n:я ПОШlманиа llpИПЦЩЮВ lIЩотопоточиого программиро:вания Б
рамках шш.тформы .NEТ. Но перед, тем. 1фR перей'Т1'J 11 .изучению ПростраIi.с.тва имей
Sуst:еm.Т1Jrезdiпg, 1VlliI иоnытаемсI.l' выяс:нвть. RaК C.~ среда СШ обрабатывается
опеРaL,1'И.О1iНОИ' системой Wln.'32. .
560 Часть 111. Программирование компоновочных блоков .NET

Хостинг общеязыковой среды выполнения


Для конечного пользователя запуск выполняемого блока .NEТ доступен с по­
мощью простого двойного щелчка на соответствующем файле *. ехе в окне про­
граммы Проводник (или активизации соответствующего ярлыка). Но вы должны
помнить из главы 1. что каркас .NEТ Framework (пока что) не интегрирован непо·
средственно в ОС Windows, а опирается на ОС. Во время установки Visual Studio
2005 (или .NEТ Framework 2.0 SDK) на вашу машину устанавливается и окружение
среды выполнения .NEТ (включая все необходимые библиотеки базовых клаССQВ~
Также напомним. что Мiсrоsоft предлагает свободно доступную программу уста­
новки (dotnetfx.exe) среды выполнения .NEТ, позволяющую настроить мщпину
конечного пользователя на поддерж~ компоновочных блоков .NEТ.
Поскольку ОС Windows не имеет встроенных средств понимания формата ком­
поновочных блоков .NEт. полезно знать. что происходит в фоновом режиме. когда
активизируется выполняемый компоновочный блок . В ОС Windows ХР основными
шагами будут следующие (вспомните из главы 11. что все компоновочные блоки
.NEТ содержат информацию заголовка Win32).
1. ОС Windows загружает выполняемый двоичный файл в память.

2. ОС Windows читает встроенный заголовок WlnNТ. чтобы определить (по фла­


гу IМAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR). является ли двоичный файл
компоновочным блоком .NEт.

3. Если образ является компоновочным блоном .NEт. загружается mscoree.dll.


4. Затем mscoree.dll загружает одну из двух реализаций CLR (mscorwks.dll
или mscorsvr.dll) .
5. В ЭТот момент ответственность за въшолнение "прини;м:ает на себя" среда
CLR. выполняющая все связанные с .NET задачи (поиск внешних компоно­
вочных блоков. выполнение проверок безопасности. обработка СIL-J(ода.
сборка мусора и т.д.).

Итак. mscoree.dll- это не сама CLR (как говорилось в предыдущих главах).


Хотя вполне возможно идентифицировать mscoree .dll с реальной CLR. на самом
деле указанный двоичный файл - это "развилка" на пути к одной из двух возмож­
ных реадизаций CLR. Если соответствующая машина использует один процессор.
загружается mscorwks.dll. Если машина поддерживает мультиnроцессорный ре­
жим. в память зarружается mscorsvr .dl l (это версия CLR, оптимизированная для
работы на машинах с несколькими процессорами).

Параллельное выполнение CLR


·Копнув" чуть глубже, МЫ увидим. что платформа .NEТ поддерживает парал­
лельное выполнение. т.е. на одной маШине можно установить несколько версий
платформы .NEТ (во время создания этой книги были доступны версии 1.0.1.1 и
2.0). Сам файл mscoree.dll размещается в подкаталоrе System32 каталога уста­
новки Windows. Например. на моей машине mscoree.dll "проживает" в каталоге
С: \WINDОWS \ sуstеmЗ2 (рис. 13.11).
Гпэва 1,3 , ПроцеССbl, ДOM~I1Ы Щ)J.1110жениЙ, "КоНтексты И XOCTb/CLR 561

"" Edt \1Iov ~ 1'_ .. .-


C !illi O С, 1 PS-d! t. '~ 1 IШJ.
r6t;;;oow;;;i;-;a2-~-'- . ' -"--'.-'~
' ----.~~:~ :C~.111 ~

Рис. ,3,11. Ф~~тsсm.ее,dll Находится В 'КЭТ8;I10ГВ sуstеmЗ-~

ПQсп.е загрузlUi ms с;: а!: е'е . dl1 по реестру еис~МЪ!1. Wln32 (да, па p~ecтpy эmoй
СПQтеМЫ) ВЫЯСНЯетс& 'номер последней из устанf,)вл~нных версий 'И путь устанОВ­
ю, .NET Fгame:work (ИСПОJIЬзуетс.я В-е1'вь HKEY_LOCAL_MACHINE\Software\MI'O rosoft\
.NШrаmеwоrk, рис. 13.1'2).

"" " ",'1/ f ,1"", 1_ IIr1ilx I

Рис. 13,12. аЫRСfolf;!ние версии и пytиустановки пllатформ:ы ,tiJET

После щrредмения версии и: пути установки адатформы: .NEТ ~ П8.МН'l1Ь зarружа­


e'rCЯ ыуЖнав: версИ.R Il1scor:Wks~dl1/mscorS'l/r .dll. На \l40ей мщmmе корневым пу­
Тем уств.новки IIJIЗтформы: .NEТ является с: \W1NDOWS\Mi,crosoft, NET\Framework.
В у:кaщunrQМ RaТВЛО1'е есть специальные ПОДЮlтaJlOТи дll.Я ..NET версии LO.l.l и (на
ВреМя COOД~ кяиrи) т~еЙ..версии 2,0 (см. рис. 13.13, вantияомера ВерсИЙ.мо­
гут бытр другими).

Загрузка конкр,етной версии CLR


Когда rnscoree.dll onpеделяе-r (с помощью реестра систе~),· накую версию
1I!$!;()'ТwkЭ , dl1/msсоrвrv.dll эarруэить, читаewЯ ТЗJ<же раздел Роliсу (Политихз)
ветви НКЕV_l,.ОСАL_МАСНIN.E'\Sоftwаге\МiсrosоЩ.NЕТFгаmеwork реестра. Б этот раз~
де.лааписывается информация обtювлеtiий CLR, НО1'орые M-Oryт в.ьmom-IЯТься с
безопаСДОСТI!Ю. нanpиыер. е.сли~апускаеТСJl xoмnoHoBo~ БЛОR. который был
построен с использованием .NEТ версии 1.0.3.705. msаоrе€i,CЦl узнает из файла
И:ОJIИТИI91. что ВП0лне I"iеэопаоно эarр~т.зитъ версию 1.1.4322.
-
562 Часть 111. Программирование компоновочных блоков .NET

". v 2 О 4iJf,I) 1 GJj'6I~


Ah! Edt Vll!w f!lvorllм . ТooIs ti@IP

о вad< -о l.t .]c) se...d1 FoIders!m]-

,Мdt~ ri5'С:\~WSlМiаosoМU\ff_ork\VLо.'Ю6(J7~~ " ""~ ~" ~""- "-,~} с1 Ga


~_.~_~_. _ _~_ .. _ ... , .~. _ ..••__ ~~._._... ~. :E
Fo!der. Х mSCDrS'JW.Oxe mscortim.dII ..

13 10 Мi<rosoft.NEr
S ·~O Fr amework
0,,1.0.3]0,
Ii Q _l.1.4322
IE Q "2.0.4ОБО7

Рис. 13.13. Файл mscorwks .dll версии 2.0

Все это происходит незаметно в фоновом режиме и только тогда, когда извест­
но. что обновление обеспечивает правильное выполнение. В редких случаях воз­
никает flеобходимость заставить тэ coree. dll загрузить КОНlсреmную 13ерсию CLR.
и тогда вы можете использовать ДЛЯ зтого файл *.config клиента.
<?xml version="l.O" encoding=»utf-8" 1>
<configuration>
<startup>
<requiredRuntime version ="1. 0.3705" />
</startup>
</configuration>
Здесь элемент <requiredF.untime> указывает, что для загРУЗRИ данного компо­
ново"шого блока следует использовать только версию 1.0.3705. Поэтому. если на
целевой машине fleT полной инсталляции .NEТ версии 1.0.3705. конечный пользо­
ватель увидит окно с информацией об ошибке среды выполнения. показанное на
рис. 13.14.

о
111 "'" tI1iS appb1ion, YCU ~st mцotnllll_ of tho! fiIIOiOМng _ _ af tho! .I'EТ ~k:
,,1.0.3;105
СantoЮ 'fOAJf i!1P1111a11ian ~ fiэt i'IS~ m.tobtalning tho! ~ _ of """.NEТ Fr"""""orI<,

Рис. 13.14. Элемент <requiredRuntime> порождает сообщение об ошибке


среды выполнения, если указанная версия CLR не установлена]

] Сообщение гласит: "Чтобы въmолнить это приложение, установите одну из следующих вер­
сий .NEТ FrameWork: vl.О.З705. Обратитесь к разработчmty приложенИfI для получения ин~
струкций по поводУ установки нужной версии .NEТ Framework."
ГЛВflЗ 13. ПРОцес.с;ы, домен'ыгтрильжений, коНтекстЫ и )(OCrl>! CLR 563

ДОполнительные XOCТbI CLR


Толыю ЧТО ОIШсШПlЫЙ процесс обознаЧ)LI'1 ОС;tЮВ'liЫе шаги. :преДПР'ИI:lИМa.eмые
опеpanиОННОI{ системой Wl.пdо\vs для: ХОСТИ,FП'3 CLR [J.O умолчlШFiIO, кorдa запуска­
ется в.ыnoлня.емыЙ комnоновоч.8ШЙ б.лОI<,. HQ МiсroврИ: предлагает множество Пl)И >
ложеНИй, Еоторые могут действоnзть в обход ИCnOЛЫJyемого по умoлчai-ШЮ поведе­
ния. используя flPогра.м..мную зarрузку CLR. Нацример. Мiс rosoft .Internet Ex:plorer
может аarружатъ своимn bctpoeHl-U)ЦI4И СреДСТI!амИ' ПОJlыювателъёКnе элемеНты

управленин WIndows Fоnnэ lvnpав.Щ;Iем.ыЙ ЭЮ3ИJщлев'Г теперь уже уетаревfiIиX э'Ле­


.мenTOB управления ActiveX). ПоеледнщI 'Версия Mkro$f)ft $QL Server (с l'.ЬДОВым на ­
званием Yukon и официrotbПЫМ названием SQL $eIV~r 2005) также спосо6на. осу­
щесТВЛ.ЯТЬ непосредc1'вe1lный .хоетинг CLR.
HaKaH~Ц. Мicros@ft определила набор пв'терфеЙс.:QВ, позволяющИх разработ­
чикам строить их собствеШШIе ПОЛЬЗОЩiтeJlЬСЦJj"е XocТbl сш. Эта можно сде.J1aТЬ.
ИСПOJlЬ3уя соответствующий прогр~ый l,{ОД С/С++ или 'i5ибли.оте~ СОМ-Типа
(mscor~e. tlh). Хотя сам процесс uостроения ПОn:ь;30затеЛЪСJ<ОГ6 хоМ'а CLR ИСЮIЮ­
чительно пр ост (особенно при иcnmlЬ30~а.ЕШИ б:иб;:.rиотеки: СDМ-тиna}. эта тема -вы­
ходит за рамки Нantего обсуждения. ЕCJШ вам НУЖf.J;i!. ДОПШIlmтелъная. инфорМэ.дщI
по данному ВOQ}JОСУ, IiСети вы можете пай lИ мнощеc:FВO статей на этуreмy (просто
выполните ПОИСК по юоочу MCLR ЬО$ts ).
Й

Резюме
Целью этой гдавЬТ было выяснение Тмо. какобрабатыва.ется ВЫnOJIНи.е~I,Й об­
раз· .N.ET. Вы ИЪJМИ !ЭО3МОЖВ:ОСТЪ убедиться в '1'ОМ, что ~e привычно е повят.ие
проце~са Wir132 было 1щутренве иЗменено с тем. чтобы адаптировать его к тре ­
бованиям CLR. ОТДf'J~Ы'J,:ый пр(щ~сс (коТОрЫМ MOmнO nPОJ:'раммноynравлятд с по­
МQЩЬЮ ТИllq S,ystem.Di.,.grJ()st,j:cs.Process) теперь компонуется из множества .до­
менов црилежеJШЛ, И]IoJ,еwщих изолИрованные и !reЗавясимыf' rрaJUШЫ В рамках

этого процесса, Один црЬцесс может содержа.ть множество ДОМБНОВ приложе:юm,


IЩ}цдый щэ которых може"l' обрабатывать и въmОлнлтъ люб.ое число СВЯ3aюJЫX ROM-
понQвочных. блоков.
Кроме ТОГО, 'К8..1fЩI:!Щ домен приложения может содержать любое Чlfсдо контеlt."
став. lkпоm.зуя 31'01' ЩJnoЛIiите,лыtЬтИуроnенъ изоляции nmol!. t.,'PeAa CLR может
ffip.щтnpовать, что объеl\"ТЫi со специальными требованиями будyr обработаны
1\Оррспно. IЛaва З<Цteрщаетс.я раССМО'lJ'ением ,деталей ТOJ10, ь.."аК сама CLR обраба~
тъщаеТСЯ оперaцl':l.ЩЩОЙ системой Win32.
ГЛАВА 14
Создание
многопоточньIX
u
ПРИЛQ, жении

В
мы .NEТ
предыдущей rлaве мы рассмотрели roaимосвязъ меящу процессами. домена­
ми DpИJ1ожения и :коите:КС1'ВМИ. В этой MJ>l ВhlЯcнm.~. ка:к 11 ра:мнах JJШJТфор­
crp01fib мноrОПОТ(Jчные ПРИЛQже:юm и как в УC.IlОIЩЮ!: :множества потоков
гарантироватЬ целоcrность совместно и~полъзуемых ресурсрв.
Наше обсуждение снова начнеТСIfJ с раС'смотрения ТЩJа деле'f'ата .NET. что ­
бы rIpИйти 'IC пониманию его внутре:н:uей поддерЖR1-1 асюаронных вызовов ме­
ТОДОВ. Вы УВИДИ1'е. что такой подход позволяe'r а:&томаТJj'чесни вызвать метод во
вторичном потоке ВЫПD.'JНения. Зате'М мы исследуем "J;~IIbl прос-rpаБства имен
Sуstе·m.Тhгеаdiпg. ву.дет рассмотрено множество ТjIПО~ {1'.iJ read. ThreadSt.a:rt
и т.д.l. noзвотnoщих с лег.костыo создават& дonщIIщтe.rrы;[bl~ ПQТО1Ш. Конечво. слож­
носТь разработки МИQrопоточных при.ш)ж~ ЗaJЩ19чаетСА не в создании поroиов.
ав гарантии того. что ваш npоrpaммIO;IЙ J(OlJ,' будет иметь над,eжgые средства обра­
ботки вонфликтов при нощуренmОМДQстynек общедос'I}'IIНЫМ ресурсам. П(!)этому
завершаете!! тава рассмотреЮfем раЭJl)fЧ»blX примитивов синхронизаЦии. пред­
лагаемых каркасом .NEТ Framework.

Взаимосвязь процессов. доменов


приложений, контекстОВ и потоков
в предыдущей гдаве об~УЖДaJ'ЮСЪ 1!онятие nommro. IЮТ0рЫЙ бblJl определен. 1ЩIC
IJY"П:1 иопо'Лщ::;ния в рамках въmОJllШемоrо npиложениs. И ХОТЯ: мноrиe лрилощсниа
.NEТ имеlO'Г ТOJ,IЪКoOДКН поток н, тем не менее.. оказываются еченъ nолеЗНЫМИ,.
!IерВИ'ЧН!:iIЙ потОк компоновочного бло1t:a (порождаемый средой СШ при BъmoJItIe­
нии Main ( )) J\Щ~ет создавать вторичные ПОТОКИ ДЛЯ рещеиия ДОПОiТI'FJИТeJIЬНЬD! за­
дач , Реа.лизуя допoлнИ'tельные noтшm. вы можете строить прmrюжеmm:с л:учшим
ОТКJJ:ИКОМ 1;Ia действия пользователя (пе не обязательно БО.jIее б1:UСТРО ВЫnОЩЩЮ­
щие своизад:i).~).
Пространство имен 3ystem.Тhre.ading содеРЖ1tт раЗ.lщчные 'ТmIЫ, позволяю­
щие созд~а:r~ мноtoпоточные ПрИложеmm. Основным т:идам эдесь можно счи-

1
566 Часть 111. Программирование компоновочных блоков .NEТ

тать класс Thread. поскольку он представляет данный поток. Чтобы программно


получить ссылку на поток, вьшолняющий данный член в текущий момент. просто
вызовите статическое свойство Thread.CurrentThread.
private static void ExtractExecutingThread()
{
11 Получение потока, ВШIОЛНtlDЦeго
11 в данный момент данный метод.
Thread currThread = Thread.CurrentThread;

Для платформы .NET не предполагается прямого однозначного соответствия


между доменами приложения и потоками. Напротив, домен приложения может
иметь множество потоков, выполн.нющихся в рамках этого домена в любой МОмент
времени. Кроме того, конкретный поток не привязан R одному домену приложения
в течение всего времени существования потока. ПОТОRИ могут пересекать грани­
цы домена приложения. подчиняясь правилам потоков Win32 и целесообразности
CLR.
Но, хотя активные потоки могут перемещаться через границы доменов прило­
женил. в любой конкретный момент времени один конкретный поток может вы­
полняться в рамках только одного домена приложени.я (другими словами. один по­
тов: не может работать в нескольких доменах приложения одновременно). Чтобы
программно получить доступ к домену приложения, содержащему теEJ1ЦИЙ ПОТОR,
следует вызвать статический метод Tl1read.GetDomain ().
private static void ExtractAppDomainHostingThread()
{
11 Получение домена nPИJJоженив, содержащего текущий поток.
AppDomain ad = Thread.GetDomain();

Любой поток в любой момент времени также может быть перемещен средой
CLR в любой из имеющихся ROHTeRCТOB ИJШ помещен в новый контеКС1: Чтобы по­
лучить текyIЦИЙ контекст. в рамках которого оказался поток, используйте статиче­
ское свойство Thread.CurrentContext.
private static void ExtractCurrentThreadContext()
{
11 Пon:учение контекста, в рамках которого
11 действует текущий поток.
Context ctx = Thread.CurrentContext;

Снова подчеркнем. что именно среда CLR является тем объектом, который отве­
чает за помещение потоков в соответствуюrцие домены приложения и контексты.

Как разраБОТЧИI< приложений .NET. вы обычно остаетесь в блаженном неведении


относительно того. где заканчивается данный поток (или. точнее. когда он поме­
щается в новые границы). Однако вам будет полезно знать различные способы по­
лучения соответствующих примитивов.
ГhaBa "4, Создан~е МНоtоооточных nр'илuжеl'lИЙ 567

Проблема конкуре'нции и РОЛЬ синхронизации потоков


Одним пзмножеС1'ва "преимущеСТjJ" {1{И'l'а:й'ге источmmов проблем} мноrmroточ­
нorо программирования являtп~ ТО, что .в~ :имеете очень узкие возможНости Ш>Н­
'ТроJШ В отношещ;ш J1'C11G.IIЬЗОВавид ПЩ'О~ОВ операционной flИстемой и средой CLR.
Напрнм~р, построив блок прогрaм'МJЮГО кода. GО3ДaIOrЦИй НОВЫЙ nОТОЕ выnолне­
ШIЯ. ВЬ1 не можете гарантироваТh. что этот поток начне1"' ВЫШl1IНЮ'ьс,Я немедленно.
Скорее. такой npогрaммный. нод TO~HO "д.аст ИНСтрукцию" oneрациОl1НОЙ си<:теме
начать выnолнеFДre потопа :кан ~ожно быстрее (что :обы'1НО" оз:наqает момеНт. когда
н:аcтyuит оч~едъ этого IIOTOк,a У It;тщнир.овm;ИI-ta пО1'DКOВ).
Кроме ТОГО, цосROЛЪк,у потоки 1'40f}'Т nеремещатъся МеждУ границами прило>ке­
ния н KOlITe~CTa по требованию СJя. RЫ ДОЛЖНЫ следить за теМ. ,каюfе 9.11ементы
Dащега прщжоЗJrefflI,Я открыты 6ЛW/JШ/O nomnJtb8 [1:е. 'позволяют ДPcтyI1 hmожества:
ПОТОКОВ). а кaюrе операции OIщ,~твa:iOТся аmoмnрнылtU (операции. tэткры1ыe дnл
мнo~eтвa IIOтоков. JlOTeI-ЩiIЩn;НО опасныl]. для .примера предположим. что ПОТОК
вызывает яеIшторыjij метод IЮ!iliреТНото объекта. Гlpедj10ЛОЖИМ также, что посде
ЭТЩО ПО1.'оа получает ДНСтРУ.lЩИЮ от JJJlfil'IИР(ЛIтr(mtэ .пОТOJ{оtJ приостанО1lИТЬ выпoJI-­
нение. чтобы позволить д.рутому потоку доступ К таыу же методУ .ото же объ~'Х'а.,
ЕсЩ1 орш'инa.тrьНJ:;IЙ роток еще не эавеpпmл:свою тек;ущую операцюо, второй
вхо,дяIЦИj.j nQTOff мохсет получитЬ для просмотра объект в частично , измененном со­
стoщm;и. Бзтом случае lIТОI'ЮЙ пD'r6R. по сути. будет читать неtюрректные данные.
в ре.ЗУЛhтате чего воз;ншщут досадные (и очень трудные для. выявления) OI1Шбitи,
:rщторые .хара.к'J'ер~зуютC,fl :иеустойчивостью ирм "Воcnроизведе.нии и ОТЛaд,I\е.
Атом-а.рные операции, с ДРУГIiJЙ стороны. всегда беэопасны в МНОIРПDТОЧНОМ
окружении. К СО,щaJreПИЮ, толыю для очень небольшого числа операций из библи­
DТeH базо~ых IOЩСЩ)В .NEТ можно гаравтироватъ.. что эти операции будУТ aTI!JМap­
НРХМИ. Не .IЩlЩ~СН атомарной даже операция npисваивание значения ч.леl]J-пере­
ме:н;нойl ЕСJШ в документации .NET F,пrmeWork 2.0 SDK в отношении кrocой-либо
опеpaщur сдециa.JIЫIO не оговорено. что данная Qлера:цм.я является атомарной, вы
ДОJШЦIЫ предполагать, что эта оперa.n.ия явлнется ОТКрЫТОЙ влил1ШЮ потоков и
принимц.тъ СЩЩf1;альные меры предосторожности.

Теперь Бам д.олжно бъггь яCEi:о, что домены м"ногопоточного приложею",я тоже
открыты ~;Щ:Лf~ИЮ цоТORов. 1IОСКОЛЪКУ Потоки могут пытаться ИСПОЛЪЗ0ватъ дocтyn-
1Щlе фУ~ЦЩИQнальные возможности одновременно. Чтобы ЗaJiЩТИТЬ ресурсы npи­
ложеl'lЩJ ОТ возможных искажеШIЙ, раэрабоТЧИЮlМ .N'EТ Прщmдится и.сподьэовать
тещ наз~аемые npимитивы rют.аков (т.ш<ие. :как блОЮIpовки., мониторы И атрибут
[5упсhr~:Юizаtiоn,JJ, ЧТQбы КоНтр<шировать дocтyn въmолня:еМЪJX ПОТОlЮВ.
Цельзя утверждать,. ЧТСI шrarrформа .NЕТИСЮПОЧlma все ТPY,/IНOCTJJJ ВО3.IO.1.EaЮ~
"1.ЦJ}C :цри прстроении устойчивых МНОГfШОТО-ЧНЫХ ПрWlоженml. 11.0 теперl, этот про­
цесс э!щчител.ьно ynpощён. Исполъэуя ТИIIЫ. оn'pе.целеЮihtе в ПРОС,транетве имен
:5У'5 tem. Тhrеаdiпg,.вы: получаете БО3МOЖ1iостъ создавать , дополнительные ПОТ01m с
'МИШШaды1blМИ усилиями и МИНИМЭЛЬНЫМИ проблема:ми. Точно так ~" иorда npl'1~
Jl;ОДИТ IIpeМfl блокировать or.гкpьtгыe элементы данн:ы:х. Бы.можете иcnол:ьзовэ.ть цо­
ц~тельные типы. :которше обеспе~т -ге же функциОНElдЫ'Iые возможцоети.
что и: прпмитив:ы потORО:В WlnЗ2 АР] (НО при этом используется Ha~HOГO более ах·
~а11Наяобъeюwш модель).
Однако и.спользование пространства имен sys,t em.Threa:dipg - это не един­
пвенный путь построеmш мнQгопоточпых програм:м .NEТ. В ходе нашего обсуm-
568 Часть 111. ПрограММl1рование КОМПОНОВОЧНЫХ блоков .NET

дения делегатов (см. главу В) мы уже упоминали о том. что все делегаты NEТ об­
ладают способностью асинхронного вызова членов. Это - главное преимущества
платформы .NEТ. поскольку одной из основных причин. в силу которых разработ­
чик создает потоки. является необходимость такого вызова методов. при котором
не возникает блокировок (т.е. имеЮiО асинхронного вызова). Для достижения та­
кого результата можно использовать и пространство имен System. Threading. но с
помощью делегатов это делается намного проще.

Краткий обзор делегатов . НЕТ


Напомним. что тип делегата .NEТ- это обеспечивающий типовую безопасность
объектно-ориентированный увазатель функции. Когда вы объявляете делегат .NEТ,
компилятор С# отвечает на это созданием изолированного класса, полученного
из System.MulticastDelegate (который, в свою очередь, является производным
от System.Delegate). Эти базовые классы наделmoт каждый делегат способнос­
тью поддерживать список адресов методов. которые могут бытъ вызваны позднее.
Давайте рассмотрим декларацию делегата BinaryOp, который был впервые опре­
делен в главе В.

/ / Тип дenе:roаo:rа С •.
public delegate int BinaryOp(int х, int у);

В соответствии с данным определением BinaryOp может указывать на любой


метод с двумя целочисленными арryментами, возвращающий цело численное зна­
чение. После компиляции соответствующий компоновочный блок будет содержать
полноценное определение класса, которое динамически генерируется на основе

декларации делегата. В случае BinaryOp это определение класса будет выглядеть


приблиэительно так (приводится в псевдокоде).

sealed class BinaryOp : System.MulticastDelegate


(
public BinaryOp(object target, uint functionAddress);
public void Invoke (int х, int у) ;
public IAsyncResult BeginInvoke (int х, int у,
AsyncCallback сЬ, object state);
public int EndInvoke(IAsyncResult result);

Напомним, Ч:то генерируемый метод Invoke () используется для вызова мето­


дов, обслуживаемых объектом делегата в синхронном режиме. В этом случае вы­
зывающий поток (например. первичный поток приложения) вынужден ждать. пока
не завершится вызов делегата. Также напомним. что в С# метод Invoke () не вы­
зывается в программном коде явно, а запускается в фоновом режиме при исполь­
эовании "нормального синтаксиса" вызова метода. Рассмотрите следующий про­
граммиый код. в котором статический метод Add () вызывается в синхронной (т.е.
блокирующей) форме.

/ / Эo:rо '1'ребуеo:rа ДlI. 8JoI8oaa Thread. Sleep () .


using System.Threading;
using System;
Глава 14, Создание МI'IОГQПОТОЧ"ЫХ ПРИJ10жеНI4А 569
namespace SулсDеlеgа.tе
{
pu.b1ic dele9ate irtt Вiл-аrуОр (j.,nt 1(,/ int у) ;
clas's P·rogram
1
sta;tic void Мз-i,n (str:ip.~ I1 args)
(
Сопэоlе. Wri.teLine (" * ......... СИЩtро.ннbIЙ ВЫЗОЗ .цеJl8га;:[!q * *** ... "j) ;
/1 ВaraoдID uшon:_. .о.tlо DO!llOxa.
Conscile. Wri teL±ne ("Вызван Мain (] Б поТ:о·!(е I О} .'" I
T'h read. CurrentThrea,d. G~tBa·s.hCadEl () ) ;

/ I at.Js·o . Add () • CИIUqIO,.во(С .~"


ВiлаryОр Ь = л'еw Бi-nаrуОр (Ada) ;
lnt answer ,. Ь (10, 10] I

// ЭФ. C~~ . . ~ UlqОnИ8JOl АО ___Р._


/ / рабо'Jlll И81J!QАа AdCI () .
Console. WriteLine ("В Main () еще ec'r'j;" работа! ") ;
СОЛ'501~. Wri teLine (" 1 О + 10 pasJoIO {О}.", answer);
Consble,. ReadLine () ;
S/taticint Add(int х., lnt у!
(
I/ ~ m 8IШОпir. . .oro Ii:О~.
Console.. wri teLine ("вызва,~ Add () в nO'l'o!«!! (О r , 11 ,
Thread. Cu.r:rent'Th1:ead. GеtНаэ.hСоdе () ) 1
1/ П&vаа IIpJDI.pRO 5 cup.ц ДmI
11 JDOrIJ!~ ~RОЙ Ob8.p8ЦJSМ.
1'hIead.Slee.pt5000);
retu.rn х '!" у.;

СиачarnJ. :щметим. ~жто ~ этой програм~е использу~ся пространство ~eH


:S~$tem. 'l'hrE\ading. В М~I,)Де Add () вы~~ваМСя; ,статич.ескИЙ ~eTOД Tnre,ad.
Б1еер (). чтобы при:оета,нОIlИТЬ вызывающий поток (:приБЛИЗИlJi'е,пь;но) па. ЩIТЪ се­
кущ для имитации задачи, ВЬПIоэшеJШе которой требует много времени. ПосцсlЛЪ~
MeтoA . Add О вщывается в CUfIXJЮЮWЙ форме. ме"1'ОД MainO не ,ИЩI-ечат~ет реэуль­
'Х'ат операции дс> тех пор. noI'ta не заверmится работа метОда Add ( ) .
Далееэам;етим. что метод Main О получает ДОCJ'IYП ~ l'енущсму потоку (с помо­
ЩЬЮ ТhЕеаd.СurrелtТhrеаd) И печатает его хешиРОВ8ЩЬТЙ к1:>Д, ПОCRОЛЬ'R}' этот
~рова'fПЦdЙ :код преДС'I:ЗВiJJяет 06ьeRТ в конкреТНI!JМ состояgии. с!>Отвеreтвую­
щее знач~ МC»ЮIQ испOЛ1:iЗОвать. каи ~груБЫЙ· иде~ф:юmтор ПОТО!Щ., Ta ·~ ло­
I'ИJ(a иcntmЬзуетсн в €татическом :методе Add (). как и ФJедУЩ' DЖ8дRТЪ. поcnroлъку
вся работа В ЭТОМ пр;иложении в!щ]vЛНJreТCИ И~RO цервичиым потоlCОм.
ВЫ ~1W1тe одинаковые хешированные· ЭЕЩЧеаия в ~~C~JIЬ"ROM выводе программы
'[рис. 14.1).
570 Часть 111. Программирование компоновочных блоков .NEТ

Рис. 14.1. Синхронные вызовы методов "блокируют" другие вызовы

При выполнеJ-ШИ этой программы вы заметите, что перед тем выполнением


Console.WriteLine () произойдет пятисекундная задержка. И хотя многие методы
(если не подавляющее их большинство) могут вызваться синхронно совершенно
безболезненно, делегатам .NEТ. если это необходимо, можно дать указание вызы­
вать методы асинхронно.

Исходный КОД. Проект SyncDelegate размещен в подкаталоге, соответствующем главе 14.

Асинхронная природа делегатов


Если для вас тема многопоточных приложений является новой, вы можете
спросить. чем же на самом деле является асинхронный вызов метода. Вы, без со­
мнения. знаете о том. что для выполнения некоторых программных операций тре­
буется время. ПредыдУЩИЙ метод Add () был ИСКJПOчительно иллюстративным, но
представьте себе, что вы построили одно поточное приложение. в котором вызы­
вается метод удаленного объекта. вьmолняющий сложный запрос к большой базе
данных или запись 500 строк текста во внешний файл. Пока не закончится вы­
полнение этих операций. приложение будет казаться зависшим достаточно долгое
времн.. Пока соответствующая задача не будет обработана. все другие возможности
программы (такие нак, например. активизация меню, выбор элементов в панели
инструментов или вывод на консоль) будут HeдocтynHЫ для пользователя.
Но как дать указание делегату вызвать метод в отдельном потоке вьmолнения.
чтобы имитировать одновременное выполнение множества задач? К счастью. нуж­
ной для этого способностью автоматически наделяется каждый тип делегата. NEТ.
И более того. для такого вызова вам не требуется углубляться в детали простран­
ства имен System.Threading (хотя. естественно, одно другому не мешает).

Методы Beginlnvoke() и Endlnvoke()


Когда компилятор С# обрабатывает КJПOчевое слово delegate. динамически ге­
нерируемый класс определяет два метода с именами Beginlnvoke () и El1dlnvoke () .
для нашего определения делегата BinaryOp эти методы оказываются следУЮЩИМИ.

sealed class BinaryOp : System.MultLcastDelegate

1/ ИСПО'nЬ9уе'l'СЯ: ,IIJ!JI асиихроНJJОГО ВJoI90Ba ме'l'о.ца.


public IAsyncResult Beginlnvoke(int х, int у,
AsyncCallback сЬ, ob:j ect state);
Гл~ва 14. СОЗА~lfиt1 М~ОГО'nЬТОЧНbIX rфИЛОЖВНИЙ 571
I/ И;СПОЛЬ!Зу~cI( дnи .lCЭВnеll8eюrя: ВQs~а.ща.еldOX'C saа_НИJii
1/ swзвавкоI'O ието;qa..
ри,Ь:[ i.~ l nt E:r,dl DVQ,ke (lАsупсRеS J:1 1 t :r:esult) ;

Первщй на.бор параметров, ~epeдaвaeMЫX В Baginln<voke(), ФОРМИРУ~ТСfl на


оонове формата де.легата С.# (.8 сдучае Вiп:аrуО.р это два ц~лоqи:слеfПP:d.X элаче­
ния), ПОСJlеДfЩМИ двумяар:гуме~'1'~ ВСe:I'да ЯВЛЯЮТСЯ Sуst.еm.Аsую.с'Са'll back и
S'Y$U"m.Obj,act. мы рассмотрцм ррдь этих парам~тров чуть позже,. а пока 'Что ДJtЯ:
каждого IaЗ Ш![Х мы будем цсцощ,З(i)вa:J'Ь pl1.J1 1 .

Интерфейс System .. lAsyncResult


метод Веч i r. 1 r1voke '( I всегда возвращает объе~т. pe-алцзуJOЩИЙ ИН'Т'Ср­
фе'Й:С IA sy ncRE'sult, а метод EndIL1'Jc,ke () имеет еДI:Щствеlfныii парам~тигrа
11Isyn'cF;esu]t. Совместимый (' IAsYhcR'esliIlt объщ~т, .БЩlвращае;МЫ:Име.тодом
Be. ginln·"oke (), 11 нвляетC!f тем с:внзующим ме:х;аш1;зМО:М, ЩJтор.ый ,товврляет вы­
зьmающему потоку ПОЛ.УЧЦТЬ рt'зym:п'а'Т аСИЩРОНRorОllБ!ао~а Мtn'ОД~ позже с lIO:МO­
щью E}'ldIFJvoke {). Ынтерфейс IAsyncRes111 t (одреде.ч:е{щый в Пр0странстве имен
Sys tem:) задаетця ТОЩ, Kaf1 поназа.н;о filI1Же,

puЪ.!. i с i ntarface IAsyncReSU1 t


{
Qbject . A'sj-'nсStаtе
{ get; )
'i'laitHandle АвуЛс"Wа.i tRandle { get; }
Ььоl CQТripletedSyhchr o nou's], у { get; }
Ьа о]' 1 sGompleted 1 get; J

Б самом простом случае можао избежать неnосредствeFП-lOГО Bbl3(ffl8. этщ ~­


НОВ. Требуется 'ГОЛЬRО со.хрЗНйть со.вместw,ю,1Й с IАSУЛ .СRеsult объект. В09вращен­
ньш Begil1Invc.ke (}. и передать его методу Ehdl оуо]се (). КOl'дa вы будете ГQТOBЫ
получить результат вызова метода. Позже !'!ы увидите. ЧТО у вас есть· Щ>зможность
вызывать tmены е.Шfместимоtос I.AsyncR.esul.t объекта. если вы ;;оотите ')7частво­
БаТI/ в цроцессе извлечения возвращаемого значffiJЦl метода.

Замечание. Если асинхронно вызываетСя метод. который ' Не r:lредлаrает 'возвращаемblX ЭI:1ЭЧ!ЖИЙ,
МQЖНО' \'jrq BI;>I;3Baтb И пPwТО "забыть· (1 нем. В таких [}дy"tВЯ'Х f1eT неОбхоДИмЬС-ти СОХР8J:IЯJЪ CO ~
ВМt';CtимЫй С If1..З yncRe:SiJl t об'l;jвкт и .8ыыы1з:ть·· E'!Lctln:voke () (TaI< кa~ нет возвращаемоrо
зн~ченIo1Я. которое rpe(jyercfr иэвле%.) .

Асинхронный вызов методов


Чтобы дать указание делета.ту BiharyOp ВЪ(аIЩТЪ Mt:ТOД Add О аеинхроп:щ> • .1i13--
меIЩте П'р~ды,uyщий :метод 1'181'1'1 () ТIЩ. как показано ниже.

s ·t.a tic va.Ld Мaiл (stI iГog 1) a~gB)


I
r
572 Часть IU. Программирование компоНовочных блоков .NEТ

/ / B~o,ц ID 81оШ0JIНll8Иоrо потока.


Console. Wri teLine ("Вызван Main () в потоке (О}.",
Thread.CurrentThread.GetHashCode());

// ВWЗ08 Add() 80 вторичном потохе.


BinaryOp Ь = new BinaryOp(Add);
IAsyncResult iftAR = b.BeginInvoke(10, 10, null, null);

1/ Вunо,nнекие дрyrой рабо'1'll 8 пеР8ИЧНОМ ПО'1'оке. ..


Console. Wri teLine ("В Main () еше есть работа!") ;

/ / ПOJlучекие рез~~.'1'. метода Add () ,


// хоrда это требуетс•.
int answer = b.EndInvoke(iftAR);
Console.WriteLine("10 + 1О равно iO}.", answer)i
Console.ReadLine();

Выполнив это приложени:е. вы увидите. что теперь выводятся два разных хеши­
рованных значения. посколы<у в границах текущего домена приложения выполня­

ются два потока (см. рис. 14.2).

Рис. 14.2. Методы, вызываемые асинхронно, выполняют свою работу в отдельном потоке

Вдобавок к уникальным хешированным значениям. вы также обнаружите. что


при запуске приложения сообщение "в Main () еще есть работа!" появляется
практически немедленно.

Синхронизация вызывающего потока


Для текущей реализации М а i n () диапазон времени между вызовом
Beg1nInvoke () и вызовом EndInvoke () юто меньше пяти секунд. поэтому после .1
i
вывода на консоль сообщения "в Main () еще есть работа!" поток вызова бло­
,
:1
кируется и ждет завершения существования вторичного потока. который должен
получить результат метода Add (). Таким образом. вы на самом деле выполняете
еще один синхронный вызов.

зtаtiс void Main(string[ ] args)

BinaryOp Ь = new BinaryOp(Add);


IAsyncResu l t i f tAR = b.BeginInvoke(lO, 1О, null, null);

/ / До 3'1'0%'0 8ызова проходит ненее 5 секунд!


Console.WriteLine("B Main() еще есть работа!") i
rлааа 14, СО3Д&l1ие мноrОПОТQЧНЫХ n'jЭИJ10:lk!Ii}1Й 573
JI ВIr.8_a~ I1О'1!О. ~OICМpY.'I'CII до ."8pr81UU1 ~.I)vok. () ~
in·t answer = b.EndInvoke (:i:ftAR) ;

Очевидщ), -чr.го El(ЩНХ.ро;mpaе ,Il~егатьr теряют свою привnекатe.D:Ыйость •. если


Щ>ТОК вызова :может JWiИ Опредe.nе,нных условвях БJtсжяроватъсв.. Чтобы позвOJlmЪ
выэвющ~~ npтQвy~. За1<ОИчИл лиаси:нхрон:но выэаниый метод свою
работу, 1UПерф~с Ц\s:уnсRеsuН предлaraет свойство IЭ·СОIIlрlеtеd. ИСПOJIЬауя
эroт-чдев:. П!)'1'(ffl ВЫЗ9Ва может пред вЫзовом Endmvoke О провер~ завершен ли
а~о~Ю:II3OВ" Если работа метода не завершена. IsCompleted ВDзВpatцkет
fals~ (ло:щь)1 ~ пOТOlt ВЫЗGВЗ. может прОДOJJЖaТЬ свою·раБO'l1Y. Если же IsCompleted
1I0sвра:щае-r trae (.истина). то поток выЗова:может получить результат "наименее
6,лQЩ!рyJo:ЩИМ~ спосоБОм. Рассмoтpи-rе след'УЮIDyIO у.ЩЦЙфикацию мeтdдa Ма in () .

static void мain (striлg [] .a tgs)


(

BinaryQp ::ь =
new ~i.na~yOp (.Аdщ I
IAsyncR~sult iftAR = b.i!e.giI1Invoke(10, lO, null, nblll);

/1 Э!llО coo~ б~.'I' D8ча'l'a'&iCII до '1'ах пор.


11 ~OX. "8 . . . .p1IIИ'1'CJI .JiS08 ..~,цa Add () .
"ы lе ( ! iftAR. IзСеmрlеtеd)
C.onsole. Wri. teLine ("'В Мэ.iп() еше ест!; ра:бота! ") ..

11 Т.~.рЬ· "" .ЮI8AII( I ~O _ _08 .....Qда A.dd () ...~ .


.i пt a!lSWer = Ь. Елd.I.nvake (.i:ft.AB,);

3деQЬ В;ВОд'иТСJJ .щntл, КО'Fорый будет прt)Должат.ь ВЫПолн.ение оператора


C011so1e .WriteLine () ДО тех пор. пока не завершится .trrQРИЧНЫЙ поток. как толь­
:КО ' это 11.РсщаО#де:г. вы t:;МQжете получить результат метода АШi () с уверенноtтЬЮ.
что этот метод заверДDiШ Свою pa~Oтy.
Вдобаво~ J( СJЩйству IsCQmpleted ивтерфеik lAsyncResult предnaraет авой·
СТВО Аs:упСWё!-it.Нi30пd1е /I}fЯ построения еще БOlIее гиБКDЙ ЛО1'ИI(и ожидания. Это
ei!QЙСТВО возвращает экземплнр Wa:iU1a.ndle. дредлагающий метод Wait:One () .
t;lреRМyЩеСТВQ метода WaitJola.ndle.WaitOne О в ТОМ, что вы можете У1Шэать Маи­
ctp.jальП!К' врема ожидa.шm. Ес.Ли)'ЮlЗанное вреlОlЯ ЦР~ВЪ1Шено, Wai tQne () воanра­
щает fal~e. Рассмотрите сле.цуюЩИй (обновленный) вариант цикла while:
while {! iftAR .. AsyncWai t!iandle. w,ai tOne (2'("1'00, true .)
(
Oonsole .• Wri t:eLine (ПВ Main 1) еще есть pa~Q':t'a! ");

Указанные своЙC"rВ8. IAsyncResul,t и в самом, деде обеспечива1ОТ возмОЖНость


свихро~ потона BbI;:lOBa. но этот подход 01(аэываетс.я не cIL'IIIым эФФектив­
ным. ВО МН()ГИХ omomениях: свойство 15Compl~ted подобно назоЙ.lХИВОМУ мейед­
щеру (или ,одномасснику). l<6ТОр1:iЩ цастoщmо спра'IПИвает: "Уже все сделал?"
К счастью. делerаты предлагают цeлы.if: ряд ДРУIЯХ (И более действен:н~) подходов
~ ПQДУЧения резулнrатов мe:roдов. выз~аемых асинх:рОmю.
r
574 Часть 111. Программирование компоновочны~ блоков .NET

Исходный код. Проект AsyncDelegate размещен в подкаталоге, соответствующем таве 14.

Роль делегата AsyncCallback


Вместо того чтобы выяснять У делегата. завершился ли асинхронный вызов ме­
тода, лучше иозволить делегюу информировать иоток вызова о выиолнении зада­
ния. Чтобы реализовать такое поведение, вы должны иредъявить экземпляр деле­
тата System.AsyncCallback методу
BeginInvoke () в виде параметра, значением
ноторого до СИХ пор было У нас значение null. Если вы укажете AsyncCallback.
делегат вызовет соответствующий метод автоматически, .когда асинхронный вызов
завершится.

Подобно любому другому делегату. AsyncCallback может вызывать только ме­


тоды. соответствующие конкретному шаблону. и в данном случае это методы. при­
нимающие единственный иараметр тииа IAsyncRestllt и возвращающие void.
void MyAsyncCallbackМethod(IAsyncResult itfAR)

Предположим, что у нас есть другое приложение, исиользующее делегат


BinaryOp. На этот раз мы не будем "просить" делегат выяснить, завершился ли ме­
тод Add () . Вместо этого мы определим статический метод с именем AddComplete (),
чтобы получить извещение О завершении асинХрОННОГО выэова.

паmезрасе AsyncCallbackDelegate
(
public delegate int BinaryOp(int х, int у):

class Program
(
static void Main(string[] args)
(
Console.WriteLine("*** Пример делегата AsyncCallback ***"):

Console. Wri teLine ("Вызван Main () в потоке (О).",


Thread,CurrentThread.GetRashCode():

BinaryOp Ь = new BinaryOp (Add) ;


IAsyncResult iftAR = b.Beginlnvoke(10, 10,
nеwАsynсСаllЬаСk(Аddсошрlеtе), null):

11 Здесь зr.mоn_етсн: дpyrCUI работа . ..

Console.ReadLine() ;

static void AddComplete(IAsyncResult itfAR)


{
Con.sole. Wr i teLine ("Вызван AddComplete () в потоке (О}.",
Thread.CurrentThread.GetHashCode()) ;
Сопsоlе.WritеLiпе("Ваше сложение выполнено");

static int Add(int х, int у)


ГлаlЩ -14, G(цдание МНОГОЛQ1"D'IНЫ~ 'ЛРИЛGжений 575
ОJrl$olt?WгitеLinе("ВblЗ.l'Iан Adct() в Щjто~е {О}.",
1'hread. Cu пentтrиеаd. GetНashGorJe () ) i
Tb:o:-е8d.Slе-ерt500D) ;
rеtLJПl х + у;

Снова заметим. <1170 статический метод A:dd.Gomplet:e () будет вызван- делегатом


AayncCallback тотда, ROтдаааверtпитсJ'i вhIЗDВ метода Add О. Выполнение 'этой
прЬ:граммы может подтвердить, -ЧТО именно вторичный ПОТОК ВЫnОJ:IRЯет 'Обратный
вызов AddComplete () (рис. 14.3).

PII\EI.14.3, Делегат АsулсСа 1 l'bac.k; !I деi<Щтвии

Роль класса AsyncResult


в текуЩей своей форме метод на i r. \) не ХрaIOП тип IAs упсRеs"ult. воэвращае­
мый из Begi [') ffivoke ( ). и не ВЫЗывает Endlrivake () . Более 11'ОГО. целевон метод Д~
Л't'J'зта Аь 'lш:Саll,Ьасk [в данном случае ато· меТОД llddСошр lete( )) вообще не име­
ет доступа 11 QРИГИНалъному At'J1eraтy Bi naxyO-р. созданному в KOHTel\C1:C Мiiiп () .
Можно. 1{(Jнечн(}. объпвить БiПd гуОр. нак. ('ТЭ.тичеСШiЙ член ЮJIi:!.сса. чтобы llОЗВО­
тrrь обоим методам иметь ДОC'I'JП к объе1t'Iy, :но бaJIее "эле-I'аъ:rгным''' решением нв­
ЛЯ€ТillIИС!IолъвовaIOJе ВХОДНО'ГО uapaмeTpa IAВ yncReslJ 1t.
ПоступаюЩШJ.на ВХОД параметр IAayтicВesult, перецаваемый целевому мето­
ду делегата AsyneCallback, явл.нется экземпляром масса ASYГlcRes:ult [заметьте •.
np!':фИRС 1 здесь ОТСУТСтП;5'ет).. определенноro в npoстран:стве Иl\lен Sys-tem . Run:time.
Р,еГ:lOting,Меssаgi}Щ. Статичесиое СВQйt:гвО' AsyncDelegate возвращает соъtл­
ку на орипmмъный аси:нхронньrii делегат. СQЗДанный !Де-то :в програмМе. Таким
образом, чтобы получить ссьrлну на объект делегата Bi паrуор. раэмеш,е:ННЬUi '}3

Main (). нужно прос:г@ npеобразовать возвращенный. свойством Async.Delegate


тип Syste,m.Object в 'Лш Bd.naryOp. После ЭТQТО можнО' вызва'гь Encilnvoke 0'. I<aк
и ожидается.

/1 Не sаб)Гдъ1J.!& дфбззМ'Ж'ь .чиреХТИ8У 'uзinq ,. ДЛIi


11 Sуstem.Ruпt:.iше,Remoting,Мessаgiпg!
::>tatic '\10'iд .'\ddCo1liplet.e (IAsyncRe.sult itfAR)
r
·(':Of1Sl:>lEl.'-Wri t'eLiТl@ ("Вызван AdCi.CDtт~ple1~e 1') в пптё;J",е ! О') , ,. I
Тhr>;'ёld.ОuIrепtТhrе",d.Get1-lаshСQdе());
СOJ1эаl е. w:ri tеLiл,? ("ВqШS СJIожение ВblI10ЛН€НО") I
r
576 Часть 111. Программирование компоновочных блоков ,NET

11 Теперъ получки peS~TaT.


AsyncResult ar = (AsyncResult)itfAR;
BinaryOp Ь = (BinaryOp)ar.AsyncDelegate;
Conso1e.WriteLine("lO + 1 0 равно {О}.", b.EndInvoke(itfAR»;

Передача и получение пользовательских данных состояния


Заключительным аспеRТОМ нашего рассмотрения асинхронных делегатов будет
обсуждение последнего из аргументов метода Begin.Invoke () (этот аргумент у нас
до сих пор бьш равен null). С помощью этого параметра можно передать в метод
обратного вызова дополнительную информацию состояния из первичного ПОТОRa.
ВВИДУ того, что прототипом этого аргумента является System.Object, с его помо­
щью можно передать npактичесRИ любые данные, приемлемые для метода обрат­
ного вызова. Предположим для примера. что первичный поток должен передать
методу AddComplete () пользовательское TeRcтoBoe сообщение.

static void Main(string[] args)

IAsyncResult iftAR = b.Beginlnvoke (10, 10,


new AsyncCallback(AddComplete),
"мain () б:nагодарИ'J:' вас за c:nо.ение этих т,щсе:n. ") ;

Чтобы получить эти данные в контексте AddComp1ete (). используйте свойство


AsyncState поступающего на вход параметра IAsyncResult.

static void AddComp1ete(IAsyncResult itfAR)

11 По:nучение объехта с информацией и преобразоааиие его в строху.


string msg = (string)itfAR.AsyncState;
Console.WriteLine(msg) ;

На рис. 14.4 показан вывод этого приложения.

Рис. 14.4. Передача и получение пользовательских данных состояния


Глава, '14. Созд,шvtе МflоroПОТQЮII:Ю( пРИЛОj)\(6НИЙ 577
ЧудеснО:Теперь. J:ЮIДa вы понимаете. что деrшl'ШГ ..NEI'можно использовать ДJlЯ. aв~
roматичеcrroго запуска вторично:го потока выIшненин.. обрабатывающего асш-IXpOН­
НЬriI вызов метода, давайте обратим .внимание на возможности непооредственного
"ВЗаимодействия СnOТGКaМИ с ШlМОЩЪЮ пространства имен S.11st.eтa.1:'h.readi.n.g.

ИtxD.цt:IыМ ~o.д. Проект AsyncCaHbac~Dejegate размещен 8 i10,щ<ата:логе, соотвеТСЩУlOщем главе 1'4.

П'ространство имен System. Тhrеаcliлg


в рамl{ах платформы ,NET npoc1'paH("J,"Bo имен System.Tbreading предлата~т
ряд ТЩЮВ. DОЗВОДЯЮЩЦХ строить МНщ'tЩрточные ПРИ1IОжеIЦIЯ. Вдрба.вок :к ТИIl<Ц1:,
С помощью :IWТOpыx МЩfЩО вsаимОДСЙСТБОБа.ть С о:rдеЛЫThп,.rn поток8МXI CLR, в этqм
пространстве 1ЩеБ Определе;ны ТЭЮ'lre тидPI, обеспечивающие доступ fC [lОД[l:!,~Р,ffm­
~aeмnмy средой. CLR пулу ПОТОIЮВ, простой (не имеющий графичеСIЮГО :ln:Iтерфей­
са) класс -1' i.щ€ r 11 МЦЩЕreС;ТВQ типов, npедназначенн:ь/Х дrщ Ш;JддеРЩ[Ql СИЩJЮIЩ­
зироваНЕ:QГQ ДQс-ry'IЩ fi разД~дЯемым pt;.'C)'pcaм. Описания ОСНОВНЫХ "ШGIЮВClТЦГQ
проrтраиствц. ИJ\teR npи,аедены табл. ] 4.1. (Не за6'blf1fl'Йте о том. что подробности
Бсt',ща можно на~йти в Дtп'ументации .NE1' F:ramewoT$ 2.0 SDK.)
Таблица 14.1. Подборка ТИПОВ пространства 1>1мен 3ystem. Тlпеаding

Тип Опис~ние
lnte:rlocked Прэд.nаГQет a-tомарны~ оneрацИI-1 д;I1\1 ТИnQВ, алФьпы)( д,ля MRO-
mecrвa потоков.

Мол itor Qбеспе\iивает СИН)<РOl-lизацию' оfu!ЭIПQ6 !1О1'OIЮВ С ПОМОЩЬЮ


блокировок и оЖИдаНI1Й/СИГНaJIОВ. kf1lO'1e!loe CЛQOO С# lос)(
использует тИп 1"1011 it0r в фОНОВОМ Pe\1l<.11Me'

МuCe.x Примl'1ТИn СИН>;JiЮ"iИэац~WI. иcnользуемыи для еиНхроНизаци ....


I:Iзаимодействии меЖд:у rPal-I\1ЩIМ1/I доменов nриложеНИR

PMa!'l!eterizedTf!re,ad.Star't Делегат (ПQя'вившиji1{)я только В ,NП 2.0). позщэля!Фщий лотежу


ВЬ.lзываl'Ь методы Gлюбым числом apryMefqOB
Эеro.арЬоrе Поз.воля~т ()грвничить число потоков, которые могут иметь KOtt-
куреЧl'Ныiil ДОСТУП к ресурсу ИЛИ (j)пределе~нюму типу ~CYPCO~

Th'read ПредспmляEfr лоток. RЫПОПНЯIO.1.ЦИЙОЯ в среде CLR. С помощыо


ЭПJ'ГО типа' мож~ю Gоздавать дополнитепьflыe потоки в ориги­
НЗfll',tjl)М ДОМЕЖе приложения

ThreadPool ПРЗВОЛЯЕ!Т взаимодеЙСТ6q8З.ТЬQ пулом потоков, управляемым


средойСLR в рамках Aalf~loro процесса
Пере<teнt" представшноJЩ\'1И уровень лРJilоритета потока
(Highest, ыru'Lall и т.д.)
Tt.r~a.dStart Делегат, иопользуемый:для указаtМя метода" вызываемого p;nя
AaH~JQr() потока, 8 QТJJИ.~ие от Parame-t€~i zed!LrtTead5ta..r:t ,
целеВЫе методы 'I'n;геаdStаrt ДОЛЖ"lbI СОDТЭЕПСТ80ВдТЬ фИIGC\.t­
P08t\HHOMy wаблОhlУ
ТlпеаdStа te Пере'lВНЬ. ука,эывающий состояния, допустимые ДЛЯ данн'ого
гют.окв (;F<.'i.)ш)ing, АЬоrtеd и т,д.)
Tiruer ОбеJ.:;пеЧ~Jвает механизм ВЫПOJFI-IЕ!tIИЯ М8тtЩ8 'Iврез заданные
IIIlпеРвaJl'Ы sрамени

TimerCa llback Делетат, исi1Dльзуемы:в сшюкуmЮСТIiI С· rnпаМИТimеr


578 Часть 111, Программирование компоновочных блоков ,NET

Класс System. Threading. Thread


Основным в пространстве имен System.Threading является ЮIaСС Thread, Этот
liЛасс представляет собой объектный контейнер отдельной ветви выполнения в кон­
кретном домене npиложения. Он определяет ряд методов (как статических, так и
общедоступных), которые позволяют создавать новые потоки в текущем домене при­
ложения, а также :приостанавливать, останавливать и завершать отдельные потоки.

Рассмотрите описания основных статичесl\ИX ~eHOB, приведенные в табл. 14.2.

Таблица 14.2. OCHOBHble статические члены типа Thread

Статический член Описание

Сur:rелtСопtехt Доступное только для чтения свойство, возвращающее контекст, в кото­


ром выполняется поток в настоящий момент

CllrrentT:Гiread Доступное TOl]bKO для чтения свойство, возвращающее ссылку на выпол­


няемый в настоящий момент поток

GetDomain ( ) Методы, возвращающие ссылки на текущий домен припожения или


GetDomainID () идентификатор домена, в котором выполняется текущий поток

Sleep () Метод, приостанавливающий выполнение текущего потока на указанное


время

Класс Thread также поддерживает набор ~eHOB уровня экземпляра. Описания


неIЮТОрых из этих членов приведены в табл. 14.3.

Таблица 14.3. Члены уровня экземпляра типа Thread

Член уровня
Описание
экземпляра

IsAlive Возвращает погическое значение, сообщающее о том, запущен ли дан­


ный поток

!sBackgTound Llитает или устанавливает значение, сообщающее о том, является ли


данный поток "фоновым" (дополнительные подробности будут предло­
жены чуть позже)

Name Позволяет задать понятное строковое имя потока

?riority Читает или уотанавливает приоритет потока, которому может быть 1-1а­
значено зна'lение из перечня ThreadPriority
ThreadState Читает информацию о СОСТОЯНИII! потока, которая может принимать зна­
чения из перечня ThreadState
Abort() Дает указание среде CLR завершить поток как можно быстрее

Interгupt () Выводит (например, путем активизации) текущий поток из периода ожи­


дания

Join() Блокирует вызывающий поток до завершения указанного потока (того,


для которого вызывается Join ())
Resume () Возобновляет выполнеНие приостановленного ранее потока

start () Дает указание среде CLR как можно Бытрееe начать выолнениеe потока
Suspend () Приостанавливает выполнение потока. Если поток уже приостановлен,
вызоа Suspend () ипюрируется
Глав(! 14'.Созлsние, MltOranOfD't!ibjX ГtРИJtОJkени,й 579

Получение информации об отдельном п, отоке


Напомним. что точка входа компоновочного блока (т.е. метод Иа i (1 (') ) при вьmол­
FItяШНокааьrвaeтсн в I1ервИЧВоМ потон.е. Ч1'обы привести пmичный при:мер иепОЛЬ­
ВОВ8ЕИЯ "ГIИIii Thr·e.ad, предположим. что у нас естЬ новое нонсольное Х1риложение С
именем TIH:eGi.dSiats. Вы знаете. что статическое c1l0ik'1'B() T1'lread .'СUrrе'дtтЬ,.г еаd
IJ(')3ВОЛЯСТ ПOJIyЧИТЬ 'IИII l' hr ead. npедЬтавЛЯЮЩий выuо.ШIЯеl!'r.ы:й в настоящий мо­
мент llD'TQК. ПОlJУЧИВ текущий по'l'ОК. вы можете йывести на ::mран ра3Л}JЧНУЮ ин­
формацию о потоке .

/I Не ""аБУдЬ~е у~азёl,'l'Ь 'us.ing ,j длJl ПРО.Ctrp~с~а J:!М8~ Systent. Thr~adin9.


staf..it':: ~Clld' Маш 'stтiпg~] <!rg's}
i

I/ Поп~еиие '1'ех1fЩtU'о ВО'1'ожа и наsкaчеиие ему имени.


'])hread 14ri.mn!,'YTI'lr·eaa = ТМ" еаа. Curre.п:tThrea.j;
pri.m.. гуТЬ read. Name == "ТhеРгi:J!lqл уТ1-\Jrеаd";

/I nодробнOC'1'Jf ХОСEiUП'& ДCDilеиа npи.п.ожеНmI и 1СОИ!l'eIl:С'1'i!i.


COi'!S.O 'le. W.. i·teLine ("'ИМЯ 'Т'Е!:КУШего ДOM€aa rr:pY.lЛО'l<€>Ния: f О} ",
ТhУ'е,з:d . Gеt1)ощ",i n () . Fr lendlyNaJQi?) ,.
C;0f1S01e. Writ.еL.i..ПЕ ( "'Иден:~ифит<::а'l\ОР текущеL"О Ю2>оj?l' ~кс та: (О. t ".
Tt,1"€!3.d. Си r rer'!tC6 nt"e 'x t. COl1text LO) ;

/ / Вшldд инфорка.цик о дaliНOK nO'1l0XE! .


\::С1'ЛВQ1е. Wr i'leLiле (, "Имя ИОТОК~ : {. D ] ",
pri!1'!ary1'b teo-:J. Name) ;
CQrtsQle . WJ;i teL1.;r18 ("Эапуще.!;! ли доток? -[ О ] ",
p.r::.i,Ifl6 гу1'г! read . lsAl ive) ;
COnso1e. Wr iteLir,," (' '' УрФ веl:l ~ ПрИОр!4'!' е та : (О J 11.
Р r.i.rrlt! ry1'~a"ei3 d . Pr iori t у) :
COnS.o l.e . Write.LiTH:" ( " CGC'TOI3НJ-1e ЛОт·о)(d; ,О ! п,
рriщаrуТh.r-е.:)Q.Тhrеа:dStа:tе);
C4" hsol-е.RеаdL,iле () ;

На рис. 14.5 покааан ВЬ1Бод этого ПРWiожеп:и;н..

Рис. 14.5. СБОРСПlП1СТ\llk.И' о rlOTOKe


580 Часть 111. Программирование компоновочных блоков .NEТ

Свойство Name
Приведенный выше прогрaммный код достаточно понятен. но обратите внима­
ние на то. что класс Тhread предлагает свойство с именем Narne (имя). Если вы не
установите для него значения. свойство Nam.e будет возвращать пустую строку. Но.
назначив данному объекту Thread в качестве имени понятную строку. вы можете
сильно упростить процесс отладки. В Visual Studio 2005 в режиме отладки можно
использовать окноThreads (Потоки). доступ к которому можно получить. выбрав
DebugqWindowsqThreads из меню. Как показано на рис. 14.6. в зтом окне можно по
имени идентифицировать поток. который слеДует проанализировать.


ть .,,,а. IEI

Рис.14.6. Отладка потока в Visual Studio 2005

Свойство Priority
Далее заметим, что тип Thread определяет свойство с именем Priority. По умол­
чанию все потоки получают приоритет Normal (средний). Но вы можете изменить
зто значение в любой момент времени существования потока, используя свойство
Priority и связанный с ним перечень System.Threading.ThreadPriority.
public enum ThreadPriority
(
AboveNormal,
BelowNormal,
Highest,
Idle,
Lowest,
Normal, / I Значение r испo.n:ьзуемое ПО УМОJIчаНИl).
TimeCritical

При назначении потоку приоритета, отличного от принимаемого по умолча­


нию (ThreadPriority.Normal), вы должны понимать, что не обладаете слипmом
большими возможностями контроля в отношении того, когда планировщик пото­
ков переключится С одного потока на дРугой. 'Уровень приоритета потока flБляется.
лишь "подсказкой" среде CLR в отношении того, насколько важно вьшолнение дан·
нога потока. Поэтому поток со значением ThreadPriority.Highest (наивысший)
не обязательно гарантирует данному потоку абсолютное преимущество.
Снова подчеркнем, что в том случае, когда планировIЦИК потоков полностью
занят текушей задачей (например, синхронизацией объекта. переключением или
перемещением потоков). уровень приоритета будет. вероятнее всего. соответству­
ЮЩИМ образом изменен. Однако в других случаях соответствующие значения про-
Глава 14. Создание MHorOnOTO~Hliilx ПРИ1l0жеflий 581
ч'm'ает среда CLR, которая и Rыдаст планировщику потоков указания о том, нак
дyчnlе всего .организовать ББamование времени. При 1Ipочих. равных условиях по­
ТО1(Й с 1-IДеНТИЧItblМ приоритетом ДОЛЖНЫ ПОЛУ'IaТЬ примерно ОДиRaRовое время

ЩIЯ выполнения своей работы.


НеОб;tодимость изменения приоритеl.'ОВ IlO1.'OROB вручную вОЭни.teaет очень ред­
КО. Теоре.тически MO.1l'mo ПОВЫСИ'lЪ приори1'ет для hшожеСТВа ·ПОТORов так. ЧТО это
не позволит Потокам с бьлее низкими ПрИDритетами выIIл1:нlтъъ работу на иХ уров­
нях (поэтому испол:ьауй.те укаЗа}IJ'Jые b-озмоЖfЮЩИ с ОСТОРОЖНQL'ТЬЮ).

Исходный JtQД. Проект Тhr~dStats размещен в подкa-rалоге, со.оrветствующем таве 14.

Пр:ограммное создание в·торичных потоков


ЧтОQЫ программно СОЗДаБать ДЩlOднителъ;н:ые ПОТОIЦJ. 1lbЫlОJIНЯIOЩНе свои Ьт­
делъцые вада'<JИ. вы ДОJlЖ:Н.Ы с.леДQваТh ЩIpлне п()нятным указанным ниж.е рено­
м:е:ндaдищ\fI.

1. для ВЫбранного типа созлайте метод, котор:arn будет исдолъэаваТЬС,ff В каче­


стве точки вх.ода нового потоМ.

2. 'Создайте делегат Р ·а r а me ·t ё r.i z е d T·h r еа


d S t а r t 1й1l~ уже уетареJJШ»Й
TllIMdStart). передав его конструктору адрес MtlO.IIa. оnpeделenноro на шаге ] .

3. Создайте Объект Tt'!l:edd . передав KOHcrpYКTOP.Y деJfегат Pararn.ete.r izedThread-


Start/ThreadStart в виде артумента.

4. Sадай~ пqдxОДffiЦие начаШ:iньre :харaкreриC'ПIlm ПD1Г<Жа (ими, npи:оритгет И т.д.].

5, ВЫЗОНИТt": метод Thread .• St·art (). Это у.каза;ние КaI:t ~ОЖН:О быстрее старто­
вать nWOK ДЛI:I метода. на щ>торый ссылае'J'C1l, делегат, созданный на шаге 2,

Сorла~о шагу 2. Иh:J!teТСЛ .возможность иСпШI.b30ватъ оди.н ив двух разных тn­


пав делегата д:л!я метода. uредназначеIШОГО ~ ВЪ!Полнения 110 ВТ\Jриqsом потоКе.

Делегат T1He.ad3tart ЯЩlяетси Ч4СТ~Ю иp(}CТP~CTBa имен Sу.в t юп.ТIlrеаdiпg со


времен .NEТ вt;:рсии 1.0 и MO~eт у:казЪUЩl'Ь Щi люБQй метод. не имеющий аргумен­
'IЩI ~ не ВООВpaщa1QЩИЙ .н:цчего·. ЭГ9Т делегат УДDDПЬ использовать тогда, J:tщ"да. ме­
Т()Д доцщев: вьш.оJIНJlТЬСЯ в фоновОм режиме без взаимодействия с ним.
'Qчещщным Qтрцнич-е.нием Т11 r ead S t. а r t ,mшяе'Гся отсутствие ларам.етров.
Поэтому в .NEТ~.O пред.дагаетi:;я; тИ11 делетата Pa;ramete.:: izedTtJ read.Sta.t t. дony­
CRающий д~ред~ч:уодногЬ параметра. типа, Sy~. tе!tп. O.bj ее t . lIоciWльву с IШМощыо
Sуэtеm.ОЪjес::t можн{)предс'I1SВйТЬ все, что угодно. вы МОЖf:Те.nередать этому де­
легату щобое число парам.етров в виде пользовательского Масса или Ci'pyRТypы.
3~Me1ЪTe, O-ДНaRtiJ. что·де!lfег.а:r P-аrаmеtеrizеdТhrеш.istа.r t может ув<а.аывать толь-
1,0 IЩ Методы. возвращающие v c;•.id.

Работа с Aen·eraTOM ТhreadStart


-Что~ paCCMOTP~ТЬ процесс создания мпОГQIlOТОЧИО1 ·0 приложени.н. на праь.-ти­
ж: (а тш,же продемонстрировать rю.п:ьзу соответствующего подхода), предположим,
ЧТ0У.fЩС есть lЮНСОJlЬьЮе приложение [.simpleМultlThreadApp), ItO'l'opoe позволяет
RЩI~QМУ ЛOJThВОБаl'(ШЮ в.ыбраn.. в прШIОЖении .либоисt:IO.1lЬЗОвaюre одного пер-
582 Часть 111. Программировани~ компоновочных блоков .NET

вичного ПОТОRД, выполняющего всю работу, либо разделение ее на два отдельных


потон:а.

После того как вы обеспечите доступ к пространству имен System.Threading С


помощью ключевого слова С# using, первым шагом должно быть определение мето­
да, который будет выполнять работу во вторичном потоке. Чтобы сосредоточиться
на сути механизма построения многопоточных программ, здесь этот метод просто

выводит последовательность чисел с двухсекундными задержками перед каждой


операцией вывода. Вот полное определение соответствующего ютасса Printer.
public class Printer
(
public void PrintNumbeI"S ()
{
1/ Отобр~ение информации по~ока.
Сопэо1е. Wr-itеLiпе ("-> {О] вьmОЛНRет PrintNumbers ()",
Thread. CurrentThread. Name) ;

11 ВЬп!iод чисел.
Сопзо 1е. Wr-i te ("Ваши числа: ");
for(int i = О; i < 10; i++)
(
Console.Write(i + ", ");
Thread.Sleep(2000) ;

Console.WriteLine();

Теперь в Ма! п () нужно предложить выбор одного или двух потоков для вьmол­
нения задач приложения. Если ПОЛЬЗ0ватель выберет ИСПОЛЬЗ0вание одного по­
тока, просто вызывается метод PrintNumbers () в рамках первичного потока. Но
если пользователь указывает два потока, создается делегат ThreadStart. указыва­
ющий на PrintNwnbers (). Объект делегата передаетс.fI конструктору нового объ­
екта ТЬ read и вызывается метод Start () , информирующий среду CLR о том, что
поток готов к обработке.
Сначала установите ссылку на компоновочный блок Sys tem. Windows. Forms . dl1 и
с помощью MessageBox.ShQW () отобразите подходящее сообщение в Main () (смысл
этого станет ясным при запуске программы). Вот полная реализация Main () в нуж­
ном виде.

static void Маiп(stгiпg{] argsJ

Console.Writ.eLine("***** Чудесное приложение


Thread *****\п");
Сопsоlе.WгiLе("Хотите иметь [1] или [2] потока? ");
string thгеаdСоuлt = Console.ReadLine();

11 имя ~екущего потока.


Thread primaryThread = Thread.CurrentThread;
primaryThread. Name = "Первичный";

11 Вывод ИЯформации Thread.


Console. Wri teLine ("-> {О) выполняет Main () "1
Thr-ead. Cl1rrentThread. Name) ;
ГлаВ1I 14. СоздаНl1е Мf:lОt"опоtDЧН'Ы)( п.Рl:!i1OжеIf11i\ 583
j I Cos:\taJD[e раБОЧ8it'О .x.nacca.
PTin,ter р = n e w Рr,l.пtеr () ;

з"" i t сп ( t h.r: eadCo::m t )


{
ca~e! "2,'1:
11 Теперь создание ПО~'О1<;а.
Thre<id :Ьасkgrm!шdтhrеаd ,.
new, 'tlл е.а lii (nе<д' Tb reGi d'gtarL 'р . ,рr:LТIt.Nu!nb€-r $') ) ;
bacKgrdUfH1Th re~d. Name = "Вторичный";
b 9 c kgro'uM.JT1H ead. Start ();
b ::.~ki
са.зе 11}":

р. Рl::intNшrtbе Т 5 ('};
b r-ea k ;
def .. u lt :
COJ1So1e.Wri t e L3. neC"B.aIJГJo'! ,указания не яс ны ... буд ет 1 nо::г'о:к.");
goto c-a.se п 1~ ;

.1 / Вwnолнение ДОm:lJlиитеm.ВQП работы.


,M essag{'JBo.x. 3rlo", ("Я заi-;f.ЯТ' !' '', "'Работа в гла;В E;i ОМ ]'1r:>'J:O'Xe ". ,") ;
Сощнй ? :?,;еаd.L:lr) €,-,) ;

Если теперь запустить эry nрограмму е- ОДi[-ЩМ потоком, :ВЫ оБШЧiJу~те. 'ЧТО.
ошю сообщения не будет отображенО. до тех пор. no:a:a на ~ОНСОЛЬ :J;Ie б3lдет Bыеде--­
на вся последовательность чисе..1L Здесь бь{щ! YJЩЗa}Iа пауза npиб~JIТельно в две
секунды ПОСJIе вывода ка,ждor,() иа чи:сел:, поэтому цодаБJ'lQe повеД~е программы
не вызовет восхищения коIreЧНОГD пользовате.rlJ{. Но ес:1IИ вы 13ы6ерете ва,риаl'I't с
двумя IЮТока.1\Ш. окно сооБЩеШ1:Н появится .вемедденно, посколь~ дл;я вывода чи­
сел ffi} IЮ1iСОЛЬ будет :ш.:пользова'tЬея свой ующадЫIы:ИобъеR;Т ThJr1ea'd '(рис . 14.7).

Рис. 14.7. МногопотоlIные приложеН~f1"боnее ОТЗЫВЧИВ!>I" при ВЬJдllУе своих РВ;lуЛЬТf!ТОВ

Здесь 6ЭЖJ-1O отмеТить , что при ПОСТРОСНИI1 МИОГОIJОТОЧНЫХ лрил[)же-шш (с при­


ме,нением асинхронны!!. д~)еrах<:ц~J I-Ia M,g пnцrщ ~ ,oдmrм Jlроцr-сеорам "вы не получа­
е!те прИJ1'ьжеI-1ие, 8ЫТlQJlЮUQЩfЩСЯ 6ыс:грее, чем ооавuллет ПJЭоцессор- мапrины . При

1
584 Часть 111. Программирование компоновочных блоков .NEТ

запуске этого npиложения с использованием как одного . так и двух потоков числа

будут отображаться одинаково. Многопоточные приложения позволяют улучшить


NОnlЗывчuвосmь" приложения. Конечному пользователю может казатЬСя. что та­
кая программа работает быстрее, но на самом деле это не так. Потоки не имеют
никакой возможности ускорить выполнение :ц:иклов foreach. операций вывода на
печать или сложения чисел. Многопоточные приложения просто позволяют рас­
пределять нагрузку среди множества потоков.

Исходный код. Проект SimpleMultiThreadApp размещен в подкаталоге, соответствующем главе 14.

Работа с делегатом ParameterizedThreadStart


Напомним. что делегат ThreadStart может указывать только на методы. воз­
вращающие void и не имеющие аргументов. Во многих случаях этого будет впол­
не достаточно. но передать данные методу. выполняющемуся во вторичном по­

токе. вы сможете только с помощью делегата ParameterizedThreadStart. для


примера воссоздадим программную логш<у проекта AsyncCallbackDelegate,
построенного в этой главе выше. но на этот раз используем тип делегата
ParameterizedThreadStart.
AddWithThreads и укажите using
Сначала создайте новое консольное npиложение
ДЛЯ пространства имен System.Threading. Поскольку ParameterizedThreadStart
может указывать на любой метод. принимающий параметр System.Object. соз­
дайте пользовательский тип, содержахций числа для сложения.

class AddParams

pub,lic int а;
public int Ь;

public AddParams(int питЫ, int пuшb2)


{
а = numbli
Ь = питЬ2;

в классе Program создайте статический метод, который с помощью типа


AddParams напечатает сумму соответствующих значений.

public static void Add(object data)


(
if (data is AddParams)
{
Console. WritеLiле (" ID потока в Add (): (О)",
Thread.CurrentThread.GetHashCode() );
AddParams ар = (AddParams)data;
Console.WriteLine("{Ol + {l} равно {2)",
ар,а, ар.Ь, ар.а + ар.Ь) i
rлава 1'4. СОЗДЗliиемногопаfО'lНЫХ /1.рt1ложениЙ 585
Пporpзммный :КОД М.ai rJ О в данном случае предельно прост. Просто испОЛЪ3уЙ.те
РаramеtеrizеdТhrеаdЭtаtt вместо Thread9taxt.
static VQ·1 d Main(st.ring[] ~rgS)
{
'c:ons'ole.Write:L ine(n*"''''*'' СЛО)r(е.ни€ с t;>t5Ъ.ек'та.ми Threa:d ..... *-"'''"'');
COrlsol!'1. Wri teLine (" ID.поrt'Q'!Сq Б Маiл О: {О'}" r
T''1.roead. СiJ.пe.ntТhrеаd. GetHashCode () ) ;

AddP.arams .ар = пеIЧ AddJ;>.arams (:J:Q, 1О):


Тhrеэd t = new 'Th.re·a d (nэ'il Раraroеtэт:h zedJihr-е,,;dStаrt (Ааа) ) ;
t .~tart (ар);

ИРХОАНblj,'j ·код. Проект AddWIthThreads размещен в пqДкатartоге, сt)отввл;твующем главе 1.4,

ПриоритеТНЫ'е и фон'овыe потоки


Итак. вы научщIИСЪ ПРQграмjI4JЮ создавать щщы.е ЦОТОКИ въшолнения с помо­
щью пространства име» Sу,stещ.тhrеаd.ing. тепер~ дc:mайте ВЫЯC1ПDl!. чем 01ИИ'Ча­
ются npиорвтетные и фоновые потоки.

• Пр.uoрuтemuые Т1..QЛ10К.ti обеt;лечивают теRYщему прилощеНU10 защИ'IY от


преждевремеmrого эаооршеmш. C~a CLR 1',Je npекра;ги'f pa(5o'ry приложении
(лучше сказаn,. Jle вьпруэит соответствующий домен прцяожfши.я.), лоха не
завершат работу все nриоритетныс пото.ки.

.. ФDНОВьtе n.оnюкu tиногда :нааываемые деМОН4Ми) рассматрпnа;ются средой


СЩ как во.зобновл.яеМЬiе ветв:и ВЪШОЛfI~Нцg. RОТОр:ЫМИ: .можно преJi'ебреч,Ь
:в люб.оЙ момент времени (~аже при ВЫПOJIFlении ими (."воих . задач:). ПОЭТQМ)!.
Rorn,a все приоритетны:е потоки завершаются. вее фоновы:.е потоки бу.цут 1Щ.­
вершенът a:ilТОМЗ1'1iiЧеакив реауJШI'ате :выrpуsЮf домена прило.же,пщ.

Важно понять. что по.IU1ТИЯ npиоритетн:ого. и ф.оновOI·О Пото:КОВ - это не ~;ИНО­


HnМЫ поliяfi{Й ШфБИЧ1-1QГО и рабочего потока. По умолчанию R,ажды:й J;ljJTo.R. СОЗо­
даваемый с. помощью метода Thread .Start О. автоматически. ока;зыв~:гсл проори­
mertlН.blМ ПОТОКОМ. А по. Зliачит, <По домен DpИJIоже1ЩН вебjДет :выгружен до тех
пор. пока в нем все потоки не эаверuta'I' свою работу. В большинстве cдy'-IaeB ЭТ0
будет именно. тем повеДением.. lCоторое требуетса.
Но предположим.. что нам нужно вызвать Printer.PrintNumbers () во вторлч"
.ноМ потоке, который до.лжен действовать. каи ФЩIо.вый по.Тан. Это означает. что
ДЛЯ метода. :На lЮТорый укаэьrnает типThre<;td (посред(:Твомделегата Thread'S tart
или Parame.t erizedThreadSta.tt). должна щшуСКВ:J:'ьсн ВОЩl.1ОЖЩ){..'ТЬ безбояе-знен­
ного его завершения. KaR ТОЛыtо все пр.иоритеТI:!Ьte ЛОТОIЩ закОНЧ;:П с,вою работу.
ДJш настройки -такого пото.ка достаточно устkUlОВИТЪ аначенд:е true (истина) для.
свойства IsBackgroutl.d.

sratic vl:Jid' l~~in (str:l.rщ(] args}


{
J?iinter р = леw P;I;inter () ;

1
586 Часть 111. Программирование компоновочных блоков .NET

Thread bgroundThread =
new Thread(new ThreadStart(p.Prin t Numbers));
bgroundThread.IsBackqround = true;
Ьgr о uЛdТhrеаd. S tаrt() ;

Обратите внимание на то, что метод Main () здесь не вызывает Console.


ReadLine (), чтобы гарантировать присутствие консоли на экране до нажатия ЮIа­
виши «Enter». Поэтому при вьшолнении этого приложения оно сразу же преRра­
тит свою работу. так как объект Thread сконфигурирован для работы в фоновом
потоке. С на чалом работы метода Ма i n () создается npиopиmem.нЫЙ первичный по­
ток. поэтому. как только выполнение программной логики Main () завершится, до­
мен приложения будет вытружен. и это произойдет до того. как вторичный поток
завершит свою работу. OAНaRo. закомментировав строку, в которой устанавлива­
ется СВОЙСТВО IsBackground. вы обнаружите, что на консоль выводятся все числа.
поскольRY для того. чтобы домен приложения будет выгружен из содержащего его
процесса. все приоритетные потоки должны завершить свою работу.
Обычно конфигурация потока для выполнения в фоновом режиме может быть
полеЗна тогда. когда соответствующий рабочий поток выполняет Некритичные за­
дания. которые оказываются не нужными после завершения выполнения главной
задачи программы .

Исходный код. Проект BackgroundThread размещен в подкаталоге, соответствующем главе 14.

Проблема конкурентного доступа


До сих пор все многопоточные приложения. созданные вами при изучении ма­

териала этой главь~, были устойчивыми в отношении потоков. поскольку в них


соответствующие методы вызывались только одним объектом Thread. Конечно.
некоторые из ваших приложений могут быть настолыю же простыми, но боль­
шинство многопоточных приложений содержит очень много вторичных потоков.
е учетом того, что все потоки в домене приложения могут претендовать на до­
ступ к открытым данным приложения одновременно, представьте себе, что может
случиться, если к одному и тому же элементу данных получит доступ множество

потоков. ПоскольRY планировщик потоков может приостановить работу потока в


любой момент времени. что будет, если ПОТОR А будет отстранен от выполнения
своей работы на полпути до того, как он эту работу завершит? Поток В будет чи­
тать некорректные данные.

Чтобы ПРОИJшюстрировать проблему kom-typенТJЮГО доступа. давайте построим


еще одно консольное приложение С#, которое мы назовем MultiThreadedPrinting.
Это приложение будет использовать масс Printer, создащIbIЙ нами ранее . но на
этот раз метод Pr i n tNumbers () "заставит" текущий поток делать паузы произволь­
ной длительности в соответствии со случайно генерируемыми значениями.

public c lass Prin t er


(
public v o id Pr iot Numbers()
Гла'Вв 14. Создание МI10ГОПОТQЧ'I'1ЫХ -Г/риложен~й 587
fQr (iпt 1 = О; i < lOj 1++1
{
Rалdшn, r = пе~ Rаш101J1 (.) ;
Thtead . Sleep (1600 ., г. N'e :xt (5) ) ;
Соп'sоlе. Wr 1,te (1 + '., "):

СОП$оlе. WriteLin_e () ;

M~TOД Иа i f1 () отвечаетэа со:щание- массива из десяти об'1.е1i:ТОВ Т h т 'е а d


fc- УНИ;ЩlЛЬНЫМИ именами), каждый ИЗ , КОТОрЫХ: вьt.'U;Шает ОДИfi И ТОТ же энзеМIIЛЛp
Рriпter.

class Program
{
s',t at.Lc void Main( s tr'illg[] ilrgs:)
j
cousru,e. Wr 1 t-еL1 r.e ( Ну ... " *'* Синхронизаu,ия. ПQ'rСУ<оСНJ .. ~,~ '" *\I! '7! :

P:r1tlt",r р = nес" Pi1n_t6!:t();


/I Соз;цавие, 10 nO'1'olto'a 7 у.QЗ~i!IDIQJiJ(. lЦIi O~ и тo:r.e меТОД
1/ ОД"ОЮО и 'l'Щ:'о ~ оБОИlC'J!iI.
Tbread[] tb'r -ead's = new Thre,a d[lO];
for (iлt i = О; i '( 10; iH)
!
threads l :i J =
n:<~М Thread (new, ThreadStart.<p, I'l"intN\lmbers));
thr~acls \ i J . Name = string. f'оrшаt ,( " Р.аб u.....-ий по:r,С!к lН 'О)"', i);

/I Теперь C'1!ap~ JQa)QОЖ'О их &их.


fo:t'ea:ch (Th'I e:ad t in threads)
t,Btart(};
COn.501e . Re,a dLiI\e () ;

Перед T~M «ан вьtnОJIНI:IТЪ Тестовый запуск про,граммы, давайте обсуди:м :со,
оrnет<:твуюtцyю npобnему, Зд.ес:q первИЧНJ;!Й поroк в p~lIofКax домена прилоЖt1НИя
порождает десять ВТОРИЧНЬJX рабщпцr. ПОТОООВ'. ~oмy рабочему потону дается
ур;аэание въtзват-ь метод P.rint_Numbers () одного'J;J ТОГО же эн:эсм:гumра P,r-i nt er.
ПОСКOllli;НУ ЗJ];есь не предпринято НlЩaIOЦ( мер по блоки:рованию 6бщедостynныx
ресурсов дш-uroго объекта (КDНСQЛl~). имеется болъш;i!Я веро.ягтоСТь ТОГО, что ТеЕу'
щий поток бу)];С'Т npиостан;овден до 'Г0ГО-, 1ЩК метод P"rilltblum'b 'e rs _() зaEmiЧИТ вы·
ВОД BteX своих реЗУJlI;I'Гатов. Бh\ лезцаете точно, :когда это СJ1YЧW.IЪСЯ (:и случитс;я JП'I
ВQобще). поэтому Е)'ЖНО быть WТФ:ВЫМ к непредвиденным реэулвтатам. Например,
может ПtiJ1y'-ШТLСЯ BlЦBOД, показaн:ный на рис,. 14.8.
ВЫПQЛIПIте прююжение еще lIf"CКОЛЬИО рЩ!. на рис. 14.9 ПOItазана ,цpyтaFI воз­
можность вывода (вапщ результаты. очеВJЩНО', тоже буд.Y'r' цpyrими).,

~
588 Часть 111. Программирование компоновочных блоков . NET

Рис. 14.8. Конкуренция в действии, первая попытка

Рис. 14.9. Конкуренция в действии, ВТQРая попытка

Ясно, что проблемы здесь деikтвительно есть. Каждый ПОТОI< дает указание
объекту Printer печатать числовые данные, и планировщИI< потоков запускает
выполнение этих потоков в фоновом режиме. В результате получается несогласо­
ванный вывод. В этом случае мы должны программно организовать синхронизо­
ванный доступ R совместно используемым ресурсам. Нетрудно догадаться. что в
пространстве имен System.Threading есть целый ряд типов, имеющих отношение
к синхронизации. А язьrn программирования С# предлагает специальное ключевое
слово, как раз для решения задач синхронизации совместного доступа к данным в

многопоточных приложениях.
ГМва 14. C03AВH~e Мl'ФiОП0ТОЧНЬ)Х ГJРИJ10жен.иЙ 589
З8МfJlfакие. ЕслИ У вас не получается mHep~Ol!aТb непреДl!идеННI>1i11 8МВОД,. 'увеличьте 'Iиело
потоков с 1О ДО 100 fнаПРИМ6Р) и1tИ добавьте в свою программу вызов Thre:a d. 51 еер ( ) .
,8 kOtЩе концов. 8ывсе ра'вно СТОllкнетесь с проблемой КОНКУРЕЩlIНQГО Д{)СТУГJR.

Синхронизация с ПОМОЩЬЮ ключевого слова lock в С#


Первой из возможностей, которую вЫ можете ПJ?имemnъ в С# дл.я: CIO-IXpониза.
ЦIO.I ДОС'l~'JЩ К COвмeCТJ:lO исnользуемhI:М ресурсам. ЯЩIЯе1'СВ использование ключе­

вого СЛОВа 19~k. Это НЛ:ЮЧeJ30е слово позволяет определить XORТ~кcт' оператDpОВ,
:которые ДО.JJ;ЖНЫ СИНХрОНИз.И-РGВaТЬСfl междУ IJОТОКСЩИ. В реаупьтате ВХОДJIЩИе по­
ТОКИ не cмoryr прерватъ T~ ПОТО:К. пока 01f выполняет свою работу, Ключевое
СЛQВО lock требует. чтобы выуказaJIИ мщжер ,(объектную ссылку). который потре­
буетс-я- rт.oToвw ддя входа в предеJJbl КОНТl'Щста lock. При блоиир.овке метода урcmня
<шземrшяра МОЖНО шшользовать просто ссылку на текущий тип.

/1 Иcnоm.ао.аиК8 T.~~O Clб~'J'а • JC&UoТJt8кapxep_ ПО~О~·.'


}ос]с (t:h,i $)
[
11 Веш. npQ:rop~ ХОД • 'ЭТQJI ·ХОК'1!8ХС'1'8 QJ(~a8'1'CI!!I
11 YC'1\Q~nJUIDI • О!1'ИourенИи пО'1'ОЖО •.

Прив-mrмaтeJIhНОМ взучеnии метода P~i[jtNumbers () C'I'aн()BJ.;rТCR ясно, что :со­


BM<hICТНO используемым pecypeoN;, за ДОС'ryII k 1toтopoмy соцерtnIчают потоки. яв­
ляется окно консоли. ПомеСТll'):е в рщ.mи соствe'rC'Гвyющеrо KOWreКCTa блOЮlpОВ~И
все операторы вааимодсйс'I'ВИЯ с :nпro:м COfiBo1e так, пах показано цже .

'p ublic ~оИ Рr i:1tN)JПIbеrs О


(
lbck(thi.S)
{
11 Вlil:ao:ц . ЮtФор~Тbrеаq..
Cp11s0-1е.Writ;еLinе ("-> {О} .вЫnОЛНR€!Т РтiлtNштlbе.I'S О" ,
ThJ:.ead.Cur.rentT,bread .,Name) :

1/ ВlDtOД ЧКСIIJJ.
ССФ~..юlе. ,Writ·е ("Ваши числа ~ П);
fOI' (int i = D; i < 1 О; i ++)

I\€1.лdоm
r = new Random () ;
Sleep (1 ооЬ ... r .1'Iext (5 J)
T,Me-ад .. J
COh$ole.Write(i ~ ", ");
)
Cansol-е.Wri't.еLinе()t

'Тем самым вы создадите Meгroд. который по~вэл:ит тену:щему потоку заверШJJТЪ


выDлненцеe jj;воей задачи. КШtТO.1IЫЩ поток ВС'I}'ПИТ В :КОНТeJ{С'Г блоIЩРОВКИ. СООТ­
В~ТСТВУlOЩl\1Й мар:кер блшшровки (В .данном сдучае ЭТО ссылка на 'I:е.кущий объеRТ)

J
590 Часть 111. Программирование компоновочных блоков .NET

станет недоступным друтим потокам. пока блокировка не будет снята в результате


выхода потока из контекста блоRИРОВКИ. Например. если марн:ер БЛOI<ирОВЮ1 по­
лучает поток А. то друтие ПОТОRИ не смогут войти в контекст до тех пор. пока поток
А не освободит маркер блокировRИ.

Замечание. Если пытаться блокировать программный код В статическом методе, в 1:>1 , очевидно,
не можете использовать кпючевое слово t h i s, Но в этом случае можно передать объект
System. Туре соответствующего класса с помощью оператора С# type o f,

Если снова вьшолнить это приложение, вы увидите, что теперь каждый поток
получает возможность закончить свою работу (рис. 14.10).

Рис. 14.10. Конкуренция в действии, TpeTbll попытка

Исходный код. Проект MultiThreadedPrinting размещен в подкаталоге, соответствующем главе 14.

Синхронизация с помощью типа System.Threading.Monitor


Оператор С# lock на самом деле является ЛИIIIЬ ключевым словом, обознача­
ющим использование типа класса System.Threading.Monitor. После обработки
комuилятором С# контекст блокировки превращается В следующее (ВЫ можете
убедиться в этом с помощью ildasm.exe).
public void PrintNumbe rs()
{
Monitor.Enter(this) ;
tr y
{
Глава 14. СоздаН\Iie мноrnпоточных пр~no'же~ий 591
/ / ВЫвод ИIЩlорнаФtИ Thread.
С~:n-sсlе.Wrltе'Linе("~> jDI ВЪШQЛНЯ:ЕТ Р)riпtN\лnЬеrs()"/
'.!'tlread. C1l1Trent Thre.e d. Name) ';
11 ВJ.tвoд чиеen_
CO.n.Eo l€. W.r.it·e ( '''Ваши чи сла : "1:
fo J." (ii.1t i = ', О ; i < 10; i-t+)
·l
.Rалdош .r' = пеw Random () ;
Thread.Sleep(1(rOO k r.N'eKt(5))j
Co·r'E Gle. 't1,r l.te (i +", ");

COnSble.Write~in~f};

:Еillэl1у
!
Nanitor.Exit(this):

Во-первых, заметим, ЧТО' конечным получателем маркера потока, .который был


J1Ш.:;;aJН в JЩ че(:тве аргумента ЮIЮчевого, (Слова lock. яв.hЯется метоД ИО.пito.r.E ri ter j ) .
вQ~lп'орых. вес.ь протрамм:ный ItOД в рам:ка..х 1tORтeHCfta соответствующей блОJ<ИРQВ­
к,и ПОМеЩ~ Б.БЛОЕ try. СоотвеТ{)твующий блок fina l ly таращирует. что MapJ:<ep
ПОТО}Щ будет освобожден (с помощью метода l'J,oni tor. Ея it ( J). lIеЗillJиlCnМО от 1IС'­
щrюЧЩiИЙ, Rm'opble мотут вoзникa::rъ В сред:е. выполнении. Если ивменЦIЪ npoграмму
МыltiТЪн:'еаdSb.аrеdDаt? таК, чтобы ТИIН1l0п itоr Йсполъзовал.ея непосредсТDelЦlО
(щuc это будет сделано "JYТЬ позже), ТG) ее вывод QС<ГШiетм тем :Же.
ПРlj исп<шьзовании .ltlIКiЧевот,о слова 'l oc к, кажеа:ся. требуется меныnй.И ВВОд
ПРОГРЩVlМНото коца, чем при явном .ilсшr.шьзованшJ типа S·ystem,Threadi!1g.
Мог] i t от. поэтому вы :можете задать вопрос о преимущеетв8..'Ii; нenосрсдеТJ>e.l:IЩJГО
~е;uользова:ния типа Mo.nit,o!'. КРВП{ИЙ ответ: КОJ-rrролъ. При ИСnОЛЬ30вЩiИИ типа
Monl tor вы можете дать указаниеакorивному потоку nодожцать (с DЩdОЩblO метода
Wai t ( )), информи:pQВaть ожидающие потоки о зав.ершении 'l'еRYЩего ПОтОRЗ ((' по­
МО~Ю методов Риlзе () и Р tlls.eAll () ) и Т.д.
В большинстве сл:учаев вам будет вполне Достаточно В0ЭМОва.tOстеЦ. обеспечи"
Баемых ИЛЮЧДВЫМ СJюDом С# lock. Но если:вы 3а.'ltотите рассмотрет}. дpYТlle члены
клаеса MorJi t-ш:, обратитесь 1( дсшу'ментэции .NEт FIa1Ilework 2.0 SDK

Синхронизация с ПОМОЩЬЮ типа


System.Threading .Interlocked
Б зто все-гда веритсSf с трудом, пока вы не проверите соответствующий цро­
граммный код GIL. но' и оп~раЦJiIИ присваивания. И ба30выеарифметические otte-
раци:и не ЯВМIOmся аmoмдрl-tЬLМtL IТ9MOМYB пространстве имен System.Thtea.ding
npeДlIaгaeТCJ'I тип, nOS.80.11SЩUЦИЙ воздейеТl'ювать на отдельным элем€НТ данных ато­
марно сменыпей нагРУЩ{ОЙ, ч;ем это целает тиn Mo-nitОI. Тип масса lnterloc'ked
Qпредел~ет етатwreские ч;лены, "(iШисавия Ii6TOPЫX приведены1 в таБЛ. 14.4.
592 Часть 111. Программирование компоновочных блоков, NET
r
Таблица 14.4. Члены типа System.Threading.lnterlocked

Член Описание

CompareExchange() Безопасно проверяет два значения на равенств{), и если они равны,


заменяет одно из значений третьим

Decrement () Безопасно уменьшает значение на 1


Exchange () Безопасно меняет два значения местами

Inc rement () Безопасно выполняет приращенив значения на 1

Хотя это может и не казаться очевидным на первый взгляд, процесс атомарного


изменения одного значения является вполне типичным в многопоточном окруже­

нии. Предположим. что у нас есть метод AddOne ( ) , который увеличивает целочис­
ленную переменную intVal на единицу. Вместо программного кода синхрониза­
ции, подобного следующему:

public void AddOne ()


(
lock (this)
{
intVal++:

можно предложить более простой программный код. в котором используется ста­


тический метод Interlo cked . Incr-ement (). Просто передайте переменную для при­
ращения по ссьmке. Обратите внимание на то. что метод Increment () не толыю
изменяет значение поступающего параметра, но и возвращает новое значение.

public vo id AddOne()
(
int newVal = Interlocked.lncrement(ref intVal);

В дополнение 1\ Increment() и Decr-ement() тип Interlocked позволяет ато­


марно присваивать числовые и объеIcrные данные. Например, если вы хотите при­
своить члену-переменной значение 83. вы можете избежать необходимости явного
испоЛЬЗОвания оператора lock (или явного применения логики Monitor). если ис­
пользуете метод Interlocked.Exc hange ().
public void SafeAssignrnent ()
(
Interlocked.Exchanqe(ref myInt, 83);

На:конец, при проверке двух значений на равенство. чтобы обеспечить потоко­


вую безопасность элементу сравнения, можете использовать метод Interlocked.
CompareExchange (). кш< покаЗано ниже.

public v oi d CompareAndExchange()
(
/ / Если значением i IIВЛ5lе'l'С51
83, измени'1Ъ еl'О на 99.
Interlocked.CompareExchanqe(ref i, 99, 83):
i Глава 14. Создаftие .tlНоrоnотnЧНЫХ прИ11ОжеtМi 593

Синхронизация с помощью атрибута [Synchronization]


Последним ие рассмо'Греаньщ: ЗД~СЬ дримитиво:в СIUIХJЮJ;IИЭации будет атрибут
rSулсhrоnizаtiОn] • .I<ОТОРЫЙ определяется в ПРОCТJYdЩ:твеИМ~JI $у, sтеm.Ruпtimе.
Rm:noting .Солtехts. Этот атрибут уровн.и класса NJf! G~ОП;lсноС'FИ поток.аэффек­
ТИВПQ блокирует весь програмю-IblЙ НОД члещ>вэкзем.щшра.. Цогда среда CLR раз­
меЩает о15ъек'f. nмеюЩИЙ атрибут jSy;n.c:;n:ranizatic:m], ОЩi помещает ЭТОТ объект
в рамки СЩПРОI-IИзйррваl-ШОГО контекста. Вщ дрлЩl-Щ пЬ~ из rлавы ] 3. что
объекты. :которые не ДОЛЖНЫ IlOI{.Идатъ Rонтекqцые границы, ЯВJIЯю'i'CЯ npои..·uю­
д8ыии от Соnt.эх-tвооndОЬj e.ct. Поэтому, 'ЧТобы сдедат!? тип .класса Friitter устой­
чивьtм В O'fношеющ потоков fбез добавлении протраммнQТО кода защиты потоков
вручную). слеДУе"t изменить еоответс~щее определение так.

UЗil'lg System. Runtime . Remoting . СQпt~:хtз;

11 BCC!I мe'1'o~Prin.te% '1'e.~ъ ПО'1'окоуq!l'OЙЧИUI!


r SynohronU.at.Lon]
рuЫ ic class Pxfti'ter : C"ntextB9\lndObj ect
[
public VQ i d PriIltN.u mb.ers ()
{
, ..

ЭТQТ по~рд можно назвцть способом со~даниа потокоустойЧИ'вого програм­


много КОДi3.ДЛЯ ленивых, пеСRЩlЬКУ здесь нетребуетснlI:ЫнСНЯТЪ. Ка:к:не фрагменты
типа Мд!'УТ иcnьnъцшть ~e- ниеUi1НИX ПОТОКGII. !JIaвным Нf:ДОстаТНQ"М Этог.о под­
llрда Я;В'Jlnется ТI',), что даж~ ·если канОЙ-ТО метод и не испытывает влияния внешних
по:rоков. среда CLR все РЩ1Нn блокирует обращение:к этому :il.leTo~. Оче;вИДI-rо. ЭТО
.Может УХУДШИТЬ обпще хауахтерисТИЮI ФУНКЦИ:овированин типа. так что исполь­
зуйте ~ан:ный ПОД2ЮД с осторожлостыо.
Итак, мы с вами обсуЩJЛИ цeлblЙ" ряд подходов . к решеНиio вопроса СИНХРОRИЗИ­
pOBaRFJoro AOC'IJlI1a к общим БJ1OIi8М данн:ых. B~дьтe уверены. в пространот.ве имен
sy's tem.Thre.q:ding еСТЬ и друтие ТИПЫ, которые я настоятельно рекомендУЮ вам
nостеш:нно иеследрвать. В зввершение этnй шаны. посвященной протрамМйро­
ванию потоков. дalШЙте рассмотрим ТРИ доподнйтельных 'ГИIIa: Ti.rne,tCal1back.
' :!'imfqr и ThreadPoo].

Проrраммирование с помощью
таймеров обратного вызова
Во многих цриложения:х ВОЭН1Щает flеQбходимьсть вызывать КОНRрет.яый M~
ТОД через р~гул:ярные ПроМежутхи времеlUl, Нащщмер. в одном qpWIOжении 'МО­
жет ПОТребоваться отображение тещтщего времeIЩ в строке соетоl'Il:ЩЯ с ПОМОЩЬЮ
неК6ТОРОЙ вапомоrа1'елъной фушщии. В другом приложещrn може-т цонадобпты:я
IIерподичесю-I'Й ВЫЗОВ вспомогательной фущщии. ВЬЩQшщrощей ~ фЩIOВОМ режиме
какИе-то .неRри-rич:ески~ эадач:и, например DpШЩРl\}7 rlOcтyтre;mцJ цoBыx соqбще-
594 Часть 111. Программировани~ компоновочны)( блоков .NET

ний электронной почты. Для таних ситуаций можно использовать тип Sу s t ет.
Тhгеаdiл g .Timer в совокупности с соответствующим делегатом TimerCa l lback.
для примера предположим, что нам нужно создать консольное приложение, ко­
торое ежесекундно выводит текущее время, пока пользователь не нажмет клавишу ,

завершающую выполнение этого приложения. Первым очевидным шагом здесь яв­


ляется создание метода. который будет вызываться типом Timer.
class TimePrinter

static void PIintTirne(object state)


{
Сопsоlе.WгitеLiпе("Время: (О}",
DаtеТimе.Nоw.ТоLопg·ТimеStгiпg(}) i

Этот метод имеет один параметр типа System.Object и возвращает void.


Танан структура метода обязательна, поскольку делегат TimerCallback может вы­
зывать только тание методы. Значение, передаваемое целевому методу делегата
TirnerCallback, может представлять любую информацию (тан. в случае злектрон­
ной почты это может бьггь имя сервера Мicrosoft Exchange, с которым требуется
взаимодействие в ходе процесса). А так как параметр является типом Syste.m.
Object. в действительности можно передать любое число аргументов, если исполь­
зовать System.Array или пользовательский класс (структуру).
Следующим шагом является настройка экзеМШIяра делегата TimerCal lback и
передача его объекгу Timer. Кроме делегата TimerCallba ck, конструктор Timer
позволяет указать дополнительную информацию (в виде System.Object) для пере­
дачи ее целевому объекту делегата. временной интервал опроса метода и время
ожидания (в миллисекундах) до начала первого выэова. например:

static void Маiл(stгiпg[] args)


{
Conso le.WriteLine("***** Работа с типом Timer *****\п ");

11 Создание деnегата. дn_ !t'ИПa TiJaer.


TimerCallback timeCB = new TimerCallba c k(PrintTime);

11 УС'1'aJlовка параиетров таймера.


Timer t = new Timerl
timeCB, 11 Тип деnеrата TimerCallback.
null, 11 Информация ДJI5I вызываемого нетодаиnи n.ull.
О, 11 Время о_дания до старта.
10 О О) ; 11 Интервan между вызовами (8 ниnnисехундах) .
Console. Wri teLine ("Нажмите «Епtег» ДЛЯ завершения работы ... ") ;
Console.ReadLine() ;

в данном случае метод PrintTime () будет вызываться примерно каждую се­


кунду и методу не передается НJmaКОЙ дополнительной информации. Чтобы пере­
дать целевому объекгу делегата какую-то информацию. замените значение nul1
второго параметра конструктора подходящим значением (например, " Привет" ) .
Следующая модификация метода Print'rime () использует переданное значение.

....
i
Глава 14. GО3Д8.н.ие миотОnОl'ОЧНЫХ Приложений 595
sta'tic void Print'I'ime (Qpject st;ite)
{
Console.WriteLineC"BgeUR: i O). ПарамеТi?~ \1J",
D<lte~ im-e .N'o\~. То LqfigТщев't rJ.ng
(), s ta.te . ToSui'ng (~ ) ;

на рИс. ] 4.1] I101tазан соответС1Щующий ВЫВОД .

p~c. 14.1 f. Таймеры за ра:бетой

MexoAtlIiIilt код. Проект TimerApp ра,змещ!'JН в под~таI10ге\ соответmвующем ГJJaBe 14,

Пул потоков CLR


3аклЮЧJ5тельнсЩ- теМой нащего обсуждеюш в этой отаве" посвященной UОТОIiШY.I,
будет пул потоков CLR. npI~ a"X>IНXpoннo.М в.ъдюве ТИl)IШ с ПОМОЩЬЮ делегатов {rю­
средством метода ВеЧinI,f\vоkе О) иедьзя сщыать. '-ГТО среда CLR буквально' создает
совершенно новый IIOTQJ<. В целn.", ,эффективности метод HeginI Dvoke () дел wата
ИСlIолъау~ дул (динамичеС"кyIO областъ.) рабочих цотоков, поддеРЖ~IВаемых средой
выполнeн:uя. Чтобы ПОЗВОЛИТЬ :вам вЭ''nIМо~ейсТВовать с ЗТ~ пулом рабочих IIO'!{)-
ков. npоетрaRCТБt.1 ИМfШ Syst"m. Thr~,a,r:ii fJg пред.тrагает тщr класса Т11 reactPool,
Чтобы поставит}> 1;tЫЭОВ метола В очередь для обраQОТКП рабочим ПОТОКОМ из
пула. используйте меТОД Тh r!'?iЭ'dРоol . Que1j eUserWorkJ tem ('). Этот метод ЯВJJЯетс:н
nерегруженныМ', чтобъ/ вдобавон J< Эlfэемruщpу делетэ,та WaitCallb.~i(::k имелась ВО3-
МОЖ}IOСТЬ указать необязательный Syst.em .. ОЬ j ее t ДДЯ пользовательских ДaJ-ЛПill(
соСтояния.

р'ирНе sealed class l'hreadPo01


I

public statie bool QаеllеU"е;гWо'rklt.еПl (WaitCallba.c]c саllБас:k),


p.tJ.blic stat.ic: Ьtюl Ql.\ еuёUs ~rWо!kГtffin (W<JitCq.ll back callBa'ck:,
object state ) ;
596 Часть 111, Программирование компоновочных блоков ,NET

Делегат WaitCallback может указывать на любой метод. имеющий один пара­


метр System.Object (для представления необязательных данных состояния) и не
возвращающий ничего. ЕсJШ при вызове QueueUserWorkltem () вы не предложи­
те System.Object. среда CLR автоматически передаст значение null. Для иллю­
страции методов очереди при использовании пула потоков CLR давайте рассмо­
трим следУЮlЦ)'Ю программу. в которой снова используется тип Printer. Но на
этот раз мы не будем создавать массив типов Thread вручную. а свяжем метод
PrintNumbers () с членами пула.

class Program
(
static void Main(string[) args)
{
Console. WriteLine ("Старт главного потока. ThreadlD = (О 1",
Thread.CurrentThread.GetHashCode()) ;
Printer р = new Printer();
WaitCallback workItem = new WaitCallback(PrintTheNumbers);
/ / Очере~.. из 10 810130808 не'Х'ода.
for (int i = О; i < 10; i++)
(
ThreadPool.QUeueUserWorkltem(wo rkItem, р);
I!

Console. Wri teLine ("Все задачи в очереди") ;


Console.ReadLine() ;

static void PrintTheNumbers(object state}


{
Printer task = (Printer)state;
task.PrintNumbers{);

Здесь вы можете спросить. разве выгодно использовать поддерживаемый сре­


дой CLR пул потоков вместо явного создания объектов Thread? Тогда рассмотрите
следующие главные преимущества использования пула.

• Пул потоков управляет потоками эффективнее. поскольку минимизируется


число потоков. которые приходится создавать. запускать и останавливать.

• При использовании пула потоков вы можете сосредоточиться на своей кон­


кретной задаче. не отвлекаясь на вопросы инфраструктуры потоков прило­
жения.

Однако управление потоками "вручную" может оказаться предпочтительнее,


например. в следующих случаях.

• Если требуется создавать приоритетные потоки или устанавливать приори­


теты потоков. Потоки, помещенные в пул, всегда являются фоновыми пото­
ками с обычным уровнем приоритета (ThreadPriority.No r ma l ).
Гла.еа 14. Создание MHorOnOTO'If,tbI)( пр~ложе~ий 597
• Если требуется создщъ поток е фнксировэнш.rм идem.ифика1fQРОМ, чтобы JЩе­
лась возможность завершить. np:иостановить ил'и обнаружитц его по име;н;и.

ИСХDд.нwjij .IфД. ПРоеКl" ТhreadPI::JotApp размещен а подкаталоге, С(jОl'ветс:rеУlOщем гnaB6 14.

На этом наш знскурс в IvIногопото~ое прогр.а,ммИРGЩCnJ;ие .NET завершается.


Пространство имен sуstеm.тЪrеаdin.g. без CONU-JeFЩя.оцредел.нет множество дру­
Diic ТШ1ОВ. :кроме тех. KQТ-opыe сМоти уместиться в ра..1\ЩЭХ обсуждения данной rла.­
.вы. Но сеИ-Час йы имее!l"е проt..rНЫЙ фундамент. ко.торый LIо3ВОЛwr вам раСШИрять
CВO:fI анmш:я.

Резюме
8та глава начадас:ь с рассм.отреП1fЯ -Гo~o. как настрои:гъ ТИП делега:r.a .NEТ 1Ja
вызов Meтoцo~ в асцJiXPОю;rоЙ' форме . К.ап бъшо поШiаЭНо. методы Begi:oIh'J'oke ()
и EndIhvoke (} ПОЗЩJ.rrюoт JWCJ3eHHO управлять фОНОВЫМИ потоками с минималь­
НЪЩn ус;щrиями и ЦРaJ(тичес~и бе& llроблем. В .ходе обсуждеIn1Я БЬJlШ рассмотре­
ны интерфейс l1\.syncResU1lt f[ тип класса AsуnсRеБDlt. Эт'И n-rпы обеспеtшвают
ра3.!Uiчиые· СПОСQбы СПВХРОfIИЗации :ВЫЗОВОВ И noлyченив: DОЗ.l1ращаемых знэчеНИЙ'
методов.

Оставшэ.пСсЯ часть maвы ()ьта посвящена выяСнению pom-z пространства .имен


Sу ~tе-Пl_'rhr еа 'diпg. ВЫ )'<lНа,ли о ТОМ. ЧТО' В резулшатс создана..l приложе:ние1vl до­
ПОДНИ'Fельных ПОТQIЩВ программа получает (м:mrмyю) ВОЗМОЖНОСТЬ выполнятЬ
МНОЖ!:С'Х'ВО3М~flЙ QДН;ОВремев::но , Были раСсмотрены различные сrroсобы ЩIJЦIn'Ы
бщжов проrРЩ4,МНОГQ· ЩJда. УЯЗВИ:М:ЫХ в отношении ПО1'О:Кcm. чтобы при COm.{e.CTHOM
исцодъзовании ресу;рщш потоками не .происходило повреждения данных. Наконец.
ВЩ узнали о ТОМ, что среда CLR подцерживаe'I 'пул пОтокав с Ц~J1ЬЮ nо-выmе.в:шr об­
щей ПРОИЗВОД:ИТ~НОСТИ системы и УДQ.бства ее использования.

./

1
ГЛАВА 15
CIL и роль
динамических
компоновочных блоков

В Э.ТОИ гmme СТаБятсядве заДачи. В первой lЮЛ(])Ji!ине ГШUiы будет рассмотрен


СИНТЮtOис и семантика Я"3ыка CIL (Соmшоп Iпt.егшеdiatе Langtrage -
прамежут.оЧНЫЙ язъш) намного более поДробно. чем в преды,цу.щИХ главах.. ЧеClГНО
общий

roВоря. при создании npогрзмм .N'EТ вПолне можно обоmисъ и без неnосредствеll­
ного· и<!учения подробностей внутреннего устройства СIL-кода. ОДНaIЮ, изучив
основы CIL, вы получите более глубокое понимание того, кaI< функционируют s:e-
KOTOpЪJe Ммагические;' особеннос:тн .NЕТ{:например. меЖЪЯЭЫНО1Юе :наследование}.
Н CJставшейс,Я части l'ла:вы будет исследована роль npостранствз имен Syst€m.
Re"flectlon.Emit. Используя его типы. вы получаете возможность СТРОИТЬ rtpo-
граммиое. обеспечевие. способftое генерировать .кО:МnОИОlЮ"ШЫе бл.Оl.И .NEТ:в па·
мяти :во время выnаЛНiШИ:fl.. Формально ·комnоновоqИJ:;Iе блоки, определенные и
ВЫIIO.1IНяемые й rr.aм.яти. называют· дUf.tамuчес-кшиu KDMn0}-t080Ч1LЫМLI. б:тtoк:aмu. Raк
вы можете догадаться. эта специалыraJI :ВОЗМОЖНОСТЬ .МЕТ 1'ребует :шания Я3ЫJ{а
СП-. поскольку от вас потребуется УRазать набор CIL-ИlIС'l'pyКцrm. Е(}торые будУТ
использоваться пры С-uз.дании RGМПО:НОвоЧН'ого блока.

Природа программиро'ванИSI в терминах CIL


CIL - ~T() родцой Я3Ьf}( IМатформы .NEТ. i\oгдa вы создаете компоновочный fЩOIt
,NEТ. используя тот yuр;nuшеМhJЙ SIЗЬШ:. "Иоторый ВЫ предпочитаете, еоwrветст,ву-
10IIUfЙ КОМlIЮ1J'Jтор переводит .ващ исходIJыIй нод В терМИНР1 CIL. Подобно 'шQбрму
.нзьп~ nPОГРa1'iIМИРОвания. ЯЗЫ.К CIL npeЩIal'ает множество прurра:м.мньц и СТРУК­
rypныx )JeJ\:c.eм. ПОСКолькУ CIL являетСЯ одним из язынрв программировaнщr .N.E1~
не дрлж.но быть удивитеЛh1JЫМ ТО, что ВПОJIН( ВD3МОЖНG созДl:ЦЩТЬ КОМlJОНD~ЧН;ЪП~·
б:ГЮIЩ .NEТнепосредственно (' ПОМОЩЬЮ CIL и СIL-компилw.rора (i l asm.ex e), ВХй ­
дтцеro :в ста:ндар-myю поставку .NEТ F'rашework 2.0 SDK
Хотя вполне очевидно, что 1JИШЪ немногие ПрОгpaм:NШСТЩ пр.едпоч1}'Т строИTh
свои .NЕТ-приложепия непо(~редствеFIНО на язьmе CIL, ЦЗЫК сп.. сам по себе Я&­
mI~Т(,;Я ·чрезвычайно интересным объщстом Д:1lB щrrе.лле~ТУОЩЬ~lРГО исследОвавИя.
Проще roвФря, чем лучше вы понимаете гр~тщ'ql CIL, T~M увереЮiее вы будете
r
800 Час'i~ IП. П~огреММИРОi1аll~lе lCоммн.ОIЮt.f~Ы)( оnаl(ОВ .JilЕТ

себя 'ЧYJtCТВOЩiTh !s мире не'};~иал:ьных лрием.ов разработiЩ .NE't. Если roВOpnrn.


~omcpe'ЦIO. ТФ- раэ:рабo:I"ПШ. -Обдздаю~ X'lDниманием: saьша C1L. ПQ.rtyЧа8Т CJ1t\A.Y-
Ю~.

.. ПOl!ИМ2lRИе "ГОТО. RЗ1t ра:3J1WШЫ@ ЯЗb!RИ nparpaммnpQВa1lliя .NEr ~е:u;иp,yIO'r


БВОft 1tЛЮ'ШБы.е слава в ле-ысем:ь:г CJL.

• 80зМmRйость i1J;И3ассБМБ.лирОВill-JИ.Jt I<OM11OHoвoxrныx- блОШIВ .NEТ. Р1Щa!I'\'ТИрОВа-


1П1Н тrpoгpaм:мнoyo m~n CIL Iit песреlio:мт.m:nяции 06Но.&яенноro БВЗ!'lВОГ0 кода ~
иr:n.rёненныи ДВОИЧI-IЫЙ !юд .NEт.

• 8озМеЖНосn. построения rдинaмич-е:CШi:X к('Jмn(jнv'воч:вiых fiл(]Кф~ с помощыр


алементов npостранстваиме.н Sys:te.m...REftee:tiOD_M\it.
• ИСПOJ]ЬЗОвaJ,Шe. тех воэ,М(ЩI'НQстей C'IS (Соrшщ:щ__ 1УРе 8ys-i,em ~ общая oocтe~
-мв. nпroи). IФТIDj1Ьте не IIОДl{е-;рж:ивrщrгcяуцравляе-~i;И H~ болtf!t J:I'EдCO-
-нoto. урОВНЯ'т И(} СУЩf'Ч'l'ВYJЩ' ЕЮ- YP!JВШ C1L. ~т C.JL двляеТСff е:ДЩПClj1ВеНflЪ1М
ШlЫЩrМ .NEТ. ПОЗВОJJЯЮЩ'ИМ по-.rrуч.и-,г~ ДЩ'тyJi[ .f!;O BC~1\o): fНJЭТ>fQJf';Н:ОСТ$JМС1'S_
~p. ЙCII6.iJ:ь8y.s: C1L. вы можете опре~л:ят:ъ -cr.л:eн,ы1'1 ПОJДI PJоб~"IИJOГО
уровня ["'nORe ПО<lВDЛеБЬ в C:iI].

Свова ааметим.. 'Ч"rGiбы: бьmо преДё;'rIЬjщ, неЩJ, ч-то еед11 вы не ~rrщте-ymy6J1ЯТЬСn


в де-т6ЛИ iЩ.\>-тpeшreге уеil'РО1i1ствацpnгpi:1.ММЖQго Щ>Щ:I. СП-. ~ мож-е-т ('НЬ!ТЪ нцрлн;е
ДD<rr:UОч.н.о О1:ВОemш в'G9М~IW'CТей б:n:б-mroт~ l5аЗQВЬр' ~cc:OB- .NEТ ВО .tКНQ.flt!ll;
ОТНОIIieНИ'Ял роль DОНИМ0НШl' Fl3bl:Ш\ ClL at:JЗJIOrn'ЧJ{a POJm; ПQним8ШЫJЩЪЩЭ а,()ссм,­
блера лрОГР~СТ<Ш. ИCJ]OJJЬ3~ С(++)_ Тоем. H-'l'9 '~iJ; fШ~RОЛl4iJ9F1е~
воэможнос-m. пptllцe вахDДИтh хш;уoyмнъre p6!J'lтmJf ~~lX задач. с уче'ПJl\,\, ТQH­
Я'И:Х требований среды .щюrpам:м:ирав.aн-и.R (и средЫ ВЫ;I:lI)~в;ииJ. 'Тrщ что t"t;дл цbl
rOTOBbl пpиmrnь llblЗОВ. д3.lШЙТe npистynиМ к расCМDтpeaиIO о\Юбенностf?ij C:lL.

SаNS'faИМе.С,педует ПОI-1IШВ1Ъ-" 'fТ.o в ;!UJflh!0Й' mв:вe Нfi'f1редлaJ'автr;я so~!rt(Jp(,}>i<tee и ис-,ерпьrВ8JOщае


О'Пl<\сariИ~ P-ИНIa"C!lfса И сем~I'!ТIO\ICИ са .. ЕсJJи ваМ требуется IiC9CTGPQI'!I-Iи)i аНaJJlIlЗ а~аМОЖНQIJ!Г2f1
CIL, оорЗ1Wеаь к кaW'eJesoi'! ВооК. C/L Pragramтing: fJn(jfif т.е HcoQof ,NffТ (Дрrвsз. 2002:).
!
ДирективЪ1, атриБуты и коды операций CIL
.в начаj'reп~,iн HoвorO Л$ЫRa НиЗRoro УРО:ВШL такого Юi.Rсп... ви неЦI<WМе,ЕЩО
обпа.ружи·ге новые ДдЯ себя (а чаето .1 кaжyI1(Иеся: не.лQr~) 'ИМ'eЩL .ДЦя 9~НЩЬ
лривыtJНЪE{ nОНJiI'ИЙ. Раcc'Мu'Гpfпе. в:апример, сле.:цующий;. -набор 9ЛВМентCiВ.

! n.e~, publiC, tJ'd,~ / Ьаэе: , 9e.t. S-el.. ~xplic:i't, 1.JI'!s-аfе, ёnU!!\, opet.C!t-o,r r pa.!t'tial!

Б;ы. cкQpe11: все:{'О. JifДеЦUlФI:Jj1:.Щpуere N. -И3:К IVIЮчеШ\J,C .слова язьmа с# (Ii это ира­
В1IUmЛоJ. 'Но если ~ИСМQТРeтъt;Jl ~э:деменгЩl:{ зтor(} ЩJ.бора бодtx: .1IИИ'мательнu.-ВЫ:
CМ<'»R.eтe З!Ц4е'1'ИТЬ. ч.rrо 'ХО1i'Я i1дeC'~ ~r:й элеме.нт n .1ШJlяется k,o-почeвьtМ:
C:lIOBOU
CI, (1)}Ш иМеютщщершеН1if.Q pa2IНУЮ еемацтищг, Нanрпмер. КJ~чеВCJе dlOBO ЕШU!li
шrpед~ет ТИi1. прOIQВQДНЬ1Й от $:У8· t~щ.Е'РU:Щ, а ,JЩЮчевые c.rщ;ва thi$ п base пО­
зв('JJIя'ют СС'I:iJlIз:гы:;а. еQОТS~тcrв~1ЩО~ на 'reJt.:'V'Щ}JЙ оБЪe.Iq IЩП р.одите.JIЬCRИЙ IL'I~
объехтз. RлкiЧroJОf:,('";ш;IВО ЩJ$р:U= иепо~с~ЦЩJ CQзр:щ-n;u;: бnока программноtQ
JЮда. Ete'гOpъm не Д~Ж~ неrщсрс:ДCТ1feщдJ RонтроJЩpова:ТI>СЯ средой Ct.R. ~ клю-
1:ffmоеСЛЩIО EJp~Tatoat ЛD3ЦОЛЯ~Т liцд:трmnъ CK!~ (~ElO IW.еновfШНьm)

L
Глаllа 15, Cll и роль ди1it8мических компоновочных fu!O«O!! 601
метод. который будет вызываться Totдa. когда вы .n;pю.tензете заданный оператор
С# (например. зшш сложения).
В ОТlШ'!Ие 0'1' TaROro высокоуровпевого языка. какС#. язьmСIt. не просто опр~­
делает свой собственный набор юnoчевblX МОЛ. Набор леl(сем. понитныхкоМ1Пi1JЯ­
тору CIL. разделяется:на три большие категории. в 3аВИCJ1Мости от СeмaJlТичеСRОГО
подтекста:

. ' диРекТИВЫ CIL;


• щ-ри:буты CIL,i
• RО.ф.I операциД CIL.
~.щш Jштегор:rМ:1 лексе;м сп. вы,ражаетси с J:IОМОЩЬЮ евоих специaлыtых син­
таксичеоких КОНСТРУ'fЩИЙ . а qu..щ лексемы Qf)ъeщщщотчя с тем. чтобы в реЗУJlЪ'tате
ЛЩIУЧИЛСЯ: 'работоспосо()НЬ1Й щ)мnо~овочщuй блап .NE'Г.

Роль директив CIL


I1p~e ВC~l'O. есть МFJожеСТВ0 известных деКG~ CIL. которые используются для
ОПИCQЩ'(и ЛОЛНОЙ структуры RОМnОНОВОЧН;ОГО блu}щ . NEТ ЭТI1 лексемы нааывают­
CJf дuрекmrtвамll'.. ДиреНТИВb,l CIL исполъз~ТGR ДJDl Шlформирования ROМЛИ1I1Пора
CIL о том. I{ЕЩ опреДеЛЯТь ПРООТРЩlства ~eн, тшIы и: ШteнъJ. содержащиеся в коМ­
поновоЧlJОМ БЛОRe.
СИНТaRсичеСIЩ лирt:;JtтJ:I1ЦiI оБО3trачщотСя с пqмощыо префикса, npeAC'I'aailen-
Rctrрточкой (.) (HaEp~ep • . Л 81)'\:espace , . ,сl<!ss • . рu,~Йiсkеуtоkеf1 .• override,
.1I1ethod , . i3sse'ffi'bly и'Т. д.). Таи. если 1:ЩШ фЩbr *.Н (обычное расширение для:
файла,содержащего программнщ ~oд CIL) sмeeT одну директиву. nатеэрасе и
три ди)Jектцвы . с lC1Js $, KOМДI:ЦI~rop CIL с~нерирует RОМПЩlОВЬЧНblЙ блок. RO'I:QРЫЙ
определит одно пространства ~MeH .NEТ 11 три тица кnacca .NE'I:

Родь атрибутов, 'CIL


Во МRогих, случаях ДИР~RТЩЬ) ClL Gами ПО c~e окаэыва:юТС.я IreДОСтаточно ин­
формати:внымн. 'Чтобы да;гъ исчеРffi;lВ~щее определение соответствующего 'l'ИПа
:NEТ или его VЕI;Ш. :nОЭТQМУ мtщ.с.щ: директивы GIL СОnPОВQЩЦaIOтcя pa~
атрШiуrnaмu CIL, СООРЩ?JQЩИЩi u том . .КаК дЬmинВ' обрабатываn.CJ) )\fJJI1.faff дирelt-­
'[ИВа. Например, дцрецтива .cla,s s ~жетсоцровож.д~ вурибутом publi:c (что~
бы задать параметрЬ) ВИДИМОСТИ T;r~). атрибутом ехLeпd s (чтобы явно у-кааать ба­
зовый ЮJaСС типа) W1И атрибуто,М imр1еiПIепt.в (чтобы задать 'СIШСQК интерфейсов.
Iшдцерживаеlyiых тщюм).

Роль кодов операций CIL


Посдеоnpеделенин R{}МnОНОВО''ЩОГР бло~ .NEТ. пространства имен. и: набора
ТИ;nОS » теРМйF[ах Сп.. с исцользовсщием JiЩзлиЧНЬiX диРектИв и ,('вяэа:Ан'ых атрибу­
"ОВ oCTaeTCSj (j)дНО - ЦPeд:1lfnfЦfTЬ ЦРОГрЗ'мМяую .лоГИКу реализации тиna . ЭТО' JШЛЯ­
ЕТС.ц: 3аД.."iчеЙ ~коCiщJ Dтщрrщш1. В '('Офтветствии С 'традицияМИ ~руг:ИХ НЗЪ1ROВ нИзкою
YPOЦfЦl. Y.OДl>l операций Сп... кан правп.л:о. имеют прос'IО .неnpоиа:носиыые аббревиа­
туры. Наттрю.1!!р. q~l'Oбы оцр~еmпъ переменнуюстр:о.н:и, испо.n:ыyется не ПОНЯТНЫИ
КОД О(l~Рp.цIЩ I"o'S.d$trin g. а l clstr.
602 Часть 111. Программ~рование компоновочных блоков .NET

Но все же. что не может не радовать, некоторые KOДbI операций CIL в точности
r
соответствуют их аналогам в С# (это. например. Ьох. ипЬох. t:.hrow и sizeof). Вы
сможете убедиться в том. что коДЬ! операций CIL всегда используются в контексте
реализации члена и, в отличие от директив CIL. они никогда не обозначаются пре­
фиксом. заданным точкой.

Различия между мнемоникой и кодом операции CIL


как только что объяснялось. коды операций. например ldstr. используются ДЛЯ
реализации членов данного типа. Но в реальности лексемы (в том числе и ldstr)
являются мнеМОНШСQМи CIL. представляющими на самом деле двоиЧНЫЕ коды опе­
раций CIL. Чтобы пояснить различие, предположим. что у нас есть следующий ме­
тод, созданный средствами С#.

s tatic int Add(int х, int у)


{
return х + у;

в терминах CIL сложение двух чисел представлено кодом операции ОХ58 .


Аналогично для представления вычитания используется н:од операции ОХ59. а дей­
ствие. соответствующее размещению нового объекта в управляемой динамической
памяти. обозначается кодом операции 021.73. С учетом сказанного должно быть
ясно . что СIL-код. обрабатываемый JIТ-компилятором. на самом деле является на­
бором двоичных данных.
К счастью. для каждого двоичного кода операции CIL есть соответствующая
мнемоника. Например. мнемоника add может использоваться вместо ОХ58. S 11 b -
вместо ОХ59. а newob j - вместо ОХ73. Ввиду указанных различий между мнемони­
ками и кодами операций. нетрудно догадаться, что декомпиляторы CIL. такие кан,
например. i lda sm.exe. переводят двоичные коды операций компоновочного блока
в соответствующую мнемонику CIL.
. method public hidebysig stat ic iпtЗ2 Add(int32 х,
int32 у ) ci l managed

11 Лехсема 'add' является более понятной мнемонихой CIL,


// используемой ДJlII представления хода операции ОХ58.
add

Тем. кто не сталкивается с необходимостью разработки низкоуровневого про­


граммного обеспечения .NEТ (например. пользовательского управляемого IЮМПИ­
лятора), обычно не приходится иметь дело непосредственно с числовыми кодами
операций CIL. Поэтому практически всегда, когда программисты .NEТ говорят о
"кодах операций СП;' , они (как и я в этом тексте) имеют в виду набор более ПОI:lЯТ­
ной мнемоники. а не леЖaIЦие в ее основе двоичные значения.
Глава 15. CTL и роль ДИIo1в.мичеСКИ)t 1tO..,ПОНОВО'!НЫ)( блщюв 603·

Добавление и извлечение данных:


стековая природа CIL
ВЫСOlЮУРОВlre,вьre ЯЗ&II<И .NEТ (напрuмер,. тюще JШR С#) nЫТaIO!J'('..Л максимадЬ-
1-10 снрыть шr:mоуро~невые с;дожностц . О.д1ПIМ И~ аодентов рааработ.ки .NE'I КРТО­
РNЙ Ol<азываетсн CICphI'TJ:,1M особеt!llо ХОРОЩQ. ЯВJlяете.я 'Тот факт. что сп, ЛВ)J,Яется
яаъrком, Цt'ЛИIWМ осноВШПtьtм на стек.овам программирОOOЩПI. :НanоМlЩМ. 'ЧТО при
исследовaщm:пространетва имен SY$teJТ,.Col1ect,.ioJ1s (см. rna:вy 7) а.щ (1 вами вы­
яснили. Что тип Stack можеТ ИСПDяъзоnаться д:r1Я доб~л~я :з;ыачения в CTel!:. а,
ТllliЖtJ для УдМеltИЯ из стена ;щаченил. ра~ме.щенного на Bep~ стека. R,oнеЧНIJ,
разра:БОТСПI1Ш СIL-приложешщ для загру:щи и выгрузки ЗffaченЩ1; 'fJe ПС;ГЮЛЬЭУЮТ
непосредственно объект Syste:ro.. Col l ect i N!.;> . S t .ac;k, одщuю он;и 1JРим,е.шпот ана­
логичные операции.

ФормальНQ о.бъект. испоЛЬ'зуем,ыii Mfl Х,р>щеFlИЯ набора 3щ;iчен1Щ, НQ.зывэется.


вuрmуальНbU>1 cnteftOM 'BQtnO}['I'I.eHUSI. вы .сможете убе.диться в ТОМ,. ~ITe CIL 11.редла­
га.ет :целый рлд 1(()Д(Ш оперauий. K0Тop~e ИСПОДЬЗУЮТСJl дл.я доб<uщ~ни.я ~:lНачеFlИЯ в
CTeIC: соответствующий процесс назыветбl1 загруз~оll Toqнo так },Ке СЦ.. оnpеделя­
e:r целый ряд. других кодов операцщ1. {юторые переноснт здаЧtЩИе с веpIIIИЩ;J сте--
1{а в памятЬ (например. в ДЩ.8ЛЪНУЮ цер!':менную): для оБОЗНqчеlЩl'f ЭТОГО процесса
используется 'r~рмйН coxpaн.~Hцe,

В CIL просто невозмоЖ'Но nOЛУЧИIDb доступ '~ мемеитам дщrных 1it'поередспен­


ИР. и это .l taCaeTCf\ как ЛQкалБliО Qпре.деленньrx :цеременныж, T1Ut 11; ВXOДНIill" аргу­

Mel.rтO"B методов. а таюке lДщей ~ типов. Н,УЯЦЮ С'начЕЩа ЯЩJО аатрузить эле­
мент в Сl'ек. чтобы Затем "гыx0.IlКI:lYГЬ - его ОТ1'уда ДJ1Я далънеi):ш~Г9 исщо.ЛЪ30Вания
(помните о.б этом.. J'ieдb щ,IенНО поэтому fiiлон пр{)]:р~ого· ЩЩаСIL может казать­
ея HeCHO.'1.blI;O нзбыТQ'ffiЫМ).
Чтобы понять. КЭIС CIL ИСПОЛЪЗУ~Т стековую М'oдe,lIЬ, рассмотрим простой
С1l-Метод F'rintМеЭSаgе [), КОТQРЬЩ не имеет ~pгyмeHTOB и ~ero не возврзщает.
В рам}ЩХ реалиЗдЦ:ИИЭТОro меТQд.а вы ~TO ElывоДите :iш:ачение лоЮШЫIОЙ СТРО­
Щ)ВОИ пере~еш-IOЙ' в лоток стандаРТБ0ro вьmОДi!t.

publi.c '1 ioid Prii.f1'1LMe szage ()


[
'~tring туМеВ$,а([6 = "ТIрr.Щет. ";
C<)D sol е. W.r i tе:Liле ~myMess:age) ;
,)

Если расомотреть резУJ,IЬТ8Т транСJШЦИИ этого. метода КОМПКЩIТором CIt В тер­


мины CIL. Быl cpa;~y ·эа:м.е'fИте. что :метод PrintMe.s:sage О (lцределяет ячеЙI\y хра­
нения. дJJ;я: ЛОRa.rtьfIОЙ пер~енноЙ. используя директиву .locals. ЛОЮLThНая строка.
затем загружается и сохраняется n ifГOЙ лщ(ВЛ'ьноц перемещюй с помощью Ш>ДОВ
операций l .d $.t r (~aгpyaкa .строки) и stloc.Q (это мо,щ:поцро.ЧJJТ3'IЪ, нан "запомнить
текущее значеliИе В лакЭЛЬНОЙ перемel'ШОЙ СИF.Iдексом О"').
Значение (CHo~a с индек.сомQ) затем загружается в Цaм.fITЬ С помо.щьювода опе­
рацИJ1 ld 1 ОС.О ("эагрузить локальный аргумею с Ш;lДtlliС9М 0'1 для -использования в
вызове метода S')!S tеm.Соnэоlе. Wri teLine () (yJщзaJ:ЦIрм С ПОМОЩЬЮ кода шrерации
C'all}. Наконец, rJIЩИСХОДИТ вщщра,т:иэ фyнкдm,! через код операции ret.
r
604 Часть 111. Программирование компоновочных блоков .NET II
,

. method public h id еЪуs ig instance void Pr i n t Message () cil managed


{
.max st.ack 1
/ / Определение ЛОJtaJIЬНОЙ СТРОJtОЗОЙ переиенной (с индехсом О) .
. locals init ([О] string myMessage)
/ / Загрузка Сorpожи со значением "Призе'1'."
ldstr "Привет."
/ / Сохранение CTpOJtOBoro значении в стеже » nОЖaJIЪной переменноЙ.
s tloc.O
/ / Загрузка значения с индексом О.
ldloc.O
/ / Вызов метода с текущим sначениек.
ca ll vo id Lmscorlib]System.Console: :Write Line(string}
ret

3амеЧ8liие. В программном коде CIL поддерживаются комментарии, использующие синтаксис


ДВОЙНОЙ косой черты (а также синтаксис /* ... * /). Как и в С#, компилятором CIL комментарии
просто игнорируются,

Челночная технология разработки


Вы уже знаете. как использовать ildasm.exe для просмотра программного
кода CIL. генерируемого компилятором С#. Однако вы можете не знать о том. что
ildasm.exe позволяет записать СIL-код. содержащийся в загруженном компоно­
вочном блоке. во внешний файл. Имея программный код CIL в своем распоряже­
нии. вы можете отредактировать и с помощью ilаэm . ехе - I~омпилятора CIL-
СI<омпилировать базовый код вновь.
Формально такой подход называется челночной Ii1eXl-/.OлогuеЙ разработки. и эта
технология может оказаться полезной в следующих случаях.

• Перед вами стоит задача изменить компоновочный блок. ДЛЯ которого нет
исходного кода.

• ВвидУ несовершенства компилятора языка .NE'f, сгенерировавшего неэффек­


тинный прогpaммный код CIL, вы хотите изменить этот код.

• Вы создаете компоновочные блоки, взаимодействующие в рамках СОМ. и вам


приходится принимать ВО внимание то. что некоторые атрибуты ШL (Interface
Defmition Laпguаgе - язык описания интерфейса) в процесс е преобразования
могут теряться (например, СОМ-атрибут [helpstring]).
Для примера использования челночной технологии разработки создайте новый
файл (He lloPr og ram. cs ) исходного кода С# с помощью обычного текстового редак­
тора и определите в этом файле следующий тип класса (можете. конечно, исполь­
зовать и VisuaI Stuшо 2005. но тогда не забудьте удалить файл Assemblylnfo.cs,
чтобы уменьшить объем генерируемого CIL-коАа).

/1 Простое Jtонсольное приnо.еиие на языке С#.


using Syst.em;
Глава 't5. CIL и POilh ДИ~JаМИ\l'ес.1(ЙХ 'КО..,ПОМ80ЧНЫХ блоков 605
c1a::ss program
{
st_atic v 'o id Miiin (g·t,riЛr;) I J a.rgs}
\
CODsole.WriteLlne("Hellq tIL codeJh);
Cor.$ole . RеаdLiщ;; () :
}

СQ~'раните этот файл в ПDдх-рдящем мест!: H.a~oeM диспе и скомпилируйте его


с nOМOIЦЬЮ црограммы -csc. ек.е.

csc Rel.loProg-rаш.С5

теперь откройте полученный файл HellOI:1z::ogram.exe' c помощью ild~a·sm.exe


и.., исrюлъэу-я опцию меню FiI~<:;> DUmp. сохрamпе Мсьrрой" цроrраммный код CIL в
. новом файле"*. i1 (Не llo.prDgram. Н) на вашем жееr.rкoм ДИСI<e (ЗRачеЮUI,. предлага­
емые D полвл.!ПОщемCJI диалоговом окне. ВIIолн-е подОЙДуТ для наших целей]. Teц~pp
вы можете· рассмотреть Э'IОТ фaй.JL используя любой 're'КСТовый peдмcrop. ВОТ слет·
ка QТIWрреН'tиро.sанныЙ и снабmel-n-ШЙ IlеlW'1'ОРьrми комменraриям.и ре~ультат~

11 КOИnоиоаочюtе б.nо1Uf 1 иа !liolt'OplIJe JGI Ce&naeм:CJI.


· assernbJ у ежt..еrл ШSСQr11Ь
1
.pиblickeyt,;JkeIii = (Б'7 711. 5С 5,6 19 34 БО 8'9 )
· уе r 2; О. О ; О'

1/ Наш JCо!Шоиово~ б;n9~.


· а 5'S embl у Hellc Prugrarг.
!
· h'a БЬ a.1gQr:L tllШ Охс) 0'00 8 0'04
· ver О: О': О: б

.lIIDdul e Hel}0Progтa.ffi.e~
· irna\1~ba s е ОХЮ О 4 D:C!O О О
.ttle alignment ОхООООО200
.эtас k.l.·е-эегvе .C>X~ 0'100'OOO
.$ut)-s'y si;em ОхОООЗ
· сФг·flа,g.s О~ШОООО'ОQ1

1/ Опредenеиие JШасса PrоgriШ\ .


. elas~ priv$te эut,,;, 'ansi Ьеfогеfiеldiпit Progr'"bln
e.xteDdв [mS.согliЬ]"Sувtел.. ОЬ] ect

,method' pi!:ivat~ hic1E;by~ig s .ta tic void MaL'1. ( эtI 1оч [1 args}
ci 1 mana.ged

11 Об,оsas '1eюte Э!!'QrО кieФОда I ItВ.X '1'О'ltor эхо~а


11 :8ыnоnиаеиоro Фaйn& •
. entrypoi!1t
• rnaxэ,t аск 8'
I1 0000: пор
Н,_DDоl; l,dst,.r "ВеНо ClL oode!"
606 Часть 111. Программирование компоновочных блоков .NET

1L 0006: cal1 void [mscor1ibJSystem,Console: :WriteLine(string)


r
IL - ОООЬ: пор

1L- ОООс: call string [mscorlib]System.Console: ,ReadLine()


1L 0011 : рор

1L 0012: ret

/ /KOHCТPYlC'I!OP, заданный по умолчанию .


. method public hidebysig specialname rtspecialname
instance void .ctor() cil managed

.maxstack 8
IL 0000: ldarg. О
1L 0001: call instance void [mscorlibJSystem.Object::.ctor()
Н, 0006: ret

Во-первых. обратите внимание на то. что файл *.i1 начинается с объявления


всех внешних компоновочных блоков. на которые ссъшается данный компоновоч­
ный блок Здесь вы видите только одну директиву .assembly extern ДЛЯ одного
обязательно присутствующего mscorlib.dl1. Если бы ваша библиотека классов
использовала типы из других внеППIИХ компоновочных блоков, вы бы обнаружили
дополнительные директивы .assembly extern.
Далее следует формальное определение вашего компоновочного блока
He11oProgram.exe. для которого указана версия 0.0.0.0. назначаемая по умолча­
нию (если вы не укажете иное значение с помощью атрибута [АsэеmblуVеrsiоп]).
После этого приводятся другие описания компоновочного блока. для которых ис­
пользуются дРугие директивы CIL (такие. как .rrюdulе •. imagebase и т.д.).
После указания ссылок на внешние компоновочные блоки и определения теку­
щего компоновочного блока идет определение типа Program. Обратите внимание
на то. что директива .class имеет несколько атрибутов (многие из которых необя­
зателъны). - например. атрибут exteГld5. задающий базовый класс типа .

. c1ass private auto ansi beforefieldinit Program


extends [mвсогliЬ]Sуstеш.ОЬjесt
l ... )
Большой кусок программного кода CIL соответствует конструктору класса. за­
данному по умолчанию. и методу Main () . Оба они определены (в частности) с помо­
ЩЬЮ директивы .met110d. После определения этих членов с помощью подходящих
директив и атрибутов они реализуются с помощью различных кодов операций.
Важно понять. что в CIL при взаимодействии с типами .NET (например. с
sуs t е т. С оп 501 е) всегда необходимо использовать абсолютные имена типов. Более
того . .I{ абсолютному имени типа всегда должен добавляться (в квадратных скоб­
ках) префикс с понятным именем компоновочного блока. определяющего этот тип.
Взгляните на СIL-реализацию Main () .
. method private hidebysig static void Main(string[] args) cil managed
{
.entr:ypoint
.rлахstасk 8
Гла!l3 15. CIL и РОЛЬ ДИl:lами~ес~их lЮМnОНО80Ц~ЫХ б110КОВ 607
1:1:. OOOO~ пор

IL-ОЮ'ёl} ~ ldз,!.. r "НеllС) 'l.IL CQde!"


IL- 000.'6,; caU vbid !rnscoriib] sу.stеm.Со~эоlе~ ~W'riteL:ine (striog)
lL ОООЪ: пар

lL (НН)с: са 1] .s.t r Lng [ffis cer l.:Lb] зуs tem,. Cohsol е; : ReadL.ine ()
IL-'1JОl1
; 'РР}?>
IL .0012: ТЕ,,;

Реализация КОНСТРУК70ра. зманкого ПО УМQЛЧадию, 13 теРМ}IНщ, программво~


ГО, К6да ciL включ:аеr еще одну оТNOСmцyюсян заГР~Rе m:IСТРYlЩИЮ (lda r,g. о),
н даннОМ случае аваченnе зarpyжaется в <::тe1t не КЗR :поЛl;rЗOlШте.1lЪЩШЯ перемem-rая,
укаЗaI-'l:И:ая нами, а н.ак 'l'еItyЩая: об:ьекТRая СС:ЫЛl{а (nодробности. WfQl't1 процесса бу­
ДУТ О;п;Исаны lIOзже), Таклее обр,ащте lшдмание на Т0, 91'0 конструктор. задa:ннъrn
no умолчанию. явно Bы3ыает конетрук:гор базового шraсса ..

. metho"j P 1.lblic hidebY$ig s.p.eciaLnaroe rLвре,с iаl 'лаm е


iasta.!7Jc:e veid . ctor О с11 mOlnaged

• IJ1CLX:S tack. з:
I L_'0:0 !) О : 1darcl. О
11'_ О-ОО 1: C4lJ.l iЛ8 tаrщ'€i \J\Qid rms.cQr 1ibJ Sy:s t€1!! .. Qbj ect: : .oeter О
11, ОСОб : · ret
J

Роль меток в программно, м коде CIL


Вы. конечно, заметили,. что н Ъ"'а'ЩЦоil строне прогрщ.fМНОГО :кода peщrцщщии со­
дерЖИ1'СЯ npефикс:в форме лексемы IL_XXX: (нanрцмер. IL_О(ЮО:. l:L_Q'O Ol: и т.Д.}.
эти лексемы называются метками /СОда. и он» могуг иметь любой ВИД, RaIroИ вы
Т@ЛЫЮ пожелаете (лишь БJ>I ОНИ не дублиРОВaJIИСЪ в :цределах оДното и T\JI'O же
Ш>R'rеR~та), При эanиси содержимого НОМЦОНОБОЧЖ>ГО блока в файл с ПCIМОЩЪЮ
ildasrn. Е:хе автоматически тенерируютсн метки .кода. имеЮIЦИе ВИД 1 L_Х ХХ:. Но
ВЫ можете ИЗМенить их с T~M. чтобы они стали 'БOJI~е информar:r.иnНiЫМИ .

. method pri'\:ate hi!iebysig .stat.ie Jlo.id Маlл (strlng [] aIgs) сН !J]iJщаgеd


(
. entrypbip.t
. maxs t 1:I<;:k S.
Nothing 1; пор
Lo&d_String; l.d:зtr "Hell", сгL oode!"
Р.ICi.nt'l'ОСОПSОlеl cal l v"id
I~sаоri~lSуstещ.сonsоlе~:Write~nе{зt~q)
No~g_2: пор
WaitFo%:....KeyPress: ,::al l string
Imsсо%:Ць] Sуз,tem. console: :ReadL.:i,pe ()
ReII19v:eValцeFromStaclcl: рар
Lea.ve Function: t-е1С

Суть R ТОМ. что боm.дпщ~во .меток :кода сЬвс.ем неО.()язательпо. ЕДШIственным


случаем. когда меТ1;СИ IWдq: OJ(азываютt.я ПО-НЗСТОRШ,eмy полезНЫМfl (и обязатель­
ными). является с.дуча:й, j\\ОГДр в программ:ном Jitще CIL используются ветвлени~
r
I
II
608 Часть 111. Программирование компоновочных блоков . NEТ ,

или ЦИКJП1ческие конструнции. Например. в нашем случае вы можете исключить


метки вообще .

. meth od private hidebysig s tati c void Main(stringlJ args) cil managed 1


{ !'
. entrypoint i
I
.ma ~s tack 8 I
пор

lds t r "Hell o С11 code!"


call vo i d [mscorlib]System.Console::WriteLine(string)
пор

call string [mscorlib]System.Console: :ReadLine()


рор

re t

Взаимодействие с CIL: модификация файла * .il


Теперь. когда вы понимаете. как компонуется базовый файл CIL. давайте завер­
шим наш эксперимент с челночной технологией разработки программ. С помощью
изменения СIL-кода в файле *.i1 мы должны выполнить следующее.

• Добавить ссылку на компоновочный блок System.Windows.Forms.d11.


• Загрузить локальную строку в Main ().
• Вызвать метод System.Windows.Forms.MessageBox.Show (), используя ло­
кальную строковую переменную в качестве его аргумента.

Первым шагом является добавление новой директивы .assemb1y (с атрибутом


e~tern). которая укажет. что используется Sуэtеm.Wiпdоws.Fоrms.dl1. для это­
го просто добавьте в файл *.i1 следующую прorpаммную логику после ССЫЛRИ на
внешний компоновочный блок mscor1ib .
. a s sembly extern Sys tem.Windows .Fo rms
(
.publickeytoken = (В7 7А 5С 56 19 3~ Е О 89)
. v er 2 :0:0:0

Значение. указанное директивой .ver. может у вас оказаться другим. поскольку


оно зависит от версии платформы .NEТ. установленной на вашей машине. Здесь
указано использование System.Windo ws.Forms.d11 версии 2.0.0.0 с кодом откры­
того ключа В77А5С561934ЕО89. Если открыть GAC (см. главу 11) и найти там IЮМ­
поновочный блок Sys tem.Windows .Fo rms.dl1. можно скопировать правильный
номер версии и значение открытого ключа со страницы свойств этого KOмnoHo,
вочного блока. J
i
.method private hideb ysig static void Main(string[] args) cil managed I
!
!
.entryp oin t
.max s tac k 8
I
11 Задача: написать новый СIL-ход.

I
t
Глава 15" CIL и роль динамиче~~и)С J(OMnOHD,SD"IHbIX БЛDКОВ 609
Итак. целью являетсв помещение I-IОВОЙ строки В с'тек и вызов мето,да
MessageBox...s'how (') (а не меТОДа Console. WriteLine О). Напомним. ~ при уиааа~
mrn имеии. внешнerо -пma слс:дует ИСПQl[ЬЭ,оватъ абсолютНое имв типа l» coвoнyn­
~roсти ,С ПОН.R"ГНЬrМ именем 1ФМn01iOвочпоrо блова}. С учетом 'ЭТDго оБНОflите МетОд
Main (), так. ЮiI:it nа1Ш.Зано ниже.

,щеthоd p:ri'7at.e hideb,ysiS1 stiitiG \!,oid Main (,.н,rirЩ[J аrgэ) cil matlag€d
{
.entrypoiht
.,maxst'ilck е
ld.tr "CIL работает лр-енрасво! ,1'
саl1 v,aluet.Yp~ I8ystem.Wihdows, FoIrnS)
Sуstею.Windоwэ. E'arms. t ialogR9sult
1

[ S ys.tero, Windows. Fb rms J


SysteIt1' .Win,dow$ . g,оrюs .Mes.s:ageBox: : Show (st:ringj
рор

ret

Б ре.'ЗУЛliГате ВЫ' полytrnте програм:м~й JЩД CIL, сооnетствующийсдедующему


опредеЛЕНИЮ КJщсса С#.

p\lblic ,class l'rogra.-n


!
static void Маin(striлglJ args,)
(
Syste..'!1. Wiш;{оws . FCirms • Меэ Б:ач'еВоХ. SllOW ("CIL работ.ает прекра'С1-{'G!") I

КоМПИЛЯЦИII C,IL-КОАа с ПО~ОЩЬЮ jlasm.exe


Сохранив измененный фюl.л ". i 1. :вы МОЖб'Fе СJюмпилиров;;rть I-Ю:вЫЙ КОМI10-
новочный МОВ .NET. используя ДЛЯ 'ЭТОГО уl'ЮIИТУ i 1 а '" т. е.х е (Iф:МЛИЛЯТОР CILj,
Возможно. вы удивитееь тому. что КОМПИШlТор CIL имеет гораздо меньше опций
шJмзщцнiЙ стрmш. чем к.омnилнТОр С#, В табл. 15,1 привадятс.я:й:Х опи:са.чи 11 ,

Таблица 15.1. Опции командко-й СТРОКИ il.аЗJТI.ехе

Опция Оп коан ке

Включает ищ110рмацию ОТJl,а.ЦI(~1 (таl~УЮ ка!( имена nокаль'lыx пеlJемен~ых


и apГYM~HTOB. а также ROMepa строк)

СОЗД-ЗЕП 6ЫХQДl:IOЙ файЛ' " .,dll


Создает выходной файл '* .ехе, Это Зlilдчение УСТдНдВЛl4вэется по умолча­
нию" поэтш.'l'" его МОЖ~IО опустить
I}:ey Компwrирует КQМfJ'ОНОВD'fНI:!JЙ б.,fЮКСО стратим ИМ811€М" испольЗуя задан­
ный фаЙcrl ~. Sj1k

/noaUtolnh~rit ЗапрещаеТ автоматичеСкQе наследование ТИПО8клаос<\ из ;sуstЕЦТL.


bbj ect , Ko~дa 1<онкрerныи базuвorй 1V18CC H~ опреДелен
/outpl1l Указывает ИМ~ И ра().ширен~ выходного фаЙJI.а, Если флаг /щ)tрЬt не
используется, ИМЯ 8ыrщцного файла будет GOoтвeTC'fROвaTb иМени перво­
го JIICxOAHOтO файла
61 О Часть 111. Программирование компоновочных блоков .NET

Чтобы откомпилировать обновленный файл simp1ehe11oc1ass.i1 в .NЕТ-файл


* . ехе, в командном окне Visual Studio 2005 выполните следующую команду.

ilasm lexe Hel1oProgram.il


Если все пройдет без сбоев, вы ДОЛЖ~iЫ получить вывод, подобный показанному
на рис. 15.1.

Рис. 15.1. Компиляция файлов *.i1 с помощью ilasm.exe

После этого вы сможете вьmолнить свое новое приложение. Достаточно очевИД·


но, что теперь вместо сообщения в окне консоли вы должны увидеть анна Windows
с вашим сообщением (рис. 15.2).

Рис. 15.2. Результат челночной технологии

Компиляция СIL-кода с помощью SharpDevelop


Для работы с файлами *.i1 вы можете использовать бесплатную среду разра·
ботки SharpDevelop (см. главу 2). При создании нового "IiOмбината" (для этого вы­
берите Filer::;>New Combine из меню). одним из вариантов выбора является создание
рабочего пространства СIL-проекта. Хотя SharpDevelop пока что не предлагает
поддержку Iпtеl1iSепsе для СIL-проектов, леI<семы CIL выделяются цветом, и вы
получаете возможность Rомпилировать и выполнять свои приложения непосред·

ственно в окне IDE (а не в командной строке, как в случае ilasm.exe).


Глава 1З . CJL и J}O/lb ЩlнамичесJCИХ КОМnОНОВОЧt.lЬ1х БЛОJliОВ 611

КОМПИЛЯЦКЯ CIL-КОАас ПОМОЩЬЮ ILIDE#


ЕClIИ вам интересНQ ЦQэн('пери:ментироваfl'I> с ЯЗЫWQМ ар()гра,ммировани.я;
CIL. реноменд,у:ю загрузить C~ после;днlQЮ верCИlO бееwrаТБОГО реДaRтора ис­
ход.в:.ых TeltCTOB CIL. J1М(:'J9.Щe.rо оnrрыть.tй: код и нсцшание п.,п;:JЕ#. Этот инстру­
M~Н'1. ПQдоб;Но ShагрDеvеl0р. обе('пе'цша;ет 1!Jыд"..дениеe ЦВе'fОI\f кmочевых ело.в и
ирограммных СТРУКТУР. интеграцию с ilasm.exe 1'.1 набор r-оответствующих НЕ­
('трументов. В o-:rmJЧJifе от SharpDeveJop. послед.нця версЩI ILLPE# поддерживает
lntеlliSелsе для CIL. Уетцновщик lЦОЕ# МОЯffiО З.G1.Грузитъ со С'fра:ницы h t t Р : j ./
ilide. aspfree·s erver. com/defa:ul t-ел . GjSPX (ЭТОТ адрес tJ.RLможет изменитьСя).
НЭ рис . 15.3 поназаНQ ОlЩО ЛJDЕit В деЙСТвии.

~: KoIli;J~

:Е ~ ~ Т-=- 'n.:"Ot; з1 S:j; b.;ra.m L::.1},i:i..::) ~.


rr::t .~;s,$!- pj- 1. ~ t:e a~t.o В'IJ!I") ~()'t'ef j ~jttЦn) t ~
w<ti61>ds III.5.,o.r 1.ЩI Si~·",,~ .o~iect .
;:!.е; fд.:г..;s;:5-:'!. =-:r_,::rS' 1:': ':' !1!i .
.' , 1

~ m6 L~G prl~6 bi~Sj9 stat~e VA l n


<tiJ
. ;;r {. .~
. e1H':""~Qi'"Ot
afJ :~1
~. нi!lliiP~_
. ~'G .t::.it'6~ fI
l.<1~t·. "ciL 1.$ Jt8:Y ·"..,o·l·"
:3! (Re.oo=o:
<>0;11 v'!\ :! ",... t~"!:i\> (SYSt19l\, WimiQ••. ! S "" PiDgrвm
5~·iltere .. ·W':""ncfo~ .F-'l,~. 5' i3.1 D~ae:..3;U~'t::
t". i\O't."Щ '''"coriё.$yotem
• <;l<""..жIО
.[Sy"t=.}/ln:db.....,.J'onu]
~у~~.ft:'-.in~ D (ol!J , ~-e::.~...мe~g.!9-e:~,o.x; :'~,
.., м.., V»d!"""9U"'9'!1

~.

Рис. 15.3. Peд~ктop ILIDE# - бесплвтнаg среда разработки Дriя CIL

Роль peverify.exe
ПIШ создании мл::м модифmc<:lЦИИ компоновочных бдоко-в. в ROTOPbl2( ИСПОJ1Ъзу~
етсн nP0I'p8ММНЪТЙ код ClL. BceT~a целесообразно n'роверитъ. будет ЛIi скомпи.лиро­
вmiНЫй. двоичный образ правильно сформирован с ТОЧRизреНfrя npaвUJ"J .NEI: ДJm
этого МD<ив:о .иеПОЛЬЗ0вать средство RОМaJЩНОЙ строки pever.ify.Eixe.
peverify НеllБРТDgr.аm.е х е

Этот :иnстру..меliТ НРОВt:pИ1" .вее коды оueрaциjf в ука::щнном компоновочном бло­


не н:а соот~тствие щщвнлам CIL. Например, в теРМШl.ШI: СIJ,.-Rода стек оценок дол­
.жен .всеЩg опу(:тоmатьсн перед выходом Иq фун;ю:щи . Есл.., ВhI забудете извле<ffi из
lie;r:o наине-то з.наЧt:НИЯ. КОМIПШlil'Ор i la SЛ1.·ехе все равно Сl'еRt:ри.рует допуСтимый
'КШ4П9НОIЮЧ;J~ЪЩ бло:к ('nОСJЩЛЪJ'(y 'RОIIДIИЛИТОРЫ ":iщ.ботятся" ТОЛЫШ о cl..l:Н.ГflQ.КCU).
612 Часть 111, Программирование компоновочных блоков ,NET

А вот peverify.exe. с другой стороны. заботится о семантике. Если вы забудете


очистить стек перед выходом из функции. peverify.exe сообщит вам 015 этом.

Исходный код. Файл HelloProgram.il размещен а подкаталоге. соответствующем главе 15,

Директивы и атриБуты CIL


Теперь. когда вы знаете, как использовать ildasm.exe и ilasm.exe в рамках
челночной технологии разработки, мы можем заняться непосредственным анали­
Зом синтаксиса и семантики CIL. Следующие разделы предлагают описание про­
цесса построения пользовательского пространства имен, содержащего определен­

ный набор типов. Чтобы упростить рассмотрение, эти типы не будут содержать
никaRогО программного кода реализации их членов. После ТОГО как вы поймете.
как создаются пустые типы. вы сможете сосредоточить все свое внимание на про­

цессе создания "реальных" членов типа с помощрю кодов операций CIL.

Ссылки на внешние компоновочные блоки


с помощью любого редактора создайте новый файл. назвав его CilTypes.il.
Сначала вы должны указать список внешних компоновочных блоков, используе­
МЫХ текущим компоновочным блоком (В нашем npимере мы будем использовать
только типы изmscorlib.dll). для этого нужно указать директиву .assembly с
атрибутом external. При ссылке на строго именованный компоновочный блок.
такой как mscorlib.dll, вы должны также указать директивы .publickeytoken
и . -"er .

. assembly extern mscorlib


{
.pub1ickeytoken = (87 7А 5С 56 19 34 ЕО 89 )
. ver 2: О : О : О

Замечание. Строго говоря , явная ссылка на внешний компоновочный блок mscor lib .dll не яв­
ляется обязательной, поскольку ilasm.exe добавит такую ссылку автоматически,

Определение текущего компоновочного блока


Следующей задачей является определение компоновочного блока. который вы
хотите построить. Это делается с помощью директивы .assembly. В простейшем
случае компоновочный блок можно определить с помоIЦЪЮ простого указания по­
нятного имени соответствующего двоичного файла.

ff Наш ЕоиnоиоаоЧНJo!Й бnОЕ .


. аББеmЫу CILTypes ( }
Это действительно определяет новый компоновочный блок .NEт. но обычно в
рамках декларации компоновочного блока размещаются дополнительные дирек-

I
i
1..
Глава 15, CIL и роль д",нами~ес~ю: ~оМilO!'Iовочftых блоков 61 3
ТИВЫ. Дr,!и нашего. ИР.l<lМ.ера добавьте в определение .ЕомшшовочtlогоблОI(R J-юмер
верси:~ 1.0.0.0 с по.мРЩIl1Р дире:ктивы .ve:r (заметьте, что. все "Числовые i1деатифи­
каторы в оn,редедении дolDRны раЗ.дe.1lВТьсядвоетЬЧtlем. не ТОЧКОЙ. кан в С#').

1/ В&III 1tOМnСЖО30ЧиWй блох .


. assem:bly CIL1'ype s
{
. ver 1: О :0: О

ПОСROiJIЬRY ROмпщшво.чщ"IЙ блок СI:И уре s явлнется ОД1iОМОдУЛьным KOМnOHO­


ВОЧI-IЬЦ.] блоком, ОDpe,ll;елСЩ1:е ЭтЩoQ компоновочного блока эавершаетСfl единствен­
нои директивой .I11фdul€. которая )7RaЗЫВает официальное имя ДВОИЧНОГО .NET-
файла. CILTypes .d l1 .
• зssешыly CILTyPes
{
.ver 1:0:0:0

11 3'11011' MOдY.nь ~oJ1,.~TCJ[ ОДS<ЖО~ИЫК 1tомnоио.очИiDI бn:оXQМ •


. module С I L'l''t'pe s . 'dll

Kpoм€ ДИ'рmcrив .а sэе-rnы1y и .rг.odule. есть идрутие СIL-дцре:кrrивы. обеспечи­


вающие дальнейшее 'уточыени.е структуры создаваемого двончного фаftJJа .NEТ,
Б табл. 15.2 предлагВЮ'1'СR описаНWI еще двух дире1\Т)IВ УРОJ;lНЯ II;ОМПОВОВОЧНОГО
блока.

УвбпиЦВ 15.2. Дополнител.,ные' директивы KOMr!OHOBO'll'tOro блока

ДИР8kТМlа Оnиc.кме

.mrеэоurеев Е.СJ1И JCомпоноаочныйБПОil исп~льзует встраИ,вэемый P~()YPC (I:faпример. ТО'iеч­


ный риСУНОк илt1 таБЛицу CTP°I<), эта .дир~ктиза ИЩТОllьэуеl'СЯ для I!IД~НТИфИ­
~ИИ имаки ФSЙJ1Ci, СQдер*ащеro такоЙ ресурс. 8 ГnВBe 20 PflCYPCbl . NEТ рас­
'оматрив.аЮIСЯ ПОдРобно

• subsyst eI!\ Эта дирвkТI4ВR CIL используется ДЛЯ указSНI<IR предПО4Т14ТeJ1ЬftОГО подьз()ватель­
OJCora июерфейса Ц1IЯ выполнеН~11iI ItOMnOfi080"l'fforO блока , Например •. значение
2 оэмачавт, ЧТО КОМПОНО80~\.I:ЫЙ бло!< ДОl\Ж8Н sы10J1нятьфI В рамках граф~L.\ескorо
ИН'1'ерфе~са с 1'10ддержкай форм, а зна'ЦjМИ!; Э оэма~ает консо,льнQe Прило.жеН'.1В

Определение пространств имен


Итак. вы cmpеделили ВИД своего КОМIIOНQВОЧНОГО 'блока (и иербходим~е внеШliие­
ccыmm). Теперь можно создать ПРО(',l'ранство имен .NEТ (МуNаDiе 'э расе,), щ:,пользуя
длв ЭТОГО директиву • hг:mеsр,а,се.

11 Н&III. ICQJШOИО»О~ блох им•• ': QiItltO pPOC~P&КC:~.O _.и .


. n~ilp.Q. M.Yl'1a:nespaee {)-
Как и в С#, оп}>едмеНйе пространства йМен CIL можно вложить 110 внешнее
просТранство й.Мек. Вот npимер' влCnКе.иия нашеrо npO~TpLUicrBa имен в ROpJ:feB~
пространство ИМен с именем l nterteChTr-а.:i,n l щ!_
614 Часть 111. Программирование компоновочных блоков .NET

.паmезрасе IntertechTraining
(
.пашеарасе МуЫаmеsрасе ()

Кроме того, ПаЕ и С#, язык CIL ПОЗБоляет определить вложенное пространство
имен так.

11 Определение вложенного ПРОС'1'ранства имен .


. namespace Interte chTraining.MyNamespace{ I

Определение типов класса


Пустые пространства имен не представляют собой большото интереса, поэто­
му давайте выясним, как в CIL определяется тип класса . Вполне лоrично, что для
этого используется директива .cla ss. ОДНаЕО эта простая директива может иметь
множество дополнительных атрибутов, уточняющих природу соэдаваемого типа.
Для примера мы добавим простой общедоступный класс с именем MyBaseClass .
как и в С#, если не укаэать базовый класс явно, соответствующий тип будет авто­
матически получаться из System.Object .
. namespace MyNamespace
(
11 в качестве базоиого хласса npeдполагаетси System.Object .
. сlазз public MyBaseClass {}

Для создания типа класса, являющегося производныM от любого класса, отлич­


ного от Systern.Object, используется атрибут extends. При ссылке на тип, опреде­
ленный в пределах того же компоновочного блока, CIL требует, чтобы вы указали
абсолютное имя (ОДНaI<О для базового класса из того же компоновочного блока вы
можете опустить префикс. представляющий понятное имя компоновочного блока).
Так, следующий вариант модификации MyBaseClass приведет к ошибке компи­
ляции.

11 Это хонnилиpоватьси не будет!


.namespace MyNamespa c e
(
. class public MyBaseC l as s (}
.c l ass public MyDerivedC1ass
extends МуВааеСlазз {}

Чтобы корректно определить родительский класс ДЛЯ MyDerivedClass. следует


указать полное имя MyBaseClass. как ПОIШЗано ниже.

11 Тах будет лучше!


. пarnе з расе MyNamespace
{
.c l ass public MyBaseCla s s ( }
.class public MyDeri v edClass
extends МуНатезрасе . МуВазеСlазз {}

L
Глава 15. G.IL и роль дин.а...,ичес~и)\ !(ОМПОНОВQЧ~JЫ~ блuк.ов 615
В:цобавоlC 1i а'fрибугам p.1 Jblic и extends оцредедеlЩе клаСС:-<i CIL может иметь
мпо<Sест.во доIIо.1п-rителы1х c.nецификa:rоров, эаДаЮIЦИ:II; параметры видимоеТ}I
типа.· размещения палея и '1~Д . В табл. 1'5,3 прецлагаЮТlliI описания некоторых
аrриб.утов.• которые MOryт исnолъзоваты::;n с диреК"I'$ОЙ .class.

Табпица.15.3. АтриБУТЫi, которые могут ИСЩЩЬЗОВfПЬСЯ G ди.рекlV1ВОЙ • сlазз

Атрибут Описание

p Ublic, pt' ivat,e, в C/L оrtре;целяеТСЯ1\fНQжества нтриБУТО8. используемых ДnЯ )'ltaза­


n e.sted C\ssembly, ния параметрав ВИДимостИ ТИl1а. Как Ви;Ql1те, CIL прв.щщг~ет целый
н е э t .e d ЕаЛ1аmIг..s э·еm., ря:ц возможностей, не доступ н bl>( в С#
ле-stе.d f,a rn i ly,
~e~~ed famo rassem,
J:Jeзtеdрut~ iC,
ne;s·:ted prlvSitE
db.stI'aet Эти два· ~прибyra можно добавить к директиве . с 1 аЗэ. '11:06\>1 опре­
sealed· делит", абстраJffiiЫЙ клаес или изолиров'анный класс, соответственно

,Нl t O Эти атрибуты i1€ПОJlЬЗytUrсн ДЛЯ инфармирсванИ.fI ClR а том, как


S8qu erти·a 1 ал8дует размащать ПОЛЯ данных в ГIВм.яти_ для l'ипев класса впоЛflе
ezpl:J..cit П1Jдой,ti.ет вариант. 14спалi;.3'{tЗ.МЫЙ 110 УМОn'lанию {au.t G)
extends ЭП1 атрибуты 'ПОЗВШl1ЯЮ'f определить б~Qвый КJlBG(; .... иna (с помоЩью
lmple!t1eIits eXLEhds) i.щИ реализовать интерфеf1С (с ПОМОЩЬ'О il~рlещеJ1ts)

Определение и реализация интерфей;сов


к;:щ бы оТО 1:ТРавно ни выг.шщело. ТИПЫ ивтr;рфеiiса о.предедяютс..Б в сд- с по­
МОЩ4Ю щrрeI<ТИвы "сl.зss. Но когда директива .tlais5 С'опро:вождаетсн: <!.'Трибутом:
inte r:fpce. СОrfгвeТr;твующий тип реализуется. оок ,IЩ интерфейса c1'8 (СO:mпlоп
'ТУРе Syвtenl- общая СШ;тема типовt. Пос.ле опре.деленпs ШlтерфeJ.iс МЩIЩO привя­
Э~Ь 1{ типу ЮIасса ИJШструк:rypы с помощью сrL~а1::р:ибута imple:ments .
. лаmе:sрасе JlfryNaroesp.ac$
{
11 Опре;це.пение ИИ'l'ерфеЙса .
. cla5S publiC' ~nterface. 'IMyII1t.erface ()
,J::'las::;:· рцЬН с MyBaseCla s. s f t
11 'rеперь DeriV'ed'l'estClass ре·a.mrзуе~ IAmAлInttеr.fасе .
. cla5s public И1 Dеri'v~d'ClаS$
'е)': t· е,щ1э M yN.aJ!l€ Sр'Эс е
• Му;Ва s'еСl ass
imp:lements MyNarnes<pa.c e. IM'y lt!terfQ Pe I}

MaI\ былo поЩi1Зано в главе 7, Щ'l1'ерфcii.cы мo.ryт выступать в качестве баэов:ых


интерфейсов в ОТ'нощен:ии: других типов интерфе.Йса. в результате чего соз:дают­
ся иерарх..Ю1 Wlтерфе..Й'со:в. ОдЩ:lliО, вопрвки вашим ВО3МОЖНЮI догадкам . атри­
бут exten'd.$ нельз.f1 :использовать для получения Иfi:терфейса А из интерфейС'а В.
Атрибут ex t€'nd~ испо.льзуетсл толыю дл.в:ра~аю1J'l базового масса "j·ШЩ. Ч:rо5ы
расш,nритl:, интерфейс. вЫ ДО.1IЩНьr еще раз. ИСПr.JJIbзОВCnЪ атрибут i mplemMt5.

1
616 Часть 111. Программирование компоново"ных блоков .NET

11 Расw:иpеиие ии~ерфеЙсо• • ~ерикнах CIL .


. class public interface IMyInterface {}
.class public interface IMyOtherInterface
~plement8 MyNamespace.IMyInterface {}

Определение структур
Директива, class может использоваться и для определения CTS-СТРyRтурbl,
если соответствующий тип расширяет System.ValueType. Кроме того, такая ди­
ректцва .class сопровождается атрибутом sealed (поскольку структура не может
быть базовой по отношению к другим типам, характеризуемым значениями). Если
вы попытаетесь сделать иначе, ilasm.exe сгенерирует ошибку компиляции.

11 C~y1t'1'ypa всег,ца ,цоnжиа бiJ'1'Ъ иsо.пироваиноЙ .


. class public sealed MyStruct
extends [mscorliblSystem.ValueType{}
Полезно знать о том, что CIL предлагает специальное сокращение для опреде­
ления типа структуры. Если вы испольэуете атрибут value, новый тип будет про­
иаводным от [mscorlibJ System.ValueType и получит атрибут sealed автоматиче­
ски. Таким образом. можно определить MyStruct так.
11 сохращениа. sаписъ ДJJ. оnpе.це.пеИИR c~yx'1'ypIoI .
. class public value MyStruct(}

Определение перечней
Перечни .NET [как вы помните) получаются из класса System.Enum, произво­
дно го от System.ValueType [и, таким образом. тоже должны быть изолирован­
ными). Чтобы оnpеделить перечень в терминах CIL, следует просто расширить
[mscorlibjSystem.Enum.
11 Перечеиlo •
. class public sealed МуЕпиrn
extends [mscorliЬ]SY8tem.Enum()
Как и для CTPyRТYP, для определения перечней имеется специальное сокраще­
ние, атрибут еnит.

11 Сохращ.ииа• • аписlo д.п. оnpе.це.пеии. перечи ••


. class public еnuш МуЕпиm{}

Замечание. Последний из фундаментальных типов данных .NET, делегат, тоже имеет специальное
представление в CIL. Подробности можно найти в главе 8.

Компиляция файла CILTypes.iI


Даже если вы не добавите никаких членов или иного программного кода в опре­
деленные вами типы, вы можете скомпилировать файл *, i 1 в :компоновочный блок
DLL (иное просто невозможно, поскольку вы не указали метод Main ()).

i
L
ГЛiва 1&. CIL и роль дина-мичеоки)( ~OMnatl080!ilH~X блоков 611'
О'!~ройте окно IФwuщио{t строхи и BBemr:re следующую воманцу,

ilаsп~ / d1l CilTypes,il


После этого вы сможете OтlqlьrrЬ свой ДВОИЧНЫЙ файл в ildasm.exe (рис. 15.4).

"',1,

"' .~~
1j! . . МjI'jlm8lp«8·rмwmfю
'" • ~~JЖ8,~!rUtf­
'1' .• "'t'Name5JЖe..м~аи.
~ • Mt'N~ ,~\itIerI'i'8dC!1IS5
!I! 1f~--t8'."'~
'1\ ~ ~~te.МyStrtJCt

Р .. с. 15.4. СодержиJ,4М КОМnОНОВО4НОГО блоrcа СIL'I'уреэ. dll

Про~рив ОQдt!ржимое свое.го ВОМЦОНОВО'IНQГО б~'о~а. ·за,.пустите ДЛЯ него


pe~erify.e~e. В ре",улъта.те будет вщдан ЦCJIЬJЙ РJЩ сообщений об Qшиб1G:lX. па­
скощ.ку все· ваши Типы пусты, Чтобы лощить. шщ ~anОJI'НИ1Ъ ТJПIЫ содержимым.
•w.r Д01,lЩВbl снаЧlЦlа рассмотреть ба3QDPlе тиIlы дarщыx CJL.

ИСJОДtuolЙ код, Файл СНТурев. 11 размещен в подкаталоге." с;оо.ТВ~ТСТВУlOще)ll гла"е 15.

Соответствие между типами библиотеки


базовых кna'ccoB .НЕТ, С# и CIL
в табл. 15,4 помзано СООТВe'rCТЗDe:М:ежцу типами базовых MaCCOfi .NEТ JI RЛЮ­
чевЪ!ЫИ ОJLова:м:и С#. а TaIOI(e между ключевыми словами С# и -командами CIL. Там
те преДстамеиы СОRращ~нные обоэначеlll{Л J(oRCTaнТo 'Используемые для СIL-ти­
ПОВ. Чугь позже вы сможетеубедиТ&СЯ }) ТОМ. ЧТQ при .1J(:Пcmьзовании IWдО-В оп~ра­
ЦИЙ CIL часто ИСПОJIЬЗyЮТСИ и ССЫЛ}GI на ЭТИ KOHCТaн:rы.

Определение членов типов в CIL


Вы уже знаете. что Т0ПЫ .NEТ могут опреде:цнтъ p~e ЧI~. Перечнн со­
держат В~~ОТОРЫЙ ~абор ЩР ИМ~ ~. Зi.i8.чени.й. CтPYкWbl и ЮIасСЬ1МОГУТ име:rъ
ROHCTpy:t('1'Op1!1" J'I(ЩЯ.. }4етоды. ~ойства, С!1'а'iичесюre члены и т.д. В преДЬЩуЩИх 14
;главах вы уже ~оrnи ~eтъ фрагмеН'VЫ оцредe:rжeIOd1 eJL ДЛЯ такихзлем:ентов. НО.
-тем не Me~ee, ИIPRе предлarаетс~ JCР~:rRая св'щка TOr'O. KaR различные Члены ото­
бражаются В. пр~Т.ПВы CIt..
618 Часть 111. Программирование компоновочных блоков ,NET

Таблица 15.4. Связь между типами базовых классов .NET и ключевыми словами С#,
а также их проекция в CIL
Тип базового КЛючевое Представление Обозначение для
класса .НЕТ слово С# CIL КоtlстаlПЫ CIL
System.SByte sbyte in1~8 11
System.Byte byte unsigned int8 О1

Sуstеm.1пtlб short iпtlб 12


Sуstеm.UIпt1б ushort unsigned int16 ТJ2

System.1nt32 int iпt32 14


Sуstеm.U1пtЗ2 uint unsigned int32 U4
Sуstеm.Iпtб4 long .iпtб4 18
sуstеm.UIпtб4 ulong unsigned iпtб4 U8
System.Char char char CHAR
System.Single float flоаtЗ2 R4
System. ОоиЫе double float64 R8
System .Boolean bool bool BOOLEAN
System.string string string
System.Object object object
System.Void vo.id void VOID

Определение полей данных


Перечни. структуры и классы могут поддерживать поля данных. Во всех случа­
ях для указания таких полей используется директива . f i е ld. Например. чтобы до­
бавить немнОГО сути в каркас МуЕпит, давайте определим ДЛЯ него три пары имен
и значений (заметьте. что значения здесь укаэьшаются в скобках) .

. class public auto ansi sealed МуЕпиm


extends [mscorlib]System.Enum

.field public static litera1 valuetype


MyNamespace.MyEnum NameOne = int32(O)
.field public static literal valuetype
MyNamespace.MyEnum NameTwo = .int32 (1)
.field public static literal valuetype
MyNamespace.MyEbum NameThree = int32(2)

Поля, размещаемые в рамках контекста типа .NET. производного от


System.Enum, сопровождаются атрибутами static и literal. Вам должно быть
ясно, что зти атрибуты соответствуют полям данных. имеющим фиксированное
значение и доступным из данного типа непосредственно (например. с помощыо
МуЕпиm .NameOne).

Замечание. Значения. присваиваемые полям перечня, могут также быть шестнадцатеричными.


Конечн-о, при illrptщftлeilШil Полей .п:а вных в пределах класса ЮIИ струюуры ЮiI
не !'IтРт'IИчeНЫ и:спольваВанмем ·TO.IIblro ontpblJ"ЫX Статических зmтера.пОв. Можно,
иanpн:мер. добa"ВИTh Б МуВавеСыэ s поддержку двух nриватных полей данных уров­
НII экземпляра .
. сlаээ pJJ1Hic' f>.1yВa$2'.cj ~ S:s
(
. fie~d private ,S:t.:riclg stringFlelj
.if:i:e.ld private iлtЗ2 intField

Как ив С#. поJШМ данных ЮIЗ{~са б.YдYl' автоматичесRИ назначены под:х:одm:r.tиe


зна:чеЕ:ИН ДJШ иеполъзовани.н по рЮл:чaниIO. %о15ы ыоэволитъ пользователю объек~
та указать во время создания объеwга поЛЬЗовате,iIЬCIrn.е знн-gения для npиватНblX
n:Оll€Й данных. придется (:конечно) создаr.rь полыювате.ГIЬСние шmструнторы,

Определение конструкторов типов


СИGтема СТБ (общая система ТИПОВ) гюддержшщет lЮНСТРJI'ТОРЫ -как УРОЩ'IЯ
ЭRЗемmmpа, T<lli и уровня масса [ста'1'ичеСКИ{:J КОНСТРУКТОРЫ). В термиlЩX CIL д;r,rя
ионструкторов ур.овня ЭR3е:мnЛЯра ИСПОЛЬ3}'f::ТСЯ ле-Rсема . с tOT. а Д:IlJl статиче-еR;ИX

ROHcTpyкrOPOB - лексема. <;ctor (.кОElС'груктоР нлнссаj. Обе Э".г.и дe:кce~ CIL дошк·
БЫ СОПРОВОЩI(атьсн атрибута,\'ll<! rtspe<.:ialna111e (crreциальное имя Бозврщца~мо·
го типа) и з peci а lname, Эти атрибуты n.cЛЩIЬэуюrтся для 'ИдентмфИJ\:эцИИ специ·
альных лексем CIL. позволяюЩШI: yшиJ\альноеТOЛRование в каждом иэыке .NEТ.
Например. в С# 'КOH(,тpyI.."ТOpы не опреДeJ,ШЮТ возвраща~мый ТИП, ощщхо- в терl\olИ­
пах CIL ВD3Вращощ:мым значением конструктора на самом деле будет voi d .
. cla.ss public MyE<3$,eClass
I
• fieJiij private strin.g 5t]fiI~чF'iеl-d
,field private illt32 intFielt!

.meth'bd pUblic Ilidebysig specialname з:tsресiаl~


i.n-stanoe V'oid .ctoZ"\stf:il1q ЗI if!t32 i) -cil llliulaged

/ / Зад~qа: ~обааи'l'Ь sеобхо~ ПРО~aJOUUIЙ lI:ОД . •.

Обратите внимание на то, что директива _c:t()r сопровождается :атрибутом.


inst 1.щсе (пос~олъRY это не статический rw.нcTPYXTOpJ. Атрибуты сЕ mапауес!
о'Звачают, что в ЕонтеДС,те этого метода содер"китC$f Л:РQГРaммньtй НОд OIL {а не
r!pОГРaNlJ\lffiЫЙ КОД, не являющий,ся управдяемы.м), который может ИСПОЛh30DаТЪС$
в мeтшrатформеНJThIX запросах.

Определение свойств
Свойства и Мет9ДЫ т.аюJte дмеlОТ cnециаЛЬfiые npедетanдеН:Jil.J1 в C[L Чтобы в Ha~
тем примерi' обеспеЧИТh ~ :MYB--::tSеСlаss lIоддерЖl<уomрытого свойства Т1-1еэ'triпq.
можно J,-JСПОЛЬ3(jва:гь С'.JIедУющl(Й ClL-яод (аамеп,<ft'. QrO з.п:есь Щ1Я'ГЬ ш;по;n,вуется
атрибут sресlа1паrnе).
620 Часть 111. Программирование компоновочНых блоков .NET

.class public MyBaseClass

.m.thod public hidebysig spacialname


instance string get_TheString() cil managed

11 За~ача: ~оба.и~. необхо~иxwй проrpакинwй ХОД •.•

. method public hidebysig specialname


iпstалсе void set_TheStr1ng(str1ng 'val u e') c il m алаgеd

11 За,цача: ~О!S"'И'1'. к.обхо~ npоrp&МXJWЙ' ХОД •.•

. p~op.rty instance string TheS t ring()


{
.qet instance string
MyNamespace.MyBaseClass: :get_TheString()
.set instance void
MyNamespace.MyBaseClass: :set_TheString(string)

Напомним. что в терминах CIL свойства будут представлены парой методов.


имеющих префиксы get_ и set_. Директива .pr o perty использует соответству­
ЮIЦИе директивы .get и .set, чтобы связать синтаксис свойства со "специально
именованными" методами.

i!
Замечание. Указанные выше определения свойств компилироваТЬСR не будут, поскольку пока
не реализована сама логика чтения и модификации данных.
'ITO
,
J
Определение параметров членов 1
Теперь предположим, что нужно определить методы, имеющие артументы. По
сути, указание аргументов в CIL (приблизительно) соответствует аналогичной опе­
рации в С#. Например, аргумент определяется с помощью указания типа данных
после имени соответствующего параметра. К тому же. как и в С#, в
чиваются возможности ввода. вывода и передачи параметров по ссылке. Также в
CIL обеспе­ Ii
CIL позволяется определять аргумент массива параметров (в С# это делается с по­ I
мощью ключевого слова params) и необяэателъные параметры (которые в С# не i
поддерживаются, но допускаются в vв .NEТ).
1
Чтобы показать пример определения параметров непосредственно в CIL, пред­
положим, что нам нужно построить метод. который получает iпtЗ2 (по значению),
iпtЗ2 (по ссьmке). [mscorlibJ System.Collection.ArrayList и имеет единствен­
ный выходной параметр (типа iпtЗ2). В терминах С# этот метод должен выглядеть
приблизительно так.

public static void MyMethod(int inputlnt,


r.f int refInt, ArrayList ar, out int outputlnt)

outputInt = О; 11 Прос~о чтоб~ yдo.n.~.op~. хоиnиn~~ор С#."

L
rЛ8ва 1'5. e.lL и роль АИil6Мlllческих КОМ'ПОН~8'ОЧI'IЫХ блокС. 821
Есщп сnpоеф{ровать ЗТОт МfЛ'о,ц В CIL-ROA. вы оБЕаруж:ите. \{ТО ССЫ;!IJЩ н:а "ара­
~eтpы С# r>'1lI$Т обозна'Чею» змаlЮМ aмnepcaндa (&), цоt'!авленноrо в B1tЦe суффmtса
R'rИПY дa.нюiIX. соответствующему параметру (intЗ2&). дм ЗЫХОдSЬUt парам~ров
тоже исmшьзуетса суффикс &. НО, 1(роме-того. они ' обозначены мapKepQ~ CIL [outJ.
также обратите внимание 8'11 то, Ч'N) В ТОМ случае. когда пар~тр ям,нетс-и CCJ:,I-
llО~nШОМ (как тип tmзсоrНь 1System, СоНесНоns .:11.1: raY.Lis t в нашем np;u-
$е.ре) , ему npедшеО'J'вует лексема сlаЗ8 (H~ путайте с дирекrивой .cJ,assJ), '
,method pu.blic hldebys.iq !ltatic '1010 My-i"!&t~"cI (int32 inpu~ :r [:It:" ,
int:Э2' refInt,
с1.&.. trn5'oorliz;] Sys-t6rг.• CQllect ions .1\.Х' ~sy!..iat ar,
Iout] int32IoutputIn!:.,) сН Щ5n5gеd

Анализ кодов операций CIL


ЭаилЮЧИ1'eJIЬНОЙ темой кatпего обсуждения в этой глuе в Qтношении IlpOrp'IЩ­
Щlо~ хода сп.. будет роль )CQAOB операций. Нanомним,Ч1'О ROA Оnе.рации - Э1'О про­
сто яе1«:ема CIL, J(СШО.ttьзуемая;wIЯ построеииs лотикиреализации даннщ'о 'Шен.&,
Пon:Н1itЙ набор 1tOoЦoa операЦИА сп. (который сам по <:ебе ДОllР1IЪRО велин} MO~ji~
разБИть "а СJltдующиl:: "олы.uие натеrории.
'. .коды оперtЩИЙ Д.ttR ynра:8ЛеИИИ npограммой
., кoды оп~раqиЙ ДJt.Я OUtmW ~ажений
• ~ДЬ1 операций AЛJI ос:ущеЬ1'меНИIi дocтyna ~ а,~а1.fепиJiiМ в пrrмвти {через па­
paм~pъt. ЛОКМЪНЬ1е п~рем~Rныe ~ 1;'.п.)

Чтобы продемонстрировать BeROTOPWe ВОЗМО~ОСТИ реалиаfЩWf членов сред·


ствами C1L,B табn. 1,5.5 npeдmu'ВfQ1'CR описания не,еоторых на ~OJlec часто не·
польэуемщ КОДОв оneрsдi.Jй. неП6среДС11ве8НО С;в,RЭIi:ЮfЬ'D( с )10rИ80Й реалиgации
членов. Кроме TOVO, КОДЫ операций в Д8.ЮtО~ 1'аI5JIИЦ~ СГРУ'пnир~ ПО функци­
ональности.

Тl5nИЦI 15~5. коды1 операций CIL, СВАзаНt;ые с ревлизацией членов

KoAW ОП.lрlцмА ОП.,О8МIIII

add. зцЬ, t11ul, П038QI!1(ЮТ ltыnQJIt1я:rJo СЛQЖ8Н~8, IIIJЧ.ИТ8НИ8, ·ум~ожеjo1ие 1'1 Д8/18Ние ,1]/111' rrap
d.iv, tеЛl Э~ВЧВНI'IА (r.m 1 аОЗ.PJЩВ8Т QCTaTOK от АВJlSJ1ИR)

4nd,ot',not,xor ПоiВОЛIOOТ .0If10лttiП~ ООQцет.отаУJQЩИ8 БИНВРКЫ8 'OnВрltции iЦ!111 пар зtilчеНtlЙ

~eq. CiJt, clt Позволяю'!' opal~BBTb nаруане'iВНIIIЙ из стека РВЗJlИЧН"'М~ оnосоеами,


нвпр~~еРI

сес:р OPSI'i81'f1ll1l 11 ОТНОW8НИl1 Р.lемотвв


C9b~ арваН8liиев отношении "oo.nьше "
~H.: tрЬН8Ю4111 QТI10wении "M8HbUJt
ll

Иопольэуют~" ДIIЯ l<онаертиро"tlИII CCbII1O'il'lblX ТИПОI и типов" харвктеr;lиау­


BMI:!I)I 3fi/а4ени~ми
,....

622 Часть 111. Программирование компоновочных блоков .NET

Окончание табл . 15.5


Коды операций Описание

re t Используется для выхода из метода и (если это необходимо) возвра­


щения значения вызывающей стороне

b e q, b g t, Ые, bl t, Используются (В дополнение к множеству других родствеl~НЫХ I(ОДОВ


switch операций) для управления логикой ветвления в методах, например :
b e q: переход к заданной метке, если выполняется равенство
b gt: переход к заданной метке . если больше
Ы е : переход к заданной метке, если меньше или равно
bl t : переход к заданной метке , еСЛ}1 меньше

Все коды операций , связанные с ветвлением, требуют указания мет­


ки СIL-кода , по I(ОТОрой должен осуществляться переход в том слу­
чае , когда соответствующее сравнение возвращает t r ue
cal l Используется для вызова члена указанного типа

new a rr, ne wobj Позволяет разместить В памяти новый массив или новый объект (со­
ответственно)

Следующая большая Rатегория кодов операций CIL (подмножество которой по­


казано в табл. 15.6) используется ДЛЯ загРУЗRИ аргументов в виртуальный стек вы­
полнения. Обратите внимание на то, что эти относящиеся к зцгрузке коды опера­
ЦИЙ имеют префикс 1d (load - загрузка) .

Таблица 15.6. Коды операций CIL ДЛЯ помещения данных в стек

Код операции Описание

l darg Помещает в стек аргумент метода . Вдобавок к общей операции


(с множеством вариаций) l d arg (для которой требуется Уi(азать индекс, идентифицирующий
аргумент) , есть множество ее вариаций. Например, ldarg с число­
вым суффиксом (ldarg_ О) используется для загрузки соответству­
ющего аргумента. Другие вариацИ}1 l darg позволяют с помощью
кодов констант CIL из табл.
15.4 указать конкретный тип загружае­
мых данных (например, l darg._I 4 для i nt32 ), а таКЖе тип данных
и значение (l da r g_ 14 5 для загрузки i nt32 со значением 5)

idc Помещает в стек значение константы


(с множеством вариаций)

ld f ld Помещает В стек значение поля уровня экземпляра


(с множеством вариаций)

l d l oc Помещает в стек значение локальной переменной


(с множеством вариаций)

ldob j Читает все значения объекта, размещенного в динамической памяти ,


и помещает их в стек

l ds t r Помещает а стек строковое значение

Вдобавок R множеству специальных кодов операций з агрузки, CIL предлагает


набор RОДОВ операций, которые непосредственно Wвыталкивают" из стека самое
верхнее значение. КaR продемонстрировали первые несколько примеров э той гла ­
вы. удалени:е значения из стека обычно выполняется с целью последующего со-
Глава '1э . CIL и РОЛЬ ДИ~IIМ'и\lески)( R1)мпаНОВОЧi;lЫХ БЛОХ(iВ 623
.хрal:t€нил QFOго, значен'ИЯ в ло«алъной памяти для дальнейшего ИCnОЛЬЗ0Вания (Ha ~
.приМер. 11 качестве параметра I1рЙ ПОСЛедУЮщем выэове MeTnдa)_ с· yqeToM этого
становится ясно. почему мнorмe КОДЫ операд:иЙ. СВЮаШlЫе с удалением 'Текущего
ЗШ!.чения из виртуального стека 1IhШолнet·1ия. имеют upефи:кс.st (store - сохр"­
l:Ш'l'ь)_ Соответствующие описания приведе-ны в табл. 15.7.

таблица H~.7. Коды операций дnFl извлечения Д~HHЫX из отtжа

Код операции Описание

ро р Удаляет значение, нахо.дящеес~ 1:1 НВСТОЯЩИЙ момеl'lТ на верши ..~е стека, но не


06еСrie<iИВ:ЮТ coxpal:!e!-t!4e этого Зl:iачеfjl1Я'

s t.aJ:'g 'Сохраblяет значение из верШИНbI стека в э;ргументе метода с указанны ... ИflДeJ(СОМ

s t loc: УДiЦJяет зна'lв\:iие, наХQДЯЩееся нз В~:IP.ШИf.Jе стека, и запоминает зто ЗНfl!-jВНЩ! в


(С; мна)К€спюм перемеtJной с указэмным ИЩtексоМ Из списка ЛQJ(а11,ЬНЫХ nepj:!MeflHblX
вар~ий)

з~~Ьj Колирует значение 'У1<аэанного типа Из стека в' память ПО укззанному адресу

st s f l tl \3aMeHI«I"T ЗН3 L lеНие СТ8l'иуесI<OП} поля З!i!NекИ'вм из стека

Следует ТaIORе знап; о том, что РШШИ"'IНЫе коды оrrераций CIL при вь1II.OJ1нении
СВОИХ задач FteЯВНD удa.n.я.Ют аначe:EIИН Из стеЕа_ Например. при вы:Чи'ТаниИ ОДНОГО
'ЧИсла из другого (' noмmцыo' onерацИа
s u Ь сле.цует учитывать ТО. что прежде чем
ВБШOJmить соответствующее вычислеиие, sub Uвытолкнет" из C'r'e.IШ ДВЭ доступных
зн.ачеfiИЯ. Пос'ле ВNJ1олнев:ия опервции в creI< добавляется резуJ!Н!'ffi' 'tKaR неожи­
д.aщrоl).

Директива. maxstack
При реarшзацки метода B<tuocpeдcтeeннo средствами CIL нужно помнить о Gпё­
ЦИIЩ~НО:Й ДlfРСliiтиве, :которая нээ.ывается .ma-xstаtJ<. Кан следУет из ее названия,
директива .maxst.acP:: задает мaRсимаuьное число перемеRНЫХ. которые :может
.вмес.тить, CTCIt в любой J\JOM€''Н'J' :времl.'!!НИ при.-выполне:нии метода. н; счастью. ди­
ре;кти:ва . fпахst ack име€'г ·зпачениё по yмШIчa1ШЮ [В}• .:которого' оказывается доста­
т<эчно Д1Ш подавляющего большинства методов. создаваемых разрабо:гчmcами. Но
'J. вас также .есть воаМОЛffiОСТЬ опреде1IИ'ТЬ ·это аначение явно. ~LТО.бы при JКeЛЭlIИИ
БрJ"LНYЮ j'ШlЗ3.l'Ъ число локальвых пер€МеннЫХ II с:теке .

. met h o.d publlc !'1idebY5 i q iпstаr1се vc\id


Sp~ak() ~H managecl

11 в XOH'1'eX~'1'e Э'1'О1'О Ne':l'oAa • С'1'ех ПQиещае'1'СR ~ОИИО


11 омо SЩtЧEЦUiS$ (e>rpОХОИ!Й mI"l'ерош) .
. lDaxstack. 1
l d ,g t r "'ВCё~ прив е т' .. , ..
call l,-oiq [Jl1SCO:r liЬ].Sуstеш. ~ ODgol~: ; Wri teLine (.stri п g)
ret
624 Часть 111. Программ~рование КОМПОНОВОЧНЫХ бло~ов . NEТ
r
Объявление локальных "еременных
Давайте выясним. как объявляются локальные переменные. Предположим. что
мы должны построить В терминах СILметод MyLo calVa r: iab l es (). не имеющий ни­
каких аргументов и. возвращающий void. В этом методе мы дОЛЖНЫ определить
три локальные переменны е типов Syst em. Strir1g . Sy s t e m. I nt32 и Systern.Ob j ec t.
В С# соотвеТСТlIУЮЩИЙ npограммный код мог бы ВЫГЛядеТЬ так. хак показано ниже
(напомним, что ЛО1i:aJIьные перемеННые не получают зиuч ения по умолчанию , по·
этому им перед ИСПОJIЬзованием необходимо присвоитъ 1-Iач8.1tьные значения).

риЬНСI sta t ic voi d MyLocal Vaz:iabl es ()


(
stri ng mys tr - "CIL те d ude .• ,";
int rnyIn t. .. З З;
obj ec t rnyDbj - ne w obj e ct ();

Если создавать муLосаlvаriаblеэ () непосредственно в CIL, можно было бы на­


писать следующее .
. rnet hod publir hi de bysig s tatic void
My Lnc a] Va r i able. ( ) ci1 manage d

. rnax st_iilc k 8
11 Оnp.д.n.IIИ. '1'р8Х nOK&mo1UlX п.р....иlOUC .
. loc.l. init ([О] з tr i п q rn ySt't , [1] iпt З2 rnYln t , (2] object myObj )
11 Э.:оруах. строхи •• "РТУ.n"lOIЙ сф.к .моnи.u •.
ldзt r " C1 L те d ude ... "
11 И8Ц • • •U . т.куще:rо aK• • • KКII и сохрак.ни••:ro
11 • nохan.. иоА n.р.... кКой [О].
зtl 0С.О

11 Э.:ору.х. хоист.итм тип. '14'


11 (сокращени. Д.1UI 1ntЭ2) со а •••• ки ... ЭЭ.
ldc . iIJ 33
11 и•• n.ч.ни. Т.ICущ8rо ...... IIИ. и соир.и.u••:ro
11 • nохu...оЙ пер .....иой [1] .
виое .l

11 Со.~аии. .o.o:ro оlSмхт. и nои.щеки. .:ro • ст.х.


n, wnbj inз ~ а псе void [mscnr llb]Sy. te m. ObjccL ::. cto r()
11 иа.n.'II.ИИ. '1'.Х)'Щ8:rО ••••• ни. и СОХРU8JIИ• • 1"0
11 • nох.n...оЙ n.р.и•• коЙ [2] .
зtlо с.2
tel

Как видите, в CIL при размещении 1I0Кальиых пер еменных сначала ИСПОЛЬЗУ ­
етсЯ директивu . 1о с а 1 s с атрибутом При этом
8 скобках каждnн пер~меи·
i ni t .
иая свnзываетс.fl со своим ЧИСJIOВЫМ индексом (здесь это t о J, [ 1 J и [21) , Каждый
индекс идеНТИфицируется типом данных и: (неоБЯ :J8ТМЬИО) именем переменной.
После определения ло"аi1 ЪИЫХ п еременных сОотВ етствующее значение загружает·

L
Глава 15, CIL и ролЬ динами' ~еоICИХ JtQМ'ПОliO'SОЧfiЫХ 6ло~ов 625
СВ В с:те-х (1:' ПОМОЩЬЮ подходящих кодов операцИй, СМВIЩНЪ1Х. с ааI"РУЗКЙЙ) И за·
П9МИНUТСЯ s ЛЬКSЛЫlойперемt1ННОЙ (с ПОМОЩЬЮ ;цодхо.zuпцих кодов операцйЙ f1)114
со~а.неНЮ1 аначенmt),

СВЯЭJl.В8ние параметров с локальными пвременными


Вы толыш ЧТОJЩД~, ка}( в CIL с ПОМОЩЬЮ .loca l in;J.t
объ.RвляютCSi .!1lJJ(aJIЪ.
:ные, п~ремеИliЬt~. однаийнужно еще выJlнить.'RaJ< IJер~s.'ГЬщ)'С!тупающие параме·
тры Л0~ат.мым. методаМ .. Рассмотрим СЛe.цyIO.Щий статяч~щmf:! метод С#,

plibJ.1c .'tat1c iлt Аdd( iл t ./ 1'лl.l:lj


(

ЭтОТ внешне "fн"ьиины" JI4~ОД S 't'ерминЬХ CiL сущеетве:аrlО боnе_е ".щrоrоCJJО­


вен", Зо-перВЬJ.X. поступающи~ аргументы (а иЬ) следу~ ПОМе<:ТИТI:! В 8ЩJту~ат.}jЫЙ
стен выпо;щеU1l с помщ,цъю кода cmерацйu Ыа rg (загрУЭlCQ a,pryмewre.]. Затем иt\­
I10ДЪ~TCJI ВОД операци:и aJфi. чтобы швлечь АВа значения 113 степ, яаАТ)'l СумМу
и сяовв c:oxpaBtnЪ $начение 8 стеке. Нatcoaeц. эта сумма иэвnекаетсв ив стека "
80эвраща~ТС,JI ВbIЗывающе-й G'rороие с помощыокода оперrщ~ J:;ei,1 ЕCJЩ Д»3~C'
се.."4бnИРОВIJ.тъ YRаванвый mетоД Cj с ПОМОЩЬЮ iidВSIn.е.хе.ВЬ1 обна,ру.tКИТEI. ЧТО
КОМnИnЯ1"ор С'ВС .ехе добавдяет мNажес'1t1iО .цОАОJ1I-ШТМЬНЫХ леnсем. ХОТЯ cyt.ЦHOC"Tb
CIL-кoдa. Ош\'З1$IШСТСR ~ючитеJ1hШ> ПРО~'l'ОЙ,

.met:hod publ1~ hidе);,УЗ19 stH 1(:' 11.1't. З2 Jl.dЦ '(1Г1t31 а' ,


iпtЗ1 .1:1) ril m.a.nag-~cl

. rt\a.xsta.c к 2
ld&~O'. О 11 Эаl'РУЗМ I а' 11 c:rel'C.
1da'1'9,1 11 ~аl'рузка 'Ь f 3 C'I'!i!1i:.
adci /I СJlрже И,11t'1 э тих 'аf{е;ч~R.Юf.
ret

сIфыаяя СС.,IЛК8 this


ОБРS:ГИ1'е вниман.ие на ТО. ~ТO в pa.MK~ riРОf'раммиого МДО CIL ДIlИ ссылок на
Д1Ш вxmЩfiill{ арryмСЯТQ (11 и Ь.) иоnо.тТЬ;3уЮ"ГСЯ lOJ.X t1ЩJ,еJ.tcbl позиции (ИНДеКС ОН ИЩ1(!КС
1. UOCKOJJblCY шщекс:a.uиn 8ЦРТУальnом стеиt! ВЫПI:lЛНI!ШfJI .начинается с нуля).
При аКW1изе np()rpaммnoro ~oцa и его ~оздаmrn непосредственно в CIL CJI~дyeT
быть очень внимательным. ПОСИОlJPRy КtЦКДШ (не~таТИ1Jесний) метод. ИМ~ЮЩКЙ
lIxодныеа.ргу.меиты. QSТQМ'QтичесJ{И получает неквныЙ" ДОI1O.llНИТeJ1ЬНЫЙ пара:мехр.
который ЯВJlВе-.гм ссы;mой на текущий о~ъеит (ето ДОJlЖНО ныоать fШaJ}ОТИЮ С
· КJ]'юqевым СЛОВОМ cf t.hi ... ). ПUЭ'l'ому, ~с;t1и одредеnит.Ь метод 1\d.ct () ,пак неСТQТИ­
ческий

11 У. . К8 118Ц8'1'CJI Сl1lа'»JRilСJUlИJ
P\lbHC.1 int Add ( 1nt е I .i nit: Ь)
j
626 LlacTb 111, Программирование компоновочных блоков ,NET

то входные аргументы а и Ь будут загружаться с помощью ldarg.l и ldarg.2 (а не


с помощью ожидаемых ldarg.O и ldarg.l). Причина пак раз в том, что ячейка О
будет содержать неявную ссылку this. Рассмотрите следующий псевдокод.
// Это тоnько псевдокод'
.rnethod publi c hidebysig static iпtЗ2 AddTwo1ntparams(
МyClass_HiddenТhisPointer this, iпtЗ2 а, iпtЗ2 Ь) cil managed

ldarg.O 11 Загрузка МyClass_НiddenThisPointer в стек.


ldarg . 1 11 Загрузка 'а' в стек.
ldarg.2 11 Загрузка 'Ь' в стек,

Представление итерационных конструкций


Итерационные конструкции в языке протраммирования С# представляются с
помощью ключевых слов fo r , foreach, whi l e и do, каждое из которых имеет свое
специальное представление в CIL. Рассмотрим классический цикл for.
public static void CountToTen()
{
for(int i = О; i < 10; i++)

Вы можете помнить о том, что коды операций br (br. Ы t и т.д.) используют­


СЯ для управления потоком программы в зависимости от выполнения некоторого

условия. В нашем примере мы задали условие, по которому должен произойти вы­


ход из цикла. когда значение локальной переменной i станет равным 10. С каждым
проходом к значению i добавляется 1. после чего сразу же выполняется тестовое
сравнение.

Также напомним, что при использовании любых кодов операций CIL, связан­
ных с ветвлением, нужно определить метку для обозначения в программном коде
места. куда следУет перейти в случае вьrnолнения условия. С учетом этого рассмо­
трите следующий (распшренный) программный код CIL, сгенерированный с помо­
щью ildasm.exe (включая и метки программнога кода) .

. method publi c hidebysig static void CountToTen () cil managed


{
.maxstack 2
. l oc als init ([О] iпtЗ2 i) 11 Инициализация локальной целой 'i' .
11 0000: ldc.i4.0 1/ Загрузка этого значения в стек.
IL 0001: stloc.O 1/ Сохранение значения под индексом 'О'.
IL 0002: br.s IL 0008 11 Переход к 1L_0008.
1L 0004: ldl oc .O // 3аJ'рузка значения с индексом О.
1L 0005: ldc.i4.1 11 Загрузка значения '1' в ст е к.
IL 0006: add /1 Добавление в стек под индек сом О.
IL 0001: stloc.O
1L 0008: ldloc.O 11 Загрузка значения с индексом 'О'.
IL 0009: ldc.i4.s 10 11 Загрузка значения '10' в стек.
1L ОООЬ: blt.s IL 0004 11 Меньше? Если да, то к IL 0004.
IL OOOd: r:et
Глава 15. Cll и роль ДинаМI+'tеСt(ИХ ~MnOHOBO'lHblX БЛО:КОII 627
в СYJЦНОСТИ, этот прmpамМНЫЙ 'lЮД CIL н.ачив.ае1'Ся с опредезreн~q .щж:щьной
Переме:нной int32 Е аагру3J\."И ее в отек. Затем осуществляются переходы между
RОМан,цзми с метками 1 L_ Ь 00 Q и IL_ 0:00-4. прйчем :каждый раз значение i увели­
чиваеТ~J:1 нв: 1 :и npоверя.етс.я .. ОGТалось ли это эначеаи:е MeHblI1e ] О. Е:еди nех. 1'0
ПРОШJXGДИТ BЫXDД из метода.

Создание КОМПОНОВОЧНОГО блока. NEТ в CIL


Tenepb. освоив СИНтаксис и семантц:ку OlL вы Mo~eTe 'зацрепить свои знания
на практике. noстроИlJ приложение .NEТ с ИСПOJIЬЗоващ,ем ТЩ[ЬRР CIL И текстового
редантора. Ваше лриложеиие будеТСОGТООТЬ из npимтuоro щщомqдульпЬто * .,О11.
coдep:)'1~ГO два оnpеделе:в:ия: ТИРОВ !\'.iIac<:;a. и КОI:IСОIЦ.J;ЮТО "', r?хе,нзаимодейству­
ющега с этими ТШlaiVШ.

Создание CIICars..dlJ
Первым делом С]lедует поетронтъ файл ..... сЦ 1 дин испо,щ.:зовани:Я R:лИ'ен­
ТОМ. Откройте JПOбой TeнC1"OBъ.m р.едfШТОР и создайте нов:ы:й Файл *, J.lc именем
CILCa r s. i 1. Этот ОДНОМОЦVЛЬНWЙ }ЮМnОНQIЮЧНЫЙ блон будет И;СlJOльзоватъ два
внешних двоичных файла .NEl: поэтому вы можете Ыli\.чать С~QЙ файл пр.о:грам­
~шога кода CILTaK
I/ Ccыn~a !ia xnscoz:lib. dll н
/I System. Windows ..FOr1US . dll
..а.sэеrnblу еХХ.егр IfIЭС.Q rl'i Ь
[
· fШЬ} н:: k.eyt oke :n = (В7 ТА 5С % 1& 34 .Еоеэ
• \ТЕ'.J; .2 : О ;0 : О
r
. a sa emt.>l у exter:n ,Б:;$ tern. wi J'. dаwз'. i;1orms
{
· p ab lick.eytoken = (В7 7'1:1 5С 56 19 34 ЕО 8 9 )
· '~e r 2: ci :0 : (j

11 OIlJ'8де:nеиие о.дКОМОдУЛЬИОI'Q icомпоноаОЧИО'гО БIllОU .


• д;sзеrnblу С rtCa t s
1
.hastl algor:J.tГJ!!1 O~OOOOB004
.\Те. ! 1 :.0 .:1.1: О
j
•modQ le С 1 LC'a Ts • dl i

Ita:к уЖе было cкa~HO. этот :КОМП'он(!)вочньтй блок буд~ ('рдержатъ два типа
класса. Первый тип. CILCar. одреде.л.яет два mm:я данных и дQJ\ъзовате.льский КОН­
структор. JЗ:rорf,JЙ тип. CatTnfQHelpe:r. оupеД('ЛlIет еди.нс1'В~rый статичес.юШ ме­
тоде именем Dis:pliiyCarlI1fo 1), ~{оторый исдользует(:ILСаг в Ш1чествеnaраметра
в:возцращает ';Oid. Оба тица ЩiXодятс.я в пространстве .имен CILCar s . В терминах
СILкласс ёlLС@т мОЩ1Jо ~аливовать 'l'ЩС.
628 Часть 111, Программирование компоновочных блоков .NET

11 Р8&11И'_ЦМII nш_ CILC_X8, CILC_r ,


.namespace CILCars
(
. сlазз риЬНс autc алеi beforefieldinit СНСи
extends (msccrlibJSystem.Object
(
11 ПОn8 Д8.JIIDIX CILC_Z' •
. field publio strin9 petName
.field public iпtЗ2 currSpeed
11 П~_о._,.~сккА ХОИСЩРУR'ОР, хо,орыА Д_.' non•• o._,~
11 .о_ио...оо,. nPИС80И,. пота д-и.....
. method public hidebysiq specialname rtspecialname
instance void .~tor(in t3 2 с, strinq р) c1l managed

.mв.хstасК 8
11 З_rpv.х.
n.paoro _рrуи.и,. 8 с,.х И .м.о.
11 хоисщрух,ор& e_.oaozoo м_сса.
ldarg.O 11 объе~~ 'this', а не iпtЗ2!
саН instance vOld tmsc:crHbjSystem.Object:: .ctcr()

11 T.n.~ ••rpy••• n.p.oro и 8,oporo _рryи.и,о •• с,.х.


ldarg.O /1 об~ек~ 'this'
ldдrq.l 1/ аргумент i пtЗ2

11 сохраи.ии•• n.и.и,_ '.p1IIIIlD1 С,.Х8 (1nt 32) • поп. cuZ'Z'Bp•• d.


stfld iпtЗ2 СILСаrз.с!tСаr: :currSpeed
11 Э_:rpV.Х8 аЩРО.О80:roо .рrvк.и,. и оохрак.ии•• поп. p.tN81118.
ldarg.O 11 об~ехт 'thiз'
ldarg.2 11 aprYMeKT string
scfld string CILcars.CILcar: :petName
ret

Имел в виду. что настоящим первым аргументом любого нестатического члена


являетCJЯ объектная ссылка thls, D первом блоке СIL-кода мы просто загружаем
эту объектную ссыnиy и вызываем 1WНCTPYКТOP базового класса. Затем поступаю-
11.U1C аргументы конструктора помещаются в стек и запоминаются в ПОJ111Х данных
типа с rrомощью кода операции stfld (сохранение в поле).
Далее, вы должны реализовать второй тип в данном пространстве имен. а
именно тип CILC,HInfc. Суть типа находи'tCfl 8 статичес!(ом методе Display ().
ОсновноЙ задачей этого метода является получение поступающего параметра
C!LCar. извлечение зяачеНИlllIОЛЯ данных и вывод его в окне сообщения Wlndows
Forms. Вот полим· реWIиаiЩИЯ CILCar!nfo. а далее следует ее анализ .

. class public auto ansi beforefieldinit CILCarlnfc


extend5 [mBcorlibJ Зу,st61m.ОЬj61сt

.m~thbd PUbliC hidebYlig static void


Display(cla5s CItCa.rs . CrLCar с) ci1rnar,,;geci

L
Глава 10. C1L 111 роль динамически); kQМП();НОВОЧНbIX 5.1101<.08' 629

• rr,ax,s,t ack: ,Е)

11 Нам iI}I'1IЩ& lloJ:anIoKlUIcтpokO• • • n.р8М8ИИU.


,l(jl:als irl'it ('[О] зt.ring caption)
/1 Эarpузха O'l'pО-КИИ 8Jl:СДИО:l;lО CILCar 8 ~~.x_
ld~t.r "C:K0p-0с>J:ь, j О}: fI
ldзt"g.D

/1 Помещение ~'И.ТI.аос- p8tN8.!118 Ц.СС4 CILC • .r а cure", и


11 aioi8o. C'1'&Imf1I8CXO:tlO М.'1'оД" sainq, J'C).~t ,() .
ldfld sttiпg СIТJСаrs.СIIХаI:;реtl~Э:I!Iе
~all !it.ri)'}g [msCQ.rl.ili] Systern.3't ring: :F<:,rmat !str:i;ng. obj~ct)-
c ,_Ь
s ,tlo_

11 Эа:tlpуак& saa.ч.еиюr поп. ou.trSp. .d и по.лучение e:l;lo. C~OJl:O*01:'O


/I ПР_ДС'1'UnеИи. (обр.ТИ'»е анюсаИИ8 н • • • 08 ToS'tr1nq О ).
ldarg.Q
ldflda int 32 СI LСаз::.,. Сl LCar~: ctJr.r:Speed
с:,аll instan~ string: [msеФхliЬ]Sуstеm.lГ!tЗ2: :ToSt:rlng()
ld l oc.O

fI в.зо.:вие'1'Оpjа Иа_saqеВОх. ЗЬО" () с $U'pVlКe~ эиа:.I8.НИIIИИ.


ca1l valuetype [Sy,s'tem. Wind,ows. F'am.s]
System. Wind<:M.9 _F;Jxms _Dla 10 gRe-эu l t.
rSystem-, 1tH ndows • Fохшs]
&ys t.:m .1'H-р _d Qw $-_ F'оrrпs. МеsваgеБQХ: : Stl ,ЭW (st:r:ll~g , str ingJ
р.ор

ret

Хотя здесь об.ъеМ проrpаммноro }\ода CIL ;юметяо болъще. -чем в случае реаливз­
фш CILCar. на саМоМ деле вre.довсmЬi!О просТQ. Bo-перВJ:ilX_. цосколысу вы определя­
ете стаl'ичесmIЙ метод. вам ае nPИД€,ТСЯ ИМеnO дело (,'0 СRР~ТОЙ объе-ктной ССЪ1ЛК"Й
[поэтому 1ЮД операц,ии lda:cg. О деЙствите.iТ,ЬНО 1щгруж,ает поступающий apryмeнт
CILCa:r).
-Метод начинается с зarpУЗRИ СТРОХi.И ("СКФРОС1'Ь { О]: '11 BCТtlC, эа НОТО'рой еле­
/We'I apтyм:e!iT CILCar. Когда эти два зна"IeIOtfI онаaъmащтся- в нужном месте. затру­
ЖаетCII 31Iачение поля petName й выlыветс;яя статический метод Sy,stem.Strin.g.
Fшmаt ,). чrrобы вместо эа.1Idешающих фигурных СJtоб"н получи'] ъ имя CILCar.
Тз же (!jбщая npoц(ЩУра выполняется И при обработке поля GuzrSpeec!. но еле­
~e'f отметить. ЧТО эдесь исподыуетел код_ опе.РацlJИ ldarga. которая загружа­
ет адрес аргумента в С.тек. Затеи вызьmается-Sу,st;.еm.Iпt32.То.Stri'D'gU. чтоБы
n:pеобраз.оваorь значение. рааме-щенное по укааЩ:IНОМУ адресу. в строковый тип.
Наконец. !{отда обе строки отформэ:тирщшнытак. ~В; требуется. выаывВоется ме­
ТОД Me$sageBox: •.5how ().
Теперь вы мажет.ескомли.лпроватд свой новый файл *, dllc пам.оiЦЫo i lasrn.e-xe..
используя iQOMaндy'

ilasm (С1l сп.СаrS.il

а затем про~рwгь ЦQлyqе-нНый CI1rlЮД с ПОblОЩЬ1О piE!verify. ехе.


peverif-y CI1CaI:S. dll
630 Часть 111. Программирование КОМПОНQВОЧflЫХ блоков .NET

Создание CILCarClient.exe
Теперь нам нужно построить простой компоновочный БЛОR .. . ехе. который дол-
жен вьmолнить С.ПедУЮщее .
• Создать тип CILCar .
• Передать этот тип статическому методу CILCarlnfo. Disp1ay (),

Создайте новый файл *.i1 и определите внешние ссылки на mscor1ib.dll и


CILCars .d11 (не забудьте поместить копию этого номпоновочного БЛОRа .NEТ в Ra-
талог приложения клиента!). Затем определите единственный тип (Program), RO-
торый использует компоновочный блок CILCars.d11. Вот соответствующий про­
граммный RОД. приведенный полностью.

/ / С~1СИ на внешние КОМnОНОllочные бnо1СИ .


. assembly extern mscorl i b
(
.publickeytoken = (87 7А 5С 56 19 34 ЕО 89
.ver 2:0:0:0

.assembly extern CILCars


(
.ver 1:0:0:0
)

/ / Наш вwпоnииeмый КОМnОНОIIОЧНЫЙ бnок .


. assembly CILCarClient
{
.hash a1gorithm ОхООО08004
.ver 0:0:0:0

.modu1e CILCarC1ient.exe

// Реanизации типа Proqram .


. патеврасе CILCarClient
{
.c1ass private auto ansi beforefieldinit Program
extends (mscorlib]System. Object
(
.method private hidebysig static void
Main(string[] args) c il managed
[
/ / Обозначает точку IIхода *. ехе .
. entrypoint
.maxstack 8

/ / Обoьявnение nокапьного типа CILCar и добавnение в сте:к


/ / значений дnи BWSOlla :конструктора .
. locals in it ( (О ] c l ass
(CILCars] CILCa r s. CI LCa r ПlуСilСа [)
ldc. i4 55
ldstr "Jun ior "
Глава 15, CIL и рОЛЬ динаМИ'lеСКIAХ КОМf1OНОВОЧН:ЫХ БТIQКОQ, 631
/ / Со'Зда~ , ROBOI'O CILCar,; сохранеиие и а а%1руз 1Ca' сCloUПtИ.
nerw,obj' lrrstancE J7эid
I Cl.LCar: s ] CIL:CiiI.! В. СПСа;::; . с t or (int 32,stri ng)
<otloc .O
1 ::i1,c"'::" О

/ / В'wзОВ
Display () и передач~ lIерхи~rQ ЗlJlач-.ния из ~elta.
cal 1 vold IC1l!Ca:rs]
~ILСаrs.СILСвrlпfо::Di sрlау(
с l,as.:s (С P-,-Cai:s'] CJLCa:tS,. CTLCar)
ret

3дec~ единствеW;IЫМ' JCOДOM операЦииj заС1I"JЖивающим комментар:{(я, является


.ent rypoint. Н.апомним, ЧТО этот КОД операЦИи используется Д;7IЯ обоэначеН)1Я :ме­
ТОДOi. RОТОРый. Д(i)джен выступать в начестве 'm'Ч1Gl входа модуля". ехе. Ввиду того.
'l1U среД<;l СЩ уIДeIIтИфиЦирует начал:ыIый 1ireJ'OAДJ1H 'ВЬШo.JЩения имеНИ(iJ с помр­
ЩЬЮ . ent rypoi qt, qaм метоц iJ'jf(}Жe<r наз.ываТЪС:Я !(;Q.К угООно (ХОТ8 g нameM npимере
он наЗblВаетс:я Ма in () J. в ОСТ8.лыroм СIL-код метода Mai [1 (') предС1;'ащцrет действия.
CJ!Я3Rнные с добaшreнием значl':ffiИЙ в сте:к и извлечеЮ1ем их из стека.
Заметьте, ОДН81Ю, что ДЛЯ созда.ни.н CILCa rJ>tсttолъвуется: КОД операции .Л8wоЬj.
В связи С ЭТ~ наПQМНИМ, что при вызове члена ТИЩ1 JlenocpeДCT.8eI;lHO в CIL вы
дОЛЖНЩ ПР:I04енить. 0и1ГгaI1:СИС' с иcnо:лъэьван:ием двойного двоето"Ш;Ц и. «ан всегда,
YJCазатъ. абеОЛЮТI:Iое И~ типа. ВОill1pl'ШЯВ сказанное,. вы можете СIЮМIlИЛДр(!}вать
свой 'НоВый файл ~ помощью i la9lIi.exe. проверить IIОЛУЧ~ННЪJЙ JФМIIQНОЩ)ЧНЫ~
блок С 1IОмоЩЬ)О ·реv·еrifу.еке. а. затем ВЫполнить IIpотрамму.

i.Lаsш Cil CaI Client, 11


ре1Тегifу l:;: i J CarClient. щхе
С11 Са у(,1 iе:пt . еже

На рис. 15.5 ПОRaЗз'н I-сОнеЧJ:lЫИ реэудътат,

Рмс.15.5, ваш CILCar 1\ деЙеТ.8ИИ

На этом. 6ЬШОmщв первYJO зада"ry"ЭТЬЙ главы, М1>I заI<оI1чиМ освоеНие азбуки


сп... К этому м\)ме:нту. Jl надеfЩ:Ъ. вы J1Iep.eннo сможете o .rtipЫТb J1Юt)ой БОМДОНО­
В().·ЧнъШ б:щж .NEГ с цомощы) ildasm. ехе и луч:ще понимаете. Ч'IО nPQИСХРДИХ вну­
три него,

Динам.ические компоновочные блоки


JCaк В'uд~:ге. процес<: СI!J3Дани.я сложного npн:ложения .NEТ непосредственно в
CIL ОК<Iзываетсн ДОВОЛЬНО трУдоемким. С ОДНОЙ <."'ГОроиЬ(. CIL ImJ1Яt:ТС.1'I 'Чрезв~'iШЙ-
632 Часть 111. Программирование компоновочных 6поков .NET

но выразительным jjЗЫКОМ программирования. позволяющим взаимодействовать


со всеми программными конструкциями. допустимыми В CТS. С другой сторо­
НЫ. создание CIL-коАа является делом скучным, утомительным и сопряженным
с множеством ошибок. Хотя верно и то. что знание - зто сила, вы можете поин­
тересоваться. действительно ли это так важно. чтобы "загромождать" законами
СИНТaRсиса CIL свою память. Я отвечу так: это зависит от многого. Конечно, для
большинства ваших .NEТ-проектов рассматривать. редактировать или непосред­
ственно создавать программный код CIL не потребуется. Но, освоив азбуку CIL, вы
теперь готовы к обсуждению динамических компоновочных блоков (которые на­
зываются так в противоположность статическим компоновоЧНЫМ блокам) и роли
пространства имен System.Reflection.Emit.
Здесь сразу же может возникнуть вопрос: ·в чем разница между статически­
ми и динамическими компоновочными блоками?" По определению. сmцтuчеCJCU.e
компоновочные БАОICU являются двоичными файлами .NEТ, загружаемыми по за­
просу CLR непосредственно с диска (в предположении о том, ЧТО они размещены
где-то на вашем жестком диске в физическом файле или. возможно. во множестве
!,
файлов. если компоновочный блок является многомодульным). l{aк вы можете до­
гадаться сами. каждый раз. когда вы компилируете исходный код С#, вы получаете
статический компоновочный блок.
ДинамичеСJCий компоновочный блоJC, с другой стороны, создается в памяти
"на лету". с использованием типов. предлагаемых пространство м имен System.
Reflection.Emit. Пространство имен System.Reflection.Emit делает возмож­
ным создание компоновочного блока и его модулей. определений типов и логики
реализации CIL прямо в среде выполнения. Создав КОМПОНОВочный блок таким
образом, вы можете сохранить свой находящийся в памяти двоичный объект на
диск В результате. конечно, получится новый статический компоновочный блок.
для понимания процесса построения динамического компоновочного блока с по­
мощью пространства имен System. Reflectio!1.Emit требуется определенный уро­
вень понимания кодов операций CIL.
Конечно, создание динамических компоновочных блоков является довольно
сложным (и не слишком часто применяемым) приемом программирования, но зтот
подход может оказаться полезным в следующих обстоятельствах.

• При создании инструментов программирования .NEТ, позволяющих по тре­


бованию динамически генерировать I{омпоновочные блоки в зависимости от
пользовательского ввода.

• При создании программ. способных динамически генерировать areИТbl досry"


па к удаленным типам на основе получаемых метаданных.

• При загрузке статических компоновочных блоков с динамическим добавле­


нием новых типов в двоичный образ.

Учитывая сказанное, давайте рассмотрим типы, предлагаемые в S у s tem.


Reflection.Emit.

Исследование пространства имен Sуstеm.Rеflесtiоп.Еmit


Для создания динамического компоновочного блока требуется в определенной
мере понимать коды операций CIL. но типы пространства имен System.Reflection.
ЕmН в максимальной мере "пытаются" скрыть сложность CIL. Например. вместо
Гмва 1,5, clL и ,ро.ль Д~'fl-ами'tеск"х kОМПОНОВочlotbJх БЛ01СО,В 6ЗЗ

npямого ухазаюtя :необходимых ДJlреКТйВ й атр~бутов CIL при определении типа


ю:ra:сса вы моЖете lТpOC-TO использовать Кi'Шсе ТуреВи:!. lder. ТQЧ,НО так же. чтобы
определить новый ионстрyI<'rор 'уровн.н экзеМIIЛJiPa. Ire:'t НИI<а1<ОЙ необходимости ис­
пользовl1ТЪ specialnamE'!. rti5pecialname И лексеМJ;id .:ctor - вместо это.го. MO~tнo
просто klСПOJIЬЗова-:гЬ СопstП1сtо'rвuildеr. 011й'Саиин ключевых '-щеJ,Ю}J простран­
С1'Ва PJ:МeIt System.'Reflection.Err,i1: nPИВQДa-roя 11 '1'абл. 15.8.

l'Iбnица 15.8. Избранные члены пространотва имен Systern .Rеfiесtiоп.Ешit


Описание

As"sernblyBuilder ИспользуеТСFl /J,I1Я созданИf1 КОМПОНI;)ВОЧI'tОГО бло.ка (* .,dЦ или


•• !!!хе,) е среде Вl>lполнеflИЯ . В СЛy>lзе *.е,хе следует вызвать
M~TD,Ц ModuleBuilder . ,S etEntryPoi:nt (,), LfГ!Юыукзэе'J1l
М810д. ЯSЛЯlOщийся ТОЧIC(jИ входа i3 МОдУЛЬ, Если ' точка B~Oдa не
указана, БУ,Ает СгеНf\РИРОВQН ".dll
Моdl.llеБuildеr Используется для o.nрЕщеления МНОJl(~СТПВ ~одУлей в рамках
даННоГо. tcомпонеВОЧRОГО блока

ET~urn6u} lder ИСI1€1J1ьзуе'l"СЯ ДJ1Я создания типа перечня ,NEТ

TypeBui1der Может I!\ОПОЛЬ3Qsаться ДпR со.здания классов., интерфейсов,


структур и делегатов в РаМках модУЛЯ в среде ВЫllолнем~lI;

MethodEuilder ИсполъзуlOТСЯ ДЛЯ создания членов ТИI'IЗ (таких I(8!( методы,


EventBuildeJ'; 1I0KaJlbHbIe nepeM9Mlible. сгdйспщ, KOHCТPYJ..qOPbI 111 атрибуты) в
LооаlВu.ildел среда JjЫПОЛflеt!Иi'I
Prqpe.rtyВUil(ier
Fie ldBU-i lder
ConstructorBuilder
СuзtоmАttriЬutеВuJ.lder
Pa,ramete,rI!!uil:der
rLGene r '4 tOT Генерирует коды операций CIL в двнном Члене rИl:1а
QpCodes dбеспечиваеr. множеorво ПФМй, ото.бl1)вжающихся 8 КОдР) one--
раций CIL, Этот ~(;I используется BMeC't8 с рsзли-чн-ыии 4J1енами
Sys te~\ .Re (!.ее t.1on .Emit. ILG.елеrаtоr

в общем, ТЩIЫ nространст!щ ИМfffl System.Ref1EHJtion.E:n-it при построении


,цинамическоre ДВОИЧНОГО MO~ ПО.ЗВО1ЩI()Т предстЩtц1Ъ "bpxpыe~ лексемы CIL
nрограЩvIНЫМИ единицами.. Возмо~оети И~ПОДЪ~ОВ~f! МИСЛ'ИХ из унаэ~
Ч1JевQВ буцут n:рi::щемонстрировama 11 сЛецующем npимере,н: о. 'РП1 ILGenerator за­
сдуЖивает отдельного об~е,IЩЯ.

Роnь System. Ref]ectlo'n. Emlt.ILGener'8tor


как слецует из самого имени YRa~aннorQ типа, рОЩ:. ILGеле~аtоr заюпочается
в добавлеиш!' КОДОВ оnерanий СIL:в дEЩНЬdt Чl1ен ТЩIа . O~ЬN,НO B~ Необ~одkМo­
CТIi flепооредственн.о, создава~ объe.юr lLGenera tor \ а Fl)1»Ф0 просто получить дей..
CТВ~ 'СеЪ1Що/ н;а тип ILGener,a tor. ИCПQJlhЗУН ТИПЫ. c~e с 1{oмno~oв­
пц!кОм (ТIi*Ие KE\1(Met.hodВuild'er и COFlst1:uctorj:!u,i ~de:r) . НщIрцмер:

11 ПО'n''I8JUJ8 IL08ne:ator ка OCl"'X'l'8 CQn.tr'\1ctorau~ld8l:'


1/ CI 8Dl88_ '~ycto~1dldeJ:",
.,.......-

634 Часть 111. Программирование компоновочных блоков .NET

ConstructorBuilder myCtorBuilder =
new ConstructorBuilder(/* ... различные аргументы ... */);
ILGenerator myCILGen = myCtorBuilder.GetILGenerator();

Имея ILGenerator. вы можете генерировать "сырые" коды операций CIL. ис­


пользуя любые из целого набора методов. Некоторые (но, конечно же, не все) мето­
ды ILGenerator описаны в табл. 15.9.
f
!
;
,
Таблица 15.9. Подборка методов ILGenerator

Метод Описание

BeginCatchBlock () Начинает блок catch


Begi nExceptionBlock () Начинает блок неотфилырованного исключения
BeginFinaJ.lyBlock () Начинает блок finally
BeginScope () Начинает лексический KOHTel<CT

DeclareLocal () Объявляет локальную переменную

Def ineLabel () Объявляет новую метку

ЕmН () Является перегруженным и позволяет генерировать коды операций CIL


EmitCall() Вставляет код операции call или callvirt в ПОТОк CIL
EmitWriteLine () Ге~lерирует вызов Console. Wri teLine () С различными типами ,
i
значений I
EndExcept iолВlосk () Завершает блок исключения

ЕпdSсоре () Завершает лексичеокий контекст

ThrowException ()
(Jsi ngNamespace ()
Создает инструкцию для генерирования исключения

Указывает пространство имен, которое будет использоваться для


оценки локальных переменных и наблюдаемых значений в текущем
It
активном лексическом контексте

Ключевым методом ILGenerator является метод Emi t (), который работает в


совоку~пюсти с типом класса System.Reflection.Emit.OpCodes. Как уже упоми­
налось в этой главе, данный тип открывает большой набор доступных только для
чтения полей. отображающихся в коды операций CIL. Полностью зти члены описа­
ны в оперативно доступной системе справки, но целый ряд примеров вы сможете
увидеть и на следующих страницах.

Генерирование динамического компоновочного блока


Чтобы про иллюстрировать процесс определения компоновочного блока .тт
в среде выполнения, давайте создадим одномодульный динамический компоно­
вочный блок с именем MyAssemb1y.dll. В этом модуле будет содержаться класс
HelloWorld. тип HelloWorld подцерживает конструктор. используемый по умол­
чанию, и пользовательскИЙ конструктор ДЛЯ присваиванин значения приватной
переменной типа
string. Кроме того, HelloWor:ld предлагает от­
(theMessage)
крытый метод экземrumpа с именем SayHello (), который вьmодит приветствие в
стандартный поток ввода-вьшода. а также еще один метод экземпляра, GetMsg (),
который воэвращает внутреннюю приватную строку. В результате вы должны про­
граммно сгенерировать следующий тип класса.

L
Глава 15. CIL ирм!> ,IIинаМИ'iески)( KOMnOt'lO.1!o!l~lbIX блощв 635
/1 Эwо'l! ~CC б?'А" соа·,цан ~ ~е.це~о.ииении
IJ с помоЩъ,lO Sузteпl. . .R.еflесtiQП .Emi.t::..
~u bl i c clas ,s Rello(iJDrld
!
:f:'x1 vat r= string t:hеМе э sаg-е-;
Ht= l lovvo.tld () {]
Н.е 11оWотlй (strlrщ $,) { theMe s $,qg~ = &.; )
рпЬ И с strinёj GetMsgj) 1 rе':Urл t1н~МеЗ'Si1-,g;e':}
pu..? lic v " id SayHel l o ()
1
Эу stеm.СОJЦ; о 16. Wri teLi r'E- ("iJp11!J5E>.T ОТ КJ'Ia-c.ca He11oW.o-rld! ") I

ПредполоЖИм •. вы СозДали новый. проект J(ОНСо.лъного nрИЛDжеНЩl в VisцaJ Stuclio


2005. назвав его DYJJ.AsrnBu ilder. Лереi1МеIIyЙте исходный класс' в MyAsrnBui ~der 1:1
GllJ!flДел:ит€ ста'l'ИЧесIШ:Й Meтoll, С иМен~м C rea:te-МуАsщ (). Этот единс~е:нш.IЙ метод
будет ответственен за следующее:

• определение blIРaI{теристих динro.mческого 1ю.мпО1l0ВОЧ1l0ГО блOlta (имя. вер·


СИR 1:1 т..д.);

• Р~ШIИЗацию типа HelloCla$'s;


• заПИСЬ' КОМПОНОВОЧJ;ЩГО блока. G'I'енеРИРОВaJfiЮl'О JЗ :паМятц, в физическИЙ
файл.

Таюке QтмеJ'ИМ, ~TO MeTO~ CreateMyAsrrJ \) ИGПОЛЬЗУет В Еачестве единствеВВDТО


параметра тип Sуstеrn.АррD б mаil1, который будет ИСПОЛЬ30JШтъtJI для получения
доступаR пту A.$$embl.yBuilde:r. сдgз<ЩfЮМУ с те:кущиы доменом приложения (см.
таву 13. rде об~ущдаются дoMe.ны ДРШЩЖf'ШIЙ "NET). ВОТ по:ЛНblЙ программный
l\:Од. с последующим аl1W,IИЗQМ.

1/ 1hIЗываnцa. сторона поmшает ~иа AppDomain.


publi c s t atic vc>{.d C ff'eateМyAsm (.AppDontain Сl)rдррDom.аiц)
j
11 YC!1'ilКOB1I:& общJa; харахтерио'1!ИК 1I:ОМRОНОВОЧlf;QI'lО ~oxa.
As·serrlblyNam.e a3$etтLblyr~an'j!e = пеw~ssеlТjt,lуNcдJ'Clе(J;
д;>s еnфlуNаmе.Nаmе '= "МyAssemblY"l
.аss;еПЮlуN:lТТ!е.Vе-гsiоn = new VersiOh("l.Ci.O.(J");

1/ Со~;ЦаиИе RQBOJ!O 1I:о.шОИО:ВОЧJ10~О БJIо~а'


11 :в pa.-1I:1iIX '1'exYJЦero .,цонева при.nОll!eииR.
Ass-еmblуВulldеr а·.зэе:mblУ =
С'.lrАррпоmаiп. Deil!1!eI'Yham1~sembly (i.is·semblyNarue,
A5'semblyBui l derAcccess .Save);
11 ПОСКОnЬJtY соз~аетс.· ОДВОkО;ЦУnЬ!tWЙ: .il:ЩШQИОЗОЧньdi б.tJок.
JI JOO[ Ii.tJЩJI. будет СО!Ша.да'1'Ь с ~H_ ·ХОМnОВОВОЧJIQrО блока.
MGdul S~:l1i.1d·e1[" mocl:u.1 е =
а ssemыl •• Define.DYnClroi c'Modul€ ("MyA.ssernbly", "MyAs<.SeI1tbI'j.. dl1") I

11 Опре:делевие Q'П:pblТО:l'О XJtaCCiI с JoIiI:Iеиеи "HellDWorld".


TypeR\J i lder beHoWo;r ldClas'@ =
]'[;э dul е . D",f i '!l~ T ype С "ИуА:s:s.e.пfulу. !1el 1oWor ld" •
Т'ур еАttхiЬut..ез .PL1blic) ;
636 Часть 111. Программирование компоновочных блоков .NET

1/ Оnpедe.nеиие прнаатвой п.рекеиноЙ Strinq с именем "theМessaqe".


FieldBuilder msgField =
he lloWorldClass. DefineField ("theMessage'l,
Type.GetType("System.String lJ ) , Fie ldAttribu t es.Private );
/1 Создание ПО'nJoзоаат~скоro конструктора.
Туре[] construct orArgs = new Type[l];
constructorArgs[O] = typeof(string);
ConstructorBuilder constructor =
helloWorldClass.DefineConstructor(MethodAttributes.Publiс,
Ca llingConve nt i on s.Standard,
constructorArgs) ;
ILGe nerator constructQrIL = constructor.GetILGenerator();
constructorIL.Emit(OpCodes.Ldarg_O);
Т уре objectClas$ = typeo f(object);
Constructorlnfo superConstructor =
objectCl ass. Ge t Con structor(new Т уре [ О]) ;
constructorIL.!mit(OpCodes.Call, superC onstructor);
constructorIL.Emit(OpCodes.Ldarg_O) ;
constructorIL.Emit(OpCodes.Ldarg_l) ;
constructorIL. Emit (OpCodes. Stfld, msgField);
con structor IL .Emit(OpCo des.Ret) ;
1J Создание конструктора, sаданвоrо по уио.nчаllИJD.
helloWorldClass.DefineDefaultConstructor(MethodAttributes.Public);
// Теперь создание м.тода Getмsq () .
MethodBui l de r getMsgMethod =
he l 10WorldClass. DefineMethod ("GetMsg",
MethodAttributes.Public,
typeOf($tring), null);
ILGener.ator methodIL = getMsgMethod.GetILGenerator () ;
methodIL. Emit (OpCodes. Ldarg_O) ;
methodIL.Emit(OpCodes.Ldfld, msgField);
me t h odIL.Emit( QpCodes.Ret) ;

1/ СоздаНИ8 К8тода SayHello.


MethodBuilder sayHiMethod =
he11oWorldClass. DefineMethod ("SayHello",
MethodAttribute$.Public, null, null);
methodIL = sayHiMethOd.GetILGenerator();
I
I
mеthоdIL.ЕmitWritеL.iпе("Привет от класса HelloWor1dl");
methodIL.Emit(OpCodes.Ret) ; I
11 Геиерироааки • •nасс. И.ll0Wоrld.
helloWorldClass.C~eateType();

/1 (Н.об •• аТ8~НО.) сохр.иекне КОNПокоаочноrо б.nох. а ф&й'n.


assembly. Save ("MyAssembly. d 1 1") ; I
' Глава 15. CIL ~ POl1b ДКН8МI1'Ч8t:~ИХКQМ!1Q!i'CIВОЧI+ЫХ 6noков &37

Генерирование компоновочного блока и набора модулей


:метод на~я с ~ МШisщвлъного наБQра харaRтерисntк КОМПОNОВQЧ­
нoroбnока, дmtчero щщользуются, тиm.tАвsеmы1Nаrnеe иVеrsiоn (определенные в
проетра1lстве J:lМea 'Syatem.Ref1eotion). Затем с ПОМОЩЬЮ метода. уровни ЭRЗСМ­
ПДfIР8, АррDоmа..J,.n.Dеfil1~Dул-аmiсАsзеmblу () вы получаете тип AS'semblyBui1der
(напомним, "что ВЫ;:IШВЮЩая C'I'opoкa передаст в метод CreateMyAsm () ССЫЛКУ на
AppDomain).
11 УС~.ИО8" Оещих Х*Р"JctlI~иC'l'JOt 1C0Мn~~ОЧИО,t'0 бnОJ:&
11 и ПОny!l..иir8 до~п. ас mnуАsа8DIыlзu::L,lс1ц• .
publ:Lc sta:tic voicl CreateМyA5m (АррОотаin curAppl)om&:!.n)
(
AssemblyName , аз.g,еmblуNаmе = aew AssemblyName {) I
asseIilыl Na.me.Name'" i'ИуАs.sеm.blу";
assemblyNam.e. \1erslon = л,еw Ifersi оп (" 1.0 •• О. 'О М ) :
11 Соад&IiИ8 11081)%10 ICОЫnОИРQЧИОJ:lO б.цОХА 8 ~8X~ AppI)OJD&in.
As.se:mblyBX1ilder аЗЗетЫу = '
curAppDoraain . De fine~'!lami сАз В'етЫ у (, assemы1 y'Name,
АSS.El!!tb, ~У'В\;1ЦdеrАсо~s'Э. Save) ;

к;n\ видите, при вызове АррDamа:Lп. DeflneoynamiC:'Assembly() :ВЫ доJlIJtЩЫ ука­


З,атъ ре~им доступа н КоМпоНовочному блоку, Этот режим может ,3здава1'Ь(;1t дЮ­
бщоlЗ значений. указанНых в табл. 15.10.

Таl5тща 15.10. ЗАачеНlltя переЧI-IЯ Assemb!yBuilderAcce'ss

3иачeJlме Описание

J\e:(lectionGnJy ДинамичесжиJl\ КОМПОН' овочн.ыЙ блок МIDжет только рто!5Рa\IКаты:.:я


Run Динамичеакий КОIl1ПО»О!JОЧНbWi блок может ~ыпоЛtiятьоя в памятИ.
но не сохранятыся на диск

R.uI1And'8av.e Дина~И~ISСКИЙ комI1qнаво'н1ы1 блок может выполняться iI памiП.!.I


и сохранщься на AI!lCK
Дl-fнаМl'!'-lеский I<ОМЛЩIОВОЧНЫ~ блок може;r СQХР1JНЯТЬая на диск,
НО не в!)(полНя1ЪСSI в памяти

Следующей задачей я'8ЛЯется определение .набора мо.ду.rюй ддя навато К'ОМ­


ПОRовоtшоrо блока. ПаСНQЛЬКУДВННЫЙ lШМПОНQJЮЧNьЩ бло~ :яmтяется одН'омо­
д:rльным. 8Ы должны определить только один модуль. Ес1!И С помощью метода
DеfinеDупаm'iс-Моdu.lе () требуется построить мноroмо.цульнъtЙ ЖОМПОFlОВОЧНЫЙ:
б.lIOlt, вы доЛ:ЯШЫ :yRaaan необнзательШ>lЙ' второй nарш.lетр. ЗCl.дaJОIЦIЩ им'п дандо-
1'0 модулЯ (например. mуМоd.dоtдеtщоdulе). OдNaRO при создании одномо.цулыщго
КОМnОН(lВоЧkоtо блока имя МОДУЛЯ будет ЦДентично имени CfLl\iQrO ~ОМПОНОВОЧН9rо
бnана. 'Га'К ~JIЯ иначе, после завершекия: работы метода DеfihеDуnаmiсМоdцlе ()
вы n(")~-re ССЫЛКУ йа действ~ный тип Мщitll€1. ~.L1ild еr,

11 0ро1l'OДYJЦoКilЙ JCOIШО.О~О"'1DiIЙ бnОJC, .


ModuleHuilder Jтюdulе. =
assembl"J. D8f.iIl8DynlLllLicNod1.118 ("МуАsэеmblу", "МуАв st'mi:lly. d.ll") i
r
638 Часть 111. Программирование компоновочных блоков .NET

Роль типа ModuleBuilder


Тип ModuleBuilder является ключевым типом ДЩI процесса построения ди­
намических компоновочных блоков. В сооТветствии с возможными ожиданиями.
ModuleBuilder предлагает целый ряд членов. позволяющих определить множе­
ство типов, содержащихся в данном МОдУл:е (классы. интерфейсы. структуры и
т.д.). а также множество встроенных ресурсов (таблицы cтpOIi .. изображения и т.Д. ;
формат ресурсов .NEТ будет рассмотрен в главе 20). Некоторые из методов. отно­
сящихся к созданию инфрастрYIСТУРЫ модуля. описаны в табл. 15.11 (каждый из
этих методов возвращает тип. представляющий тот тип. который вы собирались
сконструировать).

Таблица 15.11. Подборка членов типа ModuleBuilder


Метод Описание
De f iпеЕпum ( ) Используется для генерирования определения перечня .NEТ
DеfiлеRеsоu rсе () Определяет управляемый встроенный ресурс. который должен хранить­
ся в данном модуле

DеfiлеТуре () Конструирует TypeBuilder, который позволяет определять типы зна­


чений. интерфейсы и типы класса (в том числе и делегаты)

Ключевым членом класса Modu leBuilder. о котором следует знать. являет­


ся Defi neType () . Вдобавок к указанию имени типа (в виде простой строки). вы
должны использовать перечень Syste.i1t.Reflection.TypeAttributes. чтобы не­
посредственно описать формат типа. Основные члены перечня TypeAttrib utes
представленыв табл. 15.12.

Таблица 15.12. Подборка элементов перечня TypeAt t r: ibu tes


Член Описание
Abs tract Указывает абстрактный тип
Cl a ss Указывает тип класса
Inter face Указывает тип интерфейса
Nes te dAssembly Указывает, что класс вложен в область видимости компоновочного
блока и поэтому доступен только для методов соответствующего ком­
поновочного блока
Nеs tеdFаmАпdАssеш Указывает, что класс вложен в область ВИДИМОСТИ семейства И компоно­
вочного блока и позтому доступен только для методов, прииадлежащих
пересеченl1Ю соответствующего семейства и компоновочного блока
Nes tedFamily Указывает, что клвсс вложен в область видимости семейства и поэтому
доступен только для методов соответствующего типа И его подтипов

Nеs tеdFamОRAssеш Указывает. что l<J1acc вложен в область видимости семейства или компо,
новочного блока и поэтому доступен только для методов. принадлежа­
щих объединению соответствующего семейства и компоновочного блока
NestedPrivate Указывает вложенный класс с приватной областью видимости
Ne s ted Public Указывает вложенный класс с общедоступной областью еидимости
NotPublic Указывает класс , не RВЛяющийся открытым
Publ i c Указывает открЫТЫЙ класс
Sea l ed Указывает изолированный класс. j<ОТОРЫЙ не может быть расширен
3eri a l i zable Указывает класс, допускающий сериализацию
Глава 1"5, CIL И РОЛЬ динаМk'!еских КОМП .ОНРII.ОЧН/jIХ блоков· 639'

ГенеРИРОВatlие типа Hel10Class и принадnежащей


:v . v
ему строковои переменнои
Теперь вы l1ШЦiмаете роль м~ода:Моdu:rеВu'ildеr .. Сr~аt.е'l'уре О. и пришло вре­
мя'Выяснить. -нак СI:еItеРИРОIlатъ открытый тип ндacc~ J'iellQWo'rld и приватную
С'ТрОRОВУЮ nеременную.

1/ ОпреДIiЩ&ЩSе o'1'Jcpы'1'0гo 1'ШАсса J«yAssешыly _HelloWorld.


:rypwJJllder t~el10Worl.dClas.s =
rnoctu] е . Qef i реТуре ( "NyA.s.se."!)b 1 у' . Неl1.эWоr 1 аН, J'ypeAt'lr iliutce'S .P!iЭl ic') ;

11 Опред~eJЩе цривa.,цnе.ащеЙ х.па.соу Iq>ива'l'НQЙ перемеИRО" Strinq


11 с им.еН88 thеИеsваgе.
FiеldЗuildеr msg-Field =
ГЬЕ:l10Wоrldti:l,зss. ,DeiE i..Deflie'ld ~ "tbli!.Message ",
t yP.e of (strinlj) ,
FieldAttriQ\lte9. jPr:ivat~) ;

Обрат~те внимани~ на то. что метод 'I'урвБuildе г.DеfiпеЕ·iеld() обеспе­


чивает до,Ступ J( титху FieldBLJilde.r . .класс TypeBu ilde r определяет также дру­
rие M~TPДЫ, обесцечи:вающие даСТУЛ"R друтим типам ~построителя". Например,
~efirle-Constrouctor () возвращает CO'n6truct Qr:Bui.ld·er, Ое fi n,el?rope.tty () -
Frope rtyBl1i lder 11 lЩ,

Генерирование конструкторов
мак уже УЦО.\I!ИШЩосъ выше , Д!lН оцределения RoнCтpyETopa пmа может исполь~
зоватьtя метод ТуреВцilФ~г. Й1 е finеС О Qst:ГLLсt.оr () , Однако Б Нa.tIiеЙ реализации
r«lliСтрултора Неll0СlёJJS :Э. чтобы назначить поступающий пара метр внутренней
rrр»ватной СТРOI{е. м:ы добавим CIL-KO~ в тело Iюнструтпор:а 11еnосредственн.о.
Чтобы uолуtmть. ТИП ;LLGепега t о r', вывывает(:н метод C~et 1 Н;е I."lS·ni t а r () сооrшет­
ствующего 'fИlla ··ПОСТРQ~те.I'Щ", на .kOТОр'ЫЙ имее.тся ССЫЛWd (в данном случае это
ТИЦ Сons ,t l"цсtОГВ uildег).
MC'(QA, Erni1;. () класса lLGenerator отвечает за размещенйе CIL-кОда в реa.rmэа­
Щ1И члена. Сам метод Ещi t () чщто :использует'ТИn Jr.JJaCcaOpCodes. который с помо­
ЩЫQ полей . AOC'lynныx только для чтения, отрывает, "OC"ry1l к набору 8Одоволера­
ций CIL. Например. :ОрСоае$ . Re't YK--dЗывает возврат 1Iы0ваa 1\1етоца" Qpcc.des..st f ld
в:I:.tполняет rrpисва:и:вat1J;Jeзнач:еmrя. члену- перемешюЙ. а Opcodes. Call ИCnO.1lhэует­
са дJlЯ вы;,!ова методщ lB H~ с:ц.vчае это н.aнcт-ppcrop базового класса). Сучетом
сназзннorо расс:мотрит~следуЮшуЮ протраммную лdгику нонстрУ1<ТОр.а.

11 Созда:иие ПОЛЬ90:аа'l'ельсхоrо Jl:ORCТPVJC'l'opa ,iQceJaltE!i'O


11 ОДИJI ap~1]i System. StrU1g..
Туре [ ] €: onst':::·\.Jct:G.r1u:g:s = 11ew "Туре [1] i
СШJst Л.1.сt li' rAr<=J"s j О J = type0f 1., tril1g \ ;
":oJjSt rur::t arB111 lder ~ on '5 t ructor ~
he U oWo:rldClas5 . Defil1eCons'tr LlctO-I tMetho.d Attr ibLj.t eS. P 1Jbli с,
Са ll ingconvehtiQr')S. 9tOLпdап:1~ cOl).st :z:uг.:tbr]lxgs j ..

1/ Теперь в хоиС'1'рУ~ОР .цQб~srе'l'СИ иеоб.хоДМНЫЙ СП-КОД .


lLti€'.ne r a ·t.or GD-пstru.сtотI L = '00hs"trL1c1r.or • .(~etILG,eQe ra tor \);
ri
640 Часть 111. Программирование ~омпоновочных блоков .NEТ

constructorIL.Emit(OpCod8 •. Ld&rq О);


Туре objectClass - typeof(object)I
ConstructorInfo superConstructor -
objectClass.GetConstructor(new Type[O j );
constructorIL.Emit(OpCod8 •. Call,
superConstructor) : 11 ВыаО8 КОИС'1'рук~ора ба.О801"О к.пасса.

11 Зажору. ка yxa.a~8n.
'thi.' об~к~а 8 C~8K.
constructorIL.Emit(OpCod8 •. Ldar9_0);
11 Зажору. ха 8ХО;ЦНО1"О аР1"УМ8иorа
8 C~8K И сохраН8НИ8 8 ,m 8qFi81d.
constructorIL.Emit(OpCodes.1darg_l);
constructorIL.Emit(,OpCodes.Stf1.d, msgField); 11 ПРИС.О8НМ8 meqFi81d.
constructorI1.Emit(OpCodes.Ret); 11 Bo ••paor.
Вы. конечно, хорошо знаете. что как только для типа определяется пользова­
тельский конструктор. конструктор. заданный по умолчанию, автоматически "ОТ­
ключается". Чтобы переопределить конструктор. не имеющий аргументов. просто
вызовите метод DefineDefaultConstructor () типа TypeBuilder. как показано I
ниже.
j
11 ВоссorаНО8леиие ICOHC'1'pyxoropa, аа;цаиио1"О по умолчанию.
helloWorldClass.Defin8D8faultConstructor(MethodAttributes.Public); 1
{,
Следующий вызов порождает стандартный СIL-RОД для определения ROHCTPYК-
тора. заданного по умолчанию .
. met hod public hidebysig specialname rtspecialname
instance void .ctor() cil managed

. maxstack 1
ldarg.O
call ins tance vo id [m.scorlibJ System. Obj ect: : . ctor ()
ret

Генерирование метода HelloWorldO !


Наконец, рассмотрим задачу генерирования метода
чей здесь оказывается получение типа
Class.
MethodBuilder
После этого определяется указанный метод и получается
SayHello ().
из переменной
Первой зада­
helloWo rld-
ILGenerator, по­
I
зволяющий добавить соответствующие СIL-инструкции.

1/ Создаиие метода SayHello.


MethodBuilder sayHiMethod =
hеllоWоrldСlаss.Dеfiпемеthоd(ПSауНеllо",
MethodAttribu t es.Public, null, nuli) I
methodIL = sayHi Method.GetILGenerator();
11 Bwвoд на хонсодь.
method I L.Emi t WriteLine("Bceu привет!");
methodIL.Emit(OpCodes.Ret) ;
Глава' 15. C!l И' рол!> I1И'намич-ески,х k()t,+поНовоч",Ы'х блоков 641
8ftecQ соэдае'rС,fJ открытый метод (МеthсdАttгiЬutез.РubliС), не имеlOЩ)'lЙ па­
paMe'I'p.QB и де вО'звращаюIIШЙ ничеГО' (на это уназываютзначеrnm rшll в вызове
befi.l1.eMethod (}). Также обратите внимание Ita вызов EmitWriteLine (J . ЭТОТ ВClIO·
магате-w>ный член Ю1асса ILG.e nerator авто.матичеСЮJ записывает CтpQgy В стан­
цартю>1Й поток вывОда.

Использование динамически. сгенерированного


компонов'очного блока
Теперь. ttor,цa имеется вся I1pограммн.ая лаГИRа. позволяющая создать исохра­
нить комnоН'овачный блок., нужен kлассдля запусltа этой ЛОГИКИ. для тoro чтобы
ЗВМIWyTh цикл. npeдnоложим. ЧТО в npoewre определен второй класс, названный
AsmReader. СnoмоЩЫ() метода Thread.GetDoМain () в Main О шшучаетсадосryn-к
ТlШ'yЩему домену Приложения. Ito'toр'ый используется для npяня'ГИЯ динамичеCJШ
со:щаваемоI:'О комионо1ючllOГО блока. I1Ф:J.yЧив соответствуюrцyю ссъшRiY. вы МОЖt!:­
'Те вызвать Метод Сrеа:tеМуl\S.щ t) .
Чтобы сделать процесс вемного более :интересным. nOCJre 3Э.Dе'рIIIeНИЯ вызова
CreateMyAsrtt') будет выполнено ,цинамичес1tое с8изы1шrnеe (см. :tлаву 12), обеспе­
чивающее 'зэrрузку f{0.80ro КОМПОJ-IОБО"-П-I()ГО ' блока в память и взаимодействие с чле­
нами масса HelloWorlc{
usiлg SyaU!m;
uslng Systern. Re fle~tior:J. 'Ent:L t:
u$ing S»stem.Heflet:tiop;
!J6-lлg S\7зiеrn. Тhrе-аdiлg:

publlc class \"rogram


\
staj: ic vold ,Naib ('string[] qyg$)
'(
ICGnsole.Wrlte1ine (""""'****"'" ЧУ'nЕСНЫ!оl П.оС'тр.оитель **** ***"''''*''');
Сопэс:lе. Wr i te~in ,e ("**" ДИ:Е3.МИ"(еСj(\1~ КЬМI!Q}jО'ЕОЧНЫХ БЛЬКQВ ** .t ");
/1 Пo.пyт,zеиие ,цOll.8Jlа npwrо~ AШI данврго ПО!l.'оха,
AppDomain curAppDomain = Thread.GetDomain(),

/1 С~дание дииа!О!ЧеС~Q:иtO хомпонозоЧ1iого бло1tа ё поиоIII;D» f (х) •


с r,e ateМyAsJJ1 .(cuIAppDomai.n) ;
С<:)л.s·qlе. Wri teLin~ (" -> 3а'8е рiJJение создания My}\:e s-embly. dll . ") ;

11 теперЬ sа.гру,з~а К08oro ХОМnQИО:80ЧRО;roо бnОQ 11:-, фuта.


Сойsо·lе.Wr.Ltе:Linе ("->' 3a-ГРУЗ1<;i3, MyAs.S.6.iTtb.ly.dll из Файла. ");
A,s<sembl)' а = As-sembl у. Lo",d ("l1yAssembly") ;
/1 Поnyчение 'rИIIa. H$11014orlcl.
'Туре hello = a .. Ge1:'Iype{"My.~..5's@mbly ..Hell0Wcrld");

IJ СоЗдание · oCSъeJII'.1'& НelloWo:rld к 1D1З.оа иужкorо ItOBC!l.'pylt'l'Opa.


ConsoLE'.Write ("'-> B<len~Te СО ОGЩе!нvfе ДIЩ д;ла.сс а Re-ilоwотld: ...);
striгщ ],[,sg = с.ОDэоlе..Rеa.dLiпе () ;
рЬ] Е ct
[ ] cto:!:"lIxgs = л.еw оЬj ett 11] ;
ctorArgslOl = l11$ g;
object оЬ] = Pictivа t о,r . .сгеаtеI!'Istалсе.(.h:еll·о, .c:t.o.r~..Lgs);
642 Ча~ть 111, Программирование компоновочных блоков ,NET

/1 Вызов SayH ello м вывод возвращениой строки.


Сопsоlе.WritеLiпе("-> Вызов SayH ello() ");
Cons ole. Wr i teLin e (" через динамическое связывание.");

Meth odlnf o mi = hellO .GetM ethod ("Say Hell o ");


mi. !nvok e (obj, null) ;
возвращаеor об'Ъеltor,
11 ПОД1UПDЧеине GetИsq (). Ме!1'ОД Invok e ()
да.
1/ содержащий возвращенное значение иеorо
mi = hello .GetM ethod ("Get Msg" );
Cons ole.W riteL ine(m i.Inv oke(o bj, null) );

.NEТ, способный создавать компо­


Б результате создается компоновочный блок
новочные блоки .NEТ в среде выполнения.
ша­
На этом наш обзор CIL и роли динамических компоновочных блоков завер
ирить горизонты вашего понимания си­
ется. Я надеюсь, эта глава позволила расш
семантики CIL.
стемы типов .NEТ, а тar<Же синтаксиса и

ически созданный компоновочный блок в


Замечание. Обязательно загрузите свой динам
и пространства имен Syste m.
чтобы выяснить, как функциональные возможност
ildas m.ex e,
Rеflе сtiОЛ.Еmit реалИЗУЮТСR в программном коде
CIL,

размещен в подкаталоге, соответствующем главе


15,
Исходный код. Проект DynAsmBuilder

Несколько слов о System.CodeDOM


создаются динамические компоновоч­
Теперь, КОI'да мы с вами выяснили, как
ные блоки с помощью Syste m.Re flect ion.E
mit и различных лексем CIL. я должен
простая) альтернатива. Платформа
сообщить вам, что есть и другая (часто более
м .модель DOM для npozpam.ml-l.Ozо !Сада
.NEТ предлагает технолоI'ИЮ под название
тавить структуру .NЕТ-типа в незави­
(модель Сodе ООМ), которая позволяет предс
тного графа. Построив такой граф с
симых от языка терминах с помощью объек
Syste m.Cod eDOM , вы получаете возможность
помощью членов пространства имен
программного кода. соответству­
динамически перевести его содержимое в файл
или любому языку стороннего по­
ющего любому языку (С#. Vlsua l Баsiс .NET
ООМ). Кроме того, пространство имен
ставщика, обеспечившего подцержку Сodе
ва имен могут ис­
Syste m.Co deDO M.Co mpile r и связанные с ним другие пространст
I'рафа, находmцегося в памяти (или со­
ПОJIЬзоваться для компиляции объектного
ческий комIIоновочный блок .NEт.
храненного) объекта в действительный стати
подробноI'О обсуждения технОЛОI'ИИ
К сожалению, в этой книге нет места для
ия, выполните поиск
code ООМ. Поэтому если вам нужна дополнительная информац
Ментации .NEТ Fram ework 2.0 SDк.
по ключу "CodeDOM. quick ref'erence" в ДОlty
Глава 1,? CIL liI ро.пь Д~flаМ\1чеGIU4J( ICQIlAПОНОВОЧН.bIХ· бпоков 643

Резюме
в этой .главе upeДJIRГae'],'c,ft кр~ткий ьрзор B03-МОЖНОС'I-е'Й сщrтаксиса и (.:еман ­
тики <i:JL. S ОТЛFlчие от управляемых H3Ь1КDB высшето уровня. таких как, нanри­
.мер., С#, в CIL не цростооцредел:яется набор ЮlЮчевых СЛОВ. нЬ и ДИРeJtтивы (для
опреД~eJ-ЩЯ: С'l"руктуры liQМIIt!Новочвого блока и его тиncлi), а;]риБУ'l,ы (угочннющие
,JЩрa:ttтерuстщщ СОО'l'ветствующей дирекТЮ!Ы) и Eoды операций (:используемые д.!ш
рещmзации члеIШВ типов). Еыл T~~ рас('МОТРен хом':!:IпJIя'toр ClL (ilasm.-exe}.
вы узыади О :гам, щш и~elЩ'ТЬ оод"t'ржимое ЮJМпоновочнего блока .NEТ, нerюcред­
СТ'~ЩilЩ Д3мещ:rя ero програм:мньiй IiOA CIL. 11 рассмотре.ли осн.овные этапы про­
десса noс:rpоения -КОМПQНОВОЧНОГО БЛiJЮi .NEТ с по:моtцью CIL.
Вторая UОЛОВ.f.IfЦt Г.lДiВЫ бы.ilа посвящена оБСу-.tRдени:ю np tiC: тpaнCTBa имен
s.y steIТL.~ ef le.ctior]J Emit , Используя СОDТВетствующие 'ГИlIbl. БЫ можете С03-
Дa.Iщть КОМЦОF,lрщ)чные блоки .NET в ШIМЯТИ динамически. при желании мо,ж-
1-10' ТЗJ.'Щ\е сол--ра:нить GОЗДанньrй в памяти образ в фиэигчес:ком файле на дис-ке.
М~:iOГи~ 'ТИПЫ Sy,s.tem .Re f l Bct а. оп. Emi t авто:матич.еСКИ генерируют ПОДJ!:Qдящие
ДI!rрекгиры и атрибут:ы. CIL, Используя другие свнэamrые сшши тИIlliL таки~ как
Co,nstrt.,fctorBuilder, Type-Bu:i ldeT ит.д. Тип ILGe:r::eratill: мо~еТИEIlоЛЪЗ0ва:1ЪСЯ
A1Цl доб<ЦiJIеJfl'lЯ Н:~QБХQД:ИМЫХ .кодi!lвалерациИ CJL в члены типа. И хотя qществует
цеЛЫ}1 ряд всцомuга'ГеJIЬ~ ТИПОВ. ПРИЗБанНых УПРОСТИТЬ ПРDцесс создания про­
.грамм при ИСПQJiЮОВании :КОДОВ операций сп., ДlJЯ уtmenпюrо СО3,цЗния динамиче­
С1КИХ :НОМПDНОВОЧl-iЫX блоков вам rrtmaд0б:ится хорОшее nонимaiil:ие язьша CI'L.
ЧАСТЬ IV
п рограммирование
с помощью
библиотек .NET

в этой части'...

Глава 16, ПрострэtlСТВQ имен System.1O


Глав~ 17. Сериализация объектов
tЛЗВ8 18. Удаленное взаимодействие .N'EТ
rnaBa 19, Создание окон о помощью Sуstеm.Wlпdоws. Forms
Глава 20, Визуализация граФических ДЭН HblX средствами GDI+
Глава 21 ,. Использование элементов управления Windows For:ms
rnaBa ,22. Доступ к базам даниых с помощью ADO.NEТ
Пространство
имен System.IO

П .
ри ссщц2iRИИ поJiШЩf:IiНЪ1iX цpиii~ Иctafю!Ш'"J'~IЫЩ щщща ~~Ii3М"~1'Ъ
СOX]!lанешtВ, EШI.}1QРМa.rnц:r 'МfflЩIy сea:яu~ ДQ.cтупа nCШЫiЮЩIТf'.rrа, В Э1'~)Й rJЩ­
.ве:рwсмз.триваеТС,!;I ЦeJa.m p.a;n, :вQЦpOC,O:н, cВ;!iЗв)'J1IIbl!o.2 € Рf1,ащ1J3~' 1}'ВO~~ щu.вщда
11 .NEт. ПеРD"ОЙ ffiш:reй ~~ч.ей будет I.itt~ ба~о~ l'1:I;rIСЩ 'Qцрt:.Щ~..дI:Щ.fЩJE
:!l прос'JJР2I:1CТве ШIttН В'iStе'I1l .IQ,:и J\1I:oИJ~ ТОГО, 'iШ.Jt РРШ'Р~ ('r4{('~-
1оШ можв.а 1!3!1reНl\Й'1i РООQ'ШЙ ШJi1'aJior -~ с ГрУК'1:00 ф.iйпов М<ШlЩ'Ibl'_ Цfl/me 131'01'1,\1 щ.r
paCtJ>MМJ!IFIМ Р:il3ЛВ:ЧНЫе, IlФзмйtЖнёt'т'R "n'i!ПИЯ Н; 1fШlИСИ ~'BЦъr~ 'щ! фa:iЦr~'W С ~и:м­
JЮЛЫ:{QЙ. доо-wчnо:Й н ~ ОРJ'аиизацшей'. й T~ ffi3 ЩiмR'J'J~.

Аиализ простр:анства имен System.IO


в J'JB"J' npt)e'Ipaнcтъ[), имев $уst:ЭЛJ. 10 ла:rmeтr$ "I'О'Й .,sет.ь)О БИGлиm:1Ж ffа~Clв.ык
JIЩi.е"'::О:@', .1roтgpая 'о'бе.~,ш~1' ~!i1J':lJ{БЫ ВВОД3-1W!1'IОЦ;I. }сак,цла фaИnDВ. 'I.U и: дли t1a-
.м.ю;и. П'1Jf.[QРН(! любому JIpyrOI\>IY' upot."1'paнctrвy Юl(:1L BYf\t€!тri .:со· rпrp~fШЯе'I CBoj.~ на­
бор lI'Л<1сеОIi. :а:шерфе-Жов. лере1Utm:. Cl"py:t§.ryp ~ Д>eJlt:raТOB. бtщьШ;иЙl'!lnа из к;t)'ro­
рьш ~Д~ЖИ'r.C;fI 11 mS~Q.!l ib. idl l. ВJЮбiшoк 1t TI\ma.II!. содержnщпмcя в ;ms=-tlib .iOil1.
"'Ifl.CТb ЧЛ:ЩIQS S'I'i. tE>ro , Ю :еQД,е.pлtИI'CИ в :J(oMROJ-Ю80Ч1-ЮМ блоке 'SJ .5.~e т. д,] 1 (:воо 1!pO-
Щ'пы ;в V±щщl 8tuщо 2005 aвrrоl'dЖI ИчrJ;:NИ :уеТШIавmmaют её!.IЛf(,9 па. оба Э<m. ;!ЮМПQ­
JiQ~ блОFЗ-. u()этщ"ry; It-a,м -f>6 Э'J'О'М бееп\:й(OИ'tЫ!Я не ~}.
Зада"'tcli МtЮn:~~ пmOB. пр~ $.~"ste·1t\.. :r{) ,. Ю!.iIяIn'CJI про-граым1IR щ!д"
Д!:р.$ДЭ ~ес-ttих операдИЙ с }(;а'rrUlDТIThШI! фзй..'ШМИ. Но tCТЬ JiI ~e .'ШIШ. dбе­
феJ"!1lDaIOщие nOMep~1' 'ОI1брациЕ: <и'еUйя: J!I зaIШсЕдmm:ьn: tё;tpORoEblX буферов .. 3.
Т<И~@ · nеnо~w.,цC'tВeшu.m: дРетуп Jt UftMHm. Чrrо.бы np/,,;цоt."JIа:вmъ B~M оБЩУЮ ~ap­
"J.'i.ИЩf фymщw.она;,'IЫШIК IЮЗJ\IU:).ИWt.\CтеИ ДP·OCТpё1'Ie'l'OO. имен S::~'5.н;·Пi •.IО'. в ~абJL ] 6. 1
!)~W его (5а;аовые (I-reабс-трan-rn::ыеj w,naecbl.
'Вд.(]ба~щl( ~ :rrим THn~M. · доnyсt'а:ющим с-оздание Э1Nit:>:hПI.ляров.. в- E-УВ,К81'n .• го
'Щ1Ре'д~е.я ue.'I.bl'Й р:цд rr.e~Й. а. тaюsе 1'!абор абс'1'ражтй:ых 1(л:аОСiJD. {flJt г€аш,
·T~xtRe~d~r. "reJ:tWx1 ter и у,д,). ~o~opыe i)'беt2.11е"ШВэ.ю1' ОТ1q)Ь1'тh.fЙ iII.Ш~---МОРфт.1Й
m'JТ~феЩ: веем свtл'i;М ПРOn~Q~i'{ЬJМ RJ1acca:м. БOOlе~ ПО.ЦрОбнан йй:ф~рмi!ЩИ.Я об
эnюt "rttDЩ{ будет пре.,!'t.JшrаТl>CR :е l1pOцессе Ц3Jlbll~I'О об~Jj ~ИaJlа
":п-ой Г.JUmь'f.
648 Часть IV. Программирование с помощью библиотек .NET

Таблица 16.1. Ключевые типы пространства имен System. то

Неабстрактный тип
Описание
lUIacca ввода-вывода

Bina r yRe ade r Позволяют сохранять и читать примитивные типы данных (целые , логи­
BinaryWriter ческие , строковые и другие), как двоичные значения

Buffere dStream Обеспечивает временное хранилище для потока байтов, которые мож­
но будет направить в другое хранилище позже

Dire ctory Используются ДЛЯ работы со структурой каталогов машины. Тип


Di rесtоrуl п fо Directo r y предлагает свои функциональные возможности , в основ·
ном через СТ8Т11ческие методы . Тип Di rec to r ylnfo обеспечивает
аналогичные возможности с помощью подходящей объектной переменной

Drive l nfo Этот тип (появившийся В . NEТ 2.0) предлагает подробную информацию
о дисках, установленных на мащине

File Используются для работы с файлами . Тип Fi 1е предлагает свои


File l n fo функциональные возможности, в основном через статические методы.
Тип Filelnfo обеспечивает аналогичные возможыости с помощью
подходящей объектной переменной

Fi1e St r eam Позволяет реализовать произвольный доступ к файлам (например, по·


иск), когда данные представлены в виде потока байтов

Fi1eSystemWatcher Позволяет контролировать изменения внещнего файла

MemoryS trea m Обеспечивает прямой доступ к данным , сохраненным в памяти, а не в


физическом файле

Pat h ВыполltЯет операции с типами Sys te m. Str i ng., содержащими инфор,


мацию о файлах или каталогах в независимом от платформы виде

StreamWr i ter Исполызуются для записи (и чтения) такстовой информации файлов.


St r eamReader Эти типы не поддерживают доступ к файлам с произвольной органи,
зацией

Stri ngWriter Подобно типам St reamReader!Str eamwriter, зти классы тоже


Strin gRe ader обеспечивают Обработку текстовой информации . Однако соответству,
ющим хранилищем в данном случае является строковый буфер, а не
фl-lзический файл

Типы Directory(lnfo) и File(lnfo)


Пространство Syst e m.IO предлагает четыре типа. позволяющие как обработ­
ку отдельных файлов. так и взаимодействие со структурой каталотов машины.
Первые два из этих типов - Di rectory и File - с помощью различнЪ!Х статиче­
сRИX членов позволяют выполнение операций создания . удаления. копирования и
перемещения файлов. Родственные типы Fil e l nfo и D irе с t о r Уlпf о предлarают
аналогичные возможности в виде методов экземпляра (который. таким образом.
необходимо будет создать). На рис . 16. 1 показана схема зависимости типов. свя­
занных с обработкой каталогов и файлов. Обратите внимание на то. что типы
Di r e c tor y и File расширяют непосредственно Syst em. Objec t. в то время как
Di r ec to r ylnfo и File l nfo получаются из абстрактного типа Fil eS ystemlnfo .

L
Глава '16, Г!ростр-анатво j.1МЩ1 System .1O 649

Object

Di recta.ry

, File

Р} lеsу,s·tе,шLrnfо'

Dir~ct'0ryJij fo

Рмс. 16.1. типы' обеспечивающие работу с каталогами и файлами

Вообщеговорн • .F'ilеlПfо и DirectoI,ylIJLO явпяюТС8. лучппщ ~wбором .для ре­


курсивных операций (таких ЩiК', например. составление· перечнн всех по,цкаталorОБ
с даннЫМ ROрнем), nocK~КY члены массов Direc't ory и F11.e обычно возвращают
СТРОJ!Oвые эначенИR. 8. де строго I'ИlшзированныЬ 'объекIТЫ,

Абстрактный базовый класс FileSysteinlnfo


1)/щы Dl ге'сtогуIt-lfо и F.:llе I л .tо во МНOI'ОМ нЭ!следУЮ'I свое по!Зеденне от аб­
страктного Qазового I~.i"щссq Fil .~,5ystemI.iIfo_ По большей чаmи ч.лены клас.са
Fi 1 eSystemIГlfo используются для уоnyченив об:ЩиХ харантерИСТIfК (таких шrn:.
напри:мер . .время СО3ДaIЩJi, разл:ичные атрибуты и т.д.) соответствующего файла
ми ка,талОl:а. В табл. 16.2 описанысвой.ства F'ileSyst.emIrJfo. предстanляющие
наи60щ,ший ШIТetPес..

Твбnица '16.2. СвоЙ'сrвз FileSY$temInfp


СвоЙСТВQ ОП,исаRИ~

At tribu't.es ЧИ"i"ает ,ИЛи устанавливает атрибуТЫ, озs'lэанI~ыe с текущим файлом, ГlIЩА~


ставленным В, леречне Fi leAtt:tibutes
Creat:i!o.nTim:e Чита~т или уртанавлива.е1' время создания ДI1Я текущеrо файла иJ1и IЩталога
Exists Может ИСПOJIЬЗОВаП'СЯ для ВЫЯСГtения ТОГО 1 существует Л\1 данный файл ил/А
каталог

E~tensiem Читает расширение файле:

FullNam-а Пьлучает полftЫй ПУI'Ь каталога или фаЙ118


Las tj\.cc;e~Time Читае; или устанавливает время r:lОследИ6rо доступа к текущему файлу И.rJI1
катшюгу

liastf1J:i teTime Чищет или устанавливает время последнего оеанса эatlиси 'в текущий файn
/АЛИ Каталог

Natne Дл~ фaiiл0В получает имя файла . Для каталогов П"ОЛY'ffi.5Т ИМЯ последнего
К"в:галога в иерархии, если Т,акая иерархия существует. Иначе П(fЛУчает!о1МЯ
·к;атаЛDг'а

1
650 Часть IV. Программирование с помощью библиотек .NET

тип Fi leSys temI nfo определяет также метод Ое lete (). Этот метод реализу­
ется прОИЗВОДными типами для удаления данного файла или каталога с жестко­ \
I
го диска. Кроме того, перед получением информации атрибута может вызываться
Refresh (), чтобы гарантировать то, что информация о текущем файле (или ката­
логе) не будет устаревшей.

Работа с типом Directorylnfo


Первым из рассматриваемых в нашем обсуждении типов. связанных с ре­
ализацией ввода-вывода и ДОПУСRающих создание зкземпляров. будет класс
Directorylnfo. Этот ЮIасс предлагает набор Ч.iIенов, используемых для создания,
перемещения, удаления и перечисления каталогов и подн:аталогов. Кроме функци­
ональных возможностей, обеспеченных базовым RЛассом (FileSystemlnfo), класс
DirесtоrуlпfО предлагает и свои члены, описанные в табл. 16.3.

Таблица 16.3. Основные члены типа Directoryln fo

Члены Описание

Create () Создает каталог (или множество подкаталогов) в соответствии с


CreateSubdirectory() заданным именем пути

Delete () Удапяет каталог и все его содержимое

GetDirectories () Возвращает массив строк, представляющих все подкаталоги теку­

GetFiles ()
щего каталога

Получает массив типов Filеlпfо, представляюЩИХ множество


файлов данного каталога
II
MoveTo () Перемещает каталог и его содержимое в место, соответствующее
заданному новому пути

Parent Получает каталог родителя указанного пути

Root Получает корневую часть пути

Мы начнем работу с типом Di rectoryI 11 [о с попытки указать RОНКРетный путь


каталога д,iIЯ использования в качестве параметра конструктора. Чтобы получить !
доступ к теь:ущему каталогу приложения (т.е. к каталогу приложеюrя. ВЫIIОШ-IЯIоще­
гося в данный момент). используйте обозначemrе п. 11, Вот подходящие примеры.

/ / Привязха х техущеиу хатanогу приложения.


Directorylnfo dirl = new Directorylnfo("."); I
,
/ / ПрИВRзха х С: \Windows с nокoщыо строхи,
/ / ДJIH хоторой ухазаио "ДОCJ10:sиое" пр_еиение.
DirectoryIl1fo dir2 = new Directorylnfo(@"C:\Windows");

Во втором примере предполагается, что передаваемый конструктору путь (путь


С: \ Windows) уже существует на данной физической машине. Если вы попытаетесь
взаимодействовать с несуществующим каталогом, будет сгенерировано исключе­
ние System.IO.DirectoryNotF'oundException (RaТалог не найден). Поэтому если
вы укажете каталог, который еще не создан, то перед его использованием вам при­
дется сначала вызвать метод Create ().

I
l
["пава 16. Пространстtlо имеf! Sуstеm.Ю 651
1/ flршur81Ci1 Х lleiсуще.с:.!1J!.yuцeиу 1C&!Z!&Jtorv с ПОC3Jед~ ero соз.ца _ _ .
birecturyIl'1fo· dir3 =ne ... Di.тесtо:r;уlпfо.(@"С: '\Wi:ndQws\Тееtiпg"):
di rЗ. Create () ;

После создания объекта DirэсtоrуlпfР вы можете исследовать содержимое со­


ответствylOЩero каталога с помощью свойств. унаследоnвнцых от FilеSуstещln fo.
Например. следуlOЩИЙ КJ1ac~ создает нQвый об"еI<Т Direc't оrУlnfФ, СВЯЗ~:Ы:Й с
С ; \ Wiпdоw s (дрИ необход.I'IМОС'ТИ ltзмеНИТ,е З'1'от NYТJ:> В соответств;ии с УСТaliОВJtЗМИ
(-истемы па вашей машине) и отображаюlIIИЙ ряд цнтересных стат.истиче~ ДaJCI­
ных об указанnам: каталоге iPис. 16.2).
cla:$"3 P,togr<f1i1
I
atatic void Main (striI1g [] ar'gs)'
t
C<>nsOle. WriteLine (," ,-II""'*-Ji Забавы G Dirе ё tо.rу ("1nfo) .** '*\0",);
Dir\?,c toryInfO dir = l1eli1 DirесtоrуТnf' 0(@"С;::\WiпdQWS") I

/I ~фop!!fa~, о Jt8'1'UIo:rre.
Соns~,)lе .• W-:titеLiriе (""!О,'Н,. Информация о ~ата:ltоге ~""'**"},;
C,or..sQle. Wri t eLine ( "Пс:'1ГНО~ им.л: {О J .;, cti.r. FуllИ.aJt\е '}, ;
"y~ .t-1
"<-ОПЗ'9 1е _,.,. .
",~~ne """"". 101 " , .d 1' r. N ате)"
("rA."_. ' .
C01'lso1e. Wr i teLln.e (" РодW-rель: f О} ;" di:r. Pare!').t) ;
СCiП$ . оl~.W,:r i tеLiле('·Создан~ {О} 1', dir.Сrеаtiолt!:imе);
С (;ns оlе.W'r'itеLiпе("А'I'р'и.бу\ш'!: {О}", dir.Attrib!Jte,s ');
ConsOle. W.ri teblne (i'КQРRевой K3.T.aJ1o.;г~ [О I "'. dir. Root) ;
CoI'tsole. 'W;ritеLiпе ("* * .. ** *" ....... "'*.*'** ~.* * '" *......... *** .. **. ,* . \п") ;

Рис. 16.2. ИНформация о каталоге Windows

'ПереЧ'ень FileAttributes
Свойство AttLibiJtes, .Предостanле,EJ:FJое оt)ъеJ:t'I'OМ П;L-еS}''S1:е.mlлfо. обеспечива­
ет получеI-ше раЗJIllЧНОЙ щrформaщm о тенущем хатапоге nл:и файле, и вся она со­
держится Б перечнеFil еllttгiЬutеs, Имена полей' этого церечня rоворят сами за
себя, но неНO'rорые менее очевидные имена эдесь сопровощдa:IO'I"CЯ коммeн:raр:иями
(подроб'нОС'ГЕ ВЫ ищете в докумеmaдии .NEТ F'ra:mew(Jrk 2.0 SDK).

1
..,....
,
I

652 Часть IV. Программироваl1ие с ПОМОЩЬЮ библиотек .NET

public впит FileAttributes


{
ReadOnly,
Hidden,
J/ Фaй.n, RВЛЯXIЦИЙСИ часwЬJO операционной сисwемы или используемый
/ / ИСКJП)чительио операционной системой.
System,
Directory,
Archive,
/ / Это ини зарезервировано ДЛИ использовании в будущем.
Device,
/I Фaй.n RВлиется I иормАльным' (есЗIИ ие имеет ДРУГИХ
/ / установленных атрибутов) •
Normal,
Temporary,
/ / разреженныe файлы обычно RВJIЯID':l'СЯ большими фaй.nами,
/ / даииwe ~OTOPWX по большей части - нули.
SparseFile,
/ / БJIо~ пользова'1'ельсlCИX даииwx, связАИНIoIX с фaй.nом или ~а'1'алоI'ОМ.
ReparsePoint I
Compressed,
Offline,
/ / Фaй.n, ~оторый не будет ииде~сирован службой инде~сации
/ / содержимого операционной системы.
NоtСопtепtlпdехеd,
Encrypted

Перечисление файлов с помощью Directorylnfo


Вдобавок к получению баЗ0ВОЙ информации о существующем каталоге, вы мо­
жете добавить в пример несколько ВЫЗ0ВОВ методов типа DirectoryInfo. Сначала
используем метод Gе t F i 1 е s ( ) . чтобы получить информацию обо всех файлах
*.Ьmр. размещенных в каталоге C:\Windows, Этот метод возвращает массив типов
FileInfo. каждый из которых сообщает подробности о конкретном файле (подроб­
ности о самом типе FileInfo БУдУТ представлены в этой главе немного поэже).

class Program
{
static void Main(string[] args)
[
Console.WriteLine("***** Забавы с Oirectory(Info) *****\п");
DirectoryInfo dir = new OirectoryInfo(@"C:\Windows");

/ / Получение Bce~ фaй.nов с расширением Ьшр.


FileInfo[] bitmapFiles = dir.GetFiles("*.bmp"}:

// С~оль~о их всего?
Сопsоlе.WritеLiпе("НаЙдено (О) файлов *.Ьmр\п",
bitmapFiles.Length):
Гл&ва 16. Прос.транство И'-1&Н System.IO 653
/I ВJ.!aO'Д юtформаQJm О ф.мах.
foreach (File'Iofo f ;1.n Ьitmа.рFilез)
{
COn,501 е. )oVri teti!!'e (" *** ... "'*" '" "'* .. * ... *';' .. .......... .. ,. ** ...... \11" ) ;
.Сопsоlе_ Wr1t ,eL.lne .( "Иv..я: {О} ", f. NаПlе -) ;
C.onsole. Write:L:Ln6 ('i~аЗ)IJе:р: {О} ", f. L:engt!1) I
Console.W'rit;e1ine,{"\,:-оздан: {О'] ", f.СrеаtiQпТiJ:rlе)i
Console .Wr i t:еL'iп-е (" АТР11буты:: 10} ", -f. At tr ibi:ltes) ;
СОП501е.w~itеLiпеJп .... ~**** .. *~ ... *r·***~,,,,,,,,~ ~~* ·\пl,);

3а.uустив это nриложение. вы увцдите спищж, РQДР9ный по:кааанному на


рис. 1'6.3 fва.пщ ~зультаты 'Могут бъrrJ.> др:уги~t).

Рмс. 16.З~ Иl-tфоРМЗЦIllR о файлах с ТОЧt!ЧНЫIМИ изображениRМИ

Соадание подкаталогов с ПОМО,ЩЬЮ Directoryl'nfQ


Бы :можете программ.ао paC-ШИРИТ1:1 CTpyJaypy каталоr-а. И-СDОЛl1ЗУ8' метод
Direc'torYlnfo.createsubdlrectoryo. Этот МеТОД с :ЦОМОЩЪ1Q однorо обращения
1( функции цоаводяеТ еоздаТh как О:ЦИН поЩtаталог. так и. МДОЭftе~тво вло{Ке~
поЩtaТaJIоrов. Для примера. рассмотритс:: следующдй блон ЦРОГРaмJ\ЩОГО кода. рас­
ширяющий структуру Ra.T&;Iora. С: \ WindDws II}"Тем со<m1ЦiИЯ неско.тц.;и:их ПОЛЬЗОЩI­
тедьских подкаталогов.

clp.$·s, P;rogram
~
static void Maih (st:r:J:ng!] аrgэ')
!
со:nsаl€.-W'rit.еLinе (n .......... заб.ав~ t; DireC'tot'Y (1пfо) **H~\()';. ) I
Directorylnfo Ф,r = TLew OirectQryIrofo (@"е: \Wiлdоw.s··!) ;
654 Часть IV. Программирование с помощью библиотек .NET

/ / Создание \MyFoo в исходном хаталоге.


dir. CreateSubdirectory ("МуРоо") ;

11 Создание \МyBar \МyQaaz в исходном хаталоге.


dir.CreateSubdirectory(@"MyBar\MyQaaz");
}

Если теперь проверить каталог Windows в окне программы Проводник, вы уви­


дите там новые подкаталоги (рис. 16.4).

Рис. 16.4. Соэдание подкаталогов

Хотя вы и не обязаны использовать возвращаемое значение метода CreateSub-


directory (). полезно знать, что в случае успешного выполнения тип Directorylnfo
возвращает созданный элемент.

/ / CreateSubdirectory() возвращает об~хт DirectoryInfo,


/ / аредс'1'авлlUClЦИЙ новJo!Й э.nеиент.
Directorylnfo d = dir.CreateSubdirectory(nMyFoo");
Сопsоlе.WritеLiпе("Создан: {О) ", d.FullName);

d = dicCreateSubdirectory (@"MyBar\MyQaaz");
Сопsоlе.WritеLiпе("Создан: {О) ", d.FullName);

Работа с типом Directory


Теперь, когда вы увидели тип Directorylnfo в действии, рассмотрим тип
Directory. По большей части члены Directory "дублируют" функциональные воэ­
можности, обеспечиваемые членами уровня экземпляра Directorylnfo. Напомним,
однако, что члены Directory возвращают строковые типы, а не строго типизиро­
ванные объекты Filelnfo/DirectoryInfo.
Чтобы про иллюстрировать некоторые функциональные возможности типа
Directory, заключительная модификация этого примера отображает имена всех
дисков, отображаемых на данном компьютере (для этого применяется метод
Directory.GetLogicalDrives ()) и используется статический метод Directory.
Delete () для удаления ранее созданных подкаталогов \MyFoo и \MyBar\MyQaaz.

L
cl~$., P;rpgrwn
-{
6,иtiп "lO"id Nain 'l st;ring t1 ~!,g$'}
1
11 СmIOQ!{ ДI4;~()}t ,1!i]UIRQX'O '~QМПЬ~а •
.s±1: ".'1'1g t~ ":iriv.~'S, = Dii. rS .сt.а>.:гу ,'[jeti€l(J::' сдl D:r i.чеэ. С). I
CQti~IO.l Е • Wcrl t€!'L,i!,e ( ·" ifi()"i'· JillGtшИ, диС1':И:.: W'~ ;.

f(),r~Cib ~'$t:r irl(] ~ ;\;JJ ф;j "'~'s.~


Co~e'O.1 е . W':r,: i' leL.1roВ l fТ -> \'0 j q ( s) 1

/.1 У~гmевие СО!li;qaиsorо .•


·i.;ccl1;,Jol е. -щ~itf.!tinJ;) ! "Иf)l,»iЮ~1-7~. <Еm~ч:;;> ;<i.Ji\'3 ~~ij~LИ:" l!.Q";ГlUI:C!Ii'-ШВ~ ') ;
,~tl~-cr18 , Е-е.шll.fве О I
tty
1
D± J:'scI:iJJ'Y. DehJt-=:( ~ "'С: \W1J:!&~'$\1J.!у\F&lQ...г) "'
JI ВШClpФи' ПА~, aootJ~~~ ~ trИ JЩ
1I ~~ DOДilll~'Ц1IР:ll'И.
;О:.!, I:E<ct'",r.y • ooJ.e'te J ~ "'С: \ Wi.tLl:1iJ~....s'-\му:ваJ."" ( t:rit1e) 1

.;':a't сЬ (lq~!;""p.t. ;k)c ~).


~

Ра60та о тИпОМ M 'accaDriveLnf'o


Б· .NEТ 2.Й DpoC1'palk.<rвO имен ,~z;tla!l1,.I() пре~~ ~~€ ИI\'Iфе)! Itr i ..~ Jл:jd,
подобно D i r-e с 't о r у Л 81:1. Cfg 1 t al ех i \' е ~ ( )' .ета~еqшP.i ~W.!X pr i "",-31 [j f о .
Get.Dr i ve·5 ~ J nОЗ.Бомет J:1ЬUIСEtИТJo имейа ДIifCR(}~ маnцшц, OjUlaJ(G, ~ О'ЩМ"ЦJеат
Di t 'e lZt!C/,r y,.:ee:tko-gir:.aJ:Ьriv_еs О. кдасс Пхi'о1-е-I п,f~- ~бес~ет ИНО1Ш't"Т,rщ до­
П(l~1-lOЙ ItШФОРМ.ации (W1ПРИМЩi. h-.»фQРМil:ЦНI!O Q -т;шnе tWС1Щ, свобliJjl'!\l.О~
npОСтрaRёiI'!I€. метш -тома и ~,tt.J. Paecмo.тpIЦe "СJlt'дyIOШИЙ прдм:е'j!) Щ>flТРа:м:мцоro
1ЩДа.

.:;1 ~~S Рrфq,Л !i!J)


1
9 ta'-t i t:void Мaiti ! 9 ttif\9 [1: a,r9$-rj
[

/1 ПоnУ'че..'Ше ИНФ~АUЮJ о J(jIJC:UX-,


I4J-'/~lJl'f''i11 J IT!j!·QX1.",-е-з = D:ri1;,€'JrJf~"G:e'tf'I:i."ES.! .);
656 Часть IV. Программирование с помощью библиотек .NET
r
I

/ / ВloIIIод информации о СОС'l'оJIНИИ.


foreach(DriveInfo d in myDrives}
{
Сопsоlе.WritеLiпе("Имя: (О)", d.Name);
Сопsоlе.WritеLiпе("Тип: (О)", d . DriveType);
// Проаерха дисха.
i f (d.IsReady)

Сопsоlе.writеLiпе(" Свободн о : ( О )", d.TotalFreeSpace) i


Сопsоlе . WritеLiпе("Формат: {О) ", d . DriveFormat);
Console.WriteLine("MeTKa т ома: (О) \ п", d.Volum.eLabel);

Console.ReadLine()i

На рис. 16.5 показан вывод. соответствующий состоянию моей машины.

Рис. 16.5. Сбор информации о дисках с помощью DriveInfo

Итак, мы рассмотрели некоторые возможности классов Di r е с t о r у.


Directory Info и Drivelnfo. Далее вы узнаете, как создавать, открывать , ЗaIiРЫ­
вать и уничтожать файлы, присутствующие в каталоге.

Исходный код. Проект DгiveTypeApp размещен в подкаталоге, соответствующем главе 16.

Работа с классом Filelnfo


Как показывает пример MyDirectoryApp, класс Filelnfo позволяет получить
подробные сведения о файлах. имеющихся на вашем жестком диске (время создан­
нин, размер, атрибуты и 1:Д.), а также помогает создавать, копировать. перемещать
Глава 16. Пространство .иМам System,ro 657
1J YJЩЧJ"ожат~ фa:iЩы. Вдобаво:к к набору фушщио1Ia/Iъных возможностей. унасле­
ДЩ!aннblX ()Т Fil~Systemlnfo, RЛа.сс Fileln{o имеет свои унинвлъные члены. и не­
IЮторы~ ИЭIiИХ опишrn:ы в табл. 16.4.

Таблица 16.4. Наибm1ее 800кные ЗЛf!менты FilеIпfо

'{лен Описание

Appt;ndText (,) Создает Т1I1H StrearnWriter (буде'" ,оПIo1е.ан МЗ)I{&) ДI1~ добаsлеf.l\о1Я тек­
став файл
СоруТо () J(dпирует существyiOЩИЙ файл в I<IОВЫЙ файл

Ctea;te () Создает новый файл и ВО3!lращает тип Fi lеStrеam(буде.т описан' поз­


же) дnl'l взаимодейств~я (; создаНliыt;4 файлом

Crea,t eText.. (') Создает тип StrearnWri tet, k!)ТОРЫЙ записывает новый текстовый, файл
Delete (J Удам'вт файл. к которому приs~заж экземr:щяр filelnfo
Dire-ctory ПалуЧQет э~мпmф KaTВJlOгa Р'l)Дитеhя

Di,ectoryName, Лолучвет пОЛный путь к капlлОГУ родителя

Lel1g~h ПОЛ)j'lает ~a3Mep .екущего файЛа или каталога


MoveTo() Перемещает ук~эаниый файл в НОВОВ Mecro, имеет fJГЩИЮ ДЛЯ указания
но:воro ~мени файле
,Name Получае-т ~IMS1 Файла
ОраnО Открывает файл с эадаННblМИ ВОЗМОЖНОСТЯМ~ чтения/записи и совмест­
ного доступа

O;penRead ( ) Создает Fi.lestrearo () ДОСТуПОМ только для чтени~'


OpenTel':t ( ) Со;щает тип B'trea!l1Reader (6УДе!Т ОnИСat! позже:) Для чтения ив суше­
ствующего текстового файла
Openvvr i'te ( ) Создает Filеstrеаш с ДЩЦУnОМ талька для :записи

'Важно понимать. что большинство члеЕ:ов класса Fi lelnfo .возвращаerr специ­


альный об<ъ.ем ввода-вывода (File-St!l:'earo. StreamWriter и т.д.), Rоторьпl ПО,зво­
лит начать чтение и:ли запись данных в соответствующем файле 11 самых разных
форматах. Мы ИСС.l1едуем указанные 1'иIIЫ чуть позже. а по.ка что давайте рассмо­
Трим различные способы получения деСRриnтора файла с помощ~IO ТИIJa класса.
Fileln.fo.

Метод Filelnfo.CreateO
\
'! Первая воЗМожность созданй.fl деС.крип>гора файла обеспечивается Meтo.n:oM
р): le Info.Creat:e О.

P(Jb}.i.c claBs Program


{
stctic void Mcin ('String[1 'Э,rgs!
{
11' Создание ИОIIОro ф&ЙJf& 8& :цис.Jtе С.
FileInfo f -= new FileIX1fo (@"'С; \ Test. dat" J ;
ri1еS,rrеэl)1' fs = f .ol Cr~s.te () ;

11 ИСDот.sав&аке o&ъeX'l'& FileStreiam .•.


658 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NEТ

/ / Заltpbl'J:'ие файловоrо ПО'1'оха.


fs. Close () ;

Обратите внимание на то. что метод Filelnfo.Create () возвращает тип


FilеStгеаш. который. в свою очередь, предлarает набор синхронных и асинхрон­
ных операций записи/чтения для соответствующего файла. Объект FilеStгеаш.
возврarценный методом Filelnfo.Create (). обеспечивает полный доступ чтенйя/
записи всем пользователям.

Метод Filelnfo.OpenO
Метод Fileln fо.Орел () можно использовать для того. чтобы открывать сущест·
вующие файлыI и создавать новые с более точными характеристиками. чем при ис­
пользовании Filelnfo.Create (). в результате вызова Ореп () возврarцается объект
FileStream. Рассмотрите следующий пример.

static void Main(string[] args)

// Создание HOBoro файла с ПОМОЩЬЮ File1nfo.Open().


Filelnfo f2 = пе" Filеlпfо(@"С:\Те5t2.dаt П );
FilеStгеаш f52 = f2.0pen( FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None);

/ / Испоnьsоваиие об~Х'1'а FileStream ...

/ / ЗаХРЫ'1'ие файловоrо ПО'1'оха.


Е52 .Close () ;

Эта версия перегруженного метода Орел () требует укаэанйя трех параметров.


Первый параметр задает общий вид запроса ввода-вывода (создание нового фай­
ла, открытие существующего файла. добавление данных в файл и т.п.) с помощью
перечня FileMode.
public епит FileMode
I
/ / Дае'1' операционной СИС'1'еие ухазаиие созда'1'Ь НОВJo!Й файл.
/ / Если файл уже сущеС'1'вуе'1', rенерируе'1'СR System. 10. IOException.
CreateNew,
/ / Дае'1' операционной СИС'1'вме ухазаиие созда'1'Ь новый файл.
/ / Если файл уже существуе'1', он буде'1' переписаи .
Create,
Ореп,
/ / Дает операционной СИС'1'вме у:каsаиие O'1'ICpbl'J:'Io файл,
/ / если он сущеС'1'вуе'1', инаЧе следуе!l! созда!I!Ь НОВJo!Й файл.
OpenOrCreate,
Truncate,
Append
Г11а8916, ПростраНСfао имен .Sуstе!П./О 659
Второй парамсзр. значение иа перечнл FileAcc'l2s$. ИCnОЛРЗ~СЯ ДlI.Ii Qпределе­
JЩn характеристик чтейИя/заillfСИ 1} 'СОQтвеТС:(ВУlОщем rютоке.

public emim :F:J.le1\.coe.s,s


{
B-ead,
.w rite,
~eadWrit~

Н:mQнец. третий параметр. Fi leShare. yкa~ыветT :возможности совместного ис­


ПОJ1Ь3ования: файла другимn деекрипторами Фaйт.J~. ВОТ RШt ВЫГЛЯДИТ соответству,­
ющий: перечень.

рцbliс ,enum FileShcre


{
.NQле,
Rea·d,
Writ·e,
I,{eadWrite

Методы Filelnfo~OpenReadO и Filelnfo.OpenWrite()


X.OTJif метод F i 1 е I.n fo. Орег! (,) и обладает очень гибtCИI\lЦf ВО3МОЖRОСТЯМИ по­
..fI;y'Ч'еI:JИЯ' дескриптора фa:fu:ra •.кл.асс Filelnfo Т<ШЖе npедлагает 'tЛe:Flbl с цме~шми
орепRеаd () и OpenWrite ().. Ra.it вы можете ittOraдaThCf!. ~ТИ методьпю:шращают
ДОлЖНЫМ образом сКонфиrурировgннI:irЙ ТQЛЬ.ко.дЛJI <Пе:Ция или ' толъtIO ДЛЯ за­
IIИС~ 1'ЩJ File·S trea.!IJ, без необходимости у.каЗ8.flПfI еdQТВетст:вующщ ЗНаЧений
пе~Щt~Й.
Подобно Filelnfo.Create {) и Fi lеIпfо.Ореn (). методы Opef1REad () 11
OpenWri t·e (). воз.враЩaIOт объект Fll'e St rearn.

static void м .. iл~striпg tl arg6}'


j

/ / Попучei1lИ& обоъежlt'a. tileStJ;'eaJII с .цОС'1'?ЛQК 'l'О11ЪХО ~K Ч'1'8ИИ5r ..


FileTnf9 fЗ = l1ew Fil.е1.пfо (@"С: \Tes.tj! . ,dаt ) П
;

Fl1eStr-еаm I eadOnl y5t:x:earn = f3. Ope'nRead ( ) ;


/1 иоnom.!tdвамие об<иuc'1!а FileStream . ..
readOlllyStrearn.c:'!ose ();
11 Пои)''l$ииe a&J,er.t'a
F11eStr8&ln со ДОC'Z'УПОМ '1!ЩIIoХО ДJП[ S'IШИСИ.
Filеlп.fD f4 = .new FileI.nfo(@'''C;\ Test4.i!a·iL");
FilеStгеЩI1 w.r:~ teOJllyStream = f4 .Opel'lwr·ib.e();

11 Испom.заВasиe oбoJ.eX'J!& :JiI.ileStream . ..

J 'Wri teOnlyS,tream.Close!);
660 Часть IV. Программирование с помощыо библиотек .NET

Метод Filelnfo.OpenText()
Другим членом типа Filelnfo, связанным с открытием файлов, является
OpenText (). В отличие от Create (), Ореп (), ОрепRеаd (} и OpenWrite (), метод
OpenText () возвращает экземпляр тиnа. StreamReader, а не типа FileStream.
static void Main(string[] args)
{

1/ Попучение об'loеJC'l'а StreamReader.


FileInfo f5 = new FileInfo(@"C:\boot.ini");
StreamReader sreader = f5.0penText();
/I Испопьsоааиие об'Ъ8lC'l'а strеaшReаder . ..

sreader.Close();

Чуть поэже вы увидите, что тип StreamReader обеспечива.ет возможность чте­


ния символьных данных из соответствующего файла.

Методы Filelnfo.CreateText() и Filelnfo.AppendTextO


и последними интересующими нас на этот момент методами будут CreateText ()
и AppendText (), которые возвращают ссылку на StreamWri ter. как показано
ниже.

static void Main(string[] args)


{

FileInfo f6 = new Filelnfo(@hC:\Test5.txt");


StreamWriter swriter = f6.CreateText{);
1/ ИСDопьsование об'Ъ8lC'l'а streamWriter ...

swri ter. Сl0зе () ;


FileInfo f7 = new FilеIпfо(@"С:\FiпаlТеst.tхt П );
St.reamWriter swri terAppend = f7 .AppendText () ;
/ / Испonьsоаание об'lo8lC'l'а StreamWri ter . ..

swriterAppend.Close();

Вы должны догадаться са..1\1И, что тип StreamWriter npедлагает способ записи


символьных данных в соответствующий файл.

Работа с типом File


тип Fi 1 е предлагает функциональные возможности, почти идентичные воз­
можностям типа Fi 1е 1n f о, но с помощью ряда статических членов. Подобно
FileInfo, тип File предлагает методы AppendText (), Create (). CreateText (),
Ореп (). OpenRead (). OpenWrite () и ОрепТехt (). Во мноrих случаях типы File и

L
Гла;зэ 16. Пространство ИМ~I'I Srstem.1O '661
Filеstrеаm'ОКазываются ВЗaIOЮзnменяемъtмn. T~. D-JШЖДОМИ& upедp.rд;ущщ щrщ­
Меров вместо Fil&Stream можно использовать тип Fi 1е.
static vC>-l.d Ma'in (string[] ,args)
(
j I Попучеиие О'б'W!JtТ& FileSqecn с DОМCiЩЪJ) Fil~. Crea te () .
Fi1 $S tre<;\m f s = Е'Не. c::reate (@ nC,; \T~st:. da t',·:) ;
f:s. • cl o..se ( ) i
/ I ПQ;nучеюte 06'ИJC'1:а. FileStream с ПОIllOЩЫD File , Ореn () .
FilеStrеdП1 [э2 = File.Opel'! (@"С; \1'e'8,t 2 .det n I

filеН!йdе.О'реГ!ОrСrеаtе,
fileAcDe-3s·.RеаdWri te., Fi1eSr.a:rce. Nc:ne) i
fзl . Clc:se О i
11 По~евие объеJCша.Filеstre_ с .1I,QСoryпои mom..Jil:O Д;n.SI 'И'еИИ5l:.
FileStream r~QdOnlyS.trepm = File.Open.Rea.-d.('@"'ТеstJ _ . dаt" !;!;
r,eadOnJ у,S·lIеа.щ. С 1 ОВ'е (J ;
/J По;nyчеме O~X'1!a, Fl.lеstrеш с доступом ''I'onьxo ДШI запиеи.
i"ileSirea:m writeOnlyStream = FH е .. ОреПWJ:i t,e \@"Test4 . dat n ) :

writeOnlyst.r:6!aJ1\ •.Clo'se'O "


1/ Получение О&их!I'а StreamReader.
Sj:.rеiЩtRеаxj,еr srea·d er = Fil.e. Ope.nT~xt (@"С: \bOQt. ii)i ") ;
sT€'a dec Сlозе () ;
/ / IlOJt)l'ч.еВИ8 иес~о~~.~ объеll:'Z'ов StreamWri t;er.
!ОtrеапJW:rit.е.z 5writ.er = F'ile. Cteate'rext l@"'C : \Test.3. t1'lt IТ) ;
swt i ter . tlos€ () ,;
StreamWri ter SWJl'i tеrАррелd = Fi.J..e .AppendText. \@"С: \F 1Г-"11 Те .. t. txt ");
5writerAppend.Clo5€();

Новые члены File в..НЕТ 2.0


в отличие от Fi1eInIo. тид f 'ile по;адеРw:I1В<ctет [11 .NET 2.0) В:еСIЮЛЪ:n'''О' СIЮ­
их собствеFЩъ1X уцикaJIЬJiЫX члевов, ОПИСaнIOJ котрр.ыхuриводатс.и в табл. :i6.5.
СПОМО!ЦЬЮ этих члеаов можно существе.ЩlО упростить цроцессы чтеНИ.fl. и записи
тeIf.М'O~ ДaнIO>ЦC..

Таблица 16.. 5. Методы типа Пle

Метод
~eadlUJ.ByteJ3 О '{).ткрьщает ук~за.н~,ы~ файл, ВОЗВр'~щает jЦВОИЧНЫе. дa!1fJble в ВИД!'! мас­
cl'ffia байтов, а затемзакрь,взет фаил
~eac1'AILLlilf?S () Оl1фывэет у~азанный файл, возвращает с~маольные ДEll"lliые в виде
массива стро'К, а затем закрывает файл
P-еа,dAllТех t 'o Oткpt,'вэеr указанный файл, возвращает символьНые дIlliHЫ.e в эиде'
Sу,s,tеm.Бtr:iлg, а Щlтем закрывает файл

Writ.elйlВytes () O,·t<:pbIBae.T указЗ\i}lЫЙ файл, 3qписывает массив байпщ з затем за­


Крывает файл
Wri.teAll I..iЛез О Открывает ykaaaHI:IЫJ7i файл, заП(.iсывае" ' мас.сив. строк" а 3атемЗ<JКРЫ­
вает файл

Wr:i teAllТe.xt. (.) ОТlф.!II.Вает ук:а=,анный файл , зап'исываеi СИМ81JJ1ыtые даt:JНf.,те, а затем
заl\:рывает ф1J,ЙЛ
662 Часть IV. ПрограммироваНl1е с ПОМОЩЬЮ библиотек .NET

При использовании зтих новых методов типа File для чтения и записи пакетов
данных потребуется всего несколько строк программноro кода. Более того. каждый
из указанных новых "шенов автоматически закрывает соответствующий дескрип­
тор файла. например:

class Program
{
static void Маiп(striпg[] args)
{
string[J rnyTasks = !
"Прочистить сток в ванной" I

"Позвонить Саше и Сереже",


"Позвонить родителям",
nПоиграть С ХВох");

/ / Записать все дaННЪJe 11 фaй.n на .цисхе С.


File.WriteA11Lines(@"C:\tasks.txt". myTasks);
// прочитать все своваи напечатать.
foreach (string task in Fi1e.ReadA11Lines(@"e:\tasks.txt"))
{
Сопsо1е.WritеLiпе("Нужно сделать; {О!". task);

Очевидно. когда вы хотите быстро получить деСIq>ИПТОр файла. ТlШ File из·
бавит вас от необходимости ввода нескольких лишних строк. Однако преимyIЦе­
ство предварительного создания объеI,та Fi1eInfo заключается в том. что тогда
вы получаете возможность исследовать соответствующий файл с помощью ч.пенов
абстрактного базового класса Fi 1eSystemInfo.
static void Main(string[] args)
{
// Вывод информации о файле boot. ini
// с Dоcnе.цуации отхрытиеи .цоступа mолысо ,цлк чтеНМR.
Fi 1е1 n Ео bootFi1e = new Fi1e Info (@"е: \boot. iпi") ;
eonsole.WriteLine(bootFile.CreationTime);
Сопsо1е.WritеLiпе(ЬооtFilе.LаstАссеssТimе) ;
FileStream rеаdОпlуStrеаm = bootFile.OpenRead();
rеаdОпlу8trеаrn.еlоsе() ;

Абстрактный класс Stream


к этому моменту вы уже видели множество способов получения объектов
FileStrearn. StrearnReader и StreamWriter, но вам придется еще читать и записы­
вать данные файлов. связанных с этими типами. Чтобы понять, как зто делается,
нужно ознакомиться с понятием потOIШ. В "мире" ввода-вьшода IWmOK представ­
ляет порцию данных. Потоки обеспечивают общую возможность взаимодействия
с последовательностями байтов. независимо от того, на устройстве какого вида
(В файле. сетевом соединении, принтере и т.п.) они хранятся или отображаются.

.
L
Глава 16. Про.страНСТВD имен system.1O 663,
АбстранТВЬ!Й КJlace S~_stem. IO.stTearn определяет ряд членов, обеспечивающих
поддеWIP'Y q.шxррнноro и i:!СЙНХfЮННОГОl!Заимо;цей:ствив с ЕюситмемдafIНЫX (ска­
жеы. с файдом: ИДИ обл~тыо памяти). На рис, ] 6.6 пав:азано llесКШlЪКО потомков
типа StrеЩТ!.

Strea1t1,

FJleStream

ыleтoT-yBиы.m.

I EufferedStream
Рис. 16.,6. Типы, ГТРОИЭВОДНЫВОТ StJreq.m

3a...e-"8НJ11e.
Сл&дуе, знать, ЧТО ПО~lятие потркэ примеНI1МD не ТОЛЬКО 1( файлам ~1'ЛИ оБЛ;;JСТИ nSM>I-
ТИ . Без сомнения, бlilбл'и,отеки .NEТ ot5есПeчmJают t:lотоковl;Iй доt)туп' к сетям И ДРУГ1-1М С8ЯЗВI'r­
I:IblМ С по1'ОkaМИ абстракЦиям.

Напомним. что потомки Strearn: пред~а1ЩЯЮТ даIЩ.blе в виде ~cыpoгo· ПОТОЩi


байтов, IIOвтоМУ работа с uо::rоками может быть весьма неnoщпноЙ', Ыекоторые от­
ноcлurиеся:к Stteam -тn:r:rы nо,ддерЖQвaIOТ ЛDщх- ~TOТ терми.n. ПО' сути. 0знаЧ<iет
процесс полyqения и иэмеsешщ теRYJЦей позиции в uoтщсе. Ч'ГО(}Ы понять фун.кци­
опальные возможности. npeддa'raeMbJe ,классом St.ream. рассмотрите егО' базовые
члены. описанные в :rаБЛ. ]6_6.

Табтща 16.6. Абстрактные 4f1eHbI St r::eam

Чпенw Описаliие
C&nRea'd Qоределяет; поддерживает ЛИ текущий поток "тение , 'поиск и/или запись
J;~I'1'Se~k
CanWrite
Close () Завершает текущиЙ nGТOK н' освобождает все свя,занНые с тщщt.:11У1 Т1ОТОКО/>А'
ресурсы (например, coквl'ы и дескриmоры фаИ~lOа)
Flu.sh () ОБНОВЛЯElТСВЯЗаннOIИ иСтО'lНИК ДЗНrlbIX ,1OIJ1l.II хрэмwrli!Ще в соответСТllИИ с Л:i­
~ЩИМ СОСТОRнием буфер<\. а затем О"IИЩает буфер. Если ПОТ0К не реfИ'lизует
буфер" ЭТGт М(ПОД Не Дед'ает НИ'!I;\ГО
Lengt~ Возвращает ДflиflУ ПО'tOка в байтах
Position Определяет позицию в теl(yщем 'потоке

ReadO Читает ПОС118дрвателЬfЮСТЬ байтов (или один' байт) из текущего поток,а и СДЕИ ­
ReadI3yj:eO п~ет ук~тещ, ПО3ИЦIIIИ В {;оответствии со С'IитаННЫhl '1ИС110М бай'mв

Sieek () Ух;танзвливаат указатель в задаНIiУЮ позицию в текущем потоке


SetLeng tb () Устанавливэ~т дТ!ину тек~щеFО пщока
iilr.ite () 3а"ИСЬJi~ает ПdслеДОllaтелl,НОСТ!> баiilтоа !И!l11 один байт) '1'1 текvщий ЛОТЩ< ~
WriteByte () сдвИгает указатель позиции в ооответсТЗИИ СО c'lитанныM числоМ баiilТОl:\'
т
664 Часть IV. Программирование с помоЩЬЮ библиотек .NEТ

Работа с FileStream
Класс FileStream обеспечивает реализацию абстрактных членов Stream в
виде, подходящем ДЛЯ файловых потоков. Это довольно примитивный поток- он
может читать или записывать только один байт или массив байтов. На самом деле
необходимость непосредственного взаимодействия с членами типа FileStream
возникает очень редко. Вы чаще будете использовать различные уnаковщulCU тю·
moКОВ, которые упрощают работу с текстовыми данными или типами .NEТ: Однако
для примера давайте позкспериментируем со средствами синхронного чтения/за­
писи типа FileStream.
Предположим, что мы создали новое консольное приложение FileStreamApp.
Нашей целью является запись простого текстового сообщения в новый файл с
именем myMessage .dat. Но IIОСКОЛЬКУ FileStream может воздействовать только
на отдельные байты, требуется перевести тип System.String в соответствующий
массив байтов. к счастью, в пространстве имен System.Text определяется тип
Encoding, предлагающий члены, которые выполняют кодирование и деКОдирова·
ние строк и массивов байтов (д.ня подробного описания типа Encoding обратитесь
к документации .NEТ Framework 2.0 SDK).
После вьmолнения кодирования массив байтов пере водится в файл с помощью
метода FileStream.Write(). Чтобы прочитать байты обратно в память, необхо·
димо переустановить внутренний указатель позиции потока (с помощью свойства
FQsition) и вызвать метод ReadByte (). Наконец, массив байтов и деКОдиРован­
ная строка выводятся на консоль . Вот полный текст соответствующего метода
Main ().
// Не аабу~ьте 'использова~ъ' System.Text.
static void Main(string[) args)
(
Console.WriteLine("***** Забавы с FileStreams *****\n");
/ / Получение объеltТа FileStream.
FileStream fStream =
File.Open(@"C:\myMessage.dat", FileMode.Create);
/ / КОДИРО!lание С'.1'РОl'tИ в !lи.це масс_а байтО8.
string msg = "Привет!";
byte[] msgAsByteArray = Encoding.DefaUlt.GetBytes(msg);

/ / Запись byte [] !I файл.


fStream.Write(msgAsByteArray, О, msgAsByteArray.Length);
/ / ПереустаИО!lха !lИУ'.1'реииего ухаза'l'eJIИ позиции ПОIPоха.
fStream.Position = О;

/ / Чтение ТJШов из файла и J51oQ10Д и:а JCоисо;m,. .


Сопsоlе.Writе("8аше сообщение в виде массива байтов:");
byte[] bytesFromFile = new byte[msgAsByteArray.Length];
for (int i = О; i < msgAsByteArray.Length; i++)
{
bytesFromFile[i] = (byte)fStream.ReadByte();
Console.Write(bytesFromFile[i]);

L
Глааа 16. ПРQСТРЭНС:ТВО и~ен SY$tem.1O 665
1/ Вlaoд lЦехО~Q.&1IИ.0%'О сообщение.
С:оnзоl е
. W'rit€ (П \:nДе!<QдироваЩ'Iье с;ообщеl;iие ~ ");
Console_ Wri t,eLll'1'e (Епсоding. Default • G€tstring (bytes.FromFile) I ;

I/ ~&аеPJllевиie по~аll:&.
f,s,tream.Close(J ;

ХОТЯ в этом прцм, ере файл данн:ы:ми не заполняется, уже здеоь становится
очевидныM rna.внъ.Щ :цедостатoR работы СТИПQМ FilеS:tгеаm,: приходится воздей­
СТJIо&этъ непосредственно на отделъ:в:ы.е байты. Дрyrие ТИПЫ. яв:тшющиеся произ­
III;1ДНblМИ <>Т S'trea.m, работают анало:г.ИЧН'о. Например. чтобы записать nоследова­
-reлъность байтов в 3~ область.пa:мJпИ, :МOНm"o исnoльзоватъ MemoryStream.
Точно Так же, чтобы переда'FЪ массив байтов по сетп, вы можете исПОЛЬЗовать тип
:Networ kS,t ream.
J:t счщ:тью. цространство имен Sy.stem.l.0 предлагает цеirый ряд типов ~чтеНия"
_я, ·зади~·. "ЦНRЩIсулирyIbщих особеIlНОСТИ работы с типами. ПРОИЗБОДНЫМI1 от
Stream..

ИСХОДНЫЙ KDД. Прфект FileSh<eamApp ра<\м~щен 8 под~аталаre" сооrветuщующем главе 16.

Р:абота с StreamWriter и StreamReader


Класс1:.I Strea:mWrlter .и StreamRe'llder оцаэъшаютс.я подезньт тогда. 1WJ'да.np:и­
Х'одится ЧИТать -ИЛИ записы:ват,J>' СИМВОJIЫlые д~e (например. строкИ), Оба эт.и
типа по УМОЛ'Ulнию работают с символами UntщЮе. ОДf!ЭJCО 'Вы можете изыенить
ЗТМ устаноmcИ" предостаВWJ ccъrJIRY на пра~о о;Itонфщурировaнный объект
System :rext. EncDd.i I)g. "t:lтоБы ynpoститд расС1ilЩ'l-рение. цредпоJЮЖИ:М. что :пред­
лarаемое по у:молчав.ию кодировациев симвOЩ:J Uц1соdе как раз и ЯВJШется под­
ХОДЯЩИМ.

тип Str'e-аmR~аdеt ПО.1IyЧаетсн из абстраитноro ТИЦа Te,x tRedder. То ию можно


CRазать и о родственном П(].Je S-triлgр,еаdеr (он fJудет обсуждаться в .этой :rлаве
позже). Баэовьtй К/laCC T!e xtReader обеспечивает каждому из этих ''nоследователей''
Очень неDОЛЫПоц набор Функционаil,J>НЬД возможностей, среди которых, в ча.стна~
сп!, ВQзlYЮЖНОсть чтения сцмволов ИЗ ПОТОI«! И IIX .добщmение в пОТОк.
1'иD Stre,a mW'riter (нан..и StringWrit:ec j{оторЫЙ,.~е будет рассматрив.a.тъQ.я
П~3Же) IIол:учаетс.я ИЗ абстрзJtnщго базового ю.Щt:са TextWriter. Этот класс оПре­
деляет члены. ПО3ВQЛЯЮ1Цие ПРОИЗВСIДНЫ};J тиrra.м, ~tывaтъТm:t.стовые дашrые В
ИМeIOЩ}D1СН символьный ДОТОЦ , ВэаимОCЩJЗЪ Мt!ЖдY Э'I'ИМИ :аОВЫJ\Ш 'J'.ИПВ.МИ ввода­
вывода показана на рис. 16.7.
Чтобы помочь вам понять ВОЭJ!,&ощности зan.и:с1r классов StreaJtiWri ter и
StringWriter. в тa~. 15.7 :щreддагщотt'Л оIIИСЩПIН рсншщых члеНОВ абстрактно.tа
баЗЩЮfО кла~а 'rextWriter.

--10... .
т
666 Часть IV, Программирование с помощью библиотек ,NET
I
Object

TextReader

StreamReader

StringReader

TextWriter

StreamWriter

--1 Stringwriter
Рис. 16.7. Читатели и писатели

Таблица 16.7. Основные члены TextWriter

Член Описание

Close () Закрывает записывающий объект и освобождает связанные с ним ресурсы,


При этом автомаm'lески О'IИщается буфер

Flush () Очищает все буферы текущего записывающего объекта с тем, чтобы все дан­
ные буфера были записаны на соответствующее устройство, но не закрывает
сам записывающий объект
NewLine Указывает константу обрыва строки для производного класса записывающего
объекта, По умолчанию признаком обрыва строки является возврат каретки с
переходом на новую строку (\т\п)

Write () Записывает строку в текстовый поток без добавления константы обрыва строки
Wri teLine () Записывает строку в текстовый поток с добавлением константы обрыва строки

Замечание. Последние два из указанных в таблице членов класса TextWriter, вероятно, по­
кажутся вам знакомыми, Если вы помните, у типа System.Console есть члены Write ()
и Wr i t еL i nе (), записывающие текстовые данные в устройство стандартного вывода,
На самом деле свойство Console. Iп является упаковкой ДЛЯ TextWri ter, а Свойство
Console.Qut - ДЛЯ TextReader,

Производцый класс StreamWriter обеспечивает подходящую реализацию мето­


дов vv:r:ite (), Close () и Flush () и определяет дополнительное свойство AutoFlush.
Это свойство, когда его значеliие равно true (истина], заставляет S"trearnWriter
при ВЫПОJ1нении операции записи записывать все данные. Можно добиться луч­
шей производителыiсти,' если установить для AutoFlU$h значеliие false (ложь).
поскольку Иliаче при каждой записи StrearnWri ter будет вызываться Close ().

!
l
L
[Л3iа 16. npocrpaflcTBo име~ISуstеm.10661

Запись в текстовый файл


Рассмотри,м цример работы с 'ГИlJОМ Strea,mW'ritez:. Слецующий класс создает
новый файл remiTlders .tx't с ПОМоЩЬЮ метода File.Cr~ateText(). С помощью по­
лученного ' объента S'tr,e·a:mv.:riter в новый файл.добавляются опреде.ireнные тексто­
вые ,дaщn.rе, 'RaК, показЩЮ ~~.

s tatic VQid Ма:Lп (s't .r iлg (] атчs I


{
' СОf)'sC\lе, .Wгit-еLiпе (<I'H"* За ба:вы: с StrearnWriter/St:te,amR,e a'd er ~""" \I1 ");

/ / Поnучеиие StreamWr,i ter :и ,Запись C'J!POIICOInIX A~.


StxeamN:t iter: wri ter = Fi1e. Creat~re~t (''' .rem.inde:rs,. txt" ) :
writer. WriteLine (,"Не за.быть 'О ДJl-iе J,ТDждения мамы .•• /' :);
writer. WriteLine ("Не з.а:бы'r'~ о, дне РОЖI1ёНИЯ: щЕIы .• , п, ) ;
writer. Wгi :tеLiле {"E~ заБЪ1rJ1f; (> ,слеДУ,JQЩих, чи'Слах; ") :
fprlint i = О: i < 10; i++)
1'Iri t€r. Write (i + п П) ;

1/ Bt!!rUQ вавой, стром.


w z:i t c,r . Wr i t е' !\П i ter .1\Jew:Line) ;
11 ЗаxpJoWИе авТОIA.!РкчесJCИ Вmlче'1' запИсь всех ОСI1'U~СЯ , .IC~!
writer. cio$,e () ;
Ct;Jn,scle . W"r i t'eLlne ("С0З-ДдВ фай,л 1.1" sапие-аны Н12КОТ0рые идеи., . ") ;

ВЫП.(iЛПИВ эту ироtрамму'. вы можете провернть содеР'J<ИМое НОВО'ГО Фа.йла


{рис. 16.8}.

IИ" _БЪ!'1'1> ~"" РQ:>r;ц"IU1ЯJ<a1<!;<···


,Jie " aбblть а
CI
_15 Рrnot:I,.,юr.ll лаm. •••
н!! =быть Q с=mшпiio:х Ч>i>::'JПiX'
0 1 2 3 " 5 ,6 7 8 9

Рис, 16.8. СQдержимое ваШего файла ж. txt

Чтение из TeKCТOBoro файла


Теперь ВЫSIСНИМ. как npограммны:ми cpeДCТB~ читать ДAHRЪ:Ie 1~,З файла.ис­
пользуя еоO'ЛJСТСТВУЮ!ций тип Stre.amReader. Вы ДОnЖНЬJ щ)~нитъ, '!То этот' ЮЩСС
ПDлучается из Text.Reader. Фушщионалъные вовможнос:rи которого ОЦИCЗНI>J' в
таб.i-r. 16.8.
668 Часть IV. Программирование с помощью библиотек .NET
r
Таблица 16.8. Основные члены TextReader

Член Описание

Peek () Возвращает следУЮЩИЙ доступный симвОл без фактического изменения


позиции указателя считывающего обьекта. Значение
-} указывает пози­
цию, соответствующую концу потока

Read() Читает данные входного потока


ReadBlock () Читает максимальное заданное число СИМВОЛОв текущего потока и запи­
сывает данные в буфер. начиная с указанного индекса
ReadLine () Читает строку символов из текущего ПОтока и возвращает данные в виде
строки (пустая строка указывает EOF - I(О~lец файла)
ReadToEnd () Читает все символы, начиная с текущей позиции и до конца потока, и воз­
вращает их в виде одной строки

Если теперь расширить имеющийся масс MyStreamWriterReader. чтобы ис­


пользовать в нем StreamReader. вы сможете прочитать TeI~cToBble данныIe из фай­
ла remi nde тв. txt. каи пшtазано ниже.
static void Main(string[] args)
(
Console.WriteLine("**~ Забавы с StreamWriter/StreamReader ***\п");

/ / Теперь чтение данных из файла.


Console.WriteLine("BoT ваши идеи:\п");
StreamReader sr = File.OpenText("reminders.txt");

string input = null;


while ((input = sr.ReadLine()) != null)
{
Console.WriteLine (input):

Выполнив программу. вы увидите символьные данные из reminders. txt, вы­


веденные на ИОНСОЛЬ.

Непосредственное создание типов


StreamWгiter /StreamReader
Одной из смущающих особенностей работы с типами из System.IO является то.
что часто одних и тех же результатов можно достичь в рамках множества подходов.

Например. вы видели. что можно получить StreamWrlter из File или из Filelnfo.


ИСПОЛЬзуя метод CreateText (). На самом деле есть еще одна возможность полу­
чения str:eamWriters и Str:eamReaders - зто непосредственное их создание.
Например. наше приложение можно бьmо бы переписать в следУЮщем виде.

static void Main(string[J args)


{
Console.WriteLine("*** 3-абавы с StreamWriter/StreamReader ***\п");
ГЛЭ;ВII 16. Прост.ранС.тво l1"1eH S,y.stem.1O 669
11 Get а StreamWrlterand wzlte atring data.
S-trеаmWr:itеr writer = newS·t reamWr i ter (" rerninёier:s. 1:)!t,j) :

11 ИD" r ••dda:ta fram file.


St.reamRE:!>ader sr =. new StгеаmRе.аd-е~ (1. rеmindjaТЭ. txt") ;

Видеть так много идентичных, на первый 1tзгЩfД. ПОДXQДОВ К реалJlЗЗЩЩ вво­


да.:вывода. мож.ет быть, немноro стрmmо. 11(1 имейте в В1Щ.У. что конечнщм резущ!­
татом gAeOh оказывается гибкость. Так };iЛИ иначе. вы СМОГДИ увидеть, как МQЖНР
извлекать символьные данные из файлов и домеЩQТ.q их в фЗЙЛЬJ. ИЩIЩ[Ь~}J пщы
St reamW.r .iter и
S':.reamF.e:ader, И теперь мы с вами можем рассмотреть PQЛЪ щщс­
сов Strinql~ritbl: и StringReader.

Ксходный k-OA. Проект StreamWriterR~aderApp размещен в I1O):J,КаТ<lЛРГ~, СОQтаетствующем rла­


'де 16.

Работ,а с тип, ами StringWriter и StringReader


Используя ТИI1hI Stri.ngWriter и $:tringR~ade:r, 'IЦ,I мож{,"те Dбращаться стек­
СТОВОЙ информацией. как с потоком СИМВОЛОВ D пцм,нти. Это м-ожет оказаться по­
ле3ным тогда. когда tJеоf)ходимо добавить ~имво~о информациЮ в соответсТву­
ющий буфер. Б с.iIедующем npцм:ере '(щок' строJ<Qвых данцых записывается 'в объеш
St r in.qWrite.r, а не Б фt;lЙд да лцкaлыrом жестком диск-е.

staJ;.ic voi~ Ма iп (stri n-g I ) args)


f
CbnBo.1,e.\'1ritel, i.ne ("*"'* ' Забавы с :3 t:ringW'rl terl St r;-i,пgRеаdеr "''''''\Тl ~');

/1 Создавие StrinqW%i ter и DIIIOp, CЮlВomtlDa д&ИJiI,tX • ПIUlJl'no ..


StringW.r;i te:r: strWri ter = f!-SW Stri ngwri ter () ;
.s't..tW'I'i ter. ~ri t€I,J.ne ("Не забы'r'l> о дне ро2l\.,lJе.ния мамы .. . ");
.9 t rW.r-i t e r . Close () .;

11 ПOJiуч_иие КОПИИ содерZlИDJ:'О (оохраиеВВGr!) s C~Oll:e) и


/I BWВOД ~a .O~C011Ь.
COD8Dl e. ,Wx i teLin.e ("Содержимое 5tringWri ter·; \п \ о ·} п, зtroWri ter) ;

Бвиду-тоI'О. Ч'l'о n S.triпgWrit-€r. и .s.treamWrlter ПОлуЧают.ся из одноrо.и T~ГO


же базовOr'Q нлас(':в, (TextWri1;e·r). дл,tr- юп НСП~JlЬЭУе'1'СЯ приблизитe.J1ЬНО о,цина-
1tОВая IIpограммная Л'оnца зал'неи . ОДЦaIl;О ВВИдУ самой Nroей 1ф':ироды. • .класс
StringW.riter noзводяет иэвле% объект SУ:;11::еm.Тех:t..St ;r ШgБD.i ldе.r СПОМОЩЪЮ
метоДа GеtSJtt'iпgБui lder () .
st:atic void Main (string [ 1 ,a.rgs)
{
Conso1 e . Writ:eLine (" ;н,. ,,, Эilбam:il- С S tlOingWr i t er / St.r i i1g Reader ,. * * \n") ;

I
1
670 Часть IV. Программирование с помощью библиотек .NEТ
r
/ / Создание StringWriter и B:WВOД сиивоnъlWX дaНIWX в П8.llJR'Ъ.
StringWriter strWriter = new StгiлgWгitег();

/ / Получение внутреинerо StringBцilder.


StringBuilder зЬ = strWriter.GetstringBuilder();
sb.:r:nsert(O, "Эй!! ");
Сопsоlе.WгitеLiле("-> (О}", sb.Tostring(»);
sb.Remove(O, "Эй!! ".Length);
Сопsоlе.WгitеLiле("-> (О}", sb.Tostring(»;

Чтобы про читать данные из символьного потока, используйте соответствующий


тип stringReader, который (В соответствии с ожиданиями) функционирует так же,
как и родственный ему ЮIасс StreamReader. Фактически ЮIасс StringReader про­
сто переопределяет наследуемые члены, чтобы обеспечить чтение из блока сим­
вольных данных. а не из файла.

static vold Main(string[] args)


(
ConsoJe.WriteLine("*** Забавы с StringWriter!StringReader "'**\п") i

/ / Создание StringWriter и _од сиIIIволыwx ДёUUD.IX В ПaNRТЬ.


StringWriter strWriter = new StгiлgWгitег();

// Чтение дaнных изStringWri ter .


StriлgRеаdег strReader = new StringReader{writer.ToString{);
string ibput = nuJl;
while «input = stгRеаdег.RеаdLiле(» != лиJl)
(
Console.WriteLine (input);

strReader.Close() ;

ИСХОДНЫЙ КОД. Проект StringWriterReaderApp размещен в подкаталоге, соответствующем гла­


ве 16.

Работа с BinaryWriter и BinaryReader


и последним из рассмотренных здесь средств чтения/записи будут ВiлагуRеаdег
и ВiлагуWгitег. которые получаются непосредствешiO из System.Object. Эти
типы позволяют читать и записывать дисltретные типы данных в соответству­
ющий потон: в I<Омпактном двоичном формате. l{ласс BinaryWriter определяет
чрезвычайно перегруженный метод Write (). позволяющий поместить тип данных
в соответствующий поток Вдобавок к Write (), класс BinaryWriter предлагает до­
полнительные члены, позволяющие получить или установить тип, производный от
Stream, и обеспечить поддержку прямого доступа. к данным (табл. 16.9).
Глав.а 1'6. nРОGф8liсrво имеЯ Sуэfеrn.IО 671
Таблица 16.9. ОСНОВН,ые члеl:tы В'i.даrуWritеr

Член Описание

Ba;s€!S,tream. Овойство , Д'ООтyr1иое ТQЛЬКО.дl'lSl \fТеНИЯ . О(jе()пе"l~вает доступ к потоку,


используемому. с оБJ>SinРМ Bir1E.rywrit.er
Сlфэе () Метод, заВе'ршающий двоичны'й пcrщк
I'l!J.sb С) Метод, аыполняющий очистку ДВОИ'IНОГО потока
Ве.е!;: () Метод. устанавливiOOЩИЙ УIGlЗЭТель позиции в 'Текущем потоке
Wri te (.J Метод. записывающий значение в текущий поток

Кл:асс Bll1.tJ.rYReader дополняет фунrщиоцалъные 1I0ЗМОЖНОСТИ, преддатае:мые


1UIehiD.-Ш Bina,ryW,riter (табл. lб.]()) .

Таблица 16,10. Основные чnеl::lЫ ВiпаryRеаdеr

член Опмоаl'lие

Ваsеstrе;аП! Свойство , ДOeтytJHoe ТОЛЬ.КQ.ДЛR чтения, ООеСП€'!j.1Вает ДО(;:гу.п к потоку,


испольэуем-ому с обьектом :ВiпarуRед6е .t
Сlоsэ О Метод, завершвющий дIIОИЧНЬ/И' поток чтения
:PeekCr'<ir () Метод,. воввраща ~QЩИЙ следующl-'\И ДОСТУf1НЫЙ' СИМВ(jJ1 бесе факrW1еСkОГО
смещеНИll указателя позиции в потоке

F&.adO MeT~A , qчитыающийй заданное МffOжёr:тво байтов ил~ СИМВ,ОЛОВ И заломи'­


нающий их во 9ХОJjI,НОМ маССИВfJ
ReadXX;b{ (,,) Класс Bi.n,aryHe- аdеr Оf1р'еД~lIяет множество меп,JДЩ! Re,HiXXXX (), "за­
хватывающих" СJ1еАyIOЩИЙ 'Тип из ~О:1'1жа [Read!>'oolean ( ),
R€a:dByt е () ,
R.8аdlrJtЗ2 () и Т.д.)

в tпедующем лрим~ре в ,НЩlыИ фai\л *. qat записы:ваеТС:fI целый ряд ТИПОВ


данных,

s·t а ti.c va.id: ма in (str l.ng 11 ,йcgs)


I
11 О~~ие с~аиса ДВОИЧRОЙ З&mlСИ вф&Йn.
Fil·elnfo f = new FileIn,[o ("BinFile. dat "') ;
Blnary-writexbw = Щ~'vi B-iп..зrуWritе r (Е • Ope!lWr:tte () ') ;
1/ IJеча?!Ъ 1Щфорющии о 'Ж'ИПе ВазеStzеаа.
1/ (в ~НнCIМ CJt)'Чае э'Ж'о System... IO. Еilеэиеато.) .
СQД$ оlЕ! .WritеLinе("Базо:s:ЫЙ ПdТОЖ: ~O)1<, Цw.. Ваsеst:rеаm)I

11 СоЗДiiJ.В,Иe пqрЦYI:И дая!lVJ[ ДmI сохраяеllЮl -а фaйnе.


doи'Ые aDo1.lble = 1234 .67 ;
int anlnt. = :Н5б7;
с ha,r I ] actl8.rA.rra'1 = ( 1 А " ~B', , С' );

/I Звшroь даяиых.
Ь"" .Wri t-e (aDoubl е ) ;
bw.Write (аnfлt-j i
bw. Write '(а Cha.rArr а1 ) ;
bw .'Clo$e () ;
672 Часть IV. Программирование с помощью бибЛl-\отек .NEТ
r
I
Обратите внимание на то. что объект FileStream. возвращенный из Filelnfo. t
OpenWrite (), передается конструктору типа BinaryWriter. С помощью такого под- f
хода очень просто выполнить "расслоение" потон:а перед записью данных. Следует
осознавать, что конструктор BinaryWriter способен принять любой тип. произ­
водный от Stream (например. FileStream. MemoryStream или BufferedStream).
Поэтому. если нужно записать двоичные данные, например. в память, просто ука­
Жите подходящий объект MemoryStream.
для чтения данных из файла BinFile.dat тип BinaryReader предлагает мно­
жество опций. Ниже мы используем PeekChar (), чтобы выяснить, имеет ли ПОТОI<
еще данные, и в том случае, когда он их имеет, использовать ReadByte () для по­
лучения значения. Обратите внимание на то, что байты форматируются в шест­
надцатеричном виде и между ними вставляются семь пробелов.

static void Main(string[] args)


{
/ / ОlJ1хрытие сеанса дзоичной записи в фаЙJI.
Filelnfo f = new FilelnfO("BinFile.dat");

1/ ЧlJ1еиие данных
s виде 11 сырых" бaЙ'l'оз.
BinaryReader br = леw BinaryReader(f.OpenRead());
int tешр = О;
while (br.PeekChar() != -1)
{
Console.Write("{0,7:x)" br.ReadВyte());
i f (+нетр == 4)

/I Запись хаждых
4 бaЙ'l'О:В :в :виде нозой с'l!pо!tи.
Console.WriteLine() ;
temp = О;

Console.WriteLine() ;

Вывод этой программы показан на рис. 16,9.

Рис. 16.9. Чтение байтов из двоичного файла

Исходный КОД. Проект 8iпагyWгitегRеаdег размещен в подкаталоге, соответствующем главе 16.


Г1ffiВВ 1.6. np,OCJpaHCTBo ИМ:е'F{ System .1O 673

Программный мони'торинг файлов


Теперь. когда вы уже знаете ВОВМОЖНQСТИ рЦ.;.VIИ'll-1ЫX средств чтения и ;щrIИСИ.
давзи,те. рассмотрим роль J(11acca Filesyst.emVla.tCher. Эnп ТШI може'Г быть и~ю­
ЧИТe.I1ЬПО JJолеэен тоща, Когда требуется npограмм:в:ы.й мониторинг файлов. и:мr.IQ­
ЩИХСn в данной Си.стеме. В qаС·ЦiQСЩ. с помощью ТИJ1а i'ileSYstemWa1:.chcer МQЖНО
.кQНТРОЛИРОВать любые из деItст8МЙ. JRааCЩI;iЩ в перечне, tiotifyP'ilter$ !значе.шt
его членив очевидны. но в случае необжодимоеу'и боле€. ТО'tfНyЮ инфор:мапию мож­
ПО получить с помощью GПРавоЧlIOЙ систем;ы).

FtIl:',lit: еll\Щ1 зу~t, еm.IО.NОLlfуFi.ltетS


[
.А t t l' Ш1J tes
, с ;rea t i Оl'!Т im!;;"
DirectorYName., tileNarne ,
Ld 's, tAe;cess, LastW:rit.e.
5t:ec.:uri t y, Si.28,

Первым делом длнработы с 'fИDDМ l"i le'Sys-tеw·Wаtr.hеr нужно установить СООЙ­


CT1JO PQ:th, С JЮМDIЦЫО -нотарото МОЖ1Ю У'!'аиат-ъ ~МЯ ('и МЕСТО РАЗМещения) катало­
га, сод~ржщцеf'О контролируемые фай..1JЫ, 11 СПОЙСТ.БQ Fllter, е IIOмощыо ко:roроro
ш:rр.еделяютсн раClIIИреЮ151 мнтролируемъц:: фаЙ,Д-0I!.
После этorо МDжноуказать обрабоТI<)' еоБI;l.'ГИЙ dlaoged. Сrезtеd и Dele.1:e d. ко­
щрые работают Е СОВоь.1'I1НОСТИ С дeдeгaТQM f i l c6'g'y;; t emЕё" еп t ~, a tl d.1E::r. Э"О'1' делt"­
тат может вызъmать , люБОЙ ::м:eТQД. ('оотвеТ<."l".В)'J()IЦ.Ш·1. след.vющему шабл.оЦ.v,

11 Делега~ FileSy-st.emEvе;ntНand.1~ должен YXa!l_a~lo ка метоцы,


1J Юlеml;Нe сще,цyщyz' сигиат:уру .
'vcid :МуN'аt.i fJ. c:.at l Q11Handler .lob-j'ect s01;Tce, FileSystemEventA;tg.s ~)

Точно TaR: же событи~ Rе:пащеd МQШНО обра:бО1:ать (. помощью Tl'llIa деде-Т'ата


ReТJamea ЕУеп t На о dl е r. сцособноп'l' ВЫЭЬПЩТЬ методы. соотвеТ(':твующие следую-
щему шабдону. . .
11 Делег&'l' RenamedEventHandlez АО.пжеВ Уltазыз-а'IЪ на. MeTOAЬi,
I1 .имt!lоцие сn.е;ЦyжiЦyJO си:rнаawpу.
v,oi<! M)1Not i и са и О)) Нал dl е 1: (object. SQuroe, RепащеdE'IIentArqs Е )

Дм и;лл.юстраци:и rrрощеоса МОRи:roриR.rа файлов преДПОЛ{))tШМ , "по:мьr созда­


ли 1ia диске С НQцьЩ l'iаталот с именем MyFojlder. еодержащий раэзшчвые файлы
r. tx t (назовите их так. кЭЕ IIOЖ("дае-те ). Следу1Ощее к()в~ол;ьное приложение -осу­
II1.есТЩIяет MOНJi.fТOPЦHl' фafu"Iое ..... txt 1'1 каталоге MyFo~der и ВЫВОДИТ сообщения
о cdб'ЫТfIЯХ. соответствJЮ1ЦИX создam41O. УД8Лению, .JofзменeJ!ИJO' или пер.е.йменава­
ШlЮ файлов.

statlc v'0id Maih [s пing!] iH-(j!;<.)


I
соns.С'. lе ,wr itеLlпе ( " ... 7 ..... ~ Чудеср-щИ MOH,f'Гi;, P '~ ·a'ffi:r~E "'*-II"'*' \п"l;
11 ~CТaHOВKa. дy>rи ~ля: ха'1'цо;rзна!5mo;цения: .
Fi l-€SУ$t'еТflWаt сЬе у wa t 'C;}li= ]'C '"' n-ew 1."1 l e '$ y:st e!'tIWa.t chE'J:' () ;
t.r y1
watc:.h(' r. 'Р';! th = lЗ 'I t : \ МУР'о} cler" 1
674 Часть IV. Программирование с помощью библиотек .NEТ

catch(ArgumentException ех) {
Console.WriteLine(ex.Message);
return;

11 Yc~aвOBKa Фиnътро. Ha~e~.


watcher.Noti.fyFilter = NotifyFilters.LastAccess
I NotifyFilters.LastWrite
I NotifyFilters.FileName
I NotifyFilters.DirectoryName:
11 Ваб.JШДение ~ОJUoКО s& текстовloOIИ файлами.
watcher.Filter = "* . txt";
11 Добаапение обработчиков событий .
watcher. Changed -1-= new FilеsузtеmEvепtНапdlеr (OnChanged) ;
watcher.Crea t ed += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged};
watcher. Renamed += new R"e namedEventHandler (OnRenamed) ;
11 Начanо на~еНИR sa ха~anогои.
watcher.EnableRaisingEvents = true;
1/ o.trдание CИI'Нanа DOnЪSOBa'1'eJl:R ДnJI ВilXoдa из прогрaимw.
Сопsоlе.writеLiпе(@"Нажмите 'q' ДЛI'! выхода из лриложениl'!.");
while (Console . Read () !='q');

СлеАУЮщие два обработчика событий просто BЫBOДnT информацию О моДИфи·


кации текущего файла.

static void OnChanged(object source, FileSystemEventArgs е)


{
11 Уведом.nение об ИSII&неиии, ооздании и.nи у,цanеиии файла.
Console. wri teLine ("Файл {О) {1}!", е. FullPath, е. ChangeType) ;

static void OnRenamed(object source, RenamedEventArgs е)

11 Уве,цoиnение о переииеновании фaйnа.


Console. WriteLine ("Файл {О) лереименован в\ n (11 " ,
e.OldFullPath, e.Ful1Path);

Чтобы проверить работу этой программы, запустите приложение и ОТI<ройте


Проводник Windows. Попытайтесь переименовать, создать, удалить файлы *. txt
в MyFolder или выlIJшитьь с ними какие-то другие действия. Вы увидите, что кон­
сольное npиложение реагирует на эти действия выводом различной информации
о состоянии текстовых файлов (рис. 16.10).

Исходный КОД. Проект MyDirectoryWatcher размещен в подкаталоге, соответствующем главе 16..

I
j

l
Рис. 16.10. Н~БЛЮАение за текстовыми ф8Мам~J

Асинхро.нныЙфаЙловыЙ ВВОД"'ВЫВОД
Взаверщесще вщnего об;:Jора цр.остранства имен SystE:IТ1.IO д~айте ВЫ:nСЩЩ\d,
:КаЕ осут.цеств.цяетqr асивхроннuе вэа:имодействие с ТИШIМЙ File5treq:m. ОДИН из
ва:риантов поддер:щки аЦllIХронного взаимодействия в .NEТ вы уже видели np:и
I
ра(;смотр~щ~н l\>IНОГОD.О'ГОЧНЫХ прюIOЖенйЙ СМ. м,аву 14). Ввиду Toro, что BBOД-B~­
ВОД может ;mнш.ЩТh lIrщотовремени. все nmы. производнъtе 01' SJ5'tern.I(j_Stre~m.
наследуют мво:жество методов, раэрemЯ10ЩИХ асинхронную обработку ДCЩfЦ,PC.!\щt
и следует ожида'IP. дЩ методы работают.в С'ВНЭ1~е с пшом IАsу:r~С"Rеs11lt-,

pur.J.ic abs,t.ract clasis $Y5't.6ID . 10', S.t:ream


f'ii1arsh.alByRefC)i::i j'ect,
1 Di.sРОSiфlе

ptJblic li'irtual I AsyncRe<s:(l lt BeginRead~b,yte IJ bu:ffer ., i'ot offseL,


i.!1t. COUr\t, ASyncCq11back са llback, Ghj ect s,t.a te J ;
p1).blic Vi.'гщаl lАs.уш:Re:S1Jlt be<jinWrite(by'te[] bU'ffe!", int offset,
int count, AS\fТJcC a llba:ck callback, obj 'e ct !7tat~);
p,u.blic virtual iпt LndRead (IAsYnr;JResu.lt async:Re:s,u lt);
риЬН с virt.uC\lvoi·j En'dWrit.e tlAsуш:RеsUlt аэуnсResult);

Работа с аСИЯХРОННbJМИ возможностями типов, гrpо'J'!3водных от 8узtеm,


IO.Stre·a.m, аналorи:чна работе с а~иuхронШi!МИ делегатами f.J асинхронными уд.а­
ле~ вызовами метОДОВ. Мал0uеРОRТНО, '-lТО аеи:нкронJ:ЩЙ подход может су­
щественно у.лyчп:rИТЬ дocтyn н файл'nм.. но естъбольmая верЩl'fНОСТЪ тш'о. 'что от
асинхронном обработки получат ВМГОду друтие потоки (например. пcrюлъэуюt.цИе
со:кеты). Так:или Иfшче. слецующий nример ИJЩJQстрирует 11QДХОД, в рамках кото­
РОГО lIli.I МDжете асинхронно взаимодейcrвовать €ТЩ10М Fi leS'tream.
claS$ Froqram
{
st-atic void !:1ain{st.ring~J args)

Co.nsole ,Wr i teLine ("CT:ap'I' оер'еиЧНQГО ntITo~A. Thread'I D = .1 О} " ,


'!'hrea.d. Cu:r-rе,'l"t.Тt)rеаd. GetEasnCode (}) ;
676 Часть IV. Программирование с ПОМОЩЬЮ бибпиотек .NET

11 Следует испоnъзо.ать зтот конструктор, чтобu получить


11 FileStream с асинхроНIDIИ доступом дли чтеНИJl и записи.
FileStrearn fs = new FileStrearn("logfile.txt", FileMode.Appe nd ,
FileAccess.Write, FileShare.None, 4096, true);
string rnsg = "это проверка" ;
byte[] buffer = Encoding.ASCII.GetBytes(rnsg);

11 Начa.nо асинхронной записи.


11 По окончании вuзываеТСJl Wri teDone .
11 Объект FileStream передаетек методу обратноrо .uзова,
11 как информации СОСТОRИИJI.
fs.Beg inWrite(bu ffer, О, ЬufЕеr.Lелgth,
леw AsyhcCallback(WriteDone), fs);

priva te static void WriteDone(IAsyncResu lt ar)


(
Солsоlе.WritеLiпе("МеТQД AsyncCallback для ThreadID {о }" ,
Thread.CurrentThread.GetHashCode(}) ;

Stream s = (Stre arn)ar.AsyncS tate;


s. EndWr Не (ас) ;
s .Close(} ;

ЕД:Инственным заслуживающим внимания моментом (при условии, что вы пам­


ните основные особенности использования делегатов! ) в атом примере является та,
что ДЛЯ разрешения асинхронноm поведения типа Fi l eSt сеат вы должны испаль­
завать специальный KaHCTpyRTOP (RОТОРЫЙ здесь и используется). Последний пара­
метр Systern.Booleafl (если он равен true) информирует объект FileStream о там,
что соответствующие операции должны вьшолнят:ься во. вторичном потоке.

Исходный КОД . Праект AsyncFUeStream размещен в подкатапоге, саответствующем главе 16.

Резюме
Эта глава начинается с рассмотрения типов Direct o r y (I nfo ) и Fi l e (Info)
(а также нескольких но.вых. членов типа File, появившихея в , NEТ 2.0). Вы узнали
о том, что эти классы пазволяют работать с физичеСliИМИ файлами иди каталога­
ми на жестком диске. Затем был рассмотрен ряд типов (в частности, FileS t сеат),
полученных из абстрактного класса Strearn. ПОСRОЛЬКУ типы. праизводные от
Stream, работают с патоком "сырых" байтов, пространство имен System.l0 предла­
гает множество типов ввада-вывода (StreamWriter, St ri[1gWriter, Bin.a ryWr iter
и т.п.), упрощающих процесс.
В працессе обсуждения был также рассмотрен новый тип .NEТ 2.0 DriveType,
вы узнали о том, как контролиравать файлы с помощью типа FileSysternWatc her
и как взаимодействовать с потоками в асинхронном режиме .

I
l
ГЛАВА 17'
Сериализация
объектов

И э rJl2ЦJbl 1Б вы УВliaJШ О функциональных ВОЗМОЖFIОСТ,Щ, :ш:редоставлею-IЬ!X


простраТiСТВОМ имен Syst ет. ТО, Было дщщзэцо, чте ЭТО npостранcrво имеJ:{'
сРДержит мяоже(:т~о тюroв ввода-вывода . },':Оторые могут исцол:ъзо-е-атъсв: для Ч1'~­

:ния )) еоХращШин данных в соОтветс1'ВИИ с задаШllitМи парам~трвми размещения


(или ЭВДaJ:.ЦiЫМ, фор:матом)_ В ЭТОЙ шане будет -рассмотрена родствеmrnя тема сери­
CiЛrJдацн,и объею'ГlD,В- С помощью оБЪeItта серимизащ-w МоЖНО сохраинт.ь и восст8.­
шrnлt<lВаТl>· состшцще абъевта в .тобом прOIofi~водном 01' 9i1'5-t~ш.lQ. б1'J еаm типе .
Б:ы сразу соrnаситесь с тем, что возможность сери,ализэЩII:IJ ТllПОВ играет КЛf6че­
вую рот при IЩIIДpОвaюm объеnов На УДал.e1IНyЮ мщmmy tэтот процесс будет те­
МОЙ об<:j'ждедия следУЮщей rnaBbl)_ Однако CJ'I(ЩУ~ также леНJJМaТЪ, что сериали­
заЦИfl о:казываетс~ пОлезной и сама по себе-. и она, CRQpee всего, будет играть свою
po.ttъ во М1ЮТИХ ваших _NEТ-приложениях (ШU\ распреДt'.;IIенJ-:[blX, та]> и обычных),
Б зтой главе l\~ обсудим различные acnex:тw е,х-емы с;е.риaJ1J1:задии _NEI 1ШЛЮЧ!1Н
Мнощеетво НОВЫХ атрибутов. появюшrихся с щ.IXОДОМ .NЬ'Т 2.0 Н' ПОШlОЛВЮЩНХ вы­
ПОЛНЦТЬ ПО.i'IЪ.ЗОЩПeJLbС:КУЮ настройку соответствующего 1;Ipодееса.

ОСНОВЫ сериализации объектов


Термин серuалШQI4.UЯ QЗ1iачает процесс п€рен"саСОСl'СУНflШI объеF<.'Та n П{')Т(JК,
Соответствующая С(jхрав:~.нная посдедовательностъ данных еодерЖIП ВСЮ инфор­
'J.ШЦИЮ, необходимую Д:щl реlФНСТР~llliИИ объен,та. если в далъиeйIUем ВО3J-1щает
нееб:х:о;цнмость Б е1'О ИСПОЛh30ваfЦIИ . С 110:М9ЩЬro такой l'ШНОЛОГЙИ оч~ просто
90Хpaня:rь отромны{" объеl\o1ыI данных (~ ('..a:мь~ раЗFlliIX форматах], Во ююгих елуча­
'ПХ сохранение данных приложеш.ffi с ПОJlЩJJЩЮ сервиса сеРИШIИааu:и:н ОJr.aзывает­
ся fIЭJtdНОГО менее неун..ТПОЖИ-'/.Д, q,eM црцмое иепользование сре,дс-тв чте:Нltя/за1lliСи.,
предлarае""IЬ1X в pa.\I'КaX пространствс;1 ИМ'!:'ii $у s lеш.l0.
Цредподожим, например, Что вы еоsдади ПРКЛQЖeJ:iиес графичеСf\НМl'дперфей­
СОМ и хотите обеcnечить конечным ПОЛЬЗОВ'ателим воЗможнос'!'Ъ сохранить инфрР­
м:аци:ю об и1< ЦРl'Щllочтеcrи:ях. Дли этого вы можете определить RЮ'НJ (; (.например, с
именем OseJrPt efs). Инкапеуд:ирYЩЩUЙ. СК,ажем, 20 палей данных. Ксди иещшьзо­
вапсь тип Sy s.'te'I1I . I О . Е!iлаrуwritеr, вам придется вручную сохранять JW,Ждое ЛОJ1e'
объ.еlпа lJ·s.eTf'tE'fs. А l'lогда БЫ захо,'ТИТ!: flагрузить Aal-lньf.c НВ соответствующего
файла обрflmо' 1! память. IЩ]\/f nPИ;IJ.еТС'R ИСПОЛЪ~ ОВ!)ТЬ SYS,t" e!li. }О , ]Зi n'axyR'i?ade:r

1
678 Часть IV. Программирование с помощыо библиотек .NET

и (снова вручн.ую) прочитать каждое значение, чтобы сконфиrypировать новый


объект UserPrefs.
Это. конечно, выполнимо, но вы можете сэкономить себе немало времени.
просто указав для масса UserPrefs атрибут [Serializable]. В этом случае для
сохранения полного состояния объекта достаточно будет нескольких строк про­
граммного кода.

static void Main(string[] args)


{
/ / DpttДПОJlаl'а8ll, '1'1'0 ~. U.8rPrefs
1/ ухааано (SerialiZable] .
UserPrefs userData= new UserPrefs();
userData. WindowColor = "Yellow";
userData.FontSize = "50";
userData.IsPowerUser = false;
/1 Т.перь cOXP&JDDI о&их'1' • ф&й.nе user. dat.
BinaryFormatter binFormat = new BinaryFormatter();
Stream fStream = new FileStream("user.dat", i
[
FileMode.Create, FileAccess.Write, FileShare.None);
binFormat.Serialize(fStream, userData);
fStream.Close();
t
Console.ReadLine(); I
Сохранять объекты с помощью средств сериализации .NEТ очень просто. но
процессы. происходящие при этом в фоновом режиме. оказываются весьма слож­
ными. Например. когда объект сохраняется в потоке, все соответствующие дан­
ные (базовые массы. вложенные объекты и т..п.) автоматически сохраняются тоже.
Таким образом. при попытке ВЫnOJШИТь сериализацию производноro класса в этом
процессе будет задействована вся цепочка наследования.
как вы сможете убедиться позже, множество взаимосвязанных объектов можно
представить в виде объектного графа. Сервис сериализации .NEТ позволяет сохра­
нить и объектный граф, причем в самых разных форматах. В предыдущем примере
программного кода ИСПOJIЬзовался тип BinaryFormatter, поэтому состояние объек­
та UserPrefs сохранялось в компактном двоичном формате. Если использовать
другие типы, то объектный граф можно сохранить в формате SOAP (Simple Object
Access Protocol - простой протокол доступа к объектам) или в формате XМL. Эти
форматы могут быть полезны тогда, когда необходимо гарантировать, что вanm со­
храненные объекты легко -перенесут Мnyтешествие" через операционные системы,
языки и архитектуры .

Наконец. следует понимать. что объектный граф можно сохранить в любом про­
извоДНом от System. IO . Stream типе. В предыдущем примере объект UserPrefs со­
хранялся в локальном файле с помощью типа FileStream. Но если бы требовалось
сохранить объект в памяти, следовало бы использовать тип MemoryStream. Это не­
обходимо для того, чтобы последовательность данных корректно представляла со­
стояния объектов соответствующего графа.
Глава 17, СериализаФ1R объеJCТОВ 619

Роль объектных графов


КaIi уже ynОЩIНМООЬ, цри сериализации объеl'l."Та среда CLR учитывае'Г с.осто­
ЯШ1Я всех сIlязащIых объектов. :МНоЖество связанных объентов предС'l'aRI-rяе'I'СЯ
объеютlНbtllt графом. Объе~т~ ]'рафы обеспечи:ва:ют простОЙ способ учета Взаи..у­
iIЬVI связей в Мfiожестве объектов, и не обязательно. чтобы ЭТ}i СВЯЗИ в ТОЧ:НОСТИ
npоецйроnалис:ъ в RJlЭссичеС1{пе связи об'ьектrIO-ориеНТJiiРОВ~ОТО npограммиро­
ванин (такие 1{э'к QТJ-Iошения- Gтаршmreтва 11 подчинешroсти). ~()ТЯО1:m моде.ПИРУ1ОТ
ту парaдиrму до~точно J,l:ороnЮ.

Каждому оБЪе1cry n объе;нтно:м графе назначается уника.щ,ное числовое ЗRaч:е'


ЮIе. СледУет иметь ц в~щу, ЧТО эти ЧИСЛD1JЪ1€ знач:енин. припиcъmaемые членам:n
объектном графе. nPОmlВОЛЬнbl и.не имеют lIИЩUФГО СМЫСЛа вн.'е графа. После на­
эна'1еНйя всем об-ъеRтам ~ис.тювОго знач:ения объewr.Н.ыЙ граф может :началъ запись
мнОЖества зависимостей каждого объекта.
д1m пр:имера предположим. чТО вы соЗДали множе~тво класciJв, моделирующих
типы автомоБИJIей (а ЧТО же еще?). Бы имеете базовый .J(,.lI8.CC, назвmmъlЙ Car {авто­
мобиль] . КОТОJЩЙ "имеет' Radio (радио]. Другой КI1acc. JаmеsВожlСаr [аВТОМ:dбиль
ДЭkеЙ"Мса Бонд.~), расширяет базокый 'Тип Car. На рие. 17. J показав ВОЭМОЖflI:JЙ
uбъеmнblЙ граф. мод~ующий уназанные взаимосвл~и.

Radie

Рис.17.1. ПРРСТОЙ О.бъектныЙ ,раф

При чтении объе.ктных- графОв ДJUI соедИ1ШЮЩИК стрело:к ВЫ можете исnoльз:о~


вать выращения. ~зависит ОТ" и ·'ссылае'1'О.я на". ПОЭТОJ.tY на рис. 17 .1 вы можете
lIИдеть . ..:(ТО .масс Сау ссылается на класс Radi о (В сщ1У отношенйя локализацш-t: .
~Мэ-а"). а класс Ja:mesBoJ'"LdCar ссылается па Car (В сИJ,Iy отношения подчинещю·
СТИ. '1а - а') и на radia (в силу 1'0.0. ч'to СОО'rветствуюlЩIЙ защищенный ч:леI:l~nере­
менпая ДЩIДЬЩ ю:щс.сом наследуется).
Конечно. для пр~дставлеНlliI графа связаННЫХ объе:ктов .среда CLR каРТЩ!Ь1 в
па.Ч:IПИ не рисует. BMeGTO .этого В3аим0СВЯЗИ. YRЭЭaнI-Iые в диaIфамме. предcraвлв:­
ю't'сл матема'Тичес~ой формулой . .которая ВЫТJЩДИТ -примерно так..

lCar 3" r e t 2], [Radi o 2], [JarnesBo11dCax 1, ref- З, ref 2]

ПРОВJШ.lDпирооав эту формулу. Щ>I с;нова увидите. --.:по объе'КТ 3 .сСеН) имеет за­
в;mлIМОСТЬ в О'FНотеюm объе;rста _2 (R.a'd ic). Объект 2 (На dio) в:влнется ·индивид,v­
aI1ИС'ТfJМ". которому ншtrо не требуетсs;i. Нан:ошщ. оБЪе!(Т 1 [,Jame5B~:mdOar) имеет
завИСИМОСТh в OТ}IошеНItи КaJI: (!)QЪC;Rl1il З,так и объе:кта 2 . В любом случае. ког­
да выоJшяетсfl сеРИЗ)1изаuця или реI<ОНОТРУКЦИJI Э'l<эеМПJшра Jа.шеsВоn-dсат.
680 Часть IV, Программирование с помощью библиотек .NET

объектный граф дает гарантию того. что ТШIЫ Radio и ear тоже будут участвовать
в процессе.

Приятной особенностью процесса сериализации является то. что граф. изобра­


Ж.aJоlЦИЙ взаимосвязи ваших объектов. создается в фоновом режиме и автомати­
чески. Позже. в этой же главе. вы убедитесь. что при желании вы все же можете
участвовать в построении такого объектного графа.

Конфигурирование объектов ДЛЯ сериализации


Чтобы сделать объект доступным сервису сериализации .NET, достаточно по­
метить каждый связанный класс атрибутом [Seriali zable). И это все (правда!).
Если вы решите, что некоторые члены данного класса не должны (или, возможно,
не MOryт) участвовать в процессе сериализации. обозначьте соответствующие поля
атрибутом [NonSerialized]. Это может быть полезно тогда, когда в классе, пред­
назначенном для сериализации, есть члены-переменные. которые запоминать не

нужно (например, фиксированные или случайные значения, динамические дан­


ные и т.п.). и вы хотите уменьшить размеры сохраняемого графа.
Для начала вот вам класс Radio. обозначенный атрибутом [Seria l izable],
за исключением одной переменной (radioID), которая помечена атрибутом
[NonSerializedJ. и поэтому не будет сохраняться в указанном потоке данных.

[Serializable] public clas s Radio


(
pub l ic Ьо01 hasTwe eters ;
public Ьоо1 hasSubWoofe rs;
p ublic douo1e[] stationPresets;
[NonSe r ia li zed]
publ ic strinq radioID = "XF-552RR6";

Класс JamesBondCar и базовый класс Сау. также обозначенные атрибутом


[Serializable]. определяют следующие поля данных.
[Seria1izable] public class еау
{
public Radio theRadio = new Radio();
public Ьо01 isHatchBack;

[Serializable]
public class JamesBondCar еау
(
public Ьо о1 canF1y ;
public bool canS ubmerge;

Следует знать о том. что атрибут [Serlal izable] не наследуется. Таким обра­
зом, если вы получаете класс из типа. обозначенного атрибутом [Seriali zable ] ,
дочерний класс тоже следует обозначить атрибутом [Serializable). иначе он l
при сериализации сохраняться не будет. На самом деле все объекты в объект- t
ном графе должны обозначаться атрибутом [Serializable]. При попытке с ПО- j
j

L
МО)]ц'Ю в,iпаr' уF'оt"щ:зttеr или Saa.pFor,rnatter выполнит.!,. сериализацию объеь."Та.
ке !1QДJreжащегосери'WП'tЗа.Ции, в среде ВЪ1полне»ия l'енерируеТС~ nсклю~,е.ние
SеriаJ.i2аtiОо.ЕхсерЦс~n.

Открытые поля, приватные поля и открытые с,войства


3сщ..етим. -что в YН<'J-~aнн:ыx выше :EtЛaссах'nО.JI1I данных были оupедедеиы откр.ы­
тымJf ТОЛЬКО ДЛН Т'ого,. 'чтобы удростить прйМер. КОIre'tшо. с ТОЧIШ зре1QiЯ об'J;>ев.­
l'но-ориецтировщщого подхода npеДПО'чтительнее исполЬзовать дрищт:гные дан­

ные, дoc'JYIIНЪТe через открытые свойства. Также для проe>rоТ'ы не было опредедeнri'
юma.JGШ доцъэователыжих 1:W1ICТpyкropoв для этих THUOt'l. DooroMY все Щ :ПОJJ,Я да;н­

ВЩ, не ПОду'ЦlВШИС начальных значений. получат эна<fени-я. DpедусмотренНДI~ПО


~VМОJlЧанию.

"ОТОДВИi;1УБ~ принциnы 06ъеКТI-IO-ориент.ироваНI:1юrо uрОГРqммиро~аюш в


староц)'. Щ~Т можете спросить, какие именно оnреде.J:leНИИ полей' дан.нюх ож.ида~
ют видеТ1!" разлпчны,е среДСТ1iIа форматирования при отравке этих данных в
k

паток. Ответ здесь завис.ит от МНОГОГО. Есди -В:ы сохра,нцете оБЪ~RТ ~ помощью
Вin~l-уFоrmаttеЕ, то определения не имеют абсолютно НИRaRОГО зн~чеНИJ!. ЭтQТ
ти;n ПР~ЩfазШiЧен д;nя. оохране,ния всех npеДНЭЭН8'lewJbl.'С ДЛЯ' серщщиэации попей
1'ИПа, н~зав:ис:нмо 0'1' то:го. ИВЛflЮТСЯ ЛИ они общими ПОJJЯ~:И, цриватными поля­
МI11 :и;ди приnатными полями, достym-lымй черftЗсвойства типа. Одщuщ Ситуаци8
Оlщ~~ся ,соверnrенн() ИНОЙ. если Вы ИСПО.JJ:bэуете тип XmlSerializer или тип
DоарFфпn,а-t. t 'e I'. эти ,mпы :в.ьптолнmoт сери:алиэацию nш,-.,ысо 6T~PЫTЬ1X ПОлей дан­
НЩ ~ прйватныХ данных, доступных через OTJ{P;blTt>I(' свойства.
напомним, однако. что если имеlOТСЯ ПОЛЯ ДЩ-nIых., КО'горые ~Ы не хотите со­
xpaI-lЯТЬ в объектном графе. -вы может.е селективно ИCnОЛЬЗЩЩ1Ъ Д.JIЯ них атриfiyF
[NQлS.е:rlаlizеd]. кан это сделано со CТPOКOBblN( щmем типа R,a;Ho.

Bbl60p формата сериаnизации


После ROнфигураnни 'rИIIОВ ддя участцн в схеме сериализации . NEТ ОЛедУЮЩИМ
maтOM Я1i;Ще1'СЯ выбор фQрмата. КO'I'GlРЦЙ должён ИСUОЛЬ30ВatI'hСЯ при оохране'НИI1
объе1tТНого rpафа. В .NEТ 2,(} вы Щl(еете Щl выбор три варианта.
• Bin".ryF.o,r matter
• s: oapFDrmat L€1:'

• Хm l SеriаЦzеr

1)m BiГlaryFormatter .въшолн.я:е::r сtФИaJТ-ИЭЗЦИЮ объектного графа в потрк, ис­


ПQЛЪЗУ1J ком:ruщтный ДВ0ИЧНЫЙ формат. Этот тип определен в paМJtaX прострсщства
ИМ~Н sуstеm,RlJпtimе. Serialization.Formatters . Bin,ary. нвляющегосл ча{:тью
Ш$соrliЬ.dll. Тавим образом. ДЛЛ сериа.ilЙ3аЦИИ объектов с исполъаовющем ДНО­
~o:ro фор!\щта НуЖнQ только унзза"I\Ь (В С#) следуюrцyro директиву lc1Sil1g.
/1 Пoзryче1'iиВ АОС'l'yn", iC BinU'yFoD1!8 tter ЩJ JI1scorlib .. dll.
ЩliNЭ :3Y-'it ffi11,. RUntiJne. ЭеriаlizаtiСл .. F'o,rn·lat.:bers.Binary;
тип SoapP:orm-аttеr предt'1'авл.нет .rpаф в виде СОобщения БОАР. Этот ТIШ Qпре­
делЩI з· пространстве име.н S'y stem ..RUI'Itime. ~eriali z at idn .FОI Ш С! tter-s. S'oap.

1
682 Часть IV. Программирование с помощью библиотек .NET

которое содержится в отдельно.м. КОW1Qновочно.м. блоке. Поэтому, чтобы предста­


вить объектный граф в формате сообщения SOAP, вы должны добавить ссылку на
System.Runtime.Serialization.Formatters.Soap.dll и указать (в С#) следующую
директиву using.

11 Дo.nжиа бlolТЬ уха!lаиа cCloIJIlta


// на Sуstеш.Runtime.Sеriаlizаtiоn.Fоrmattеrs.Sоар.dll!
using System.Runtime.Serialization.Formatters.Soap;
Наконец, чтобы сохранить объектный граф в формате документа XМL, нужно
указать ссылку на пространство имен System.Xml.Serialization, которое также
определено в отдельном компоновочном блоке- System.Xml.dll. Поскольку все ша­
блоны проектов в Visual Studlo 2005 автоматически ссылаются на System.Xml.dll,
вам нужно просто использовать следующее пространство имен.

1/ определено в Sуstеш. xml . dll


.
using System.Xml.Serialization;

ИнтерфеЙСbl IFormatter и IRemotingFormatting


Независимо от того, какой формат вы выберете для использования, все они по­
лучаются прямо из System.Object и поэтому не могут иметь общего набора чле­
нов. наследУемого от какого-либо базового класса сериализации. Однако типы
BinaryFormatter и SoapFormatter имеют общее множество членов по причине
реализации интерфейсов IFormatter и IRemotingFormatter (тип XmlSerializer
не реализует ни одного из них).
Интерфейс System.Runtime.Serialization.IFormatter определяет базовые
методы SerializeO и Deserialize (). выполняющие основную работу по переме­
щени.ю объектных графов в потон: и из нета. Кроме этих членов, IFormatter опре­
деляет несколько свойств, которые используются реализующим типом в фоновом
t
f
режиме.
r
public interface IFormatter
{
SerializationBinder Bi nder { get; set; f
StreamingContext Context { get; set; }
ISurrogateSelector SurrogateSelector { get; set; } ,.
l'
ob ject Deserialize(System.IO.Stream serializatio nStream) ; r
!
void Serialize(System.I O.Stream serialization Stream , f
obj ect graph) ; ,
t

Интерфейс System.Runtime.Remoting .Messaging. IRemotingFormatter (ко­


торый используется в
перегруженные члены
.NET на уровне уда.пенного взаимодействия) предлагает
Serialize () и Deseriali ze (), более подходящие для ис­
I
I
пользования в распределенных операциях. Заметьте, что
лучается из более общего интерфейса
IRemotingForroatter по­
I
Iformatter.
I
publi c interface IRemotinqFormatter : IForma tter !
(
object Deserialize(Stream serializationStream, I
HeaderHandler handler);
I
1

L
Глава 17, Сер~аnизация об:ъектов683

void Serialize (Stream serializationStr eam, object gr,ap"h,


Header- [] hеа.dеr-з) I

ХОТЯ в болЬШИН'стве операций сериализацни I\aм. В()ЭМОЖНО. и не придется вза­


имодействовать с этими ИRтwфе'йсами непосредстnешtо. не заБЫвайте о том. что ·
-интерфейсный полиморфизм позволяет ИСПQЛЬЭОIЩТЬ экземпляр ЕiпaryFОТПL.аttеr
ИЛИ So apFormatter ПО ccыmte 'нa IFq,rmatter. Тщщм образом. чтобы ПОСТрОить
метод. ВblП<шнШОl.IЩЙ серJ{3.;I!ИЗЭЦИЮ ()бъектноro граф~ с r,юМQЩЫO moбoro яз этих
классов, NЫ можете иcnо.'lbSOватьc.nедyIOЩИЙ цpoгp~ код.

static v o id Se'rializ~ObjectGrapj} (IFormattar i .t fFormat,


Stream dеs,tЯtrе am , ob jec t graphl

i tfFодnаt .$.eriaJ.ize (de s tStream, gтapb);

Выбор формата и точность типов


ОчевидНО. сутью разЛИЧИЙ yшtзaННPtX трек форм;атов являетс~ TQ. пак имеmrо
оБЪеКтный граф перевоДИ1'СН в ПОТОК (В ДВОИЧНОМ форма~. формате ЮАР или ·чи·
c.ТOM~ XМL). Но СЛ~дУет зна'Х'ь и о несI«()JlЪКЩ. более ЦутончеЮIblX~ различиях. оео­
беПiro в отношении TOI'O. llаCRОЛЬ~О раЗJIИЧЦЫ~ форматы г~a:нT~T '/ТlOЧ1tоетъ
типа. При использованнн типа BiТlaryFor:matter буlIYТ' сохрa:нsnъся не тom.xo·
ПOOlЯ данных объе1tТCIВ иа объектного графа. но и аБСОJIЮТНО~ и~ }ЩЖДототиnа,
а также полное имя onредет.uoщerо тип I:WМlJОнОDО"ШЩО ~щща. Эти ДОПОЛНИТелЬ­
ные ·эпемеитыданных ~елают В i паrУfоrш:'аttеr идеащ.врrП вырорОМ. Karдa. вы
хотите передать объеRТhI по значению (Rщrpим~р, JtaI( поmryю щmИю) >За граJiИТi;J>r
мaUJШrы (см. главу 18). КaJt уже отмечanосъ. чтобы достичь тЭRОro уровни точно­
сщ Eina.ryForma.t ter учитывает все пOJVtданных типа (как открытые, такй при ·
ватные).
Но Soa.pFo r:matter и XmlSeriaJ.i z;e:r;. с црyrо:й СТОРЩIЫ, це пытаются сохра­
НИТЬ тип аБCOJUd'ГВ.О точно, поэтому ОНИ не аапясывают абсолютные ИМеНа типов
и КОМПОНОВОЧНЫХ блоков. а сохраняют только ВТКРШТъ.Iе ЦЩIЯ ДЗННЬ'IX И оТКрЫтые
свойства. На первый взгляд. это lCВЖe'I'CЯ orранцченисм. ~O' реальная причина это­
го сtфываетtJ1 в ОТКРЫТОЙ природе npедетавденЩI Д3.fЩЫХ XМL. Если вы хотите
wхранить объеКТНlitе графыт.сщ. чтоб:QI ОIП{ MOI'JIU исшщьзоnaтъся в .пюбоЙ опе­
раЦиОННОЙ оистеме (Windows ХР. ОС Мае Х. Рa,эJIИ<lНЬ1е вариации oiniX). в рамКах
любого каJШaса npиложeJ.пЩ (.NEТ. J$E. СОМ и т..д.) f:J щооом языке.программи­
рования. . .нет Rеобхс)Димоети ПQДДС:ржизать аосOJIlO1'ЯyЮ Т9~OCть. ПОС1Юлыty у вас
вет Г8paнrpии.. 'что вее :воэможные получате.ци cмoryт Щ)liЯТЬ типы Д3нAъJX. специ­
фичные для . NEт. в ЭТОМ случае идеальНЪJМ в~бором RВ1IЯЮТ'CЯ Sot.1p.Fo;rmatter и
XmlEerial i zet.. гаранТИРУЮЩIre }Ia}Jболее ШИРIЖYЮ доступность сохраненного
объектного графа.

I
--L
684 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NEТ

Сериализация объектов
с помощью BinaryFormatter
Чтобы по казать, как сохранить экземпляр James80ndCar в физическом файле,
давайте используем тип BinaryFormatter. Подчеркнем снова, что двумя ключевы­
ми методами типа BinaryFormatter являются Seria1ize () и Deserialize () .
• Serialize (). Сохраняет объектный граф в указанном потоке в виде последо­
вательности байтов.

• Dе s е r i а1i zе () . п реобразует сохраненную последовательность байтов в


объектный граф.

Предположим, что мы создали экземпляр JamesBondCar. изменили в нем не­


которые дю·шые и хотим сохранить этот "шпиономобиль" в файле *. da t. Первой
нашей задачей является создан:ие самого файла ... da t. Это можно сделать с по­
мощью создания Эliземпляра типа System.IO.FileStream (см. главу 16). Создайте
ЭRземпляр BinaryFormatter и передайте ему FileStream и объектный граф для
сохранения.

using System.Runtime.Serialization.Formatters.Binary!
using System.IO;

static void Main(string[J args}


{
Console.WriteLine("*** Забавы с сериализоцией объектов ***\п");

// Создание
JamesBondCar и установка данных состояния.
JaroеsБопdСаrjbc = new JаmеsВопdСаr () ;
jbc.canF1y = true;
jbc.canSubmerge = Еа1зе;
jЬс.thеRаdiо.stаtiопРrеsеtз = new double[] (89.3, 105.1, 97.11;
jbc.theRadio.basTweeters = true;

// Сохранение объекта в файл CarData.dat в Двоичном формате.


BinaryFormatter binFormat = new БiпаrуFоrmаttеr();
St уеат fStream = new FileStream ("CarData. dat",
Fi1eMode.Create, Fi1eAccess.Write, FileShare.None);
binFormat.Serialize(iStream, jbc);
fstream. C10se () ;
Console.ReadLine() ;

Как видите, метод БiпагуFогmаttег.Sеriа1izе () отвечает за компоновку


объектного графа и передачу соответствующей последовательности байтов неко­
торому типу. производному от Stream. В данном случае таким потоком является
физический файл. Однако можно вьшолнять сериализацию объектных типов в
любой производный от Stream тип. например в память, поскольку MemoryStream
тоже является ПОТОМНОМ типа Stream.

L
[лава 1"7, Сериали~аЦl"!I объектов 685

Реконструкция объектов с ПОМОЩЬЮ BinaryFotmatter'


Теперь npеДn0J10ЖИМ. QTO J;Jbl хотите п:pQЧ11татъ сохраненные данные Ja.'1'IesBondCa-r
из ДВОИЧНОГО файла назад S рбъектную uеременную. ПрограММl-10 OTtcpbtJ~
CarData .dat (с ПОМОЩЪЮ метрда OpenRead ()). вызов:итеметод Deseria1ize'O объt::tc·
та BiTJ.a-т уFш:mаttеt. Мето-д DeSeria1i ze () bos-sращает 'обr:ЦИЙ тип .system.dbjec-t.
поэтому Ba.'14. придется ВЫnOJЦiИТQ пвное лреобраэоваmщ. ках покаэa:1iо nиже.

s"tatic v~id Мaihlst:ll:ing!J args')


[

I/ Ч~еJU,f. JiamesSondCar MS ;IIIIОИЧКОI'ОфаЙJtа.


fS ,t,r-еа,m = Filе.ОрепRеаd("СаrQ-<.1t:a.d.аt"):
.JamesBOJl<iCa r cp.rF'r omI)i,g,k '"
{ JаmеsВолdСacr)Г) i.лFотma"t. Deserlali ze (fStream) 1
Сонэоlе. W:titeLine (iiмож~т Щ1 ЫЩIlИна ле'1'а'l1Ь? : ! О}",
carFrorr.Di sK. ~a I1F ly') ;
fStrearr... -Сlоsе ,) ;
СО!1.'lюlе .. R€adLine (') ;

О~ратите EjвимаIOi1е на то. что hpи-вызове D€!.serialize О мето;цу передает-­


ел проиэIюдн.ый от Щ:rе.arn ТИП. yкaзьmающий меетохранения объектнш'о графа
(в ДaJЦJOM Cдy'-~~e ЭТО файловый nOTotc). Так что проще уже н.еt~уда. Пр сути, сначала
вужно обоз;н.аЧ'ИТЬ атрибутом (Se ti al i z abl е 1 все юrассы. nреДНЭ:ЗRа ченные ДЛЯ
сохранени.яв потохе. После этогО HyтilO ИСI1ОДЪЗОвать тип BinaryFormat,t ·eor::. ЧТQ­
БQl передать ОО~КТ'НЫЙ граф В ДВOI<lЧНЫЙПОТОtc }I: извлечь его oтryдa. вы можете
УВИД~Ь двоичный ,образ. l1р,t;дстaвmnoщиii э:квемnл.яр JamesBondCar (рис. 17.2),

... х

ly ,L
h=R~~10,~s~~tcaB
е;ЬК .' • _ SiJllpJ.~
SerJ."Hu.tion _'R"
d,io., " 1. 1' " , •• , .

"' ,~ple
~;а:-l~liZ<l ~"i.onR~
dic " .. . has"!:"ee.t
e~ na$Subl;.Т""f,er
-s , st'аtici1lPreoi",tэ
, . '

'" " ', :рзэз


5VStH f ·fFZ'tf.fH i
FX~

. Р.ис.. 17.2. Сериал",зац~~ J amesBond.C'a r с ПОМ1:JЩЬЮ Binar yFo:r;matt er

1
686 Часть IV. Программирование с помощью библиотек .NET

Сериапизация объектов
с помощью SoapFormatter
Следующим вариантом является тип SоарFоrrпаttе:r:. тип SоарFо:r:шаttеr со­
храняет объектный граф в сообщении SOAP (S1mple Object Access Protocol - про­
стой протокол доступа к объектам). что делает этот вариант форматирования пре­
красным выбором при передаче объектов средствами удаленного взаимодействия
по протоколу НТIP. Если вы не знакомы со спецификациями БОАР. не воЛНУЙтесь.
В сущности. SOM определяет стандартный процесс. с помощью которого можно
вызывать методы не зависящим от платформы и ОС способом (мы рассмотрим
SOAP чуть более подробно в последней главе зтой книги при обсуждении Web-cep-
висов XМ:L).
в предположении о том, что вы установили ссылку на компоновочный блок
System.Runtime.Serialization.Fo:r:matters.Soap.dll. можно реализовать со­
хранение и восстановление
JamesBondCar в формате сообщения SOAP с помощью
замены BinaryFormatter на SoapFormatter. Рассмотрите следующий программ­
ный код, который выполняет сериализацию объекта в локальный файл с именем
CarData.soap. [
using System.Runtime.Se:r:ializati on .Formatters.Soap;

static void Main(string[J a r gs)

1/ Сохранение объехта в файл CarData.soap з формате SOAP.


SoapFormatter soapFormat = new SoapFo:r:matte:r:();
fStream = пем FileStream("Ca r Data.s oap ",
FileMode.Create, FileAccess.Write, FileShare.None);
soapFormat.Serialize(fStream, jbc);
fStream.Close();
Console.ReadLine() ;

Как и ранее. эдесь просто используются Serialize () и Dese ri alize () ДЛЯ


перемещения объектного графа в поток и восстановления его из потока. Если от­
крыть полученный файл *. soap, БЫ увидите Б нем элементы XМ:L. предстэвляющие
значения JamesBondCar и взаимосвязи между объектами графа (с помощью лексем
#refJ. Рассмотрите следующий фрагмент ХМ:L-кода, соответствующий конечному
результату (для краткости здесь опущены указания на пространства имен XМL).

<SOAP-ЕNV:Епvеl о ре xmlns:xsi=" ... ">


<SOAP-ЕNV:Воdу>
<аl: JamesBondCar id="ref-l" xmlns: al=" ... ">
<canFly>true<!canFly>
<canSubme rge>false</canSubmerge>
<theRadio hrеf="#rеf-З"/>
<isHatchB ack> false</isH a tch Ba ck>
</ al:Jame s BondCar>
<аl: Radi o id="rеf-З" xmlns: al=" ... ">
<hasTweeters>true</hasTweeters>

ь
Глава 17, СеркалкэаЦИII объеп.QВ 687
<has SubWoo f er's:> falве<,,!hаSSUЬWО0 f ers >
< s,tationP1;-msеts href=-"#re Е-4" 1>
</8.1 : Radio>
<SОАР-ЕЩ: 'Array id=·".ref'-4" 8ОдР-ЕNС ~ arrCly1:ype="xsti:double [3') ">
< item>Э9.З</itеm>
< i -lеm>l О 5.1</ item>
< i t-em·>'97 .1 </ i t .em>
":'/SOAP-ЕNС:Аrrар
</ S'OAP-_ENV: Bodj>
<!SOAP-ENV;ЕJJvеlоре>

Сер, иализация объектов с по'мощью XmlSerial1zer


Вдобавок к SOAP и ДВОИЧ1iОМУ формату. компоновочный блок sуз·tэm.Хml.dll
предЛагает третйЙ формат. обесnечиваец'ыИТЮlОМ Syst·em.Xml. Serial.J:.zation.
Хl1йSеr-iаliz: еr, который МОЖeJ' ИСПОЛЪЗРВ;:tтьс:я ДJIЯсохранения СОСТOJlЮUl данно­
.ro объекта в виде Мчистого'" XМL. в проти;вопальжностъ данным XМL. yпagовзнным
:в сообщении
SOAP. Работа с этим тщщм neмffOГO O'LПИчается: от рабаты с ТИПЗJ.Цi
SoapFormatter И ВiлаrуFоrmаttеr. Рас~ртрим следУющИй nporpaммньtЙ КОД.
uiiiI)g Syst eno. Хтl. -Бет ial izв t iCXni

statie ",oict Маlл (;:It:rl n g[} аrgэ-)


.{
...
/I Co~ o~!I!a • Фай.в СаrDаta •.1IШl • Фора"м 1РЩ.
Х1!11Беriа1j.2:е.r xmlF6:tmat = new XmlSeriali2Jer (typeC1:f (JаmeэВоndСаr),
new Туре 1] { typeof (R~dio ,), typeof (Сах) J) I
!Streal11 = new FileSt.?;-е ·am. (,"са;rDаtа. x'ml",
Fl leMGd~.Create, FileAcces:>.Write, FileSha~e.None);
xmlFQrmat.S~riali~e(f5tre~, jbc);
fS:t:, ream ; CJ оэ-е (') ;
, ..
Эдесь maвIIbЩ ОТЛИ<lИ~ mшяется 'J'O, что тип XmlSerializer требует jlRaЗЗНJm
IШформiЩЩI О типе СОQ'J1Je'1'отвующего элемента объектноrо rрафа:. O(5paT~ ВIШ­
мание 1i.a то. что первый аргумент ко:нструктора XmlSeriali z,er опредeJJЯет корне­
вой элемент XМL~ф~а. а ВТорой apryмeiIТ яВля:ется массивом ПШОВ sуstеmД'уре.
оодержащих M~Taдaнныe щщчиненных элемеfrf()В. Еелй 3aI1IJiНy"rb :в сrеFJериpQВан­
вый файл СаrПа t а . хтl, 1tы увидите слеДУЮЩИЙ XМL-кoд (здесь он 'nРJШОДИТСlf 'в
сокращецном; виде).

<:·?хтl v€'nsi:QIl="l.O·" еtiсоding="цtf-В' ''?>


<.1аmе&ВолdСаr xrnlns~xsl =" .•. ">
<tl).eRadiQ}>
<hasTw:eetexs>1:rl1e</hasTweeter's>
<hаSSЦЬWОоfе1r's>iаl 'зе< / hаS.St:JЬ·Wо,(С}fе.rS>
<stationPresets>
<d:oubl.e> е 9 . З</dочblе>
6ВВ Часть IV. Программирование С ПОМОЩЬЮ библиотек .NEТ
r
<double>105 .1 </double>
<double>97.1</double>
</stationPresets>
</theRadio>
< isHatchBa ck> false</isHat~hBack>
<canFly>true< /c anFly>
<canSubmerge>fa l se</ca n Subm e r ge>
</JamesBondCar>

Замечание. Для XmlSeria1izer требуется, чтоБЫ6се типы в объектном графе, предназнечен­


ные для сериализации , ПОРдерживали конструктор, заданный по умолчанию (так что не забуДЬ ­
те добавить его, если вь! определили пользовательские конотрукторы). Если это условие не
будет выполнено, в среде выполнения будет огенерировано исключение InvalidOperation-
Excepti on .

Контроль генерируемых XML-АаННbIх


Если У вас есть опыт использования XML-технологиЙ. вы должны хорошо знать
о ТОМ, что в документе XМL очень важно гарантировать соответствие элементов
набору правил, обеспечивающих "допустимость" данных. Следует понимать, что
·допустимость" XML - Аокумента не связана напрямую с синтаксической правиль­
ностью его ХМL-элементов (например, с требованием о том, что все открываемые
элементы должны иметь закрывающие их дескрипторы). Скорее, допустимость до­
кументов связана с правилами форматирования (например. поле Х должно бь1ТЬ
атрибутом и не вложенным элементом), которые обычно задаются ХМL-схемой или
DТD-файлом (файл определения типа дщсумента).
По умолчанию все поля данных типа [Serializable] форматируются, как эле­
менты, а не как ХМI . . -атрибуты. Для контроля того, как XmlSerializer компонует
генерируемый ХМL-документ, следует указать для типов [Se r ial i zable ] дополни­
тельные атрибуты из пространства имен System.Xml.Seria l ization. В табл. 17.1
представлены некоторые из атрибутов. влияющих на I<одирование xML-данных.
передаваемых Е поток

Таблица 17.1. Атрибуты пространства имен System. Xml . Serializa tion, связанные
с сериализацией объектов

Атрибут Описание

Xml Attribl.lteAttribu te Члеti будет сохранен в виде ХМL-атрибута

XmlElementAttribute Поле или свойство будут сохранены в виде ХМL-элемента

XmlE numAttr i b u te Имя элемента перечня

XmlRo o tAttribute Атрибут, контролирующий формат корневого 3I1eMeHTa


(пространство имен и имя элемента)

XmlTextAttr.ibute Свойство ИI1И поле должно сохранятьоя в виде XML-текста

XmlTypeAt tribute Имя и пространство имен ХМL-типа


(лава i7, СеРИ11ЛIiIЗiЩ~Я О'бъек-гов 689
Дня примера даеаЙ'ге снаЧa.J1Д ВbIНСНи--М, как ПОЛЯ данн:ыхJаmеsIюndСаr СОЛl'а­
НВЮ'ТСЯ Б :x.ML-донументе в настоящий момент.

<'.ктй vеr'эiол="l. О" еn'Сбdiг,g="'ut f-Э


"?>
<; JamesB:::on"jCact' =1115: Х1Б l="'h tt:o.: { /w-w''iJ. w3 , 0'1:9/20 01} XMLSahemb:-ill:St3t'lСе"
;щ\lns: х&d=ПЬt't.р: /) .11'<'1\'. wЗ. Q\rg /2О OlJXМlJSchema n>
, -8 •

<canFly>true</canFly>
<сапSub1'tle:tqе>fаl.е</сanSubmех-gе>
.; / ~1amesB~ndCa.r>
Ест! вы хотите указать пользовательское пространство имен XМL. соответству­
ющее 'LlamesBondCar. икодироватьзна"'lенпя салFlу псапSuЬmеrgе в виде XМL~
атрибутов. это МQЖИОСД~.Il:ать с ПОМОЩЬЮ изменения (.Jпред~~нил ,JаIilез.1i30пdС:::аJr в
С# следуюЩИМ образом.

[&erializa:ble,
xmlRQot {Namesp&Cle =
"bttp: Ilwww. intertJilCl~tr&in,i.nq. СОЩ"} J
public шав,а ,"ra."'11,esl!cH)dCar , ,Са:!:
{

[X1nlAttribute}
p'Jbl i~ bcol c<!.n.W' 1 у;
[XmlAttribu t.J
public boolci13.hS!;!brnerge;
}

Эт() др.tI)Щi1) дать врезущ;татft след,ующий ХМL~Д(j)I\'Умент (обратите внимание :на


отдрываюЩИЙ ЭJlемент <JamesBondCar».
<?хпй \7-еr,:lо.л="l.CJ" enc.a.dil1g=r u tf-8"?>
I(J~mesI\<)ndCi'.\! Х-"1]".1 0..$: ~!;! i=.I' http~ 11\~wщ. wЗ. org 120 О l/XМl.$J:;nehla -in,,,t<<ЕС(§ n

Х1111 n.s: xs(j= j'http:t Iwww. w3. o1'g./2 О Dl/XMLS'chertl5"


canFly="trua"
canSubtllerge=·,j false 11
xmlns="httpll/www.intertechtra.i.ning. СQПI">

</ Jэ..mеЭВQТldСат>·

Rон~чн6. есть МiЩ)Жест~о други~: а:;гри:БYVОБ. lcoтopble B~] можете ИG'П0ЛЬ301ЩТЬ WЩ


управления пр~щесс.о;tvr геliерцршщния XML-ДQЕ}'Меита с ЩJМОЩЬЮ XmlSerial.izer.
Чтобы ОЗН8.*омитьщ'.[ C~ всеМ}1 ОIlЦИi'U'6И. в;ыполните поиск информации о прострав­
стве имен Sy:s1:lSm.Xml,Serial i:1\a.tion J3 дщtум:ента.ции .NET Fщmеwork: 2.0 SD~,

Сохранеlние коллекций объектов


теперь вы зНаете, Пах .С!жранЙть 1'1 потоне отдельнь.rи объек'I, й давайте выне­
l-Il1."М. кан CQxparmTI> множество оБЪ€КТOII. Заметим. что метод Sei; ialize () интер­
фейса 1 F\э:rmаttе,r не Пl'Jзволяет ухазаrrъ llpаиавольн:ое число объектов (а только
один System ..Dbject). Аналогично. возвращаемым аначе.нием Deser:iali.ze () ,тоше
.!ШЛяется одйн sуst..еш .Objec1:..
690 Часть IV. Программирование с помощью библиотек .NEТ

pub.li c interface IForma.tter


{

object Deserialize(System.IO.Stream ser ializationStream};


v o id Seria li ze (S ystem.IO.Strearn ser ializationStream,
object graph) ;

Напомним. что Sу s t е m. О Ь j ес t фактически представляет весь объектный


граф. Поэтому при передаче объекта, обозначенного атрибутом (Serializable]
и содержащего другие объекты [Se r ializable], будет сохранен сразу весь на­
бор объектов. Большинство типов. находящихся в рамках пространства имен
System.Collections и System.Collections,Generic, уже обозначены атрибутом
[Serializable]. Таким образом, чтобы сохранить набор объектов. просто добавь­
те этот набор в контейнер (например. в ArrayList или List<» и вьшолните сери­
ализацию полученного объекта в подходящий Поток.
Предположим. что в класс JamesBondCar был добавлен конструктор с двумя
аргументами. чтобы можно бьшо установить некоторые начальные данные состо­
яния (обратите внимание на то. что конструктор. заданный по умолчанию, был
возвращен на место в соответствии с треБОВан.инми XmlSerializer).
[Serializable,
XrnlRoot (Namespace = .. http://www.intertechtraining.com~. ) ]
public class JamesBondCar : Car
{
public JamesBondCar(bool skyWorthy, Ьооl seaWorthy)
{
canFly = skyWorthy;
canSubmerge = seaWorthy ;
}
/ / ДnR XmlSerializer нужен ICOHcorp)'XTOP, за,цаниый по уиоnчaJiИ8)!
public JamesBondCa r() ()

При этом вы сможете сохранить любое число объектов JamesBondCar так.

static void Main(string[] args)

/ I Сохранение объекта List<> с набором JamesBondCar.


List <JamesBondCar> туСатв = new List<JamesBondCar>();
myCars.Add(new JamesBondCar(true, true»;
myCars.Add(new JamesBondCar(true, false»;
myC a rs.Add(new JarnesBo ndCa r (false, true»;
myCars.Add(new JamesBondCar(false, false));

fStream = new E'ileStream("CarCollection.xml",


FileMode.Create, FileAccess.Write, FileShare.None);
xmlFo rmat = new XmlSerializer(typeof(List<JamesBondCar»,
new Туре[] ( t y peof(JamesBondCar), typeof(Car),
typeof(Radio) ) ;

I
L
Глава 17. СвриализацияобъеКI0В 691'
х:mlFОПТtаt. Sбт .,ial ize (fStИ?alТl, !!1УСё1,ts),;
fSitrеiШ1. Cl:ose (J ;
ConsQle.ReadLins(i;

Снова oopaIЦaeм }II-шмание на то, ЧТО 110:причине ИCllолыювamm. Хшl Seriallzer


требуетс:в :уЕа3юЪ инфор.'\faЦiПo типа Д1Ш :каждorо из 06ъекТffiI, :вложенных в нор­
невой объект [жотnры:м в данном случае являетСЯ List<». При ИС!iолъзовавии
Вitla'IуFОIirJ.ilJ:1.еr.и.л:и SoapFor!lli3tter прorpаммная доги:ка буДет еще проще.

зtа,tlс void Main (string[J aI;gs)


{

Ij CO:q>aJIe$e об'ы!I'.l1а List<> (IIlYСа:rз) :в д:вomlИОИ ФОр:ма'l'S.


List<~ТаП\еsВ<щdСаr? ПlуСаJ;$ = ne.1tJ Li st<JiШ1€s::ВоndСаr> () ;

BinaryFomatte!' binForмat = nlE!W BiDaryFormattt-r (]:


tnrearn fStrceam = new F'ileS'tre:q.!11 i"Al1MYCars .dat",
Fil"eJll10de. Create I Fil~Acces.e:. W.r:'i te , FileShare . Nопе) ;
biIiFQrmat .SarialiZi<; (f~,tre<,-m7 myCars.);
fStream. Close О ;
,Co!1sQle . ReadLine () ;
]

Превосходно! К этому моменту в!'1М дрткно быть поя:ятцо, Щ1К ИСFIQJJ,ЫЮватъ сер­
ВИ:СiС~ри<щ:и:эаци:и объef(ТОВ Д/!Я ynрощ~ nPОJЦeсса сохранения И восста:цовления
данных вашего цриложенщr. Теперь ДaJЩЙТе ДЫЯСНИМ, IЩ.I:\ использова::гь ПWJЬзова­
'1'eillIс:кие настрьЙRИ продесса серИ<)"лиЩIЦИИ.

ИоходtllolЙ КОД. Проект Эimрlе5еГiоlizе размеще~j iI nQдкатщюге; СООТВ€('rСТ8УlQщем таве 17,

Настройка процесса сериаnизации


Б БОЛЪU1И1-Iстве случа.en типовая. схема сериализации. предлarаемая платфор·
мой .NEП'. будет :именно тем. Ч'FО требуется. Тогданужна ПРО~'I'оnpименитьатрибут
[seri.alizableJ и передать объектный rpаф выбрam:юму средству формати:рова­
ЮЩ. НО в He1tOTopыx случаях МоЖет по~ребова:l'Ь('j'f1'tоррen'"Тиров:ка тото, K~ обра­
батЫваетсл объектный ~раф 11 процессе сериалй:эации. :Например. в соответствии
с ВнyrpeIOiИМИ npавилами: вашей :компании все поля дaнliЬnt дрлжны СШ'ранmъся
Б формате Bepxuero регистр<1 или. возМожно. вы хотите добавить 11 поток допал­
нитедьnые Э,JIемевТbI дш-1НЬtX. KO"i'Qpble не проецируются непосредственно в IIОЛЯ

сохраняемого объекта (это могут быть шraмnы временй, уникальные ИМена ИJ!И
ЧТО-l'О nиое).
дna .fIепосредет:веН1JОГО У"!астил J3 управлении процессом сериaJ1ИЗаци:и объек­
ТОв пространство имев System.Rubtime.SeritJ.lization преД;1lэтает cneдиа.1'IЫIЫе
ТnШЦ. В табл. 1'7.2 описаны те НЗ них. о которых вам следует Знать.
692 Часть IV. Программирование с помощью библиотек .NET

Таблица 17.2. Основные типы пространства имен System.Runtime.Serialization


Тип Описание

I Serial i zable в .NET 1.1 реализация этого интерфейса была наиболее


предпочтительным методом пользовательской сериализации
объектов. В .NET 2.0 для настройки параметров процесса
сериализации предпочтительнее использовать новое множе­
ство атрибутов (они будут описаны чуть позже)

ObjectIDGenerator Тип, генерирующий идентификаторы злемеНТОВ объектного


графа

OnDeserializedAttribute Атрибут .NET 2.0, ПОЗВОЛЯЮЩий указать метод, который вы­


зывается сразу же после выполнения реконструl<ЦИИ объекта

OnDeserializingAttribute Атрибут .NET 2.0, позволяющий указать метод, который вы­


зывается в процессе выполнения реконструкции объекта

OnSerializedAttribute Атрибут .NEТ 2.0, позволяющий указать метод, который вы­


зывается сразу же после выполнения сериализации объекта

OnSerializingAttribute Атрибут .NEТ 2.0, позволяющий указать метод, который вы­


зывается в процессе сериализации

OptionalFieldAttribute Атрибут .NEТ 2.0. позволяющий указать поле типа, которое


может отсутствовать в указанном потоке

Serializationlnfo По сути, этот класс является "чемоданом свойств", содержа­


щиМ пары имен и значений, представляющих состояние объ­
екта в процессе сериализации

Более глубокий взгляд на сериализацию объектов


Перед тем RaR рассмотреть различные способы настройки параметров процесса
сериализации. БЫiЮ бы полезно выяснить, что при зто м происходит "за RYлисами".
Когда тип BinaryFormatter выполняет сериализацию объектного графа. этот тип
отвечает за передачу в указанный поток следующей информации:

• абсолютных имен объектов графа (например. MyApp.JamesBondCar):


• имени компоновочного блока. определяющего объектный граф (например,
МуАрр.ехе):

• экземпляра класса SerializationInfo. СОДержащего все данные , поддержи­


ваемые членами объектного графа.

В процесое реконструкции объекта тип BinaryFormatter использует ту же ин­


формацию. извлеченную из соответствующего потока. для построения абсолютно
точной копии объекта.

Замечание. Напомним, что SoapFormatter и XmlSerializer Не сохраняют абсолютное имя


типа и имя опредеЛЯiOщего компоновочного блока. Эти типы заботятся только о сохранении
открытых полей данных.

Общую картину можно представлятъ в виде диаграммы. показанной на рис. 17.3.

L
ГJЩваl 7, СеРVlдЛизаЦI1Я объероs 693

,Se:ti"li za~i'onI[)f о ПОП1/(


ОбъеКi j----------~ ФQRмаnер
Serialize (J

Устройства
хранения

(фtjЙr.I, памrnъ,
Навl>lЙ 5eri<lli Z4\-l\3nIТllr ,
Поток буфер, Cl!жет).
~------------1 Форматгер
объект DeserialiJ:e .()

Рис. 17.З. CxeM~ ГJроцещ;., сериаПИ;Jj!ЦL'IИ

Кроме перемещенuн необходnмых данных в потоК 11 ИЗБЛе"!:енин их из IЮТОК8..


среДСТВ9 фор.'I<I8.тироnания (формапер) ан.ализируюrr члены ЬОЪeJПН'OJО графа на
ПВЛИЧ1!е t.l"1еЩГIQЩИX элемеоН1.'"оЕ 1lliфрасгrpукrypы,

• Выя(:няетCJi, обозначен ли объеи'I атрибутом 1SeI l а 1 i "аЫе.] " ЕСли нет. то


:геиеРlIруетсн ИСЮIЮчение Бе l' iriJ.i4':З Li G'flEx.cept iort,

• Еt'ли объент оБОЗЩ,l''lен атр~БУТ9М l::',e.ri,alizable.l, то f:jыясвне'J'!:,Я. реализу­


ет ли объект ИIперфейс I5-SI: iCDl i ,"аЫе. ЕСJЩ д<;l, то Д!)Я об~кта B:JilijblBaeTC.ff
Ge.tOlJj ect Data ().

• Е;сли QБЪеКт нереализует ISerializable, исподьзуетс~н типовой про­


цесс сери а ЛЮi!эдии, сохраннют"я BC~ поля, Et> QБD3f,!ачrнные атрибу1'О1V1
tNоnБ6r~аlizеd] .
Вдобmюн 1\ ЦЫjffitтIеНШ0]']оддерж:нти ТИIIОМ интерфейса ISeLializable. фор~
маттеры (В .NET 2.0) 01':вечaIO"Г Talf~Ke З8 выявление поддержю! СООТl'!етству;юш:и~
1)i1И типами qдeHOJ), обознач~ннь,1Х атрибутами [Олse.riа1izing1. [Orr$erialize.d].
[OnDe.sexializlogJ или СQnD€эеr:iаljzеdl. РОДЬ ЭТЩС атрибуroв буд€т об('у~да'J'Ъ­
p5f {Ю8же, а поша что мы P~CCMOТPJiIМ pO;IIЪ J Sе r i '" 1 i zable.

Настройка параметров се'Р·иаl1изации


с ПОМОЩЬЮ ISeriallzabIe
Объеf\ТЫ, ооозначеnИЫе атрибутом [Sеriаlizзt·lе]. имеют возмоЖНоСТЬ ре""
лизоват:Ь интерфейг !3€ria 1 iz.a.bla. В ЭТОМ СJ:ryчаевы :можм-е ~учаcrловать ~ в про~
цecc€:' с ери ализад!'Щ , вЫnолНЯЯ" любое пред.варительное или последующее форма ~
тирование данных, "УШtЗD.НЦъrП ИН1"ерф€йс оЧе:нь прост. ЛQСКQ.!tЬk'"У ОН определяет
еШ:n;JСТI$elЩbl'Й метод. GetObjectDa.t.a/).

/ / Дпs на'С!1'рОЙ1m npоцесса сериanи'!31ЩИИ реa..nизУЙ... е IS&clali:zable,


рп.ыlсc inte·rf.ace I&eI·ializab.le

vaid G,,·t.Objее tDa ta (Seri aL zati erHnfo iПfо,


Е;t:rNrmiпgСQнtе:кt cOritext);
694 Часть IV. Программирование С ПОМОЩЬЮ библиотек .NET

Метод GetObjectData () вызывается форматтером в процессе сериализации


автоматически. Реализация этого метода предоставляет через входной параметр
Serializationlnfo серию пар имен и значений, которые (обычно) соответствуют
полям данных того объекта, который следует сохранить. ТИп Serializationlnfo
определяет перегруженный метод AddValue () , имеющий множество вариаций. а
также небольшой набор свойств, которые позволяют читать и устанавливать имя
типа, имя определяющего компоновочного блока и значение счетчика членов. Вот
фрагмент соответствующего программного кода.

public sealed class Seriali z ationlnfo : object


{
public SегiаlizаtiопIп fо (Туре type,
IFQrmatterCo nverter c onverter);
public stгiлg AssemblyName { get; set;
public string Full Type Name { get; set;
public int MemberCount { get; }
public void AddVallJe (string пате, short value) ;
p"Llblic void AddValue (stгiпg пате, ОIпtlб value) ;
public void AddValue(5tring пате, int value);

ТИпы, реализующие интерфейс ISerializable, должны также определять спе-


циальный конструктор в соответствии со следующим шаблоном.

/ / СпедУет npедnожить пользоватеnьсхий ХОНСТРУХ'1'ор c.neДyDцeГo вида,


// Ч'1'обы среда выполнении ногла установить состоиние вашего объехта.
[Serializable )
class SorneClass ; I Ser i alizable

private SomeClass (Serializationlnfo 5i, StreamingContext ctx) ( ... )

Обратите внимание на то. что для области видимости этого конструктора ука­
зано private. Это вполне допустимо, поскольку форматтер получает доступ к это­
му члену независимо от его видимости. Эти специальные конструкторы чаще всего
обозна чаются как приватные, чтобы обеспечить невозможность случайного соз­
дания объекта пользователем объекта с помощью такого конструктора. Заметьте.
что первый параметр этого конструктора является (как и ранее) экземпляром типа
SerializationInfo.
Второй параметр этого специального конструктора является типом
StreamingContext, содержащим информацию об исто<щике или пункте назначе­
ция битов. Самым информативным членом этого типа является свойство State.
которое представляет значение иэ перечня StreamingContextStates. Значения
этого перечня соответствуют базовой .композиции текущего потока.
Честно говоря, если вашей задачей разработки не является низкоуровневый
пользОвательский сервис удаленного доступа, вам вряд ли придется обращаться J{

указанному перечню непосредственно. Тем не менее, ниже привоДЯтся имена ЗJIе­


ментов перечня streamingContextStates (подробности его описания можно най­
ти в ДОкyJ\'lентации .NEТ Framework 2.0 SDК).

I
i
L
Iлава -r7. СериаЛIol~аu~~ объектов 695
publi(: епuт 5trеаmiпgСШJtеztStаtеs

(;ro,s sProt,:'93 S I

Сrоз sMach.ine,
File,
Pe.r:s icr't.ence r
Remoting,
Other,
Clone,
СrО$sЛррDоmаitl,
All
}

Чтобы иллюстрирова'IЪ ВОЗМОЖНОСТИ настройъ:и nрОЦеСса сериализcLЦИи с по­


МОЩЬЮ ISeri.ilizable, предположим. что у нас есть тип Ma~ca. I{()ТОРЫЙ опреде­
ляет два элемента строков:ых даннъп(. Кроме того. предположим, ЧТо :все СИМВОJГы
этих. строк ДОЛЖ1IЫ (юхранmъся в лотон, в верхнем реrистре. а восстанавливать­

ся из потока ~ :в нижнем. Чтобы учесть эти треБОБаНИЯ.вВI 1vroжете реализовать


:rSerlalizable так,ка.>< I10казаНQ Irnже (не забудьте у-м.азать Us'ing~Дfl простран­
ства имен SYBtem.Run:time. Serial iz.at.:Lon).

[5е..\:'1 aliz·abl е]
olas5 MyStr.ln,gDat.a ; I3e:rializable
{
public striлg dаtаlte:пtОпе, dataItemTwo;

publie MySt:ril1gDatn (){ 1


рrivат.еMyStriro:gData(Seriali:zationInfo si, Strеаmi;пgСЬпtехt. ct;;::)
J
11 Ре~атации чnе'Иа из патоха.
dataTt.9.ii10ne 151. GetString ("First_ Гt.ro.") . ToLow.er ! ) I
clat.aItemTwo = "i. G:etS't.ring ("'datBIteIl1TWQ") . ToLower () ;

"7C'id ISer i аН ~abie. c-,е-r.ОЬj ectDa'ta (.Seri~li zati onTnfo iпfо,


Str~a1riingC(IJntext ctg)

11 НanОЗI~еце объеК:Тai S&riali.zationInfo


/I форм.а~QlJiЦlНЬDfИ Д&ННIIIИИ.
inf Q • AddVa lLie (" Fir st _ J tem" I dat.a 1 t~mOn;e. ТоПррет !) J ;
infQ.AddVaJ,ue ("da·talt~mTwo·', ctat..3.Iteтr\ТWo. 'foUpp~r ());

J
Обратите BI-щмаБ:ие на ТО, что при kнапОJШен~п'i" тuпа serial izaticnlnfo в ме­
тоде GetDbjectDa.t.a () JЩ mpебуlЩ1CЯ, чтобы Э1lеме.нты: дшtЮ::JX 1taaывал:ись ОJU!иа~
ROI!() с :внутренними 'lлензми-перемеnцым:й Т1>ща. Это МОЖe'I' OЮlзатъс.ч пo.r-теЭЩ;11ti1
тогда. когда нуЖНО выделmь ДЗlmьtе из сохраненного формата. При зтом не сле­
дует за~бывать о ТОМ, <ПО.дщI ПОJIy'iеющ значений из прив:атноro :tЮНСТРУI{1'ора ие­
оБХОДI-{М:О ИСПОЛЬ30вa:J.Ъ имена, которые назначаются в ра.\1«ЮL GetObjectIJata ().
696 Часть IV. Программирование с помощью библиотек .NET

Чтобы проверить пользовательские настройки, предположим. что вы сохранили


ЭRземпляр MyStringData с помощью SoapFormatter. 3arляJ:JyВ в резулЬ'lИРУЮЩИЙ
файл *. soap. вы увидите. что CTpoRoBble поля в нем действительно представлены
в верхнем регистре.

<SOAP-ЕNV:Епvе l оре xml ns :xs i =" ... ">


<SOAP-ЕNV:Воdу>
<al:MyStringData id="ref-l" xmlns:al=" ... ">
<First_Item id ="rеf-3" > ЭТО НЕКОТОРЫЕ ДАННЫЕ. </ First Item>
<dataltemTwo id=" r еf-4">ЭТО НЕКОТОРЫЕ ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ
</data ltemTwo>
</al:MyStringData>
</SOAP-ЕNV:ВОdу>
</SOAP-ЕNV:Епvеlоре>

Настройка пара метров сериалиэации


с помощью атрибутов
Хота реализация интерфейса ISe rial izable в .NET 2.0 все еще допустима,
для наСТРОЙRИ процесс а сериализации теперь более предпочтительным считается
определение методов, наделенных одним из целого ряда новых атрибутов. связан­
ных с задачами сериализации (это атрибуты [ OnS erializing]. [OnSerialize.d ] ,
[OnDes er ializing] и l OnDeserialized]). Использование этих атрибутов ОRазы­
вается менее громозДRИМ. чем реализация ISeria 11zable. ПОСRОЛЬRY тогда не воз­
НИRает необходимости вручную взаимодействовать с поступающим параметром
Seri alizat i on lnfo. Вместо этого вы получаете возможность непосредственно из­
менять данные состояния во время воздействия форматтера на тип.
При исполыювании этих атрибутов методы должны определяться так. чтобbl
они получали параметр StreamingContext и не возвращали ничего (в противном
случае в среде вьшолнения генерируется соответствующее исключение). Обратите
внимание на то, что не требуется учитывать все указанные атрибуты сериализа­
ции - МОЖНо учесть только те стадии сериализации. ДЛЯ которых следует выпол­

нить перехват. Для примера рассмотрите новый тип [Serializable] с теми же


требованиями. что и у MyStringData. но на этот раз с использованием атрибутов
[ОпSеriаliziлg] и [OnDeserialized].
r SerializableJ
clas s MoreData

public string dataltemOne. dat altemTwo:


[OnSeria1izinq]
i nter nal void OnS erlalizing(Stream i ngContext conte xtJ
{
/ / Выполняется в процессе сериализации.
dat aItemOne = dataItemOne.ToUpper();
dataItemTw o = dataItemTwo. ToQpper();

L
Глава 17. Сериал.lшщt1Я Qбъе:ктов 697
IOnDeser~alized]
.i. л t .e :rn·a l v od.dDnDes€,t iali z,e'ё/. (St ге:аюl rigСопt е хt 'с ол t е:rt r
j
/I ВJ.tn.on.!UIe~C:SI пс зааершевии ре1(ОЯС'1'р~1ЩИИ об'loeJt'1'i! .•
dа t аItеlrЮП<Z = dзtаJ tЭl1Ю пе . To1ower ();
da1:altemTwQ = dа.1;; аItеmТwО. Т о:Цоwеrt);

Если :ВЫIlОЛНИТЬ сериал:иэациюэтог.О НОвОгО типа . JJbl снова обнаружите, что


р,анные сdXpаннются в верхнем регистре.. а ВQСС'ПIнаВJlИВаютcn - п нижцем.

МCXQДНЫЙ код. Лроект CustomSeiial~ation размещен в n:о,цканv:юге .. COOTBeJCrBYtol.lletvl .лаве 17.

Поддержка версий ' сериализации объектов


8эавершени.е o()cy~дe'J:ЦtН э,той главы мы рассмотрим тему Ilодц,ержки верси:й
сеРJJаЩfЗацuи объек:гов. Чтобы ПОНЯТЬ. почему э:го необходиью. :мы используем
с.щ::дующ.иif сценарий. ПредПШ1ОЖИМ. что:мы создалИ масс UserPrefs (он уже уtю-
1'vЩЩЩСЛ в начале улавы) так, кАН пdRа аано ниже.

( 8:Е:Х i .a l 1: ,~aЫ e)
c la~ g IJз'\О- ;r P:ref;s
{
p I.J,b l.ic "t r: i пg gb j Ve Lsi ,rm = "'1.1)";
p Ubl ic СоnзоlеСu l0r Ба c 19 'If"u-ndС910r;
-public СолsоlсеСо1Э I For-еg.со'l1ТidСо] сч:;

рмН о UseyPrels ()
(
Б i.1 <
: itg;rщшd.dо l оr = 'e on 5~) 1eC 01 !) t . Hlack;
F'ore g:r o\in dCo lt.r = С' Qдзоlеr о lЬ r, Red;

i'enеръ предположим. что у ЕтС ecТh пр}Шожение, :в ~OTOPOM JJЬШОЛНЯется Dt;:ри­


злизация Эh"земrшяра ЭТото ЕЛаСса С nОМоЩblО BidaryE"o:cmat te r ,
э·t а t i. с; VQiQ Main ($.tr iпq [ ] arg;;)

Clscer F r ef:; lф '= rlew Us er i?re f s {:} ;


1:1р . B a'ck.gto l .1Dd-Со lО1' = СО rtзоlеСоl,,'г. Darkblue I
и р. Р0 r eg r:ОШli::! С,;:,I,:;н- ~ ,Со й асоl е'С о l о:!: • Whit ё ;

I/ С:охран~иие З!С$eиn.nRpа 1]serPre;fs з ф&ЙJtе.


B i n.a r't'f"'!:Jll (J· t leт :I:.il'J~erma t. = n ew B:i nar y r.cr ma t. l e r П ;
Stream f oStrec.m = new ? ile'Sj~ ream (@' ''С: \иэе х. dat " ,
Fi l еМ о.dе .С'rеэ't, ," ,
Fi l e Ac:.ces s . Wri±.э , F'i.1 ~, S:rL ';Jre .Nl",rl'e) J
~ iг,F0.rmdt.S'Еriаl i z.,е (fь't r е.а.JIL , \J p);
f Str e.a.m. с 1 О{$ е () ,;
СоТ\з оl."'. Беа:dLiЛ<iJ () ;
698 Часть IV. Программирование с помощью библиотек .NEТ

к этому моменту экземпляр UserPrefs (версии 1.0) сохранен в C:\user.dat. Но


давайте добавим в определение класса UserPrefs два новых поля.

[Serializable]
class lJserPrefs

public string objVersion = "2.0";


public ConsoleColor BackgroundColor;
public ConsoleColor ForegroundColor;

11 ЯвлSQ)l1'С. нозыми!
public int BeepFreq;
public string ConsoleTitle;
public lJserPrefs()
{
BeepFreq = 1000;
ConsoleTitle = nМоя консоль";
BackgroundColor ConsoleColor.Black;
ForegroundColor = ConsoleColor.Red;

Теперь представьте себе. что это же приложение пытается реконструировать


экземп.л:яр сохраненного объекта UserPrefs версии 1.0 TaR. как показано ниже
(заметьте, чтобы этот пример работал, предыдущая программная логина сери али­
зации была удалена) .

static void Main (string[] args)


{
/1 Загрузха эхэeкп.nRpа UserPrefs (1. О) в память?
UserPrefs ир = null;
BinaryFormatter binFormat = new BinaryFormatter();
Stream fStream = new FileStream(@"C:\user.dat",
FileMode.Open, FileAccess.Read, FileShare.None);
ир = (lJserPrefs)binFo rmat.Deserialize(fStream);
fstrearn. Clos€ () ;
Gonsole.ReadLine(};

Вы увидите ORНO с информацией о следующем исключении, сгенерированном


средой вьшолнения.

Необр.аботанное исключение: System.Runtime.Serialization.SerializationE~ception.


Член 'BeepFreq' в клас се 'VersionedObject.lJserPrefs'
не присут ст вует в сохраненном потоке и не обозначен атрибутом

Sуstеm.Ruлtimе.Sеriаlizаtiоп.орtiопаlFiеldAttriЬutе.

Проблема в том, что оригинальный объект us е r Р r е f s. сохраненный в


C:\user.dat. не сохранял два новых поля, присутствующих в обновленНОМ опреде­
лении класса (это поля BeepFreq и ConsoleTitle). Очевидно, что это настоящая
проблема, ПОСI<ОЛЬКУ ДЛЯ сохраняемого объекта вполне естественно эволюциониро­
вать в процессе существования.
Гllава 1(. СеDl<lализац~я ,Qбъе"тов 699
До выхода .NE1' 2.0 едвнс'tВенной ВОЗМОЖ1ЮСТЫО для. уче.та тorQ. ':11'0. еохр~:ьпщ­
ный объект может не иметь всех новЫх полей из обновлеНJjОЙ и более ДОЗДП~Й
верс~IИ класса. была необходимость реализацИИ I-Sеr:iаliШJ>blе J;1 осуществление
:контроЛJi ·НРУЧJiYЮR. Но С Появлением .тт 2.0 новые поля могут ЯВJ;Ц,) обоана­
чатъсн атрибутом [Op'c'ional FieldJ (опредменным в paJ'-4К""dX пространства имен
SY$t.ein. Runtir1te.. 5€.riali zation).
[Serializable ]
cla8s OserPrefs

public Co rlsole:C;olo.r Ва'сkgrоuТ).dСоlоr;


р'иЫ ic. СО1)5:.01 еСо 1 0 I Fоrеg.rQlПld.Cоlо]';

11 ЯаJJQ'l'CR ИО8ЫНИ!
[Opt ional Fie ld.1
publi~ int BeepFreq;
[Ор;;. ionalFieldl
public .'3tring СОТlЭQlетitiе;
publlc llseIPref's ()
{
B,eepFreq =- 1,0 О О ;
Coti501e'Title = "Моя КОН С ОЛ'Ъ" ;
Вsсk-grщmdс,йоr = ', СолsоlеСо} o r. Вl "ck,;
Fоrеgrtюпd17010r = ~'')r:чю lе Соl О!;. R€d;

'}

Когда форматтер peHOHCТPYJIPyeT абъект XI обнаружи:ваех:. что отсутетвующие


поля помечены, щrn: цеоБЯЗQ.Тt;ЛЬные. ИСImIOченnе сре-,ды выmнения уже не гене­
рируется . .вместо ,наго дa:Ell'):ы •. которые бъmи соJl."ранелы. проеци:руется обратно в
t";yЩествующие поJUC (в дщrnом случае это вac.kgroU'i;!dCC>lOI и ForegI:01JТ.dCol cr). а
QСТЭЛЪНЬТМ полям лри~щmаются значеЮfВ. предусмотрешIыe по умолчааию,

Зам~.. аюre., Следует nOHi/lM'ClТI>, ЧТО иCfЮl1ьзоааНillе [Optiona lFi.e ld] не реш.ает проблему вер­
,СИИ сохраненных объектов полltlостью. Однак{) ЭТОТ атри()утобг[)n~.. ивает решенИ'е самой ТИС
пичной l1PаблеМЫ(Д(j)(jаз.~ение "!овых r1OI1ей даtJflЫХ). Дnя реше.нИя более СJ10ЖIr1ЫХ задач ЛОД­
деРЖКи версий .все же ГiЬтребуе·тСR реШJИЗациSJ интерфеиса lSe.r i а 1 izable,

ИсХDдныii код. Гlpoe:к, Vers'lOoedObject размещен в подкаталеге, соотвеТОТВУJ0щем главе 17.

Резюме
:в этой rлаве предпагаетс.н обсуждение сервисов сериэлизации. Вы MorJm убе­
дй:г.ьсn в ТОМ. что платформа .NEТ для :корреi{Т]:{ОГО учета все.го множества СВН­
зar-rnыx объеК:юв. подлежащих сохранению:в ~OTpJ{e. использует объектные гра­
фы. Когда RЭЖДЫЙ "-иен объею-nото графа об6.зн;а,че;н атрибутом i·-S~r:1 аl i:Z аЫ е J .
rI
iI
700 Часть IV. Программирование с помощью библиотек ,NET

дан.ные можно сохранять в любом из нескольн:их доступных форматов (в двоичном


формате, формате SOAP или формате XМL).
Вы также узнали о том, что процесс сериализации допускает пользовательскую
настройку в рамках двух возможных подходов. Во-первых. у вас есть ВОЗМОЖность
реализовать интерфейс 1 S е r: i а1i z а bl е (с поддержкой специалъного приватно­
го конструктора). чтобы влиять на то. кан средства форматирования сохраня­
ют поступающие данные. Во-вторых. вы можете использовать множество новых
атрибутов. появивш:ихся в .NET 2.0. которые упрощают процесс сериализации с
ПОЛЬЗОВqтелЬСRИМИ настройками. Следует просто применить один из атрибутов
lOoSeria l izing], [OnSeri ali zed ] . [OnDeserializing] или [OnDeserialized ]
к членам, получающим параметр St reamingContext, и форматтер обработает их
соответствyI01ЦИМ образом, Завершается глава обсуждением еще одного атрибута.
[OptionalField], который может использоваться ДЛЯ поддержки версий сериали­
зации типов.
Удаленно,е
взаимодействие .NEТ

Р
,
азр~@Q,Т'llIКИ. не iще_;щn;в~ 'ШIbГ!<:I ра!'Ю1l"Ы t'; rI.:-'Jl;uтфо~IQЙ ,.N'E'I. обычно CtТllOM'I
,'N'ET 'J'{J.rLbIro rr 'срI;ДС::fвам J::~Дщmя ая't'е,рне;r-n:ри.1iIЩfim-ni:Й (noeHOJIblg ··
"1(1,'116 .вщЦJ?I'ИPJreп:'ICЛ 1: ·j~I':r~e:l1 »1, е1:lQТlll!'ТUБУ--Ющy"r..м пу.юграм:\1НJ:.YМ Dоо<;>:rreчeJ:,m­
,NEJ'"

ем1. вы уЖе uмщш в~~\щщmг.Е, yQ~CJl :&'~M. "'!ТО это далeJ:ОО n~ nm.., Со>здaI-Iй~
vreъ. IIpЮIflЯmБии пй.Л.Re'ТDН lI;IЩfJ. fl.)t'lWi'j :rf IJче..щ. yвx-o~ (щ> ~oxtJ p4'JlXM;U.mpQ-
вaШIоvr) ..во~жВJfi.tтыD Wl"щрор:иы .~ В ]'IJ'("~~' iiI'fG~ '~'!!lфоРN11iЩЪJE ~Щjя'Ие р:1эра­
ботчшm J.-ш:т; не :tj'Мею~ J~'fIЩ'[~'WОТО ндьгт,а. t;КiIJ&I'!'8'Ъ.I' пре,!{I'lQi.ТЦU'ать., <{ТО Wel)-
Gepllи.t.ьJ =~11. uбe~rre"Пf;!>.im'IТ 'fjдиш.'J:,ве:EJЩ:I'Й· еl'f~CQб В'Iam.щ(J~t:1'!mя ~ rд.iЩ~IМИ
й5ъP...кr~ Эru тоже '!1В {lОО'Т'Вt<:'т("'I'l"У~[J дei:i'с-rnИil't:'i,IЪ'JjЮ~m. J.1,сцьщ.3УН: OOCr..t'I УДa::iJ~И­
JШrо в~:utШ'Тl'l'il.'i .NEТ. ~'1~ С'Тptl'Щ~I'!lДffорa.нroвы:е распред~деlЩЬЩ др",ло~
жeнш;t. ~ j~JIШ в:ичer~ ol'llцmTb С. ИТТ'Р JЩf!: »п. ,(еела вы ЭТОМ зЮt(:r:rнте).
П€p:RtJи за;цаll'}ciii; ВТ(JЙ щавы 1i"J!'iliяетсы P8ecмo~~ Юf:3,i;'.о~IJ:Щ-~,е!!!J:.ц( щ'm-lfJf1t­
иоt"It'!Й, И-l!5Ш1лЫi)i""МШ; tp~доЙ CLR Ц.\!;ЕЯ treре,цJ1l.'q1И 'I.qflфоp:мa.щm ;ю гp~ доюt;.нщз.
nPШЮЖ~Н1-dt При Ьб~оfum:м;JЩа.тtf:'ПRО"О ~1>.\1QдеЙс:г13i:Ш ,1'lЕТ ..и1.'J'(Q~У­
ется. ~mОШбdПЮ G,i1ецна.л:ыtЫ;!i;;ТI$:мп.шm, ~ 'Б:ак aг~ll'Т ,{me. pI'I.ЦУ-Мfl;ЦУl1h}, I;Дt:m.':r,
1Юii.ршалннг l!('J f'(jЫлse '(:ХctЮpЫЙ ПРО~ЙШJ~~t.>:'rt.m маpm;aд;кш)' -ДО ~щч.еFIШt'J.
'сеpIreРн:.ая шcrl'1Ви:...~аIJ,ИЯ: l'I'б'ьскюв Iв !ЗрОiU1Ш)IIOпroЖНОc::t:ь m:I'Иelз'f~ Щ"J'ИЩIЩ.щцл)
JjI 1:11;. lThcllre ~ЬШ(lн&m.я e)r.rИ эти::к G~h'tК. mрмm,ЮБ ЦуДеl1 Dре,!J,Лt'URЩlJ.m~€~К:{J
:npnмepQВ ~Ig ,по~;а.EiJlJIЮ\~ТРЙр'У;ЮJ,tiш п;рсщеre Пl:)С1:'рО~mп ра(щреДeJ[~-
1!ЫХ с'Исre),j( в Р8,юmос IIJlRтфйрмы .NE'f:

Поняти' е удanенноrо взаимодеi1с,тв'ия iNET


;,sbl Д{).ГJ lRJiliЪ] DO.."'1LНМ'ГЬ И'S ' Х'ШМ!Ы 13. чm д(J;ШtН пр,WЮЖ't"/-{Uй ' (~o\ppDoma:ll::il задае't
.;I.IiIГМ"R.есв:~ граяицъ1' 1iIЩ{flJrIlI'IeffИИ КОМI1OIЩ,ВОЧНQТО бл..жа ,Ъ!Е1' в }>ами3'Х npr:lЦl>,Ce21
WjЦЗ2.- JJo~e \jIТQrt) O<reI!:Ь ,8,ЩЩ-IQ .1:IД1J. дадьнеЙШ'еr() обе}-ЖД~iШИ pactrp~~eM­
'I1.Щ]( др~..е:юrй .'NЕ'Т. ПD'СJ'~щtpЩ7 }JдaJtl'ЛiI'Щf: в.;юu.'\.щдеЙemmJi? оэва~ет ~дecь Ri'!
~бtJЛt5е ''-feМ ~~дейст,вие д.вy~ o"'т.,~o~. f:(lоfuц'mщlIILКСЯ Ч13рt!1:I rpаиицы: ,цамеы()в.
GDIi>ТЩl'\';l'!1.woщrЮ )1~ ЦWЩfJ~~е,.-I).rй мщyr фи~еCШI IЮXO):.~Я I6 сm:щmщn
:у.мG8ИiШ. .

• , ]bia ,iJ;OMel'lfl щ:Ц),д](~же-~я оup-ед.е-д.ены "в рю.шах ОJIНШО 'U -r-йш же l1JlOц:t~
(И JТI)8!Т()МJ! на Qi!Шеf( и, roй же ~ЭN!JИне).
702 Часть IV. Программирование с помощью библиотек .NET
r
• Два домена приложения определены в разных процессах на одной и той же
машине.

• Два домена npиложения определены в разных процессах на разных машинах.

С учетом этих трех возможностей становится ясно, что удаленное взаимодей­


ствие не обязательно предполагает наличие соединенных в сеть компьютеров. На
самом деле все примеры, представленные в этой главе, могут вполне успешно вы­
полняться на одной автономной машине , Независимо от расстояния между объек­
тами, в отношении взаимодействующих агентов используются термины ч клиент "
И "сервер". Упрощенно говоря. к:лueнm- это сущность, пытающаяся взаимодей­
ствовать с удаленными объектами, а сервер - это программный агент, содержа­
щий удаленные объекты.

Пространства имен удаленного


взаимодеЙствия.NЕТ
Перед тем как углубиться в детали процесса удаленного взаимодействия .NEТ.
мы должны выяснить, какие функционалыiеe возможности предлагают про­
странства имен, обеспечивающие удаленное взаимодействие. Библиотеки базовых
классов .NEТ содержат очень много пространств имен, позволяющих строить рас­
пределенные приложения. Большинство типов, содержащихся в этих простран­
ствах имен, находятся в mscorlib_dll, но дополнения и расширения базовых
пространств ИМен вынесены в отдельный компоновочный блок System.Runtime.
Remoting .dll. В табл. 18.1 предлагаются краткие описания пространств имен уда­
ленного взаимодействия .NEТ2.0.

Таблица 18.1. Пространства имен . NEТ дЛЯ ПО,llДержки возможностей удаленного


взаимодействия

Пространство имен Описание

Systern.Runtime.Remoting Базовое пространство имен, которое


должно использоваться при построении

любого распределенного приложения .NEТ

System.Runtime.Remoting.Activation Относительно малое пространство имен,


в котором определяются несколько типов,

обеспечивающих тонкую настройку про·


цесса активизации удаленного объекта

зуз t em.Rurltime .Remoting .Chann els Содержит типы, представляющие каналы и


прием кики каналов

System.Runtime.Remoting.Channels.Http Содержит типы, использующие протокол


НПР дЛЯ транспорта сообщений и объек­
тов в удаленную точку и обратно

Syst em.Ru nt i me.Remoting.Channels.lpc Пространство имен, которое появилось в


.NEТ 2.0 и содержит типы, использующие
архитектуру IPC Win32. Архитектура IPe
(Interprocess Communication - взаимодей'
ствие процессов) обеспечивает быстрое
взаимодействие доменов приложений, су·
ществующих на одной Физической машине
[лава 18. Удвле~НDе взаимодейотвие . ~ET 703
Окончамие табл : 18.1

Проотранство имен ОПИ~lUfе

sys t .em.Rur,t·im!:! .Remot ing Ба3050е' пространство имен, ICoropoe


.должно ИQIiI()лъэоваться мри построении
люб:ого раопределенного прилmкения .N.EТ
.s;.yst.iIRL.Runt.ime .Rеuюting ..Jj:ct i vatj сп Относительно малое прОСТранство ИМ!Щ
в котором определяются' несколЫ<о типов,

обеспечивающих· ТО~КYIO НЗQТроi:iку про­


ц~cca, активиазции удgленноro объеКfа

System.Runtirne . Remating.:Channel5 Содержит типы, nре)),ставляющие каналы и


приеМ~IИII!'И ICаналО8

.5уs.tеm.Ruпti те .R€lnotj ng.Ch·annels .R1tp Содержит типы, ИСI1QlJЬЗ\l19Щf1е npOTOK(i)j')


HlТP ДЛJI rранспоprз сообщений и об1.е)(­
ТОВ в уда11енJ,4УЮ точку и обрапю
Syst.еm.Рun·tiше .Remot·:irig .СЬаrшеls. Ipc npocтpaHClBO имен, I(OTopoe появилосЬ в
. NEТ 2.tJ и содержиr(п,пы, И{JПОЛЬ3у;1ОЩOllе
архите~у IРСWJIlЗ2. Архитектура IPe
(Irltеrрг;:юеss Communlcatibn - ВЗjlимодеЙ·
отsие процессоз) обеcnе<fИ'В<lет .БЫстрое
взаимодействие домеНов приложениЙ. су·
ществующих на 'ОДНОЙ физ~еской м:аШИ~lе

ЭУВ tem. RU'l1.time. ReТ1'l0t.ing. ChaJYne 1з_ Тер Содержит nml:lJ, ИСГlользующие ЛРОТРКОJ1
тср для TpCllicnopтa сооБЩ6f-1ИЙ и объеl(rов
в УДВl1еl+НУЮ П)4КУ и ОбратНо
SУ5t,еш . Ruht i'roe Жешоti.ГiЧ .Contcexts По:reО,I1Яет itонфИГУРИРQВCiТЬ параметры'
объектногО' контекста
Sуstеш. Rш,t. i:ше .Re.mating .·Lifet;.irne Содерж.итПIПЩ, ~npавляющие ЦI'IКЛQМ су·
ществования УДaJЩI'IНЫХ' объвlI'ТОВ
Syzt.em. Ru:nt i.me.Rernoting. мessag.:!.ng Содержит 'rипы, IIIСПОЛЬЩI6Мые Д.ЛЯ созда­
НИЯ tA передачи объеКТi!)В' сообщений

sуs\:.еrn.Ruлt iniе.Неrtюtiri1;J. Metadata СDдержИ1l'ИПЫ. IIЮПОЛЬЗ'l8мые ДМ! на­


Сrройки параметров геке,РИР08аМИfl .и фор·
матировзгLИЯ еЬобщеl'lИЙ' SOдp

$y'stem.R1:lJ'ttime. Rеmоting.Меtаddtа.WЗ'с}\sС\2QОl Содержит rИПh!I, ГlредставлЯЮЩlo1е Форма.т


XSD (XМL $chema Definition,- определе·
fI~e схемы ХМЦ 8 Goo-теТС11!и'И со CTaft·
дартами КонЬОРЦиума WЗС, Оplill1ЯТbIМl'I В
2001 гощ
Sys.teт .RЩlt.i.rrIе.Rеmоtiпg.МеtаdаtаSеrViсеs Содержит типы. ИСПDдl:iзуемые средс:твом
комаНДМОЙ C-ТPOKL1 SQ.a Р Зu·d.s. e~e при
ltонвертироваfiИИ метапаННЫХУдщ1енноИ ин­
фj')aCТрукryры .NEТ BXML,cX€Mb/ (и обратно)
Sys:teJIJ .Ни !) time. Re.moti n-g. Рr0хiез СqдержliП l'Ипы, обеспечивающие фуI-lКЦИ,
{)HaдЬH!:>Ie возможн;}сти ДЛЯ объектов, вы·
(10ЩII'IЮЩИХ з.адачи агент;;, (;ргоху')

Systel:fl.Runt:irne .R~mоtiлg. Ser'l1ices ·Qпредеl1яет ряд общи)сбазО8ЫХ класса.В


(11 иН'терфеИёО6). КOiОРЫ.е обl!iчko испоi!llr
ЗyIOтся тол'ько внутренними агентами YfJP-
ленного взаИМQjJ,еЙС1'ВИfl
т
704 Часть IV. Программирование с помощью библиотек .NET I!
Каркас удаленного взаимодействия .NET ,
Когда клиенты и серверы обмениваются информацией через границы прило- I
жений, среда CLR вынуждена использовать низкоуровневые примитивы. обеспе- !
чивающие настолько "прозрачное" взаимодействие сторон. насколько это возмож-
но . 81'0 значит, что вам. как программисту .NEТ. не НУЖНD создавать огромные по
объему блоки программного кода поддержки сетевого соединения, чтобы вызвать
метод удаленного объекта. Также и серверному процессу не нужно "НРУЧНУЮ" из ­
влекать сетевой пакет из очереди и преобразовывать сообщение .в формат. понят­
ный удаленному объекту. Вы вправе ожидать. что среда CLR позаботится () таких
деталях сама, используя свой стандартный набор примитивов удаленного вэаимо­
действия (хотя. при желании. вы тоже можете принять участие в установке пара­
метров соответствующего процесса).
В сущности. слой удаленного взаимодействия . NET обеспечивает акI<уратную
совместную рабоry следующих четырех ключевых элементов:

• агенты;

• сообщения:

• каналы:

• форматтеры.

Давайте рассмотрим каждый из указанных элементов по очереди и выясним.


как их комБШIация позволяет осуш.ествлять удаленные вызовы методов.

Агенты и сообщения
Клиенты и объекты сервера взаимодействуют не напрямую. а через посредни­
на , обычно называемого агентом (ил:и ргоху-модулем). Роль агента .NEТ Эalillючает­
ся в создании для клиента иллюзии того , 'lТO он взаимодействует с запроше нным
удат1енв:ыrM объектом в одном домене пршюженuя. Чтобы создать Тal{УЮ иллюзию.
агент предлагает интерфейс (члены . своЙства. поля и т. д . ), идентичный интерфей­
су удаленного типа. С точки зрения клиента данный агент и является удаленным
объектом . Однако "за кулисами" агент переправляет вызовы удш:rенному объекту .
Формально тзкой агент, вызываемый клиентом непосредственно. называется
прозрачнbtМ агентам (tгansрагепt ргоху). Этот объект. генерируемый средой CLR
автоматически. не сет ответственность за проверку того. что при вызов е удаленно­

го метода клиент получит нужное число параметров (и они будут нужного типа) .
Поэтому прозрачный агент можно интерпретировать. как фикс ированный слой
в заимодействия. который нельзя программно изменить или расширить.
В предположении о том. что прозрачный areHT может выполнять проверку
входных аргументов . соответствующая информация упаковывается в другой гене­
рируемый средой CLR тип , который называется объектом сообщения. По опреде­
лению все объекты сообщений реализуют интерфейс System.Runtim e .Remot ing .
Messaging. I Message.
puыlcc interface IMessage
(
I D i ~tio na ry Pr oper tle s { get; }
Глава 18. Удадемюе ~заlfМощеЙQ1&l-1е .NEТ 705
кав видите, интерфейс lMessage определяет единственное свойство (с именем
properties), :котщюе обеспечивает дvстyn к RоллеIЩmf. ИСЩЩbl;jуемо.:й wщ ~aнe-
1ЩJJ предоcrавдевных клиеНТОМ аргументов. ПОс:JJе НIlПОЛ1Jения объекта сообще.­
ния содержимым средой сш. он будеrr передан pOДCТ1lel:IНOMY пту. Щlзывае1l{ОМУ
реал.ЬНЬLМ. агентО.II" (теа! proxy).
Р~альный агент - это c.yIЦI-IOсть. которая фактиr.rеоки посылает объект соdбще­
ElИJi В1!iЮfал (пон.ятие .н.анала будет о.бсуждаТЬСJl ниже). Реальнh1Йа.г~нт. к0'торый
(В Ь г.пичие от npозрачного. Ш'ента) мОЖет быть pl1ClJtupe1i програщМИСТОМ, пр~Д~
ставлнется базОВЫМ "l'ИПОМ класса С именем Re:alProxy (что и следовало ОЖJJдатъ).
Снова следует iюдчеркнутъ. что. среда CLR генерирует Ю!ИентСЕуЮ реюmэqцию
реалъПОl'Ciarента. для иСпользования по умолчанию. котораа впщrне ПОДОЙДеТ
-вам если, не во всех, то. в большинстве случаев. Но 't.lтобы им.ет1;. цредставление о.
функциональных возможностЯХ. предлатае.мых. аБСТР<lI\ТН:ЫМ базовым массом
RealP.r.oxy. Изучите фОРМaJffiное определение этоtQ 1:ИЩl.

рпЫ ic аЬ$t:tiЭ.С t сlаsэ RealProxy : b'!::>j ect


{
ObjRef Crea'teObjF:ef (Туре :r;eq1JE-stег.1Туре);
pt:lbl.ic 'l.тirtпаl
public virt'Oa.l nоо1 Eq\J:als (ob~lect obj);
р'дЫiс ".. irtual In·tPtr GеtС:ОМIUп)mоwл (Ьоо1 fI ,sмаr:sЦ.эll€-dl;
p.ublic virtual int GetflashCt:>de ();
рцы1c v-:irtщll vroid GetOb-j'-ес .t, Dаt а (. Sе·ri,аlizаtiQпIПfО .info.,
s·trеаmiпgС<iJntехt context J ;
public Туре :GetPrQ\~iedType (.) ;
publie static obj .e.c t "GetStob[Jata (Rea lFroкy гр);
publ l c virtual ФD J е ctGеtТтаQ$ра rеntРrоху (.) ;
рнЬНс 'Гуре 'GetType (! ;
public IС.;щstruсtiопRеtl..lтnМеэsаgе lr.1itlC3.1izeServer01;1je!;'t t
ICQI.1struct:10nCal1Me$sage сtоrl<lэg·) ;
public virt\il~ l I~ssage IDvoll:e (ТИеss.аt;j€ 11tSЯ) ;
p'UbliG virtual void SetCOм.rUrt'knowi1 (7ntPtr ±) ;
public static void SetSt;ubDa.t.a (RealPro sy r p , object st!'lbData);
puhlic v.lrtua·l I!Jl.Ptr 3-uрроr·tSlпt.еrfа.с е (ref :( ;.uici н .а);
рliЫ.lС virtual эt:tinу Tj)String ():

Еcлu ~Ы I:fe занаты построeInreм ПOJIbЗ0Ва.тI:'ЛЪСКОЙ реалиэ-aд;иJ! реа:т.цого areн:ra


1ЩИентCJ.. 1'9 единственным интересНЫМ ДЛЯ вас членом будет RealP.tQxy.lnV'oke О .
С ПQМОЩЬ;Ю 'M~тoдa Invake () сгенерираванюэш средой CLR проараqНЪ1Й агент в фо­
НОВОМ ре)f:CИМ~ передает форматированный объект сообщения пту Р,еаl Р! оку.

Каналы
Поt'ле TOГO!l'.llli aгeH1ы проверят:и о"Iформатируют I10ставляемые ЮJИeJJ,том. аргу­
менты, уцаковав их вооъект сообщения. с.ооП!етC'l'ВУЮЩИй. IM9S8age-совмеСТИМJ>IЙ
'РЩ передащ'(:нр'т реаЛ:ЫlоГО aгtЯl'Т8. объекту канал.а. liaналы - это сynщоC'tи. О,....
вечающие- за транспортировку сообщения удаленному объекту и. ec.'Ui это неоБJ.'О­
ДИМа, 38. '1'0. чтоБЫ возвращаемое нначение от удале:аноrо объекта было доставлено
рбраТl:I9 юшеR'ty. В библиотеках базQвых Iwacc.OB .NEТ 2.0 предлагаются ГOТ9~ыe
реaJIИЗщиитре.х К'dНa:TIOВ;

J.
706 Часть IV. Программирование с помощью библиотек .NEТ
r
• ТСР-канал:

• НТГР-канал:

• IРС-канал.

ТCP-к:aнa.n. представляется типом масса TcpChannel и используется для пере­


дачи сообщений с использованием сетевого протокола ТСРЛР. Класс TcpChannel
удобен тем. что форматированные пакеты оказываются исключительно ~легки­
ми~, поскольку сообщения превращаются в плотный двоичный формат с помо­
щью BinaryFormatter (да. именно того BinaryFormatter. О котором шла речь в
главе 17). При использовании типа TcpChannel удаленный доступ осуществляется
быстрее. Недостатком является то. что ТСР-каналы не согласуются с брандмауэром
автоматически и могут требовать вмешательства сер висов администратора систе­
мы. чтобы получить разрешение на пересечение границы машины.
В противоположность этому. Н1ТР-канал. представляемый типом класса
HttpChannel. преобразует объекты сообщений в формат SOAP. используя для этого
соответствующий форматтер SOAP. Вьппе вы могли убедиться в том, что SOAP опи­
рается на XМL и поэтому результат в данном случае оказывается более объемным,
чем в случае TcpChannel. поэтому при использовании HttpCliannel удаленный
доступ может осуществляться медленнее. Но, с другой стороны, протокол НТГР яв­
ляется гораздо более дружественным в отношении брандмауэра. поскольку боль­
шинство сетевых экранов позволяет текстовым naкeтaм направляться через порт

с номером 80.
Наконец. в .NEТ 2.0 предлагается доступ к IPC-кан.алу, представленному типом
IpcChannel. который определяет коммуникационный канал связи для удален­
ного взаимодействия с использованием IPС-архитектуры операционной системы
Windows. ВвидУ того. что IpcChannel при пересечеюrn: доменов приложений дей­
ствует в обход традиционных систем сетевой коммуникации. IpcChannel оказы­
вается намного быстрее, чем НТГР- и ТСР-каналы. однако. может использоваться
только для взаимодействия доменов приложения на одном u пюм же комnьюте·
ре. Поэтому IpcChannel ае может применятъся для построения распределеЮ-iЫХ
приложеНИЙ. допускаю.щих испольэование множества физических компьютеров.
Но тип IpcChannel может оказаться идеальным вариантом тогда. когда вы хотите
обеспечить наивысшую скорость обмена информацией между двумя локальными
программами.

Важно пониматъ. что вне зависимости от типа канала, который вы выберете


для использования, и HttpChannel, и TcpChannel. и IpcChannel реализуют ин­
терфейсы IChannel, IChannelSender и IChannelReceiver. Интерфейс IChannel
(как вы вскоре убедитесь) определяет небольшой набор членов. обеспечивающих
обrцyю функциональность всех типов каналов. Роль I Ch annelSender заключается
в определении ДJIЯ каналов общего множества членов. позволяющих отправлять
информацию данному получателю. С другой стороны. IChannelReceiver опреде­
ляет множество членов. позволяющих каналу получать информацию данного от­
правителя.

Чтобы позволить приложения:м клиента и сервера зарегистрировать выбранн.ыЙ


ими канал. вы должны использовать метод ChannelServices.RegisterChannelO,
который получит тип. реализующий IChannel. Вот фрагмент программного кода.
который показывает. как домен серверного приложения может зарегистрировать I
,
L
Гпава 18, УД8J1еJЧнее ;ц~аимьдвЙс:гв.ие .NEТ 707
~JТP-RaНaЛ, использующий порт- 32469 (аН;;!.JЮl",ИЧRЫе ~О3'МОЖНОС'fи ЮIИента будут
продемонстрированы 'ЧТЬ позже).

/I СО!5Д;а.ике и ре~С'Ж'раЦlfii BttpChann&l.-серара с пор'Юм З'Z4б9.


l'IttpChiannel а = .a€ w HttpC!aannel ! 32 4 ~6З) ;
ChaIlfl€lServices. Rеgi.stеrСhQ:tlЛеl (С.) i

Снова о роли формапера . НЕТ


ЗанлюЧИ'ГeЛbl-IЫМ ~ле:меНТQМ ГQ.iЩВОЛОМКИ удаленного В3aиI\iод~йtтDИЯ .NEr ,лв­
J1иется фgрматтер. 1'ипь1 ТсрСhаЛI1~l и Н1:,tрСЬаnпеl используют свои внутренние
фарма:ггеры. задачей J{оrrОРЫХЯ8дяе:гсн перевод объщnа СDDбщени.'R 11 термины со­
О"тветствующШ'о протокола. Как уже ГОВОРИЛЩ:1;>, тип ТсрСhа.Л леl йctюльз~т ТИП
БiJJа:ryFоr:mаttеr. вто времн как ТЮI H1:,tpLhartpel испольЗует ФУНКIIИOНЭЛЬНЬ1е
возможности типа SoapFQrmatter. Qпиршд'.Ь на :ll!а:в:ия, пьлученны€ ВIIpеД'р1ДУ­
щей г.лаве.. ВI:ilДОЛЖНЫ пониматъ. кан СOQтвеl'СТВуЮщиИ шшал. фQРМа1"Ирует nOC'I)'-
лвющие сообщени.я.
После создs.ния форм:атирощmного со~бщени.я оно пе:редае1'СЯ в кaнan. по кото­
рому в кон-це концов доетиrае:г целевого ДOM~Ha дриложения. Там это сообщение
преобразуетсз из сцецифичес1Ш.Х Tep:мm!OB проm.кола обратно в t'ермины .:NE1'.
после чего элемент. который цазьшает~ дu.спет'Wр. вызывает нужный метод уца-­
ленн(jГО oбrьекта.

Общая картина
Бели 'у вас CJT чсге-ния npедьщущих разделов .Уже T'o.rmвa идет .кругом.. "Не llШЩ­
куйте! Проарачн:ый ateHl', реалъnый атент; об~' сообщения и диспетчер:вм мо­
жете. МАК правило, просто игнорировать, noсколъ:кучаще всего вам вполне подой­
дут параметры удалеННого взаимодеЙст~uя. пре;цлarаеМЪте по умолчанmo. Чтобы
аанрепить Б па.?dЯ.:I:И' соответствующую JIО~едоnaте'ЛhНОСТЬ событий. р::tсемагрите
рис. 18.1. на I(QTOPOM UQКfJ.$<ще I:;xeдm I1роцесеа: RоммУни:кации двух объе}L"ТОJ> на
раЗНblХ доменов n:риложещIЙ .

.ДоМ8Н~ИЯД Домек прмложения В

ОбъеlfТ удaI1енныи
кnиента объект
"
..
t j
Праэрачный
агент
ДИGneТt{eр
J
I РеалыныИ
агент .~ Н Формаrreр KaHal'1
,
Форматтер
I
I

РЕ. 18.1. Арките1<ТУРЗ удaJt~Н}jQгО В3димодей,отвия ,NET,. nP'6дпагаемаfl по УМllilчан~ю

1
708 Часть IV. Программирование с помощью библиотек .NET

Несколько слов о расширении стандартных возможностей


Ключевой особенностью СJIOЯ удаленного взаимодействия .NEТ является то, что
большинство предлагаемых по умолчанию слоев удаленного взаимодействия мо­
жет быть расширено или поm-юстью заменено разработчиком приложения. Так,
если вы хотите (или, возможно, вам нужно) построить диспетчер пользовательсн:их
сообщений, пользовательский форматтер или реальный агент, вы имеете для этого
Бсе возможности. Вы ТаЕже можете добавить дОnОЛНttтельные слои. ВЮIЮЧИБ в це­
почку обработки пользоватеЛЬСlше типы (например, пользовательский приемник,
используемый ДЛЯ предварительной или последующей обрабОТRИ сообщений). Вам
лично, возможно, НИRогда и не придется: модифицировать базовый слой удаленно­
го взаимодействия .NET, но фант в том, что платформа .NEТ предлатает простран:­
ства имен, позволяющие решить такую задачу.

Замечание. В этой главе тема расширения базового слоя удаленного взаимодействия .NET не
обсуждается. Чтобы узнать, как это сделать, обратитесь к книге Ingo Ааттег, Advanced .NEТ
Remoting (Арrеsэ, 2002).

Термины удаленного взаимодействия .NET


Подобно любой новой парадитме. удаленное взаимодействие .NET предлагает
свой собственный набор трехбуквенных акронимов. Поэтому, перед тем кЮ{ рас­
смотреть первый пример програММIIОГО кода, нам с вами придется определить не­
СRОЛЬКО терминов, обычно используемых при описании приложения удаленного
взаимодействия .NEТ. Как вы можете догадаться С3J.\1:И. соответствующая терми­
нология используется для описания ответов на ряд общих вопросов. вознинающих
при построении распределеm·юго приложения. КаЕ передать тип через границы до­
мена приложения:? Когда именно будет антивизирован удаленный тип? Кан управ­
лять ЦИliЛом существования удаленного объекта (и т.д.)? Когда вы поймете соответ­
ствующую терминологию, вопросы построения распределенных приложений .NEТ
уже не будут вам казаться столь запутанными.

Варианты маршалинга для объектов: MBR и MBV


В рамках платформы .NEТ вы имеете на выбор два варианта того. НаЕ предо­
ставить удаленный объент длиенту. Упрощенно говоря:, маршалttнг описывает
правила передачи удаленного объекта из одного домена приложения в другой.
При разработке объеRта. предусматривающего удаленное использование, вы мо­
жете выбрать либо семантину MBR (maтshal-by-reference - маршалинг по ссъmке),
либо семантину МВУ (marsl1.aI-Ьу-vaIuе - маршалинг по значению). их различие
заЮIЮчается: в следующем.

• MBR-объекmЬL Вызывающая сторона получает атента для осуществления до­


ступа R удаленному объекту.

• MBV-объекmЬL Вызывающая сторона получает полную копию объекта для: ис­


пользования в своем домене приложения.

L
[лав-а 1В . Yд~eHHoe 8ЗзИМоАеИс;.Т81Iе .NEТ 709
ПР11 ИСДQДЬЗ0вании типа,· отвосяn:tегося к МВR--объею-ам, cp~ CLR обеспечит
С0зданщ: в ДQМ~R~ приложения ЮIИeВТа прозрачного и реал:ън.ого aremOB. вто вре­
мя" юш сам ~R-объeкr будет оставаться в домене npиложения сервера. При вы·
ЗО~f' методов удалem-ю:ro типа kJ:IИ.e1-ггом еистема yдaJ1С1-н[оto · взаимодеЙСТВИЯ .NEТ
(схема КQТОРОЙ описана ВЪШ1е) активизируется, "ЧТоБЫ вьmолнить задачиynаковки.
отлр<:itВкц и получения и:аформадШ1 при обмене даlUfЫми через границы домецов
прцложениЙ. Для этото МВR-объевтю имеют рлд ~воЙств. "nростирающихсJ'l~ за
ра~ши Щ фи;щ-чесного распо1iDЖени:я , Бы увидите . QTO МВR-объе>кты имеIOТ раз~
~e ош{ии конфигураЦии. 01:носяiЦиеся 1(. их активизации и ynраВiIецию ци­
КiIOМ сущ~~твоВю-nш. Б арОТИВ(1ШОЛОЖНОС1'Ь этому. МБV'-объеICТЫ npeДСТaВ'ЛЯJQт 00-
бой ЛОК(1..'lЫfые lCоnыи удалепЙых объектов (испольэующйе прото.кол сериaлuзации
.,N'Eт. который был рассмотрен в главе ] 7). МВV-объекты: имеют намНого меыъше
ОIШJ'IЙ щшфигурации. пoctwЛl:ЩY их ЦИЮI сущоствавания }(ОИТРОШ1руется неuocpед­
СТIЩННO :клиентом . Подобно любому дРУГому оБЪекту .NET. после того хах ЮШ~НТ
ОQВQбодит все ссъm:Ки.на MBV-ТИn . ЭТОТ ТИП станОВИТСЯ потенциа:;лыrым объектом
~lЩМа:ния ДЛЯ сборщика мусора. ПОСROЛЪНУ МВV-пшы ЯВ)1ЯЮТСЯ лоIщлbl1ыIщ КО­
IЩНМИ удaJreПНhlX объектов, 'процесс ВЫЗ0ва.клиевтоМ: членов соотвеТСТБующеГ0
пша, вообще "Говоря. не предполагает никакой сете-вой активности.
СЩ:дУ~Т ПОНИJl..ШТЬ. что вполне естественным ДЛ1I еервера ЯВJlЯетСfl поддержн.а
дoi..,"1)'II2t R множеС'тву МВR- и МВV-типов. вы можете танже ДОl'адаться, 'lТO MBR-
ТЩIbl обiiP'IRо поддерживают методы. возвра:щаЮ1ЦИе раЗЛИ<UIЫе МВV-пmы. ЧТО . в
~eM-TO. НiШоминаег эвтома'1'изироваffilое предn:pиятие. где од»Н оо-рект создает
~ Bы'ыкaeTT другиесв.язаНliые оБЪeIffЪ1. Здесь возникает следующ»й вопрос : Юllt
~рн(}>игурирова'J)Ь пользовательскИЙ тип }{Ласса для ИСn()ЛЪ~О1ЩНЮl в виде МER­
~ МВV-объе:кта?

Кон,фигурация МВV.. объекта


IlpoQecc RОНфиrypaц:Ии оБЪeR1)а для исnom.З0Вания в Bnдe' МВV"'тила абсоmoтf.CО
адалргичен процесGY канф.игурацим объекта ,lLТIЯ сериа.JI1fЗaц)lИ . Просто об'1>НВИте
соответствующий тип с атрибутом 1Se r: iaH z-abl e.) .
[Serializable]
puJ;,l.i с: cla.s s Sport·.'s C.ar
{, .. }

Конфигурация МВR-объекта
MBR-об'Ь'еR'JЪ! не маркируютС'я специальным атрибутом .NET. а получаются
(нвно ИJШ. не.явно] из базового K.l'[aCCa s уstеtп .MarsI1a l BYRefQbj ect.
р.uы l сc cl t1!1s Sр оrtэ С аr Fа с t о r у t нa.r4hal;ByRefObject
! ... )
Формально тип MaJ::shaJ.By&efObje o,t определяется с)те.цуюlIЩМ образом .

public abs tract cl a ssмarshaUyRefObject '~ Dbj e ct


{
pub.li t vlrt Иса l Obj~ f Create Obj Ref (Туре rеqu ез-t е dТS'ре ) !
риЬН с virtЦр l Ь о о1 Eqi.1a1S ( opj ec r. ob j );
p ub l io vi r·t ual i.nt Ge tl:las hCode ();

1
71 О Часть IV. Программирование с помощью библиотек .NET

public virtual object GetLifetimeService();


public Туре GetType();
public virtual object InitializeLifetimeService();
public virtual string ToString();

Функциональные возможности. наследуемые от System.Object. вполне понят­


ны. а роль остальных членов ОПИсана в табл. 18.2.

Таблица 18.2. Основные члены System.MarshalByRefObject

Член Описание

CreateObjRef () Создает объект, содержащий всю информацию, необходи­


мую для генерирования агента, который будет использо­
ваться для взаимодействия с удаленным объектом

GetLifetimeServices() Возвращает текущий сервис-объект, контролирующий по­


литику цикла существования для данного экземпляра

Ini tializeLifetimeServices () Ганерирует сервис-объект для контроля политики цикла


существования данного экземпляра

Можно с:казать, что суть типа MarshalByRefObject заключается в определении


членов, которые аатем МOIут переопределлться для того, чтобы программно управ­
лять циклом существования МВR-объекта (подробнее об управлении циклом суще­
ствования объектов будет говориться.в этой главе позже).

Замечание. То, что вы СКОНфигурировали тип в виде MBV- или МВR-объекта, совсем не означа­
ет, что зтот объект следует использовать только в приложении удаленного взаимодействия, а
означает только ТО, что этот объект можно использовать в таком приложении. Например, тип
System,Windows.Forms.Form является потомком MarshalByRefObject. Поэтому при
удаленном доступе он реализуется как МВR-тип, а в других случаях он будет обычным локаль­
ным обьектом в домене приложения клиента.

Замечание. Как следствие предыдущего замечания обратим внимание на то, что если тип .NEТ не
лредполагает сериализац~о и в его цепочке наследования нет MarshalByRefObject, то
такой тип может активизироваться и использоваться только в его исходном домене приложе­
ния, Т.е. такой тип является kohteKCTHO-Связанным (см. главу 13).

Теперь. когда вы четко понимаете суть различий между MBR- и МВV-типами.


давайте рассмотрим некоторые проблемы. специфичные для МВR-типов (к MBV-
типам это не относится).

Варианты активизации ДЛЯ МВR-типа: WKO и еАО


Еще одной проблемой выбора, возникающей перед вами, как программистом,
является принятие решения о том, когда следует активизировать МВR-объект и
/Согда этот объект должен стать кандидатом для участия в процедуре сборки му­
сора на сервере. На первый взгляд. такая постановка вопроса может показаться
странной. поскольку. очевидно. МВR-объекты должны создаваться тогда, когда
клиент их запрашивает, а уничтожаться тогда. когда клиент заканчивает работать

I
1...
Глав'с!. 1В . Уl1аnеН'fIое взаимодействие' .NП 711'
с НИМИ. Коnечно. именно :клиент предоставляет слою )"'дa.J(е'JUJО]'О вааимод.еЙстви.н
ивфар.мацюо о CBOe.'III желании вззнмоцействовю'Ь ~ уда:ленным ТИIIОМ, но В ответ
наaanpос н.лиента серверное ПРИЛQ1Кel-ше имеет возможность срздатъ С'Оответ'стВу,

"
ющnи ТИI1 не сразу.

Причина такого. казалось бы. CTP~HH0ГO ловедеffi!lJf1 СIЩЩUЩ' с о:пти:мизацией.


ТGЧНее. каящый МВR-тип можно н.ас:троить на qJ{ти.вЕ;зац1fЮ' с исruЩЬЗОШlнием
ОДНОГО из двух следУЮЩИЙ ПОдХОДО»:

• :как o(iще,и'3вестt!ЫЙ объavт (Well~Кnоwп Obj;~ct - WКO); ,

• шш. 06ъект. шcrив:ируемы:й IЩ:иеНТаМ (CJiel'lt ActiVJ.:ited Object- ею).

Замечание. ЛотеfЩl1aJJliffi>lМ ИСIO'If1ИКОМ недор~умений здесь l18ЛflетCJI то, 'пр в, лит~рiпуре, по-­
священной . NEТ, BMeuтo, акронима WКO также используют ЭАО (Server Activated' Qbject - объ­
ект, аКТИВ'и~руемы.й сервером). мрониМSАО ВСТРВ'Iзется в целом ряде статей и J<Ниr. 'О8язан­
~ЫX с .NET В этой г"щве" В соотвеТGТВYlИ с современной ' теРМ1о1нологией, будт ilfспОльзоваТЬСR
з6брев'иатураWКО.

WKO-объеlПЫ- :;JТQ МВR-ТИПЫ, ЦИifЛ .существования l".0TQpЫX ПОдlюнтролеа не­


посредственно домену при.ложения сервера. llриложение клиента активизирует
уд.аде.нныЙ ТИП. используя пщ,lfl1'цое оБЩtшзвеСТНое. cтpoltoвoe имя (отсюда и »03НШt
теpA1:Шl WКOj. ДОМеЕ ЦРИЛD:щешm сервера размещает WКO~'ГИПЬ1 тогда, I'югда 11:ЛИ·
ент ВQl:ЛOlIНяет первый 1ШI30В метода данного оБЪeIпа (через rrpoзрачnый a:reHT). а
ke moгда, 1Wща программцый :код:юniента ИСПОЛЬ3У'ет ключевое слово u'e w -или ~оща
вЫЗОв ПРО;ИСХОДИТ -чере,э стаТИ<lескиИ :метод. AL'"'ti 1Jator. G.etObj ec't (). например:

/I .DOЖIучеюсе а.гев'1'а. ЦnЯlУ,Jfa.nе1UlО:roо объеX'I!!I,.


I{ ЭТа С'l'pОIl:з' ,ite nproоДИ1l!' х и~д;nенкоиу СQЗiЦaюoD WKO-'l'ИJU/.!
БЬjесt remQt.€Ofi j = Ac.ti v-a to,r .GetOb je.c,t { /* 'парамt>,',Гры ..... / };

1/ ВЫЗОВ .ие!l!О'~ цa.nениаrо WКО-11'Иtiа. Эо310 I1рИзО;ЦИ1]! :к СО$,цaIOal


/I WK()-.об~Ж'rа и аJ.tзову мe!l'o,J;ta Re.turnмess&ge () .
RЭ f(юtеМе5 s аge ОЬjес t.. sim~й е = ( Rе rh о t еМ€s sаgеш:,j ес.t. ) r e!J)Q\;;e Ob ) ' ; ~on :;o l e .
Wr i tеLiпе (nСервер <Ртвеча е !L': 10 j ,n, s imple. F.eturnМe$sa,ge() ) i
в чем здесь зДpal'!ЫЙ CJl.lIЫ.C)I? При пщом подходе простое предложение ооэда'l'Ь
:объект не. :ведет R немедлелному J:IJЩy сетевого обме,lЩ даннымt1 . ДpyI'ИМ слtЩСТВii­
ем является Т,О, что WКQ-ТИПЫ могут СО~Jщ.)Щ.тьея moлько (: noмощ.ью KOнcтpylC'fl1.Qo­
ра. ,задiJ.f.lН.oго 1'10 умолч<жшо. это разумнй. DОСКОЛЫ'У fWHCJPYКTOP удадснног{) -гипа
ис-пользуется ТОЛЬJ{О ТОТДа. когда кдиеuт ВbIПолннет вызов ЧilIена. Так что средэ вы­
llОшreлия не и'меет НИ1ЩItо!Го ЮЮТО варщ:щ'ffi выбора, IqJ0Me вызова конструктора.
ззданнorо типом ПО Yh'ЮЛЧaIil'I.IO.

'3амечaииs. Всерда помните О том. 'fTO i(юбой WКQ·rиГ1 должеfj иметь KOHCTPV1<Т0P , зэданны.М ПО
УМОJP'jЩ'j ~ip!

Е~ли вы: .хОТИ'f,е разр.c:шnrгь R.'IиеН'Iy создавать удэледные МВR-объент:ы ,с помо­


щью rюльвопателы.'-lfЛГО консТруктора. сервер ДОЛЖ(ф СКО~фИГУРИJЮватъ соответ­
ствующий объект. шщ, САО-обrъf!к1': ЦиЮ1 сущеетвоWtlllЩ САО-объецтов КО;НТРОЛИ­
руется доменом npиложения ю'Шенw. При доступе J{ САО,тиду соответствующИй
обмен данными о серверОМ прОиtJroДИт у,же при иcnощ,ЗОБRЩlИ Rлиerrтoм JtJIЮчf:ВО­
Г9 <,;Лова new' (с любым КОНСТруктором ТIШЦ) WIИ тй11а Ас t. lv.,tCir,.
712 Часть IV. Программирование с помощью библиотек .NEТ

Варианты конфигурации WКО-типа: синглеты


и объекты одиночного вызова
Наконец. еще одна проблема выбора для МВR-типов в проекте .NEТ связана
с тем, как сервер должен обрабатьmать множественные обрarцения к WКО-типу .
С САО-типами эта проблема не вознинает. посколы<у для них всегда есть однознач ­
ное соответствие между I<Лиентом и удаленным САО-типом (эти типы являются
объектами. НУМУЛЯТИБНО изменяющими параметры своего состояния в процессе
выполнения вызовов I<ЛИентов)_
Одним из вариантов является конфиryрация WКО-тmш в виде СИН2леma. В этом
случае среда CLR создаст один ЭI<3eмllляр удаленного типа, который будет пршrn ­
мать запросы любого числа I<Лиентов. Этот вариант оказывается естественным
тогда, когда нужно поддерживать состояние типа. одинаковое для всех абонентов.
выполняющих удаленные вызовы_ Множество клиентов могут вызьшать один и тот
же метод в одно и то же время, поэтому среда CLR помещает каждый вызов кли­
ента в новый ПОток Однако обеспечение гарантий того. что ваши объекты будут
реентерабельны. является вашей обязанностью, и для этого следует использовать
подходы, описанные в главе 14.
В противоположность синглету, объек:m одиночного вызова- зто WКО-тип, су­
ществующий только в контексте вызова отдельного метода_ Поэтому. например,
если WКО-тип. сконфигурированный с учетом семантики одиночного вызова, ис­
пользуется 20 клиентами. то сервер создаст 20 отдельных объентов (по одному для
каждого клиента), и все эти объенты станут кандидатами для участия в процессе
сборки мусора сразу же после завершения вызова метода. Как вы можете догадать­
ся. объекты одиночното вызова. будучи объектами. не меняющими своего состоя­
ния. поддаются масштабированию лучше. чем синг.леты.
Задача определения конфиrypации состояния WКО-типа возлагается на сервер.
Программно указанные варианты задаются с помощью леречня System.Runtime.
Rеm о tiпg.WеllКпоwпОЬ j е с tМо dе .

pu blic епиm WellKnownObjectмode


{
S ing l e Ca ll,
Sin g let o n

Сводная характеристика MBR-о6аектов


Вы имели возможность убедиться в том. что для конфигурации MBV -объек­
тов долгих размышлений не потребуется: нужно просто применить атрибут
[ Sе r i а 1 i z а Ь 1е ] . чтобы позволить отправку копий соответствующего типа в до­
мен прИ". IOжения клиента. С этого момента все взаимодействие с МВV-типом про­
исходит В локальном окружении клиента. Когда клиент завершит использование
соответствующего МВV- типа. этот тип становится объектом внимания сборщика
мусора. и никаких проблем не возникает.
Но для МВR-тилов имеется целый ряд вариантов нонфиrypации. Вы видели, что
MBR-тИII допусн:ает варианты конфиryрации в отношении его времени активиза­
ЦИИ. состояния и управлеНJiЯ цимом существования. Чтобы представить весь на-

L
ГпаВ~1! 18. YAaI1B/Hi.oe' R~аимодеtlCтвие . NEТ 71 3
боримеl91ЦИХСЯ возможностей, ~ табл. 18..3 пOJtазано. как WКO- и CAO-dбъеI<ТЫ со­
отноСйТСЯ: С вариантами J;lО!;lедеЩi$J. "ROторыt>тол::ыro ЧТО бьmй нами рассмотрены.

Таблица, 18.3. Опции конфигурац",и ДЛЯ MBR·T""nOB


Характеристиха
Ловедение WКО-тиnа Поведение САО·тиnа
МВR"Gбъекта

Onциисо:щания WKO-ТIIIJ1blJ могут активизироваться только D едО-ТИПЫ' MOryт ак:rивизи­


экземпnяра' ПОМОЩЫО 1(онструктора. S3ДаНl-lого гю умол­ роввтъся с ПО"1ОЩЫО ЛlOбого
чаНllllO, КrlТОРЫ" зацускаеТС!1 nplII перВ'ом конструктора ТI'Iла.. УдaIТенныи
ВЫЭ'О8е Метода kЛиентом объект создаетс,Я TOFAa, Korдq
вь!зываIoщэяя CТOPOl'iil использу­
ет семантику K()f(CТPYKTopa (11i11'1
1WI kti\Таtэ::.-)
Упр~влеlt",е WКО-типы можно QКШlфИГУРИРОЩПЬ, КаК 'Цикл сущеС1'ВОВания САО·Т1I1па
соr:то'яl<tием GИЬirnеl ~mи объект {)Диночftого вызова. IЮНЧJол~руеТСR ВШЫВlЩlщей
Си,нтлет можэт оБСЛУж'liШЗтЬ множество стороной, пФэтому САО·lИП.!;>I
1U1JII8HTOE! и является объ.екroм, ~УМУЛ~Т1I1вно ЯВЛЯIOrСf1 объ.ектами, I(YMYM~ ­
измеl-lЯЮЩИМ параметры авоеТОСОСТQЯНIIIЯ ,ИНIiО изменяющими парв.ме:тры
I! nроцерсе вы\)лненияя аЫЗОВОl;Il(J]иантов , oвQ.ero СОСЛ)I1I'fi11R 8 процессе
Об-реп одиыoI110гоo вызова сущеотвует 8D1nОJlнеI1ИЯ ВыЗОВОВ kЛиентов
TOlltiKO в процесое двннотО I3bfэоеа l(I1и.еНта
и IjВJlяеТСI1 объектом, не меняющим своего
состоон.ия н процеос!>. выполнен'ИЯ

Упраtmение Для WКО-типов, ЯВJ1JI'I()ЩИ,i\\GЯ ()JJlнmеmми" ДlHI CAO-lVIпов используеlСR


циклом используется схема лизингового управления схема ЛIllЗИНГОВОГО УnРЭВIlеffi/\Я
существо BВI'I ж1 (которая буnет (}Пис.'!.на в ЭТОЙ таве позжеl. (КGТQрая 6YДST описана в З'fQИ
wко-типы. ЯВJ1.ЯIOЩиеI:Я оБЫJктами оди­ главе поз>te \
нфчното вызова., .окаЗblваются объектами'
вНИМЩi-ИЯдl1я сБОРЩика мусора G'РCiЗУ же
по завершении вt.lзова M~Q,qa'

И.нстаЛЛЯЦИJl приложе' нии, ИСПОnЬ. зующего


.,
удаленное вз:аимодеистви'е

Хватит ;;ЩРОНИМ9JJ! К э'],'~му момmлy ВЫ поч:rи ro:rOBbl R построению сноеАЬ пеlJl­


B01'Q .NET-приложeнI'JЯ, Щ~ПОJIЬаующего удапенное взаимодействие. Но перед тем.
}{ш{ ото сделать, :мы ДОШЩЦ,l обсудить Oro:ry' деталь: npoцедуру ИНСiаллнции. Лри
СQ~д:ании ПРИ:ЛОЖeflИЯ удалеl-ШОГD ВЗ'8ИМоде'йСТВREI ЖТ ВЫ. скорее всего. будете
и~етъ три (да, именд{) три, ~ не два) рааных:комIюRовоч.':R:ых блоха .NEI: СОL~ЛЛ­
ющих Baц:re IIрищлнение. Я уверен. Что лер:в.nre . дв.а компоновОЧНЫХ блока ВЫ СМО­
жет~ у!{арать CaмI'I.

• Клueн.m.. Этот nомnоновочньщ!3лок цредставляет' сущность (напрИмер, npи­


ложение' WlndQWs FomlS или :&о;аС{JЛ1:'FJ.Qе ЦРИJJОЖ1"ниеj, эаинтереGова'f!НYЮ в
tЮду"{eJ-um доступе к УДj'lJIЩI)iОМУ 06ъщ>:ту.

• Сервер. Этот компонroючньrй блок представmrет СУЩНОСТЬ .• щ)лучаюIЦyЮ


кав:альные заnРОСbl от уДаленцого клиеl'ца и 06сд)'ЖI:cm<ЩIщyIO удаленные
объекты.
714 Часть IV. Программирование с помощью библиотек .NET

Но к чему же тогда отнести третий компоновочный блок? Во многих случаях


приложение сервера обслуживает третий компоновочный блок. определяющий и
реализующий удаленные объекты. для удобства л бу1IY называть этот компоновоч­
ный блок общим компоновочным блоком. Такое разделение компоновочного бло­
ка. содержащего удаленные объекты, и хоста сервера оказывается очень важным,
ПОСIЮЛЬКУ компоновочные блоки и клиента. и сервера устанавливают ссылки на
общий компоновочный блок, чтобы получить метаданные типов. допускающих
удаленный доступ.
В простейшем случае общий компоновочный блок размещается в каталогах при­
ложений и клиента, и сервера. Потенциальным недостатком такого подхода явля­
ется то, что клиент ссылается на компоновочный блок, содержащий программный
СIL-код, который никогда не используется (и соответствующий программный код
нельзя будет скрыть от конечного пользователя). Чтобы устранить этот недостаток.
заметим. что общий компоновочный блок нужен клиенту только для получения ме­
таданных удаленных типов. Но ЭТО можно обеспечить и следУЮЩИМИ способами.

• Сконструировать удаленные объекты с применением программных техно­


логий, использующих интерфейсы. В этом случае клиент может установить
ссылку на двоичный блOI< .NEТ. содержащий ТОЛЬRО определения соответству­
ющих интерфейсов, и ничего более.

• Использовать приложение командной строки s o apsuds. exe . С помощью этого


инструмента можно сгенерировать компоновочный блок, содержащий ТОЛЬко
метаданные удаленных типов ,

• Вручную построить компоновочный блок. содержащий ТОЛЬRО метаданные


удаленных типов.

Тем не менее, чтобы упростить изложение материала этой главы, мы с вами по·
строим и YCT8.l-Ю'вим общие компоновочные блоки. содержащие как необходимые
метаданные. так и ClL-код реализации.

Замечание. Чтобы выяснить , как реализовать общие компоновочные блоки в рамках указанных
выше альтернативных подходов, прочитайте книгу Тот 8агпаЬу, Distributed .NET Programming
in С# (Apress, 2002).

Создание распределенного приложения


Ничто не принесет большей радости. чем создание реального распределенно­
го приложения на новой платформе . Чтобы ПОRазать, как быстро можно создать и
запустить приложение. использующее слой удаленного взаимодействия .NEТ. мы
построим простой пример таного приложения. Выше уже отмечалось, что такое
приложение должно состоять из трех КОМПОНОВОЧНЫХ блоков .NEТ:

• общий компоновочный блок с именем SimpleRemo tingAsm.dl1;


• компоновочный блок клиента с именем SimpleRemo teObj e ctClient.exe;
• компоновочный блок сервера с именем SimpleRemo teOb j ectServer.exe.

l
Глаед 18. УдаJlеЮWEl взаимодействие .NE1 715

Создание общего компоновочного блока


CHa<Ja1ta ()03~ общ~й "Компоновочный блок, S.impleRerr>.C'tingAsm.dll. на
KOTOPЫ~ б-у.дУТ 0С'blJЩТЬСН"Как сервер, таи :и клиент. В SimpleRerno,ti n.gAsm. dll
сщреДf:JЩIется еДШiствещrьnl :МВК. ТИn с именем RernoteMessageObj-есt. который
ПОДlI.еРЖИВ~I;Т два QТ1tpl;>IlЪ1X qлена.. MeTQA DlщйауМеssаgе () ВЫВОДИТ В ОКНО КОН­
СОJ1й cepBep~ ПОСТa.вщI~Мt)е клиmrroм со:общевие. а ReturnMessage (.) возвращает
некоторое соо6щеш~е :клдев'ry. Вот полный программный код этой новой оиБД!iti>­
теки :классов С#.

namезраее Bimple·R emot ir'ig/\srn


[
11 д.пи ЭФОЖ'О 't'ИП& при у,цапенкох д.ОСо:1'упе
J/ будEКi ИQЦOJЦoзоаз-o:rъся иарllllUIlЩZ' по сашке (МВR) ..
pl1bl ic' clas.s Р.ernо ·t!3МеэsаgеQЬj E-\;t: Мa:rsh!L1Qy_fDbject:
1
pl:it)lic RemoteМessag'eOЬject ()
{ Consol е. Wri teLine ("(;оздаi!ие Remoteffl;jssa'geO,bj ес1;.! '.); }

I/ Эо:1'~ М8!!!о,ц Щ)J1учае1l! ахо,цИ)"JD С'1'року


If 00:1' lW8ыв·. . . .й Со:1'орoИы.•
publi~' void DisрlауМеэsаgе(string m~g)
j Co.h.sol е .lYri·teLin€ (" ' Соо~щеНl'1е: {01" I msg);}'

1/ З!1'(>.о:1' нешо,ц .оа:враЩ8fИ1 sвачение 8WSIoI8aщefr Со:1'QpQие.


publicstrinq ReturdMessage О
l ret utл II'ЦРИJ!I'е.1Г ОТ cepsepa!" i I

Наиболее июересю.rм здесь является то., ЧТ9 соответствующий тип получает­


ся из базового класса Sуstеm.'Мю;·shа]J3УRеfОЬjес;:t. в ре~УЛЬ1'ате'ЧегD палучеНlIЫЙ
:класс будет tаранти.рованно ДОС"'!у1ТЦЫl',f с ПОМОЩ~ агента на стороне I<ЛИtrnта.
Таюке обратите внимаиие ца ПОДЪЭ9вателъски:й Ba,pl'I~ R{}пстрyкroра. заданного
по }'1';lОЛЧaRИЮ. которью печатаетсщ)5щение пр~ СОЗД~ экземпляра ТШ1Э.. вот
.Н все. Теперь можете ещщатъ Н0ВЫЙ JЦ)МIJDНОБ~ЧНЪJЙ РДОК Simpl eRem.otingAsm .cdll
.на базе ЭТШ'() ПРОгрffil[М)'{ого }!:ода.

Соэдание компоновоqного блока серв.ера


Напомним; что ~ОМПОНОВО"дlblе блоки сервера обсд~JВaIOl", в частности, и об­
щие IwмnанОВОЧ1I:ые блоки. соnертЗ1ЦИе объекты удаленного ДОС'IYПа. Создайте
КОRсолыrую npограмму с им:енем Si~pleRemoteObj~~~Server . Роль сер.верно­
го КОМПОНОВОЧНОГО блока заключается в том, <Jтобы OTKpliгFъ канал дли посIJY"
n:a.ющих gапросов и ':зарегистрUРОJlаTh RещоtеМеsэ ageObj ect·. iшК "WКQ-объект.
Сначала сошлитесь на КОМПОНОВОчные блоки Sузtеrn. 'Ruлti.rnе.Rеmоting.dll и
SimpleRemoti ngAsm. dll ц обновите ма ir, () T~. шш ПР~дщliГается ниже .

'lIsing sуэt:еm;
Jlsiл g sузtеrn. Rt1Лtlmе. Remot ing;
using Sj'э .tеm. Runt ime . Remoti:ng • Channel s;
uS2ng Sys,t em. Rtmtime. Remoti.ng. Channel s. Ht tp;
usinqS1щр~eRemеtingAsm;

1
716 Часть IV. Программирование с помощью библиотек .NET

namespace SimpleRemoteObjectServer
(
class SimpleObjServer
{
static void Маiл(striпg[] args)
(
Conso:J..e.WriteLine("*** Нiiчало работы SimpleRemoteObjectServer! ""*");
Со nsol е. Wr i tel,ine ( "Для завершения нажмите <Еп te с>" ) ;

/ / РеГИС'1'рёЩИR HOBoro HttpCh&nnel


HttpChannel с = new НttрСhаппеl(З2469);
ChannelServices.RegisterChannel(c) ;

/I РеГИС'1'р&ЦИR WКО-'1'Иn& с &1C'l'ИЗ&цией сингле'1'&.


RemotingConfiguration.RegisterWell~nownServiceType(
typeof(SimpleRemotingAsm.RemoteMessageObject),
"RemoteMsgObj.soap",
WеllКпоwпОЬjесtМоdе.Siпglеtоп) ;
Console,ReadLine();

Метод Main () начинается с создания нового типа HttpChannel. для которого ука­
зан произвольный идентификатор порта. Этот порт открывается путем регистра­
ции канала с помощью статического метода CbannelServices.RegisterCbannel().
После регистрации канала компоновочный блок удаленного сервера может обраба­
тывать сообщения, поступающие через порт с номером 32469.

Замечание. Номер, который вы назначите порту, как правило, выбираете вы сами (или ваш си­
стемный администратор). При атом , однако, следует учитывать то, что порты с номерами ниже
1024 резервируюrся для использования системой.

Затем, чтобы зарегистрировать тип SirnрlеRеrrюtiпgАsm.RеmоtеМеssаgеОЬjесt


в качестве WКО-типа, используется метод RemotingConfiguration.Registerwell-
KnownServ iceType (). Первым аргументом этого метода является информации
типа для регистрируемого типа. Вторым параметром RegisterWellKnownService-
Туре () является произвольная выбранная вами строка. которая будет использо­
ваться для идентификации регистрируемого объекта при обмене данными меж­
lIY доменами приложений. Здесь вы информируете cpelIY CLR о том, что данный
объект должен распознаватъся клиентом по имени RemoteMsgObj .воар.
Заключительным параметром является член перечня WellKnownObjectMode, и
ДЛЯ него здесь указано WellKnownObjectMode. Singleton. Напомним. что при ис­
пользовании WКО-синглета все поступающие запросы обслуживаются одним ЭК­
земпляром RemoteMessageObject. Создайте компоновочный блок сервера и пере­
ходите к созданию программного кода клиента.

Создание компоновочного блока клиента


Теперь, когда у вас есть приемник, который будет обслуживать объекты уда­
ленного доступа, остается создать компоновочный блок, который запросит

L
Глава 1.8, Удаленное 6~аИМDд~йств~е .NET 717
дnстyn ж СООPlетс~щPJМ ВОЗМОЖНОСТЯМ. Здесь снова создайте ПpQстое кен­
СО.i1мюе придожеии~. Установите ссЬi.1Щy на ьу'9 tem. Riшt iro е . Rero~t i n: g. dll и
SimpleRemo tirig AsТfI. dll. -Реализуйте мз ifl О так. :кав nо:казако 1iиже.

1is i ng Sys t -em;


tJsi ng System. R;uh'c i '!rle. Remбt i!1'g J
using t3'Y's 1:·am. Ruп·t ше •F.e.rn0ting . Charinel s ;
l'1s.Lng Sуstею. Runtime . p.e11)c"t i.nq:[, С'hагшеl s. Ht tp,;
usiпg S!mpJ.eRemof:ing~;

паrnезраСе $ iroрlеr<еffiбtеОЬj ectClient


[
c l ass SimpleQbjCliert t
t
s"tatic void Ма.i!'J.(.эtсing П a::rgg)

COI1'Sole. Wri teLin€- ( ..,.... ~ На-чало раСю ты Si mp l eRemot~Obj ес tС liелt I " . .. n);
СопS.оl е. Wri teLine.( "для завертеlilo1l'! .нажми-:rе <Er,ter>") ;
11 CosAA~e ИО&О1'О HttpChannel.
Н ltрСhаf.юеl с = new l!.t :cp·Cha!lLl>=l 'О;
Ch ani:\elServi ce's .• Regl!'/terthanl'1~l (с .) ;

11 ПЩl;v ...иие arеит& ~ЯI удanениorо AOC'1'3m& J: wк'Ф-WИ%II}'.


abject remQteObj = ActivatQr.GetObject (
typ6 0f I SimрlеReфоt:i Оgf\>эm , RЕ'<nwt.eМеs.sаgеОЬ:;1есt) ,
"h:ttp: !/10'СаНюаt: 324б9!Rе:mоtеМsgОЬj . soap") :
11 Иcnолъ'sоааиие уда.ле!U!~ оБЫХ!1'а.
РiЩюtеме\'lsаgеQЬjесt simple =
(RelIloleМeвs.ageOby~:c .t) r 'e m.ote Obj ;
simpl ~. Di splayMessage ("при:е:~'I' ОТ 'КJ1иенrа! ");
сопsоIе. wr 1t:eLir.e( "Сервер ГQВОР И Т: ! О } ",
simpl'e. Retuxn'Message !) );
Cdnsol'a. ReadL.:!:ne ! ) ;

1
в ЭТОМ ПРИЛrui~еmm ЮIиента o(\)pa'Т~Te внимание на следующее. Во·первых., кдц­
ею таюке ДОлжен зарегистр"Щ)овать НТГР- Rанал.. но идентификатор порта ПРИiЭТОМ
не указьшается.. поскот.ку ROНечнан тОчна Rанала ЗаДается адРесом U:RL активи­
зации. аостэвля:еМ1;IМ :клnеFi'rОМ. Пос1ЮЦЪRy клиент взаимодействует с WKO-l'ИПОМ.
ВЫ должны ЗЕТ.ющзировать коЪЩтр~ТQР типа, заданный по умолчанию . С этой
целью .вызьtвается метод f\.cti<rator_GetObjeQt () .с двумя парам'етрами. Пер:аьц.f
параметром ЩlJLч:етсц ин:форма:ци.я цrnа yдauешroго объllli1"Э, с :которым вы хотите
"ВэаимодеЙе'Гnо~аТD. ПрОЧИТ8Й1'е цо~леднее предложение еще: раз. nOCItMbJ<y' здесь
метод ]"c,tiva.tot .GetObject () требует M~aд"cIнныe onиtания объеl<Та. станОВiJтел
'пено. nочемудщi клиента rnaк.ж;е трфуетс.я ССЫJШа Н'а общий компоновочный б(JЮК!
В Rol-Щ't' r.lIaвbl БУАУ1' раеСМОТрt:НЩ' различные возможности соверmеНС1'lщващiЯ по­
веделИS1 :компоновочного блока мцента Б ЭТОМ оТн.ошении .

1
718 Часть IV. Программирование с помощью библиотек .NET

Второй параметр метода Activator .GetObjec t () представляет собой URL aкmи­


вuзaцutL Значение URL антивизации. описывающее WКО-тип, можно представить
в следующем обобщенном формате.

Сх е маПрот ок ола: //ИмяК о мпьютер а : По рт /Ur i Об ъект а

Нанонец. заметИlV\, что метод Activ at o r. Ge t Ob j e ct () возвращает общий тип


System.Objec t. поэтому для получения доступа I{ членам RemoteMessageObject
необходимо использовать явное преобразование типа.

Тестирование приложения, использующего


удаленное взаимодействие
При тестировании приложения начните с запуска серверного приложения, ко­
торое откроет Н1ТР-канал и зарегистрирует объект RemoteMessageObject для уда­
ленного доступа. Затем запустите экземпляр приложения клиента. Если все прой­
дет хорошо. окно вашего сервера должно иметь вид. показанный на рис . 18.2. а
приложение клиента должно отображать то. что вы видите на рис. 18.3.

Рис. 18.2. ВЫВОД сервера

Рис. 18.3. ВЫВОД клиента

Тип ChannelServices
Итан. объявляя существование удаленного типа. сервер использует тип System.
Runt ime .Remoting .Channel s . ChannelServic es. ТИп ChannelServices предлагает
небольшой набор статических методов. призванных обеспечить содействие в про­
цессе регистрации канала удаленного взаимодействия и обнаружения указанного
URL. Thaвиые члены данного типа описаны в табл. 18.4.
Вдобавок к методам Regi s terChannel () и Un registerChannel () с Щ ясНblМJl
названиями. тип Ch a n nelServices определяет свойство RegisteredChannels.
Этот член возвращает массив интерфейсов IChanoel, каждый из которых пред­
ставляет дескриптор соответствующего канала из тех. которые зарегистрированы

в данном домене приложения .


ГЛQва 18, УдаЛ8ННО.8 ~заИМЦАеЙdТВ~В.NЕТ 719
Таблица 18,4. Подборка членов типа Cha-nnеlSеrviсеs

Члеu Оп:м(;ание

RеgiзtеrеdСhаnлеlэ CBO~CТB4), -получающее WlИУl;:таНi!ВJМВающее CI1It1CDK за,регистриро­


ванных В наСТОIН!tИЙ' момент кзиалов, каждыЙ из которых предcraвля~
ется интерфейсом IChannel

Di spa tchMess age() Метод, выполняющий обработку цостуnающих удаленнbIX Вi!lЗОВОR

GetChanne l ( ) Метод, вООВРiiIЦalC>щИй звреmcТРИРОВaflМЫй канал с указанщ,/М 'именем


Gе Щr1sFо rОЬ j есt () Метод, возврвща!Рщltlй массив адресов URL, KDТopыe Moryf \ifСПОЛI)ЭО­
ВвтЪС!1 ДI!~ доступа к указанному Объекту

R~ gisterC hanI:\ e l (') Метод, регис,-р}1РУЮЩИЙ канал о СОlJтвеч:цryющими JI.I1наль~ ~p­


ВlllсаМVI

Uп:r:еgistcеr Сhаnпеl (J Метд,· Dтменяющltlй регистрацию ДOHHora кан.ала 1'1 У@ЛЯЮЩl1И ЭТОТ
канал .из ОllИ~1(8 зарегистриРованныХ

Определение. ИН"teрфейса lСЬаоnеl 01\'вэываетс.я ИС.Н.1IЮЧИ,reльно простым.

pli1bl1"C i n t e rfaC'8 Ic,h,am:l~l


{
st:й ng Сh€lПl1 еl Nаmе { g~ t ; }
int C'hannelPriority { get.i }
~ tr:i D'g p az se (э triиg u:rl, ref St r: i:ng Qbj E'.\1; H1RI) ;

Нап видите. lЩЖДЫЙ ханвд nолучв.ет попятное СТРОJшвое ИМЯ. а ТЩPRе СВО:Й
уровень приоритетС!. Например, ~СДИ; добавить в метод Ма i n () приложе»ия
Si mplёRёmоtеОЬjесt<;:liеnt слеДУIQIЦУ19 прогрaммнyIO, логику

/I СцисоllC всех зар~овaиНirx жа.ВЗJl08.


J C11annel [] bhannelOb)& = Chann.€ "l,Servi cgs , Re gi s.tе.rеdСЬanл р 15;
fOT lё!bl::'b (ICh anne ;L i i n cbanrll'!lObjs}
!
С0пsоlе. Wr i t.е.L.iле ("ИМЯ ка:Щiла : [О}", :L. СhаrшеlNaIrrе) ;
CQr,s .o l e . WriteLine ("ЛРИQр=ет: {О J ", i . Ch"ш пеl Рriо6 ·t;f) ;

ТО в OIте коJiСQЛИ ltЛИ~llта ВЫ увидите вывод. ПОДОQ~ покааанном:у .на JlИС. 18.4.

Рис. 18.4, СпиtdК каналов 8 окне 'клиента

1
720 Часть IV. Программирование с помощью библиотек .NET
г
Тип RemotingConfiguration
другим IUIЮчевым типом удаленного взаимодействия является тип Remoting-
Configuration, который, в соответствии со своим названием, использует­
ся для настройки различных параметров nриложения удаленного взаимодей­
ствия. Бы уже видели этот тип в работе на стороне сервера (при вызове метода
RegisterWellKnownServiceType ()). Другие заслуживающие внимания статиче­
ские члены этого типа описываются в табл. 18.5. а возможности применения неко­
торых из этих членов будут продемонстрированы в оставшейся части этой главы.

Таблица 18.5. Члены типа RemotingConfiguration

Член Описание

Appli c atio nld Возвращает идентификатор приложения, вы­


полняющегося в настоящий момент

Арр i iсаti ол Nаmе Возвращает или устанавливает имя приложе­


ния удаленного взаимодействия

Proce s sld Возвращает идентификатор процесса, выпол­


няющегося в настоящий момент

Configure () Читает файл конфигурации и устанавливает


параметры КОНфигурации инфраструктуры уда­
ленного взаимодействия

GetRegistere dActiv ate dClient Type s () Возвращает массив объектных типов, зареги­
стрированных на стороне клиента для удален­
ной активизации

GetRegisteredAc t ivated ServiceTypes () Возвращает массив объектных типов , зареги­


стрированных на стороне сервиса для активи­

зации по запросу клиента

GetReg is teredWellKnownClien t Ty pes () Возвращает массив объектных типов , зареги­


стрированных на стороне клиента в качестве

WКО-ТИПОВ

GetRegisteredWellKnownServic eTypes () Возвращает массив объектных типов, зареги­


стрированных на стороне сеРВИСil в качестве

WКО-типов

IsWellKnownCl ie ntType () Проверяет, является ли указанный объектный


тип зарегистрированным WКО-типом клиента

Re g isterAc t i vatedClientType () Регистрирует объект на стороне клиента как


тип, позволяющий активизацию на сервере

RegisterWellKnownClientType () Регистрирует объект на стороне клиента как


WКО-тип (синглет или объект одиночного вызова)

RegisterWellKnownServiceType () Регистрирует объект на стороне сервиса как


WКО-тип (синтет или объект одиночного вызова)

Наrтомним, что слой удаленного взаимодействия .NET различает два вида


МБR - объектов: WKO (активизируются сервером) и САО (активизируются кли­
ентом). К тому же, WКО -тип может быть активизирован либо нак синглет, либо
,
,

Глава 1.8. Удалеflffое 8З!lицодейотТ:JН!! . NEТ 72.1


как объект ОЩlНОЧНQГО ВЫЗ(?Jва. используя ФУНlillнона.лъные IЮ3МОЖНОСТИ т.ипа
RemotingConfig'\lriit ion, вы можете динами-qески подучить такую информа­
ЦВ!Q в среде нъrполнeJШЯ. Например, если добавить В· метод Mai n. () rrpилОЖeнIiЯ
SimрlеRеnюtеОЬjееtSеrvеr следУЮщие строки i1'pогрaюmщ'о кода:

stiitic v.oid Мain {st:ti:nq [] args)


[

1/ УС!1'Ь01iиа RоИЯll1S0Ж'Q _.ии ,ци,. ,qаикого прИ'зюX8IIИR сера ера •


Rе.mо.tiД9СодiigurоаtiоtJ , AppH-еаtiш.iNаmе =
"jJS:f:jBoe oepBep;I;oe п-риложе-ние";
Сa.nSQlе . Writе.Linе("·ИМЯ npи,ложеЕИ'Я': {()J",
Re.! fioti.rrgCc.nf1guratiOD. Appl icatidnN.ame) ;
j1 ПОnV'iениt! _c.CюIii 'DШОJl WellKnowttService'l'ypeE-ntry,
11 предrJl1!iUШRDЦМx .з&РIIX'Кс:~ироJlalJlO18 WKQ-О&5еJC'1'J,t'.
WellKnown:ServiceTypeEn tr,y [] WK05 =
Remоti·л.gСопfigu~аti.оn . G е tRе9'iSt е rефilеllКпоwn&е:t .v iс е'1.' уреs () ;

11 lWвo~ ИВформацмн.
foreca.cb. (Wel1Кr1:ownS'erviceTypeEntry wko in Vi\YOs)
j
Совsо1 е. Wri tеLinэ {"J-f...мJ1l ~лока, содерж",щего WKO: {О}" I
wko.AssemblyName);
.cons,o.le .WriteLin.e ("'URL да}1НО го W!<(J\J (О f", 'WKQ·.Object..!J'J:1 );
.Cor,sale ..Wr,i,teLine ( ."ТUr1 W!<O: [О)", wko.ObjectType);
Сь!') sole , .>'11' i t.eLiIH:~ ( "Р.е!ЖИм активизап.ии WKO: (().I "., w.k a. MQde);

ТО вы ДОI1lЖНы. У'}Jидеть сцисок всех WКО-типов . зареrИС'rрдрii>в.аиных доме­


ном приложения cepJ3epa. В·blПDЛllИВ ЦИКЛ по в-с-ем элем.щrта.м массива типов
Wейi:КпОWn$еrViС'еТуреЕntrу, .можно выяснить хараъ;n:PИС"ГJIЮJ каждого из wкo­
объектов- . ПФсколъну вЩпе- серверное прйложение рerистр-ирует ТОЛЬКО ОДИН 'l'ИП
(SimpleRemotir1gAsm,RemoteMessageObject). вы ПО)Jучите вывод, локаза1:lный на
рис.18.5.
Друтим ва.жныr,{ МС'I1рдом типа Re mo·t..i п g С оп f 1: g u х-а t . i 011 srвляется ме'tод
tоп.fiq.цrе ~ ). ~сиоре вы сможете убедитьея. что Э'rот стат.ич~lШЙ член IlD3:ВОЛЯе1:'
доменам прщюже}ЩЙ IWИент.а и сервера ИСПOJIъ-зоnать файлы щэнфиiypа.ции уда­
)]енноrо вэаимоде:Йст~ця..

I
i

Рис. 18.5. Стаtистика сервера


rI
,
722 Часть IV. Программирование с помоЩЬЮ библиотек .NET
I,
Снова о режиме активизации WКО-типов
Напомним, что WКО-типы можно настроить для работы либо в режиме сингле­
та. либо в режиме объекта одиночного вызова. В настоящее время ваше сервер­
ное приложение регистрирует WКО-тиn с использованием семантики активизации
синглета.

// Сингnеты могут обcnуживать множество клиентов.


Remoti ngC onfiguration.RegisterWellKnownServiceType(
typeof(S:impleRemotingAsm.RemoteMessageObject),
"RеrrюtеМ sgОЬj. soap",
WellКnownObjectМode.Singleton);

Снова обратим ВlШМание на то, что WКО-синглеты могут получать запросы от


множества клиентов. поэтому cинrnеты связаны с удаленными клиентами отноше­
нием ·один ко множеству". Чтобы проверить это непосредственно. запустите при­
ложение сервера (если оно в настоящий момент еще не ВЪШQЛНЯется) и три отдель­
НЬ!Х приложения l\Лиента. Если посмотреть на вывод сервера. вы обнаружите там
толыш один вызов заданного по умолчанию консТРУКТора RemoteMessageObj ect.
Чтобы рассмотреть поведение объектов одиночного вызова. измените сервер­
ное приложение так , чтобы в нем регистрировался WКO-объект. поддерживающий
активизацию одиночного вызова.

/ / WKO-тИIW одиночного вызова саsзаиы с XJIиентом


/ / отношением "один JC одноиу".
Remot ingCo nfiguration.Regis t erWellKnownServiceType(
typeof(SimpleRemotingAsm.RemoteMessageObject),
"Re mo teMsgObj .soap",
WellКnownObjectмode.SingleCal1) i

После перекомпиляции и запуска серверного приложения снова запустите три


l\Лиента . На этот раз вы увидите, что для каждого запроса клиента будет создан
новый Remo teMessageObject . Итак, если вы хотите сделать данные состояния об­
щими ДЛЯ множества удаленных клиентов. то активизация синглета оказьmается

единственным подходящим вариантом, поскольку тогда все клиенты "общаются" с


единственным экземшrя:pом удаленного объекта.

Исходный код. Проекты SimpleRemotingAsm, SimpleRemoteObjectServer и SimpleRemoteObjectClient


размещены в подкаталоге , соответствующем главе 18.

Установка сервера на удаленной машине


к этому моменту вы реализовали ВОЗМОЖI-IOСТЬ пересечения границ приложения
и процесс а на одной машине. Если у вас есть возможность связи с другой маши­
ной. вы можете расширить свой пример так, чтобы клиент мог взаимодействовать
с типом RernoteMessageObject через границы машин. ,для этого необходимо сде­
лать следующее.

ь-
ThЗ.ВQ 18. УдалеЮ10е ваакмодеЩ:Т8ие .NEТ 723
1. На М8'f!IИНe сервера создайте и о:rkpОЙ're для дос'ryIm пшmy.1J которой буду!'
Содержатьс.и кaмnОНODоч'ные блоки серверной С'ООJЮНЫ.

2. скоди:рyiiте ROмпопов(j~е блови SimpleRembteObjec.tS·arver .ехе и


!5imрlеR~пюtingl!sm.dll в эту IiaiIкy.

З. Отltройте прое~ SimpleRemoteObj ее eel iелt и. измените URL активиэзцnи в


соответстции с именем удаленной мanщны, например:

11 ПOJJучеJi:йе ar€I;\'l'a дJЛЯ удаленноро объекта.


object remoteOhJ = Acti va.tO:i::. Ge~ObJect (
tУР~Оf(SiЩр'lеRеmоtif\9'Аsm.Rеroоtем~ssqg€ОЬjеl,;t) ,
"ht:t:p~ IIИмrlУ.цanеиноiiМalllJOGl; З24БЭ /RemoteMsgObj ,эоар"1;

4, 3апустиrе приложени~ Simple'RemoteObjectSenтer.exe на машине сервера.

5. Эanyс.-mтe приложение SiщрlеI{еm.оtеОЬjэсtСЦеnt .ехе на ма.шИне клиента.

6. Orюmьтесь на с.IIШlRy кресла. расслабьте~ъ иу1Iыбитеq. .

Замечание. Вместо n.онятного имеЮ1 Машины URL аКТ~l!иза1Ш1II ~ожет ук.аЗl;oIвать ее IP.eдpec.

Использование ТCP~1(aHanOB
в настоящий момент ваш УД3JIffiiНЫЙ объект ДО(,'ТJIIеи череэсеТt'I!QЙ ПрО'I'О:КОЛ
Н1ТР. Как уже ynоминалось выше, ЭТОТ ПРОТОВ;QЛ впошre COBмeOT~ с бранДМауэ­
ром. но .-еверируемые при э.1'ом: пакеты ЭОАР H~OГO QраздY'FJ>1'~ (по причине ·пред~
ста:в.ilеНШi данных в форма-rе XМL). ч.ТQбы уменьшить сетево;й трафmc, ЫОЖНО из­
менит1:. КO:МnОffовочные блоки клиelt-rа и сервера так, чтобы в юп ~СJ;I~ЛЬЗОВался
1СР·1tанал и. следова:ге.лы-ю,nш BinaryForma'tter, Бот подход.mцan модифmaщии
компоновочного БЛOI11а с.ервера.

Звмечание, Для файJюв с ОПр.е,целениями объехтов, .ДОС1Упных П.О ТСР-каналам о Эaд8J'lным URI,
чаще всего (1tQ не обязательно) иqщmьзуетЬя рааширение "i<. retn (от remote -удаленный}.

11 к~ахIJ:lИPО.- д,пlr ofllpaepa.


usinq sy.stев.· Run,tiшe. Reшotinq. СЫшnе1а . Тер:

static yoid Ma:i.p (stringT J a:rgs )


"{

11 C08~e ~oвoro Тс;рСЬannel


= new T.c pChaf\nell 32 4 б g).;
ТсрСhal'1пеl е
ChannelServiceS·, RegisterChahhel (:е);

11 Р8%lИaozpaцJcUl~-О~'1'8 • ре-.е ОИВ1"nе,а.


RemetingConfigura:,t i оп. Rеgiз terWellKtlOW11Setvi сетуре (
tyPeo'f (Simpl eRemotif\:g:Asm. Remot еМеэsа geGbj ec;tJ ,
"RQoteМsgO])j. rвm" ,
W€llКhOWnObjectMQd.e. SiлglеСаll);
COP$ole. Rear::lLine О ;
724 Часть IV. Программироsание с помощью библиотек .NET

Здесь в слое удаленного взаимодействия .NEТ регистрируется тип System.


Runt ime. Remoting .Channels. Тер. TepChannel. Кроме того, изменен URI-объект
(теперь для него задано более общее имя RemoteMsgObj. тет вместо *. эоар, что
явно указывало на использование. SOAP). Модификация приложения клиента тан
же проста.

/ / I(орре1l:'1'ИРОIlICИ ,цп. JCJ1иеК'1'а.


u8inq System.Runtime.Remotinq.Channels.Tcp;

static void Маiл(striпg[] args)


{

// Создание Ho.oro TcpChannel


TepChannel е = new TcpChannel():
ChannelServices.RegisterChannel (c) ;
/ / По.nучение areH'1'a ,цп. удалениоrо оБIJoe1l:'1'а.
object remoteObj = Activa t or.GetObject(
typeof(SimpleRemotingAsm.RemoteMessageObject),
"tcp; //lосаlhОllt:Э2469/RemоtвМsqOЬj .rem") ;
// Иcnо.nьзо.ание об~еХ'1'а.
Remo teMessageObjeet simple = (RemoteMess a geObject )remoteObj;
simрlе.DisрlауМеssаgе(" Привет от клиента!");
Co ns o le. Wri teLine ("Сервер г о в о рит: {О)", s imple. ReturnMessage () ) ;
Cons o le.Read Line();

Единственным заслуживающим внимания моментом здесь является то, что


URL активизации RЛИента теперь должен содержать признак канала t ер: 1/, а не
http:// . Во всем остальном программная логика здесь оказывается идентичной
программной логике HttpChannel.

Исходный код. ПроеКТbI TCPSimpleRemoteObjectServer и TCPSimpleAemoteObjectClient размещены


s подкаталоге, соответствующем главе 18 (оба эти проекта используют созданный выше ком­
ПОНОSO'iный блок SimpleRemotlngAsm.dll).

Несколько слов о IpcChannel


Перед тем как перейти к обсуждению файлов Rонфиrypации удаленного взаи­
модействия. напомним, что .NEТ 2.0 пред.пагает тип IpcChannel, обеспечивающий
самый быстрый из возможных способов взаимодействия приложений на одн.ой ма­
шине. Задачей данной главы является изучение возможностей построения распре­
деленных приложениЙ. выполняемых не на одном, а на множестве компьютеров.
Поэтому по поводу использования IpcChannel обратитесь к документации .NEТ
Framework 2.0 SDK (как и следует ожидать, соответствующий программный код бу­
дет почти цдентичен программному коду, необходимому для работы с HttpChannel
и TcpChann e l).
Глава 18. 'Удаленное вэаимодеЙСiвие .f\JEТ 725

Файлы конфигурации удаленного


, ....
взаимодеиствия

И~.:ВЫ успе:шно построилираспределе:ШlDe приложе:ние. используя СЛОЙ уд.а­


ленного взаимодействия .NET. В связи сданными npимерами следует обратить
ВНИlI>щн;ие на то. что лолуч.енные прило.жеНия клиента и сервера содержат боль­
шой объем Кжестко~ кодируемой программной лапШИ. Напрuмер. сервер указы­
B!leт фИRспровавнъп'i .идеm:ифинатор лорта. фиксированный режим шcrивиаащщ
J;I фиКСИрОванный тип .канап:а. Клиент. С дpyrой стороны. "жесntО" 'l\oДl<!pyeT И!\/Щ
удалепнdгф' оБЪelrТ.а. с которым пытаe'tCя вэamюдеЙствовать.
ООТЛOCИJ'есь, сJIl'lI.IШ,ОМ .наивно преДIIОШ'i!Oать. что. исходные па.раметры npoeKra
смогут оставаться неИзменными при люБDК инсталл:m.rии: приложения. В цдеаде
такие параметры. каЕ номер порта. режим акТйDftзации и другие подобные эле­
менты. ДОЛЖНЫ lЮЭВОЛЯТЪ дин&Мич.е.окое изменение без' перек()мпюf.Щntи и пере­
установки IГpИложений .клиента и сервера. В соответствии са с~емой удаленного,
вз:а:и:м:одействИfI _NБТ все yrюмянуrые здесь проблемы Mo.ryT быть решены ~ помр­
ЩЬЮ фaitл.а RОНфитурации уда.ч.енноtо вэаююдЕЙСТВИЯ:.
ВС'I10:мните ИЗ главы] 1 о- ТОМ. что файл * .,config м,оЖitо ИСI10льзоват:ь ДЛЯ
"подсказок" среде CLR в отношении места нахождения внешних IOOМIlOново".!.fJЫХ
, блоков. не()БХодимыхдлSJ. работы прИJldЖения. Эти Же фаwrы '. ~ lD n f i g могут ис­
подьэоватъс.а: и ДШI ИНфОРМИрования CLR о делом параме<гров ):дап:emIОГО 13заимо­
дейСТВ.й..!;l • .как на стороне ЮIИeНТЗ, так и на стороне с,ервера.
При создании файла *_config' ,ДЛЯ указанна различнъ1Х п.ар.:;метров уд.wrС1ПfОГО
В3<lИМОДе'Йеrвия используют ЭllеМflБТ <.sуstеrn.ru.ntiroе .l.-еroоtiп g > . ЕсJШ у вашего
прИлоЖения уже есТЬ фc=iй1i *. сЬп fig. в 1tO't'OP0МO уlсазаныI uapa:мeTpJ>l размещеЩ1[J!
КОllШОНОБОЧНОТО блока. Вы можете добавигьэдементы vдале:нного взаНl\40дtЩСТIIИИ. '

:8 ЭТОТ же файл. Единый файл" .config. содерmащий I'I настроfПUI у~еmюro вз;а-
имодеiicтвия " 111 информацmo привязftИ. ДOJ.iЖe1I RЬШlЯДетъ примерно так.

~'CDI1fig1,.1 :J;a-ti on~


<8 ystem. ru.nt.iЛI€. remoti:ng,>
<! -- napaмewpiol 3tдaJ:tенноpQ .В'9~одdс'l.'ВИЯ >JI'mИен!1'а. ~ ~pBepa -- >
</ system. rlln't:.im'e. it~,Jnot ir1~J>
Ч1Н'~!:. ime:>
<! -- ивфориaциR привпхи JIЮИПОИОВОЧ:J:I.О:'-'О б.n~'а - >
</ r1mti1t!e:>
<. /-сои f i <[!Lп.а,t :Ь-оn:>

ЕслИ вам нечето yRMaTJ. в отношении nривяз:ки .кО!\ЩОНОВОЧНОГО блока. вы


мож.e-rе опусти'Гь соответствyro.ru:иii элемеuт < гшн. i те:> ~ использовать в файле
". config шаблон сле.1Q'1Otnеrо вида.

<Qопf ig U'J::at} оп>


<В,у 53 t-em. runtime _ remb't ing:>
-<! -- napaNewpu YJI~eJU!oZ'O .:saиио,ц8ЙС'l.'lВ~Я 1ICЛШЩ'1'а n рервера >
</5 уз tem _ r"l1iJtime. rеwоt.i!щ>
</c.onf ig,аrаtiсп>

1
726 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NET
r
Создание файлов *.config сервера
Файлы конфигурации на сторон:е. сервера позволяют объ,явить объекты, кото­
рые будут доступны ДЛЯ удаленных вызовов, а также задать параметры канала и
порта. Рассмотрим следУЮЩИЙ вариант программной логики сервера.

/ / "Жестхо fI задаинаR npоttpаминаR логиха сервера нттр.


HttpChannel с = new НttрСhаппеl(324б9);
ChannelServices.RegisterChannel(c) ;
RemotingConfiguration,RegisterWellKnownServiceType(
typeof(SimpleRemotingAsm.RernoteMessageObject) ,
"RemoteMsgObj.soap",
WellKnownObjectMode.Singleton);

Используя элементы <service >. <wellknown> и <channels>. эту программную ло­


гику можно заменить следующим файлом *.config.

<configuratior,>
<system.runtime.remoting >
<application>
<service>
<wellknown
mode="Singleton"
type="SiJDpleRemotingAsm.RemoteМessageObject, SimpleRemotingAsm"
objectUri="RemoteМsgObj.soap"/>
</service>
<channels>
<channel ref="http"/>
</channels>
</application>
</system.runtime.remoting>
< /configuration>
Обратите внимание на то, что значительная часть информации удаленного сер­
вера указывается в контексте элемента<service> (не сервер!). Его дочерний эле­
мент <wеllknоwл> использует три атрибута (mode, type и objectUri) для регистра­
ции WКО-объекта в слое удаленного взаимодействия .NEТ. Элемент <channels >
может содержать mобое число элементов <channel >, которые позволяют опреде­
лить вид канала (В данном случае это НТГР), открываемого на сервере. Для тер·
каналов вместо h t tp нужно просто использовать лексему tcp.
Поскольку в этом случае вся необходимая информация содержится в фай·
ле SirnpleRemoteObj ectServer. ехе. соп fig, метод Mai n () серверной сто­
роны значительно упрощается. В нем остается выполнить только вызов
RemotingConfiguration .Configure () и указать имя соответствующего файла
конфигурации.

static void Main(string[] a rgs)


{
/ / Регисorpaциg ИКО-объекта с помощью Файла •. config .
RemotingCo nfiguration.Con f igure(
"Simple RemoteO·b j ect Se r v er. ехе. config") ;
Console.Writ e Llne (" с тар т с ервера! Для ос тановки нажмите <Е п tе г> ");
Conso l e.ReadL Jne ();

l
L
Глwre 18. УД3JJ811IЮ$ взаимодеЙСfВие .NET127

Соэдание файлов *.conflg клиента


Клие:uты то)ке мотут ИСПО1IЪЗоватъ фш1.л:ы .. . Cdnfig yдanешroго вэаимодcikТВЩI.
В ОТlIИ'Ше от файлов шшфиryрац:ии сервера. в фвйлц ROнфиrypaции к..mеыт8. для
идеmифmrnции 1'IМени WКО-объекта йсполъзуется элемент <clie1'1t>. Вдобавоц R
возможности динамичеСRОГО изменения хщрамеТР9В удаленного В3aщltОДейСТВИfl
без переlЮМIlИJIЯЦИИ базовог() программного ~oдa. файлы * .СQпНg КJlИelfГа ЦОЗВО­
JlЯ)()Т создать Т1Ш arсшта genосре.цcrвeн}tО с помощью :к:mочевогослова С# !16W, не
ИСЛОЛЬ3УН метод A.ctivator .GetObjec t(). Предположим. например, что у нас еСТ'1?
файл *. c.oni i g КЛИ'евта со следующим содершимым.
<co·n f igu raf.i оп>
<зу s1em. л.:шtimе . r еm о t lпg>
<a 'p plication>
<c1.i~t d.i!JP1ayNams ~ "SiJlJpleRemoteObjectClient">
<welIknown
type=" Siщpl~оtiП9Ав111. ReDIo~ssag8aьject , Si1DpleRemotingAem"
url="httр:IIIoCaJ.hsst , З2469/RemoteИsgObj.SQар"/>
<!clj.ent>
<с;:hэ.ппеls;>
<сЬаnnеl re~="h-t:tp" 1>
< IthапIi:еlз>
</ аррНсаЦ0П>
< / system. rlшtimе. remo·ting>
< / c.anfi gura ti од>

Тогда можцо изменить метОД Main () юmента тц.

.'i! ta:li о void Мain (6 Lril'lg [J O1T9'S)


{
·Remot iлgСопfigur~t·iО1) , COnfigur·e (
"S.iшрle.RелюtеОЬ:!ес .tС l iеnt. eoК€. config ") ;
11 При ИОПО~$ОliаиимфUb:tа ~. config 1UJИ8И'I' ~~It' COSдa'Dt '1'ИП
11 .а8П9СР8д.Clt'Веико· о пoarощыQ JUUD'lе&ОЖ'О СШOJlA ' пеу' •
Rernot·eMes,s;ageObject .simple = пе"'; Re:moteMe,s.sageObject ():
simрlе.[lisрJауМе s sаgе ("Цриве11 0'1' JФИента Р' ,);
Con·sole . Wri ·СеЬ.in е '("Сер.вер гО}щрит: (О} '.', simple. Rеtl1rпИеssаgе ()) ;
Солs·(Йе . Wri teLine ("CTap'l' I<:ЛИ6н.та! Ддll с>ст,э.нОБКИ .нажмите <Ent er> ") ;
Cor).sole . J<,eadLi.rte () ;

При выno.тtении этого варианта прилажеюш BЫIlOД ОRaзы:еаe'.tСЯ аналогич:ным


исходному.·ЕслИ клиент пожелает иепo.nь~ова1'ь ТСJ?-Rанал. то дл,я свойств ш:l эле·
мeJiТ.a <we llknown> и ref эл~меНта <channel> слецует :ВMecтv ht.tp Ylщsать tcp.

Исходный код.. Проекrы SllТJpleRemoteObjectServerWIthConfig и SimplеRвmotеОЬjrreЮНеntWilhСопfig


ра,змеЩefiЫ g ПDдкаталОГ$. C-ОQТ8e1fСТ8УЮЩем rnаве 18 (оба эти проекта ИCnQЛь;JУЮТ I:1p3данtiый
выше КОМflOНОВОЧИЫЙ БЛОk SimрlеRеmсШпgАsm,dll"
r,
728 Часть IV, Программирование с помощью библиотек ,NEТ

Работа с МВV-объектами
Наши первые приложения удаленного взаимодействия позволяли дос1j'II кли­
ентов к одному WКО-типу, Напомним, что WКО-типы (по определению) являются
МВR-типами, поэтому доступ клиента к ним осyrцествляется через агента-посред­
ника. В противоположность этому, MBV-типы являются локальными копиями сер­
верного объекта, обычно возвращаемыми открытыми членами некоторого MBR-
типа. Вы уже знаете, н:ак настроить МВV-тип (следУет обозначить соответствующий
и.ласс атрибутом [Serializable]), но МВV-тип в действии вы еще не видели (если
не считать обмена строковыми данными между двумя сторонами). для иллюстра­
ции взаимодействия MBR- и МВV-типов мы рассмотрим новый пример, в котором
используются следующие три компоновочных блока.

• Общий компоновочный блои CarGeneralAsm.dll


• Компоновочный блок клиента CarProviderClient.exe
• Компоновочный блок сервера CarProviderServer.exe
Как вы можете догадаться, программный код приложений клиента и сервера
более или менее подобен программному I<OДY соответствующих приложений преды­
дyrцeгo примера, особенно в том, как эти приложения используют файлы *.config.
Тем не менее, давайте разберем соответствующий процесс построения каждого из
у!<азанных компоновочных блоков по очереди.

Создание общего компоновочного блока


В ходе нашего обсуждения процесса сериализзции объектов в главе 17 мы созда­
ли тип JamesBondCar (в дополнение к связанным классам Radio и Саг). Бибmютека
программного кода CarGeneralAsm. dll будет использовать эти типы. поэтому сна­
чала выберите Project~Add EXisting Item из меню и добавьте в свой новый проент
библиотеки классов соответствующие файлы *. сз (автоматически созданный файл
Classl.cs можете удалить). Поскольку каждый из добавленных типов уже обозна­
чен атрибутом [Serializable]. они готовы для маршалинга по значению в отно­
шении удаленного клиента.

Теперь нам нужен МВR-тип, который обеспечит доступ к типу JamesBondCar.


Чтобы сделать ситуацию немного более интересной, наш МВR-объект (CarProvider)
будет поддерживать обобщенный список List<> типов JamesBondCar. Тип
CarProvider определит два члена, которые позволят вызывающей стороне полу­
чить заданный тип JamesBondCar, а также полный переченъ List<> соответству­
ющих типов. Вот весь nрограммный код для нового типа класса.

namespace CarGeneralAsm
{
11 Этот 'l'Иn ЯВ'nRе'1'СЯ: мва- об>ъеlC'1'ОИ I обеспечивauции доступ
11 IC СОО'l'Jlе'1'С'1'8YJOЩИИ мвv~'l'Иnаи.
public class CarProvider : MarshalByRefObject
{
private List<JamesBondCar> theJВCars =
new List<JamesBondCar>();

l
Глава 18. Vдаяеtlное ВЗQимодействиl'! ,NEТ 729
11 Добав.пвниев аписе!\: несхсшьJCИX 1IiI1IIИИ.
puыcc CarProvider!)
j
Сопsсlе. WI i t'eLi.l"ie ("СDздание постаВЩИR!а маш .......н" ~
;
theJ!3CarS.Add(.new .JаmеэЕОi":\dСаr ("QМ.obile",
'HO,tru.E, t;r-ue));
theJBCars.Aqd(rI6W ~ТamеsВо[)dCаr'("Flу€>-r", 140, tr\::le, false~);
trlеJзс2.I:s.А<1i'jlпеw ..Jащ6sвQndсаt("Swirnmеr", 140, falS€, true));
theJВCars. Add (nа'оН ,Jа.m.е$'ВоцdСаr ("Ba$ieJВC". 1.1 О, false, talse)·).;

[1 Dоnyч_RИe всех Ja:mesBondCar.


p1.1blic Lt$t<JamesBondCCir> GеtДllAцt.оs ()
{ ret IJr1Э trle,JIЗCars; }
1/ ПОлуч_ние одного JameSВondCa:t.
pUblic JаrtleэВоnclСаг GetJECByIndex (irH. i)
{ геЬыгп (,Ia:mesВondCar) theJ:JзСаrs [i] ;}
1

9братите щщмание на то, что метод GеtА1Ншtоs () возвращает 'В1I)1трев:в:иЙ


тип List<>. Очевидный вопрос: lШR дюmblЙ ЧЛ.ен :простраНСl"Ва имен Sys1:-ern.
c;oll ections. Ge D eri с представщrе'IlСЯ вызывающей стороне? Если посмотретв
orЩ:С8ние атого типа. в· докуме:нтациИ .NEТ F1"В111еwогk 2,0 SDK, вы Ьбнаружюе. что
List.<> СОПРQlюждаетс-s а'J'РI!бутом [SeTializable].
[Seria,liz.ableAttrib1Jte {! 1
public С1аЭ'$ List<T> : 1 Lis·t." :r(:оllесtiсю. IEn,umera'ble

Тa:J\Ш,( образом. ДJlfj всего сод(;ржимоro 'ГJ1Цa List<> буде'r ИCnО:П.Ь30ШЩ марша­
JIЩil' 110 значению (если содер.жащи;еса в нем типы тацже допуqнa;IOТ ееР~aJШза­
цию). Это очень удобв.ая ocol1leRRocTb удален.н:ОI'0вз~одеЙствИ'я .NEТ и ЧЛ~НОВ
библиоте:к ба;'.lОВ~ ]ЩН.ссов. Бдор<Ц!ок l't п"ль:\ф:вате~ МБv- и МБR-nmам, КО"
торые вы можете ооздать Сами. Ш9бой тип и;3 би.блиот~ б~ОБщ..'!: ]щассов, сопро­
.важда.юЩИЙся аТ]';Iибутом ISeri а 1 izable J', танще с:аособен БЫСтупать в 1tЗ.честiзt'
МВV-T:mтa :в архитск:rуре удаленного 1Iзаимодеи.отЕЩИ ,NEт. АшщОГИЧFЮ. :люБQJfТflJТ.
долучающцйса: (не:посредсТВfЩНО ИДИ RDeEieИRQl щ~t'1аrshаlВуR€fОЬj eet, мож;ет
ФУШШ;ИЩПiipовать, щm МВR-тип,

3l1М8чанме. Следует знать о том, ЧlО 8oapFormetter не (1()iDдерж.иsаетсерИ"anи~ацию обобщен­


HI:IIX типоа. При СОЗДilнии методов, получающих или возвращающих обобщеliНЬ!Э типы (напри­
мер, Liзt<>)" ВЫI ДQЛЖНЫ IiIЕПОЛЬЭО'ВЕ\ТЬ ВitшrуFОrIТ1i:1ttе:r и объект 'ТсрСhапnеl,

Соадание компоновочного блока сервера


R9мnОНОВ6ЧНЫИ БJIO:R сервера (G.arPJ3QV'idiarSe·rve:c.ex€) я рамках метода Main ()
'Содержит следующую :пр.огра:ммвую .!lогику.

ЦЗl.пg Syst.em;
u,51ng '::'.уstеП1_Rl.1пtiцц~. Rempti 1:19;
liSi ag S ystern. R(1Il tiше • RenlOt i ng . Сhаnnеlз;
ц:эing System.R-сщ tirn!E' .Remoting . Cnan.ne 1з, Bttp:
1:15209 са:rGenеralAзm.;
730 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NET
r
i

паmезрасе CarProviderServer
(
class CarServer

static void Main(stringfJ args)


{
RemotingConfiguration.Configure("CarProviderServer.exe.config");
Console.WriteLine("CTapT сервера! Для остановки нажмите <Enter >");
Console.ReadLine();

Соответствующий файл *.config почти идентичен файлу *.config сервера.


созданному в ПРeды.ЦYIЦем примере. Единственным заслуживающим внимания мо­
ментом здесь является определение значения URI объекта ДЛЯ типа CarProvider.
<configuration>
<system.runtime.remoting>
<application>
<service >
<wellknown mode="Singleton"
type="CarGeneralAsm.CarProvider, CarGeneralAsm n
objectUri="carprovider.rem" />
</service >
<channels;>
<channel ref="tcp" р о гt="З2469" />
</ channels>
< /application>
</system.runtime.remoting>
</configuration >

Создание компоновочного блока клиента


Наконец. рассмотрим приложение клиента. которое будет использовать MBR-
тип CarProvider для получения отдельных типов JamesBondCars и типа List<>.
После получения типа ОТ CarProvider вы посылаете его ВСПОМОГаТельной функции
UseCar() для обработки.

изing System;
using Sуstеrn.Ruпtimе.Rеmоtiпg;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using CarGenera~;
using System.Collections.Generlc;
namespace CarProviderClient
{
class CarClient
{
private static void UseCar(JamesBondCar с)
{

L
Глава1В. Удаленное взаиМОД~ЙС1аие .NFГ 731
Consol е .Wr± teLine ! 11-> Имя: {.о},", (;. PetNa..l11e) ;
G0!'1so1e ...W"L"itеLiле ("-;> Мак;:; . с:корастъ; ('О)", с. MaxSpeEC) i
c:onso], е. Wri teI,ine (" -> ('п@собн":)(:''I!Ь Т1JIa8q.Th: {О J ., 1
L:. isS",aWDrt1'1~') ;
.сопsоlе .W:r{teLirJe (Н._> СлоС.обаость лет"iть: 101 ''',
с. iз1"1 ightWоrtЪу ) :
с.ошsоl€. Wri teL.iле.(} I

$tatic VQidMa:in (striп,g []args)

Rеm.с,tiлgСо:пfigurэtiоп. с.олfigщ::е.l ~Cc.rProv{·,jerClie1it .ехе. cdnf{g'~) ;


(I Ссsда.вие пос.'1'_ЩИХ~ ~.
Car'Provtder qp = l1ew СауР rНЧl de:F() ;
/I IIoлучеlDll! IЩpI!ОХ10 оБЪeIo:'l'~ JВC.
,JзmеsВопdСаr qL",r = с;р. G'etJВcвyInde~ (О) ;
I! По.лучеИl4e всех об'Ъехтов JВC.
List<:J$.rnesBondCar> ~llJl"Сз = ~p.'GetAllAut9s ();
1/ Использование первой lIdaпlивы.
ТJseCar(qCar) ;
11 И:СПО:ПЬSОВaIЩ:е все;х ~ • :r.ist<>,
fo·rea.ch (,JamesEkmcICar j .in ;зllJВСs)
Пsе12ау (j ) :
СО'h,зеlе. Wri t~Line (.IC"l'ap'J:' КП'иента! дл}'! ·о.сТ2iгЮВ!tи нюr.м.'":<I"е· <En.ter>ТO j ;
Сойsоl е. REa>::fLi пе·1 ):

Сод~рши:мо.е файла *, oonfig на стороне E1IИвнта 'Тмже COOToel'lC:rnyeт OJю,да:ни­


iIМ. Здесь нуж~ю просто изменить URL ЗЖТИШIЗ<iЦJiШ.
<солfi"!щ'аti Оп>
<i3:Jl1i; сет. пшt iше .. remoting>
<appli~:atit)(J.>
<clie11 t pi,splayNaro,e = "Сатсl ien·t ">
<well kriowI1
t уре= "CarGener.alAo$111. CarPrOlJi der, CarGener.<J lASF(!'''
ихl=" t.cp·; //localhaBt; 3Lд 69!ca1:"provlder .тет')} >
<!client>
<с'h,алnеls>
<о.hа'[ш€Ol :пJf=~httр" 1>
</chaf1f\el,,>
<!;;iрг1iсаtiQn>
</ system. runt irne. rелюting>
<!configtJratioJ;1>

Теперь .зацустите СВQ;И цриложетщ сервера Н н.лиента (щшеЧfЮ Жt;>., в УШ18ШПIOМ


пормке) и расс~отрите соответсТ1\УЮЩИЙ ВЫВОД. В окне RО;НСООЯ Ю1Иеtпа будУТ
предстaюrенЬJ объекты JаЛ1.езВоI1lJС'аr и СQответствующая информация для каждо·
ГIII ТИПа. Напомним, ЧТО вы В8~действvете с Li$t<> и тицами JашеsJ3Q.ndса:I.
ПОЭТОМУ вы работцете с их чледами в рамках домела прцлом.сенин ЮЩецта. таlt}Щ}/
оба YRЩШНlI;Ьrx т,ИI'1Н обозначены атри.бутом lSerializable].
732 Часть IV. Программирование с помощью библиотек .NET
r
Чтобы доказать это. измените вспомогательную функцию UseCar () так. чтобы
она вызывала метод TurnOnRadio () для входного объекта JamesBondCar. Теперь
запустите приложения: сервера и :клиента еще раз . Обратите внимание на то. что
на машине lUlИента теперь появляются соответствующие сообщения. Если бы типы
Саг, Radio и JamesBondCar были сконфигурированы. как МВR-типы. сообщения
бы появлялись на сервере. Для npоверRИ получите каждый из указанных типов из
MarshalByRefObj ect и переКОМПИJIИруйте все три компоновочных блока (для гаран­
ТИИ того, что Visual Studio 2005 скопирует самый последний CarGeneralAsm.dll
в каталоги приложений клиента и сервера) . Теперь при выполнении приложения
окно с сообщением появится на удаленной ма.шине.

ИСХОДНЫЙ КОД. Проекты CarGeneralAsm, CarProviderServer и СагРгоvidегСliепt размещены в под­


каталоге, соответствующем главе 18.

Объекты, активизируемые клиентом


Все созданные до сих пор примеры удаленного взаимодействия использовали
WКО-типы. Напомним. что WКО-типы имеют следующие особенности.

• WКО-тип можно сконфигурироватъ как СШlГЛет или как объект одиночного


вызова.

• WКO-ТИП можно активизировать только с помощью конструктора типа. за­


данного пО умолчанию.

• Экземпляр WКО-типа создается на сервере при первом запросе члена этого


типа клиентом.

Экземпляры САО-типов. с другой стороны, можно создавать с помощью любо­


го конструктора типа. и они создаются тогда, когда клиент использует ключевое

слово С# new или тип Activator. Цикл существования САО-типов контролируется


механизмом лизингового управления .~'Eт. Следует знать. что при конфигурации
САО-типа слой удаленного взаимодействия .NEТ генерирует специальный САО­
объект удаленного взаимодействия для обслуживания каждого клиента. Важной
особенностью САО-объектов является то. что они продолжают существовать по­
сле завершения вызова отдельного метода (и. таким образом. являются объектами,
кумулятивно изменяющими параметры своего состояния в процессе выполнения

вызовов клиентов).
Чтобы проиллюстрировать соответствующую конструкцию и использование
САО-типов. модифицируем наш уже имеющийся "автомобильный" компоновочный
блок В нашем МВR-классе Ca!:'Provide!:' определим дополнительный конструктор,
позволяющий клиенту передать массив типов JamesBondCar, предназначенных
для размещения в обобщенном списке Lis·t <>.
publi c class CarProvider : MarshalByRefObject
(
private List <JamesBondCar > theJBCars
= new List<JamesBondCar>();

public CarProvider(JameeBondCar[] theCars)


I
ГЛ;lва 18. Умле,НfJое ВЗ311модеЙЩВI1,е .NEТ 733
Got!sol е . ~ri t ,e Line ("С'оэ'дание JЮСТCl13щика (;;ат ") ;
Cof.ls.o-le. W:r itceI"ine ("с ПОМОЩЬЮ п(')лъзшзательскогф КDIiC'rp.ynQ.pa '.') i
'theJВCar.s.AddRange (thеСаrз) ;

t.lтобы ПОЭВодИ'ТЪ ВЫ6l:Ш3ШQщей ctopOJ-.rе ЩtтЮlЩ3ИРОвать CarProvidE!r с ПОМОЩЬЮ


nOBOra KOfJCT'p)'Icropa. нужно построить прШIоженле сервера..}{ОТОРое зарегистри­

рует Carpr;ovid'e r. щщ ело-тип. а не ,как WКО-пш. это .~ОЖ1;fО <:делать лроrpамм­


но с ПОj\ЮЩЪJ(i) Ме'ТОда, аналогичного Rem()tiпgСоnfig,urаtiО-tl~Rеgis{еrАсtivаtеd­
Se.rv i'ceType ( ), иди с nОМ(JIЦЬЮ файла .... сonбч на стороне сервера. Чтобы ~жестко'
задаТЬ, имя САО-объе:кта в программнОМ: . ш>де сервера, передай~информацщо типа
или типов (после создания "и регистрQЦИИ J(8Яала). жак предлщ-ается ниже.

11 "Же:етхое tt ухаЗАиие :то:го, ~ТO


CarPro,v ider ИlS»II8'1'CtI САО-'1'КПОМ.
Rert'tt>tir,qCorif igU:Lati QП. НеqiЗ terActiv3.tedServiceType (
tJ:'peof (CZIOC,,-:r~nera lАЗI"" CarPl"OV iqe,r) );
Есливы.npеДПО'tJ'I'етеисПЬЛЪЗОвать файл *.config.1!MeCTO ЭJIецевта <wеl1kЛri)W,П>
'иcnолъзуйтеэлемент <activi'ited>, как ПОRазано ~.

<copfiguratio:c>
<~Y$\em , runtiro", ,remoUng>
<applH:ation>
.:;se!'vice>
<асНva.tj:!dtype ;:: "CA()CarGene;~~, Car,P~ov.i.der •
CAOCarGene:ra1.ASIn" 1>
<lservic!'J>
<c'hanпеl,s >
<channei rеf="tсрП роrt="~24б, 9" />
< /chii nnels>
<I аррН<С'аиоп>
</ ::;ystem. !'t,!ntime. rеmо:tiлg>
</СОf1fi'gШ-д tion>

Нанонец. 'нужно обвовl'ПЪ приложение ШIиепга. и не TOJ[blI:O с целью учета со­


ответствующего, файла 'Iт. соп fi:g (ИJUI ЛРOl'рам:М1-Iыx иЗМеНений в базовом коде) для
запроса доступа Б удаленНому САО-объек'ry, но и с тем. чт:обы вызвать соаданнЫЙ
пользовательский ROНСТ.РУЮ'ор типа CarPrQvid.ei:. Вот шm до."1Жен ВЪJ]'JlЯДCТЪ мdди­
ФlЩl<фовашп.IЙ мето.д Main () на стороне JtлИ~нта.

static v9id Main (;зtrin,g l1 a:rgs')


{
11 ЧтеНИе обаОllпевиого фaйnа *. c~iq.
Remо,tiпgСОnfigurаtiQП. Confiqure (
»'CAOCa:tPrQ'.riderCl ient, ехе, соп fig") ;
J1 Со!l;Ц&lЦle xac~a 'ФИJ:lО. дn5l: пере.чil.ЧИ поc:luаВЩИJ::У •
JamesBondCar[] саrэ =
{
newJamesE,o n,dC'ar ("Vipe[" ", 1 00 , txue , f q ls,e),
пеw Jam~,эВ-опdСаr ("Sh-a:ke,n "', 1ОО, fa;.l-se .• t ;r:lJe,j,
nа ... Ja.rnesBOndCar!·' SH~irred", 1:00 r tI1J.~, r.roe)
734 Часть IV. Программирование с помощью библиотек .NET

/ / Тепер!> ВWЗОВ Пo.J1loао.а~схоl'O конструктора.


CarProvider ер = new CarProvider(cars};

Обновленный файл *. с о n f i
9 :клиента также должен иепользовать элемент
<activated>. а не элемеwr <wellknown>. Кроме того, свойство url элемента
< с 1 i е n t > теперь должно укаэывать адрес зарегистрированного СЛО-объек­
та. Напомним. что при регистрации типа CarProvider сервером в виде WКO­
объекта. клиент указывал соответствующую информацию в рамках элемента
<wellknown>.
<configuration>
<systern.runtirne.rernoting>
<application>
<client displayName = "CarClient"
url = "tcp://localhost:32469" >
<activated type =
"САОСаrGeпеrаlAsш.. CarProVider, САОСаrGeпеrаlAsш." />
</client>
<channels>
<channel ref="tcp"/>
</c hannels>
</ application>
</system.runtirne.remoting>
</configuration>
Чтобы "жестко· запрограммировать запрос СЛО-типа клиентом, можете исполь­
зовать метод RеgiэtrаtiопSеrviсеs.RеgistеrАсtivаtеdСliепtТуре (). как пока­
зано ниже.

static void Main(string[] args)


!
// Испo.m.зование ".естхих" значений.
RernotingConfiguration.RegisterActivatedClientType(
typeof(CAOCarGeneralAsrn.CarProvider) ,
"tср://lосаlhоst:З2469"};

Запустив на выполнение обновленные компоновочные блоки сервера и клиен­


та, вы с удовлетворением обнаружите. что можете передать свой пользовательский
массив типов JаmеsВолdСаr удаленному объекту CarProvider через перегружен­
ный конструктор.

Исходный код. Проекты CAOCarGeneralдsm, CAOCarProviderServer и CAOCaгProviderClient разме­


щены в подкаталоге, соответствующем главе 18.
Гла'l!a 1'8. YAaneJ.tHoe взаимодеЙСТ'Вllе .NH 735

Схема лизингового управления ЦИКЛОМ


существования САО-тиnов и WКО-сингnет'о ' в
Вм уже видели. что WKO-ТИnы. СКQнфИIурироваШ'I:Ы.е ддя W<тивизаД,ИИ ()Д~­
HO~OГO DЫЗОва. существуют l"ОЛъ~О в npоцессе 1't1o/Щe:tо щ.1ЗQва метода, ПО<Н'9МУ
Wl{()-тиnы одиночнт-о вызова .яв.люотсн объектами. H~ мевяюI1:UfМИ ('воего СОСТо­
яния в nроцеtсе ВhШОJIвеl-nШ. как только текущий вызов заJJершается. WKO-'ТИn
одиночного вызова ст,ановится объектом. преднаэnаченным ДЛ.fJ участия в оч-еред­
поЙnpоце.цуре, сборки мусора.
с другой с"1"ороны.сАо-'лшы. а также wко-типы. ~ОНФИГУРИРОЩUlНblе дщJ эн­
nmизации .в виде синrлета. являются по СI3Ьей природе об1;.eRтами. :КУМУ.JJJ:l.ТИВНО
иаменЯЮIцИJ\oIИ параметры С1Юеrа соcrояния в процессе ВЪШ9JШе1ПIЯ Вbl,ЗоВi'Щ кли­

ентов . У'-штыва."1 ЭТИ дведостуПН:ые ОIЩйИ УСТаА:ОВIW 1.Wнфигурации. возникает


следующий вопрос: как процесс сервера ·узнает" о TOТ\II, ~1ТO ПQра УНlfЧТОЩИТЬ такой
МВR-объскr? Если сборIЦIШ мусора на сервере УНИЧТQ)l<ит МВR~объеl\ТЫ. НВХQдЯ:­
щueся:в иСПользовании yдarreв:ным JСЛИентом. это ~дaCT проблемы, А если cepB€:-
ру придется ояшда<rь освобождения МВR-ТИUОI:\ c.rrиunЮМ ДQЛГО • .это Фтрmщ.телыю
повлинет на р~боry систеМ1Ц. особенно если соответст{i)'19ЩИ{: MBR-рf)ъеJtТbl удер­
ЖИВI:IJОТ важные ресурсы (CВSI3Ъ с базой nанш.rx. неуцрmшцемые ТЮlЪ). или ~ие-"ТQ
другие pec.ypcьr).
Циi<л сущеСТllOвашш МВR-объекта:. nwmющето~ ело-типом или WKO-сиJtгл~­
тОм. контролируется rID схеме лизинrщюгu управлеJlИЯ. ноторая тесно связана с

uрощессом сборки "мусора ,NE'Т: Если ~BpeMn аренды" МВR-о6ъежта. юшлющегося


CAO-ТИhОМ пли WKO-СИJП'летом. истекает. объе~ (тано:вится кандидато.(\! на уч~ .
стие "в очередном JtЩUlе сборки муСОра. YLC1К и :\!' случае .moБОГQ дрyrОl"О ,NEТ-типа,
еCJшудаленный объект переоuределлет System.Object.finali.'le () (с помощью
сиятзжси:са Д~CТPYКTopa С#). то среда выполнении .NEТ автомаТИ'1f'СКИ :запустит
СООТВ(:''J'ствующую логику фmiaJ1ИЭации.

Схема лизингового управления~


испоnьзуемаSl по умолчанию

Для МВR-объектов. ЯВЛяЮЩИХСЯ CAO-rrипам:И или WКО-сивглепми. nРIlМепя­


ffIUi так называемый llШJUН2 по у.мол.Ч!iНWO. дремя KOТQpOГO paIJHO ПЯТи :МИНУ1.'ЗМ .
ЕСШ-f среда выполнения обнаружит, что МВR-обrъеltt mmтoщийся САО~ТйnOМ пли
WKO-СIO-IГ.Летом. QCта~тся неэнтииНЫМ: в L'е"lен:и:е IIЯТИ мйнут, )1,едаетСfl ВЫ:ВОД о том,
ч.то -клиент больше не испоm.зуетданнЫЙ удалешшй объект. й ПОЭ'I'ому ЭТОТ объедт
може:r использоваться в процессе сбор:ки мусора. При этом совсем не обязательно.
чтобы после истече-НRя времени .JIИзmItа объект помечался ДЛJiсборI-tИ мусора ~Ie­
мед7LеНJ-Ю. На самом деле е.с1Ъ МНОТ(:) возможностей ВJIИЛТЬ на поведение, эадanае ­
мое JIизшrtом по 'умолЧаlПlЮ.
Например. при RaЖдРМ вюэове 1UПtентам члена удаленного МВR-объеита. щщя­
lOщeroся. САО-типом или wk.O-сшn-:лет.ом. :вр~мя лиаинrа снюва ус'ta.mlВJlИВ'ает!':Я
равным пяти :МИнyr.;LI\i. Но. нроме автоматичесrrorо обlюВJrения И1Iтерв.ала .еремени
лизинга прй IiЫэове КШlента., среда выпо.iШениИ .NET обеспечивает трЕ доцолни­
тельные а11ЬтернаТl:ШЬt..
736

Часть IV. Программирование с помощью библиотек .NET

УстанОВКИ лизинга по умолчанию для удаленных объектов могут переопреде­


ляться файлами *.config.
r
• Могут исrюльзоваться спонсоры лизинговой схемы сервера, действующие от
имени удаленного объекта. время лизинга которого уже истекло.

• Могут использоваться спонсоры лизинговой схемы клиента, действующие от


имени удаленного объента. время лизинга которого уже истекло.

Мы рассмотрим каждую из указанных возможностей в следующих разделах. а


пока что давайте рассмотрим установки лизинга. принятые для удалеююго типа по
умолчанию. Вспомните, что базовый класс MarshalBy RefObject определяет член
с именем GetLifetimeService (). Этот метод возвращает ссылку на внутренний
объект. поддерживающий интерфейс System.Runtime.Rernoting .Lifetime. ILease.
Интерфейс ILease можно использовать для управления параметрами лизинга дан­
ного ело-типа или WКО-синглета. Вот формальное определение этого интерфейса.

p\lblic interface ILease


{
TimeSpan Current Lea s eTime { get;
Leas e State CurrentState ( get; )
Ti me Spa n Init i alLeas e Time { get; s et;
TimeSpan RenewOnCallTime { get; set; }
Time Span Spon sorsh i pTimeo ut ( get; s et;
void Register(S y st e m.Runtime,Remo ting . Li fet i me.ISponsor o bj);
v o id Regis t er(S y stem.Run time.Remo tin g . Lifet i me. I Sponsor obj,
Т imеS р а п renew a l Tirne);
ТiшеSрап Renew (ТiшеSрап rепеwаlТimе) ;
vo id Onregi ste r(System . Runtime.Remo ting.Lifetime.1Spons or obj);

Интерфейс 1Lease не тольно позволяет получить информaцmo о тенущих параме­


трах лизинга (с помощью CurrentLeaseTime, CurrentState и 1ni tialLeaseTime),
но и обеспечивает возможность построения "спонсоров" лизинга (более подроб·
но об этом будет говориться позже). Роль каждого из членов 1Lease описана в
табл.18 . 6.

Таблица 18.6. Члены интерфейса ILease

Член Описание

Current LeaseTirne Читает иНформацию о времени. оставшемся до отключения данного


объекта при отсутствии новых вызовов методов объекта
Curr e ntState Читает информацию о текущем состоянии лизинга. представленную
значением перечня LeaseState
I n it i a l Leas e Time Читает или устанавливает исходное время лизинга. Исходное время
лизинга - это время от начала активизации объекта до истечения
лизинга при отсутствии новых вызовов методов объекта
RenewOnCallTi me Читает или устанавливает значение времени. на которое вызов уда­
ленного объекта увеличивает значение CurrentLeaseTime
Spon sorshipTime ou t Читает или устанавливает значение времени ожидания спонсора ДЛЯ
возвращения времени возобновления лизинга
Regi s ter () Перегруженный метод, регистрирующий спонсора данного лизинга
Renew() Возобновляет лизинг с указанным временем

Unregi s ter () Удаляет указанный спонсор из списка спонсоров

__ _ ___L
Гnава 18. Удаленное взаимодеЙr.;tвие, .NEТ 737
Для И·.JIЛlOетрации особwностdi JЩэинга по умодчапию длл YAa.i1:CRПЫX САо­
тщюв И WКО-сингдетон ЩIР~eJ.IИМ в нащем те:кущем проекте CAOCa:r;Gener::alAsm
ho-вьЩ :ЩIУТре)'JНИЙ I:Щас(' ы,is-еlnn fo. Статичес1ШЙ член 1.easestats () ЭТОРО клас­
са ШiIВuдит 1Пiформацию о те1()lЩеМ JllI:зинге ДЛJ11'ипа Car:provider в окно ROHCQ-
ли сервера (н~аабудьте указа.ть дцрективу t1,.ing длн пространства имен 5ystern.
ЕUDtimе..RеП\о·!i:ir,q.Lifеti те. что(iы с;ообщиТJ~ коМI1ИЛЯТОРУ о месте нахожде'f-ШН
щ:rределeд.W3. типа IlсеаБе).

iпtеrn.аl clai'JE Leд.'S·e1n1' G


{
public g·t.atic void Lе.асз!О!$t.а:'t,s (JLe.as~ itfLea's.E'lj
j
СО)lэоlе. ~'i'l;"i teLir\e (".... ,, :н jiL'-!фо.рмаuйя о ЛI1ЗИ1:!г·е .. '" *'" *") ;
COD sole. Wri t.eLine (".состояние ЛИЭI'Ц'!'I1'а: 1О}" ,
itfLease.C'urren.tSta·te) ;
С~П$оlе. Writel,it1 e (п}щ,ч.а:rrЫо1()е время диЗин;r"а: ! ()} ;' {],}",
i tfLease.. Ini t ial Lеа.sеТ.i..Ш6. мi ou1Les,
J. tf"Lease .• 111i t i al Lea:seTime .Seconds) ;
С()n~юlе. W:r:i teLixle ("Теъ:уще.е время ЛИЗt>!НГ'ёJ. ~ (О); (1 j 11 r
itfLеаs€.СurrепtLеаs-еТiro€.Мinlllеs,
it:fLеiJ ·sе .. 'С1J iтелt l.Jеj':l :sеТijЩ~. S',ЕС.Qлds) ;
C:onsole. Wri:le:Li.ne ("ОБН О Б;l1~Н:И€ Бремени 'ПРО1 gЫЗОБ е : и)}: Ц }".
1 tfLеаs€-.RеnеwОnСаllТiniе.Мir?utе5,
i tfLease. R.elle·wOnCq..ll TimE • ~CQnd::.) ;
СепзаJlе . Wr i te1.ine ( ) ;

Теперь преЩIOlIQЖИМ. что Lea·seIl'!fo.LeaseStat$ (') ~ЫЗJ;UВаетсg в Paм1t8X ме·


'Годов GetJBCByITJ{l.ex () и GetAJ.IAutos ()типа CarPrQvider. После перекомдwrя.
ции КОМШlН.овочных БЛС)lСОВ сервера и кл:иеl'IТа (снова для гарантии того.. "11,"1;1 Cl:f-
CttMa Yisttal Studio 2005 скопирует ca"МyIO' ПОC:Ilецнюю и наиболее ;полную верс:ию
'CarG.eneralAsm.dll в каi!алоги п'риложеНИЙ.клиента и сёрвера).. ВЪПIOJПIИте при­
ложение etдe раз. ОЮЮ ' КdНСОЛИ вашего сервера дo.ro:н:нo теперь быть пох6~М1М JЩ
то. liOTOPoe ПОRaаано на рИС . 18.6.

Рис. 18.6. ИнФорм/Щия IlИЗиtlга ПО УМОДЧSНиlО для CarProvid~r


738 Часть IV. Программирование с помощью библиотек .NET
r
Изменение параметров схемы лизингового управления
Очевидно, параметры лизинга по умолчанию не могут годиться во всех случаях
и ДЛЯ всех удаленных САО-объектов и WКО-синглетов . Если вы хотите изменить
типовые установки, у вас на выбор есть два варианта.

• Установки лизинга. приняты е по умолчанию, можно изменить с помощью


файла *,config сервера .

• Установки лизинга. приняты е по умолчанию. можно изменить про­


граммными средствами путем переопределения членов базового класса
MarshalByRefObject.
Каждый из этих подходов действительно позволяет изменить установки ли­
зинга. принятые по умолчаюпо, но между этими подходами есть принципиальная

разница. При использовании файла *.config сервера установки лизинга приме­


няются ко всем объектам. размещаемым в рамках процесса сервера. При перео­
пределении отдельных членов типа MarshalByRefObject появляется возможность
изменять установки лизинга для каждого объекта в О'IДельности.
Чтобы продемонстрировать изменение параметров лизинrа по умолчанию с по­
мощью файла * .config. добавим к ХМL-данным сервера дополнительный злемент
<lifetime>.
<c onfiguration>
<sуstеm.ruпtiше.rеmоtiпg>
<app lication>
<lifetime lеазеТimе :: "15М" renewOnCallTime = "SM"/>
<service>
<activated type =
"CarGeneralAsm.CarProvider, CarGeneralAsm" />
</service>
<сhал л.еls >
<channel r e f="tcp" роrt="З2469" />
</channels>
</application>
</sуstеш.ruпtimе.rеmоtiпg>
</configuration>
Обратите внимание на то, что в значениях свойств leaseTime и renewOnCallTime
используется суффикс М. который. как вы можете догадаться сами. при установке
времени для лизинrа обозначает использование минут в качестве единицы изме­
рения. При необход:амости числовые значения злемента <lifetime> могут также
содержать суффиксы MS (миллисекунды). S (секунды). Н (часы) и даже D (дни).
Повторим. что при изменении файла *. с о n f ig сервера вы изменяете пара·
метры лизинга для каждого САО-объекта и WКО-синтлета Б рамках сервера .
Как альтернативу. можно использовать программное переопределение метода
InitializeLifetime () конкретного удаленного типа.

public class CarProvider : MarshalByRefObject


{
public override obj ect InitializeLifetimeService()
{
Глава 1'~, УдалеНi'tое вэа\1моде~rПБие ,NEТ 739
11 ПолуЧение ',I!ехy:JUЙ иаФОрма.ЦМk nиu!tt'a.
IL~88e itfLеti$,еIпfо =
(ILea5e) bas€. Iaitializ:eLifetimeServic-е () i
/ IИз~еJiеме J'СI1'3ИQIЩК.
i t fLeas.el [1Lo .IPci t ialt,еа:зеtrimе = l'imeSpan. FrопiМiп'utеs (:'iO') I
i t:fLеаsеlпfО .. R€'пеwО11саllТiЛ1Э = Time3pan •. F:t-оmМ:iпutеs (10);
retU1:\I'1 НfLеаз,еlnfо;

Эдесь CarProvidel' устанавливает значение 50 мину] дли lлi tii31L~аsеТinч'!


и aJ{agение 1-0- ДlШ R6IJ ewOnCall Time. Снова поДЧ-ер:кпем. что препмуществом
переonpеделения метода Iлi tiаlizеLifеtirпеБег.riсеs () явлнется ВОЗМожнОСТЬ
насТрDЙКИ RaiiЩorо удаrreшtого J'ИIfЗ в оmелъностн,
Наконец, чтобы иоабще ОТЮlЮЧИТЬ orpа:ничеНИJ'I ДЛЯ времени лизинга ДВНН-ОГ0-
САО-'1ШШ ил:и-WКО~синrnета, переоnpeдел:ите Iпit:lа1i-zеLiLfеtiilТlв8еrviСЕ<$ О так,
чтобы возвращалось 3Начени:е rJ1Jll. Б результате вы, по сути. укажете МВR~тип.
J:tоторНi'Й будет СуЩествовать все время. пока будет рабетать XOC't-ПРИЛDжение
сервера.

Настройка параметров лизинга на стороне сервера


Вы только видt!JПL, что переопределеш-rс метоД;:1 101 t;ializeLifetimt;$ervices ()
МВR-тиnам .позволят изменить Тe-J\)'щие пара:метры лизинга -Во -время активиза­
ции ТИПа, Но что делать., еG.ТJИ, удаленному пту Hymнo изменить fШраметръi: лйз'ИН~
Ia nncле акгmшзаций? Предположим. НЗllpJGreр. что тип C-аrРrоvidеr прtщлагает
новый метr;щ, въпю.n:няющий операцию. требующую МНою времени (на.пРи:мt:Р~ сое­
n;инение с удалеШIOЁ базой данвы:хс последУЮЩИМ чтением большого набора зшm­
сей) .. Перед Нf:рт.лом ВЫIЮmrенил такого ~здaнйlI вы :можете программно изменить
--время ЛИ3ИI-П-а так. чтdбы Б ш:ryчae. :когдз естатак времени стЗ:нШJИТСЯ менее одной
минyrы. Вpt1МЯlIIiIЗИl:П'a СJ:Юва увелич:ивалось ДО десяти мивyI; для этого можно ис­
llСJ;ЛЫЮВа:гь наслeдyeмьre методы ма r:shдlВуRеfОЬjе-с!:;GеtLi fet iтеSеЛ7 i-ce () и
I1ea-sе.Rепеw () так. ШLi:t upeддarаетсл:ниже.

11 1tОРР.8k'1'!Иро:вха nараие'.1'рClS mlsJIR'I>a на cwарои:е- оервера.


11 Предпоmп'ае'1'CJf, "''1'0 Эr1'О Щ)~ JI!В~О;Ц ~ C~P!rov.i,.der.
publiQ void DоLеhgt,hуОр:еi!аtiеп(}
{
ILease i tfLeaselnfo = {IL~ase) thi$ . GеtLifеtimeS.епгi.се () ;
if (i t пеаве]; пfо. C'L11'"1' е rftLE аэ'е Time .• TotalMin1Jl:.es < 1. О)
i tfLease lпfо _Rerнi,'w (Ti me$PaJl. FrofГLMihutes (l.о) :) ;
1/ Вuпоnневие д.питenьиой операции . ..

Настройка П'араметров лизинга на стороне клиента


н ДQполнение к ухааа:нным ВОЗМ:ОЖflОСТам ILease. домен прИЛQЖeНИН R.JIИeIf'Га
-тоже может реryлиро:вать теEYЩIiе параметры Jiliэинrа ОАО-т.ипов и WКО-сингле­
тов, с которыми осуществля:етс.я УДW1effi10е 6заимодеЙствие. ДJШ этою wrиент J(Ьл-
740 Часть IV. Программирование с помощью библиотек .NET

жен использовать статический метод RemotingServlces.GetLifetimeService ().


в качестве параметра указанному члену клиент должен передать ccыm~y на уда­
ленный тип так. как ПОRазано ниже.

// Коррехтировха параке~ов лизинга на стороне клиента.


CarProvider ер = new CarProvider(ears);
ILease itfLeaselnfo = (ILease)RemotingServices.GetLifet imeService(cp);
i f(itfLeaselnf o .CurrentLeaseTime.To talMinutes < 10.0)
i t fLe·aselnfo. Renew (TimeSp an. FromМinu·tes (1 0 00) ) ;

Тm<ой цодход может быть полезен тогда, KorAa домен приложенин: клиента готов
начать выполнение длительной операции в потоке. использующем удаленный тип.
Например, если одно поточное приложение должно напечатать документ. содер­
ЖaIЦий 100 страниц текста. очень велика вероятность того. что удаленный САО­
тип или WКО-синглет может выйти за рамки отведенного для процесса времени.
Надеюсь, вы уловили оБIЦyЮ идею. хотя здесь. I{онечно, более "элегантным" реше­
нием является создание нового потока выполнения.

Спонсоры лизинга сервера (и клиента)


3aJ<Лючительной темой нашего связанного с лизингом обсуждения ЦИlwа суще­
ствования САО-типов и WКО-синглетов будет cnOl1.copcmвo лизинга. как вы только
что убедились, для каждого объекта САО-типа и WКО-синглета имеются параме­
тры лизинта. используемые по умолчанию, которые можно изменить неСКОЛЫiliМИ

способами. как на стороне сервера, ТаЕ и на стороне клиента. Но. независимо от


конфигурации лизинга типа. в конечном итоге время лизинга МВR-объекта исте­
чет. В этот момент среда выполнения отправит данный объект в "мусорник· ... ну.
хорошо, почти отправит.

Суть в том. что перед тем, как пометить ненужный тип для отправки сборщику
мусора. среда выполнения проверяет. не имеет ли данный МВR-объект зарегистри­
рованных спонсоров лизинга. Простыми словами. спонсор - это тип. реализую­
щий интерфейс ISponsor. который определен так. как показано ниже.
p ublic i nter Ea c e System.Runtime.Remoting.Lifetime.ISponsor
{
Time Span Renewal(ILease lease);

Если среда выполнения обнаружит. что у МВR-объекта имеется спонсор. этот


объект не будет сразу же отправлен сборщику мусора. а будет вызван метод
Renewal () объекта спонсора.. чтобы (еще раз) добавить время к текущему времени
лизинга. С другой стороны, если окажется, что для данного МВR-типа спонсора
нет. цикл существования объеlпа действительно закончится.
Предположим. что вы создали пользовательский класс, реализующий ISponsor
и вызывающий метод Renewal () для возврата КОlШретной величины времени (че­
рез тип TimeSpan). Тогда как ассоциировать указанный пm с данным удаленным
объектом? И снова это может быть сделано либо доменом приложения сервера,
либо доменом приложения клиента.
Для этого заинтересованная сторона должна получить ссылку ILease (с помо­
щью наследуемого метода GetLi fetimeSe rvice () на стороне сервера или стати"
Гла~а 18. Удаленное вэа~IМОАе~!:Тв~е .NEТ 741
ч~qtОro MeТQдa Rеmоtiл:g6е-гviсеs.G'еtLifеtimе:Sегv 'i. с,е () на стороне КJlИeIЛ'а) и
в~эвать Re~,isterO,

// Р.еrиС!'1'рация- спонсора ка ~paвe серзера.


CarSpon$C 1:' JI'1уSр,;:)Л:>ог = n~W Са:r~Р (}ЛЗQr!) ;
:r.L eas e Н.. J1Lе'8.sе:tпfо = C.;rLease) t .bls. GetLife-tirnеSеr','1се () ;
i tf:LeaseInfo. "Rеglэtе!: (rny S:pon SOT} ;

I/ Ре~Q'1'раЦй:. споисор~ на СФЭJ!оке JCJUteИ!1'а.


Ca.rBpGns·o,r тУЗРОП,5Рlё = 11,e w <;.a r$ pcmsQr (-) )
Car P'r ov:L der ер = new, Са :tP.rov .ider (ce rs). ;
lые,аэе i t.fLeas~I n 'f Q '= ( I L~~s e ) Remoting.S ervices. G'e "tLifeti1'iieSe!:v:ice (ер) ;
i tfLe a se l rJfo'. Regi s ·!:.e r ,(щуSро nsоr);

Б любом С--луч3.е. если. .к.J:IИевт И.iDI сервер желают о'Гм~нить с;понсорство" ЭТО
можно сделать с помощью метода I.Leas e. UnregisterO . ВЩlР1'iмер:
/ / ~~ние спонсора дn. ,цаниоro об'ЪеХ!1'8.
itfLeas€ 1 nf о . иI1 Т !;)g :i5 ter (ЩУSР О П$О r) ;

Зам.ечание~ Об"еКГbl ~лиента,. имеющие СПШIСОРЫ, кроме· ftео,бхQДИМОСТИ реали.эации ISрсщ.sь r


,DЩТJкНы быть r1РОI4ЗВОДНЫ~И от Harshal ByRefObj e.ct, поскоnы(У Jo:лиент AQJ[)Kef.l пер.едать
cnQHCOP в 'удаленный ДОМеН rrрилежени.я ,

.как видите., у-nравление ЦИКЛОМ ёyiцес'I'ВnВШnШ МВR-ТIШОВ, ку:муллтивно изме­


I:1)Ш)щих парам:етры своего' сос1'Ояния. в Процессе выllлнени.и..вы:ыо:вовB клиентов,

о:казываеТGЯ немного бо:лее ОЛОЖНЬ!М . чем ПРQСтая сборка мусора. На стороне пре ..
имущее тв мы имеем wtIJЮlCuе воэма:ffi;НОСТИ удрaвлeIIин ОТ1:JОСИТtmЪно того, КОТД8

имelПiО следУет уничтожить .удаленнЫЙ ТИП. С другой стороны, "iущес-rnyет вероят­


nОСТЬ того, что удаленный .1tИJl будет унИЧтожен без ведома кпиеRтa, Если romен1'
по.пьпается вызвать члены типа. уже удаленного »з .памяти. среда :выполнения
СГеЕерирует ИCЮJючение S;{$ ·t.e.m.R1 lr1t.ime.Hernotin.g.p-епюt ingЕ]tсерtiоп,.ив этот
1\IQМeHT клйе.нт МОжет либо создать новый 3RзеМ'IIЛ.яр удаленного типа, либо вы­
uQлнйть другие npе.цусмотренны.е ДJLЯ такого c.ny<rая действШL

Исхо#,мыА ICtЩ. Проекты CAOQar(J.eneralAsmLease, CA0Ca'rProviderServerLe$e 11 CДOCдrPfQviderCliemt ..


Lease размещены в nод~аr~логе, соотgетс.твующем ГЩЦIВ 18,

Альтернативные хосты ДЛЯ удаленных объектов


При И3Y'"lении материала ЭТОЙ главы вы СОЭДa.J-Ш rpyпny КОНСОЛЬНЫХ е:ер:верных
XOCTO:Q. оОеспечив<uощux. доступ к некоторому множеству удaJ1енных объекl'QВ. Е('Шl
вы имеете опыТ ИСПО.!IЬзоеан:ия R.1tассичоокоЙ модели DCOM (DisfIibuted Component
ObJect Modcl- распределенная модель lшмпонентнъix объектов). соответствующие
шarи могут пока3аThСП ваы немного странными. В мире DCOM обычно строится.
ОДИJil СОМ-сервер (на стороне серверэj. со~ржащий удаленные объе1tты. gоторый
неСет отвщственность и за прием за.просов. пост)'пающих от удаленного клиеНJ;а.

Зто едщiСТВe!iНое DСОм, nриложение *.ех!': "СПОRОЙНQ" зafРУ'lRается в фоновом ре­


жиме без создания, в общем-то, kIенужного KOMnНДНOTo Ol{па.
742 Часть IV. Программирование с помощью библиотек .NET

При построении КОМПОНОВОЧНОГО блока сервера .NEТ велика вероятность того,


что удаленной машине не придется отображать никаких сообщений. Скорее все­
го. вам понадобится сервер. который только откроет подходящие каналы и за­
регистрирует удаленные объекты для доступа клиента. Кроме того. при наличии
простого консольного хоста вам (или кому-нибудь другому) придется вручную за­
пустить КОМПОНОВОЧНЫЙ блок * . ехе на стороне сервера, поскольку система уда­
леНного взаимодействия .NEТ не предусматривает автоматический запуск файла
* . ехе на стороне сервера при вызове удаленным клиентом.
С учетом этих проблем возникает следующий вопрос: как создать невидимый
приемник. который загрузится автоматически? Программисты .NEТ предлагают
здесь на выбор две возможности.

• Построение .NEТ-приложения сервиса Windows. готового предложить хостинг


для удаленных объектов.

• Разрешение осуществлять хостинг для удаленных объектов серверу ПS


(Intemet Information Server - информационный сервер ИнТернет).

Хостинг удаленных объектов с помощью сервиса Windows


Возможно. идеальным хостом для уд;шенных объектов является сервис Windows,
поскольку сервис Windows позволяет следующее.

• Может загружаться автоматически при запуске системы

• Может запускаться. как " невидимый" процесс в фоновом режиме

• Может выполняться от имени КОНRретной учетной записи пользователя

Создать пользовательский сервис Windows средствами .NET исключитель-


но просто, особенно в сравнении с возможностями непосредственного исполь­
зованияWin32 API. ДЛЯ примера мы создадим проект Windows Service с именем
CarWinServi c e (рис. 18.7), который будет осуществлять хостинг удаленных типов ,
содержащихся в CarGeneralA s m. dll .
В результате Visual Studio 2005 сгенерирует парциальный класс (названный
по умолчанию Servi c el). полученный из Sy s tem.Servi c eProces s .Servi c eBas e ,
и еще один класс (Pr o gram). реализующий метод Mai n () сервиса. Поскольку
Ser v icel нельзя считать достаточНо информативным именем для пользователь­
ского сервиса. с помощью окна свойств укажите для свойств (Name ) и Serv iceName
значение CarServic e . Различие между этими двумя свойствами в том, что значе­
ние (Na me) задает имя, используемое для обращения к типу в npограммном коде ,
а свойство ServiceName обознаqaет имя, отображаемое в окне конфигурации сер­
висов Windows.
Перед тем как двигаться дальше, установите ссылки на компоновочные блоки
Car GeneralAs m.dll и System.Remo ting .dll. а также укажите следующие строки
директив using в файле, содержащем определение класса CarService.

us ing System. Runtime. Remo ting;


usin g S у s t еm.Ru пtimе.R е m о tiлg.Сh а ппеl s .Нttр;
u s ing Sy s tem.Run t Une. Remo ting.Channelsi
u s ing Sys t em. Di ag nostics;
Гла&а 16. УдалеННDе 8ззимодеЙ{;ТВИ'f! .NEТ 743

1~[3
--~ 'ii

S 'ii

': ". 'I -~"; ~, - - - - "--


it

РIЮ. 1'8.7, Созда.,~ие рабо"его П'РОСЧlан~ТlЩ HQВOГO проекta. Windows SefVioe

Реализация MerOAa Main()


M€Тoд Маiл (') JtJJзс-са Pro.g Lam обеспечивает ЗВПУСI( сервисов. опре.делен1lых
fI ррое:кте, путем передачи массива типов S-е.rviс-еНаз·е статйЧесномум:етоду
Selrvice :B.ull (). ПриумовИи. ЧТQ имя ПОЛЪЗ0ватwъс,кого сервиса было :иаыенено
с S е Л7iс·еl на СаrSеп'iее ,ВЫ дьлжны ИМf:'rь СJlе.цующее олр~деление кла.сC<J. (Д)'lЯ
Я<:JjОСТ~ программното кода эдесь удал.еныl Rомментарии).

s:tatic clas:s P'r Qgr а-т


j
s tatiG "oid Маiл()

Se rvi сеВа se [] Se.I:,,·i ces-'Г оR1J I1I


Serv i сез"ГdRllП = new 'Se rviceBa:se ГJ I new CarService /.) };
Servi СА--Вi!i Зе. RtjD (~!:! .tv i c.esTb:Run,) ;

Реализация метода CarService .,OnStartO


Вы . В.ероятно. уже доrадыэаетесь. какав прогр~ая логика долщна исполъаQ"
ваться npJoi запуске полъзова.'reJI1:;С1ЮГО сервиса, НЩ1ьмuим., что цеJIЬЩ C';J1:'Ser;vice
явля.е1"СЯ Быполnенис: Т'ой задачд. KOTOPyro ВJ>ЩЩlВЯД ваш :l{Qнсолъ~iJ с!:'р.вис ,
ПОЭТ.ому. чтоt)ы. ;щреги:стрировать Са r&еrvlсе.в Вlfде ~О-син.гJТ~, ДOCTYЦJ~1()ГO
ПО протоколу Н1ТР. можете ]],Q{Щвить в М,етод OnStart (') следу'Ющий программнь!Й
744 Часть IV. Программирование с помощью библиотек ,NET

код (при использовании сервисов Windows для обслуживания удаленных объек­


тов вместо "жестко" запрограммированной реализации можно использовать тип
RernotingConfiguration, позволяющий загРУЗИТЬ файл *.config удаленного вза­
имодействия на стороне сервера).

protected override void OnStart (string[] args)


{
11 Создание "OBO:rO HttpChannel.
HttpChannel с = new НttрСhаппеl (32469);
ChannelServices.RegisterChannel(c) ;
11 Ре:rистраЦИJI WКО-типа о.цииочно:rо ВioIЗОВ&.
Rernotingconfiguration.RegisterWellKnownServiceType(
typeof(CarGeneralAsm.CarProvider),
"CarProvideT. зоар",
WellKnownObjectMode.SingleCall) ;
11 Сообщение об успешном старте.
EventLog.WriteEntry("CarWinService",
"CarWinService стартовал успешно!",
EventLogEntryType.lnformation) ;

Заметим. что после регистрации типа в журнал регистрации событий Windows


(с помощью типа System.Diagnostics.EventLog) записывается пользовательское
сообщение с информацией о том, что машина-хост успешно запустила ваш сервис.

Реализация метода OnStop()


Строго говоря. CarService не требует никакой программной ЛОГИI<И остановки.
Но ДЛЯ примера давайте направим в EventLog еще одно соБыIие •. на этот раз с Ю:I­
формацией о завершении работы пользовательского сервиса Windows.
protected override void OnStop()
{
EventLog.WriteEntry("CarWin5ervice",
"СаrWiпSеrviсе остановлен",
EventLogEntryType.lnformation) ;

Теперь. когда сервис полностью оформлен, следУЮщей задачей является уста­


новка этого сеРВИСа на удаленной машине.

Добавление установщика сервиса


Чтобы получить возможность установки сервиса на выбранной вами машине.
нужно добавить В текущий проект саrWiпsеrviсе ДОПОЮ-IИтелъный тип. д,ля уста­
новки любого сервиса Windows (созданного .как средствами .NEТ. тю< и средствами
Win32 API) требуется создание в реестре целого ряда записей. которые позволят
ОС взаимодействовать с сервисом. Вместо создания зтих записей вручную. можно
просто добавить в прое.кт сервиса Windows тип
Installer (установщик). который
правильно сконфигурирует тип, производный от ServiceBase. для установки на
целевой машине.
'ГЛЭJlВ ,'8. УД8Л01'111Ое вэаИМОl1.еЙСiSие .1II1;T 745
чtоб'ы добавить УС1'аНОml1иIt дм CaTS~rvice. ОТl(рvйте окно uроектирования.
сервиса (с помощью ДВЬЙНОfО щe.тгmа ga файле C'ar S'erv i се. сэ в щmс Solutlon
Explo'ter). щелкните правой кнomroй в mo!50М месте ОIЩЗ uроеJ{~РОвавия сервиса
и выберите Add InstaHer [добавихь уеТa:JЮВЩJШ). рис. 18.8.

'Sёrvj..~iаiЪ '.П"'i;~Р%l" .,.' •. , """"' _~.l....-, ';i,ji. " ,, '!'!.!!!,


,~,·::'t "';"', ~", . . ..;~~;

Рис. 18.8. ДоБЩI:J l ение устаНGВЩИI(В ДЛЯ rmльзавательсфгосервиса ,WiлeJоws

В pe3yJты:aтe будет добавлен HOBьm ~мпонен1:. RОroрый О,Ra3ыва~тся произво­


ДJ'fblМ fiа:ЗdвогоХл.асса system.Config1Jraticm.Ins'tal1.Installer. В OJцre проек­
ТИ;JЮвания теперь будет два компонента. Тип Ееrviсеlnstаllеrlпредставмет
установIП,ИК кшiкретного ёервиса в вашем npoeK1'e. Бbiбрав соответствующую IIИR­
тограмму. вы обнаружите. что в окне properties ДЛП ево'Й'сnщ Se:t:viceName устанОВ­
лено значение типа CaTService.
Второй EOМIWi:l:eHT (servi-сеРrосеэsln:>tаllеr:1) позволяет за,дать ОКРУЖ~Щ1е,
в котором будет 'ВыполняТься устаноgденный серва.е, по У}.>Iолчанию зиаче:щt'ем
свойcrва АсСQшft (учетная записьJ явл.яется Пsеr (nОJIЪЗ0ватель).. С помощью ОЩIa
СВОЙСТВ Visual Studio 200.5 И3МeIOIте это значение на LocalServiee (ло.калъJЦIЩ
сервис). рис .18.9.
Вот :и все! Теперь С1СОМПЮlйрyй'rе свой проекг.

~J

ADaиInI:
~~.~iI'hir:n~~ l8i1I
trж1..

Рис.18;9. Идентификация CarSetv ice

1
746 Часть IV, Программирование с помощью библиотек .NET

Установка CarWinService
Установка CarServ ice. ехе на мanшне (локальной или удаленной) предполarает
выполнение двух действий.

1. Перемещен:ие скомпилированного компоновочного блока сервиса (и всех


необходимых внешних компоновочных блоков - в данном случае это
CarGeneralAsm.dll) на удаленную машину.

2. ЗапуСI< средства командной строки installutil.exe с указанием соответ­


ствующего сервиса в качестве аргумента.

После выполнения п. 1 откройте командное окно Visual Studio 2005. перейдите


в каталог с компоновочным блоком CarWinService.exe и введите следующую ко­
манду (этот же инструмент МОЖНо использовать и ДЛЯ деинстaJlЛЯЦИИ сервиса).

installutil carwinservice.exe
После установки сервиса Windows вы можете запустить и сконфигурировать
его с помощью аплета Services (Службы) Windows, который размещен в пап­
ке Администрирование панели управления Windows. В окне Services выделите
CarService (рис. 18.10) и щелкните на ссылке Start (Запустить), чтобы зarрузить
и вьшолнить соответствующий двоичный файл.

Start the service

storted

Рис. 18.10. Аплет Services Windows

Исходньtй код. Проект CarWinService размещен в подквталоге, соответствующем главе 18.

Хостинг удаленных объектов с помощью 115


Хостинг удаленного компоновочного блока с помощью сервера lIS (Тnternet
Infonnation Server- информациОННЫЙ сервер Интернет) даже проще, чем созда­
ние сервиса Windows. поскольку сервер IIS специально запрограммирован на то.
чтобы получать поступающие запросы mТP через порт 80. Поскольку ПS является
Web-сервером. должно быть очевидно, что IIS может осуществлять обслуживание
только удаленных объектов, использующих тип HttpChannel (в отличие от серви­
са Windows, КОТОРЫЙ допускает также Использование типа TcpChannel). С учетом

l
Главе 18. УДaJ1еюнre sзаимодеЙСТ1Jие .NET 747
этого ограничения. при испo.JIЬзовании ns.д,JЩ поддерЖЮI удаде:нного 1fЗ8ИМодelit­
ствия необходимо выnoл:нrrtь следующие дейc;rВия.
1. На жестком дисне создайте новую папку ДЛR храневин GarGеnеrаlАзm .• cll1.
Вэтои пarше СОqД8Йте подкаталог \Вiл. ско.пирyй'rе файл CaтGenera<lAвm.dll
в этоrr подка;гадor fiffi.npимер. С; \ПSСаrSеrVl'се \BiI1}.

2. На машине-хоете откройте tЖlющтета: Internet lnfonnatlon БеМсее [разме­


щеН1ЮI'D в naпие ААМJ:1НИ~ТРVlрование данели ynpавлemm WmdowsJ.
3. Щелкните правоЙ 1ШоIIКОИ .Б с:гршi:е узла Default Web Site (Web-УЭeJI по умолча­
шna) И выберите New<>ViгtuaJ Directory из noявившегосЯ' шлrгeJtстного меНю.
4. Создайте ниртуаЛЬ!-I1;dЙ ЕЭХЗ;llог.соотв€тсТJ3УЮЩИЙ ТОЛЬRО что СОЗЩIНI-lОИ вами
IWрневой шцmе [С; \ПSСаrSеrviсе),.Qста.л:ыще значmщя. преДЛОЖe.l'ffiые ма­
стером СD$дания Бир-1УЗЛЬНОro Jt~юга, БУдУ"Г вnoднe ПQДХОДН;ЩИМИ.

э. НаЕонец, создmrте НОВЫЙ файл КОНфmypации С l>1--менемwеЬ.GОПfig ДЛЯ на­


стройки параметров регистрации удалею-Iых ТИПОВ вйртуалъным l~ата:логом
(см. оледующи:й: фрarмmrr праграммнОТQ кода). сохраните Э1'от файл в соответ­
ствующей ROрневЬЙпanR.е (В данном случае эта I1aпка С:\ПsсаrБе.rviсе).

<СОПfigurаtiОП>
<31''' t.em. ruпt il1\.e. rеmоt:Lлg>
<.appHc~ t ion>
<ser\'iG€>
<w'e 11. kflown
Iтюdе=" SingTetOB"
'ly-pe= "t-arGеn.еral.АsR1. Ca.rP IQ'oТide:r::, СаrQenеIв.'lАsЛl"
:ОЬ:I ect~jrj ="сз·rргоvidеr. Боар" J>
<JэеrVlсе>
< сlылш? 1 s>
<clIarme1 уеЕ=" J]tt_рп 1>
<.} с·flёlлnеlз;;.
<:/applicat io·l'1>
<1 systern. п1:tJ time, re:тotiDg>
<./еоп1iguгаtiФп>

Теперь файл Саrt;;еnеr;ЙАsщ.dl1 буде'J' доступен для Н1ТР-зanpосов ПS, ины


можете обиовил, файл * .'сопflg на 'стороне ШIИенra так, ка:в: I10кa3aIfO .ниже
[конечно, YRазав Б нем имя своет.о II8~JШСта).

<сфnf i 'IJ'ira ti О11>


<"systeIТ1.. r·untime. rеmоtiпg>
<аррl iеа.tiоп>
<.cli,ent displayName = "CarClient">
<we:11 \C1!own
'type=" Са гGеnеr<ilАэш. CarProv i d"" у, Car.GeI1 е r<з). А Э!l"t "
Uy:t = "h t.tp : I /Name'l'heRemoteIISt[ost/ I1SСа.:rИоst/ caLpro'V'ider . воар n j:>
<!сli.елt>
<.channels>
<с,-hgлпеl ref="http" 1>
<. / с ha.rm е L·s >
о::: / аррl i cat ion;'
< 1'..systenl. LOn time • .сеmоtiпg>
</СQn:tig'lлаtlоrv

росле этогjJ вы см(эжете ВhШЬДl-lЯТЬ приложение ЮIИентатаи.же., 1ШR и раньше.


748 Часть IV. Программирование с помощью библиотек .NEТ

Асинхронное удаленное взаимодействие


в завершение нащего обсуждения материала данной главы давайте выясним,
КЭЕ вызватьь члены удаленного типа асинхронно. В главе 14 была рассмотрена
тема асинхронного вызова методов с помощью типов делегата. как и следует ожи­
дать. при асинхронном вызове удаленного объекта компоновочным блоком клиента
первым шагом должно быть определение пользовательского делегата, представля­
ющего соответствующий удаленный метод. После этого вызывающая сторона ДЛЯ
вызова метода и получения возвращаемых значений может использовать любой из
подходов. описанных в главе 14.
для примера создайте новое консольное приложение (AsyncWKOCarProvider-
Client) и установите в нем ссылку на первый вариант компоновочного блока
CarGeneralAsm.dll. Теперь измените класс Program так, как показано ниже:
class Program
(
/ / Де'n:eI'аor ,цnll ме'1'ода GetA1lAutos () .
internal delegate List<JamesBondCar> GetAIIAutosDelegate();

эtаtiс void Main(string[] args)


{
Console. Wr i teI,ine ("старт клиента! Для завершения нажмите <Ел ter>") ;
RemotingConfiguration.Configure
("АsулеWКОСаrРrоvidеrСliепt. ехе. eo nfig") ;
/ / Соsдание ПОС!1'uщика машин.
CarProvider ер = oew Ca. rProv ider () ;

// Создание делегата.
GetAIIAutosDelegate getCarsDel
new GetAIIAutosDelegate(ep.GetAllAutos);
/ / Асинхронный вызов GetA1lAutos () .
IAsyneResult ar = getCarsDel.Beginlnvoke(null, null);

/ / Ииитация: активности Jt.n:иента.


while(!ar.IsCompleted)
( СОПSОlе.WritеLiпе("Клиент работает ... ");

/ / Все сдеnаио! Попучение возвращаемоI'О знаqеНИII делегата.


List<JamesBondCar> allJBCs = gеtСаrsDе l .Епdlпvоkе(аr);

/ / Испonьзование всех машин из списка.


foreaeh(JamesBond Car j in allJBCs)
UseCar (j) ;
Console.ReadLine();

Здесь приложение клиента снача.па объявляет делегат. соответствующий сиг­


натуре. метода GetA1IAutos () удаленного типа
CarProvider. ПоCJ1е создания де­
легата имя вызьmаемого метода (GetAIIAutos) передается ему. как обычно. Потом
запускается метод Beginlnvoke (). сохраняется результирующий интерфейс
IAsyncRes ult и имитируется какая-то работа на сторонеклиеН1'а (напомним. что

L
Глаза 18. УЛ11.11еflное взаИМОД&ЙСТВl1е,NЕТ 749
свойство IАsуtюRеs.ul t . IsCornpleted ПDзволяе:т :ВlilЯСНИТ~. завершил ли раБОту со­
ответствуюЩий Me:roA). После завершещrя работы ~~~нтa вы получаете список
list<>. возвращеннмй методом C,s,rPr.ov ider. G~tAllAutos О 'в результате вызова
члена Ehdlnvoke (). и передаете _кaжд:blЙ объент Jamf;';1.sBondCar ста'ШЧ"ескои вспо­
могател:ьной фунтщии с- им~нем US€C~,r ( ) .

pp.l::iHc st~t.lc vai<J Uзе 'Сах (JaroesB,:mdCar j)


{
Cdnso,i e. Wri t-el!ine ("Може-'1" ли машина летать -: -( о J" r
j . l-s.F'1iq,Ь tWGrkJ!y) ;
~О(lSDlе. WI i 1-еLiпе ("'Может JШ M31.W1F.8. 1I.j1-, &вать? {Р}" I

j .isSe.aWorthy) ;

Снова подчерннем . что Jфасата использования ТЮlа делегата .NЕТэaronoчаетс,к


аТОМ, ЧТ(I форма аСИНХронного вызова удалеыных методов оказыва~тi"..я аналогич­
ной форме вызова ло:каяьных Meт~ДOB.

ИОХQДНЫИ IЦIA. Проект А&уnсWl<ОСаrРrоvldеtСПепt размеЩеН 6 'паДкаталОГе>, Сl1l0твеТОТllующем


главе 18.

Роль атри,бута !,OneWay]


Предположим. что CarPro'\,··leJex должен иметь метод AddCar(). nPИRИ~ающий
в rщчестве входного лараметра JamesE:ot1dCar и не воаврщцшоIЦYШ НИ"Jего_ Здееъ
главное то, ч-rо метод не воавращает ниЧеiЮ. Из на:зnaниn lWacca SW13tem .Runt iше.
RemCJt ing. Иеssа ging. OneWayAt'tr.:! bl.lte моЖно дога.даТl:lСЯ. '1то 8 данном с,,'l)'Чае
слой удалf.;:нного вЗайМqдеЙсТВия. .NE'fnepeAaeT ВЫЗОВ у,М:леI-ПЮЙ (."ТОроне OДНOCТtr
ронним способом и не заоОТИ7СЯ о создании инфраструктуры, н~обходJiJМОЙ ДJ,IЯ
возврата 3I?:~чеRИН (отсюда.и название оne-шау -- одностороЩfИЙJ . Бот соответ­
ствующая 1'40дифmtaДИя класса.

11 "ОБИ'iI!еm." а11'риб~а [~eWay].


iJ.siilg 3у&tеm,Ru д ~imе.Rеmоtinч.Меssаgi.ng;
..... '

TIaIt\eSpa-6е- CarGeneI;al~sm
i
v'Ubl i c cla.ss Carp:to'Jtder t1a:r.sha:lBYRefOpject
(

1/ Клиен'1' "'0*е'1' ВIЖS1!Iа,'1'Ь СОQ'»Иец<сItlВYDЦИiI ме'JIO;Ц


// и j sабt.rrJ:,· , о -~_.

['О .... еwзуj


publi с 170id I\ddCax (Jame:s!3ondCar i'Le .WJВC)
( trn€ JВl:аГ5 . Acld (newJBC) ; )

ВЬЦ~Ь1йающая стороаа ВЫi3;ЬПlает э'I'()т метод так, как обычно.


750 Часть IV. Программирование С помощью библиотек .NEТ

/ / Создание ПОС'I.'АЗщиха Машин.


CarProvider ер = new CarProvider() i
/ / Добавление новой кашины.
ep.Addcar(new JamesBondCar("Zippy", 200, false, false));

С точки зрения клиента вызов AddCar () является полностью асинхронным, 110-


скольку среда CLR обеспечивает использование фонового потока для запуска уда­
ленного метода. Поскольку AddCar () сопровождается атрибутом [OneWay]. клиент
не может получить от вызова никакого возвршцаемого значения. НО. IIОСКОЛЫ<у
AddCar () возвршцает void, это не является проблемоЙ.
Вдобавок к указанному ограничению, следует также знать о том, что при на­
ЛИЧИИ у метода с атрибутом [OneWay] выходных или ссылочных параметров
(определяемых с помощью ключевых слов out или ref) вызывающая сторона не
сможет получить модификации вызьmаемой стороны. К тому же, если вдруг метод
с атрибутом lOneWay] сгенерирует исключительное состояние (любого типа). вы­
зьшающая сторона ничего об этом не узнает. Удаленные объекты MOtyT обозначить
некоторые методы атрибутом [OneWay] только тогда. когда вызьшающей стороне
действительно позволяется вызвать эти методы и "забыть" об этом.

Резюме
В этой главе были рассмотрены варианты конфигурации компоновочных бло­
ков .NEТ. IIозволя:ющие совместное ИСIIользование типов за рамками одного при­
ложения. Вы увидели, что удаленный объект можно сконфигурировать. как MВV­
или МВR-тип. Именно от зтого, в конечном счете. зависит ТО, как будет реализован
удаленный тип в домене приложения клиента (в виде КОIIИИ или с помощью про­
зрачного агента).
При конфигурации типа для работы в качестве МВR-объекта перед вами воз­
никает целый ряд соответствующих вариантов выбора (WКO или САО. синглет
или объект одиночного вызова и т.д.). Все имеющиеся варианты были рассмотре­
ны в ходе обсуждения материала данной главы. Также были рассмотрены вопросы
управления циклом существования удаленного объекта. реализуемого с помощью
схемы лизингового управления и CIIOHcopcTBa лизинга. Наконец, снова была рас­
смотрена роль ТИIIОВ делегата .NEТ, используемых для аСИНХРОJ-lliОГО вызова уда­
ленных методов (и здесь такой вызов по форме оказывается аналогичным аСЮI­
хронному вызову методов локальных ТИIIОВ).

L
1
ГЛАВА 19
Создание окон
спомощью
System.Windows.Forms

Е ели вы npочитми пре.дыдущие 18' глав. В1У должны ИМеть ~ОлиднyIQ базу ДЩТ
использования нзьma rrpоrpам.миров·щrин С# и арХИ'J'a<Jj-РЫ .NEТ. ВЫ, ДОН~Ч­
НО же, можете 1IpИМ:енит.ь шщуч:енные энанНя д,1ПI построения КОНСОЛЬ~ ЦP~(Г
жений сJ1е;дующего поколеНИJI (кaR сд-уч:ноI). :60 B~C. наверное. больше ИJ;I'J'ереqyет
создание привлеюпельного графического ицтерфеt;icа ПОДЪЗОВ8'.геля (GUl), КРТ<фЫЙ
позвошrr пользователям взаимодействовать с ЩlIпей сщt:·темоЙ..
ЭТа tлава является первой азтvex I'лав. в HoTopЬL1I: обсуждаетсf.j: процесс постро­
ения ТРaдlЩИоН1НЫХ приложений на oCH!i1!Ie иcnо,ц:ьзования тЭR назъmае.м;ых форм.
Вы узнаете, }(ак С'оздать »выQNохудomecтвеmюе"" rnавное OJCНO, испольву,я lЩaссы
Р'огт и Applicat],cm. В ЭТОЙ главе -rаюке доказано. как.Ц KOIJTeКCTe GШ-окруже­
ния выполнить захват nОЛЬ30ватедъскоro ввода и ОТВ~ИТЪ На него (т.е. обрабqтать
событии мЫШИ и кла.вйатуры). НаЮ)]Jец, вы узв;аете, к!щ вручную или с l;ЩМОЩью
инструментов npоектиров;цшп. ВL"'ТроенЯbIX. В Vi~uaJ. ·Studio 2005, RQ~етруировать
еистемы меню, панели :внструментов, С1'роJtи ооС';тоlfIНИSI J.l интерфейс МDI (blultiple
Document Interface - много.дшtyментньtй интерфейс пршюже1;ЩЯ').

Обзор про·странства имен S.ystem.Wind'ows.Forms


Кa.R Ji любое другое пространство UMeH, SуstеПl.Wiлdоwв.Fш:roS компонуется
из раsJll.[ЧН1!IX классов, CTpYJ'tTyp, делег<!.тОв, интерфейсов и пер('чнеЙ. ХОТЯ раз­
личде между KoRco~1Iыlil (СИI) И rр'афJifЧсс1mМ (аш) интерфеЙtамй. на первый
вarnяд. J~ажется подобным Р<ЩIlJ1ЧЩО Между НОЧЫО И днем, фаъ.тич.ески ДJLЯ. С03-
даНШ! приложеншr Windows F!эПIL9 нео(jходимо т.оЛbll.оQСвоеНйе правил ММlЮIy­
lUЩИИ НОВЫМ мщ)жеством ТЩJОВ с ~<Щользованием тorо синт.ав:сиса. С#. lЮТ0рЫЙ
BlЦ уже знаете. 'с DьщокоуровневQ~ ТO'DW зрении. сотни ТИIIОБ npощранства Имен
sуgtеm.Wi,!'цjФW8.;ВОоrmз МQЗЩilО объеди:н:mi> в EJJe,цyIOщие БолыII1J:-кзтегор:ии..

• Базовая uнфрасmруют!JPQ- ,QTD тицы' npеДСТаБJIJUOщие ба;зовые операции


цpoгp-aммы .NEТ Еоrшв (.Fолn. Аррliсstiоп ~ 1:д.l. а ТВJoкe рааличнЫе ТИIIl:iI.
обеспечивающие С:QБМеСТИМОСТЪ с разработа.щц.щи ранее элементами ynpав­
лешш ActlveX
752 Часть IV. Программирование с помощью библиотек .NET
r
• Элементы уnpавлен.uя. Все типы, используемые цля создания пользоватедЬ­
с!'ого интерфейса (вuttоп. MenuStrip. ProgressBar. DataGridView и т.д.),
являются производными базового класса с о n t r о 1. Элементы управления
конфитурируются в режиме проектирования и оказываются видимыми (по
умолчанию) во время выполнения.

• Комnон.ен.ты. Это типы. не являющиеся nроизводными базового клас­


са Control, но тоже предлагающие визуальные инструменты (ToolTip,
ErrorProvider и т.д.) цля программ .NEТ F'onns. Многие компоненты (напри­
мер, Timer) во время выполнения не видимы. но они могут конфигурировать­
ся визуально в режиме проектирования.

• Диалоговы.е окна общего вида. Среда Windows Fonns предлагает целый ряд
стандартных заготовок диалоговых окон для выполнения типичных действий
(OpenFileDialog. PrintDialog и т.д.). Кроме того. вы можете создавать свои
собственные пользовательские диалоговые окна, если стандартные диалого­
вые окна по какой-то причине вам не подойдут.

Поскольку общее число типов в System.Windows.Forms намного больше 100, ка­


жется нерационалъным (даже с точ.ки зрения экономии бумаги) предлагать здесь
описание всех элементов семейства Windows Forrns. В табл. 19.1 описаны наиболее
важные из типов System. Windo\oJS. Forms. предлагаемых в .NEТ 2.0 (все подробно­
сти можно найти в документации .NEТ Framework 2.0 SDKj.
Табl1ица 19,1. Базовые типы пространства имен Sys tem. Windows. Forms

КлаСС.,1 Описание
Applic·ation Класс, инкапсулирующий средства ПОРдержки
Windows Forms, необходимые любому приложению

Button, CheckBoX,ComboBox, Классы, которые (вместе со МНОГИМи другими


DateTimePicker, Li.stBox, LinkLabel, классами) определяют различные GUI-элементы,
MaskedTextBox,Mo~thCa1endar, Многие из зтих элементов подробно будут рас­
PictureBox, TreeView смотрены в главе 21
FlowLayout Panel,TableLayoutPanel Платформа . NEТ 2.0 предлагает целый набор "ад­
министраторов оформления", выполняющих авто­
матическую корректировку размещения элементов
управления в форме при изменении ее размеров

FOrm Тип, представляющий главное окно, диалого-


вое окно или дочернее окно МОI в приложении
Windows Forms
ColorDialog, OpenFi1eDialog, Представляют различные диалоговые окна, соот­
SaveFileDialog, FontDia1og, ветствующие стандартным операциям в рамках GUI
Printf'reviewDialog, FolderBrowserDialog
Menu,Ma i nMen u ,Menultem, Типы, используемые для построения оконных
ContextMenu, Menus t rip, и контекстно-зависимых систем меню. Новые
ContextMenuStrip (появившиеся в .NET 2.0) элементы управления
MenuStrip и ContextMenuStrip позволяют
строить меню, содержащие как традиционные

пункты меню, так и другие элементы управления


(окна текста, комбинированные окна и т.д.)

StatusBar, Spl it ter, ToolBar, Типы, используемые для добавления в форму


ScrollBar, StatusStrip, To ol St rip стандартных элементов управления
1I ГЩIЩl 19. С{)ЗД8ние ОКоН с ПОМОЩЬЮ System. Windows. Formэ 7SЗ

! Замечание. Вдобаво~ I!. Sys.t!:!m ..wi.ndo1t<'S.Form~. IШМ[10новоч~ыйблок System, WlndQW·S.


Forrns.dll ОllределщlТ и другие пространства имвк, предназliачеtlНые ДnЯ ПОДD.ер)i(1<И эле­
меЫТО8 tpафИ"lеСКОГQ Иliтерфейса ПОЛЬ:Щ)Jателя .. С~)DТ8етствующИ"е допоnнитеlJЬ~lые ТИПI:II ис­
ПQльэ.УЮТGЯ. В OCI108~/OM·,· ВI"fYТP8~IНИМiII мвхаНiII<Jмами создания фQРМ и/или' разработки Visual
Studlo 2005. По Э.'ТоЙ ПРI:1ЧИl;tе Mbl QграFf~чиМt)я раСQМО'ТР6J~ием баЗQВОГQ проетранства l!fIiIek
S УБ t ею. Wi·n,dowB . Forms.

Работа с типами 'Windows Forms


При построеНЩf IJрило~reюm WIndows Forms вы може-l'е, при желании. создать
'Весь соответствующий цроrраммный .RQД вручную (:wmpимер, в редакторе Блокнот
ThxtPad). а ;:!а.тем отправИ'FЪ файлы *, 'СВ :КОМШUIятору командноЙ
.ИЛИ в рещu.:торе
строки С#с флагом /target: ~linexe. Построение весколъ1GlX npиложений WLndows
Forms вручную дает Щ' тщrыj:o бесценный 01JЪй, НО И IIlli',шгает лучше понять про­
граммнJ.>[Й :код, генери;руем;ьщ грl'lфичесЮifМИ средствами проекrnравal1ИЯ. предла·
r:аеМPIIvJИВ Р8.'МШU!.' naReтoJJ интегрИровamroй среды разработки .NEТ разRЫX про­
нав~)Дителеj,i I
Чтабь[вы МОГЛИ понять ОС}:JОИhJ процесса создания npилoжeRИЙ Windows Fbnns,
в первых ЦРИ1lij:ерах этой главы не и~пользуются графические средства проектl'фо­
вания, ОсвоЩl цр(щесс цостроения приложенйЙ W1n:dowsF'G-гms ~беэ помощи мa~
етеров ", вы без труда сможете перейти 'к ИСIIОЛЬ30ВainiЮ различн'Ых инструментов
разработкщ, ~роеЩiblX в Vi,sLlaI Studio 2005.

Создание rлавного окна вручную


в начале изучения приемов програ:ммиров8НШl WindOws Farms мы поСТроим са­
мое простое главное oRНO, так eKa38.'IЪ·, ~C чистого :ЛИСта". Создайте на своем жест­
~O1-1 диске новую па:гщу (ЩЧфимер, C:\MyFirstWindo,w) 11 в этоп пamtе с помощью
любого TeRCToJ;JGrO ре:щu.crор~ СDздайте новый фaibI Иаiп.Wiпdоw.Сs.
В Wln<io'N$ Fo.FIIls КЛ;:I.С{) Form используется длн представления тобаго окна в
ЦРИЛОЖeнIЩ.. Этр относится И R ТЛЮJНому о:кну. ЮiXQД.mцeмyся на вершине иерар­
JШИ окон:в щрщюжен:ии с шrrер<ре"йсом5Dl (SШglе-Dосument Interface- ОДНОДОКУ­
ментНЪ1Й ИRтерф\ffiG). и R модальНЫМ и немодалъным Дйалоговым окнам. и l{ роди­
тель~ .., дочерним окнам :в npиложeюm с инте:рфейсом МШ (Мultiрlе Docum.ent
Interfa,ce - МJ:;lОГОДОl\УМентныИ ИНтерфейс). Чтобы создать и о-гобраэитъ rJ'Iai!Hoe
ОiШQ прцлошеди.а. необходимо въщо.л:нить слеДУЮщие два обязаl't\'ЛЪНЬТХ IШirа.

1. Получ~ть новый масс из Syster.-I_WiI)qОWs,FоrInв,Fоrm.

2. Добавить в метод rмi 00 npиложе}JИЯ ВЫ-З0В метода АР'р1iсаtiоn.Ruл (). пе-


редав Э!fQМУ методу эиэемwIЯР проиэводвого от Form тlЦЩ в вццеаргумента.

Поэтому добавьте в файл MainWipp'pw.cs сл~дующее опреде.ш.wи~ Юlаоса.


1J'.91 t'lg Буstеm:
1JcS.l Il'g Systern.. Windows. Fоrшs,;

r! a:me5pa~e My WiOOOwsApp
{
public (Jla's,s МаiпW1Л .dоw F"orm
754 Часть IV. Программирование с помощью библиотек .NET

/ / Выполнение nPИJIожеИИR и идеН'1'ИфихаЦИJl t'лавноt'о окна.


stat ic void Mai n( st r ing [ ] a rgs}
(
App li c a t ion. Run (new MainW i ndow()) ;

Вдобавок к обязательно присутствующему модулю ms co rli b .dll. приложение


Windows Forms должно сослаться на :компоновочные блоки Sys tern. dl l и Syste m.
Windows. Fo r rn s.dll. Вы. мож ет быть. помните из главы 2. что используемый по
умолчанию ответный файл С# (файл с s с . r s р) дает указание с 5 с . е х е автомати­
чески включить эти компоновочные блоки в процесс IЮМПИЛНЦИИ. так что здесь
никаких проблем не ожидается. Также напомним . что опция /tar get:wine x e ком­
пилятора csc _exe означает создание выполняемого файла Windows.

Замечание. Строго говоря, можно построить приложение Windows и с помощью опции


/ target: ехе компилятора сзс . е хе, но тогда кроме главного окна полученное приложение
в фоновом режиме будет создавать командное окно (которое будет существовать до тех пор,
пока не завершит работу главное окно приложения) . Указав / ta r g e t : wi nexe, вы получите
приложение, выполняемое в так наз ываемом " родном" для Windows Forms режиме (без созда­
ния фонового командного окна).

Чтобы с компилировать файл программного кода С#. откройте окно командной


строки Vlsual Studio 2005 и .выберите следующую команду.

csc Ita r ge t:w in e xe *. cs


На рис. 19.1 показан результат запуска полученното приложения.

Рис. 19.1. Главное о кно в стиле Windows Forms

Понятно. что такой результат примененин средств Windows Forms впечатления


не произ.вОдl1Т. Но обратите внимание на то. что путем получения простой произ­
водной от For m мы создали главное окно. допускающее минимизацию, максими­
зацию. изменение размеров и Зal<рытие (да еще и с пиктограммоЙ . предлагаемой

...
Гnааа 19. Создаl1И~ ОКО!1 t помОЩью Syst~m :W\n.dоws.fоrmэ 755
Сiютемой по умолчанюо!,), В О'l'JЩ'<J;ие от дрyгщr. .средств р8зработЮi гра.фическоГ9
шrreрфейса от МicroS'oft. которые :ВЫ. :возможно, и~пOJJ1.ЭовМИ ранее (В час:mос'l'Ц.
это касаетсябя.бJЩQТeКИ 6а30ВЪЩ RЛассов МFC) . теперь lleт необходимости сщrзы­
lIать ~отни ·строк .npoГР8JЮdНОГО JЮда ОООТВОО'C'fвующей Шlфраструюуры (фреймов ,
докумeн-rQй.. представлений, приложeIПШ и .нарт сообщений). в QТЛИЧИ6 от прило­
женйЙ "'in32 ЛPI. использующих С. :щесь .нет необходимости Вру<-UI)'Ю реа;щзОБЫ­
BQ7Ъ процедУРЫ WinPI;oc () и wi (l~ain (). в рамках платформы .МЕТ эту ·гPftзнуЮ~
рабо'ry' выпоmцuот элементы, ИН:КaJТсу.;шрованные- в ТШJaX Form и Appl i c at.i.ofl.

ПРИНЦИП разrраничен' ИИ оБSlзанностеи


Сейчас класс МаinWiщiоw щrредeamет :метод Main () в рамка.х своего контекста.
Но. если хотите. МQЖно создать другой стат.и."ЮСкий клас{) lназове~ еГ!il Prog ram),
который бwr.ет отвечать за запуе!:l1 главпого о:кна, а задачей класса. I1pоиавQДНОГО 9т
For m. останется Henoep~e.твe:ннoe отобра:жеНйе о'КНа.

п·атеэраое Му WiCJ.elФwsАрр
{
11 Гариое oJtНO.
рu,ЬН С. clasB Иа iriWi r!dow : iform \ )

11 ~1iПt'%' пpИnо~.
pLJb lic SЦitiс. с lа з s Prog :ram
{
5·t atic VQid I'!,.H n ( ~t .t :i Jjg [] arg sJ
!
{! Не sаl!SУДЪ!l'е о 'us'inq' ~Я: SYStelll.Windo1!rs.Forms!
:i\ррliсаt:J.оп • .R!.iП (rlew MainW.indow () 1;

в результате вы обеспечите поддержку одното иа ГЛа5I-1ЪU: n::pа:вшr объe;!ttно-ор:и­


еНТй'рО8aRНОГ() программиров~ - разарт-Luчен.uе обя.заl-tl-tосmеil. Это правило
требует. чтобы класс б~ ответственен за ВЬ1Потreние МИЦW'49ЛЪ.во· -"овможного
объема работь.1. РаЭАедив ИСJЮДEiЫЙ масс на два отдельных RЛl:I.сса, вы тем самым
отделили форму Щ' С93Дающе:го ее ЮIасс~. Реэул.ыrатом оказываt:тся более мобиль­
ное (В смысле перенщ1ИМОСТИ) ОКНО. ПОСR.ОЛЬКУ его теперь :МОЖJ{Q ПОМ(jСТИТЬ 8 paм~
1m любого IIpOeК.'Щ без ,Д{шолвит-елъного метода И" iJj () . спепифичноГQ ДJIЯ данноrо
ароекта.

McxQДныl} КОД. Проект MyFJrstwil'lQOW Рi3<!М8щвн :в лоДКмалоге,. ооо-rвеl'ствующем mаве 1.9.

Роль класса Application


Класс Appli c atiQo оп:р~деля.е:r множеетВQстатнческих членов~ ПОЗВОЛЯЮЩИХ
управ.л.шъ noве.цеНИeJ\{ различных НИЗI<ОУРQвне,вых элемевтОВ .npиложeшm Window:>
FbmiВ. Класс Аррl lса.t.iС>Л оцред~ляет наБQР C'9~1:iГl'Jrii, IIО3ВОЛ.moщих. реэrцроватъ.
нanpJ.lМ'ep , на завершение раБQТЫ .nриложe.mщ или переход в состояние ОЖИДaнlfJl.
т
756 Часть IV. Программирование с помощью библиотек .NEТ

Кроме метода Run (), этот класс предлагает и другие методы, о которых вам следует
знать.

• DoEvents (). Обеспечивает для приложения возможность в ходе выполнения


операций. требующих много времени. обрабатьmать сообщения, находящих­
ся в это время в очереди сообщений.

• Exi t ( ) . Завершает вьmолнение Windоws-приложения и выгружает из памяти


домен этого приложения.

• Е n а Ь 1 е Vis u а 1 S t У 1 е s ( ) . Настраивает приложение на поддержку визу­


альных стилей Windows ХР. При активизации поддержки ХР-стилей ука­
занный метод должен вызываться до загрузки главного окна с помощью
Application.Run ().
Кроме того, класс Application определяет ряд свойств, многие из которых до­
ступны только для чтения. При анализе табл. 19.2 обратите внимание на то, что
большинство свойств представляет характеристики "уровня приложения" (имя ком­
пании, номер версии и т.д.). С учетом ваших знаний об атрибугах уровня компоно­
вочного блока (см. главу 12) многие из этих свойств должны быть для вас понятны.

Таблица 19.2. Основные свойства типа Application

Свойство Описание

CompanyName Содержит значени-е атрибута [АввеmЫуСоrnрапу] уровня 1<0МПОнО­


BO~HOГO блока
Executa blePath Содержит значение пути для файла, Sыполняемого в данный момент

Pr oductName Содержит значение атрибута [AssemblyProduct] уровня компоно­


вочного блока
ProductVersion Содержит значение атрибута [AssemblyVersion] уровня компоно,
ВОЧНОГО блока

Star tup Path Содержит значение пути ДЛЯ выполняемого файла. запустивше'го дан­
ное приложение

Наконец, класс Application определяет набор статических событий, и вот не­


которые из них.

• ApplicationExit генерируется непосредственно перед тем, как данное при­


ложение завершает работу.

• I dle генерируется тогда, когда цикл сообщений приложения заканчивает об­


работку текушего пакета сообщений и готовится к переходу в СОСТояние ожи­
дания (ввиду отсутствия сообщений для обработки).

• ThreadExit генерируется непосредственно перед тем, как завершает работу


поток данного приложения.

Возможности класса Application


Для иллюстрации некоторых функциональных возможностей класса
Аррliсаtiоп давайте расширим тип MainWindow, чтобы оц мог вьшолнять следу­
ющее.

L
Глаза 19. Со~д.аliие окон G пDмQщы0 S'.ystem,Windows,F'o'rm8 757
• ИСDолъэоват;р 3l:1ачевия, BeI<O'ТOpЫX атрибyroВ уровня КQМnOНОВО<ЩРГО бдока .

• Обраба'Ilьшатъ статическое собыгие Applicati.or,KKit,


Первой на.шеif задачей Я!Щj'{ется использование dВОЙСТВ класса Applicat ion
МИ от_Ображeюrn атрибуто:в уровня компоновочного блока. Для начала добавьте
в свой файл Maihwirrdow. СЕ сдедующи:е атрибуты (обратите БНйМaI-ше на "[о. что
3Десъ и:спольауется npостр~с·ц.ю имен 8уstеIЛ.Rеflесtiоn).

l1sing .system;
US-iI:!'g 81'е tem. wi.hd(;)IiIБ . Form$;
using Sузtеrn:. ~~fl есН сп,;

j I BecJtOm.1to атрибутов ~!II ,oЦёUUloro JC:ОИDо.во;ао'IИО'гО б~ОJ(а.


[ аs,зеmЫ y ~ ,Д,EisеmЬ1У;ССiщра,nу (" In-terteeh 'Т' rаini п g") 1 [as.sembly:
}\S'5еП1Ы ур r :OQil1C't ("Более С()13е-РШ18НRС!€ GK!-io") 1
! аs.з-еmblу :AssemblyVErSion ("1. i. Ь . О;') J
па,щеsрасе MyWindowsApp
[
' ......

Вместо тота ч.тобы отобрюкатъ атрибуты [Азsеml'йуСоmрешу) и [AssemblyProduct)


вру'!ную" иc.tIО.J1bа;уя приемы., предлагавш:иеся:в главе 12. класс Аррl iбаtiоrl по­
ЩЩiЛ$Jет еДеJЩТЬ это а,вТ01l(Щтиче<;:l\"Fr, испоllЪ3УЯ разЛИ'ЧНblе стшИЧ'еские свойства.
Нацример, можно реf:luJИЗ0вать 'Конструктор с.лецую:щеrо вида. который будет
играть POUIЬ ЕОНСТРУИ'1'ора. :,аданного по )'молчанию.

p.ublic class Мair,'Wlnd@w ; Form


!
p!1bli.;: !-1аJ.IiltНndоw ()
{
~з~аg~Вох . _Show (Аррliсаj:i0П. ProductНame,
string. Е'О.пnа t ':"3'1.'0 приложени.е с'оздаJ:lО JL.JJj:J :еа-е ко~mаиие~ {О } "1
АррlLi3аtiОГj,.сащрац~Nam.eJ );

Выполнив это приложеШ'lе. вы увидите ЩЦIО сообщеJ:.IИН, отобража1{)щее сооТ­


ве..roтвующу.ю информацnю (рис. 19.2).

(ж 1

Рис. 19.2. Уrениеаl'ри5утовс ПОМDЩЬЮ n1па Appl ica t ior.


Теп~рь позволим форме отвечать на событие Applic,a tionE;LXit , Вам. наверное.
(}удет приятно узнать, что ,для обработки событ.и:Й в paМl<ax графпчес;EtlЭге интер­
фей~~ прилож.ений WmdOWs. Fbтшs используеl'СЯ синтакеис с06ыТЩi, уже ПQдробно'
ОПЩ~ЩШЫЙ Вl>Шiе в rnaвe 8 , Поэ.тому'. чтобы fJЬШОЛНит.ь перехв'8Т статического со·

1
758 Часть IV, Проrраммирование с помощью библиотек ,NET

бытия ApplicationExit. просто зарегистрируйте обработчик события с помощью


операции +=.
public class MainForm Form
j
public MainForm()
(

// Перехват события ApplicationExit .


Аррliсаtiоп.ЛррliсаtiопЕХit t=
new EventHandler(Mai nWindow_OnExit);

private void ИаinWindоw_OnEхit(оЬjесt sender, EventArgs evArgs)


(
MessageBox.Show(string.Forrnat(
"Форма версии (О) за вершила работу. ",
Application. ProdIJc tVersion)) ;

Делегат System.EventHandler
Обратите внимание на то. что событие ApplicationExit работает в паре с де­
легатом Systern.EventE:landler. Этот делегат может УI{азывать методы. соответ­
СТВУЮ1ЦИе следУЮщей сиrнатуре.

delegate vo id Eve ntHa ndler(object s ender, EventArgs е);

Делегат System.Even tH andler является самым примитивным делегатом, ис­


пользуемым ДЛЯ обработки событий Windows Forms. но существует очень много его
вариаций. Что же касается EventHandler', то его первый параметр (типа System.
Object) представляет объект. сгенерировавший данное событие. Второй параметр
EventArgs (или его потомок) может содержать любую информацию. относяrцyюся
К данному событию.

Замечание. Класс ЕvелtА rg s является базовым ДЛЯ множества производных типов, содержа­
щих дополнительную информацию для событий из определенных семейств. Так, для событий
мыши используется параметр MouseEventArgs , предлагающий, например, такую информа­
цию, как позиция (Х, у) указателя . Для событий клавиатуры используется тип KeyEventArgs,
предоставляющий информацию о текущих нажатиях клавиш и Т.д,

Тан или иначе. если вы перекомпилируете и запустите приложение. то теперь


перед завершением работы приложения вы увидите соответствующий блок сооб­
щения.

Исходный КОД, Проект AppClassExample размещен в подкаталоге, соответствующем главе 19.

,
I

l
Глава 19. Создание (}1СОIi а помощью System.Wlndows .Forms 759

"Анатомия" формы
Те:перь, когда вы понимаете РОЛI.ТIШaАррliсаt i6tl . CJlе.цующеЙ вашей задачеи
является непосредственное раСсмоТрение фуmщионвлъrtых ВОз'можностей кл.асса
Fo r m. Как и следует ожидать, RШl.CCJ FQr:J;!:lнаследуе1' больntинство своих функnио­
нальЦliIX 1юзможнос.теЙ 61' родител:ьских КJ1aC€JOB. На рис. 19.3 показ3НО окно dbJect
ВГ0тег (В \'i~щal StHdio 2(05). в :к.отаром отображается Цепочка наследования про­
И~ОДВОГО.l)1' Forrn типа (вместе () набором реализованных ин;rерфelllсов) .

...,- - - ....
,,'-рUЬ.Чс c~ l'OrnI • svstem.W-.iJ!i(,~;€brrta{I1'...соI1Ь'tlj
'" /42m!шr 01. SY5t'eт. windows. Pomi!>
/
,' ~
...... ID!фТNie,l
.. Renfбenrs а ~',lПdt?'o'Ji ;йf m.k!g D:Ct)(,that ,m;I/tPJ'i,ip ~" ioplic-'atfo ,,'~
."" [6;\",т,м;z6""~~,,, , I $ f f'\tl!П;I:;Ej.
.-> 'W1r.~~I\(~ ('
' 'ь _'01 {GDr.~*
I
--:,~Ы~
__:~,! .
р"с. 19.3'. Г1роиt<:хождени"" TI~na J'or.m
Полвая цепочка наследо:вaнИf,{ ТЩIа F'orm включает в себя М'Ншкество базовых
:классов и ИБтерф~со.8. но здес:ь cдeдy~ подчерклуть, -Что ;вам" чтобы стать хоро­
IIIИ.М раэработчищ)м ПРИJюжений Windows Fолns, совеем не обязаmeлыш ЛОl:IИМaТЬ
роль ЮlЖцого члена всех РОЩГГe,тJЪС.ff.Ю( f!ЩlССОВ И кащдоro реали;JOВанного интер­

фейса ВЗТО'й цепочке. 3~ач:еIЦlЯ б~шинства "I1ICHOB (В частности. болъ1ШtНство


t;БОЙСТВ И еоБЫТИ5i), J(OTopble вы будете ИСПОЛЬЗQватъ e-жеДI:Iевно, очtmь просто
'у~ававливаются с помощью ощrа свойств Visua1 $tudio 2005. Перед рассмотрени­
ем ко:н;кретпых ЧЛ~ОВ, ун.ас;ледо.ваwЩill( типОм Forrn от роди:rельсЕИХ RJlaCICOB. из­
учите .иЦфОР~aцmo табл.. 19.3, ~ которрй описана роль соответствующих б.аЗОВЫХ
классов.

Вы. Я<'J,Щ'рное, eaм~) поцимаете. что подробное опвса:ние каЩДОГО члена всех
RJШ.ССОВ во цеnoЧЮ' н;аслед:ОВЩlИJ'{ PD:j:'m потребует от.цел:ИюЙ вольтой m-rигИ. "Важно
r.o1 и Form.
понять общие хара.lfl'ери;С';rики поведения. предлагаемого ппrами Солt
Все неоБХ{jдимые подробщ)ст~ о €оответtтвующих .нл:аr:сах вы сможете наmи в до­
кументации .NEТ Frшnewoг)t 2 . О SDR.
760 Часть IV. Программирование с помощью библиотек .NET

Таблица 19.3. Базовые классы из цепочки наследования Forrn

Родительский КJ1acc Описание

System.Object Как и любой дРугой класс .NEТ. класс Form-


это объект (Object)
Sy stem.Ma rsh alByRefObject При обсуждеtjии возможностей удаленного
взаимодействия .NEТ (см. главу 18) уже под·
черкивалось, что типы, полученные из этого

класса, будут доступны по ссылке (а не по


копии) удаленного типа

System.ComponentModel.Component Обеспечивает используемую по умолчанию


реализацию интерфейса IComponent. В
терминах .NET КОМПЩiеtjТОМ назы.вается тип,
подцерживающий редактирование в режиме
проектирования, но не обязательно видимый
во время выполнения

System.W i ndows.Fo rms .Control Определяет общие члены пользовательского


интерфейса для всех элементов управления
Windows Foгms, включай саму форму
System. Windows. Forms .ScrollableCoГJtrol Определяет автоматическую подцержку про­
крутки содержимого

Syste m. Wi ndow s . Forms .Contai nerContro l Обеспечивает контроль фокуса ввода для тех
элементов управления, которые могут высту­
пать в качестве контейнера для других эле­
ментов управления

Sys tem. Windo ws. Рогтэ, Form Представляет любую пользовательскую форму,
дочернее окно MDI или диалоговое окнО

Функциональные возможности класса Control


Класс Sys tem.Window s.Fo rms. Con t r ol эадает общее повеДение, ожидаемое от
любого GUI-типа. Базовые члены Cont rol позволяют указать размер и позицию
элемента управления, выполнить захват событий клавиатуры и мыши, получить
и установить фокус ввода, задать и изменить видимость членов и т.д. В табл. 19.4
определяются HeRoTopble (но. конечно же, не все) свойства, сгруппированные по
функциональности.

Таблица 19.4. Базовые свойства типа Control

Свойства Описание

BackColor, ForeColor, Определяют базовые параметры визуализации элемента


Backg roundlmage, Font, управления (цвет, шрифт для текста, вид указателя мыши пр~
Cursor его размещении на элементе и т.д . )

Anchor, Dock,AutoSize Контролируют параметры размещения элемента управления в


контейнере

Тор,Left, Bo ttom, Right, Указывают текущие размеры элемента управления


Воппd~ ClientRe cta ngle ,
Height , Width

L
Глава '19. СtJздаНJ~е окоН с nОМОЩЬ1D S'Ist&hl.Wlndows.Forms, 761
Dкr:JНЧВНМ ' 'Гв(i1/. 19.4

Clойотва Описание

En.a,b led, P'c>cused, Visi.ble Каж,nое из ЭТИХ свойств ' во~вра.щает Зliа'iечие типа Вf)оlеа'П,
укэзь!sаюLllее соответствующую характеристику оостояния
~neMeтa уnравлеf1ИЯ

MG":J..if.ieaeys С,tlтичеi::жое О'ВОЙСТВО 1 содержащее инфармIШИЮ Q текущем


оостоянии МОДИФI4ЦИРУ!РЩИК КЛi:jВИШ «ShifP. <CfrP> и, <Alt»
и возвращающее (3ту И'I:IфррмациlO' в виде типа Keys
MousSEuttot'1S Отатическое свойстlaQ, оодержащее информацию о' текущем
оостоянии KH01l0K мыии (левой,. правой, "и средней) и ВоЗSра~
щающее эту информацию в виде типаМОI1S.еВut. tQns

таыIi:lех,' Ta'bS't.op Исп()лI:>ЭYЮ"~ ДЛЯ указания Гlорядка переходов па клав.иШе


табуляции д;Г1Я злеме/'1та управления

Opacity ОпjXJДеля&т ,степ'еI-JЬ прозрачностиэлемента управления в


дробных едиНица)( (0.0 соотв.етСТ6ует аБОDЛЮПЮЙ пррзргУНi)­
СТИ,.а 1,Ь ~абсолютl'!ОЙ непрозрачности)
Te.x't Vказ.ывает TeKGТOBыe данные , зссоциируемые с элементом
ynра8ле1-!ИЯ

Cont rol s ПОЗВОЛSffiТ nолуч~ть доступ к СТIЮГО ТИillotЗQ8анной J((lГJле, LЩИИ


(Сri)лtн;.lэсоllесtiQn). содерж1itЩей вое дочерние ~леМВ}j­
LЫ управления , ьущеСТВУЮЩI4e в рамках данного ~лемента
управления

Кроме TOrt1. Rдacc Co.ntrol оnpе.дедя:ет РИД сgбыти.Й. позвоmtЮщих реагироооть


на изменемие С;ОС'Г<ЦIШfЯ Мbmm. Jr,IIэвиатуры, дrnС1'ви.н "Выделения и перетаскива­
ния объеRТОВ (а также' на :многие дрyrие дей~вия:). 8табл. 19.:5 предлarаеч'ся список
aeI\oTopых (НО далеIЩ не всех)еобытцй, сtрyшmрCiВанных по фyrШЦиоозлъности.

1iIБЩЩ8 19,5, СоБЫТI1'Я ТШ1а Contro:i


СобlofУИЯ Описание

Click, Doubl,e Click, События, ЛОЗВОЛRlOщие VЧИ'Т.blIВВТЬ состоям~ МЫШИ


MouseEnter, M0u.s.eliea:l.1,e~
Моu~]'jоWЛ, MOLlseTJpl MO'l)s€Mov;:;.,
МрцsеНavеr, HtlilseWbeel

KeyPress" КеуИр, Ке.уЬОI"Л Собьттl.-tЯ , f10ЭвОЛяющие y411ТblBaYb состояние клавиатуры

DraqТJrop, DragEnteJ;'" СобьrrИs:i 1 и~польэуемые ДП'Я kОН,.Ро,ця деt1СfВWЙ. связа~


Dr:а':j'Lеэv€, D.ragOyer ных С перт!li:OOllванием объеК10В

1?ai.,ot Событтия, ГтGЗВОЛАющие взаимодействов':iib с GDIi1-


(СМ. rnaBy 20)

Наконец. базовый RЛасс CoDtrol оnpеделнет це:J1ЫЙ рад ~eTOД08. nО8ВОЛВ10Щих


1Iзаимодействовать Е любым типом, ПРОИ-ЭВ()ДНЪJМ (')1' t'ол tro 1. При блИ1ftaЙ'Шем
рассмо'l'ренnи методов Control БЫ обнарущите . что :rvщогие из:них Имеют npефинс
ОГ1. за кorrорым следует ИМЯ соожветствующего соб:ытия (OI1Ho~seM{)v,~" ОпКеуОр.
Оn"Раl ГJt и 1'.):1,.). КаЖдЫй из этих снабщеДНbIX nрефИiJ{.СОМ виртуалъны:х методов
представляет собой обработчик соответст.вующего С'обытдя. <!а.данный IIО умолча"

1
762 Част!> IV. Программирование с помощью библиотек .NET

НИЮ. Пере определив ТaRОЙ виртуальный член. вы получаете возможность выпол­


нить необходимую предварительную (ИJШ ЗaRЛЮчительную) обработку данных, пе­
ред вызовом (или после вызова) родительсвой реализации обработчика события.

p ublic c l a ss Ma1 n Wi n d o w : Fo rm
(
p rotected override void OnMo u s eDown(Mo useEventA r g s е)
{
/ / Добав.nеlUlЫЙ npограмииый ход ;ЦnЯ; соБЫ'l'ИЯ MouseDown.

/ / Вwзов РОАМ'1'e.nьсхоЙ реa.nизации.


base. On Mo use Do wn( e );

Это может оказаться полезным. например. при создании пользовательсRИX эле­


ментов управления. которые получаются из стандартных (см. главу 21). но чаще
всего вы будете использовать обработку событий в рамках стандартного синтакси­
са событий С# (именн о зто предлагается средствами проектирования Vlsua1 Studio
2005 по умолчанию). В этом случае среда разработки вызовет пользовательский
обработчик события после завершения работы родительской реализации .

publ i c class Main Wind ow : Fo rm


(
p ubl i c MainW indow ( )
{
Mo useD own += new Mous eEvent Han d l er (Mai nWind ow_Mous eDown );

vo id MainWi ndow_Mo useDown(ob J ect se nder, Mo us e Ev e ntArg s е)

/ / Добав.nеиНlolЙ npогрaНМНblЙ !Сод ;ЦnЯ соБы'l'я: MouseDown.

Кроме методов вида О пХХХ ( ) . есть несколько других методов. о liоторые вам сле­
дует знать.

• Hi de (). Сврывает злемент управления, устанавливая для его свойства


Visi ble значение false (ложь) .
• Sh ow () . Делает элемент управления видимым. устанавливая ДЛЯ его свойства
Vis i bl e значение true (истина) .
• Invalidate (). Заставляет зле мент управления обновить свое изображеJШе,
посылая событие Paint.

Несомненно , класс Co ntr o l определяет и другие свойства, методы и события


в дополнение к тем, lioтopble вы только что рассмотрели. Но и сейчас вы должны
иметь достаточно хорошее представление об общих функциональных возможно­
стях этого базового класса. Давайте рассмотрим примеры. позволяющие увидеть
указанный класс в действии.

1
Глава 19, GQ3ABHI:1IJ окаl-i о rтОМОЩЬЮ System.Windows_Forms 763

Использование возможностей класса Control


Чтобы продемонстрировать ВО3МОЛUЮСТИ n:pименения rIeEOTOpыx ''1JТC!:JWl масса
.Сепt rol. даваЙ1'е. построим новую форму, способuyю обесneчивaтъ (;леду-.юJЩ:е .

• 0Твечать на СDБЫтяя M01JseMcIVJ: и MouseDown.


• ВыподВ1JТЪ захвзrr и обработку ввцда С Iшавиатуры. реarируя на С(JQЫТ:нt>
i<;eyvp.

Для ец-ч~а создiШте .новый цла.сс. ЛРOJ<iавцдный от Form. В конструкторе. за­


Д~OM до умоцч;;шию, мы исшшы:уем раЗJIичнъre наСЛefIYе...~е свойства. чтобы: за­
дать :исходный ВИД и поведение фdpМbt. Обратите внимание на 1'0. Ч1'.() .'щесь НУЖ1l0
УЕавзть ИСПОЛЬЗQв~е пространства имен System. Drawing, ЛОСК\)ЛЫо/ необходимо
ЦОЛУЧ}f]'Ь доступ К CTpyxrype Color (пространство ю.iен Sys tem .Dt awi IЩ будет рас­
cMOTpel;IO в следУющей таве)_

IJsi пg $Yls,tem;
и$ it!'g Эу:stещ. Win.dows _Е'оут<; "
l.Jsing Sу.sЬ:em.Па:а:wl ll g i
ГliilF.le'Space MyWindQwsP..:p,p
{
P l1bl i::; ;;:l ass Мai..в'IIНщl0 W FэсUl
\
1" L,bl i t;: Иа:lt [1 w1 rн;u.oM О
I
1/ ИСПОЛЬЗование J.laC:n~.щ>"eJIЬ1X ~O~<;:ТВ' ~ УC!r&Иовхи
/1 хараж'.1'8рХСorиx интерфейса пcmьзо:sа'1!eJ1Я.
'T ext = "Ноя фаJ-.r:т·Оj'(::тич е,С1~ая форма " .;
8ci\jbt = 300,
"W idt.b = :',0 О ..
BackColor = "Color. LffiпоJ'l-,;::hН fоn-:
Сн:rэь r = Cbl rs-ors. Ba n.d)
J

pljb.!!.ic statlc c li3 $s Frogr<ill\


{
static v oid Malnlstringll args )
t

Скомпилируйте это nPИlIожени~ u его текущем дяде. просто ЧТDбы nроверВ'1Ъ.


что вы не ДОnYL'ТИЛИ никаких опечатоl\,
r
764 Часть IV. Программирование с помощью библиотек .NET

Ответ на события MouseMove


Далее, мы ДОЛЖНЫ обработать событие MouseMove. Целью является отображе­
ние текущих координат (х, у) указателя в области заголовка формы. Все связанные
с состоянием мыши события (MouseMove. MouseUp и т.д.) работают в паре с делега­
том MouseEventHandler. способным вызвать любой метод, соответствующий сле­
дующей ситнатуре,

void MyMouseHandler (object sender, MouseEventArgs е);

Поступающая на вход структура MouseEventArgs расширяет общий базовый


класс EventArgs путем добавления целого ряда членов, специально предназначен­
ных для обработки действий мыши (табл. 19.6).

Таблица 19.6. Свойства типа MouseEventArgs

Свойство Описание

Button Содержит информацию о том, какая клавиша мыши была нажата, в соотвеТСТ8ии с
определением перечня MouseBut tons
Clicks Содержит информацию о том, сколько раз была нажата и отпущена клавиша мыши
Delta Содержит значение со знаком, соответствующее числу щелчков, произошедших
при вращении колесика мыши

х Содержит информацию о координате х указателя при щелчке мыши


у
Содержит информацию о координате у указателя при щелчке мыши

Вот обновленный класс MainForm, Б котором обработка события MouseMove


происходит так, как предполагается выше.

public class MainForm : Form


(
p'Jbl ic MalnForm ()
{

11 Дnя обработки события MouseМove.


MouseMove += new MouseEventHandler(MainForm_MouseMove);
}
1/ Обработчих события МоuзеМоvе.
public void МainForm_MouseMove(object sender, MOJseEventArgs е)
{
Text = stгiпg.Fогmаt("Текущая позиция- указателя; ({О}, {l))",
е.Х, е.У);

Если теперь запуститm программу и поместитm указатель мыши на форму. вы


увидите текущие значения координат (х. у) yIiЭ.зате.ilЯ. отображенные в области за­
головка соответствующего окна (рис. 19.4).
rnaoa 19. Соэдание O~O" с ПОМQЩЬjQ Sуstеrn . WlщjЬws.FOIms 765

Рис. 19.4. МО).lI1Т(i)РИНГ двюкеlЧ.ИЯ MblWI/I

Регистрация щелчков кнопок МЫЩИ


СлеАУеТ Iюдчеркцутъ. что событае MouseUp (KaR и M01l5eD,.~ w;1 J посылается при
щелчке Лlfilбо(j КНОПШI МЫШИ. J:<:CJЩ нужно ВЫЯСНИТЬ. RaJf.ОЙ ЮJОnКОЙ мыiIIИ был ны­
полиен щe.nчо~ (леиоi}:. правой или средней}. следует npоанализировать Значение
евоikт.в~ B-\lttQГJ класса MouseEve:n:tArgs. Значение свойства Bott.on cOOTBe'roтвyeT
ОЦItому из зн.а~еДИЙ переЧНiН 'М.:О'J5sвщ:tОRS-. Предположим, что для обрабOТl<И со­
бытия МСдJэSUр вы иэмсmurи задaщIЪCЙ по умолчанию ROHCTPYКТOP так. RaК пока­
зэяо ни;же.

pl1bl.i о J.\1aiлl'Нпdоw (J
(

IJ .;ItлR обра60'.t'ХИ CO~ blO.U $eUp.


МФ1J sеUр t= J1ew МО'tJ sеЕV~fltf1аn-dlе.LП·1аiлFоrm_МОll5>е1JрJ ;

СледУЮЩИЙ обработчm события Mo.useUp сообщает в DЩIе оообщевия () том, ма­


кай юшJПtОЙ мыши бъtл вып)Jнеn1'I Щелчок

p.ublic \Toid Mai.nFonl"t_Mous€UP (obje,ct зепdeL, Mc·u seKvEmtA.rqs -.2)


j
1/ K&JCё!IJi ЖИ:Оtt!СА ~ б1iШ~ и:а!ra~а. '?
i f (е. Butt..on == ИО).l3'еВllttоns. Left)
MeSB·ageEox . ShGW ("Ш;еJ1ЧО;К л:ево,й 1~Ы:0IlКИ, " )- ;
if (е. ButiQP ==- Мощ.:е-вut t.o n s . Rigbt:1
Ме'З'ЭC'lчеВох . .shGW ("Щелчок пра!'!ой КНО·= ... ,) "
if (e.Button == Mo:tise:But- tопs.Мid,dlе :)
Mes·sageBoK. Shcw ("ЩеЛЧО '!': (.-Р8/i1неЙ KHO:rтIOlI.") I
766 Часть IV. Программирование с ПОМОЩьЮ библиотек .NET

Ответ на события клавиатуры


Обработка ввода с клавиатуры почти идентична обработке событий мыши.
События KeyUp и KeyDown работают в паре с делегатом KeyEventHandler, .кото­
рый может ув:азывать на любой метод, получающий объект общего вида в качестве
первого параметра, и KeyEventArgs - в качестве второго.

void MyKeyboardHandler (object sender, KeyEventArgs е);

Описания членов KeyEventArgs предлarаются в табл. 19.7.

Таблица 19.7. Свойства типа KeyEventArgs

Свойство Описание

Al t Содержит значение, являющееся индикатором нажатия клавиши <Alt>


Control Содержит значение, являющееся индикатором нажатия клавиши <Ctrl>
Handled Читает или устанавливает значение, являющееся индикатором полного заверше-
ния обработки события обработчиком

KeyCode Возвращает клавиШный КОД дnя события KeyDown или события KeyUp

Modifiers Указывает, какие модифицирующие клавиши были нажаты «Ctrl>, <Shift> и/или <М»
Shi ft Содержит значение. являющееся индикатором нажатия клавиши <Shift>

Измените объект MainForm. чтобы реализовать обработку события KeyUp. В окне


сообщения отобразите название нажаТОЙ мавиши, используя свойство KeyCode.

public class Ma.inForm : Form


j
public MainForm()
j

11 Для о'1'Cnежи:вaиюr. собll'rИЙ KeyUp.


КеуИр += new КеуЕvепtНапdlеr(МаiпFоrщ_КеуUр);

private void MainForm_KeyUp (object sender, KeyEventArgs е)


(
MessageBox.Show(e.KeyCode.ToString(), "Нажата I<:лавиша!");

Скомпилируйте и запустите программу. Теперь вы должны иметь возможность


не только определить, какой l\НОПКОЙ МЬШIи был выполнен щелчок, но и то, .какая
была нажата мавиша на клавиатуре.
На этом мы завершим обсуждение функциональных возможностей базового
масса Control и: перейдем к обсуждеfIИЮ роли Form.

Исходный код. Проект ControlBehaviors размещен в подкаталогв, соответствующем главе 19.


Глава 19, СQ~дание 0110111 с пом.ощыо8уS11?n1'wiпdоws,Fогms 767

Функциональные ВОЗМОЖНОСТИ класса Form


Юlасе Form обычдо (но не обяза',Гtщ.ыю) явля~тся. непосредственным баз овым
{(."!lШJСОМ МЯ ПQЛЬ$ОВ:8.тельсЩ1.Х типев F Q.rm. В доnoлнение g большому набору tше­
НОВ, YHac.lIt'JtoBaFlHblX от классов Сопtrоl. S стоllаbl еСо!)tiо1:и. Oon t ainerControl.
тип Fфr:m предлагает СВОИ сQбственl:l,ыe функциональные вОзможJ.lОе'IИ. в частности
для r.;raвныx OI'сон. дочер~ 9!'ОН МDI'.и ДИЗЛEJТОВЫХ 01юн. Давайте сначa.riа раОСМО­
трим бащ)Вы~ своДства. I1реДСТаБ.о!1eщu.Iе. -в табл.. 19 .8.

Табmщз 19.8. Свойства типа For m

Свойства Оnиcatlие

1\.cce pt.В\ittQD Чli1Тает или уотанавливает информацию о lo:ifjопl<е:, КОТОРЗfJ будет "liвжвrз"
(В форме). К<:1ГДЗ п.олыювзтелы нажмет lUJавиwу <Enter>

AI.:tiVi2MDIcl1i .ld Ис,nользуюtся в контексте MDI-ПРl1ложеI:iИSl


lsМШСfJi Нi
IsMDltont a iпе r
С" nсе lRutto.!) Читае, ИЛИ" устанаВJ'1И,lJает икформациtp о КНОПОЧнОМ эщ!ментеулраале­
~ИfJ. КО'l'орыи будет ~fiажат', KOrцa nОЛЬЗ!)'е,атель нзжмет кnавишу <Es<;>

"Iитает IluM УGТаt'iЗВЛJ/lвает значен и е, RВflЯЮЩf!.е·ся И"iАИt<aТором наличия у


формы ЭlliраJtf!СЙ кнопки управления okHOM
For:Дffior gerSt yle Читзет или устанавливает знаqение.\. 3адающеl1 СТИЛЬ границы формы
(8 соответtТВIIIИ о перечнем .FGт m"В о rd еrs.tуlе )

-Мe nu ЧI1ТЗ8Тили устанав'ливз:ет информациК) о C.TbIJ(OBKe М~Ю) в Форме

MaJt lmi ze:p.ox: Ис пользуются AMI \1lfформациl'1 о ЖtJ1И4ИИ У формы К.IOЛОК минимизаЦИИ
HinimizeBo.;t J/I максимизации окна

УкаэывШ!т. будет ли фDрма видимоЙ в l1анеJ1И задач ·Wil1dows

Чита е'!" или ~танaJ3ЛИВ.ает зна'fенИ'е, задаiOщее началь.ную позициJp 01'1'18


формы (в соответствии· с переЧНElМ Fo:r mS t аt tРою t;.id O)
УкаЗ!ilвает (В СCiответств.ии с l1ереЧI~ем For mWi n dow"St,at, e!., в I<fikOM l;1I'1A
должна QтобрaжaIl:iС Я форма при запуске

Б ДОIЮл.неI;IИе- н щ!щ:даемым обработчинам сЬбытНй с nрефшtcоJ.l.i OJ1, предлатае ­


МblМ по умолgaдию. в табл, 19.9 npещшгается списон неJtoторых базовhlX методов .
Qпределешц,1X типрм Fо дr.\ .

tабnица 19.9. Основные методы типа For:m


Мето", Описание

A<;,t iy-ate () AКrив~зирует ФОРМУ и предостаlЫще:т ей фокус ВВОДа


Cl o SH ( ) 3акрызетформу

С еr'.t.зr 1'Фз с !: ееl'1 (:) Размещаl:j'r форму в цеlПре Эkрана

La УОО t-МDIC) РаЗМ8щаt:iТ все Д0чвр-ние формы (acoorBeTOTB~ с перечнем


l.aYOl.ltMDI;) 8 рамкзх родителЬСкой формы

S I:юw Di аl 0'g () Отображает ФОРМУ!! 8t'1Ae мода!l~НОГО" диалогового ()tt;НЗ, Более поДРОб­
но о проtраММ1IТрова н ии диалОГО8Ы~ ~ЖDН ГОВОР ИТС9t В ГЛCIВ8 21
768 Часть IV. Программирование с помощью библиотек. NET

Наконец, класс Form определяет ряд событий, связанных с циклом существова­


ния формы. Основные такие события описаны в табл. 19.10.

Таблица 19.10. Подборка событий типа Form

События Описание

Activated Происходит при активизации формы, Т.е. при получеl~ИИ формой фОI<уса
ввода

Closed, Closing Используются для проверки того, что форма закрывается или уже закрыта

Deactivate Происходит при деактивизации формы, Т.е. когда форма утрачивает теку­
щий фокус ввода

Load Происходит после того, как форма размещается в памяти, но пока остает­
ся неВIiIДИМОЙ на экране

MDIChi ldActi ve Генерируется при активизации дочернего окна

Цикл существования типа Form


Если вы имеете опыт программирования интерфейсов пользователя с помо­
щью ТaIШХ па:кетов разработки, как Java Swing, Мас OS Х Сосоа или Win32 API,
вы должны знать, что "оконные типы" поддерживают множество событий, про­
исходящих в различные моменты цикла существования таких типов. То же самое
можно сказать и о типах Windows Fbлпs. Вы уже видели, что "жизнь" формы на­
чинается тогда, когда вызывается конструктор типа. перед егО передачей методу
Application.Run ().
После размещения соответствующего объекта в управляемой динамической
памяти среда разработки приложений генерирует событие Load. В обработчике
событий Load можно настроить вид и поведение формы. подготовить содержащи­
еся в форме дочерние элементы управления (окна списков. деревья просмотра и
т.д.), организовать доступ к ресурсам, необходимым для работы формы (установить
связь с базами данных, создать агенты для удаленных объектов и т.д.).
Следующим событием. генерируемым после события Load. является событие
Activated. Это событие генерируется тогда, когда форма получает фокус ввода.
как активное окно на рабочем столе. Логическим ~антиподом" события Activated
ЯВЛяется (конечно же) собьггие Deactivate, которое генерируется тогда. когда фор­
ма утрачивает фокус ввода, становясь нею(тинным окном. Легко догадаться, что
события Activated и Deactivate в цикле существования формы могут генериро­
ваться множество раз. поскольку пользователь может переходить от одного актив­

ного приложения к другому.

Когда пользователь решает закрыть соответствующую форму, по очереди гене­


рируются еще два события: Closing и Closed. Событие Closing генерируется пер­
вым и дает возможность предложить конечному пользователю многими нелюбимое
(но полезное) сообщение "Вы уверены, что хотите закрыть это приложениеТ. Этот
шаг с требованием подтвердить выход полезен тем. что пользователю получает
возможность сохранить данные соответствующего приложения перед завершени­

ем работы программы.
Событие Closing работает в паре с делегатом СапсеlЕvеn-tНапdlеr, определен­
ным в пространстве имен System.ComponentModel. Если установить для свойства
Глава 19. Сuздаffие ОКОН с ПОМОЩрЮ s.ystem,wjndows,.For.ri1s 769
cancelE.ventArgs .Can·c eI ~Ч~ЦИ~ tтu-e (истина), фОрМе буде:г даноуказани-е ВОЭ J
врamться к .нормальной раб9те, и форма униq'IоЖена не будe'I: Если установить
дл.и CancelEventArgs .C(!.1'\cel з~а't{ени~ false (ложы1. будеТ crенерировано. собъi­
'!ие Closed. и Щ)}1Jl(Уj((ен;ие WiцdQws FbrrЩi будет эаверmeRО (дОМен npилvжеНия бу­
дет выгружен и соотве1'СТВУ19:ьЦий 'Процесс преttрвщев).
Чтобыэацрепить в :па:мятп IJосщщрвателъН0СТЬ с:об.ы'ГИЙ.l1роисходящих в рам­
ках цикла существоваIiИЯ ФОР.NlЪ1. рассмотрим новый файл м.ainWindow.cs. в КО­
тором СQ6ытШI LOiid. Activated. De~ctivate, Closi.ng ИСlоsеd обрабатыВaIbТСЯ
в ROнструкторе Юl8сса тав:. lЩR nO'liазанЬ ниже (не забудьте ,цgбавитъ в пр()грamvry
директиву usi1'\r;j' ДJЩ ЛрОС'J"PЫiСТ~ -имев Sуstеш.СоmропеоtМО.del. ч;roбы получить
доступ:к щrрeдe;n;еmпQ CdncelEven·tArgs)~

publ:i" Ma:inForm ()
I
/I ОбрабО'1':8:iI рi!UI.!rИЧИI.DI: 0о6Jin'ИЙ1J;ИIШ" с=ущеС'1'8.0ВёUUCI: Формw.
Clos{ng += new СаПс:е1Еvеf\.tИаhdlе:ё (:м а in Роr!Т'._ С;1 o$ ing) ;
Load +~ П$"" Еvеnt.Ва,nЩ?J::(МаinF9IIТ1_Lоаd);
CLo.sed += пеw ElJ'entНal1dler {МаiпFр:пп~С10зеd) ;
bttiva'ted 01,= new EV?1it!:1dпdlеr П1аinFоrnL_Асtivа:tеd);
Deac ·t:Lva·t~ += l1ew EventHandler (Ma.inForm_Deactiv ate) 1

в ,обрабо'Г'ПШаХ соБытий Laad. Closed. Activated и [)eac tivate Ц СТРОJЮвую


переменную System.String (с именем lii:eTiro~InfoJ добanдяеТC!i имя пер~а,­
ченного собыТИJ'r. ОбрабО1fЧИН события Closed отображае:г значение этой строки
i'I 01tНe сообщения.

p!;i vat, ~ void .МаiI1F.оrщ_Lоаd (ob~le'ct sende'r, Sуst,еш,.Еvепt.Args е)


{ lifеТ'illlе1пfо+= "О0бытие Load'\n"; !
pri'\i'at~ void MainF'o-rm lItctivat~d.(object sелdеr, :SуstеIп.Е.vепtАrg,;;; е)
{ lifeTimelnfo += "Goбbl!l)ие ACtl.Yllt~\n"; J
p:tivate void M'a iI1Fo:rm De.a ct.i уа, te (obj ect. setJ.d.~rr S У. ~tliWL.Еv-епtАrgs е')
I lifeTimeInfo += 'iСОбыт:ие Deactivate\l1" " }
pri va,t·e VGl.d МаiлFо:rm _ Сlоэеd (D!)je.c t sender" $У'5 teru. E.l\7entAr.g.s е,)
{
l.:1fеТiщеlпfо += " 'G:обытие Clos.ed\n" i
MessageB.o x • S:how (11 feTimeInfo) ;

в обрабОТЧИl~собЫТИfI Closing задаетCfI вопрос о ТОМ. деЙствительно.п:н поль­


зователь желает завершить работу tlpЦЛОЖeн:щI. ПРИВ'I10М используется IШC'IyI:!<UO­
щиt{ на JlX'Oд объект СапсеlЕvепtА.rgs.

p'!"i v a ·te vaid МOi iпFсптn_СID-s ir,g (::ibj ect S.епdеr, Саnс:е 1 EventArgs е)'

I
D.ialogR~s'ult dr =
меs. ваgеВо!К. S,how 1 "ВЫ ДЕЙСТВИТEJI:ЬНО хотИте заr.рыwь ПрИЛОЖение? "'"
.. Сбб!,j!.I'ие С1 psirlg ! "., MesElageBo&Bu t tOl1S . YesN'o) ;
Н (dr == D:i-alоgRе.su,lt. N'o)
e.C:ancel = t-ru8;
е.1$0;;;
е .Cancel = false .;
т
ПО Часть IV, Программирование с помощью библиотек .NET

Обратите внимание на то. что метод MessageBox. Show () возвращает тип


DialogResult. эначение которого идентифицирует кнопку (Да. Нет). нажатую в
форме конечным пользователем. Теперь скомпилируйте полученный прогрaммный
код в командной строке.

csc /ta rget:winexe * .сэ

Запустите приложеFШе на выполнение и несколько раз поочередно предоставь­


те форме фокус ввода и уберите ее из фокуса ввода (чтобы сгенерировать события
Activated и Deactivate) . После прекращения работы вы увидите блок сообщений.
аналогичный показанному на рис. 19.5.

ГХ!
Соб",т", Load
Сoбыn1e Activate
СобыТ118 De<lttivate
Событ ... Activate
Coбt.mI&~
CoбыТII8 Activate
co&.rn1e oeac~vP
СoбooIтне. дaiуже
Сабыт", Deactivllte
CDбt.mo: Adiv~e
событие C:JOSed

ок

Рис. 19.5. "Биография" типа, производного от Form


Большинство наиболее интересных возможностей типа Form связано с соз­
данием и настройкой систем меню, панелей инструментов и строк состояния.
Необходимый для этого программный код не слишком сложен, но Visual Studio
2005 предлагает целый набор графических инструментов проектировaFШЯ, кото­
рые позаботятся о создании значительной части такого программного и:ода за вас.
Поэтому давайте на время скажем "до свидания" компилятору командной строки и
займемся СОЗДaFШем приложеFШЙ Windows Forms с помощью Visual Studio 2005.

ИСХОДНЫЙ КОД, Проект FoгmLifeТime размещен в подкаталоге, соответствующем главе 19.

Создание Wiпdоws-приложений
в Visual Studio 2005
в Visual Studio 2005 предлагается специальный таблон для создания прило­
жений Windows Fonns. Выбрав шаблон Windows Аррllсаtiоп при создaFШИ проекта,
вы получите не только объект приложеFШЯ с соответствующим методом Main () , но
и подходящий исходный ТИП, производный от Form. Кроме того, среда разработки
предложит вам целый набор графических инструментов проектирования, способ­
ных превратить процесс построения интерфейса пользователя в детскую забаву.
Чтобы рассмотреть имеющиеся возможности. создайте рабочее пространство но­
вого проекта Windows Application (рис . 19.6). Мы пока что не собираемся создавать
рабочий пример. так что можете назвать зтот проект так, как захотите.

L
Глава 19, СРЗА</ние окон с помощью S~stern.windows.fQfms 771

ProjKt~:
"- .----'"........:.
"1,,"J~""'Cz
_
... - ~ -- - -
- - - \1
T"""~'
.м...i:~~'~;;";_~
, --- - '.-- -.- .---.-
\<,lПd"",..- 1,
'j, '~martD'e,u

11
-
Da_
~ ~bo
Те>1 A1Ui<a'tlon ,I;~--аt!Q~
D1O"'ibu~ >;,-.1011' S<ilunoo.
, '" Otnl<r ~'
I Jiit o,&.ffpro_~tТ~
'''' TS! Рг~Ш

!:-еагт onr~
i""-oI.,,,•...

~ L~,\~al~~~ _________ ~ ___ _.~ _. _____,_" _~ ____ '


C:'DD~~.;;J·Se~:A;;a, ....::r!';i.;,n.~Y~Qj!re!'J;'i'~~~ 2i)o5~~ ~, r-,-g.-.......,-,-,.-.
Or'...~~for~1b>
'Ома '" SOOI'<e C..--oI

Рис. 19.6 •. Пр()е~т Windows Applicatien в Visual $tudlo 2005

После заtpузки проeкr:а вы увидите OJто fipomcтирован:ия формы. tCOторое по­


зволяет строить поль:юв&--r:ельс:tt;ий интерфейс путем nеретаСКИВЭJп1Яэле:ментов
управления и R0Мпонентов из панели инсТРумеНТ[JВ Iоюю Toolbox. }>llС. 19.7). а так­
же настраивать свойства и собыТИJ-! этих элементов управлении :и коМпОНентов с
поМ'ощъю olt:Нa свойств (окно properties. рис. 19.8).

fiJ",.1 11_
, j>~\I!r

~~ip
.; ~t-lp
L S-!а~1IiII
'j,"T~
L.iTd~r
1i'! ,DiIfa;

Рис. 19.7. ПЭ1-lель инструментов Vlsual Studio 2005


r
772 Часть IV. Программирование с помощью библиотек .NET

HalnWlndow SV$~. Wndows.Fonм.Form

.~Юl[,iiLf. . ,~

A,~Pt6utton (поле)
Aa:..sibleDe.criptk>11
Aa:eS$lbI.N"","
AcceSSlbleЦoIe D<!fouIt
д!IQlvOrор F~
дUIDS...~ Font
дuloScroll False
mAutoScrollM",gon О. О
mAu!OS<;oI1МIn~ О. о ~
-- ~~-.: ~.,.-:~--.,-~: -=- - ~"':',- ~- -=-§: -.::- .-....;- ":"'-;0<.

Рис. 19.8. ОКНО свойств Visual Studio 2005

Как видите, элементы управления в панели инструментов сгруппированы по


категориям. В большинстве своем эти категории самоочевидны - например. ка­
тегория Printing (Печать) содержит средства управления печатью, Menus & Toolbars
(Меню и панели инструментов) содержит рекомендуемые элементы управления для
меню и панелей инструментов и Т.д. Тем не менее. пара категорий заслуживает
специального обсуждения.

• Соттоп Controls (Общие элементы управления) . Элементы этой категории


можно считать Ц рекомендуемым набором" общих элементов управления для
построения пользовательского интерфейса.

• AII Windows Forms (Все злементы управления Windows Fолns). Здесь вы найде­
те полный набор элементов управления Windows Fonns, включая элементы
управления .NEТ 1.Х. которые считаются устаревшими.

Второй из указанных здесь nyиктов заслуживает более пристально го внимания.


Если вы работали с Windows РЬnns в рамках .NEТ l.x. вам будет полезно знать, что
многие привычные ДЛЯ вас элементы управления (например. элемент управления
DataGrid) находятся как раз под "знаком' категории AII Windows Forms. К тому же.
некоторые общие элементы управления. которые вы могли исполъзовать в рам­
ках .NEТ l.x (например, MainMenu. ToolBar и Statusbar) по умолчанию в панели
Toolbox не показaRы.

Получение доступа к устаревшим элементам управления


Во-первых. отметим. что устаревшие элементы пользоватеЛЬСIШГО интерфейса.
о которых здесь идет речь. остаются пригодными ДЛЯ использования в .NEТ 2.0.
а во-вторых, если вы хотите их использовать. то их можно снова добавить в па­
нель инструментов. Для этого щелкните правой кнопкой мыши в mобом месте окна
Toolbox (кроме строки заголовка) и выберите Choose Items (Выбрать элементы) из
появившегося контекс-тного меню. В появившемся диалоговом после этого окне от­
метьте нужные вам элементы (рис. 19.9).
Глава "9, СО'3дание. окон с nОМРЩЬ.Ю Systli!m .Windows,.Forms 173

-~~- ' ---'-'~" -- . ~- - ~-


I ·N!1T~kc,.т,_t;'1 CC!'1~~"!!1I!1 : .~_ . _ _ ~~
. ~
. _._. ___ _.. ~,,,._. _ _
1 •. - --- ' -- ' - - " -, .~ -~----
'"~ .. _ . -~.

1' - ~ .~ ~; \
1 ,О T~ ' !iYOIenL'WJndo.... ,1'GrW C,,'Progam Ro5~~crOS<ifl:~1!ы!I aw
R.! T""I!< ~~tem:lI'lindo"' •. F"'1Щ C; 'flogr'_~''i''1IcrыQft ~ &!!Jdi '11
[

"~ И
I О тoтerМГilj МIa_f1, ~~CO/Тil"tIbt." "'Qb"lд<Se!l!bl1 ~ i I
11'
t 0, Т_ .sys-.',~$.FOI1!'J; С,'f'r_~Уo!iqoюft"""""I.s~:
~ TDQi8a, S~.,:{ndC\IJS.f~ G:,\f?rQgr""РdО$'#'1I<;"",ft V1ШЫ:5tJdi i I
I
О Tdar"""",' !'ЮQ$Of\.\'~СOПlP6th... GIObal_v Gad1e '1' I
8 T~trip S:ys~~i!5,F_ Ulilb.>1 ~'Y ca<ho! I
.i T~~~~~~•..•.,;,--.-.
FiIer.
- _с'.:ю~.:~.~~<"
------,,---
::~_I. _
._G1o!>aI,--~'I1bI~ c,orh~ >,,": Ij

l
1'~~" ~._,, - ~ ---.~_ .. ~, -,,, ,,, ,1 ;\
1----'- ......... _ _ _ _ _ _ _ : _ .. ~ __ _.. _

T....J8"ar
&cWso!... J
Vt11ion! 2,0:0,0

IЖ ,j[ cМ'~ ][ ~ ]

.Рвс.19.9. Дot\авление элементов ynравле~ИR на ПJif-lель Иliструментов

3амеiolание. МО}j(е1показатъся, 'ЧТО 8 CnI'lCK& во/<ие д.обавnения элеМВЩОS 'улравпения имеютtя по·
вторе~IИЯ !наГlример, ДnЯ ~лемента yn:pаtll1е~IИj:J 'I'oolBa.r). На саМОМ -же деле 1<.аждый Эllемент
списка yt'IИ,кален, так Ksl< соo;rветсrвующий ~лемеJ.П упраЩlения мIJIЖет иметь другую ВSIЮИЮ
(например ,.: 2 .0. BM~ТO 1,0) 11/1iIЛ11 быть элементом .NE1' Compact Framework. ЛоэroмуБУДЬt:е
внимательны , 4тоБЫ 8bItipan. правW1bR,b1И Эllемещ.

в ЭТОТ .моме;н1'. ~ уверен. вы спросите. по какой причине многие из с-rарщ,х


элеменrо~ cнpыiыы 0'l1 просмотра. Причина в том. что .ЫЕТ2.0 предлатает новый
набор :меню, Пa.Elf-!le-R инструментов и средств отображения состолlliiл. 1{ОТОРЫМ
сеroщrя отдается предпочтение, Например; вместо устаревшего элемента управле­
ния MainMenu ДЦ.II СQЗд.ания меню предлar'аетt:Я использовать элемент ynравлеlUilЯ
M~11 uStripi обеmreчивающий ряд новых ФУНКЦИЬНaJIЬНЬ1Х. зозм:о.ж:ностей пдопошre­
дие R воэмф'ж'нОСТЯМ, предл.arэвmимс.а в раМп:ах MainMenu.

Замечание. В 3Tol1\ главе мы используем новее; рекомеlfдуемuе свгодня множестео элемеНiов


управления IlOльзоватеnыжоfО интерфейса. Чтобы получить ИНФ0рмациlO" необходимуlP ДJlЯ
работы о устаре~ими типами МаiпМеп-u" Statusba:t" и другими аналогичными им тVihами.
обратит€юь 1( докумеrпаци~ .NEТ P:rame~rk 2;0 ВОК.

Анализ проекта Windows forms в Visual Studio 2005


ЛюБQЙ тип Е'О):'Щ проскта Windows Fbrms в VisuaJ Stwtio '2 005 пре:дставлен дву­
N1Л ~зaннъrми С#-фа,йдам:и, в чем М'Q:IЩlО убедиться НeшJсредетвеюю, загл.явув в
ОЮf0 S'oluti,on ExplOTer (рис. 19.10).
774 Часть IV, Программирование с помощью библиотек .NEТ

Solutюn fхрЮr." - Soh.tюп ·1estWm'_.f!iJ

;~~~t~!.~~щ~с,:"",м.,
;3 ~ TкtVI..
liI' (.\1 Properties
,*' ,.. Reterenreo
!:! §а '4Ciiм' В'
't.1 Form 1.DeSlgner.c<
.Jd Progrмn.cs

" i~ SOiUb0l1 Ех.югer


-- -
':'1
--
T••mЕxpior...

Рис. 19.10. Каждая форма являет­


ся композицией двух файлов *. c-s
Щелкните правой кнопкой мыши на пиктограмме Foгm1.cs II в появившемся
Itoнтекстном меню выберите View Code (Просмотр программного кода). Вы уви­
дите программный код парциального класса, содеРЖaIЦего обработчики событий
формы, конструкторы, переопределения и другие члены созданного вами класса
(заметьте, что здесь имя исходного класса Forml было изменено на MainWindow с
помощью выбора Rепаmе из меню Refactoг).

namespace муVisuаlStudiоWiпАрр
{
public partial class MainWindow Form
(
public MainWindow()
(
InitializeComponent() ;

Конструктор. задallliЫЙ формой по УМQJIЧaниIO, вызывает метод InitializeComponent (),


определенный в соответствующем файле *.Designer.cs. Этот метод создается в
Visua1 Stlldio 2005 автоматически, и в нем автоматически отражаются все моди­
фикации, выполняемые вами в окне проектирования формы.
для примера перейдите снова на вкладку окна проектирования формы и найдите
свойство Тех t в окне свойств. Укажите для этого свойства новое значение (напри­
мер, Мое тестовое окно). Теперь откройте файл Forml.Designer.cs и убедитесь в
том, что метод InitializeComponent () соответствующим образом изменен.

private void InitializeComponent()

this.Text "Мое тестовое окно";

Кроме поддержки InitializeComponent (), файл * .Designer.cs определяет чле­


ны-переменные, представляющие злементы управления, размещенные в окне про­

ектирования формы. Снова для uримера переТaIЦите элемент управления Button


(Кнопка) в окно uроектирования формы. В окне свойств с помощью свойства Name
измените имя соответствующей uеременной с buttonl HabtnTestButtoIl.

L
Гnа,В8 1:9. СDзд.i'IR~~ окон О nомощыо 6ysfJJm ,·Wln.dows, Рогта 77fJ

3амечакие-. Bcerдa Jiy\:t~ neрёименовать ра3мещаемы~ 3.11eMef4T уrф&!tl1e1iИЯ'. перед тем l~aK про'
rpat.iMI.1P0GaT\> обработку соБы11ll •. Ес:tlи этап).не
CAt;!'fIBJt., ВЫ, JЮt\)"ШТ. е Целый нвб.ор обработ~
'IИКОВ ОQбы-tи~ С неинформвт",вн.ыми имеi'\ЭМИ I:fвподоБИ'е but tоп2 1. (:1 ick. ПЩ:I(f!JЛbJ{'у при
ССЩДЗtЧl'1И СООJsеn::тв),ющеге имеыи ЛРУМ'ОJ1чанию к:И~ злer.ffiнlja ilpoctb до.баВ1fЯeroЯ суф·
ФИМ: в в"'де hClJJЯДКDВОro НОМ8ра переменной,

:ОбработкCI событ~Й J режиме проектированИI


06р~;гц',!'е ЩЩМaн:r.«'. па 'ГО\ "''Го- n ошre СВОЙC!ffi eCTh 1uюmш с изоБРa'ЖeJШем мол­
~. Bet. KQ1;I'~o;m(l, 'М'оже-те вручную С::ОЗN!ТЪ npоrpaммnъШ.щд. обеспечивающий
о~бо-щу сОБЫТШt"yr,JОШ!Я фGр.мы. (1tilК ~ было сделано в предьJ,Дy,IШiX. npим~рах).
ио 3'Р1'l ЮЮIlliа поэволяЮ' С!брабO'rатъ -~обbl1'Иедля .данНQЩ, Э)Iе:мейтз: ynр~ЩU)
""!3иэу~~, f1!J ра:снрывВЮЩe.roсн Cn:Иска [вверху онна С80Иcrв) Dьiб~и're ЭJl!:'мент
УПРaвJ.lеJ.nцl.'. ~or.щ)рыЙ де.лжев ..взаимодействовать" с формой. пa:ibmте еобытйе. вото­
рое вы XO,!·~ ' 9брWоrt1fJ.ТЪ, и н:тe"Ia1'3irre ИМН. «оroрое должно и.CI10ЛЬЗов,атьс,я .для
~БОТЧИRа C00ь1'J:'m;i (юm lшmо..1ШJ4'1<е ДВОЙНОЙ щелчС1К на имени собьnliЯ, что(iы
ert;НePJ<!PQВ~Tb ти::rrьнoo юш:в:виде Иr>'IЯЭл.еМ€Н\I.':El_Им~Событ'У.я).
~JIИ ~8Щ~'JЪ 60рШ5etrКy .сnбh1'l'}fя Сl iok для элемента удравл.енмл Б!u.t tоп. в, фа:шщ
F~m2. С!:> щ:mвится следУЮщий абрабO'l''lИИ ,собьrтий'
~1JIH i,c part.ial clas$ Mi!inWi I1do'i# : FOJ:11'I

J
p.wUc МaipfJiliH(\OW О

priva.t e 17ioid Ьt.() В(J t\:О{IТеэt_~'l1с.Jr;'(9EJjеGt Ь-Еnсе}', Even~tA.rg'S е)


(
!
}

ф,аЙJ1 f'oJ:'m1. DEislgne'r ,CS буд~ i':одержать Jre(IШо.п;ш.ryю инфрастрYJt1'YJW и оnи­


cшmе аоQтве~с'Гв.·{ЮщeI'О
.. ~на-дер"':менной.
-

part i,r.tй сlаБ,В МаiлWНн:l.е~J

t'

p:1l"iv:a1,e void lliitiаiiZ,еGСПlpОЛЕ:ut :С)


i
~ ...
it:11i& . Ьtn:ВП t tOl\ Test .СН с:к +=
new , $у~tе:щ, EJven tH.q.I lill~;r' ( tЪ± '" _ :Ы:.n.Вu,tt'Ш1,Т~gt ~ Cl i ~k,;
}

ЭвмечaIOf&. Кa)fl,Дbliq эneмеl~Т ут;1pщ;i/Ilffi!'\.'1J'I иМ'~е'f свое сабмтив ЕЮ УМD:J1Ч8Н.IIIЮ, J(Ol'op.oe будет об­
рабdтюю при ДВQЙНО.м ЩеЛ4ке ЮS ;ЛОМ ЭЛ\!МеКТ~ ynра:влеlШI1 в ·окне n'poeк;rl4p<>ga~ll4.R ФtJР.мbL
HanplII"'~.. собьrr.ием 'ПD УМОi'lЧ,ан~JO ДnA формы ' является L0 ~d.· так ч:то :если выполнить Д!:tОЙ'­
ftОЙ ЩеЛLJ.Qк" поместив укаЗiП"SЛЬ МЫши ~Ia ,~n Р'().!'т... cpe.!1~' раэра60ТИИ 2IIITO/:d€lnN6CQ1 ~.аПИЩ!ifj
I'1ррграМ~~IIiIЙ -кад AТlfJ обраfjОiКlIr име~ эt.ого соБыi1я•.

1
...,......
!

776 Часть IV. Программирование с помощью библиотек .NEТ

Класс Program
Кроме файлов. связанных с формой, Windоws-приложение Visual Studio 2005
определяет еще один класс, представляющий объект приложения (т.е. тип, опреде­
ляющий метод Main ()). Обратите внимание на то, что в следующем методе Main ()
вызывается App1ication .EnableVisualStyles (). а также Аррl ication .Run ().
static class Program
(
[STAThread] static void Main()
(
Application.EnableVisualStyles();
Application.Run(new MainWindow());

Замечание. Атрибут [STAThread] дает среде CLR указание обрабатывать все устареешие СОМ­
объекты (включая элементы управления ActiveX), используя SТА-управление (Slngle-Threaded
Apartment - однопоточное размещение), Если вы имеете опыт использования СОМ. вы должны
знать, что SТА-управление используется для того, чтобы доступ к СОМ-типу выполнялся в син­
хронном (а значит, безопасном в отношении потоков) режиме.

Необходимые компоновочные блоки


Н8.Rонец, если заглянуть в окно Solution Explorer, вы увидите, что проект Windows
Forms автоматически ссылается на целый ряд компоновочных блонов, среди кото­
рых будут System.Windows.Forms.dll и
System.Drawing.dll.
Напомним, что подробное обсуждение System.Drawing.dll предполагается в
следующей главе.

Работа с MenuStrip и ContextMenuStrip


в рамнах платформы .NEТ 2. О рекомендуемым элементом управления для соэ­
дания системы меню является MenuStrip. Этот элемент управления позволяет соз­
давать как "обычные" пункты меню, такие как ФаЙл<>Выход. так и пункты меню.
представляющие собой любые подходящие элементы управления. Вот некоторые
общие элементы интерфейса, ноторые могут содержаться в MenuStrip.
• ToolStripMenuItem- традиционный пункт меню.

• ToolStripComboBox - встроенный элемент СотЬоВох (номбинированное окно).

• ToolStripSeparator - простая ЛИНИЯ, разделяющая содержимое.

• ToolStripTextBox - встроенный элемент TextBox (текстовое онно).

с точии зрения программиста, элемент управления MenuStrip содержит стро­


го типизированную коллекцию ТооlStriрltешСоllесtiоп. Подобно другим ти­
пам коллекции, этот объект поддерживает методы Add (), AddRa nge (}. Remove ()
и свойство Count. Эта коллекция обычно заполняется не напрямую. а с помощью

L
Глава 19. СоздаН'ие окон' с nOМС)щ!'Ю Sys.tem.Windows.FQrms 777
различных :fшстрУМеитов p~ npо.еR.ТИРо.вaнюr. но е<::JIИ. требуется. то есть во.3.­
МОЖН'О:СТЬ обработюъ ее :и ВрyчflyЮ.
ЧТQбы привести npимер ИСПо.льзо.вания элемента ynpавлениs M!:3rtIJSt:rip. со;'!­
дайте НОВ.ое npи.'10жеиие Windows Fbrms с ИМбнем Мещ1striрАрр_ lЮместите &JЩ­
мент управления Mell.uEtrip в фо.рму В окне ПРОeI~ТИРОВания. црисвоив ему им-н
mainMenuStrip. В резулшате в файл ".Designer.cs Jtобавцтс.я :новая nере:менная.

pr.i vate System. Windо~э .. Fщ:щs . !1gm:uStrip .lltа.inМепuS t..rip;

Элемент уцра:влеПЩI MenuS'trip имеет о.чень широкие возможноcrи настройки


В режиме llpоеКТИРОf!ЩIИ.Я в V!~al Stucllo 200'5. :Например. вверху у этот о элемен­
та ynр<щлeI:IlI'J;I ec~ь малеНЫ<аЯ :rJJffi'I'Ol'paммa стрелки. в ре3улнтате выб~а которой
ПОЯВJ1Я~СЯ KQhteltCTRo.-ЗависиМый ·встроенный" редаК'юр содерЖИМого·, .RaI( пО.IOl­
.зано. на рис. 19.11 ..

t""'дl1~~!~

~ ~R!!<.J~"'"
"",:k. iTop v

C".n.~ ,.fdd...,

--------------~

PK~. '9.11. "В'СТроенный· p~aj{rop M*nwStrip.

Такие kohteketho-заБЦqiJ'dЫ~ peA;UiTOPbl оодержимorо Iioдцерживаются многи­


JI4]o! эле~ентами уцравлеН1Vl Wir.ldows Fbrшs. Что. же касается Мелu2tri.р. то СООТ­
ветствующий peAaIq0p позволяет быСтрО Сде.пать слвДУЮ1Ii;eе.

• Вставить Wстanдартную" систеJ\oIJ меню (Ffle. "БаУе, TOQls, Help и т.д.).. испо.льзуg
ccьt:lUCylfisert $tandard IteJТ'!$ (Вставить стандаРТJ:JЫеэлементы).

• ИЗМенлгь сТЬUtОВОЧ:iIое tlOведение MenuStri р.


• ОтреДaitтироватъ moбь"Й элемент Ment.iStrip (это про.сто ~быстра.я Э-I,ьтерна­
типа":в отношении воэ.мОЖНQсТИ выба.ра С.ОQТветСnWЮЩето ltо.юсретНQГD эле­
мента в OIше свойств).

В ЭТОМ примере мы ПрОЮВОрИруем :возможtюети ~~cтpoeHH9ГO" редакторз. со­


средоточивmись на со.здании системы меню ~вруч.ную-_ СJiачала Ц режиме про­
еkтироваЮfЯ выберите элемеН'1' yuравлеItЮ1 ИЕ!пuЕ;triр и определите CTaндapтнo~
меню Файл<=;> Выход. впечатав со..отвеТСТВУЮПl'ие И1\1lена в поле с щщокааJЩЙ Туре Неге
(печатать здесь). рис. ] 9.12.
1""'"
I
I
778 Часть IV. Программирование с помощью библиотек .NET

~.• lJk'Ii"oМerIuStrip

Рис. 19.12. Создание системы меню

Замечание. Вы, наверное. знаете, что символ амперсанда (&), размещенный перед БУI<ВОЙ в стро­
ке элемента меню, задает комбинацию клавиш для быстрого вызова данного элемента. В этом
примере указано &Файл~В&ыход, поэтому пользователь может активизировать меню Выход,
~tажав сначала <Аlt+ф>, а затем <ы>.

Каждый элемент меню. введенный вами в режиме проектирования. представля­


ется типом класса ToolStripMenultem .. Открыв свой файл *.Designer.cs. вы уви­
дите там новые переменные для каждого из введенных элементов.

partial class MainWindow


{
private System.Windows.Forms.MenuStrip mainMenuStrip;
private System.Windows.Forms.ToolStripMenultem fileToolStripMenultem;
private System.Windows.Forms.ToolStripMenultem exitToolStripMenultem;
)

При использовании редактора меню метод 1 n i t ia1 i z eComponent () со­


ответственно обновляется. Для М е n u St r i р во внутреннюю коллекцию
то о 1St r i р1t е mС о 11 е с t i о n добавляется элемент, соответствующий ново­
му пункту меню высшего уровня (fi1eToolStripMenultem). Точно так же об­
новляется переменнал fi1eToolStripMenultem, для которой в ее коллекцию
ToolStripltemCollection вставляется переменнал exitToolStripMenultem с по­
мощью свойства DropDownItems.

pri vate void Ini tia1izeComponent ()

//
/ / xnenuStripl
//
this.menuStripl.Items.AddRanqe(
new System.Windows.Forms.ToolStripItem[]
this.fileToolStripMenultem});
ГЛЗВ~ 19. Соадание окон с помоЩЬЮ System.Wind·ows.Forms 779
!I
/1 flieToQlSt.r'.iipМenUIt,em
1/
this. filеТЕ:юlstriр.МеПUltеm.DЕорDоwnХtеша.AddRangе (
t\e.w Sуstеm:WiпclО.W'э ·.Fоrт.s. 'l'tюlStriрltеm[ J (
this. ех} tToolS'UipMen1J1t~m}) ;

1/
/I МainWindow
11
·t..l1i S • CQDtrdls. Add.( this. meh-u-s:tripl J ;

Наколец (что не менее :ражно),элеме-цт улра.ВJIе;вия MenuSt.r i .p добавляется в


l«>ллекцию элементов управления фОРМЫ. Эта Jщrщекция БУДi!'Т рассматр.иватьсн
подробн.о в DJaJ!e 21. а 8десъ B~O отмt!"ГИTh, 'що эдемент УПРаБЛеНWl МОИtет быть
lllЩИМЫМ во uрем,я BhUIo.m-Iени~ тодыю 'в ТQM С.Jlу'ще, к@гда этQт элемеНт присут­
С1'вует в уназанноц КОJЩеJЩИИ.
LIтобыI з~рmитъ создаиие ПРОГРa:м::l\lЩоrо кода нашего прим:ера,. вернитесь в
режим проеЮ'ирован.ин и ВЫДOJЩИте обра!50ТНУ СОQЫтия С1 i а k ДЛ.iI nyн.КТa меню
ВЫход. использун ююпку собhпЦЙ, в окве СВОЙСТВ. В с:генерировШшом обработчике
со6ЫТffll BЪD10.l1Н1fi'e вызов Appl i са t iол .EMi t.
pr.ivat;.e v0id exi tToo15t:r;lpMenuIteJII_Click tobj .ect эеndе-J:, E'JtDtP:.rg.s .е)
I
.дррl i.c.a t ion. EKi t () ;

Теперь вы .можете СJWМnИЛИро'ВЭIГЬ и выполнить СВоЮ программу.. ПроверЬ1'~,


что БЫ можете завершить рабо'I}' приложе'.J-ЩЯ как с ПQМОЩЬЮ выбора ФаЙЛ Q 81;>IКQД
из меню, так и с помощью нажатия <Alt+ф>. а затем <Ьг.> на R1l!Шиатуре.

Доба' вnение элемента Textbox в MenuStrip


Д1;1Вэйте создадим HQВbrn элемеН:!' М~RЮ наивысшего YPQBHfI. ПРИСВQИВ этому
элементу имя .изменение ЦBeT~ фо~а. ПОДЧЮН:ШIЫМ элемffiiТОМ:В этом меню бу­
ДИТ не пункт Меmo. а элемент 10-01 St r i pTextBox (рис. 19.13). Добавив НОВЫЙ 3лС­
~eHT yn,равленип •. ИЗМените его имя на t ,oolStripTex>tBQxColor с ПОМQЩЪЮ ОJЩa
t.'ВОЙств,
НашеЙ целъ-ю явmreтся ВОЗМОЖRОСТЬ выбора пользователем цвета (красный,
зелевьrй, рово-в.ый и т.д.). значение которого буде.т устэ.н:овдев;о ДЛЯ СВf;>йетва
BaekColo.r формы. СPrачала обработайте событие 1,ostFDCU-s. ДЛЯ нового ч-дец-а
ToolStr:ipTe xtвQx в рамках KOHt:тpy1tТopa формы (~TO L'Обытие про:цсхо.диттотдэ,.
KOJДa ТехtВш:в Тtюlstriр перестает БЫIЪ aкT~Н:ЫМ ЭJI€Мt::RТОМ интерфеikаl.

publi'C Маiлwind.оw ()
(

toolS.tripT.extBo.x col0r. Lost'F'oc\:JIs


+= леw R:I1€ffi. tl:lзndlеr (too l S tripText ВoxCo lo r L Qs tFQC; US );

1
r
I

I
780 Часть IV. Программирование с помощью библиотек .NET t
I

~ IМinМenustф

Рис. 19.1З. Добавление TextBox в MenuStrip

В сгенерированном обработчике события прочитайте строковые данные, введен­


ные вToolStripTextBox (с помощью свойства Text), и используйте метод System.
Drawing.Color.FrornNarne (). Этот статический метод возвращает тип Color, соот­
ветствующий известному строковому значению. Чтобы учесть возможность ввода
пользователем неизвестного цвета (или любых других неподходящих: данных), ис­
пользуется простая логика try/catch.
void toolStripTeKtBoxColor~LostFocus(object sender, EventArgs е)
{
try
{
BackColor = Color.FromName(toolstripTextBoxColor.Text);

са tC)1 { } / / ПРОСТО игнорировать неправилыwе дaНIW8.

3апустите обновленное приложение снова и попробуйте ввести названия раз­


ЛИЧНЫХ цветов. В результате правильного ввода вы должны увидеть изменение
цвета фона формы. Чтобы получить информацию о допустимых названиях цветов,
изучите информацию о типе System. Drawing .Color в окне обозревателя объектов
(Object Browser) Visua1 Studio 2005 или в документации .NEТ Frarnework 2.0 SDK

Соэдание контекстных меню


Рассмотрим теперь процедуру построения контекстно-зависимых меню (т.е.
меню, раскрывающих:ся по щелчку правой кнопки мьппи). Классом. использу­
емым для построения контекстных меню в .NET 1.1, был класс ContextMenu,
но в .NET2.0 предпочтение отдается типу
ContextMenuStrip. Подобно типу
MenuStrip. тип ContextMenuStrip поддерживает
ToolStriplternCollection д.iIЯ
представления всех элементов меню (ToolStripMenultern, ToolStripCornboBox,
ToolStripSeperator, ToolStripTextBox и т.д.).
Перетащите новый элемент управления ContextMenuStrip из панели инстру­
ментов в окно проектирования формы и измените имя этого элемента управления

L
780 Часть IV. Программирование С помощью библиотек .NEТ

k. _ _ ____. _ _

Рис. 19.13. Добавление TextBox в MenuStrip

В сrенерированном обработчике события ирочитайте строковые данные. введен­


ные вToolStripTextBox (с помощью свойства T6xt). И используйте метод System.
Drawing.Color.FromName (). Этот статический метод возвращает тип Color. соот­
ветствующий известному строковому значеIШЮ. Чтобы учесть возможность ввода
пользователем иеизвестного цвета (или любых других неподхоДЯIЦИX данных). ис­
пользуется простая логика try/ca tch .
void t oo lStripTextBoxColor_Los tFocus(object sender, EventArgs е)
{
try
{
BackColor = Col o r.FromName(toolstripTextBoxColor.Text):

catch { ) /1 ПРОС'l'о Иt'НОРИРОВI!'l'Ь неправи.пьиые данные.

Запустите оБНОRlIеиное приложение снова и попробуйте ввести названия раз­


личных цветов. В результате правильного ввода вы должны увидеть изменение
цвета фона формы . Чтобы получить информацию о допустимых названиях цветов.
изучите информацию о типе System.Drawing.Color в окне обозревателя объектов
(Object Browser) Visual Studio 2005 или в документации .NEТ Framework 2.0 SDK

Создание контекстных меню


Рассмотрим теперь процедуру построения контекстно-зависимых меню (т.е.
меню. раскрывающихся по щелчку правой кнопки мыши). Классом. использу­
емым для построения контекстных меню в .NET 1.1. был класс ContextMenu.
но в . NET 2. О предпочтение отдается типу С о n t е х t М е n и St r i р . Подобно типу
MenuStrip. тип ContextMenuStrip поддерживает
ToolStripltemCollection для
представления всех элементов меню (ToolStripMenuItem. To olStripComboBox,
Too lStripSeperator. ToolStripTextBox и т.д.)_
Перетащите новый элемент управления ContextMenuStrip из панели инстру­
ментов в окно проектирования формы и измените имя этого элемента управления
Глава 19, СоздаНtlе ОКОН t ПОМОЩЬЮ System,Windows,F-oгms 781
ка fоrltSizеСоп1:ехtstriр с помощью охпа CJ:\оЙств. Обратите внимание 1-1а то,
чro теn~ьдочеР1Ше элеМенты в ContextM'eЦUStrip МOНЦiO добав.л:ять графичесни,
!!l9Ч'J'И так же, ЮlК прй реДaRТировamm MenuStrip [очeIЦJ npиятное изменеgие по
ср~неНИIb с :методом, npед.narавшимса в VfsuaJ StudJo .NEТ 2003). дтш нашего· при­
мера добaв.ыrtt три элемента ТооlstriрМеrшItеm с :t.i3.ЗВанинми ~РYf.lНblЙ, Срецн}:о!й
и l.'1елкиЙ (рис. 19.14).

с ·~)кt~~)-,..;;;tРt;;';I~'i!М;~I~-- ~ .- ~ - -'"ix_;
I
I

Рис. 19.14. СоЭда.ние Con.te:xtMen.uStrip

Это IwнтeRCTHOe менш преДназначено ДiIЯ то.го, чтобы rnЩЬ,эоватедь МОГ выбрать
размер ШРИфl1'а ДJIJI ~оdбщения, отображаемого в обnасти клиента ФОРМЫ. Чтобы
упростить себе задачу, создайте' ТИП перечнл TextFont.Si ze в PI4~ цространства
имен МеIщst1:~рАрр и ООЪЯ:ВИТеновый. член-перемепную ЭТОГ0 типа в pmvmax Fo:mn
(устано13ИВ для перемеНffОЙ значение :['extF.ontSize .FontSi zeti!orroal}.
лamеsраi;;е MainForm
r
/ / ВСПQlllоr~am.JD1Й П8р8'Lень ДJ1" pa!blepa ШРPlфorа..
en·aтn 'J'extF.ontSi-..;е

FootSj zeHnge = 3:0,


Fо пtSi ;;:eNo:rmal = ,2 о r
FопtSizеТiпу = В
}

рйы1 ic Clil5S MaillForrn : Form


{
11 ТехУ1ЦИЙ размер шрИфТ•.
private 'J;'extF()nt51~e .cu.r.rE'ont.s:ize =
'T extP·o ntSize. F'ontSi zeNatmaJ';'

Следующим шагам НВШlетсн обработка CQБЫТИf.\ Pai fJt, формы с IЮМQЩЬЮ окна
свойсТВ. Как будет показано в слещrющей rлаве, со'бытще Painl': поавОJlЯет ОТООРIl-

1
r
782 Часть IV. Программирование с помощью библиотек .NEТ

зить в клиентской области формы графические данные (включая представленный


в соответствующем стиле текст). В данном случае мы должны отобразить тексто­
вое сообщение, используя указанный по.льзователем размер шрифта. Не беспоко­
ясь пока что о деталях, мо.дифш.щруйте обрабо.тчик события Paint так. как пред­
лагается ниже.

private void MainWindow_Paint(object sender, PaintEventArgs е)


{
Graphics 9 = e.Graphics;
g. DrawSt ring ("Щелкните здесь правой КНОПКОЙ МЬШJИ ••• " ,
new Font("Times New Roman", (float)currFontSize),
new SolidBrush(Color.Black), 50, 50);

Наконец, нужно обрабо.тать события


Click для каждого. из типов ToolStrlpMenu-
Item , поддерживаемых злементом ContextMenuStrip. При этом можно, конечно,
иметь отдельный обработчик событий Click ДЛЯ каждого из типо.в, но мы укажем
один обрабо.тчик событий, который будет вызываться при щелчке на любом иэ трех
злементов ToolStripMenuItem. Используя окно свойств, укажите ДЛЯ обработчи­
ка <:о.бытий Click имя ContextMenuItemSelection Ciicked ДЛЯ всех трех типов
ToolStripMenuItem и реализуйте со.ответствующий мето.д так, как показано ниже.
private void ContextMenuItemSelection_Clicked(object sender,
EventArgs е)

11 Попучение ЭJJенента ToolStripМenuItem,


11 на котором BыnonHeH щелчок.
ТооlStгiрМелuItеm miClicked =
miClicked = (ToolStripMenuItem)sender;
11 Поиск эnемеН'1iа, на котором SJoШоnнен щепчох , по его имени.
iE (miClicked.Name == "hugeToolStripMenuItem")
currFontSize = TextFontSiz e.FontSizeHuge;
if (mlClicked.Name == "normalToolStripMenuItern")
currFontSize = TextFontSize.FontSizeNormal;
if (miClicked.Name == "tinyToolStripMenuItem")
currFontSize = TextFontSize.Fo ntSizeTiny;

11 Указание форме обнови~ь npедс~aвnение.


Invalidate();

Обратите внимание на то., что использование аргумента sender позво.ляет


определить имя члена-переменной ТооlStriрМеrщltеm, чтобы установить размер
текста. После этого вызов Iovalidate () генерирует событие Paint, кото.рое вызо­
вет ваш обработчик события Paint.
3аключитедьным шаго.м является информирование формы о том, какой эле­
мент СолtехtМепuStriр должен отображаться при щеЛЧI<е правой кнопки мыши в
области клиента. для этого с по.мощью окна своцств установите значение свойства
ConteKtMenuStrip равным имени элемента контекстно.го меню. По.сле это.го. в ко.н­
тексте InitializeComponent () появится следующая строка.

this.ContextмenuStrip = this,fontSizeContextStrip;
Гnаев 19. СоздаН14е 0,1(011 с ПОМОЩЬЮ Syslem.Windows.Forms 783
Если в:ЬШnЛНИТЪ ПРl-ш~жение теперь, ВЫ СМQЖеТt: изменить размер отображ~.е­
МDтотекcrОВОГО' СDОбщения по щелчку правой кноl1Ю1 мыши.

3аNеч~ие. С ПОМОЩЬЮ CBC~Ba ContextMenuStr'lp в каt-пекстное ме~1O можно ВКIlIO~~ТI>


любой .эЛемент управления. Например, eCJ'lId !I ДIIIалОГQВОМ rжtfе lCoflTelCCТHoro меню создаТk>
Qбъект BU t.tOE (Кнопка), то соответствуюЩloiй ПУНКТ меню .БУАет Qтображаться только тогда,
когда щеJi\ЧQК будет а.ЫПМI:lен в рабочей области КНРЛI{И.

Проверка СОСТОЯ.НИSlэлеме.нтов меню


Члены типа тооlS·t.riрNеrшН.>е1D. ПО3ВОЛНЮТ пр(')в·еритъ состояние ЭJ.Iемевта:
мещ0, cд~aTЪ его доС'ryшrым:или G.ВpЪNb1М.. В табл. 19.11 даются ОПИСaJiИЯ HeJ<.O-
"торых из наиболее ]щ'fересных свойств этого типа.

1itблица 19.11. ЧлеМЬI тиПа тоаlStriрмеmJItеrп

Чnем Описание

(:hecl.ed Получает ИЛ~ уетанавливаеТЗНa<l6IiИе" яал~ющееся иftДН'l<атором налl>lЧи~ 01"


метки Bbll!iQpa "в строке с текстОМ да,нfЮГО TocJIStr iрИеn)JItеПl
Cbe'~lcQn Click. Получает или уtiанавливает знаl.Jгние, R8f!ЯlOщееся индикатором неоБХQдим'О·
Сl',и ПOflВJJ8НIiIЯ отметки выбора ДЛЯ' Дaf!j:jОГ(J TC!olSt.ripMe.n·,J 1tem при ще1l'lке

ЕпаЫеа Получает или устанаалlilвает значение.! являющееся иr~ДIo:IкаifОРQМ ДООТУПI'iОСТl-1


данного 'Гоо] St,ripMeJJuItern

Давайте расшир.ам I:I:щnе :КО1'I.~кстно~ М'СНЮ' так. 'ЧТобы в нем ридом с выбран­
ным в настояm:иii момент nyщcrом менюотоор~аct, oтмe-rкa IIЫfiopa. Установить
отметку дщt данногоэле.мСljта медю Qчень просто (для этого ну-жно УСТaJ{ОВЙТЬ
Значеl-ше С'ВОЙСТБЗ Checked paВHJ;Ц14: ·t;rue). ()ДВа:ко Д;ЛЯ ТОГО, Чтобы проследитъ, 1(<1'
КОЙ II)l1IКТ меню должен быть отмечеI;f, ПО1'ребуется дополнительная программная
J10rиI01. Одним иэ ,60зможaых ДOДXGДO~ ЗДflСЪ яВЛi.fIется опредeJiение специальной
пере..чеННQЙ Торl,$.triРМе.РllItщп, которШJ будет представлmъэле:мент. ОThfечеttпый
в аастоюциii момент.

p)lblic class Ма:LлWi,:нdDw : FQrJТi


{

11 YU.!WВa.S!r О'ПЦlчеИ!ШЙ э1iвмеwr.


pri 'la t.e TOQlSt.r iрИёnul te1it СиТ rentCt,ec"kedlter,n;

НЩЩМIЩМ, ЧТО раамеромтексТ'а по умо.1ГИНиЮ являетсЯ техtFоDtSizе;!..Н:mt$iz€-­


Norma 1, С учетом этого нв"IвJlъным отмеченнъiМ элементом 11 'f<H:II5t r iрМ€ЮUItе'Щ
должен быть Dо'rmа1ТооlStr.iriМеЩJItе.ш" ИЗ'мСНИТе конструктор формы "(а'К, ~I(
ПОКaэaRО :НИЖ~.

public MainWir,:dow О
j
11 Ваcms.цуeиJoIiir ме'1!о,ц ДJIJiI. цеитрировaIIИи ФОРIAI.
CeТJt e rToS'creeiJ
();
lnitiali2eComponent1);
784 Часть IV. Программирование с помощью библиотек .NET
r
11 YC'l'aHosxa О'l'Ие'l'JCИ 81о1бора дn_ Э'n_И8Н'l'а М_IШ 'Средний'.
currentCheckedltem = normalToolStripMenultemi
currentCheckedltem.Checked = true;

Теперь вы имеете возможность программно идентифицировать отмеченный в


настоящий момент элемент, и последним из необходимых шагов будет обновление
обработчика события ContextMenultemSelection _ Clicked () . в нем нужно снять
отметку выбора с элемента, выбранного ранее, и отметить новый текущий объект
ToolStripMenultem в соответствии с новым выбором пользователя.

private void ContextMenultemSelection Clicked(object sender,


EventArgs е)

11 Удan_ни_ О'l'М_'l'JCИ 81о1бора д,n_ Э'n_И_Н'l'а.


currentCheckedltem.Checked = false;

i f (miClicked. Name == "hugeToolStripMenUltem")

currFontSize = TextFontSize.FontSizeHuge;
currentCheckedItem = hugeToolStripMenuItemi

if (miClicked.Name = "поrmalТооlStriрМеПUltеm")
{
currFontSize = TextFontSize.FontSizeNormal:
currentCheckedltem = normalToolStr i pMenultem;

i f (miClicked.Name == "tinyToolStripMenuItem")
{
currFontSize = TextFontSize.FontSizeTiny;
currentCheckedltem = tiпуТооlStriрМеПUltеШi
}
11 YC'l'aнosxa О'l'ие'l'JCИ 81о1бора д,n_ ноэоrо эпемеН'l'а.
c urrentChe c kedltem.Che c ked = true;

На рис. 19.15 показан законченный проект MenuStripApp в действии.

Исходный код. Проект MenuStripApp размещен в подкаталоге, соотвеТСТВуЮЩем главе 19.

Рис. 19.15. Установка и удаление отметок вы­


бора для элементов ToolStripMenultem
1 Гl1.ава 19, созд.ание ОКОн (') ПОМОЩЬiG System'windows,Forms 785

Ра'бота с StatusStrip
в дополнение к системе меню :мноrие формы предлarают поддержку CТТW01(U.·CQC­
rrюяJd.Ui.. ~оторШf обычно разМеЩаетсн в НИЖНеЙ части формы, Строка сm:тtJяmm
моЖет делиты:я па любое число "nанелей" (;: текстовЬй (или графW<Iесной) информа­
цией, содержащей пояснении Д..1IВ пyнк:rов меню, текущее времи. или спецИалЬные
Да1-шъtе прюroжеНИЯ.
Хотя подцержка строк СОСТОЯНИЯ (с ПОМОЩЬЮ типа SysteEl. Wiпdо.WS., FormS.
Stat.tJsB,ay) предлата:етсн с момента появлеНИяnлатфор:мы ,NEТ•.B .NEТ2,O вместо
простого элемента Stah1sВ.ar предлагаетсн ИCIIQ,m,зовать НОВЫЙ тип Start1JsStrip.
Подобно обычной C"I'poKe состояния. StatusStrip может состйятъ из любого чИс­
ла панелеЙ". содержащих 'ГеКСТDвы~/rpафические дaннbre, предоставленные тИПОм
'r'o.olStripStatus. Однако St.at\JsStrip может содержать и дополНительные эле­
менты, Н3Примерf следУЮЩего В:ида.

• ToolStripProgres.sBar - встроеннъпi ИНДИRaТОР выполнения (хода за­


дания).

• lIIoo1StripDropDOWnButton- встроешraя Itыоrrка. отображающая:дри щелчке


на ней раскрыJ3aIOЩИЙСЯ СПИСОК :ваРИIЩТО:В выбора.

• То olStri рsрН tBUit t~!1 -. lI(щобен То 01 Stri pDropDownBut ton, НО отображает


элементы раскрывающеrоCSl СIIИСI'э. l'фJlЪко тогда. ~orдa ПOЛh801Щтель ще)'IКает

непосредствeIOiО в областн ра:скрыающ~гося еnиска. '!:oolStri.pSplitButton


rIpe.длагает 1'акж~ поведettие. аналогичное обьrчной КН()IЩе, и поэтому может
под.цержпваn обрабmIfY саБЫПi1I сl ick.

для примера м;ы построим новый объею' Маiг.WiпdОW.В1(оторомдодцеРЖJmaет­


си простое мелю [ФаPiл ~liЗыход и Справка ~O np.oгpaMM~) и StatusSt r.ip. Левая:Па­
нель Сl'J;ЮКИ состоящrя будет ИCnOJд>З0вар,CJl длfl отображения. с"'ропо~ых данных,
соответСТВУЮЩИХ вьrбраннQМ)' в настоюций момект элементу мwю fдапри:мер, при
выборе дользоватеЛf;М элемента Выход в строке будет отобража:тъся иВыход из при­
JIоже:ния:~l·
Средняя. 'Часть С'rpОRИ состоянин буд~ отобра:щаn о.ццvиз двух ДЩfaМИЧеCIШСОS­
дaIlаемых:зна~еНИЙ. соответствующих текуще;мувременд и текущей дате. Наконец,
правая часть строRИ состояния: будет представщл~ь ТИII ToolStripbropDownBu:tton.
позволЮQЩI'IЙ полъзс!3ателю лареюnоЧИ'ГЬся с отображения .даты на ото5рмкение
врем~Щi щ .наоборот (да еще и r,:- ДИRтогра:ммой счастливого лица :в придачу!). На
рис. 19,16 llOlЩЗaRО OR1'lO приложе'ЩlсН В своем окончательном варианте,

Рис. 19.16. ПDиложеl1~е Statasstr.ip


r
786 Часть IV, Программирование с помощью библиотек ,NET

Создание систеМbI меню


Создайте новый проект приложения Windows Fbnns с именем StatusStr:ipApp.
Разместите элемент управления MenuStrip в окне проектирования формы и соз­
дайте два пункта меню (ФайлqВыход и СправкаqО программе). После этого задай­
те обработку событий Click (щелчок) и MouseHover (задержка указателя мыши)
для I~аждого из дочерних элементов меню (Выход и О программе) с помощью окна
свойств.
Реализация обработчика событий Click для элемента ФайлqВыход просто завер­
шает работу приложения, а обработчик событий Click ДЛЯ СправкаqО программе
отображает OI-CHO сообщения MessageBox.

private void exitToolStripMenuItem_Click(object sender, EventAr:gs е)


( Application.Exit(); f
private void aboutToolStripMenultem_Click(object sender, EventArgs е)
{ MessageBox. Show ("Му StatusStripApp!"); ]
Обработчики событий MouseHover:, отображающие подходящие подсказки в ле­
вой панели StatusStrip, мы с вами обновим немного позже. Пока что оставьте их
пустыми.

Настройка StatusStrip
Добавьте в окно проектирования формы элемент управления StatusStrip и по­
меняйте имя этого элемента управления на mainStatusStrip. Следует понимать.
что по умолчанию StatusStrip не содержит вообще никаких панелеЙ. для добав­
ления трех панелей МОжно использовать разные подходы.

• Создать необходимый программный код вручную, без помощи инстру­


ментов проектирования (возможно, с помощью вспомогательного метода
CreateStatusStrip (), вызываемого в paмRax констру«тора формы).

• Добавить нужные элементы в диалоговом окне, появлнющемся при выборе


ССЫЛI<и Edit Items (Редактирование элементов) из меню контекстного редакто­
ра StatusStrip (см. рис. 19.17).
• Добавить нужные элементы по одному с помощью раскрывающегося меню
новых элементов StatusStrip (рис. 19.18).
Мы испольэуем раскрывающееся меню новых элементов. С помощью это­
го меню добавьте два новых типа ToolStripStatusLabel, назначив им име­
на toolStripStatusLabelMenuState и toolStripStatusLabelClock, и тип
ToolStripDropDownButton с именем toolStripDropDownButtonDateTime. Кан и
следует ожидать, в результате зтого в файл *.Designer.cs будут добавлены новые
члеиы-переменные и соответственно обновлен метод InitializeComponent().

I
1
L
Гпэва t'9. Создание !)ХРН О ПDМDЩоlO Systerti.WimJDws.Forms 781

... .
• Г,/Н,,1 _ ~ х

.~fr""'<P';;"1,,~~ '~, ,'~P •


i'%t .~" ~i-t..~ ~ ..

0:0;' •

1-~-:'fIb:Je
f ..
i Dodc _ .

~~-_.~---
... ,.. ~ ~ :.. v,- " ... ; •.. ". ~ ... ).... ~ .....•.• _ •.• ~
;;;;\ _ .........1 i I_~ ....",........... \
i ~ IJ,,,~
,.-. '.' ... .._."....--.. _.
;; ~~'IU!\'~''t'!
~-
t r.
!

I_~ ~ ___ ._ . ___ С. < , _. . _ _ . . ._ _ • _ _ _ _ _• • _ ... _ ....... " _ _ ._ ._ _ _ .1


Рис.19.17. Контеtl;СТНЬ/Й редактор sta:tL1sStrlp

j ~~I b~
'I~ _
_~ __-- - - . . . . ._,__...._ ~ . _____ ~-. . __-~- .- --
Рис;. 19.1 в.. Добавление элементов с lТомощью раскрывающегося
меню tювbIX . злементов stа1шs5i t.:riр

3aмeтb:te, что Stаt1.JдStriр поддерживает ~нутренНКiЮ 1ЮдЛекцию ДШI пред.­


CTabJtei-lия,веех создщ-шык. па:нелеЙ.

par t.:iial c l ass Hai't lFo r:m


{

,
p:ri,,",ate v o i d Inl t ia1i.zeComponent (.)

11
1/ mairtStatusStr.ip
/1
788 Часть IV. Программирование с помощью библиотек .NET

this.mainStatusStrip.Items.AddRange(
new System.Windows.Forms.ToolStripItem[)
{ this.toolStripStatusLabelMenuState,
this.toolStripStatusLabelClock,
this,toolStripDropDownButtonDateTime}) ;

private System.Windows.Forms.StatusStrip mainStatusStrip;


private System.Windows.Forms.ToolStripStatusLabel
toolStripStatusLabelMenuState;
private System.Windows.Forms.ToolStripStatusLabel
toolStripStatusLabelClock;
private System.Windows.Forms.ToolStripDropDownButton
toolStripDropDownButtonDateTime;

Теперь в окне проектирования формы выберите ToolStripDropDownButton и


добавьте два новых элемента меню День недели и Текущее время. соотвеТСтвенно
назначив им имена dayoftheWeekToolStripMenuItem и currentTimeToolStrip-
MenuItem (рис. 19.19).
r '~ -..+";z.=_· ~~-- _ ··,="",· ~,,·-"!;,·,,~ ~,:·~ ·,::,,'- - -.~ -~ . -- ~'- ~'- -':7:"",':~" ' ~"C---~'':''' '''":-"'~':'''-:7'''''''''=''''''''''~"''''''~""'""'" . "''''.,.........-••,.".,.---,. .- .-- :-:..."."j
;.' 'МаlпForm.сs (Dl!sllIn]"I.~ ___,____ • __ ~".i~ , " -- - , --'-------_~._~ _~!
i i
• f!lПll1 _ Li Х I

Демь H.A~; "---- ~ [~=:_~~-;~I.~~~~:~~сТ~]


;, r ' V~j!~~""~;"''''' ~~r:i. x' ,,~~ " '111: '

" ')1~" , , f? '"1 ..


I
Текуще•• ре"'" !
I
_____1

IblМlnStatusStrlp О tknerDlte'ГllnaUl:Jda!e ; meмtrlpl I


!~---"~.- -,' .-.____ '_о. _ , ___ _ " ' ___ ,~ _ _ _ _ _ _ . ' _ _ ' _ ,_ __ __ _ '_ __ _ _ • • ~ '- _ __ _ ' _ _ . _ - . __ ____ __ ..1
Рис. 19.19. Добавление пунктов меню для элемента ToolStripDropDownButton

Чтобы настроить панели так. как показано на рис. 19.19. нужно установить
подходящие значения ДЛЯ соответствующих свойств в окне свойств Visual Studio
2005. в табл. 19.12 для элементов StatusStrip предлагаются описания свойств.
которые нужно установить, и событий. которые нужно обработать (вы, конечно.
можете настроить панели та1(. как сочтете необходимым).
3наЧ"ение свойства Image члена tооlstriрDrорDОWПВuttоnDаtеТimе может
указывать на любой файл с изображением. размещенный на вашей машине (при
этом. конечно, следует учитывать то, что СЛИIШЮМ большие файлы изображений
могут по рождать проблемы). Для натего примера вы можете использовать файл
llappyDude.bmp, предлагаемый вместе с загружаемым исходным кодом для этой
книги (посетите раздел загрузки Web-узла Apress, размещенный по адресу ht tР ://
www.apress.com).

L
rfltJB8 19. СОЭДilние oroht t ПОМОЩЬЮ sуs't~m.Wjndоws . FоrIТ)Э 789
Таблица 19.• 12. Конфигурация паН8щ!iii .3ta'tusS.trip
События ,цnЯ"
Члеt\-перемеНН1IR панenи Свойства АЛ1I УСТ8НОВICИ
обработки
tool St.r ipStat, UsLд.Ье liФ~!lцS·t аt~ SJ?rlng=true Нет
Text = (ПУСТО)
'rextAJ.i.g n = TopLef"C
tOolStripSta t.usLabelCloc k BQrder~ideв =All
Тежt = (пусто)

tО.:J1StriрDrD.рvо.~mВutt;Qn1Jа:tеТiПlе Tma.ge = (О:М. l-1ижii) . Не'!"

daY0ft.heWeekTQol$tripMenult: em Тек !. П~нъ неD.ели'" Mous-еНоvеr


Click
СQ.r:·rепt ~.imеТ061StriрМепult:еm "Тек ущее время' l МuцэеНо-vеr
Click

Итак., Пpo.e:RТnpaвани.е нашего графическоr.о ивтерфеl:i<щ ПОЛЬЗ0Вa:reля заверше­


lЩ. Но,. чтобы ревлиЗ0вать· ОС.1'а»tпиeся. обработчmщ событий. мы с вами до.jIа~.ны
13blЛCНИТЪ роль компоllента Тiщеr (1.'аЙ'Ж<,:р) .

Работа с ТYlпомТimеr
НanОмним. что Средня.я часть строки состо~нин дол.щна отОбражать текущее
время или те1iy.1ЦyЮ дату. в зависимости от предпочте;н;ий подьэовате:n:я.
Первый шагом на пути к достижению этой цели .явд;J!ет.сн доба:вл.ение й фор­
му ч.лена-переменноЙ Tirnce r - КОМпонeн:rа.ВЫЗЫВающ~го некоторЫ'Й метод (уна­
;Jанный С ttoмощъю обработчика соБЬJтин. '1'ic k,1 через ;заданный интервал вр~мени
(указaнm:ilЙ .с ПОМОЩЬЮ свойtnа Interva1).
Перетащите I{омпопент Time'r в О1що npоеКТИРlJjва:ни.я формы й переимену"Йтt'
ето 1) ti1DerDateTimeUpdat€: . :ИCnOJlЬВ)'я оIШО G~ОЙ~:г13., установит-езначение свой·
(:тва Intе:ь:vаl равным 1000 (это зна.ЧeJ-ще в МИЩIИсщrундах). з..зНз:чени:е С',войства
Enablecl-· paвнblMtrue (истина). Наконец, обработайте событие Tick. Перед ре­
алиЗацией ОQработЧИRa событий Tick oдpeд~тe в проекте новыIй тип переЧЮ1 с
именем Date'I'imeForma:t. ЭтОТ перечеиь, будет ИСЩЩЪ30'ваться д,!ш выяснения то.го.
что должен отображать вТФройэдем:ент Т.оо1 ~;a, r ip3.t p tu sLa1J:e l - текущее вре:мя
шти re:кущую да'l)'.

enurn .D ateTi1'l1eFotmat
I
Юю wСlосk,
SbowIJay

Построив переченъ. обновите MainWindow так паж I1редлагаетс.1Зниже.

pnblic partial ·cla s-s Ma i riWitid·ow : Fo:r;m


!
J/ Каll:ОЙ формат О"!1'QБР&lllа~?
Da t.eT'i.meForma·t qtFopna t = Ра teTimePb1.-щаt . ЭhоwСl otk;

priv,a.te VGid ti.rnerDat~:rimeUpdat~_Ti.ck (obj~ct se.ndE.r:, Ev~rltll.rgs е·)


I
striлg panellnfo ·= ,
" '1' ..

.1.
790 Часть IV. ПрограММl1рование с помощью библиотек .NET

11 Создание текущеrо формата.


if (dtForma t == DateTirneFormat.ShowClock)
p anelI nfo DateTime.Now.ToLongTirneString() :
else
panelIn fo DateTime.Now.ToLongDateString();
11 УС!1'ановка текста ,цnll панe.nи.
toolStripStat u sLabelClock.Text panelInfo;

Обратите внимание на то, что обработчИR события Timer использует тип


DateTirne. Здесь вы просто читаете текущее время ИЛ~ дату системы, используя
свойство Now, и устанавливаете соответствующее значение для свойства Text чле­
на-переменной toolStripStatusLabelClo ck.

Включение отображения
в этот момент обработчик событий т i с k должен отобразить в панели
toolStripStatusLabelClock текущее время, если значением по умолчанию члена.­
переменной DateTimeFormat является DateTimeFormat. ShowClock. Чтобы позво­
лить пользователю переключаться между отображением даты и времени. обновите
MainWindow так. как npедлarается ниже (заметьте. что здесь также указано. какой
из двух пунктов меню в To olStripDropDownButton должен при этом отмечаться) .

public partial class MainWindow : Forrn


(
11 !Сажой формат О!1'ображаon.?
DateTimeFormat dtFormat = DateTirneFormat.ShowCl o ck;
11 Ужаз~ае!1' оnseченНblЙ эnеиен!1'.
private ToolStripMenuItem currentCheckedItem;
public MainWindow ()
(
Initialize Component();
11 Эти СВОЙС!1'lIа можно TaltJlCe устанОIlИ'1'.
11 В ожие Properties.
Text = "Пример Statu s Strip";
CenterTo Screen() ;
BackColor = Color.CadetBlue;
currentCheckedltem = currentTimeToolStripMenuItem;
currentCheckedltem.Checked = true:

private void c ur r entTimeT oolStripMenu ltern_Click(object sender,


EventArgs е)

11 УстанОllка О!1'метltИ и фОpNa!1'а времени дn.: панеnи.


currentChe ckedItern.Che cked = false;
dtForma t = Dat e TirneFo rrnat.ShowCloc k;
currentChe c kedltem = currentTimeTo olStr i pMenUltem;
currentCh ecke d ltem. Checke d = true;

L
Глава 19. Соэдание OKO~ с ПОМОЩЬЮ Svstem.WindtJ.ws.FoJms 791
pri vat€ vo1.d da:yo:ftlleWeekToolзt;r ipMefl\:iltero_ Click (obj ect э:еndе-r.
Evei'JtArgs е)
{
11 УС'1'8Ш)ав:а 01l'КQ.'1'ЖИ и форJJta'1'& }:(&!1'JOI д.riJI' niUteШt.
(:щ: ren-tСЬеdre,jI'tеm. Checked = fal$e;
dtForrna t. = DateTirneForma t . БЬо.wDау;
currentCheckedItem = 'dayof-t.hеWееkТ.QоlSt.r iри~rщ:ItElm;
(;!UIJ;8ntC:heckedI tem. Cbecked = t.rue;

ВЫВОД подскаЗОJ( ДЛЯ В.ыбранных элементов меню


Ha:кo:н~ц. нужно J;rастроитъ rr~рвую mшель тап. чтобы она содерщада теlЩ'F под­
СRaЗltn .Д1Ш выбранаОГG пэлъаdВа'reJJем мемента меню. Бы знаете. что ООJIЬщипетво
IJpWlожеЮlЙ отображает в левой части строки соcrрЯ}-mя IЮЯСliЯЮЩVJP информа~
цюа (например, ~:e~oд из nPИЛDжения~). соответствующую выбрщrном:у RОIieЧ­
НЫМ поЩtзователем пу.нкту меню. Если вы обработалй события MouseHover ДШI
всех элемеJiТQБ меню l'JИЖНero уровня в MenuStrip и ToolStripDr·opDownButt.on.
то остаетел TOJIh1iO пр~свои:ть подходящее значение свойству ТЕ»: t ДЛН члевв.-пере­
меЮlОЙ tроlStriрS ·tаtusLаЬаlМеrшSta'tе. например:

р'!: i va te void ех.}. t'Iaol S'tripJ"1eI)uTt~m_ Мo1iI&e~over ' ~obj ect sender,
Even !:ЛУЧ;; е}
( 1:,001 Strip5la.t:usLa-ЬеlМеПUStа,tе. Text
= n Bы~ ОД из ЛРИЛО. жейия _'''; )

prlvate void аЬо.u· t· ТОQI$t.r1рМеn1:IIt€ш Mous.eHover (obje.t t sehder,


БvелtА:rgs е)
[ t001StripSt.atuSLabelM~nuBtdte. Te~!:
= "О't'ображение информац1llИ- а nриnожении _fl ;

pri ,ra·c.e ·I!'·oi(j: dа:УD:ftI1еWееkТо. ОlStriрМеЛUltеm~olJseH(:I\rer (rэЬj ect seDder,


Even·tA:tgs е)
(bdlStr iрStаt.uэLаЬе1МегнrS.tаtе .• Тех!:
= '~От-Ображение Т'еК'ущей даты."; }

];Jгivаtе v'aid спrrепtТimеТЬо:l.э~triРМ~п\)J·te;m._Моuвeiliоvеr (.ob}e:c t sender,


EJjen tArg::> е)
I tооlstripS ,tаtш.Lа,ЬеlМeriшst<Jtе . Text
= "О~Qбраже-ыие Т,еК;tщегр вр€м~ни." i

Итак. у :вас есть оБRО1mеШIЫЙ npoe.ll1T )щя тестового З2шуСRa. Теперь при выборс:
rtyc1кТО1l м«no ВЫ ДОJIЖНЬj: видеть в первой пзвели элемента Statu$S:trip соответ­
ствующие строки с ПОЯСНН10щей щнfJOрмациеii.

Состояние готовности
НанонеЦ. нужно гарантировать. ""(То цр}! снятии указателя мыши с DyfIRТa мещо
:ruшъэова.телем в первой теиетовой Щ\Н~ не останется -старая" ПОДСRaэJta. а. б);дет
отображено некоторое МТIшовое" сообще,fIИе (:вanp:имер: "Ожидание действий пощ..-

1
792 4асть rv. Программирование с помощью библиотек .NET

зователя"). 8 текущем своем виде наше приложение оставит в строке текст, соответ­
ствующий ранее выбранному пункту меню. что может вызывать. по меньшей мере.
недоумение пользователя. Чтобы исправить это. обработайте событие MouseLeave
для элементов меню Выход. О программе. День недели и Текущее время. Но вместо
генерирования нового обработчика события для каждого элемента. позвольте всем
указанным элементам выэьшать одШi метод с именем SetReadyPrompt () .
private void SetReadyPrompt(object sender, EventArgs е)
{ toolStripStatusLabelMenuState.Text
= "Ожидание действий пользователя."; }

в результате вы должны обнаружить. что первая панель возврашается к ото­


бражению этого типового сообщения. как только курсор мыши покидает пределы
любого из четырех укаэанных выше элементов меню .

ИСХОДНЫЙ КОД. Проект Status8arApp размещен в подкаталоге, соответствующем главе 19.

Работа с ToolStrip
ТИп ToolStrip В .NEТ2.0 предлагается использовать вместо типа ToolBar.
предлагавшегося в рамках .NEТ 1.X и теперь считающегося устаревшим. Вы эна­
ете. что панели инструментов обычно обеспечивают альтернативный способ ак­
тивизации соответствующих ПУНКТОВ меню. При щелчке пользователя на кнопке
Сохранить. результат будет тем же. что и при выборе Файл~ Сохранить из меню.
Подобно MenuStrip и StatusStrip. тип ToolStrip может содержать множество
разных элементов панели инструментов [возможности использования некоторых
из них вы уже видели в предыдущих примерах).

• ToolStripButton
• ToolStripLabel
• ToolStripSplitButton
• ToolStripDropDownButton
• ToolStripSeparator
• ToolStripComboB o x
• ToolStripTextBox
• ToolStripProgressBar
Подобно другим элементам управления Windows Forms. ToolStrip поддер­
живает встроенный реДаЕТОР. который позволяет быстро добавить стандарт­
ные типы кнопок (File. Exit. Сору. Paste и т.д.), изменить поведение стыковки и
встроить To o lStrip в Too lStripC o ntainer (подробнее об этом чуть позже).
Возможности поддержки ToolStrip в режиме проектирования демонстрируются
на рис. 19.20.

L
Глt\gз 19, Соэдание ОКОtl с hoмощыQ System.,WlndG\ilS,Fo.rm.s 793

Рис. 19.20. ВОi3J'JОЖНОGТИ режима проеКТJllроваt1ИR ДЛЯ Tool$trip


ПодобllO М е n u S t ri р и 5t а t и:8 S t ri р, ИНДИlJидуальные элементы управле·
ЩШ To 'o :).$trlp дОбавляютсЯ вО ВНУТР6ННЮЮ ItОЛЛeRЦИiO TbolStrip с НОМО "
ЩЬЮ CBOji,CTBCj.' Item:,s lэлементы). Если щеЛКВУl'Ь на ССЫЛJ(е IпэеЛ Standard Items
(Вставить стандартные елемеl-J;,r~) ВСтРоенного редактора Too1str.ip., то в метод
I.пitiа;Lif:еСоmpощедt () буд~т добавлен Массив ПРОИЗВОЦJ{ЫX otT.o-оlSt:riр1tem
типов, предСТa:J:ЩШOЩИХ С'QOтветствующие элементы,

private vaid lпitiаli'ZеСОпiрспепt (}


,(

11 Ааll.'ои,атичесхи. re'~Jd upоХ'рiUOllDlЙ жоJt


11 Д.ЦtI подЖ'о<rО!l!IЩ T'o olSt;:ip,
tM.s. 'tGolStripl. Items , ,l!;ddRапgе (new
Sуstеrn. ,:WiщJоwз ,Fo'r ms . ТооlЭt: riрltеm []
I ti'lis. nе w?о c:>lS',t ;ripButtOl'1, thi в. ореПТОСl$triрВ:рt t<:щ,
thi{; .sаvэТосl'S:triрвutt.оn, thls .printToolSt:tipB1lit.t011"
thiэ. tO'OlB't rip5epEirato'!", ~his. cutToolstr~pBbtton,
thiз• ,copyToD1S'lr ipEu tt.ol1, ийэ ~pa6teT0.o1 StripB lIt ton,
thi". toclstripS€paratorl, tbis. heip'I'oal-StriрВ'Uttо!l
)):

Чтобы nрод.еМОНСТРИРORатъ работу с Too.13trlp, в следующем цриложеlЩИ


Windows Fbrms созд.ается тип'!'ооl,striр. СQдержащий два, типа ToolStripButton
(с n:ме.нами toolstripB'\1't.top,Gr owFont и tооl$triРВ1.lttОnБЬr.:i,пkу.опt). T~
ToolBaI:Separator и ТХЩ ТооlБq;rТехtВо}( (с цмевем tC'>olSt r ipTex tBoKMessage).
Коне~nJЫЙ пользователь ПQ,'I)'Ч'зет 003М{)ЖfЮС'Х'Ь ввести СQOбщение, 1(оторое будет
отображено в окне формы с помощью ToolBal:TextBox. а два тида t:roolful rButto1iJ,
794 Часть IV. Программирование с помощью библиотек .NП

используются для того. чтобы увеличить или уменьшить размер шрифта . На


рис. 19.21 показано результирующее окно проекта. который мы с вами собираем­
ся построить.

Впечатляет?

Рис. 19.21. Приложение ToolStripApp в действии

я надеюсь, что к этому моменту вы имеете достаточно информации о приемах


работы в режиме проектирования формы в Visual Studio 2005. поэтому я не буду
утомлять вас YImзаниями по поводу построения ToolStrip. Однако следует заме­
тить. что каждый элемент ToolStripButton имеет свою пользовательскую (хотя и
достаточно примитивную) пиктограмму. которая была создана с помощью редак­
тора изображений Visual Studlo 2005. Если вы захотите создать файл изображения
для своего npоекта, можете просто выбрать Project~Add New Item из меню, а затем
в появившемся диалоговом окне выбрать Icon File (Файл пиктограммы). рис. 19.22.

_ ~~tudic> ~~ templilt6
",-"":

~ ~:
"
~ ~ .~
-G::.'1
-"i~~
i j!f ." .~
-<=
-~
1\1
"
...
а int.eroo CodI! "* Windv= u.... Cйntrol CuslDm ~ited !nherited
f<><m Conlrol Form u....Cootr'"

~ ~ LJ Jj ~~:
~
~ .. t~", у}
.,.,'. tIJ
w ob Custom Componen! SQlo..~ D.~t xr-LFJe XМL Sd1<!n1. XSlT Fi~ КТМI..Page
COl1trol d.oss

Г-- ~--.

!_
_ __.-----____.__.__ _ ___ __ ------.0-.----
-
growt=ont,1C1)

---- -- ---.-.--~ . ~.

-:""'" _~ ~
~ - .-- - . -.. -.- - - - - - -·-1
...!

дdd 11 CarиI J

Рис. 19.22. Вставка новых файлов изображений

L
Гла811 19. СоздаНl'Н~ (УКОН t ПОМОЩЬЮ 5ystem.WindDw,s.Forms 795
После этого ~Ы может~ отреДaRТИроватъ с-вои изображения с помощью окна
Colors и -комплекта И8струм;еuтав реДактОра изображений. Имен ПИEroграммы Б
.IfВ1JИЧШd, Щ>J; мощете Сi'fязаn; :Щ~. С типами ToolStripВuttQfJ с помощью СВОЙС'I'ва
Image в ОIЩе своЙств. J1Ш:JIе того nаБ вы сочтете внешний ВИД 'TaoLS,t rip удовле1'ВО­
p~, обработa:i::vе сОбытие Clie;k для :каждОI'оэлемeIiТа ТОQrStriРВllttоп.
~o,.. СООТВIn'G'П'УЮЩI<Щ проrрaммньiЙ КОДJ<d€тода lпitiа li-zеСоmропепt () ДЛЯ
первого ТИIl::,I -тооlS11riрВuttоп (ВТ9рбй T001St.ripButto!\ вы:rnндиr почти та1-С же).

pJii va Ье voi'd I 'n i tial i zceCoJI1pone'D t ()


!

11
11 toolStripВuttonGrovFont
11
this . to.olS-t.riрВuttопGrоw!"QпJ:: . -Di$рlауStуlе
=
$yst'sm. W~п-д.ОW'З . f'сэrm;? 'Теоlst rip:L tеПlDisр1 a'yS't yl.e .Ша че;
this.too-1StrlрВutt'опGrоwFоrlt. Irna'-'Je =
( (system.Drawing. IJТ\$;ge)
(т~з(')итсе5. GetQbjeot. ("loоlSt:riрВuttQлGrоwFопt. Irпаgе") )) ;
thi s. tool StripButtanGrowFont. Lmаg,еТrаI1sраrелtсоlоr =
Sy3t6!11. Dr<J,wing. CO!J.OT _. Иаg;еn,tа;
U\i s .. tool S txipBut tQllGl'Q.wF~m t . Name = "too1St.ripBut tопGГQwFоn-t";
this. tooIStripButto nGro.wFont . Text = "·to o lS·tripBt1t to.n2" ,
this-. t,yolStYipButt~nGrow'F~')'n1i: . lL'ool 'ripTex-t = "Увеличить щриф'l'";
·this . .tQolstri-рВutt 9п.Gr0w F~ пt. Click t= new
S-y.stem. EventBandler (thiS . toolstripB~ttonGrowFcm.t_t l i сх);

замеч.анме. Обр.атите внимаitие на ТО,. что значеf1ие, прИСдаиваеl.40е свойстау Ttr.age типа
Tool$t rlpBut ton, ПОЛУ'-lается с помощ~ю метода GetObject (). Как будет ПОКЦЗЭIiО в сле·
дующей главе" этот метод исполЬ'Зуетс~ для' извлечеЮо1Я BCТPOetl~biX -ресурсов, ИСПОllьзуемых
КОмПОНОВОЧI1ЫМ блOlЮМ _

ОСТ3-ЛЫЮЙ DJ10Fраммный код..-резвычайно П.р'ЮТ. В следующем оБНОВJIенном


м.aiпWi.ndоw обратите внимание на то. что текущий размер шрифта огра:юrчизaет­
ел энаЧtlliИя:МИ.из ди.ша;ЮI-fа мёJiЩV' 12 и 70.
publit: partial class ~airiWlnd0w : l"OJ:'l11
j
11 Te~, ~IDIЙ' 1( ~ 1?&.sмap1ol lI!Pи~а.
i nt o-ur:t FоntSizе = 12 ;
СQпst lпt Mi:nf'o:ntSize = 12,
COHst iht Ма::ХFоп'tЭ1ozе, = "70 ;
рооЕ с ti,ai nWlndow { )
I
IпitiаlizеСоmр"щеmt О;
'C€D terTciS creen () ;
Tex-t. = st r in-g . Forma t (ТfI3ыбр:анный .вами -размер шриф та: t {I ) " ,
cUt:rFontSi ze') j

I
796 Часть IV. Программирование с помощью библиотек .NET

private void toolStripButtonShrinkFont_Click(object sender,


EventArgs е)

/ / Уменьшение pa!SКepa ШРИф'.1'а на 5 и оБНOJI.nение з~рана.


currFontSi~e - = 5;
if (currFontSize <= MinFontSize)
currFontSize = MinFontSize;
Text = string. Format ("Выбранный вами размер шрифта: {О}",
currFontSize) ;
1 nval ida te () ;

private void toolStripButtonGrowFont_Click(object sender,


EventArgs е)

/ / Уве.nичение размера ШРИф'.1'а на 5 и обнов.nение э~ана.


currFontSize += 5;
if (currFontSize >= MaxFontSize)
currFontSize = MaxFontSize;
Text = string. Format ("Выбранный вами размер шрифта: {О}",
currFontSize);
Inval ida te () ;

private void мainWindow_Paint(object sender, PaintEventArgs е)


{
/ / О'.1'ображение сообщеНИJI поm.зова'.1'еn •.
Graphics 9 = e.Graphics;
g.DrawString(toolStripTextBoxMessage.Text,
new Font ("Times New Roman", currFontSize),
Brushes.B1ack, 10, 60);

в качестве заключительного штриха. чтобы гарантировать. что пользователь­


ское сообщение будет обновлено. как только ToolStripTextBox утратит фокус. об­
работайте событие LostFocus и ВЫЗ0вите Invalidate () для формы в рамках сге­
нерированного обработчика события.

p~blic partial с1азз MainWindow Form


{

public MainWindow ()
(

this.too1StripTextBoxMessage.LostFocus
+= new ЕvепtНапd1еr(tОО1StriрТехtВохМезsаgе_LоstFосus);

v oid too1StripTextBoxMessage_LostFocus(object sender, EventArgs е)


{
1 nvalidate () ;
Глава 19. еОI3ДQние DKOti ·с l:IомощьюSу~tеm.Wiпdоws. Fprms 797

Работа с ToolStr]pContainer
типы ToolStrip, если требуется. можно Н'астроитъ так, чтобы оliИ МОГЛИ ~cты­
коваться
W

с любой CTOPOнoji ~ ДерНе со всеми сторонами содержащей их ФОРМЫ.


Для цлдюстрации TorO,. ~iШ, это ~елать, ще.1ШВИте npавой кнопкой мыши на
с»оем элементе ToolStrip в 'схкне npоектирования фDРМЫ :и выберите Embed in
TbolStripContainer (Встроить в контейНер), ПОCJiе этого ToolSt.Ti'P будe'I' помещен
JI ToolStripContainer. Дл.fi Щ1шеrопримера .выберите опцию Dock FiIIln Form
(Стьщоmt6 1(0 всей форме) рис. 19.2З.

.
,_ _ ..' _ _ _ J

Рис. 19.23. СТЫJЮвка тооl.striрС\~i1t а iпеr 1(0 !3сей форме

Напустив эту МQДИфJJЩlЦИЮ приложения, вы увидите. что ToolS'tr ip может


перемещаться и стыковаться к дщбой стероне кi:щте1Цiера. ОдпaI\О вате пользова­
тельское сообщение 1щчезнет. Причина в том, ЧТО ' Т}IЦЫ Тоо1 St:!ii pCo!l'talner' ЯВJIЯ­
ютсн ДЛЯ формы доЧЕРНи.мu эл€мен.mCLAoЩ упрUВJIeНця. ТаК 'ЧТо I'рафичеrnое ото6ра­
:lRение на самом деле ВJmIOлняется. ВС) B.blВOД CЩJЫТ }{онтеЙНером. который сейчас
НюqJъшаf':l' ВСЮ щureнТGкyto область формы.
Чтобы решить возникшую проБJIе'~, :еуждо обработать событие J!'aint ДЛЯ
T.o.o lS:tripCOl)tainer. р. це ДJIЯ формы. Сначaщt нах1дите событие PaiDt формы в
окне свойств и ЩеЛКJ-ште право:й кнощфй If<:l текущем обрабОТТ:IИКе событий. Из
IЮНТБКcrноГО меню въrбqmте Reset (рис. 19.~4).
Это удадш' програ:М:МIiУЮ логикуобрабо1ГJ(И фfiыти.я ив Inltial.1 zeC()mponent (.).
но оставит nporpaммy обработки ~обытця на своем месте (просто, чтобы не доny-­
(:T.rrI> бе3ВQ3)!JJатную потерю npoгрaмюrого кода', RОТopblЙ ВЫ.' возможно .. 'эзхoтиre
испOJrъэoв~п.).
Теперь ФОработ.щте соб~Е:е Paint для Tool,s tr i-рСолtаirlеr и переместите
имеющийся прогрro.ц.mый' .кОД отображения из обраб(УГчш<э события Paint фQР­
мы В обработqщ{ собьrrия Paint контейнера. После ЭТОГО' МDжr:rе удалить (теперь
nYl':Той) метод Main'Wind0w_ Pa,in t ( ) .

1
798 Часть IV. Программирова~lие с помощью библиотек .NEТ
r
РrОр"rhб ' rgJ

Рис. 19.24. Переустановка события

Наконец. нужно заменить каждый встречающийся вызОв метода Invalidate ()


формы на вызов метода Invalidate () контейнера. Вот как выглядит соответству­
ющая этому модификация программногО кода.

public partial class MainWindow : form


(

void toolStripTextBoxМessage LostFocus(object sender, EventArgs е)


(
toolStripContainerl.Invalidate(true) ;

prlvate void toolStripButtonShrinkFont Click(object sender,


EventArgs е)

toolStripContainerl.Invalidate(true) ;

private void toolStripButtonGrowFont_Click(object sender,


EventArgs е)

toolstripContainerl.Invalidate(true) ;
}
/ / Теперь ".!Iахрa.mивае'.l!сtl" JCОН'.I!еЙнер, а не форма!
private void ContentPanel_paint(object sender, PaintEventArgs е)
{
Graphics g = e.Graphics;
g.DrawString(toolStripTextBoxMessage.Text,
new Font ("Times New Roman", currFontSize),
Brushes.Black, 10, 60):

Конечно. следует провер:ить разные конфигурации ToolStripContainer, чтобы


понять, как все это работает. ПОдРобности по документации .NEТ Framework 2.0
SDKBaм придется изучать самостоятельно. На рис. 19.25 показано окно завершен­
ного проекта.

Исходный код. Проект ToolStripApp размещен в подкаталоге, соответствующем таве 19.


Глава 19. С9здзние ОКОН!: ПО.I.IDI,I.IЫО Sуstеm .Wi щfОW5 . Fогms 799

ОЧень интересно,...

Рис:.19.25. ПРИ1Юж:ение ToalStripAppc допускающим C1:bl!i:OBКV тоtйstriр

Создание МD1-приложения
Чтобы заверпштъ крат:кое3I::IЩtомС1;'ВО с Windows FbП118. ·да.вЗЙТе обсудим ТО. как
настроить форму на работу в K8~recTBe родителъCJWI'О об1.екта ДШi ..любого числа
ДDчерJiИX O~OH (т.е, В дачес.т~е MDI-крнтеЩiера). МDl~прИлCiжеНШl дают nOlIЬЗ0ва~
телям .возможность oткpwвaтъ. множ~ство дочерi:mx OROH в рамках однorо и ТОТО же

главВЩV окна. В ~e МDI цаж;цое оюю nредстШJJlЯе'Т свои ·дoкyмeнт~ прило.жения.


Например. Yi.stta] Stud1b 2005.RВ.Щ'tt''ГСЯ MDl-приложен.ием • .поскольку !iы можете
OTI(PblТb :множество докум:«;нтов в PilМRaX адного экзe:мr:tшipа этого IIp:иложения.
При построеюm MDI-ПРИЛQже:н;ия с помощью W1ndo:ws F't:>rms neрвой задачей
оказывается (конеЧ!IlO же) С{)ЗДaIше HOВOro прилож.енин WindDws. И:схрдная форма
fJ приложен:ии обычно содерi$ИТ систему:меню. 'КОТорая позволяет·создавать новые
ДО1(уМе:вты (mщp~е~. содеР]jtИТ IIУНFП Фай~СО3Аать) и упорядочивать существу­
ющие оТJtрютые одна rн:аGJЩДiэМ. по :вертикали ИлиllО горизонтали).
для созданиЯ' дочерн;их OKI!IH обычно оыреде;ляется. фОсрМа-npототИ1I, итрающая
роЭIЬ О'СНоВьс дз:ш I{ЩКДОГО дочернего окна. Лосколъку РОУro явл.нется типом кла:сё.а.
любще цриватные ЩII;IНЫ~\ опред~л.енные в дочерней форме. буДУТ ДЛЯ kОНКретно­
I'ОЭ~iJеМ!7.IЩра yшnroщ.~. Например. при создашш MDI-ПРШIOжения TeRCТOBO-
1'0 pe-Аall'1Vра ДJlЯ споОр~еmш 'ffiKcTa МОЖНО создать дочернюю форму, поддержи­
вающую St;ri.ngBuild,e r. Если по:mrзователъ создаст п6ТЪ новых дочерних окон.
кaJЮ(aП' срответствующая форма будет поддерживать .своЙ собственный экземпляр
striпgВ,LJi lder. fWТОры:ми можно буд~ управлять независимо.
Kp01Vle того., МDI-пpmюшения позвоJiJПoт "объеДИRf.ГП/' меню. 1Сак уже упомина­
дось, РОД1:iтедьские ОRИН 06:ычRо имеют свои системы меfiIO. которые позволяют
ПОЛЪЗОDатeJp9 СОЗДq.БaТЬ и упорядочивать дополпителъные дочерние онна. НО ЧТQ
будет Б том случае, когда дочернее t:>ЮlО имеет свою систему меню? Если noлыю­
Ba~J1b максимиэирует lшн:кретное до-чернее окно, то система меню этого дочер­

пето ощш должна WпomоТитье.яW родительскОй формой. чтобы польэов.ате.ilЬ полу­


чил ВO~O~OCТЪ анпmизироватъ элемеllТЫ каждоЙ из имeIOlЦИXCЯ систем меню.
nРОРРЩХGПЮ имен Windows Forms определяет ряд своЙств. метОДОВ и событий.
ПОЗВОJД[lG.ЩI'iX Пр0гр~ое слиннИе систем мев.:ю, Имеется таюке система W сдИя -
нин 119 УМОЛчаниЮ". которая оказъmается DIID.лВе подходящей во мноrиx ТИПИЧИЫХ
~faIJX.
800 Часть IV, Программирование с помощью библиотек ,NET

Создание родительской формы


Для демонстрации основ процесса построения МDI-приложения создайте но­
вое приложение Windows, назвCUI его SirnpleMdiApp. При этом почти вся МDI-ин­
ФраСТРУК1Ура может быть назначена исходной форме с помощью различных ин­
струментов проектирования. Сначала найдите свойство IsMdiContainer в окне
свойств и установите его равным true (истина). В результате в окне проектиро­
вания формы область клиента изменится - теперь она будет визуально представ­
лять контейнер дочернего окна.
Затем разместите в главной форме новый элемент управления MenuStrip.
В зтом меню укажите три элемента высшего уровня с названиями Файл, Окно и
Упорядочить окна. Меню Файл содержит два подчиненных элемента с названиями
Создать и Выход, Меню Окно не содержит НИRaRИХ подчиненных элементов, по­
тому что при создании пользователем дополнительных дочерних окон новые эле­

менты предполагается добавлять программно. Наконец, меню Упорядочить окна


определяет три подчиненных элемента с названиями Каскадом, По вертикали и
По горизонтали,
После создания меню пользовательского интерфейса обработайте событие
Click для пунктов меню Выход, Создать. Каскадом. ПО вертикали и По горизонтали
(напомним, что меню Окно пока что не имеет никаких подчиненных элементов).
Обработчик элемента Файл~ Создать мы реализуем в следующем разделе главы, а
сейчас рассмотрим прогрaммный код для остальных элементов меню.

/ / Обр4бО'1'ка собbl'1'Иll фAЙJJ I ВЫХод и УПОРlIДочевие дочерних ОХОН.


private void cascadeToolStripMenultem_Click(object sender,
EventArgs е)

LayoutMdi(МdiLayout.Cascade};

private void verticalToolStripMenultem Click(object sender,


EventArgs е)

LayoutMdi(МdiLayout.TileVertical);

private void horizontalToolStripMenultem_Click(object sender,


EventArgs е)

LayoutMdi(MdiLayout.TileHorizontal) ;

private void exitToolStripMenultem_Click(object sender,


.E;ventArgs е)

Application.Exit();

Наибольший интерес здесь представляет использование метода LayoutMdi () И


соответствующего перечня MdiLayout. Программный код обработки выбора каж­
дого из элементов меню должен быть вам понятен. При выборе элемента пользо­
вателем вы даете указание родительской форме ВЬШОЛRИть автоматическое раз­
мещение всех дочерних окон ,
Гл,ава 19, СОЗДЗflL1е O~OI1 (J ПОМQщьюSystвm'.Wlпdоws.Fогms 801
Перt"Д тем Ка.к пе.реЙ'l1l{ К обсуждr:-нию процесеа создания дочерних форм, уста­
новите еще одно СВОЙСDЮ Me.nuS,t r:ip. Свойство MdiWindqwLifiOtlt,ero ИGDользуетс$.[
дм тoro. чтобы выяm.nь•.. 1taRЬй nyтtт меню наивысшего )'РОВНЯ должен иСпользо·
ваться для автоматического CIIИсна имен всех дочерних ЩШbl при {:оответствующем

выборе из МeRЮ. I1ри-свойте зн.нЧен:ие ЭТОго СЩlЙства cmeby-перемеiпlOЙ winrckw-


l'c>,~)lS.tx;i,pNenultern. По умciJIЧ:Ю-lМ1О для Э.ТОГО слисrщ и,спо:льоуется З1illчение доч:ер·
него свойства Tex:t с ЧИСЛОВЫМ суффи~сом (т.е. Fo-rm.l, Fo-rm2, Fо-rmЗ й т.д.)_

Создание доqерней формы


Теперь, ногда -у вас есть оболо'Ща MDI-контеЙНе,ра. ~HO сЬадать ДОI!ол.НИ'iет.­
ную фОр:му. ВЪШОЛНIП01'ЦуЮ роль upототщха ДiIЯ данного дочернего tлmа. Начните со
вставки новorо nша FDrm В имеющийся проект (исподъз}1Йre PrDject9AdC'.l Wifldbws
Forh1). присво'Йте зтому типу имя "Ch ildP-rоtоt ур еFоrm 11 обработайте для него со·
Быиее Click. В сгенерированном обрабртчдке событЩI путем GЛyЧaйRoго выбора
устaнhDите цвет фона для. области ЮIИента, Кроме трго, -Выведите ~rrpeобраЭО:8ан­
ЛЬе в строъ,"У" значение Со} or (llJJeт) нового объек;га в полосу заголовка доч.ернеrо
ORНa. СледУЮЩая прeлpт.tмная Л(tГlШ3 реадиwет цоставленнън: задачи.

privat~ void ChildPro-t,оtуреFОЩI_(Н. с k (oJ:.ject sепdеr , EV€J!')"t1\,y gs е)


1
11 Пo.пyqeиие ~X СJI}I"ЧaЙIQoIX 'rЩсiВ,n.
i лt т, g, Ь;
F,апdоП\ !сап =о ne w Ranосш () i
1:' = rёщ.t:r~'t (0,255);
!l = r~Л.Nехt (О, 25 5) ;
Ь = r ан. !\Jext {[) , 255);
/I СОЗДiUlИе цв.е'1'ОВОГО значеНИR дnSl фОJl".
Color ~urrСоl ·OJ:' = C",lGJr,. F rGmArgb (r, g, Ь);
this.BackCdlor = currColer;
thi g. Text = c .u.r x-Co10r . To-St r 1л g ();

Создание дочерних окоН


3акшочителъныM шarом ;:J,о'n}ЮЩ б~ть созд~ние по~одяще'й реalIИЗации обра·
ботэика событий Фай"~ Соз'Дать рощпеllЪСЦОЙ ФОРМЫ. Теперь, :кorда дочерняя фор·
ма апредедена. соответствующая программдая логика оказывается очень простой:
нужно ооздать и отобразИ':rЬ .аовый Э1(земпляр ТИJ1(i сtli ldРr оtоtуреFолn . .Кроме
ТОГО, нуЖно установить значение свойс~э, МdjР~ rепt дочерыеii ФОРlVIЫ. ут<ааыВ3-
ющее на содержащую ее форМу (В данном случае ;это ваше главное оюю). ВОТ ШiК
дrommы в:ыг.лядеть СDотвerСТвующие МDДИфИRaIЩИ ПРОГРaм1lJЪL

ptl'lale v o.icJ !l'E;'~'J;p ol"l!·i'pMel1 11r·teJТt...C l ic 'k ( ~~bject. sеГ',j€,!, :К\J8лlАrg$' е)


[
11 Создание HOBO~O дочерве~о o~a.
СhildРrоtоtуреFопТl пе wСhН d = !!I:w ChiJdP.!:o t ot.ype}·o :tJ1L() I
/ / Cct.I.nIt8: на ро.циwелъС:Хyиl форму дnя: да:КН0:tOо ,цо .. еряero охи ...
nеw:СЬ.ild . ~ИiPцерt = tbls;
11 О!t'о6ражение всшои фОрю.t.
иЕ ·II! Сhi l ,j . :1ihox'l' ~! ;
802 Часть IV. Программирование с помощью библиотек .NET
r
Замечание. Дочерняя форма имеет возможность использовать свойство MdiPa rent непосред­
ственно, когда требуется выполнить какие-то действия (или ОРI'анизовать сообщение) с роди­
тельским окном.

При тестировании этого приложения начните с создадия нескольких дочер­


них окон и. щелкнув на каждом из них. создайте уникальные цвета для их фона.
Если теперь рассмотреть подчиненные элементы меню Окно, вы должны обнару­
жить. что там представлена и учтена каждая дочерняя форма. Точно так же с по­
мощью элементов меню Упорядочить окна вы можете дать указание родительской
форме разместить дочерние формы по вертикали. горизонтали или каскадом, На
рис . 19.26 показано окно готового приложения.

Рис. 19.26. Окно МDI-приложения

Исходный КОД. Проек-r SimpleMdlApp размещен в подкаталоге, соответствующем главе 19.

Резюме
Эта глава рассказывает об основах построения графического интерфейса с по­
мощью типов. содержаш,ихся в пространстве имен System,Windows.Forms. Сначала
вам предлагается создать НеСКОлько приложений вручную. и В процессе этого вы­
ясняется, что GUI-приложение. как минимум. должно иметь класс, производный
от Farm, и метод Main (). вызывающий Application.Run (),
в этой главе показано. KaI, строить меню верхнего уровня (а также всплываю­
щие меню) и как обрабатывать события меню. Было также выяснено. как можно
расширить фyнRциональные возможности типа Form с помощью панелей инстру­
ментов и строк состояния. В .NEТ 2.0 при создании таких элементов пользователь­
ского интерфейса предлагается использовать MenuStrip. ToolStrip и StatusStrip.
а не типы MainMenu. ToolBar и StatusBar из . NEТ l.x (хотя эти. уже устаревuше
типы. тоже поддерживаются). В завершение главы бьmо продемонстрировано. как
с ПОМОЩЬЮ средств Windows Fопns можно создавать МDI-приложения.
rЛАВА 2'0
Визуализация
графических данных
средствами GDI+

П реДJ>1.цуЩая -глава npеДJtагала вводное описание :цроцеес'а лретроеш-m сш­


приложений с ПОмОЩhЮ Sуstе!П' _WindОWS_F.Gi rms . Целью этой тавы яв,ллет­
ел рассмотрение возможностей визуализации графическmt даяцых в окце фОрМЬr
(включая КеШ. вывод Изоб.ражениЙ. тmt 11 вьтод текста РЭЗ1IИ"IНЫМ}J ст.и;лями)_ M»l
начнем с О()Ще.го рассмотрения прос·транств имен. QВ8ЗЮДПЯ,Х с .выв~щом графиче­
ских данных. родИ события Fa.iht и ·всемоrущего·· объе:кта 'Graphics ,
Остальная. чаСТъ. этой главы будет поt1ШЩена способам МaI!ШI)-.mщии цветШvnl.
шрифтами., геометричесltими формшш и графическими образами_ 8 этой ГJщве
также исследуется ряд проrраммных подходов. связанных е визуализацией гра.­
фических данных. -н:апрамер. ТaIШХ как проверка ДОС1Упа пользователя .н непp.l[.
мо)тольным областнм Эltрана. ЛОТИJ{а перетасюшэяи.IJ объектов и фQрмат ресурсов
.NEТ. и ХОТя. ctI1pого говоря. по не о:гнооится к GDI+ аецосредствeJЩО. при опера­
цйях с ресурсами нередко прихОДите..'1 исполъЗоБill';Ь маниnyля:щщ графическими
данными (что оказывается цостатоЧlIO важНbIМ ДIЩ того , чтоб~ представить соот­
:веi'С'tВУЮЩий .материал здесь).

Звмечание. Если вы профаммируете АЛЯ Web. то ме-*ете ПОДУМЗiЬ. что техно1ЮГVI'И 601+ вам не
ПРИГQДIiIТС;Я, ОднаКli! на самом деn6 эти техНОЛОГИИ' не ограНИLIИВаютi:;Я талыш тр<ЩIЩИQННЫМ'И
приложеf.tИ>ТМИ - они оказЬ!ваеiСЯ' исклюЧитеЛЫ:i.Q Ва>I<НЫМИ и ДЛ~ Web-при.nожен",~.

Обзор пространств имен GDI+


Пдатформа .NE'Т' обеспечивает цеJ1ьiЙ ваБQР пространств имен для поддерЖl<И
ВИЗУfЩИВ8ЦИИ двумерв:ой графики. В ДQШ)дНе1ПJе к ОСНОВНЫМ фYIпщионаЛЪНbIМ воз­
М~UlOf~ТЯМ рнзраБI'JТ<mRа. :которые обычно прeдmiТ.а1оТСЯ !'раф1fЧeскими J1шre:r'вми
(цвета ,. шрифты. перьЯ, кист.и и т.Д, ). вы также найдете ТИПЫ. О Существл.moщ;ие
геометрические траноформацаи. сrnaживание, смешивмше ШIЛlПр и печать доку­
ментов. Вместе эти простран<..'ТВа .имен формируют тот набор :возможностей .NEТ.
который "мы панываем GDI+ (Graphics De\"lce Interfat.e '- интерфейс rраф:йчее:ких
r
I
804 Часть IV. Программирование с помощью библиотек .NEТ

устройств, интерфейс GDI) и который является управляемой альтернативой Win32


GDI АР! (Application Programm1ng Interface - программный интерфейс приложения).
В табл. 20.1 преДJIагаются общие описания базовых пространств имен GDI+.

Таблица 20.1. Базовые пространства имен GDI+

Пространство имен Описание

System.Drawing Базовое пространство имен GDI+, определяющее множество


типов для основных операций визуализации (шрифты, перья,
основные кисти и т.д.), а также "всемогущий" тип Graphics
System.Drawing.Drawing2D Предлагает типы, используемые для более сложной двумер'
ной/векторной графики (градиентные кисти, стили концов ЛИ·
ний для перьев, геометрические трансформации И т.д.)

System. Dr-awing. Imaging Предлагает типы, обеспечивающие обработку графических


изображений (изменение палитры, извлечение метаданных
изображения, работа с метафайлами и т.д.)

System.Drawing.Printing Предлагает типы, обеспечивающие отОбражение графИки


На печатной странице, непосредственное взаимодействие с
принтером и определение полного формата задания печати

System.Drawing.Text Дает возможность управлять коллекциями шрифтов

Замечание. Все пространства именGDI+ определены в компоновочном блоке System.Drawing.


dll. Многие типы проектов
Visual StLldio 2005 устанавливают ссылку на эту библиотеку про·
граммного кода автоматически, но вы можете при необходимости сослаться на S ys tem.
Drawing.dll вручную, используя диалоговое окно Add References (Добавление ссылок).

Обзор пространства имен System. Drawing


Большинство типов. которые вам придется использовать при создании GDI-при­
ложений. содержится в пространстве имен System.Drawing. I\.aI( и следует шь..и­
дать. здесь есть классы. представляющие изображения. I<ИСТИ. перья и шрифты.
Кроме того. System.Drawing определяет ряд связанных утилитарных типов. таких
дах Color (цвет), Роiлt (точда) и Rectangle (прямоугольнид). В табл. 20.2 предла­
гаются описанця НeIШТОРЫХ базовых ТИЦОВ этого пространства имен.

Утилитарные типы System.Drawing


Многие из методов визуализации, определенные объен:том System. Drawing.
Graphics, требуют указать позицию или область, в которой требуется отобразить
данный элемент. Например, метод DrawString () требует, чтобы BbI указали по­
зицию, В которой нужно отобразить текстовую строду в производном от Control
типе. Метод DrawString () является перегруженным. поэтому параметр позиции
можно указать как в виде координаты (х, у), так и в виде размеров "бокса", В дото­
ром нужно выполнить ВИЗУaJШЗадию. Другие методы GDI+ могут требовать, чтобы
вы указали ширину и высоту данного элемента или внутренние границы геометри­

ческого образа.

L
'Глева 20. $изувлизация графических даl'tl1ЫI< (;JЭ8дсrвамloI GD1+ 805
Таблица 20.2. базовые ТИf.1Ы пространства имен .sy stE:;In .DrC!wi о ч
Тип Оm4сзиие

Bi t i1J ар ТИП, инкаПСУЛЩJУЮЩИЙ даныые изобрэжеlЧИ~ ( ... bmp 'Или каког.о-,о другого)

S'': U SI1 Qбъекты BrtiSfI ИСПОПЬ3,~ЮТСЯ для запопнения внутренних оБЛ(1.(;тeii1 гp~
BH'J she s фичеСКИ)I; форм. Rапример, tаJ(их~ак ПРЯМОУГ()Л'Ы,JИКИ. эллипсы И MNOГO­
эоЦdВ i·U"Ь }!ГОЛh>Н~КИ
SY,5 t eJJ1.J3;r:u.s b~ O':
Te xtll:teBrus l'1
ВUffer9dGr aphics НОВЫЙ тип .NEТ 2.0. ~П5чиваКilщий графи ч еский буфер ДJПI ДВОЙНОЙ
буфер ~эацI1И. 1<оторая и"Споль~уется ДЛЯ уменьшения или полного ис­
клюЧенvrя влияния эффВК1<J. М6i1Ь1ЩНИЯ-. возн\,;каlOщеrо при перерисовке
изображений

Co1or Типы Coio.t И Sу st'8mrL~ь LQ r-э оnределяlOТ РАД статических свойств. до­
SystwlCole.rs ctym-ibIХ талько ДЛЯ Чтения и- исnо1l1,>зуемы x ЦЛR 110ЛУ'!ения НУ'жноrо цвета
при использовании рjjЗЛИЧНЬ~)( перьев и tщствй

Fo,n t Тип Ji'or-t инкanС'lМРУЕ1Т )!;"аракrеристики данного шрифта (li!Ювание.


F:олц' am.i 1з Пл'оfНDСТЬ, на'1ертание, размер и Т.Д,). Fon t Fami 1:/ пред-nагаsт абстр.ак­
Ц-il1j{J дrJЯ ГРУППЫ шрифтов, имеющих анапогичмый Дизайн. нО оn.ределе~­
Ные в ариации Сl'ИПSi

Grар luс э Представляет реa1lЫ-!уЮ nооеРХНОСТа наНесеНИЯ изобрзже~ИII •.8 та1ОК8


nр~длагает ряд методов дЛя ВI\,зуалиэiЩИИ' теКста. изОбражений и ' eOM€'-
тричеСl(l+Х wаБЛ0НОВ

I с:рn Представляют ПОЛЬЭoв<i'Гельские nИk1'-ограм м ы. а также 1-lабор craHAapTHblX


Sуst.еnllсоnз nl4i<ТOrpaMM. предлага-емых СИСтемой

lm",ge Тиl1 II'!I <;J;ge - это абстрактный оаЗОI!ЫЙ класс, fjеобходимый для nодцерж-
Im-а g еАnitпэtоr 1(11 фуI-Lкцион8лы-,ьIхx .80ЗМO.JКносrеИ типов Bit :map. I CDn И Со tsCir.
Тип I:maqеlilllШi9 Gor обеопечивает ВОЭМОЖIoJОСТЬ выол\;lени~'' цикла rlO
~Iабору Тl<lЛОЭ Тm а че из некртороFO заданного IIIнтервала

Р~ п i'e, Гos - ЗТО объеКТЫ. иопользуемые дл:я nостроениfТ :ГIИНиЙ J1 Кр,ИВБlХ .


P-еР.З Тип Реп определ я ет ряд GТати''iООКИХ свойств. ВQЗВРа.щвюших. мовЫи
S уз t аПlРе ,ls. объект Реп заданt'iаго цвета

po:± nt CTpYКТYPIli. npедс:rавnяющие отобраjj.,'tJние КООРДИIЩrы (,Х, у) EI ооотве,ству ­


P'Ji ri t F ющее целов з начени.в или значеlilИ€ Е плa.Eii!ющеЙ точкой. соответствен'НО

Rе сt ,;iп glе C'rpYKryPoI, представл~ЮЩl-1е размеР!:;1 nР~МОУГОЛbl-lика .(СГ10ва CI отобрiiЖSНИ­


Rect a'ngl e F ем -в Gootbetc-т:вуЮЩее ltg1l0e значенИе илl-f значение с ,плавающей 1\JчкоЙj
Si2: 6! CтPYKТYP~. Г1редст:а8J1\1ющие заданные ВрIСQТУ/lUИРИНУ (снО'ва ['; ото5рюкj;JНИ­
6i::zeF ем :в СОQ,ветствующее целое значооие или э начеliW\е с плаваюЩей ТQчкоi-i~

stri ngFOrmat ТИП. испольэуемыi1 для ИНlЩfТОVJlRЦИИ JЩЗ,JlИ Ч НЫ!! Х'арактерисtиJC размеще­
НИА' текста !В!;lра8НИВд,),{ие. ПРОМЭ-ЖУЧ<1-1 между строкаМИ и Т. Д.)

Тип . описывающи й гвометрический образ . окомпоноваНl'IblЙ ~IЗ пРямоу­


ГОЛ!:.-НИJ<О9 и траекторий
В06 Часть IV. Программирование с помощью библиотек .NEТ
r
дпя указания такой информации пространство имен S у s tem. Dra wing определя­
ет типы Point, Rectangle, Region и Size. Очевидно, что тип Point (точка) пред­
ставляет координату (х, у). ТИпы Rectangle (ПРЯМОУГОЛЬНИIi) содержат пару точек,
представлнющих левый верхний и нижний правый угол прямоугольной области.
Типы Size (размер) подобны Rectangle, но эта структура представляет конкрет­
ные размеры, используя длину и ШИРШ-IY. Наконец, типы Region (регион) предла·
гают способ представления непрямоугольных областей.
Члены-переменные, используемые типами Point, Rectangle, Region и Slze.
внутренне представлены целочисленными данными. Если вам потребуется бо­
лее ~тонкая" детализация, можете использовать, соответственно, типы Ро i n t Р,
RectangleF и SizeF, которые (I~aк вы можете догадаться) отображаются в соответ­
ствующие значения с плавающим разделителем. Но, независимо от внутреннего
представления, все эти типы имеют аналогичные множества членов, ВRЛючая ряд

перегруженных операторов.

Тип Point(F)
Первым утилитарным типом, о котором вам следует знать, является тип System.
Drawing.Point(F). В отличие от иллюстративных типов Point, создававшихся в
предыдуших главах, тип Point(F) GDI+ поддерживает целый ряд очень полезных
членов, вWnOчая следующие:

• +, -, = =, ! = - перегруженные варианты различных С#-операций;

• Х, у - обеспечивают доступ к соответствующим внутренним значениям (х. у)


типа point;
• 1 sEmpt У - возвращает true (истина), если х и у установлены равныIии О.

Дrrя иллюстрации работы с утилитарными типами GDI+ рассмотрите следую·


щее консольное приложение (названное UtilTypes), в котором используется тип
System.Drawing.Point (не забудьте установить ссылку на System.Drawing.dll).
usiпg System;
using system.Drawing;
патеЕрасе UtilTypes
{
public class PrDgram
(
static void Main(string[] args)
(
/ / Создание и смещение ТОЧJCИ.
Point pt = new Point(100, 72);
Console.WriteLine(pt) ;
pt.Offset(20, 20);
Console.WriteLine(pt);
1/ перегруженныe операции Point.
Point pt2 = pt;
if(pt == pt2)
WriteLine ("Точки одинаковы");
else
WritеLiле ("ТОL1КИ различны");

L
Глава 2а, Визуализаци.Я, грtJ.фическ.их деНfIЫil сред:ст~ами GDI-t 807
11 ИзиеиениеЗRa'lенив Х дmr pt2 ,
pt2.'X = 400.0;

11 OIrо~ражение ха.-.цоюо знач~ Х.


Cort$ole. Wr i leL.Ше' ('IПерсмя ТGЧХS 1 {О} 1, ,pt) ;
С6rrSQ'1.е.Wс:l't.еЬ:i.г.е (":Вторая ТРЧКil~' j{l } ", :pt2);
CO!'lt'",...,le • RеqшiГI'В () ;

Тип Rectangle(F)
Типы Rect а 09 le. пюдоБНt1 Poi nt, ОR:азъmаются полезными BU МНОГИХ приложе­
~ (и осо'бе.f'ПJО EI GШ-приложеШ'illX). Oдrrnм ин наиболее полезных методов типа
Rec't..CJ ng le юз.rщетсв метод
CODtairJs (') . Этот метод ПО3ВОЛНе7' ВЫНСНИ1Ъ, нwюдитс:Я
ли данный тип 1'oi пt или RectaI1g1 е в раМках границ не.шiтороrо дрyrwо 'Объекта.
Пшtже в этой же главе вы увидите. :как испОльзовать ЭТОТ метод длн цроверпи 00-
IlЦДa.ю,IЯ 13 область (iШ-изЬбрюкеНИй. А лоха что рассмотрите сдедУ1ощиИ простОЙ
ПРI~мер.

static y,.;>ij Main (st:tii1g[J аучз)


\

11 вяа.ча>Пе Poin t иахОди'1:'СR :вне np5Q«OY'I"~a.


Rе.сt·алglе rl = ITe'W Ree-tалglе(D, О, 100, 100) I
E'ol!1t. pt..J = ПЕ<W J?сiпt(lО}., 1(1).;
if(rl.Con~iцslpt3)1
Сопзоl-s. W,!:'i teLifJ.e ("'f\oi!J t наХ0ДJ!'ЛСЯ :вн.утри прямщ:rг.ОJ1ЬЮfJ'<1а J ") ;
€:;lse
'С()ь эоlе. Wri t.eLine ("Poiqt находится вне ПР'П10!у.г 1)J1$1щma! 1') ;

IJ Теперь помес~И!II Point s ПРЯИGYГОJlЪRИ1t.


рtз.Х = 5:0;
рt.З.У=3О;
Н (rl.Contains (rt3))
Сс}щ, о] е .WrlteL.ine i"E'Dint Н5,)l.t5д:ИТСЯ В:r11/Т:руг ДР;ЧМQугаrck.iЮI'if<д !");
el",E'
C:o:ill3Qle.·WYl tе.L..:iпе ('1J'1Qint нах.'ОllИТ:СЯ вне прямо.vгп.пЫ,ИТ<:/i"! ".);
COnSQle. Re",dI.iТL€: () ;

Класс Region
Т:щI 'f{egion представляет ВJIYТреннюю часть, геометричешюй ф.иrур:ы, С учетом
;ЭТОI'О становится ЯС1'Ю, п{)че1о~У KOHCTPYJtТfJpbI класса Rеgiол требуют, чтобы вы
предШ,:тави"тrи им: 6а В,ХОД иеJCОТОРЫИ y~e еущ~ствУ"НJЩИЙ геометричеСRfIЙ шаблон,
Лр(:"дположим. НЩlример, ЧТICt вы создади прямоушдьюш размером J 00>< 1 00 пик­
селей. Чтобlil получить досryп н в:нутренnей области пр.1:fМ:ОУГОЛЬНИКЭ. вы можете
H~CДТЬ следующее.
r

808 Часть 'У. Программирование с помощью библиотек .NET

11 Получение внутренней час'Х'И npяиоуrольииха.


Rectangle r = new Rectangle(O, О. 100, 100);
Region rgn = new Region(r);
Имея внутреннюю часть фигуры, вы можете манипулировать ею с использова­
нием различных членов, наподобие следующих:

• Complement () - изменяет данный объект Region на 'lЗсть указанного графи­


ческого объеRта, не пересекающуюся с данным объектом Region:
• Exclude () - изменяет даЮIЫЙ объеI<Т Region на ту его часть, которая не
пересекается с указанным графическим объектом;

• GetBounds () - возвращает Rectangle (F), который представляет прлмоу­


гольный регион. ограничивающий данный объеrп Region;
• Intersect () - изменяет данный объект Region на его пересечение с yr<аЗaR­
ным графическим объектом:

• Transform () -трансформирует данный объект Region с помощью указанно­


го объекта Ма t 1: i х ;

• Union () - изменяет данный объект Region аа его объединение с указанным


графическим объеIСТОМ;

• Translate () - сдвигает координаты данного объекта Region на указанную


величину.

Надеюсь. что вы получили общее представление об этих координатных при­


мит ив ах. Если же вам нужны подробности. обратитесь к ДOIсументации .NET
Framework 2.0 SDK

Замечание. Типы Size и SizeF заслуживают небольшого дополнительного комментария. Каждый


из этих типов определяет свойства Height (высота) и WidtJ-l (ширина), а также набор пере­
груженных операций.

Исходный КОД. Проект UtilTypes размещен в подкаталоге, соответствующем главе 20.

Класс Graphics
масс Sуstеm.Drаwiпg_Grарhiсs-это "вход" в функциональные возможности
визуализации GDI+. Этот класс не только представляет поверхность, на которой
вы хотите разместить изображение (например, поверхность формы, поверхность
элемента управления или область в памяти), но определяет также десятки членов,
Iшторые позволяют отображать тен.СТ. изображения (пиктограммы. точечные ри­
сунки и т.д.) И самые разные геометрические формы. Частичный список членов
данного класса представлен в табл. 20.3.
Кроме ряда методов визуализации. класс Graphics определяет дополнительные
члены, позволяющие конфигурировать ~состояние" объента Graphics. С помощью
присвоения подходящих значений свойствам, показанным в табл. 20.4. вы можете
изменить текущие харюпеРИСТИl\И процесса визуализации.

l
Глава 20. ВИЗ)'tJ.ЛИ..з:щия графи'lВСКИХ дзitны~ средствами GDI+ 809
Таблица 20.3. Члеl~Ы ~ecca Graphics
Методы OnиС3lf"е

FlI:.'оmНФ:: () СraтичеСk'ие МtiТОДЫ, обесJl~'U1в~ющие возмОЖНОСТЬ ПОЛУ4m-1ИЯ net\-


F:rom.FIW!'tr,.l () СТВIll1СЛЬНОГ() объектаG:орhi ев из AaHltoro ~зображеljl'1Я (например,
F:ro'mImag€= (j ПИКТDграммы,. ТО'feЧНаго ри.сунка и IД) ИЛIi'1 GUI-злеме~lТа

Cl~a;r () 3аТlОлняет обьект Gr:aph iCE; заданным цветом, ВЫПООНЯR в процеосе


за,nолн,е.НIIIЯ 6чиqтку поверхности рисования

о:r:з.wАf:,С (~ .э1~ методы используются ДJlЯ ВllзуализаЦИ\l1 ДЗННQ'ГQ изображе­


Ох аW, Вб!Zi.е.r О ния ИI1J,\ геомеl'Рl<lческого IoЩl!?ЛОНЭ, Позже вы увидите, что /!!eтOДbI
Dr:awEez,i e:rs () !):rз~Х () требуют иополЬЗовatiИЯ оБЬiJkТОВ Р'е']1- Gpr+
D,r.awCi,J rve (J
Dra_wE-J. J iР5'Э ()
-Оr.ёiwI со!'! ()
Dnawl,in~ ()
ra:a'.vL iлЕ Е .()
b:.rawP ie.,()
[)x,awPat [1 (}
DrаwRесtаТ!ф е ()
Draw'RectaBgles ()
D1::a,w:St_r ing ()
F±l l E:llipse О Эти Ме1ОДЫ ИdполЫ!-уют-с~ ДЛI1 запОлliеНIiIR внутреl1Н0СТИ даl1fiОЙ г'е­
Fll1Pa:cb (,) омеТРИ'-Lесroи формы_ Пьзже вы уШIIДIIIТе:. что ме10ДЫ D.rC!\>JXX:{ (.J
FillPie (.) треБУЮ1 ИйПQl1ьаования ()БЪ!,!1<1РВ Бr шs с) ЭОН-
.Fi 11 Ро}УЧ1J)'J ()
Fil1Rеюtаг..g} ;;" 1)

Та6Л:ИЦjl 20.4. Сilойства класса Graphics, оохраН!ilющие состояНие

С.воЙс"ГН Описание

,clip ПОЗВО.J:!\1ЮТ УС1ановить, ОПЦиИ oTCe'leJif1'R, использvемые с текущим


Сl iРВРlJпаs обье,l<том GtaphlCS
VisiblеЦ ipBo\in,d~
I.5C1ipEmpty
lsVisibblCl ip'Er;ipty
11.;!: алsfо:r m Позволяет трансформ~роват!, "Ml4pb!lbIe Ji;OopДJt1HQlbl" (п'одроонее об
ЭТОМ будет говорюьсi'1 позже)

PageUnit ПОЗ801.lЯЮТ указать на'ЩJ10 kООРДИНIlТ AI1f1 операции 8изуаJ1изации, а


Р' а, g--,;! э.t tJ. -Le 1аюке един'ИЦУ ~lзмереНИfl
DpiX
Dpi'f
Sпюоthiл gМоdе ПОЗВОЛЯЮТ задать парэмЕпры глМI<ОС"г\-1 геометРических t\бъектав j,\
Pi J!е,lО_fIsеtИоdе Te~CTa
'J'ехtReДdе.t:ihgRiпt

С:оmРОБit :!. h-gМпdе CBoCfcfВO .соmрюsit i DgMo.de задае-1 режим визуализации: либо ри~
Compo'si t i!lgQua,l..Lty СОВЭJ-IIЦ'j ПО8ерХ фона. Щ1бо соnряжение- G фОНОМ

I -rrt е.rр оlаtiiэпМоdе Указывает режИм интерrЮJlflцi1]А ДЗ>II'IЫХ меЖЩ' конеLIНЫМИ rочкз.ми
81 О Часть IV. Программирование с помощью библиотек .NEТ

Замечание. В .NEТ 2.0 пространство имен System.Drawi ng предлагает тип Bu fferedGraphics,


который позволяет отображать графику, используя систему ДВОЙIiОЙ буферизации, чтобы осла­
бить или исключить возможное мерцание, Ilроисходящее при визуализации данных. Подробная
иНформация об зтом есть в документации .NEТ Framework 2.0 SDK.

Обращаем ваше внимание на то, что класс Graphics не допускает непосред­


ственного создания своего экземпляра с помощью ключевого слова new, посколь­

ку этот :класс не имеет открытых конструв:торов. Но тогда нак получить объект


Graphics? Я рад. что вы спросили об этом.

Сеансы Paint
Наиболее общий способ получения объекта Grapr.ics заключается во взаимо­
действии с событием Paint. Вспомните из предыдущей главы о том, что класс
Control определяет виртуальный метод с именем OnPaint (). Чтобы форма отобра­
жала графические дa.IпIыe на своей поверхности. вы можете переопределить этот
метод и извлечь Qбъент Graphics из входного параметра
F'aintEventArgs. Для
примера создайте новое приложение Windows Forms с именем BasicPaintForm и
обновите полученный класс Form так. как предлагается ниже.

public partial class MainForm : Forrn


{
public MainForrn()
(
rnitializeCornponent () ;
CenterToScreen();
this .Text = " Basic Paint Forrn";

protected override void OnPaint(PaintEventArgs е)


{
11 При переоnpедe.nении OnPaint () не забу;цъorе вызвать
1/ реanизаЦIO:) базового JCЛасса.
base.OnPaint(e);
// Получение O~KTa Graphics из поступившего ка вход
/ / PaintEventArqs.
Graphics g = e.Graphics;

/1 Визуализация текстового сообщения с заданиыми


// цветом и шрифтои.
g .Drаwstriпg("Dриве т GDI+",
new Font ("Times New Roman" , 20),
Brushes.Green, О , О);

Но. хотя переопределение ОпРа i n t () и допустимо. более типичным под­


ходом является обработка события р а i nt с помощью связанного делегата
PaintEventHandler (:именно это делается по умолчаншо в Visual Studio 2005 при
обработке событий с помощью окна свойств). Данный делегат может указывать на

L
Глэвэ20. Визуализация графически)! Аанных средствами GDt+ 811
.любой ме-roд:. получаюIЦИй 'в RaчесТ8е первого параметра S'lstem ..Object. ~ в кц­
чес:rne второто --P'ai пtЕ\i'.3лtАr gs. В npe.дположении Q '1'01.1. ч1'О вы обра60Тали со­
бытие Pa.i:.o.t. (с ПОМОЩЬЮ И1-IС1'румеmов режима проектироиания: Visua! Studio 2005
или.в aj:ЮI'РамМНОМ ко,цб вручную). вы онова можете извn:ечь ОО)ъек'! Graphics из
-иостyna.ющегtJ на вход PaintEv·et1.t .Ar.9 S. Вот соот:ветСТВУЮЩИ!vl 06раэом модифИЦIII­
РОВЭНliЫЙ nporpaNIМНЫЙ :КОД.

p.ublic partial C}liSS 1'1a.inforn: fЬЩ


j
public MainForm()
1
IпitiаlizеСоmролепt \)1
Cent.erToSC:reen (] :
this _T~x!:, = "Baslc Paint Form";
1/ в Visua.l :Studio 2005 помеCТИ'I'8 Э'1'О'1' npогрaимЮrй 1ЩЦ
/ / 11 Init1aH~eCOJnponent() .
this.Eaint t= n.e.w P-aiТ1tЕv-епtl-lа.оdlе-r (Маi:.'1FОШ1_Рaint);
prl. va te vюid MainF·oZ'·m_ Раiпt (0Ь] ect s-ellder, РаiпtЕ-vепt)'\.trjЗ ~)
{
Graphic$ g "" е . ·GrajZin -1сSi
9.Dтаwstгiпg("Привет .( ;D1-+",
т)('!.., FОJlt("'Тimе$ )I1ew F_<lm",n", 2.01,
Вп1 sЬе.э" GL€e!l, Оt О);

НеасщИСИМQ от -того. на;к вы Qтвечаете на событие Pa-ut., :следУет ю-mть. Чl"о. со­
бытие Ра int ге;нер1'1'Руется: Bc.e~. KQrдa ОВifЮ С.dНOВИТС.8 ~". ВЫ. 80ЗМОЖНо..
знаете. 'ЧТо. OIffiO C''iJ}ifTae'l'CJ'j: ·грязщuм", еСЛИ пере-onpедел&еТC!l его. размер. oКI!ro (или
его часть] о:ткрьmаетси ~з-под ДРУТОТО' окна, или окно. сначала мИнимизируется.
а SjЭJ'ем ВQсстав;авли,ваетс.я:. Во все r:;лyч.aЯx, ROГД& требуетс.нперерисовка ф.ормы.
IШатформа.NЕТ гарщдирует, что обраб()"]'>ч}ш ооБЫ'DIЯ ~PaHt (JШИ пеРlюпредёлен­
Н)'J.[Й метод ОnРа int (.) I будет вюзван автоматичесЮL

ОБНОВlIение области Ю1И'ента формы


Б .ходе выnоцне;ния ариложе~.I{ GDl+ может ВD3ПИКН'JТЬ необходимость в яв­
_нам вызвеe события Paint !)мес-го ожидаН:иятЬго', Ч1Ю ОКЕЮ станет "'ес'I'е'СТВенно
ГРЯЗJ-I:Ь1:М~ _ НаприМ'·ер, вы создаете uрограмму. которая DазвоЛ:Иет ЛQЛЪзователю
выбрать подхюдящий рш.'УНОН J:lЗ вiЗ-бора точе-чных изображенИЙ:В Пilllliаоватe.JIЪ­
СН.ОМ д:иалоговом OR#ie. По.сле закрытия диалогового аКН.а нужно о.Т'о6разит'Ь вы··
бращ~ ПОЛЬ.30Ватедем рисj')~Ю~ в области JUIИeнта формы. Очевидно.. если ждать.
когда окно станет "естественнр гря:зньrм~. ПOJIbЗbвате:пь не УВЩИТ ИЗменеНИЙ.до
того. кан и~нятен разм;еры щща 1LilИ его часть откроется :из-под другого окна.
Чтобы ВЫщ!a.:n; переJ,>ИСОВItY Ol(на программ:по . просто вызвитеe .насле.цуемъпi .ме­
Т-Од I!1v.a lidate \).
p1:l1::,lic pcHtial clase МаiЛРQrh\ : F'",rm
l
812 Часть IV. Программироеание с помощью библиотек .NEТ

private void MainForm_Paint(object sender, ~aintEventArgs е)


I
Graphics 9 = e.Graphics;
/ / Здесь ВЬШОЛНRется визуализация изображения.

private void GetNewBitmap()

/ / Отображение диалогового окна и получение нового образа.


// Перерисовха хлиентсхой области.
Invalidate() ;

Метод Iпvаlidаtе () является перегруженным, чтобы вы могли указать прямо­


угольную область для перерисовки, а не перерисовывать все области клиента (что
делается по умолчанию). Чтобы обновить только заданный прямоугольник слева
вверху области ЮIиента, вы можете ИCllользовать следующее.

1/ Перерисовха прямо угольной части формы.


private vOld UpdaceUpperArea()
{
Rectangle myRect = new Rectangle(O, О, 75, 150);
Invalidate{myRect) ;

Доступ к объекту Graphics вне обработчика Paint


в некоторых редких случаях может понадОбитъся доступ к объекту Graphics
ВНЕ контекста обработчика событии Paint. Предположим, например, что нужно
перерисовать небольшой круг с центром в точн:е (х, у), где был выполнен щелчок
кнопки мыши. Чтобы получить действительный объект Graphics в рамках контек­
ста обработчика событий MouseDown. можно, например. вызвать статический ме­
тод Grapllics.FromHwnd (). Имея опыт использования WiпЗ2, вы можете знать, что
HWND является структурой Дlli~lli1Х, представляющей ORHO Win32. В paMRax плат­
формы .NEТ наследуемое свойство Ha:ndle извлеRает соответствующую структуру
Н WN D. IШТОрУЮ затем можно использовать в качестве параметра для Gr ар h i с s .
FromHwnd () .

private void МаiоFОПТi_МоusеDоwn (object sender, MouseEventArgs е)

// Получение объекта Graphics через Hwnd.


Graphics 9 = Graphics.FromНwnd(this.Handle);

/ / Рисование круга 10*10 по щелчку ИЬDIIИ.


g.FillEllipse(Brushes.Firebrick, е.Х, е.У, 10, 10);

// Освобождение объектов Graphic, созданных напря:иу.ю.


g. Dispose () ;

L
Глава 20, В'l1зуализащtfl rpаф»lчеСJ(И~ данных Gредствами GDI+ 81З

Эта лоrика отобраЖ<lет круг ,за uр(::делами обработ<nша Оп P'al n,t ( ) ., по очець,
ваЖl:1Q по}-IйМaТР. что l{()ГД~ въmОЛI;IЯетс;я обновление формы. все такие RРУГИ: ("J'и­
РaIОТСЯ! Это разумно. UDСКОл.ьку соответствующая вИзуализация выIJlwLJlась.в
KQНTeRcтe событ!'lЯ Мощ,еDо!o>Jn. Значительно лучшим иОд.ход.ом нвллen:-JI создание
в обрабо'f<.nme t.'QБыия ,M0J.1seIJc>w11 новота тиna Poih'k который ,l10ба:вл.яется х ие­
lюторай :внутр~щей колле1'СЦДИ lFJЩIРИМер. List<T>J., И только затем llыаЫWclетея
rii~al idate {}, Тогда Обраб0Т'1;IИКСdt3ытил Ра iDt может просто ~проити" пО IюЩIек­
ции и neрерисощl.ТЬ R,ажд:ый 1'01 nt,
p'UЪ 1 i с ра.гt.i a...l c.1 "S'S НаiпFN-m ; 'F'orrn
{
11 ~СQ()J;&Ьз:у<е'1'СR ~ жранеВИR всех Po.int .
pIiiV;;J',t-е L.ist <Ь' сiп't.> myIJU; = new 'Li:оt <Р оirп > О:

P11bl :i c MainFQrro.()
{

/I Добaв.nеиие в ХОillЛ81щи:iD,
mу~'uз .Add (Dew PQ i n.t (е,Х , е.У))!
1 Щ7·аl± d,a't е ();

Grap;ruas 'g e.G:raphics;


=
g . DrawSt:.ying .(ПIJрИЕе'1.' 'G.DJ+I', TLE'W Р'сюt (,"'Тimе's New RОr!liilП''', 201.,
new Воl iсЩ'ПI sh (СQlш:: .. Вlа.сk), О, ()! :
for edcll!Pc>int:p in myP'r..s)
g.F'illEllipse(Er1J>sfJ.es.Fireb.r'iek, р.Х, р.У, 10. 1 а! ;

При т.аJФм подходе уже отобрamенные J~РУI'И будут о"та:вМ:Ь('5] Па Мё'С1"е. ПО"
СКОЛЫ')' графич:ес~ШJ Il]I3~ЭJmзац:ия Q6р~баТЫБае'l"СН 'в РЭlVlJl'<lJi! события Ра i.,ryt, На
ри(' . 20',1 пОказано оющ тестовото залуСJta этого ПРКJIожен:ия..

ЛриветGDI+

, ........: . • •

Рис. 20.1. ПРfUClое графичес!Сое гфилож.ение

ИсхоДIOilЙ 1I.QД . пр.оеkТ BaslQPairitForm размещЕЖ 1;1' подка.т<ЩОге. соответ'Ствующвм главе 20_

J
r
!

814 Часть /V. Программирование с помощью библиотек. NEТ

Освобождение объекта Graphics


Если вы внимательно читали несколько посдеДJ-IИХ страниц, то могли заметить,
что в некоторых лримерах программного кода непосредственно вызывается ме­

тод Dispose () объекта Graphics. тогда как в других примерах зтого не делается.
Поскол:ьку тип Graptlics работает с самыми разными неуправляемыми ресурса­
ми, имеет смысл освободить указанные ресурсы как можно быстрее с помощью
Dispase () (не дожидаясь. когда зто сделает сборщик мусора в процессе финали­
зации). То же самое можно сказать о любом типе. поддерживающем интерфейс
IDispasable. При работе с объектами Graphics нужно придерживаться следую­
щих правил .
• Если объект Graphics был создан вами непосредственно. после окончания
его использования его следует освободить .

• Если вы ССылаетесь на существующий объект Grap h ic s . его освобождать не


следует.

Для того чтобы это стало более поRятны,' рассмотрите следующий обработчи]{
события Paint.

pr:ivate void МаiпFоrПl_ Раiпt (o bject sender, Pa i rltEven t Args е)


(
/ / Загрузка локалloиоrо файла *. jpq .
Image mylmageFi 1 е = Ima ge. Fr omFi1e ("1ands cape . jpg") ;
// Создание HOBoro объекта Graphics на основе изображении.
Grap h ics imgGraphics = Graphics.Fr:omlmage(mylmage Fi1e);

/ / Визуализация новых данных.


i mgGraphics.Fi11E11 i pse(Brushes.DarkOrange, 50, 50, 15О, 150);

/ / Нанесение иsображения на форму.


Graphics 9 = e.Graphics;
g.DrawImage(myImageFile, new PaintF(O.OF, О. О Р));

/ / Освобождение созданноrо иами объекта Graphics.


i mgG r aphics. Dispose();

На данном этапе обсуждения не беспокойтесь о том, что некоторые элементы


программной логики GDI+ могут быть ДЛЯ вас не вполне понятRЫ. Однако обратите
вн:имание на то. что здесь объект Graphics получается из файла *.jpg, загружа­
емого (с помощью статического метода Graphics. Fraro.I)!\age ()) из локального ка­
талога приложения. Поскольку объект Grapbics создается явно, после окончания
использования этого объекта лучше использовать Di spa se (). чтобы освободить
внутренние ресурсы И сделать их снова доступными для использования другими

компонентами системы.

Однако, заметьте, Dispo se () не вызывается явно для объекта, который был по­
лучен из поступающего на вход PaintEventArgs . Причина в том. что вы не созда­
вали зтот объект непосредственно и поэтому не можете гарантировать . что другие
части программы не используют его. Очевидно, что при освобождении объекта, ис­
пользуемого в другом месте программы, могут возникать проблемы.

,
l
rnaBa 20. ВI>I3Уалиэация rрафIil4!1СКI.\jt данны)( среДствами' GDI+ 815
в CIЦf3И С enшзам~. что если:вы забудете выЗв.-::nъ метод Di$РОЗ~ () ддя оРъеК­
та, реали~ующеГ(i)· lDi's pos.a:bl€, BJIYr'pel-Ililiе ресурсы будут освобождены n032f(e,
при обработке объе1\та сборщmюм: муёора (см. главу 5). В Э'rо:м сМысле ос:аобожде­
яие объе,кта img(;:r aphips, строro товорн. не является необхо1IИМЫМ. Так ЧТО, хота
явное освобощдение о{5ъсктов GDi+, созданных вами непосреДствеuно, дела.t:Т про­
гl'эммный J(фД сове:ршеннее. мы с Baм;U. чтобы сделать п:римеры llpогр:LММНОГО шща
Бэтоii щаве боле.е В:Рq::П;rnми, не будем освобождать каждый ТИП ОО1+ вручную.

Систем'Ы координат GDI+


Нашей следУющей задачей будет рассмотре»Ие коордидатНh!Zi -tи.стем GDI+.
В (701+ определяютСЯ три pa3l-IЫе сиете.МЫ коор-динат. которые исщщьзуются сре­
дой выплненин. лри определении места размещения.и разМеР(I!В ~одеf'ЖИМОРО НИ·
зуали3аЦИИ- Во-первых. есть Т:Ш нааьшаемые мщювЬ!е "оордщ-tllJТЦ>l. (и.ли вНerrтяие
RООРдпнаТЬi). Мировые координатъr представ:цяют а,бстраКIЦ1Ю рЩ!мерои )il,анБОГD
типа GDl*-. Нез.-urnсимую ОТ единиц измерениа. Например. при црорисоВ]{е npямо~
утолыmка с Yl'аза.ние:м ДJШ Р!lзмерност.и (О.. О. 100. 100). ВЫ Щ:l самом делЕ:указы­
ваете прЯМОУГО:JIЬНИIC размером 100х100 ~еди:mщ·. как вымажете дm-адатъся. по
умолчanию ~единица" - это nи~ель, ОДЫ'aRО ей: МQЖНО назначить и другую едини­
цу измеренив (дюf1М. сантиметр и 1~п.l,
Далее. есть страничные координаты (шюрдинаты стра.н;ицы). Страничные
IЩ('РДИНаты представляют' смещение в дрименеIlИИ J( ОРИГДН:;,LПЬНЫМ- мйров:ым

КООJЩJtШ1Т"cI.М.. Это удобно 1'оtда... когда вы -не XOT~Te вычислять рмещсниt't в сво­
ем программном тооде ВрyчнJЮ (ВЫ nе обязanы это дедат.ь), НЩIpJilМер,е-.с.пи у вас
есть форма. ROH:Jpaн должна оставаться в грЭНШJ;ах 100)(100 шпti:::елеЙ. вы мошете
указать страничную координату (1 00*100), чтобы В'И3УaJЩ*Щl(IЯ ВblЛоJIFI.ЯШ;iСЬ 0']'-

носителыю точни (] 00*100). То:ща в своем базовом коде nЫCMOH~eTe просro указать
мировые J«(}ОР)il,инаl"ы (избежав. тем самым, необходимости вруч:нyIO ~ЮЬ
смещение).
1-IанОНец. есть nрuборн.ые l(o()pдUHamь~ (КОQРд.щ'fan!J J~ТРОЙСТва), Приборные
:координаты представляют результат прим:е.не1ЩЯ С'I"рa:IЩЧНЫХ :координат :к ори­

:rиналъным ми;ровым :КОQрдинатам . Эта координатнЩJ rnСТУN{Э испольауетсядля


(;шределеюrя ·тОго • .rAe· именно будет показ;щ СОQ'rветСТВУЮЩИЙ тЩ1 GDl+. При про­
граммирОВaI-1ИЙ С помощью cp~cтв GDl+ nPОТР~СТ обычно мыслит втеpМIшах
Мl;Jpo.выx координат. ROTopbIe Я8ЛmoтCf1 базод ДДЯ опре·деJ]еН:И.fJ размерОв и места
раэмеЩeIIИЯ типа GDJ+, Для визуализации в Мl1роВ'ЬЦ ICOорщm:ата:х не требуется
RИКа.ЮfX специальных программных ухищpe!lliЙ- нужно просто. п.е-Рl'{дать зЫЭче­
НИIЯ :иа:мер.еВ:И:Й текущей ошфации ВИ3~.

VQid MainF o :r;~Paint (оЬ) ec.t, sе.лdе r , Ра iлt.ЕуеntАгgS е)


{
1/ ВИЗУaJmЗа.ции i:Ip~ол.'оJIыiIIIu в мироlUilX J!:ООР,ции&тах..
'Grilpnics 9 = e,Graphic.'Si .
g.Dr.awRеGtl:l.D')lе(Репs.Вlас-k., 10,10., 10.0., 100);

"За кулисами" вan:rи М1-фовые }(:<j)ОРДИ.ватЪ'I автоматически отображаются в IiOОР­


дина'ГЫ страницы, JOOтopble затем отоБРfЩШЮтс.fJ в приборвые КQордиюнъt. Во JllШQ­
тих СЛJNаях: БЫ вообщ~ .не будете ИCnЩlЬ30ватъ lюор,цшm.1Ъr страницы 'и ЦР1'2БОРJ-IЫе
816 Часть IV, Программирование с помощью библиотек , NET

координаты непосредственно . если только не захотите применить определенные

графические трансформации . Поскольку в предыдущем программном коде не ис­


пользуется никакой программной логики трансформаций, мировые. страничные и
приборные координаты оказываются идентичными.
Если перед визуа..11изациеЙ своей программной логики GDI+ вы хотите приме­
нить какие- то ареобразования. вы должны использовать подходящие члены типа
Gr a pl'1i cs (например. метод Т rап slа tеТrапsfогrп ()J, чтобы перед тем, как выпол­
н ить визуализацию, указать Ы страничные координаты" в существующей системе
мировых координат. В результат е устанавливаются приборные координаты, кото­
рые будут использоваться при выводе типа GDI+ на соответствующее устройство.

private void MainForm_P a i nt(ob jec,- s en der , Раiп tБv е пtА г gs е)


!
11 Указание смещеНИR (1 О * 1 О) ДЛR страничных хоординат.
Graphics g = e . Graphics ;
g . Transl ate Transform(1 0 , 10 );
g . Dr awRectang1e(10 , 10 . 100 , 1 0 0);

в данном случае при отображении прямоугольника его левый верхний угол


фактически будет помещен в точку (20. 20), поскольку к мировой системе коорди­
нат будет добавлено смещение в результате вызова Transla t eTrans for m ().

Единица измерения, предлагаемая по умолчанию


в GDI+ едmшцей измерения по умолчанию является пиксель . Начало коорди­
нат размещается в левом верхнем углу с увелИче нием о си абсцисс вправо , а оси
Qрдинат - вниз (рис. 20.2) ,

(О, О}
х

Рис. 20.2. Система координат GDI+, предлагаемая по умолчанию

Поэтому, если вы отобразите Rec tang1e с использованием пера толщиной в 5


IIИRселей и красного цвета, как показано ниже,

void МаiпFо г m_Р аi п t(оЬjе с t sender , Pai ntEventArgs е)


{
11 Установка кировых хоординат с использованием единиц измереНИR,
11 предлагаемых по умолчаНИlD.
Graphics 9 = e.Grapbics ;
g . DrawRectang l e(new Pe n(Co l or . Re d, 5) , 10 , 10 , 100 , 100) ;
1 Глава 20. 8и(!уаЛИЭi:ЩИЯ графических AaIiHb~X среДС1вами 6Ш+

ВЫ ДОЛЖНЫ увидеть lСfщдрат, смещ~ на 10 IIИkCелей :ВВИВ и:вправо OТHOCWl'eJIЬ­


817

на bepX1-Iеro и девого Края ющецт~кОй области формы. нак шшазано на рис. 20~3.

Рис. 20.3. Визуализация в nИj(селЫ~ЫI( едw-iицах

Выборалыернативной единицы измерения


Если BЪJ не хотите ВЪШОЛНН'IЪ визуализацию изобращеШlЙ. с иепОЛЬЗ0ванием
mшееЛЪFlЫХ ~ДИЮЩ -измерtшин. вы Имеете l30аМОЖНОСТQ И3Мщrnтъ эту 11рЩЩТУЮ по
умолчaниwУСТfЩОl!КУ с помощью свойства Раg еDпit об'J,еJ<та Gr aphi c 5. Свойству
l'agerJni:t )I.~O~O присвоить лroбос значение из персt:rвя G:rapl1icsUnit.
puЬ 1 i с' е п1.iJТ, Gr'a pbi c5Un i t
j
I! М'ироJWe I!I:ООРдииа'DJ.
W·c,r ld,
11 ПИJCсе~ МВ DидеодисlШеи n 1/100 ,цDb!.a д,mi ПР~'1'ера,.
Di sp.La:y ,
11 nИксenь.
Pl x el,
/ / С'1'андаlWН&Я '1'ОЧJ:а r;J;pииwера (1/72 ~a) .
Poi t'lt F

1/ д.'Мм.
l nc h ,
/I C'1'aR,!{apQ!l~ e~a' ДQJtyИеiWa (1/300 ~a) .
Dacu:m:en t,
/ j МИпnиме7.'р .
Mi l l irгre t~r

Чтобы llpоверИTh. кan ИЭJ\4'епяетс.я бащшЩi едшпща измерении, м,щифицируйте


имеющийсн ПРОFРам~шый 'Код "a,R, ЮiК предпагается нйже_

privat e 110i d мq. :i n Fс,Л'l";l_ Ра i о t {abj ec t s е пdет , Раi л t Еvе n tАТ <g$ е)
I
11 0wображевив Dpяиопo.nьни~а в ДIOЙМaX r а ив в пихсenих ..•
G r.арh1с э g = е. Graphics I
Ч . l"а gеUлi t = G.taphi CSLJf!it. Inё.'I1;
g .. Dr дwRес t адgl е {пе W P e l'1 (~Cc> l Gl r. Re dF 5 ), О, О, 100, 1 00);

ВЬ1 должны УВИД€ТЬ совершеннд ДРУГQЙ прямqyтольди'К, кав nО]i;;ыаио на


рис. 20.4.

j
818 Часть IV. Программирование с помощью библиотек .NET

Рис. 20.4. ВизуanизаЦИR в дюймах

Причина того. что здесь более 90% области нлиента формы занято темным
(нрасным) цветом. заключается в увазании пера "шириной' в 5 дюймов! Сам пря­
I
моугольник теперь имеет размеры 1О0х 100 дюймов. и тот маленький светлый пря­
моугольник. который вы видите на РИСyнRе в правом нижнем углу. является левым !
верхним углом большого внутреннего прямоугольника.

t
Изменение начала координат
Напомним. что при использовании координат и единиц измерения, предлага­
емых по умолчанию. точка (О. О) находится в левом верхнем углу соответствую­
щей области. Часто это и является именно тем, что требуется. но что делать. если
вам нужно поменять точку. относительно которой выполняется визуализация?
Предположим. например. что ваше приложение всегда должно (по какой-то при­
чине) оставлять пустой полосу шириной в 100 пикселей вдоль границы области
r-слиента формы. Torдa вы должны гарантировать, чтобы все операции GDJ+ вы­
полнялись в соответствующих пределах внутренней области ,
Один из подходов, который можно при этом использовать. заключается в добав­
лении смещения вручную. Конечно, утомительно добавлять значения смещения к
каждой операции визуализации. Значительно удобнее (и проще) бьmо бы исполь­
зовать свойство, которое. по сути, говорило бы следующее: "ХОТЯ я даю указание
отобразить прямоугольник с началом координат в точке (О. О). вы доткны исполь­
зовать для начала координат точку (100.100)". Это должно сильно "упростить вам
жизнь", поскольку вы сможете увазать параметры размещеl-ШЯ без модификаций.
В рам.ках GDI+ вы можете увазать точку начала координат. установив значение
трансформации с помощью метода TranslateTransform () (класса Graphics), по­
зволяющего указать страничные координаты, которые будут при меняться 1< вашим
оригинальным мировым координатам. например:

void MainForm_Paint (object sender, PaintEvel1tArgs е)


[
Graphics g ~ e.Graphics;
11 УС'1'аиosха смещеНИR (100, 100) ДJt,. С'1'раиичlbolX lCоордина'1',
1
Глава 20 , ВИ3'У~ЛIo1$ация граФИ'.Jеских ДЭНt1,Ы)( средстnами GDI+ 819
g. 'Тып з lateтt a nSfc·11'tJ :( 10\J'. 1 О О) ;

/I Эн...чеицllИИ 'ИИрОВШС xoopдJ.!Нa~ ОС'1'iUI'l'СИ (О I О, 100" 1(0) f

1/ но приборн:ые жоордИJtа.ты теперь равны (100, 10а, 200, :200).


ч. DrаwRесtang Iе (n,ew Ре.п (CJ:)io r .P..ed, 5. } I О. О, 100, 10.(1.) ;

Здесь вы ус:таНill'ЩИВаете для мировых I{Qордина. Э"flачения. '(О. О. 100. ] 00).


OдIilllW для стрвничны.Х lюординат вы указали: с.меЩС1-ше (100. 100]. П@;,!,!"1МУ ддя
npиборных ItOОР)J.ИRнт будут иcnОЛЬ30ЕЗТЬся. значениЯ (100. 100. 200. 200). Таким
обраЗQМ:. ХO'l1Я вьrзов DrawRee taTrgle () выглядИт Так. кан будто вы размеща~е пря­
МОУТОЛhJ;IИR в левом верхнем углу формы. результат будет выгметь так. RaК 00-
1taзано на рис. 20.5.

Рис. 20.с5. Р'е'эУЛЬТ8Т применеНИR см~ще~IИЯ L'Траницы

Чтобы вы имеJl1if ВО3МOЩJfОС:ГЬ ПОЭRсщ:римеm'ировап. с некоторыми способами


измеаеш'1Л каордщыТ1-ЩЙ с.и:стем,ы GD!+. J;I фaй;JJе t ИС'.xfJДНЫМ текстом примеров
ЭРой Jf.НИг.I1 (д.llЯ его загРУ31<И rrосетит~ разд~л загрузки W~Ь-у:ща Артеsэ. размещен­
:нато ПО адре<.'У w:ww. ap T €'~;;;. oow~ .ееть црц.wср приложен:ив 'с' иМенем Со.оr5уstещ,
В ЭТОМ ПРИJ10жении с ПоМОЩЬЮ двух меJUO вы можете ме'i'ШТъ начало K00{1ДlffiaT и
единицу измерения (р:щ::.20,6).

~,

.~..

Р"е. 20.6. И'змеНet'lие начала l(оорДИ~JaТ и единицы измерения


820 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NET

Теперь. когда вы uмeeтe представление о роли базовых трансформаций для опре­


деления координат визуализации имеющегося GDI-типа на целевом устройстве,
следующей задачей нашего обсуждения будет работа с цветовыми значениями.

Исходный код. Проект CooгSystem размещен в подкатапоге, соответствующем главе 20.

Определение цветовых значений


Многие методы визуализации, определеЮlЫе классом Graphics. требуют от вас
указания цвета. который должен использоваться в процессе рисования. Структура
System. Drawing .Color представляет цветовую константу ARGB (от Alpha-Red-
Green-Вlue - альфа. красный. зеленый. синий). Функциональные возможности
типа Color (цвет) представляются рядом статических доступных только для чтения
свойств. возвращающих конкретный тип Color.
/ / О,ции из _o_ec'1'1la встроенных цветов. ..
Color с = Color.PapayaWhip;
Если стандартные цветные значения вам не подойдут. Bl;I можете создать новый
тип Color и указать для него значения А, R, G и В. используя метод FromA.rgb () .
/ / Указание ARGB вручную.
Color myColor = Color. frOrnArgb (О, 255, 1 28 , 64);
Используя метод FromName () . вы можете также сгенерировать тип Color по дан­
ному строковому значению. Строковый параметр должен при этом соответствовать
одному из членов перечня KnownColor (который содержит значения ДЛЯ различ­
ных цветовых элементов Windows, например. таких как KnownColor.Wind owFrame
и KnownColor.WindowText).

11 Попучение
Color по известному имени.
Color myColor = Color.FromName ("Red");
Независимо от метода получения типа Color, с этим типом можно взаимодей­
ствовать с помощью его '-иенов.

• GetBrightness () - возвращает значение яркости типа Color на основании


измерения HSB (Нuе-Sаtuгаtiоп-Бгightпеss - оттенок. насыщенность, яр­
кость).

• GetSaturation () - возвращает значение насыщенности типа Co lor на осно­


вании измерения HSB.
• GetHue () - возвращает значение оттенка типа Color на основании измере­
нияНSБ.

• IsSystemColor - индикатор TOro, что данный тип Color являетСЯ зареги­


СТРИРОВaJ-lliЫМ системным цветом.

• А, R, G, В - возвращают значения, присвоенные для альфа, красной. зеленой


и синей составляющих типа Color .

L
Глава 20. ВИЗV811i11зация графиче<;киJo: AaHH.bIX f:р~дС'твами G[lI~ 821

Кnacc CoJorDiaJog
Чтобы (jf5е(~Ilf'.Чит:ь К{)lreЧНОМ:У пользователю прилотения ВQЗМОЖR9СТЬ .IW;нфшу·
рирщщть тип ColoL ПРQСТРанство имен Sуs·tеIJ1..WindОW$.fОЛJ\S предлагает Brтpo­
еБ.Н.bIЙ wщ.се диалогового oкna (' иМенем ColorDialo'g (рцс. 20.7) .

.~_Ii
_гггг •••
• I-ГГГ •••
•• г • • • • •
••••••••
••••••••
• •••• · t!Ч .г
,&nOtнorl.S_Jl/!lJТ~

гггггггг
гггггггг

""'С. ,20,7. ДwаЛОГQвое окно настройки цветЬв WlndDws Fon:ns

~бота1'Ь с Э'FНМ диалоговым. окном Очень просто. Для действителыJрго ЭRэем­


Iшира ТЩlа C:olorDial.og вызовите ShO··'.IDialo.g!). чтобы отобра'Зить ДШЦlOговЬе
01<НО МОД<}ДЬНО. После закрытия ДИa.ilOfОВОГО окна пользоват~ем B~) сможете из­
влечь сортветствую[ЦИЙ объект CGloc, ИСПDльзу.я свой.ство Color.DjaJog.Co1-рr.
ПРедпОложим. ЧТО.Dы:хотите с помощью CalO1'DifJ,log предост8:В~Ь IIощ>зовате­
лювозможнос:гъ выбрa.n..цвет фона дnя области к)Щента форМЪJ. Чтобы упростить
СИ'J"YЗIЩ[9, МЬJ будем отобража:rь :ColorDi а lQg тщда. когда пользователь щeлюreт Б
люБОМ месте dблв.сти .клиента.

public patti<il сlаэs ИаinFОrщ : ForJ!1


I
privat:: · Соlоr.Ща.lоg ~D1orD1g;
pxivats Colpr .currC.olpr = 'ColQr . DiIпЫ .ау;
puыc'' lY!аiг:Fоrm ()
!
IРitiаli2еr~ЩР9nent();
coloJrDlg = rlew Со} оill.iаlюg () ;
Text = "дд .. измен-е'Ю!1fl цв,ета щелкните здесь ,; Q

thi,s • M<:;:HJfSeDowJ.J += n€ .. t-1() useEveB tffaI,'dl ,e,r' CMa.:t t'1.FО,r"щ_Моusе.DОW'!1) ;

p'Iriva.t e void Маi..nF:.'Нш МoouseDoW'I1 (object s e 1"d!O;r, MCiIJs.eE·'I7eJ1tArg..;; е)


r -
i.f (colorDlg. Sr':owElia 1 og () '! =Di alogR!=sl11 t . Ca neE'l)
!
currColo'r = co1 ,01:'01"J .. Colpr;
this. Bac'kQolc,r = c:ur:tCplor i
strlng stIARGB = CQlorDlg.Colo,r .'10st,ring'C);
M~ssageBox.s::r, ow ' (s·tr,AP:.GB " "Bf,!~paH ,,~ цвеТ': ");

:J

1
822 Часть IV. Программирование с помощью библиотек .NET
т
Исходный код. Проект ColorDlg размещен в подкаталоге, соответствующем главе 20.

Манипулирование шрифтами
Теперь давайте выясним, как можно программно манипулировать шрифтами.
тип System.Drawing.Font представляет шрифт. установленный на машине поль­
зователя. ТИпы шрифта могут определяться с помощью любого числа перегружен­
ных конструкторов. Вот вам несколько примеров.

11 Соз~ание
Fon t с за.цаниыми именем типа ,И размером.
Font f new Font("Times New Roman", 12);
=
11 Соs~ание Font с за,цаННJolИИ именем типа, размером И начертанием.
Font f2
= new Font("WingDings", 50, FontStyle.Bo1 d I FontStyle.Under1 ine);

При создании f2 здесь используются связанные с помощью операции OR значе­


ния из перечня FontStyle.
public enum FontStyle
{
Regular, Bold,
Italic, Underli ne , St rikeout

После установки параметров объекта Font следующей вашей задачей должна


быть передача этого объекта методу Graphi cs . DrawString() в виде параметра.
Хотя метод DrawSt r ing () перегружен. каждая из его вариаций требует одну и
ту же информацию: отображаемый текст. шрифт для отображения зтого текста,
кисть. с помощью которой выполняется визуализация, й место, в которое нужно
текст поместить.

private void Маi л Е' о r m _ Р аi пt (object sender, РаiпtЕvелt Аrgs е)


(
Graphics 9 = e .G ra ph i cs ;
11 Аргументы (String, Font, Brush, Point).
g.Drаwstriл g (" Мо я строка", new FOI1t("WingDings", 25),
Brushes. Bl a ck, new Point(O,O));

11 Аргументы (String, Font, Brush, int, int)


9 . DrawStr i ng (" Другая с т рока", new Ро п t ( "Т imes New Roman n I 1 6 ) I
Brusbes.Red, 40, 40);

Работа с семействами шрифтов


Пространство имен Sy s tem.Drawing определяет также тип FootFamily, предла­
гающий абстракцию для группы гарнитур. имеющих ОДИНaJiOВЫЙ базовый дизайн.
но с определенными вариациями стиля. Семейство шрифтов , например, такое как
Verdana, может включить в себя несколыю шрифтов. оТJlliЧающихся по стилю и раз­
меру. Например, Verdana Bold (полужирный) 12 пувктов и Verdana Italic (курсив)
24
пункта ЯВJIЯЮТСЯ разными шрифтами в рамках одного семейства шрифтов Verdana.
Глава2Q. Виgуаllизация графи'leСКИХ.,Д:l'lННЫХ средствами GDI+ 823
K!I>BCТPYНTOP типа FOfltFa1I1ily ПОJ:ryч:ает :на щсод строку е ИМеБfi:М СeJViеЙств;з.
ШРJiфТОВ. HQTopoe вы пытаетесь llpt,>дстщшть. Посдс создания "ебщего ~емеЙетва·
flbl Mom~e с:шщать бр;лее crr~ф:ичиь~й !СЮъецт FOI1t.

p·l-iva1ce voJ.d МаiпF'оrrn_Р<iiи·t (ohject sелidеl'" P-аintЕvе;,tА:сgs е)


{
r'::rарhiсз 9 =.e.Graph.i.cs;

11 СО!9',t(з:ние семейства 'Шрифа.о.ов,.


f't,ntFC1mily П1УFаw.i11 = г_е1.\' Fо!)tFаmilу("Vёrdаnа") I

11 Передача се.llе~с~а 1I:OHC~YIt!t'Opy Fo~t.


FotL:L !ШуFопt = ne-w, Fеiчt (шуFаmilу, 1:2') ,;
g.Ь:rаWStriП<g-I"Лривет!", mуFQпt, BIushe.$,.Bltfe, 10, 1());

Больший интере-ё представляет собой ВО3МОЖfJоеТЬСDора статистики в отно­


шен:и;и данного r.емеЙства шрифтов. Скажем, вы создаете npиложение 'I'eJ-<iСТОRОГЬ
fщцIOcr'ора и хотите определить среднюю ширину симв(!л;а в конкретном объеR'te
F'OYJtFamily. Или, например, вт" нужна информация о ffilДстрочнhIX И подстроч­
ных значениях ДЛЯ данного ~ИМВDла. Для получении такой иrнформации ·тиn
FQп1.F:а:mi ly предлагает ИСIШЛЬЗ0вать СIreциалъныечлены. <ШИсан:И.я :КОТОрЫх при­
ведены в табл. 2Ь.5.

Табnиц-а 20.5. Llлены типа FOL1tFamily

Член Описание
Возвращает метрику I-/aдcтpo"ll'loro элемею-а дЛЯ <ИJ6НОВ данного
сgмейстgiЗ

GetCellDe~;ceht () Возеращэ€т метрику под(прочtJОГО эпеменТF1 МЯ членов Aa~llflOrO


семейства
GеtLiле$рас:ing () ВозвраЩает РЗСG:гояние меж,цу двумя riосllедователы1мии (;трОК!:IМИ
-гекста ДЛЯ данного' F~:JJ1tFa mi 1у с указанным F1ont'S.t yl€
GetName () Возвращает И'МЯ данного Fслt1'amllу на указаНl-I,ОМ ЯЗDll(е

IS$lyleAvailable () ИНДllllGiТОР доступности указ;:tННОГО FOl1 tStyle

Ддя щри:м-ера раССМ0rrрите ('ле.цуюЩIfЙ обработчик соБЬfТИlI Ра i.rrt, ВblI'JОДЮЦ:ИЙ


на Пе"'IaТЪ ряд х~рюn,еристик семейСТ.I;1а шрифтов Verd~.

p;:-i'Jаt.е -"O,1d МаiпFОПi'__ Раiпt (object S'EHJd·er, t>аiп,tЕv'еIJtАrgs ",),

,:;rв,рlji= g = е . Giraphi сз;


}\:щt}",ашilУ mуF'алJilу '= rtew· F,mtE'amily ("V1еrdзла" J I
Fon t myFon t. = flew Yo:r,-t. (myFaIni 1 у I 1 Z) ;
L'l,t '/ = О';
~пt fсmНlёigh:t = mу:Fопt.Ееig!"Jt;

11 О'1'обрaJll:$ЯИ~ e~ )t:э)Jepem!И M~ ~еио~ Fon,tFamily.


this. Te~t = "Един:ица измерени'я~ ,GTa:pr!ie:SUnit.. т, -h myFor1't.• Urut,
g.Dir:аwStr:iлtj("семеЙ.ство '1erdana.", myF6rJt_, Вr:u~h€э.Вlсе, 1(), ~,,):
у += 20,.
?'"
i
,,
824 Часть IV. Программирование с помощью библиотек .NET

/ / характеристики сваей нашего семейства . ..


g .Drа w Stгiп g ("Над с тр оч нЬ):е для Ve r d a n a Bo1d: " +
my Famil y. Ge t Ce1 1Ascent( Fo nt St yl e . Bo l d) ,
myFo nt, Brushes.Bla ck , 1 0 , у t fo n tRe ight) ;
у t = 20;
g .DrawString ("П одс т ро ч ные для Verdana Eo ld : " +
myFamily.Ge t CellDe scent(FontStyle.Bo l d ) ,
myFont, Bru s he s .Bla c k, 10, у + fолtНеight);
у += 20;
g . Dra wSt rin g ("Интерл и нья ж дл я Verda n a Bo l d: " +
myFamily. GetLin e Spa cing( Fon tSty l e . Bo l d) I
myFont, Bru s hes.Bla c k , 1 0, у + f on t He i g h t);
у += 20 ;
g . DrawString (" BbIco Ta для Ve r da na Bo l d : " +
myFamil y . Ge tEmHe i gh t( Fon t Sty1e.Bo l d) ,
myFont, Brus h e s. B1 a c k , 1 0 , у + font Height ) ;
у +'" 20;
На рис. 20.8 показан: результат.

'!' f Д~"ЧIЫ ~ш.. рення: GraphicsUnit.Poi .. , г;- :[E1I~


Семейство Verdana.

Надстрочные для Vегdапа Bold ; 2059


Подстрочные для Verdana Bold : 430
Интерлиньяж для Verdana Bold: 2489
Высота для Verdana Bold: 2048

Рис. 20.8. Сбор статистики ДЛЯ семейства шрифтов Verdana

Заметьте, что указанные члены типа Font Famil y возвращают значения с ис­
пользованием в качестве единицы измерения Gr aph i c s lJn i t . t'o int (а не P i x el).
что соответствует 1/72 дюйма. Вы можете преобраз овать эти значения в те еди­
Ioщы. которые вам подходят лучще всего .

Исходный код. Проект FontFamilyApp размещен в подкаталоге, соответствующем главе 20.

Работа с гарнитурами и размерами шрифтов


Давайте теперь построим более сложное приложение. позволmощее пользова­
телю манипулировать объектом Fo nt. поддерживаемым формой. Это приложение
предоставит пользователю возможность указать гарни туру шрифта. используя
встроенный набор гарнитур. доступный путем выбора СерВИС4> Гарнитура из меню.
Пользователю также будет позволено косвенно управлять размером объекта Fo nt
с помощью объекта Ti me r Windows Fопns. Если пользователь активизирует Timer.
выбрав из меню Сервис4>РОСТ? то размер объекта Font начнет увеличиваться (до
максимального верхнего предела) через регулярные интервалы времени. При зтом
отображаемый теЕСТ будет постепенно увеличиваться. что обеспечит анимаци­
онный эффект "живого текста " . Наконец. третий элемент меню Сервис будет на-
Глава 20'. ВизуаJ1иза:ция rpВфИ'lеских I1З\iНI'>IХ СР&ДС'1'вами GOH, 825
зыватъCJЯ Список шрифтов и nо:казы.вать CrIIiCOK :всех: шрифтов, УСТЩIOБJIeннblX на
маmиве .кОIfечного' nOJIЬЗователя . На рис. 20.9 Дf\МОНСТРИРУется ЛОГИ8а м'emo" 01\0-
торам идет речь.

- . " ,-,, .-,' ...'--:"1


-~'- '- - ' -" ~ ~ _.----,
, ~
' -~'1
х

, ,_ _ .J

Рмс.20.9 . Менюлраекта FontAp!:,

Чтобы начать реализацию ПРИЛQжеmш. добавьте 'в форму член Timer (с именем
s,wellТUfler). ссх:рон:у (s,t 1: Fоп tD'ace) для представлеl-lИЛ теиущ.еrо н:щванщ:r гарниту­
ры .IПpИ'фта и 1~елое число (swell Va l U.e) ДЛЯ ПРе'дставле1:nЩ веJtИЧИНЪ! и:орректиров-
1m для размера шрифта . В он'не прое"тированйл форМhI сконфигурируите Ti,m,e r
ТаЕ, чтобы он генерировал соБЫТ1re T.ick На)Кдые 100 МИЛ:1:Щсе.кунд.

pjjb1i c pe,rtial c l <"iS S MainFo1'l11 : FОЛ;n


{
p:ti va te Ti.me,t $w,,,,ll Ti rnet: = new Ti mE:,j; О ;
pr i 17':' Т е J. n t $we ll V ,31 пе '1
privat6 Bt r:i ng S L rРi:шt Fа:,се = "W in gЮings ~;

риыl c MaifjForm ( )
{
I Л.i t i a l i z е(lОl,lРЬЛtП t () ;
Ва с kCo,l о т "" Со} оу .,8 0 0" y,dew;
С'е пt,еу"ТоS с r е е п () ;

11 КоифигурацИR таймера.
swel lTlme:r .EnаЫед =tr це .;
s w,el LTime r. I п t еr.таl = 100,;
.s.we llTlme r . Ti.ck += пew Еvе.пct И а:п,dlеr (swtoil Timer Ti ck ) ;

в обрабОfчинt события Tick увеличы'e значение члена swellValue на 5,


Напомним. что целое "'JИсл6 9we l1V'a l u e будет добавля:гься к теRyЩему размеру
ШРИф'l'а. чтобы обеспечивалс'Я простой эффеR1' анимации (преДПUJЩгаетсн. что
э w еl1Vаl1Jе будет ограничено сверху манcимa.trьным 3ilэ,ч:еJ;mем 50). Чтобы не до­
пустить мерцания. которье может nPОИС:ХОДfiТЪ при перерисовхе wеИ облаети R1IИ-
826 Часть IV. Программирование с ПОМОЩЬЮ библиотек . NEТ

ента. при вызове Invalidate () будет обновляться ТОЛЬКО верхняя прямоугольная


область формы.

pr i v ate void swellTi mer_Tick(object sender, Even tAr gs е)


(
11 Увеличение те1Суще.го значения swellValue на 5.
swe llValue += 5;
11 Если значение больше или равно 50, сброси'l'Ь его в ноль.
if(swe11Va1ue >= 50) swe11Va1ue = О;
11 Обновление мииииanьной области ДЛЯ уменьшения: мерцания:.
Inva l idate(new Re c tang l e( O, О, ClientRe ct an g le.Wid th , 100));

Теперь, когда с каждым циклом Ti mer обновляются верхние 100 пикселей об­
ласти клиента, нужно найти что-нибудь подходящее для визуализации. В обра­
БОТЧИRе Paint формы создайте объект Font на основе выбранной пользователем
гарнитуры шрифта (она выбирается с помощью соответствующего пункта меню)
и текущего значения swellValue (оно задается таймером Timer). Настроив объект
Font, поместите сообщение в центр соответствующего ПРЯМQУГОJlьника.

pr i vate void MainForm_ Paint ( ю Ь j есt sender, Раi п tE'\7епtАrgs е)


(
Graphics 9 = e.Graphic s ;
11 Размер шрифта должен нахоДИ'l'ЬСЯ: в диапазоне 0'1' 12 до 62,
11 в зависимости 0'1' swel1 Value .
Fo n t theFo nt = n e w Fo nt (strFontFace, 12 + s wellValu e )i
s t ri n g message = "привет GDIt";

11 Bывo~ сообщения в центре ПРЯИОУГОЛЬНИ1Са.


fl oat windowCenter = thi s .DisрlауRесtалg l е.Width!2;
Sizef stringS i ze = g.MeasureString(message, theFont);
f loat startPos = wi nd owCen t er - (stringS iz e.Wid t h!2);
g.DrawString(message, t h efont, new Sol i dBrush(C o lor.Blue),
startPos, 1 О );

Легко догадаться, что при выборе пользователем кшщретной гарнитуры шриф­


та обработчик Cl i cked для соответствующего варианта выбора И3 меню должен
обновить строковую переменную fontFace и перерисоватъ область клиента, на­
пример:

p riv ate void aria l ToolSt r ipM~nuItem_Click(obje c t sender, EventArgs е)


(
strFontE'ace = "lI.ria1";
Invalidate () ;

Обработч:ик Click для пункта меню Рост? будет использоваться для запуСl\.а и
останОВRИ процесса увеличения текста (т.е. ДЛЯ разрешения и ОТlUIЮчения анима­
дин) . Здесь используйте свойСТВО Enabled объеlпа Timer тю{, нак показано ниже .

private void swe l l Too l S tripMenll Item_Click( o bject s e nder, Even t Args е)
{
swel lTimer .En abled = r swe l l T imer .E n abl e d;

L
Гл.ава 20, ВиэуаJ1иза~я графичес~их даннщ Gредатвами GDI+ 827

Cnисок установленн.ЫХ шрифтов


Давайте расширим программу тан. чтобы она отображала множест~о уста­
новленных на .машине трифтоs С ПОМОЩЪ10 типов и'З npOCTpaHCTJja имен
Sу:st.еш ..Drа'wi!'Jg.':!:'ехt. Это пространегво ймел содеР.ЖИТ nабор типов, которые
можно :использовать ДЛЯ полy-qения СПИСI{a шрифтов, установлeIшыхна ц~евой
машине, :и ДJJ.Н раб.ОТbl с ними. дi'm нашИх целей ДОСТ3'Гф'lНО рассмотреть только
масс lnэt,Й.lеdFОDtСо'll€·сtiа.n .
.кагдз пользователь выбирает .}1З меню Сервис~ Спи.о0!( шрИфТОВ. соответстцу­
ющий обработчик Clieked создает .экэеМПJI.lfp нлассэ. lf1st.a llecl'Fon.tCollectioQ.
Этот ЮIзес СQдержит массив 'Fопt Fami lY, ~дставляющий набор всех щрифтов.
установленных на целевой маш:ине. и З"ГDТ MaccIm можно получить. ИСIЮлъзу.а свой­
C'ffiO l.I!st.al1еdFОDtСо11е:сtion.F.алiНiе s, С помощыо свойcтnа FontFamily.Name
в.ы' м.ож€>т€ извлеЧь название rарнитуры шрифта (например. Times New Rощаn,
Ar1al и т.п.J.
Добавьте в форму привa:rный чnеu-строну с именем installedFonts д;ля хра·
ненйs -названия гарнитур. Проrpаммная лопша обрабоТJ(;И nYН:КTa меюо Спиоок
шрифтов создает :шземnляр' типа IпstаllеdЕ'опtСоllе сt iоn. <tИтает l<JМЯ КаждрТ'О
ЭJillмен.та и добaвmreт новую гарнитуру в приватный 'V1eH ins'c:alJ..edFo!1'ts..
риЫ ic parti&l с lазв ~9iI1F o rm : FOYt)l
!
/ / СодеpJlOll'l' CnИQОЖ ШРИф'1'OII.
private- st.riI'ig il1SLаJ,1l3шолt?;

/./ Обрабо'.l'ЧЮС неюо дnя: пmIучеНИjl СЩI[сJt,а шриф.'1'ОВ.


p:ri vatE' "О id ffin [];COI1fi-gShОWf\СЩ;t.s _ С1 ick€:d (oj:>.je;ct З'Ещсiщ:,
.EventArgs е)

InstalledfOJ1t.Cbllettion .fQлts = ое>'! lnsta11edFQ1').tC.olleC'\i;i~n () ;


rorCint i = О; i < fOQt,s,F'ami1ies.Length.,} iH)
iпsti'il1еdF,щts += f ont s.F'amilies,[iJ .Nam.e -t. n " ;

/ / На 9'1'0'1' pCl.'Э IfY*RO ОCSИОВИ'J.'Ь BCJD оБJIаcmьюmеит&,


/ / поохоЦЬ)!:у oI51'1:0Bnll(e'.l'~ С!1'рОХ& installedFonts в вmквeil ча~
/I обnас.~ JUIИеИ'1'Jl.
I г.vаlidаtе () ;
}

Заключительной нашей задачей будет отображе:ние строки 1 nsUJ.J,ledFont~ в


облас'FИ клиента, раСПОJI(»кенноИ сразу ~-e под той 'Частью экрана. ltОТОРЭДI ИСDОЩr
:зуется для движущеtоси 1'ехста.

priv.;;l1;.$ v:oid MainForm~Paint '(object Sбldеr, РаiпtЕvеntА'Igs е!


{
G~apbic~ 9 = e.Graphi es ;
Fo.n t tbefont = new F9nt (вtrF()nt ~а-се, 12 + S~lеllVаl.1..!-е);
st.r jл .:::! me~sage = "ЛР1llВ'ё! 'Г G:DJ+";

/I О'1'обра*Е!fDIП! со6бш.e.и;иil. • ~~еОюtа.


f10at wi.rrdowCenter = this .. Disрlа::{Rес·t.аиglе .Wid..t.bl2:
S~ z eF' s t,l пqs:J: ze= ,е. Grарhiсз .. Мед<; u:t.eSt ,1 nч (mess'age, thе'F'О,Т1.С):
828 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NEТ

float startPos = windowCenter - (stringSize.Width/2);


g.DrawString(message, theFont, Вгushеs.Бluе, startPos, 10);

11 Похаз сnисха устано:вnенных шрифwо:в в пряиоугоnьнихе


/ / под ДВИЖУЩИМС51 !1'ехстом.
Rectangle myRect = пе. Rectangle (О, 100,
ClientRectangle.Width, ClientRectang1e.Height);

// Захрашивание данной обnасти формы черным цветом.


Ч. FillR€ctangle (пе. SolidBrush (Color .Black), myRect);
g.DrawString(installedFonts, пе. Font("Arial", 12),
Brushes.White, myRect);

Напомним. что размеры "грязного прямоутольника" проецировались в верхние


100 пикселей области клиента. поскольку обработчик TiCk обновляет только часть
формы, остальная ее часть при посылке события Tick не перерисовывается (чтобы
оптимизировать задачу визуализации в области клиента).
В качестве завершающего штриха давайте обработаем событие Resize формы.
чтобы гарантировать соответствующую перерисовку в нижней части прямоутоль­
ника клиента в том случае. когда пользователь изменяет размеры формы.

private void Hi:!inForm_Resize(object seader, System.EventArgs е)


{
Rectangle myRect = пе. Rectang1e (О, 100,
ClientRectangle.Width, ClientRectangle.Height);
Invalidate(myRect) ;

На рис. 20.10 показан результат (с текстом. представленн:ым шрифтом


Wingd1ngsl).

Рис. 20.10. Приложение SwellingFontApp в действии

Исходный код. Проект SwellingFontApp размещен в подкаталоге, соответствующем главе 20.


'Глава 20 , ВI'!,зуаnИЗсЩI4Я графических Д~HHЫK оредствами GPI+ 829

Клаос FontDi'alog
КaIt вы можете догццъma:IЪСЯ. существует и класс диа.лаroВQГО окна для :нас~ой­
ки шрифтов (Fof1tDialoq). B~ этОго окна .Dохаэ!аН на lJЙ{;. 20.11.

Рис. 2О.1 t. Диалогоаое, окно Шриф1. Windows Forrns

ПОДОQI;IО типу ColorDlalcrg, рЗСC1l.ю:греНllОМУ в этой ,главе ВЫIiIe, для раБОТЫ С


F'01,1tpJ..a log Нj'Жl'JO просто ВЫ8Ват.ь метод 'SbowDialog 1) . Используя своистОО FbI1t.
МОж;f1О извлечъ тенущие хаРaRТерис'.rИltIi UIpифта дЛя иcnолъзоваНRJiI в при.iIоже­
ШIИ. ДгJЯ пр.и:мера расамО11рWFе(:Де,цующую форму. имитИРуюЩ}"'Ю логику npедЬЩУ­
щего пр6еltТfl ColorDlg. При щелЧlШ ПОЛЬЗ0в<Ш'еля в moБОI\i месте окна формы ото­
бражается диаlIопmое окно Шрифт и вмrвoДИТС.II информация Q текущем выборе.

"p·u bl i 'c pa.rtial сl as-$ ~1ai!1Fbrm ; FQ'J:m


j
privar:e F0!1_t:Dla1o:g fQDtDlg = не", FоГ!tl,i..al0Щ (');
private Гог..!: c;·urrF.oo:t = ne,W "P cmt ,( "Time..s New RЩltал"/ 12');
рuЬИ с M.;'lir"FQrт ( )
!
IйJ"tiаli2$Сащроrt, еГi t ():
С'епtеrl'О!ЭF.:rееn (.1 ;
)
prii;late 17'Oid MailTF'o:rrn Mouвe,DoW"Fl (otJject 5ender, MeuseEvenLArgs е)
! -
if (fоnJ:lDlg.$]юwDiаlс)('1 () != bialc-gRе,31J lt .С а:п.сеl)
[
е:ш:rf'(щt = f0лt_D l g . F'Of!t;
th.i s ,,'Text ~ stx·ir",g. F'G:tma t (" SEJlecte~d F'oni.: {О)", сщ"rFо,,-t);
1I1v<,ilidat E ();

}
ргivа.tе void маiг,Р-о.r:m_РeJ.nt'( оЬjе,сt $~det , РаiПI;.Еvепtl\гg-s е)
j
Grapr,ics g = e.Grapbic~'I
g.Dr""wstгi:tJg("ПРОЕ~РJ',:.д ... ", cu.rrF{)nt, Бrцзhеs.Вlасk, О, О) i
830 Часть IV. Программирование с помощью библиотек .NEТ

Исходный код. Проект FопШlgFогm размещен в подкаталоге, соответствующем главе 20.

Обзор пространства имен


System. Drawing. Drawing2D
Теперь. Еогда мы обсудили возможности использования типа Font. следующей
напrей задачей будет рассмотрение объектов Реп и Brush, предназначенных для
визуализации геометрических шаблонов. Бы. I<онечно. можете ограничиться ис­
пользованием только вспомогательных типов Brushes и Репз для получения уже
сн:онфигурированных типов со СIШошным цветом. но вы должны знать о том. что
в пространстве имен sуstеш.Drаwiпg.Drаwiпg2D есть очень много и более "экзоти­
ческих" перьев и кистей.
Это дополнительное простран:ство имен GDI+ предлагает ряд классов. позво­
ляющих изменить форму пера (треугольник. ромб и т.д.). указать текстуру I<ИСТИ
и работать с векторной графикой. Некоторые базовые типы. о ROTOPbIX вам сле­
дует знать (сгруппированные по функциональным возможностям). описаны в
таБЛ.20.6.

Таблица 20.6. Классы System. Drawing. Drawing2 О

Классы Описание

AdjustableArrowCap Ислользуются для изменения формы концов линий для перьев.


CustomLineCap Данные типы задают, соответственно, регулируемую стрелку и
пользовательскую форму конца линии

Вlепd Позволяют определить шаблон смешивания (и цвет) для использо­


ColorBlend вания с LinearGr:adientBr:ush
GraphicsPath Объект Grap11icsPath представляет серию линий и кривых. Этот
GraphicsPatbIterator класс позволяет добавлять в траектории геометрические шаблоны
PatlJData практически любого вида (дуги, прямоугольники, линии, строки,
многоугольники и т.д.). Pat11Data содержит графические данные,
формирующие траекторию

HatchBrush Экзотические типы кистей


LiпеаrGrаdiепtВrush
PathGradientBrush

Также следУет знать о том. что пространство имен System.Drawing.Drawing2D


определяет набор перечней (DashStyle. FillMode. HatchStyle. ИпеСар и т.д.). ко­
торые используются вместе с указанными в табm,ще базовыми ТИПаМИ.

Работа с типами Реп


Типы Реп GDI+ используются для построедин. линий, соединяющих конечные
гочки. Сам по себе тип РеГl не СЛИПIRОМ полезен. Для выполнения визуализации ге­
ометрической формы на поверхности ПРОИЗВОДRОГО от Contr:ol типа действитель­
ный тип Реп следует направить подходящему методУ визуализации, определенному
классом Graphics. Вообще говоря. с объектами Реп обычно используются методы
Глава 20, ВизуализацИR гр,афич~ски)( данных СР~Аствами GDI+ 831'
О.! ~,wXXX ,() . ПО3ВDл.вющие 'ОТобразить некоторый набор 1IйFtий на СQQ!ГВeТствующей
графиче.скоtl пОверхностIl.
Тип Реп onpeдeд.lIt.'Т .небольшой нэбор нонсТрукторОВ. n08ВOJrяющих задать на­
Ч311IЪНЫЙ цвет и ширину пера. По болЬшеи части функциональные возможности
Рег! задаютс.ll свойствами, :n:оддерЖИваемым-И данным тmюм. ОШIсаН>!Ji! не.которых
из этих tв<iЙс'm uредпагаютса в там. 20.7.

Таблица 20.7. Свойства Р(':;n

Свойства

Br1,1,s h Оп'ределяет тип Br ush Дn~ ИСПОЩ,ЗОВaJiИЯ с да1~liblМ типом Реп

c o l ~T OrrpeдemleT тип Соlоr.для ИСI10ЛЬ30Баt1ия С дaI'IHblM ТИПОМ Pe~

.cusJ:omSt",rtC,a:p Чщает или устаliавлива:ет гrapaMeTpы ПОIlbзеватепьскрго СТИЛЯ концов ли­


J:: ust' om~!'JdCa'p ний. , со':щзваемыx с ПОМОЩI.FQ AaHf<tOr(j типа P'ElГi. С;ТИ/lЬ J((i)НЦЩi ЛИНИЙ - это
просто термин, иопощ)зуемыИ .для обозначения 'Того' как :ЦОЛЖj;Н Вt;.lглядетt;.
НВ'lальныiif и заКЛЮLlительный "'штрих" даннфга пера. Эти СВОЙ,ствэ позволя­
ют -строитЬ ПОllь~оваiелЬСl(lILе СТWDII нз'iЭЛЭ И конца линиiil.д;ля тИ'пОв Реп

'4итает И/J1I1 vстанавл~эе.т Т1ЩIаметры стиля .... онцоВ ЛИНИЙ 1 ИСПСJlhзуеМОI'Q


ДJ1я преРЫВИ{;Т~1J( ЛИНИИ , создаваемых с ПОМОЩЬЮ дЩ'mого типа Ре п

D:as:hl'.at te! n ЧИТClет или уаraнавливает массив пальэоват-еЛЬСКОk\ маски доя риr.аваНИR
преР.ЫВИСТI>I)( ЛИliиJi1 . Соответствующие "тире " i:КJ111ДЫВЭЮТСЯ из Ge.гментов
лиНИЙ

Das±t$'t yle Читает .и:лt:1УСТЭflавливаэт параметры отиля., используемого для прерыви­


стых ЛИНИЙ" создаваемых с ПОМОЩЬЮ AaHi40r.o типа Ре п

StЕИ1tОар ЧViтаеi И:ЛЙУСТЭН8ВЛИВа.ет aCТPOEiHHblJ/l атилt;. -КОНЩЩ лИ~ИЙ, создаваемых с


'Ещ:iсер пЬмоЩЬю ДЗНflt)ГО типа Реп. Стиnь I(О!ЩОВ лl'tн'ий Реп УСТ):IнаВЛ\'1вается' в
L.i aeC;apJ, определенным!! пространстве имен'
соотвеТGJ1JJilИ с пере.чн.ем
Sys tem .Dr,aw.1 t1.g. br аwiпg2D
Wi ,dt~'l Читает WТи устанавливает ШV1:РИ11У даннаго Реп

DashQff ;;:et ЧИ'ТaRТ или устанавливает расстояние от начала ЛИНkИ ДО начала шаблона
прерывиетой лt.l1НИИ

llоr.шнте о ТОМ. Что gnоба.во'К ~ nщy Р-еn Б GDI~· предЛ.агаетС'я ko-ллеКЦИJi р'е rч~.
Использу.я ряд статических ~оЙст.в. вы мощете измеч:ь Н3 этой коллеКЦJm объ~
Реп (йЛИ tI}'ЖН'ЫЙ !IВeT) ~н:a лету", не создавая ПО.1Ibl!ователрсКИЙ 'Тип РеП ВруЧНУЮ.
ОДНШ;:О следу~ з.нщъ. ч:tо воэв-ращаеl\o,IbJ.Й: цри этом ТИП Реп Вl,::егда имееr ШИРЩ:I)'.
равяуlO 1.
Если ВаМ поющобятся: Jt<tКие-тр ;ш,зотичеекие перъя. вы ДОЛЖJ!Ы создать тип
Рео вручную. ПреДОМ'авив вам :';lТy И1'lформацид>, я теперь моту Пf'реЙТ1! Ji: цо­
строению примеро'f! геОМt"Трических изображ.енИЙ с Щ>МОЩЬ(О пр@стыlx типов РеП.
Предположим. Ч:'tо у нас , есть rnавный 06ъеI(Т р'ох-m, сп@~ный отвеЧаТЬ на запРD~Ы
визуаJJJlзaцrol. Реализация ответа вьrrляwп так.

p rivat.e v0id' Ма i лFо:rnt_Р a.L"It ( 0bjec t sэnder, РiiН 11t:Е:\i'елt.~Б g8 'i!1


{
Graph.i ~$ .g = е, G:r:apbic's,
11 Создание большого пера с:ине'Го цвета.
РЕ:!1 bll1Е:l'еп= r'J€"w Ре!' (С 01 0.1:'. Нl \1е" J' О) ;
832 Часть JV, Программирование с помощью библиотек ,NEТ

// Попучение готового пера иЗ типа Pens.


Реп реп2 = Pens.Firebrick;
// Визуanизация нехоторых шабnонов.
g.DrawEl1ipse(bluePen, 10, 10, 100, 100);
g.DrawLine(pen2, 10, 130, 110, 130);
g.DrawPie(Pens.B1ack, 150, 10, 120, 150, 90, 80);

// Рисование пурпуриого по.пигона с ПУНlCТИpной границей . ..


Реп реп3 = new Реп
(Co1or.Purp1e, 5);
репЗ.DаshStу1е = DashStyle.DasbDotDot;
g.DrawPo1ygon(pen3, пеи POint[] (пеи Роiпt(ЗО, 140),
пеи Point(265, 200), леw Point(lOO, 225),
new Point(190, 190), new Point(50, 330),
new Point(20, 180)});

// ... и пряиоугonьниха, содержащего текст ...


Rectang1e r = new Rectang1e(150, 10, 130, 60);
g.DrawRectangle(Pens.B1ue, r);
Ч. DrawString ("Эй, вы, там, наверху! .. я вам привет передаю." r
new FQnt("Aria1", 11), Brushes.B1ack, r);

Заметьте. что тип Реп, арименяемый для отображения МI-югоутольника, исполь­


зует перечень DashStyle (определенныIй в System.Drawing.Drawing2D).
public епит DashSty1e
{
Solid, ОазЬ, Dot,
DashDot, DashDotDot, Cllstom

в дополнение к улtе имеющимся элемента.м: DashStyles вы можете определить


пользовательские шаблоны, используя для этого свойство DashPat tern типа Реп.

private void MainForm_Paint (object sender, PaintEventArgs е)


(
Graph ics g = е. Graphics;

// Рисование прерывистой J1ИНИИ BДOJJЬ грающы формы


// по поnьзоватenьсхоиу шабnоау.
Реп customDashPen = пеи Pen(Co1or.B1ueVio1et, 10);
float[] myDashes = { 5.0f, 2.0f, 1.0f, З.Оf };
customDashPen.DashPattern = myDashes;
g. DrawRectang1e (c-ustоmDаshРеп, C1ientRectang1e);

На рис. 20.12 покаэан вьтод этого варианта обработчика событий Paint.

Исходный код. Проект CustomPenApp размещен в подкаталоге, соответствующем главе 20,


Гl1&ва 20, 8~зу&лизация графичеаких данкых средствами GЩ+ 833

Й, ВЫ, там,
fl8sep.xyl. Я 83f11
привет передаю.

Рис. 20.12. Работе с типаМи Ре n

КОНЦЬ! ЛИНИЙ
Если paCCMOТp~ь ВЫВОД предьЩу'Ще'ГО примера. }JbI Должны заметить. что на­
ча.lIО 1i RОЯ6r...:r; RШRДОЙ дmuщ там оформлен Bno.т"IНe стю-!дартrю - ЛИНИЯ "С'реааетсн"
лод уt·лом 90 R ее tronpа.вленИlO. НО. ИСПШIЬЗУИ перечень LiдеСар. вы имеете .воз­
й

можноеть создавать об'РеRТЫ Реп.. ДеМ01lстрирую:щие иное J;Jоведен:и:е.

public e-num LineCa-р


r
Fla t. Square t Rou-np.
'TriaГlgle, NoAТlcher:,
SquateAI'l~hO):, R(111I1·dМсhщ:,
D.i",юоndAnсhо,r·, Аrrоw·i\nюhо·J:',
AnchQ:r;M6.sk, .custanJ
1
СJlе,/o/lOЩ~ прилощенце отображает на'бqр JШНИ~. по О"Jер:еди используя каж­
дый из стилей. LineC:ap. Ко.в:еЧIЩIЙ Рei3ультаr шта,зан на РИС,. 20.13.
В соответствующ~м прогр~~м над!:' просто ИСПI)JlliЗуетсн ЦИR.JI по всем Чirе­
нам п~ре'ЦШ LiцеСар с "'ЫВОДОМ имеJЩ СООТБеl'СТВРО.щ~го элемента [НEiIIример-.
f>J:'tPwAnchor) и отображением JlИНИИ с соответствующим ОфОР1VDIением ее :КОН­
щ:ш.

private voi.d МаiлFоrro_Раint (.objec·t sender, Е"аiIltЕvелtА:i:i}s е)


{
GFapbir:sg =е. Grap'hi:c,s;
I?e'f.! thePea = new Реп (Colot. Бlа<::lt, 1 О) ;
int y{Jf.fset = 1 О;

/I По.пучеаие вi:ехraеиов переЧRII Ыпеса.р.


Array obj = Enum,GetValLtet;! (type 1) f CLineC$.p'.J) ~
834 Часть IV. Программирование с помощью библиотек .NET
r
11 Рисование линии ДЛII ;цаиноl'О ЧJIеиа LineCap.
for(int х = О; х < obj.Length; х++)
(
11 Получение сле;цyxlЦ8l'О С'rИЛЯ конца ЛИНИИ И наС'.1'рОЙ1Са пера.
LineCap temp = (Linecap)obj.GetValue(x):
thePen.StartCap = temp;
thePen.EndCap =temp;

11 Выво;ц имени иs перечИя LineCap.


g.DrawString(temp.To String(), new Fo nt("Times New Roman" , 10),
new SolidBrush(C o l o r.Black), О, yOffSet);

11 Рисование линии с COO!l.'Be!l.'C!1'В~ С'1'ИJIеи 1Сонцов.


g.DrawLine(thePen, 100, yOffSet, Width - 50, yOffSet);
yOffSet += 4 О;

Square

ltound

NoAncl1cn

Squaш\ncЬоr . . .- - - - -•••

RoundAncl1or . . . .- - - -• • •

I>WnondAncl!orAe-----е.

• ,
AncЬсrМак

Сшtоm

Рис. 20.1 З. Работа с концами линий

Исходный код. Проект РепСарАрр размещен в подкаталоге, соответствующем главе 20.


Отава 2Р. Виз,уалиЗ'iЩия ГРllфичеСUIХ дaHftЫX GреДС1'sами GDI~ 835

Работа с тип' ами Brush


'l'иnьi. прщrэвоДЦые от System.Drawing,Brusm, испальg:уютс.и ДllЯ ЗЮ-1i:шнения
имеIOщегося региона заданным Ц1ЮТОl\lS, У<IOРОМ или ИЭОбражением. Сам Масс
BrU'sh явкщется a6CTpaд:rнЫM типом, ЦGЭТОМУ он не позволяет соз~а1ъ соответ­
ствующий экземпляр непосредственно. Одцано Br'~sh может итратъ роль базово­
го масса МЯ родст:венlIbPt ему типов JЩ(,"r~ (например. SоlidВrJJзh. Hatc>h Brusb,
LiпеагGгаdiеntВЛ1S.h и :r,lI,.}.. Кроме ОТВQс,ищихся R Brusb TIlII.OO. просТранство
имен Sy·stelТl.. DrаЮп.g определяет также двц вспoмvrательнъ1Х Maf:ca, ВDзвршца­
ющие IШсти.. уяtе ско.нф;игурировaнFlЫ~ с; помощью ряда статиqе.ских свойств: эrrо
кmlССЫ Br"1Jshe.s и ЭуstеmВгushеs. Так ИJlИ иначе. пeлyчnв ЮJCТЪ. вы полуЧаете
во'Эможнос1'Ь ВhX.зват.ь любо(r из 1j{~OДOBFi ЦХХХ ()ТИna Graphic,s .
Интересно то, что на основе кисти вы МQжете создать польз:овател};СКИIJ тип
Реп. Подобным образом вы ~o~e создать себе л:iOбую подходящую киСть [КИсТЬ,
.которая "рисует" точечное изобрaжeJЩеl и въmолйять ви..wализацию ЭЗДRНfIЫX ге­
о.меТРИ'Jеских шаблонов с помощью СJ>OвфигурироваНiЮl'О объекта Реп. )J;nя при­
мере расс.1I.10три.те следующий .цариант программы, в котором ЙСШШЬЭУ10ТСЯ раз­
ЛИЧНЫе кист:и.

p.rivat·e void Мai..u:F'·ОIПl_Раiпt (object S,ehder, PaintEvent}'Irgs $)


{
Graphic,s g =о е . Graph:iQ.s ;
11 Соз;;а_е SolidВxush сивеro Ц1Iewa.
SolidRrush :bli:l..eBrush = new Solt,d ErUsh (С'оlЩ·.Вluе);
{I Пonуче.вие ro'1'оаой IШО'1'И'.ИЗ !t'ИD& Вruз·hеs ..
SQlid.Вxush реn2 = (gD.lidВrush)Br'ushes· .Fi .rebri ~ k.;
11 ВНSУaдJJf!l!ЩIQJ .exo!t'Op.iX araбnС!воа.
Ч .• Fill$l1ipse (ЫоешusЬ, 1 01 10, 100, 100);
g.EillPie(BT1Jshes.Вlack , 1,50, 1'0 , 12П, 150,90. ВО');
11 f>Исо.ание пУрDYP~оrо пcmиrона ..•
Sоlid:ВrusЬ b.r:U B r,з= па ....
801idB:tush(Color.PI1'.rple);
q,FillРоlуg:с,n(Ьrl1'S)r.З, new Point[J (леw Роi..nt(ЗО . , 140:),
ne'w Ро ± пt\2б'5, 200), new Pai.IJt(lOa, 225) (
new Ро iп t (190'" 19'0 1, new Point! 50, З"Зra) ,
l1eW POlht (20, 16 О) ) );
/1 ,.. м ПРИМОУЖООJJьвика, СОДepY3ЩEiЖ'о 'J!&XCW, ••
Rectan91e r = new .Rесtалglе 11 50 , lQ, 130, б'О' );
g.FillRесtаnчlе(ВrushеS,Вluе, 1');
9 • ]')1:'awStrin'g (" Эй . ;ВЫ, T<tM, наверху 1 ,. я вам ПР;ИВЕ! <Г пере.I\аю.",
new FOJlt("Az:ial", 11), B;r:ushes.White, r};

Нщхеюоъ, щ.l СQГJЩ~И1'есь, что .это Dpшroжение поч.ти ИДе1-JТИЧНQ еоздаНflОЙ Bыme
программе Сu' э.tоrnРеnАрр. но иcnолъаует методы Pi.llXXX!) и типы SolldBrush
вместо перьев.и соответствую.щнм им :методов Dra wXXX О. на рис . 20.14 ПОlЩЗЗН
соотве~твующий вы:вОд.

ИСХQДНЫЯ !СОд. npOBI(Т Sо)idБгushAppразмещВJi в подкаталоге, {;оnтветатвуюЩем главе 20.

1
836 Часть IV. Программирование с помощью библиотек .NEТ
r
~,. SolidB'lJShдpp GJI5I!'8J
-=t!1, вы, ГJr,'
H-rс,I:IJ '11 r;~ Б-1r,1
П! 111f I-'T 11Г'llt Д 1М J

Рис. 20.14. Работа с типами Brush

Работа с HatchBrush
в пространстве имен Sуstеш.Drаwiпg.Drаwiпg2D определен производный от
Brush тип с именем HatchBrush. Этот тип позволяет закрасить регион. используя
один из (очень большого) набора встроенных видов узоров. представленных переч­
нем Hatc]-lStyle. Вот часть соответствующего списка имен.

public enum HatchStyle


(
HorizQntal, Vertical, ForwardDiagonal,
BackwardDiagonal, Cross, DiagonalCross,
LightUpwardDiagonal, DarkDownwardDiagonal,
DarkUpwardDiagonal, 1ightVertical,
NarrowHorizontal, DashedDownwardDiagonal,
SmallConfetti, 1argeConfetti, ZigZag,
Wave, Diagona18rick, Divot, DottedGrid, Sphere,
OutlinedDiamond, SolidDiamond,

При :конструировании HatchBrush вы должны yI{азать цвет ДЛЯ переднего пла­


на и цвет ДЛЯ фона. которые будут использоваться при выполнении операции за­
крашивания. Для примера давайте немного подкорректируем программн:ую логику
из приведенного выше примера РепСарАрр.

private void MainForm_Paint(object sender, PaintEventArgs е)


{
Graphics 9 ~ e.Graphlcs;
int yOffSet = 10;
11 Получение всех членов перечня HatchStyle.
Array obj = Enum.GetValues(typeof(HatchStyle));
Глава 20. 8ИJ.уanиаация графических данных средствами GDI+ 837
11 Oirобpuaeвие 08lU1oa AI1.А Qep8~ 5 ~...еиий и.s Hatchstyle.
fo.r (.iл·t к = О; х < ;'.; хН)
{
11 I(ОИф~iЩIUII I!:ИС'1!И.
HatchStyle tem.p = (HatcnStyl€) оЬ] .Getvаlце~х);
Нэtс:;hВru.sЬ
t.heBrush = new На t:chBr\.!sh (te1llp I
Color ,·Whi te, Оо1ох .ВLзск ) ;
11 Вывод mtеви И~ rt8реЧluil Иаtchstуlе.
g .. DrawStirlilgH;emp.ToStringO, n~W FO·IJt('·J'.im~s New· RDJJ1i!J1", 10),
Б'l'uвhев. Black, О, yOffSet);
I! За~асJtа. oIh8Jt'1!81 .tIDiЦ.ХОДJlll8' Jtис'1!ыо
9. FillEllips-е (th:еБr.\)sЬ, 1.50, yO:ffSet., 20-0, 25);
yOffSet += 4 О ..

в iЖне ;Вывода будут noкаээны <iaIюШlенйые овалы для первых пяти значений
ВЦЦОБ штрИхОВRИ (рис. 20.15).

Рис. 20.15-. Некоторые СТ\о1ЛИ щтриховк~

ИсходНыi ICОД. I1роект BrushStYl'es раэмещеl1 в ПЭД!QiТа:лощ СООТ!jетствующем ГЛI!В8 20.

Работа с TextureBrus.h
тип ТёхturеВ.rush П()ЭВоJiяе:t СВJiэать е дистыо точечное изображенце. ч:'ГОбы
затем :аспоm.зо:ватъ ее Б операциях ЭaJ<Рашиванwя. Чуть поаже будет подробно об­
суждаться класс lтаче (изображение) GD1+. ТИРУ Te.xtureB:tl)sh предщ:тавл.я;ется
CChlJ1Кa на lщаqе. ~исполъэуемая этим ТИПОМ в те"lеl-Цfе всего цикла его сущ~ство­

ванил. Само ИЭООРQЖeние обычно хранится в некотором ЛЩtЩIЪНОМ файле (".Ьтр.


'" .чН. ".Jpg) ми же
bc-rроено» КОМnQЦОБочны}t блаr( .NЦ.
Давайте постр@им пр~ер ПРИJilоm:ения. исподьзующего 'rИП Text,ur.eBrusrn.
Одна 1ШСТЬ будет испол:ьэоваn.cя ДJDI закраеки областY.l 1QIИентд изобрщю:щием I!<!
файлэ: сlщ:Jdэ. Ьтр. в то время дa~ другая кисть буде!' ВЫВQДИТЬ текст ·С UQМОЩЬЮ
изображения. находя1Цегася в файле Зоа,р Ь1.1ЬЬ} ез. ~ы1\•. Соотвеп:тв.ующиЙ ВЫВОД
подаЗal{ на рис. 20.16.
r,

838 Часть 'У. Программирование с помощью библиотек .NET

РИС.20.16. Точечные рисунки в качестве кисти

Ваш производный от Form ЮIасс должен подцерживать два члена типа Brush.
которым присваиваетсв: tfовый объект TextureBrush в конструкторе. Обратите
внимание на то, что конструктору типа TextureBrl1sh требуется предоставить на
вход тип. производный от Image.

public partial class MainForm : Form


(
11 Данные дли: кисти с изображением.
private Brush texturedTextBrush;
private Brush texturedBGroundBrush;
public MainForm()
(

11 Загрузка изобрuc:еНИJI дли: хисти фона.


Image bGroundBrushImage = new Bitmap("Clouds.bmp");
texturedBGroundBrush = new TextureBrush(bGroundBrushlmage);

11 ЗаГРУЗJtа изображения: дли: кисти техста.


Image textBrushlmage = new В! tmap ("S oap Bubbles. Ьmр") ;
texturedTextBrush = new TextureBrush(textBrushlmage);

Замечание. Файлы *. Ьтр, которые используются в этом примере, ДОЛЖНЫ находиться в той же
папке, где находится само приложение (или должны быть "жестко" указаны пути, по которым
эти и.зображения можно найти). Соответствующая проблема будет обсуждаться в этой главе
чуть позже.

Теперь. когда у вас есть два типа TextureBrush, способные выполнить визуали­
зацию. создать обработчик события Paint очень просто.

private void Mai nFo rm_Paint (object sender, PaintEventArgs е)


{
Graphics 9 = e.Graphics;
Rectangle r = ClientRectangle;
11 Рисование облаков в области КJIИента.
g.FillRectang l e(texture(jBGroundВrush, г);
Гnава 2О. Виэ;vаЩ438ЦИ" графичееких .t!a~IHbГX среДС1вами НО1+ 839
11 О!l:'оБРlliJleКИе !l:'eJtC'll& lOfC'.rЫI С T.x~()K.
g. D:c.awStri ng С'Изображения :в ~ачеС'Р:е'е ~С'I'.и. СТИЛЬНО ! " I

n ew Рор. t, (" А:с t,a)3 0',/


11 I
Foot.S1:y le,. Bold I 1'"011 tSt 'Yle. Italic'), t,exturedTex'tBrush, .r);
}

JfCIoAиwii код. Проект Te,uuredBrushes размещен 8, подкаТq,JlОге, соответствующем таве 20,

Работа с LinearGradJen'tBrush
П()('П~ДНИМ из рассматри;вз.емых :в ЭТОМ разделе 1\ипоа будет тиП L i nе а у­
G:r:.ad1errtHruSb. ~О"fОрЫЙ МОЖЦО !tСПОЛМОВатъ тогда. 1I:otAa ,нужно смешать два цве­
та в гр~иентной аахрасне. Работать с этим :rипом так же просто. каж и е OCT3дq­
НЫМЙ тнпами КИСТИ. ВащцыМi моментом здесь JiВл-ается ТО, ЧТО при еоэда'Н;Ии
Linеа:tGr.зdlеI1tвruэh Jl}'ЖНО указать пару пmов Со10Т и значеНие для нацравде­
nия смепщвюшя из п~реЧWJ L,inearGradientМ0de.

р1эыlсc е~UПl' LineEirGra dientMod.e


{
НО.:!;} 2on;tal, Ve:t'tical,
F-оIwагcFDi i'щопаl ( Вас kl~fardDicgt>nai

Ч'liобы: проверить эти знач~щщ. с ПОМОЩЬЮ, Lin'еаrGrа .di,еntБrush Qтобразим


серию ПРЯМОjТО.ЛЬНИ1l:РВ.

pri\, a:'t e void t1a.i.J1F·orm_Pa..int. (obje'c t send'er~ Paint.E;vent.f>.tg-s е' !


J
Graphics g = e .GrapnicSi
Rесtапglе r = J1(eW R'ectangle (10, 10, 100, 100);
11 t'pа.;циеитиая: JtИС~.
L.:i;t;lеdrG.rаШent.ВrIJsrJ th:eBru.sl1 = null;
in t yOffSe.t = J О;
1/ получение чnе,ноз пере~ LinearGrad.ientXod8.
ACJ.'rayot.j = Ehum. Get~7.aliJes (typ.eof (LirrearC;;radie1'1'tМo(le) )';
/1 ~оБР4ЖеRИе ПРRИО~om.юuC~ ,ц,"Ти ~~IOI5 Li.nea,rG.radient:Мode.
for (il'1t1: = О i Х <.: obj , l.eflCj't.h; .х*+)
j
11 I(оКФИIГУР_uюr JCИC'rК..
Li!'!ea'x GradieJJtMQde temp = (Li 'пеаrGrаdiелtНоdе) obj . Get'ilalue '(;xJ ;
'trleBrUEb = aew LiпеаrGradiеоtВrush (х, Col o.r .Gree1'1Y:el1ol>',
С(Но:с • В..!. ue, temp!;
/I В-од r.rне,ни иЗ t;tереч_ LinearGraciientмode .
g.D:r:аwStriIig(tЕ'Щ\р.То8Ui.n.g(), леw F'ont('''T'i me5 New катаn" , 10),
new SolidBrush'(Colc.,r . Blat::k}' , О, yOffS'e ·t):
11 Эа~асха .QpSDIoyr6nь1UUta. DОД:ZОДRщeк ЖИdТJ.,1D.
g. Fll lRec:ta.ngle ~th ев.ш'5Ь , 150, yOffs.-еt , 6'00, ~Щ:
yOfH,e,t += 60;

На рис. 20. 17 ПoRаэанреэультат.


840 Часть IV. Программирование с помощью библиотек .NET

Holi.zOMaI

VtJtjcal

FOIWUdDi.agonaI

Рис. 20.17. Градиентная кисть за работой

Исходный код. Проект GradientВrushes размещен в подкаталоге, соответствующем главе 20.

Визуализация изображений
R этому моменту вы знаете, как работать с тремя из четырех главных типов
GDI+: шрифтами, перыrми и кистями. Заключительным типом, который мы с вами
рассмотрим в этой главе, будет класс
Image (изображение) и связанные с ним под­
типы. Абстрактный тип System.Drawing.Image определяет ряд методов и свойств,
хранящих различную информацию о том изображении, которое этот тип представ­
ляет. Например. для представления размеров изображения класс Image предлагает
свойства Widtl1. Height и Size. Другие свойства позволяют получить доступ к пали­
тре изображения. Описания базовых членов класса Image цриведенът в табл. 20.8.

Таблица 20.8. Члены т~па Image

Члены Описание
FromFile () Статический метод, создающий объект Image из указанного файла
FromStream () Статический метод, создающий объект Image из указанного ло­
тока данных

Height Свойства, возвращающие информацию о размерах данного объ­


Width екта Image
Size
HorizontalResolution
VerticalResolution
Palette Свойство, возвращающее тип данных ColorPalette, который
представляет палитру, используемую для данного объекта Image
GetBounds Метод, возвращающий объект Rectangle, К:ОТОРЫЙ представляет
текущие размеры данного объекта Image
Save () Метод, сохраняющий в файл данные, содержащиеся в произво­
дном от Image типе
Глава 20. В,и:щалиэаL\ИQ rрзф~чеСkИ~ дзниых. с~еДСТ8амlo\ 801+ 841
ПОСRОЛЬRY ЭRземnлnp аБGТРЩij'ЦIOrо ЮIaсса Iшаg-е шшьзя: создать непосредствен­
НО, оБЫЧI:J9 непосредс;:l'В~ННО соЗдil~тС.я ЭRзеМIIЛВР типа Вitщар. Предположим.
что у нас есть не который щ.Щ.СС FOI" т. отображающий три точечных рисунка в
oQласти RJIИента. Ующав для каждото па т.:иnев Bi·tmap ПОДХСЩящиИ файл ИЗо­
бражения . цросто отоБРа;3йте их в обрабо'Г'ЧИКе собю'т.ия Ра i I'lt. 11СIЮЛЬ3УЯ метод
G'ra,phiC5. Dra.w:I.mage () .
public parlial class l'1а.lпР'о·rm : Fd=
(
pri V'at;e Bi tmap (] шуImagЕ>В '" rJ€W Bi trnа'р' (З ) ;
public MainE'iorrn ()
!
11 Зarpузха nохалыuIx ИЗОбражеНИЙ.
ту Images [О] = .new 51 tmsJ' (" imageA. Ьтр") I
m~Imаgе-s.[l] = new Bi'tmap {"lшаче:.а. Ьтр" ) i
mуIшаgеs (2] = new 13i tmap Г' imageC. bmJ'S") ;
Сепtеri'О~СJ:'еэn{);
Initia.lizeComponent /) ;

priv.ate Y'oid Mp.lnFP.rrt\_Paint (ob Ject S'e.n:de.r, Ра iJ'l'tЕvenltArgз е)


{
Giaphics g ~ e.Grapbic$:
/I Вst8У«JЩЗ~ ИSО'брааеИИЙ.
int yOffsc8.t = 10;
f.oreac'b (Bitmap t, in Т!!'уlmаgеэ)
{
g • Drawlmage (Ь, 10', yO,ffset" '30, 9'0');
yOffset += 1 О О;

3амечанмв. Файлы '* .l;,mp, K.Dl'OpbIe ИСПQIlb;:l}чоmя s зтом примере, должны нахо,циться в тОЙ же
папке, где находится оамо ПРИЛОЖfJние (И'nИ' доJ'lж~ы Быь"жестко"' укззаны пути" па которым
эл., изображения MOIКNO найти), СоответсТзующая праблемв будет оБСУждаться в той таве
'1VТb позже,

На рщ: , 20.18- ПОRaЗa:t;[ СQответствующий ВЬПЩЦ;.


НaR()иец, необхо~о {)тметить, ЧТ'О, пеeмuтра на.имя. Bi trnар. этот масс. может
со)tерща.тъ :цзображения. co~a:HeHныe в JIЮбом из :ЦелоI'О рцда фQрматов C*.tif.
• .g1.1;.
.- "Ь
• тр и т.Д, )•

ИСХОДныi'l код. Проеl\'Т Basiclmag~ J;1азмещ~н в' ПОДIG3'Тё!Логе, соответствующем главе 20.
842 Часть IV. Программирование с помощью библиотек .NEТ

Рис.20.18. Визуализация изображений

Попадание в заданную область и операции


перетаскивания для PictureBox
Вы, конечно. можете отображать изображения Bitmap на поверхности любого
производного класса Control непосредственно, но вы быстро обнаружите, что го­
раздо более широкие возможности и лучший контроль дает размещение изобра­
жения в рамдах типа PictureBox. Например, поскольку тип PictureBox является
производным от Control. он наследует соответствующие фующиональные возмож­
ности, например. способность обрабатывать различные события. поддерживать
всплывающие подсказки или контекстные меню и Т.д. Такого поведения можно
добиться и при непосредственнам использовании Bitmap, но при этом вам при­
дется создавать соответствующий программный код самостоятельно.
Чтобы продемонстрировать пользу типа PictureBox, давайте создадим про­
стую "игру", которая будет способна ·распознать" присутствие указателя мыши на
графическом изображении. При щелчке пользователя кнопкой мыши в границах
изображения ВRЛЮчается режим "перетаскивания", и пользователь может переме­
щать изображение по форме. Чтобы сделать ситуацию интереснее. давайте про­
контролируем, где пользователь ·отпустит" изображение. Если это произойдет в
рамдах некоторого прямоугольника визуализаЦии GDl+. будет выполнено некото­
рое заданное действие (оно будет описано чуть позже). Бы. возможно, знаете, что
процесс обнаружения событий мыши в заданной области называется проверlCOЙ
nonaданuя в заданную область.
тип PictureBox наследует большинство своих функциональных возможностей
от базового класса Control. Ряд членов Control был уже рассмотрен в предыду­
щей главе, и это позволяет нам сразу перейти к обсуждению вопроса назначения
изображения члену PictureBox с помощью свойства Image (снова заметим, что
файл happyDude.bmp должен находиться в каталоге приложения).

L
Глава 20, ВизуaftиэаЦl111 графJ,1чесжих д'f.ЩНJ.IХ t;редстваМfi GDJ :t 853

ФIW1 ~ ' Фф006У вн4 ,~


- "P~Ta lIo!1'r1B~''h8PPiDu:ae'" type- "'5ystem-. Dr-aw''-;'~_ е{Бтйiр, -~y,,-t!im_ ~ra.~ r/fi" m1mbtYpe;-"Jрр! 1cit:;;:
wаlщ!> '
!l/k:l2.дgAдА"'R!\Ао-Н~МААОАf>AIJJ../JIAAКfV>.Al>AВAAQ.~I<DgдА)(А4м.~~wwм
gР8Al;1AD/АIСА/4АААР+ААID/Q1АА/4с~РJJ\""'D/д..ФII....р/д."ВР./llllwААN8А/1/1/'fIP//./I//WAP<
,AA4AM"'~дJlA"4;AA4A"A""4AAAAC7U:;:U7AA:дAA,AМ·AAA:P.A'U7l>7U71J"lJ!iiAA.~7u7U7t)7u:rwAA
~iu{r..iIJ7Lj7u7U7,~1J1u7.\МfЦJJJ7U7u7APAд.A,A!J7U7L(7tzmQ,U7(J7 L11AAМALU7Li7sJm2mQ
lдu7uwIWlAU7u7uwkАдд\:Q\:Пu7L1....ААС7u7 ... 7Aof!,,<J7u1АдЦ7u7~Мui!u7'$дi.)7u71.J7sАL.l7щ7'~A,Ah.u7tЛQ
uJi.r'i'U7'3L.u7\.i7Mul.u1.sLu !U71.1'7u7sLU7tJ7AЦotwMU?U71J"U7ulsAALuWC7U7 sLu7ulu7u7tJ7l::7LI7s~,\.f7
(j,7slIJ7u:ztйiJ7SLu7U7ДL.U'7u7С?u7U.7ti7u7С7u7IJYlClu'l'ч71М'С11J7s'tw7u.7.U7sAt.l7Ш'u7.ААС7IfiWO:u7u7
U7ALU7'u'7IJo1AAV.7 sMLtJ 7U7WALU 7U7s~u1iU\!:7U7iJ7AiAG7u7ti7 kAClI.жМIJ7 Ц ?IJWAAl.I7U7u..'Ад1.J 7sA
ALU7LI'J'SААдu 1u7.uwc'l'uГS.I\U?Ui'uWддддu7ц7,;Пu7u7u7t:fil"u7дАААдшiU7L1'7lJ7н7u?u7J.Jo1AAAмiu7\I7
U;;u7 (J7u7u7J>.AAAAAtU.LJ7u7 u 7... 7 (.17 u7s ~ 1 и7 Ц7!) 7u7s·AМ>.AAAA"AJ»..l:il1J'7L17 U71Д1PiAA/>/J.дI1;A.
AдддAuj7'cil$~.A.AAA/>IA~",AII.AAA''''d' .
,,/у,аl иВ>
-</t!at:.a>
..d.at:a name-","еl,СЙnlеs'tГl rlg'" )("', ;,~pace·_ " 'pr~er" en"
~\I'a 1uе>Г1рIIISi;'i"СТ'В1'18 ,qlOp",ar.a pecYFC"\8 .• </val ,ue>
~/a,,'t~
<!г ОО'!: >

'<: ' . " _ i)

Ptlс.20,23, ХМL,предстввuение " .resx

Создзt:lиефайла 1I. reso urces


ИМБП фa.iШ *.re.sx. вы м:ожете ИODОJП:i30вать УТИЛЙТУ reagen _е и е. чтобы сгене­
рИрова1'Ь двоичlthIЙ 'эюцшалент этого фЩз._ Дллэ:rого откройте ОЮIО 1\омацдной
стр.ОRИ VlSua1 Studio 2005. Пбрейдите ~. мрневой НRталrn-вamеТQ ДИС1\а С и Bьmoд­
ните СЛедУЮЩУЮ 1ООМЩIду.

:t€sgen rеsxf0rПJ,rеs}l. геsхf'оrm .rе sоuп::еS.

Теперр вщ можете oтъ.-pыть новый файл Ii • .rеSQцrсеs с цомоЩbIO VisUal stшllо


2005. чторы увидеть ДaiШЬJе в двайЧliОМ формате (рис _ 20,24) .

._/iмXfon..Jи i :W 1 сsl ..-- .... ~


74 '15, '12 65 ЭD-Й:- 6~i57.4-·1'iГj;:Г -.-сtiJiur~n"u ;i-'; -~
62 ьс:: .6'01 6:1 01 М; '.~jS~ БF l.I3 &5 1 . Pl.фlicl(eVl',,1(е. .,
~6 зs б·В З? 6" Э~ з1 &4 ~ S 30 Ь.1 n'biJ i'i1 5t7f 11,dSQ"..
50 41 Н 2.11. ЭБ' 8й Е4()!; 87 ЬО 50 a;.P.lDFADIo., _ . ' р ,
Ь .О 1)П 0.0 62 0101) 00 12' ба О:'() 61 "" " . f:; h 4 "
7'9 !l1) '~4 QD 75 OU Б4 00 6.5 00 п!! .}:>" p , y . D ",а е , . . ..
оп б,.~ ио ее 00 63 00 ЬЕ' 00 5)) 00 . . '11' . ': 1." ,D '. JI\
оо?? DQ &9 0:0 6.Е 67 ар .1~ 0.4
.00 .e .. S;,t.r )L.n,g" ..
()О 0;0 ДО FF "!;' Fj' РРО1
ОО U1J ао . @,._.
02 D() 00 ОС 51 15.3 7~ 7~ а ~{i БD , .... " .OSy",t_
69 ЬЕ ь7 2С 2О 56 БS 12 7& 69 :бf' ,Dranng. '\1exo$~';>
2,Е ;io ZE эа Z'C 20 43 7[, М:: 74 7Б n=2"O , O,Q. C1Lltu
15 74 '?2 Ы еС 2С 20 SU 7!) 61fiC "е~r,,,чtщ~ ,,.Fo,»l
..
5'{ '6F
:
ir·

~--:..,... .&Б 6Е III 62 Э ~ з·э &6
. ............. -................... _...
ЭS iсКе!l'Тtlken",ъrlЭi5 ",'
,~ .... ' ''''- ""':

P...~, 2(),24, Д!ЮИЧI10е предСl'i1БJТ~lие ... _resou;r; ces

Добавление файла '*.resources в компоновочный блок .НЕТ


Теперь :Можно встроить файл ".r~sourcee Б КОМПОНОВОЧНЫЙ блок .NEТ. исnолъ­
вул опцию /rеЭС'll1:С.еs ко:мrшЛlIТOра1tомандно'Й с;тршш С.#, Дmrпримера скопируй­
те .соответстВующие файлы I?r· оgrаЩ ..С$, Fprro.l.cs 'и Forml.Designer,cs 11 .кораев оЫ
Rаталог 13ашето дис:ка С. откройте оцио кома:н:ДНОИСТРОЮi Visu:al Stud10 2005 и вы­
IlOJIНй'Ге следующую kомшщу.

С$'.С I rе$оurсе~rе$хf0rЛ1.rеsоurс:€s /r;S yrtem .Dr awir!g_(Hl * .СЗ

1
r
854 Часть IV. fТрограммирование с помощью библиотек .NET

Если открыть созданный в результате этого компоновочный блок с помощью


ildasm.exe. вы увидите. что манифест соответствующим образом обновлен. как
показано на рис. 20.25.

, МАNIПSТ Г-ilБl!'Xl
l'i1d l'i1d ~x!
.uer 0:0: О: о
}
.nr~source public resxform.resources
{
/1 Offset: RXO'•• OOOO Length: DИОО'.1497
}
.module Program.exe
11 НUID; {C2C07EE1-1D18-4F78-9C6A-1082FF8C45EE}
..11 _ ............ _ _ ... п . . 41'1" о...а."аА

.(

Рис. 20.25. Встроенные ресурсы

Работа с ResourceWriter
в предыдущем примере мы использовали тип ResXEe so urceWriter, qтобы сге­
нерировать XМL-файл. содержащий пары имен и значений для каждого ресурса
приложения. Полученный в результате файл * . resx бьm затем обработан ути­
литой resgen.exe. Наконец, с помощью флага Iresource компилятора С# файл
*. тез о итсе з был встроен в компоновочный блок. Но дело в том. что создавать
файл *. r е s х совсем не обязательно (хотя наличие ХМL-представления ресурсов
может оказаться полезным, поскольку оно вполне пригодно для анализа). Если вам
не нужен файл *.resx. вы можете использовать тип ResourceWriter. с помощью
которото двоичный файл *. r e s ource s можно создать непосредственно .

private v o id GenerateRes ou rceFil e ()


{
11 Создание нового файла *.resources.
Res o u r ceWriter rw;
rw = ne w Re sou r ceWrit er(@" C : \ myRes ourc es.r e s ources");
11 ДОбавление одного изображения и одной с~оJCИ.
rw. AddRes o urc e ("happ yDude ", ne w 8i t ma p (" h a pp yDude. Ьтр" ) ) ;
rw.AddRes ou r c e ("we l comeSt r in g ", " При ветст ви е форма т а ресур со в.");
rw. G enerat~e ( ) ;
rw . Cl o s e () ;

получею-{ый файл *. res o u r c es можно добавить в компоновочный блок. исполь­


зуя опцию /r es ourc e s комп илятора.

csc /re s o urce: my re s ou rces .re so urce s *. св

Исходный код. Проект ResXWriter размещен в подкаталоге, соответствующем главе 20 .


Глава 2.0. Визуалиэация граф~ческих Aabllbll)( Средствами GDI-i< 855

Генерирован~е ресурсов в Visual Studio 2005


с файлами -I.. resx И *.resources можно, нонеmю. работа.тъ вруЧl~:УIO в команд­
нОй. стране. НФ Vfsuщ Srudio 2005 преД.1larает средства авто~аТ1'fЗации сОздания и
встраивания ресурсов- в npoект. Для примера создайте новое црилржение WmdoWs
Forms. назва:в его MyResourcesWi Г1Арр. Отирыв для созданного проеl\1'а о:КНО SblU1ion
ExpJorer. вы увидите. что теперь с каждой Фор:мь:й в вашем прилющеш'Ш М'тимати­
чески связывается файл *.resx (рис. 20.26).

{~ ~ 1)
~=-~~'~~~~:~=:;;~;/(1~б~} ~ ,-~.~
'". ;fJ!J~~
'i; ~ """"~
d ; ::Jj 'k~tln"'~
;,t;i ~ F'ormi,J3.
~""""t.~...,.
"Ы.., •
-,1) P' ;>gl·.om.",-

РIro. 20.26. Авт.омаТИ'-lt:!ское генерирование


файлов *.reS)$ е Vrsual SlwdlD 2ОО5

Придобавлеюш JШМИ реСУРС.ов (например. иэображенвй в PlctureBQx) с помо­


ЩЬЮ визуальных ИТ;Iструмевтов проектированиs этот фaй1J .... resx будет иамeRЯrгь­
ел автомаТИ'-lесRИ. Бо;rд~е ТОТО, вы 1-!е дол:ж:ны вручную обновлять этот фaiЩ, чт~бы
указать noлъзоватеJЦ>сщ(е pecypcы, ИОСКольtCY Vlsua1 StucU'o 2005 регенерирует этот
фай.п при КЭЯЩОЙ КщмдиЩщи:и проекта. Чтобы гаранnrpощlТЬ .прэвиmц:IyЮ CTP)'ltтy­
РУЭ'f()ГО файла, JJучше всего пЬзволитъ среде раарабо:I'КИ самой уцравл.ятъ ф!lЙЛам:
*. resx фQРМЫ.
Есла вы зах.ОТЦ1'е npедложнт.ь поль30вательский набор pecypCQB, 1:Ie СБЯЗШIНblЙ
непосредс:rвeвнр с дЗнвоо фоpмDЙ. проето добавь'Те RОВьtЙ фай:;ц *.reSR (11 нaJШ::М
примере э'го файл MyCustomResou.t.ces .тезх]. выбрай КОМанду Project~Aqd New Itell1
из меню tpис.20;21).
Есди открыть llОВЫ:Йфайл "".re.s.x. соответствую!ЦИЙредаИТQРДОЭВUJIИТ вста­
вить необходимые строповые данНЫе. изображенил. :щу"овые фраг.ме;нты ир,рyrие
p~ypcы, Крайш~е СЛева раскръrвающееся меню позво}ЩТ выбрa:rь ВИД ресур(;n.. ко­
торый вы хотще добавить, Сначала добавьте новый строковый ресурс с именем
VJelcomeString. чтобы с ето помощью зздать-то сообщение. которое вы хоТИте ви­
дет.ь (рис. 20.28}.
Теперь добавьте изображение nappyDude .Ьтр. сначадu выбрав Images ШI :кpaf4-
него меню, после этог() - Add Exlstlng Flle (рйс. 20,29J. а затем - )'JVаааБ фаiiл
happy.Dud~ .-Ьтр .в ПОЯВИВIIieМ:I'Щ {)Кие зarрузЮl фай:ла.

1
856 Часть IV. Программирование с помощью библиотек .NET

~: @J ~

~!

~~F~ ~!Re ScфtНOst


W~~ /пfnrmaб."
~y Con/'q.JnI~".
!~ •
. .
~~Ar J!~t 11~,
[~ '~1!:'
~ ~:
дЬou! вех Debцgger Q".. ~"""
I I{jsuaiw I

l~~~___~_-~~~-· ."~:~~-:.~·~·" _~._... __ . :=:::'-~. __~._~. ~,


tf8ne: [",~re .r"x ~_~~~~= ._ ,,'•.__.:::-:.:::.._.__....~:..._:__=.~ J __

Рис. 20.27. ДОбавление нового файла". rезх

Рис. 20.28. Добавление нового CTPOI(OBOro ресурса с помощью редактора файлов *. resx

. _ - - • • -.~ . .. __ о - _ .... --'''·1


New ImjIQ!
дdd New Iсоп
дdd New Text Fte
I
._ ______ .. _. __ .. _ " .. ,, ___ ''' __ ,__ " ._ ___"._ _._" ._._'___ . ____... . ~___ ~.JI

Рис. 20.29. Добавление нового ресурса * .Ьтр с ПОМОЩЬЮ редактора файлов * .resx
Главз 20, Виз-уа'ЛИ:<J,ЩIo1Я граф\'\'I6СХИХ данных Gредст~ами GDI+ 857
После этorо ВЫ оБIIаРУЖИ1'е. что файл "'. Ьтр скопирован в каТ<ЦlЩ' вашето npи­
J10жения. Выбрав nИl<тограмму happ yDude в редawroре .... res~ и J!сцqJlljЭ-У,я воз­
МОЖНОСТИ свойства P€ rsi stenICe. МОЖНО пе:гребовать. чтобы Д8.ЩiО(t изобрзжщmе
было непосредственно встроено в Кa:t\iШOНОВОЧJIЫЙ блОlt" а не ЗМ$ащ)сь ссылRй
на внешний файл (рис . 20.30).
(' , ~ ''''~" .~

Pf:#~~"t;:"ii';~'- ~ : '1 -'" 1 11

· рц - t!OlК:l!
~, wr.. m..r iIw. _tllClltжided",~.
~=<м~... srIe6111!1!1_CIIIVe,
u.ы <""",.ао; тl'llil!ll ",1In""""~ onM

Рмс·, .20.Зо. Встраивание У"<ЗЗаннt>/х ресуроов

Тедерь OКEJO SOIution ExplOrer предЛщ-ает новую папку ПОД названием Resource$"
1WTopaн СQдержит !!се Э.ТЩМt"-,Нт~, :встроенные :в .компоновочный блок. Легко ЦОl'а­
датьея, что при О'I"КpWТди ресурса Vjsua] stud10 2005 зaлycJCЭ.ет СdОТВетствyюtU;ИЙ
редактор. В ЩОРОМ случае, еi;ши вы теперь СЕомпйлируете свое приложение . ук.а­
Зaнщ,J.е Ba:IOl Стр([)lfй ]f ~зображение БJAyт встроенЫ .в компоновочный блок.

чтение ресурсов программныии средствами


Теперь. xwгдa вы ПЩ!l1Маете Ч"L'Ь :цроцесса встраивания ресур сов в Кtlмnoяовоч­
ВЫЙ блок (е помощьюсs,с ,ех~ или VisuaJ stndio 2оо.5). нуЖно выно:нитъ. как с по­
МОЩЬЮ тищl _Rе$о ur.:;еМа: ла g еr :црограм:мно дрочи:тать СОQТВетСТ.вующую информа­
цию д]щ ИСDОЛЫЮВaRИSI В програм,Ме. для этоrо добавъТf:' :в свою фьрму элемеJtты
But'ton и Pi.ctureBox (pJ::rC. 2О.31 J.
;-: F";;,;irt[~L- - ~, - -~;-::- QI::ii~-t .-:-; ~ --- <:~ X~l
I . -
I
I
i
I
I 1 Ц:r.еНI>tEl 8Crp13BHHI>IX с\рIжоlых Poeнн~1I(
Q.>~"'_= _~·:.J о_}:.;:=,~==ШQ
-I~ -~
-~ .

'J
0·*
f:~"*•..".."=,, '_""'I'()<.._~·.:i;.;;-""".:c"-~O' I I
,
I

_________'..J
1'140" 20.31, Обflовленн~й и.нтерфеЙс пользоеаТЩ1Я
858 Часть IV. Программирование с помощью библиотек .NEТ

Теперь обработайте событие Click для Button. Добавьте в обработчик указан-


ного события следующий прогрaммный код.

// Не забудь!t'е указать 'using'


// ДЛII System.Resources и System.Reflection!
private void btllGetStringData_ Click(object sender, EventArgs е)
{
// Создание менеджера ресурсов.
ResourceManager rrn =
new RеsоurсеМаЩ:Lgеr ("MyResource.sWinApp .МуСustоmRеsош:сеs",
Assembly.GetExecutingAssembly());
/ / Получение встроенной строJCИ (с учетом реI'Истра! )
MessageBox.Show(rm.GetString("WelcomeString")) ;
/ / Получ:ение Bcтpoeннoro изобрcucеНИII (с уч:е!t'ом реI'Истра! )
myPictureBox.lrnage '" (Bitrnap)rrn.GetObject("HappyDude");
// Необходимаll 'убор~а'.
rm.ReleaseAllRes o urces() ;

Обратите внимание на то. что первый аргумент конс;труктора ResourceManager


представляет собой полное имя файла * .resx (НО без расширения файла). Второй
параметр является ссылкой на КОМПОНОВОЧНЫЙ блок. содержащий соответствую­
щий встроенный ресурс (В данном случае ЭТО текущий компоновочный блок). После
создания ResourceManager вы можете вызвать GetString (} ИJШ GetObject (), что­
бы извлечь BCТPOeННЪJe данные. Если вы запустите приложение и щелкнете на его
кнопке. вы унидите. что извлеченные из компоновочного блока строковые данные
отобразятся В MessageBox, а данные изображения - в PictureBox.

Исходный код. Проект MyResourcesWlnApp размещен в подкаталоге, соответствующем главе 20.

На зтом мы завершаем наш обзор возможностей GDI+ и пространств имеи


Systern.Drawing. Если вы за.интересованыв дальнейшем исследовании GDI+ (вклю­
чая поддержку печати). обратитесь к книге Nick Symmonds. GDI+ Programming in С#
ancl vв .NEТ (Apress. 2002).

Резюме
Аббревиатура GDI+ используется для обозначения ряда связанных пространств
имен .NEТ, используемых для визуализации графических образов на поверхности
производных от Control типов. Значительная часть этой главы была посвящена
выяснению того, как работать с базовыми объектными типами GDI+ - цветами,
шрифтами. графическими изображениями, перьлми и кистями, используемь1МИ в
совокупности с ~всемогущимН типом Graphics. В процесс е обсуждения были рас­
смотрены и неIшторые другие возможности GDI+. такие как проверка попадания
указателя мьшш в заданную область окна и перетаскивание изображений.
Б завершение главы был рассмотрен новый формат ресурсов .NEТ. Как выяс­
пилось. файл * . resx определяет ресурсы в виде XМL с помощью пар имен и зна­
чений. ЭТОТ файл можно обработать с помощью утилиты resgen.exe, "Чтобы при­
вести его к двоичному формату I*.resources) и затем встроить соответствующие
ресурсы в компоновочный блок. Наконец, тип ResourceManager обеспечивает про­
стой способ протраммного извлечения встроенных ресурсов во время выполнения
rrриложения:.
ГЛАВА 21
Использование
элементов управления
Windows Forms

'Э та глава :предс;гавляe'I собой I<parnoe PyкonolICТВo по ИCIIОJ1Ъ30Ванию элемен-


ТОВ управления. определенных в пространстве имен Sуs.tеm.Witldоw.s.Fфr,ms.
Б тлане 19 ВЫ уже имели возмоЖНОСТЬ порабатать с :некоторыми элементами ynp<QI.
JJеНШl. размещаемыми в главнаЙ фо;рме: ЭТО MelluStl-iр. Тооl St.tip и St,atusSt.rip.
в этой rnаве мы. рассмотрим разлйчные ТИПЫ. натарые абычно рмметщот в преде­
дах обла:стиШJиeнта формы (ато. например. Bu:t'tor.!. Mas:ke.dTe:xtBox. WebBro.wser.
Mo.rrthCa1endar. TreeViE~w и "1:П.}. Paccmo-tрев базавые элементы DОnЬЭQВатедЬСRО­
го пнтерфеЙ:са. мы затем обсудим процеос СОЗДания ПОЛЬЗОВаТедьских элеМeJr"
тов управления Windows Iib:r:ms. интеЧJируемых 'в сред;у разработки: Vlsual Studio
2005.
После этого МJ:;I рассмотрйм процесс. lIo.cтpoeHЦ$l rtО;iЬ30Ватe.J:tЬt.''}ШХ диалог()вых
окон. а также роль -наследования форм, :КО7арое ПОЗВQЛ51ет создавать и~рархди
СВЯЗаIIНЬ.IiX ТИПОВ Ро гю . .в завершение главы абс)'ждается возмщкяость omыQ6-­
!СИ и: .3{J;I\.'рeтtленl..I.:Я. ",лемеятов графП'lесноrо интерфеЙс.а 11 )ЗыЯ,r:н:я.етея роль ТИПОI!
Flо",СопuоlРапеl и T,ableCQn:t.rolPanel. пред,jщгаемых В .NEТ2.0.

Элементы управлеНИJlWJпdоws Forms


Пространетва имен Эуstеm_WiпQ.Qws.Fоrms еодержщ РМ T.\ЦlPB, предСтa.вшno­
щих .наиБО.J]е-е часто и:сnoльзуемые э.цементы графичесНQГО интерфейса. каторые
обеспечивают паДl1ержку В;JшmодеЙ(.'ТВИЯ с Ш)JIЬ30ва'1'ел~м в. ПР11Ложении WiudbWs
Fbгmэ-. Многие 'ЭЛементы ynpq.влеНИJI из тех., (' которыми вы Qудете работать еже­
днeвsa {такими. nanример. ЯВЛЯЮТQЯ I;-uttQrl. 'T extBQx и: Label), Ш!'IyИПlВНi) совер­
шенно понятны. IЮ ч.табы pa!50TaT~ с други;ми, более ~ЭНЗ0тическими" элементами
управлеmJЯ и J(омnонентЩ\4И (например. е TreeView. E.tror:Pro"ider и TabCont rD~).
треi5уюТСJ1 дonодни:гель.ные ЦО.IIсненщя.
Из llIaIibl 19 вы узнади, что TIЦI Sуs ;t.е!Т).. WindОWЭ , Fоr:ms.Солtrоl нвляетен ба­
зо:вым нлассом ДJ1Я всех ТЩ\!ИХ элементов. Нanа:м;ним, ЧТО Соп! rol обеспечивает
lIQЗМО.1ннастъ обрабатмватщ СQбъrrИIJ мыши и RIlащщryрю. задавать физичеакие
ра.змеры и .nозrouuo ЭOlементов В форме с по:мощ~щ различньtx С:ВОЙСТБIВ€ i ~ r,i t
860 Часть IV. Программирование с помощью библиотек .NEТ

Width. Left. Right, Location И т.п . }, манипулировать цветами фона и переднего


плана. задавать активный шрифт/курсор и т.д. Кроме того, баэовый тип Control
определяет члены. контролирующие возможность закрепления и поведение сты­

ковки элемента (объяснения по поводу указанных возможностей будут даны в тек­


сте зтой главы позже).
При изучении материала данной главы помните о том, что рассматриваемые
здесь злементыI управления наследуют болыIIиствоo своих функциональных воз­
можностей от базового класса Control. Поэтому мы сосредоточимся (более или
менее) на уникальных членах этих элементов. Следует понимать. что зта глава
не пытается полностью описать абсолютно все члены абсолютно всех элементов
управления (это задача документации .NEТ Framework 2.0 SDK). Однако я уверен.
что после завершения изучения материала этой главы у вас не возникнет проблем
в понимании тех элементов, которые эдесь непосредственно описаны не были.

Замечание. В Windows Forms предлагается целый ряд элементов упрзвления (DataGridVi ew,
BindingSource и т.Д.), позс!,!оляющих отображать содержимое реляционных баз AaHHI;Jlx.
Некоторые из этих элементов управления БУАУТ рассмотрены в главе 22 при обсуждении ADO.NEТ.

Добавление элементов управления


в форму вручную
Независимо от того, какой именно элемент управления вы выбираете для раз­
мещения в форме, последовательность щагов, которые при этом выполняются,
оказывается одной и той же. Во-первых. вы должны определить члены-перемен­
вые, представляющие эти элементы управления. Затем, в рамках конструктора
формы (или вспомогательного метода. вызванного конструктором). вы должны на­
строить вид Iiаждого элемента управления. используя доступные свойства. методы
и события этого элемента. Наконец (и это самое важное). после установки элемента
управления в исходное состояние. вы должны добавить этот элемент к внутрен­
ней коллекции элементов управления формы. используя унаследованное свойство
Controls. Если вы не сделаете зтот эаключительный шаг. ваш элемент управления
не будет видимым во время выполнения .
Чтобы рассмотреть процесс добавления элементов управления в форму. давайте
начнем с построения типа Form без помоIЦИ мастеров. используя только текстовый
редактор и компилятор командной строки С#. Создайте новый файл С# с именем
ControlsByHand.cs и добавьте в этот файл следующий программный код, опреде­
ляющий новый класс MainWindow.
using System;
using System.Drawing;
usinq System.. Windows. F'orms;
namespa~e ControlsByHand
{
class MainWindow ; Form

/ / Члены-перемеННJol8 эnемен'l'ОВ фОРМJoI.


pri vate TextBox firstNameBox = new TextBox () ;
Глава 21, Использование элеМ8lfТО8управленияWindоws Forms 861
private БuЦ:.о!] Ьtл$ЬоwСоntт.'t'йв = Dew Butt.on О;

pub,1i:c MaiпWindow ()
{
11 КО~~rv.Pаци. формы.
th.is. ТЭхt = "П:рО'С"l1ы.е элементы управдеН14Я:";
t.hi.Б.!оН dth = ЗА С;
thj Б. Height. = 20'0.;
CenterToScreen();

1/ Добaз,nенме - !ьориу "о.ожrо !t'e~C~1I01"0 OItК••


f irstNameВox . Тех!; = "Прvlв,ет";
firз· tNamеНОJl.Sizе = пе:.; Эizе!15Q# S~1) .;
flrl;itN.аmеВОХ , LОСё\t:i.qп = new Роiл·t: 00, 10);
thi з. Cont:t:ol э. Add (firs.t NarneBox:) ;
11 ДобaJШеиие 11 форму вФJJmi цоrц:и.
Ьt;пЭhо,wС·оn't.rоls .. ТЕХ!; = ".Щелкните sщ.есь";
ЬtnStюwСопtrоJ.s,. Size
new Size (90, 30);'
=
ьtпshоwс-рп.trыl.Lосаtiопn = ne,'W Point. (10, 70) I
ьtnSЬ9WС'ОЛ trGls .. ВackColo!: =Со10%. ,DodqerВl ue,
ЬtпShо:wСоп·trоls, C:lick +=
J1,€W EventHa.n dler (ЬtлSf)оWС'опtrQlS_СliСkеd};
this. Солиоl Б. Add (b·tn:ShowContr:olS) ;
1
11 ОCSрабооп::ас06ы'l'ЩI! Clic~ .... ~DJЩ.•
Р1!." i vate void. lJtnSI:lOW,COn trol s --,С 1 ioked tbbj ect sende'I r Eveh.t:Atlj.$ е' )
{
11 Вwзов 'l'oStrinq (:) ;ЦmI JlaqOI'O ЭJ1tu18В'l'а упраиllеНИII:
11 ка Jtо.JШeJЩИИ Соnt.rоiз ФDp)AI.
эttirig сtrllr)'fф= '''';
to.reach (Сопt:.rоl с in this. CoHtrol,s}
{
C't;r l1п! 1) += St ·r ing . For.ma t (~' Элемент: {О) \rL" ,
с. TbS'ti'irig 1) ) ;
}
MessageBo~.Sh9w(ct.rlInfo,
"ЭлеменФЫ уrtраsдеj{Иr>О' помеще.нные !\ форыуi');

Добавъ-те в пространство M~eH \:'·Qn·trolsByHand еще ОДИН :класс. реализующий


метод J'1aii1 () прогрa:м).fЫ,

class Program
{
рцЬНс stat;ic voiGi Main (striЩ1[J ·a·J rgs/
{
AppT[-са,t10n. Еl.in (Qew ~1ainWindQw ()) ;
862 Часть IV. Программирование с помощью библиотек .NET

Теперь скомпилируйте полученный файл С# в командной строке. используя сле­


дующую команду.

csc Itarget:winexe *.CS

Запустив приложение и щелкнув на кнош~е формы, вы увидите окно сообще­


ния. в котором будет представлен список всех элементов, помещенных в эry форму
(рис. 21.1).

'Э""",,"Т: Sysl:em,Windowf;.Fcrms. T~!:Вo~, Text: Гlpteo,.


а-т: Sy$tem,Windows,Fonns.8UtttIn, toXt'Щ~ )А_

ок

Рис. 21.1. Взаимодействие с коллекцией элементов управления формы

Тип Control.ControlCollection
Процедура добавлеЮ1:Я нового элемента в форму исключительно проста. но
свойство Controls требует дополнительного обсуждения. Это свойство возвращает
ссылиу на вложенный Imacc с именем ControlCollection, определенный в рам­
иах класса Control. Вложенный тип ControlCollection содержит все элементы
управления, помещенныIe в данную форму. Вы можете получить ссылку на эry кол­
лекцию. чтобы "рассмотреть список" дочерних элементов формы.

/ / Получение ДОС'1')I'па lt ВJIожеНJIой коллекции ControlCollection Фориы.


Control.ControlCollection coll = this.Controls;
Имея ссыЛIо/ на эry коллекцию. вы можете манипулировать ее содержимым. ис­
пользуя члены, описания которых предлагаются в табл. 21.1.

Таблица 21.1. Члены ControlCollection

Член Описание

Add () Используются для добавления в коллекцию нового производного от


Add.Range () Control типа (или массива типов)

Clear( ) Удаляет все элементы из коллекции

Count Возвращает число элементов, имеющихся в коллекции

GetEnumerator () Возвращает интерфейс IEnumerator для данной коллекции

Remove () Используются для удаления элемента из коллекции


.RemoveAt ()

Ввиду того, что форма поддерживает коллекцию элементов управления, в


Windows Forms очень просто динамически создавать. удалять или как-то иначе
обрабатывать визуальные элементы. Предположим. например. что вы хотите от­
JЩЮЧИТЬ все тиды Button в данной форме (или выполнить иное подобное действие,
например. изменить цвет фона всех TextBox). Для Этого можно использовать клю­
чевое слово is С#. чтобы обнаружить нужные элементы и соответственно изме­
нить их состояние.
Глав.а 21. ИсполъзоааНИ'g элементов yi1{)-а!lле~N1.я Windows. Forms 863
j:>'ri\7iiIte v0id Di&able}ulButtQ1ilS ()
[
foxeQch (СQГ!tгоl с iXj th.:\.$, .Сопtrоls)
(
if (с is В'utt,Фrl ')
((Blj\.tiDn) с1 .ЕJ:lцbl~d = f~JS:e;

Исходный КОД. ПРQеК1 Qontr'olsByHand ра3'Мещеf1 в поД/(а:rnл:аrе·. t;оотееТСТВ'IIOЩIJ"'" ГJlaBe 21.

Добавление элементов управления


в форму в Vi'sual Studio.2'005
Теперь . .когда :вы понимаете суть npоце(.~а добавления э.пемеRТОВ управления в
форму, давайте посмотрим. I\aк Visual studio 2009 может аВ'Гомат:изировать ЭТот
процесс. создайте НОВЬ!Й щmен:г Windows Applicatlon. выбрав Д;J.LR него произволъ­
вое ИМЯ, поскольку этот проент будет предназначен ТОЛЫШ для тестирования.
A1iaлоrично рассмотренному в главе 19 случаю создания :м:еmo. панмей йнстру'­
meI-n'ОВ и строн СОСТОfl}JИЯ, ногдасреда lРЕ вьщолняда аБ1'оматичес1tOе добавление
Iщцходmцей переменной в файл *. De signer . cs. те же действRВ вьшолн.яютоя сре­
дой разработки и. при переIасr\,lmЩIИИ 11 окно проектировaJmЯ форми лI1)бого дРу­
гого элемента ynpавл~m , Точдо так же 1Т]JИ. измеElепии~ внешнего вида элементв с
помощью окна свойств ЮЕ иыдоЛНЯ)Отся соответствующие изменения програ:мм­
ного кода члена:-фующии InitiаlizеСоmропеntО (ТaJrЖе размещенного В файле
... DeSigne.r .СБ.).

Замечание. Напом~м, что окно С8QЙста~ ее'ли щеЛКНУIb в нем на кнопr.:в ' с ли.ктarраммtiЙ мол·нии.
позволя~т обработаТЪ ' GОбытия. элемента управления. НужМо 8ыбрать элеМЕ!1П \13 раоiфЫВ~IOЩ~"
гося СП'ИСl<lа И указать ИМ\1 метода, IЮТОРЫЙ ДОЛЖefl вЬ!эыват!>Оя дnя интересующm вас событии
(<<ли просто выполtнп.Ь дзой'ной щелчок на имени события, 'Побы сгенериро.аOlТЬ рбрабРТ'lика
соl'Jl>ПиЯ с ItfMeHeM, npeдnaгaeMblM ПО WМО/1яаниlO).

Добшщге в' оКiЮ проеlБИрОВашш формыl типЫ TextBdz (тeltCTOBoe оюю) д еи t tOrt
(юIоnна). Обратите внимание на ТО, что при измененви положения элеме.нтаynpа,в­
леRВН в 01:Ще праектирова..вик формы V1sua] Studio 2005 преДп:агаlОТСп визуалЬm;rе
FЩДС1ШЗIЦ~. Raсающиес.н размещения и выравнива.нин Э'I'ого ЭllеМ€II1'э. (рис" 21.2),
После р~:змe:iЦениff Butt,()!'! и Te~ tbo)( в окне проеlцированил формы рассмо­
трите щ~ограмм:ный код. crенерированныйв метоце 111 i't ia 1.1 z.еСоmрОQедt (). Вы
обlla.~те, что соответствующие типы aItI'Oматически были создады с ПОМОЩblO
леw и добавлены в RОЛЛeJЩпю, СоntrюlСаllесtiоn ф0рмы (В дополцеJЩе кустанов-
1(Ш>f. Щ)Т'ОРPl:е вы, воэмоЖЕю, добtlВйли (t помощью OКRa свойств).

p'r i "а te void I.ni 1.1a11 zесощро.р·епt ()


{
t:Ъi.з . bt nMyBut t..oJ1 = Fiew S'Istem. Window>i • Е"::>rщs • R'J,t tOГJ () ;
thiз . txtMyTe>l1:BQx = ле,'" Sy.stem. W.iпdoИ$. Fo:r-ms , ''Т е:! tBo x ,О ;
. ..
864 Часть IV. Программирование с помощью библиотек .NET

// МainWindow
//
this.Controls.Add(this.txtMyTextBox);
this.Controls.Add(this.btnMyButton) ;

Рис. 21.2. Визуальные подскаэки по поводу выравнива­


ния и размещения элементов управления в форме

Как видите, такие инструменты, как Visual Studio 2005, во многом избавляют
вас от необходимости вводить программный код вручную (возможно, иэбавляя и от
боли в пальцах). Итак, в результате ваших действий в окне проек'Тирования формы
среда разработки автоматически модифицирует InitializeComponent (). Но вы
также можете конфигурировать элементы управления и непосредственно в про­
граммном коде (в конструкторах. обработчиках событий. вспомогательных функ­
циях и т.д.), поскольку задачей InitializeComponent () является создание началь­
ного состояния элементов интерфейса. Тем не менее. если вы хотите упростить
себе жизнь, позвольте Visual Studio 2005 поддерживать Ini tial i zeComponent ()
автоматически. поскольку средства проектирования формы могут проигнориро­
вать или переписать изменения. сделанные вами в рамках этого метода.

Работа с базовыми элементами управления


Пространство имен System.Windows.Forms определяет множество "базовых эле­
ментов управления", которые можно видеть во многих 'IШlИqных окнах (это кноп­
ки, надписи, текстовые окна, переRJIЮчатели и т.д.). Вы. наверное, уже знаете об
основных возможностях соответствующих типов, но давайте рассмотрим некото­
рые наиболее интересные особенности следующих баэовых элементов пользова­
тельского интерфейса:

• Label (надпись), TextBox (текстовое окно) и Mask.edTextBox (маскированное


текстовое окно);

• Button (кнопка):
Глава 21, Ис'МОльз.о,вани(! эnемсюов' УПР!iвления W!11<ШWS Forms 8б5

• Ch e.c k.Вэх (кнопка с Elе~1'IСИМQЙ фщiс(ЩueЙ). Rad..ioBUt.:tO!1 (1tнQпка с эавн:си­


мой фи:ксацием) й GrMpBox (групповой блок):

• Cher.J k.edl ,iE',:t BOx (окна отмеч:аfМЩ'О СПИCIiа). Li stЕю.к fliШНО СПНCRэ ) Й CdJr.boBox
(номбини-роваНRО(' ОНН{»),

О~ В ОИВ работу с д ТИМИ 'Элемел ~ а,~ynравлеНИJI. n:РОИЗВ<JДI'Iыми от ТИП<;1


C'o гrt r с')l , мы с вами Обратим i!Нимаии:е н.а более "Э}{ 30ТИ"Iестсие-" элементы . Тa:RИе
нак Mon t rl Ca] ei1d a c Ta b Co[lt.rol. "r r<ilc"k B ~(I: . w-е-uВrоw'Sе::: и т.д.

Э'леме"т Label
Элемент управления ы Ье 1 (на:ллись) можt:т содержатъ информацию. до ступную
Т'ОJlЫЮ ДolJfJ ЧтеНИЯ (текст ЙJlИ: йзоeiрэжев:и:е). Щ!.1Ipимер. для i'DrQ, Ч'l'обы пояснить
1;Iощ,эова:геmо РОШ7 и возмоJКности И(;DОЛЫЮБани,Я" ост.альных элементов ynравле.·

1ЗШI. помещенных Б форму. Пре.ДПОЛО<i'Хf1\.1, вы С'оад<МИ в Visual Studio 2СЮ5 1JО~ЫЙ


прое.нт \\lm dпwS Forms с Jiмеием L,a b~l s lHl.dT e xt B (;:;;es . В pa:Mi!ax ПОDyченногр зн.­
аемплнра типа F'or m оnре.де лvп е метрд с ИМСt{ем С л~аt.е l.аi)еlС(оТ1 1: t ol (). }(оТор~IЙ
<;:н.аЧa.lIa СОЗД<lСТ и Rонфигурйрw-ет тип 'и а Ьеl , а зii-reм добавляет ero в lЮJilIе1ЩШО
элемrrл"ОВ управле.ниJl формы.

p::ti v,c c.e vo i d C r~.a t eLa.be l G~f} t.:r ol 1)


I
1/ Сsздазие и хонФиr,yp&ЦИЯ Labe~ .
L д.реll Ы т:л .s t l· 1Jс t ioiliJ 3 = n E, W La1;-el () ;
lЬ,l шз truсtw m; . N6IP;~ = n ] bl Tro ;,: t rLJct .loI'lS" ;
1bllns t t"u'C': i o J'S . То;,х t ~ В'ij едите <;u; аче1'!ия в о :"!;с е p~~a те !i;(:'I'a" ;
l) l I I1З't:; r1.,1с ti ,JД Б.,FОХ'lt
= nsw Pb rt"t( OТ 1'im&$ N~ w RО'fI1 сШ ", '9 ,-! :,F,
FQ,: tSt у 1е . Bo i d ) ;
lbJ. Instr uc't i ОТLБ .Aut0Siz e = t.Iu,e:
lblIns,tr о.асt iОд5 . Lос..аt.i Оt"! = ne w Sjy'st еТ11' .Dга W .iпg , Роiп t I 16, 1 ]) ;
lbl l !r.s t.rи с t.i ь пs,. $iZ' 1Э = п е·w S~l'гtеП1 . D~·а wi Гl g , S :izе ~ 240 , Н; ) ;

11 Добавление в кonлемцшо ЭnЕ!Меитов Yцpaв.neJЦIJI форю.J.


С од е I' gls . Аа<1 ' l bll n.s<t r u rt i o rt:1J ,) ;

Чтобы увидеть СЩ:IТветс'1'ВУЮ:ЩУЮ ПОДС'J~аЗ1\У Б верхнем qасти Г:IlaBHOro окна фор­


мы. ПОМf'етите ВЩЭОБ этой ВСIlОМnГ3<reлыIOИ фуН1,ЩИ',й 11 ЕОН~ТРУl{:J'OР формы.

р'11.ы 1c Мi'f ir.W iп d D W П


j
::: !"\ i ti аl i Z ЯСОП1роn е nt О;
СХеаtеLаЬе,IСощtrоl() ;
Се п leT Т аЗ е r e e n (1 ;

в ОТ1ШЧ}1(~ от БОЛЪnnПIСТВ3 други," эле'меНТQВ, эл е.менты yupaвlleнnn l' i1 b~l


не могут ЛОJ:!YЧать фоrсус Вlюда при пере~рдах ПО IшавИlIlС табу ляции, Однюю в
,NET 2. 0 для любого Элем..ент~ ynpавдеF.IИ,я Lab el можно сооДать МRеМОН1.{ческце
JCЛ.ClВUШ.LL установив ~'Iя свойства Us e t'Ll')€im O Гl ic зftRчение t l.' J8 (~1Meннo атоздач~
mre устан"d вли.вае_тс<f!. ДЩ): lЩ.НfIDrО с.'lюЙетва по УМОЛ ~lаниrJ:)), По сле этого в свойстве
Tex t надпис и можно (е ПОМОЩЬЮ tИМВСЩ21 ампеРСaRда. .&) определить Щ1М.б.инацию
КJIaВ1'll]] ДЛЯ пер ехода 11: ссютветст.вущщему ЭЛ t:IJ\.>feНl}' управления .
866 Часть IV. Программирование с помощью библиотек .NET

Замечание. Порядок переходов по табуляции подробнее будет обсуждаться позже, а пока чтО ДО­
стаТОЧliО заметить, что порядок данного элемента управпения при переходэх по клавише табу­
ляции устанавливается с помощью Свойства TabIndex. По УМОЛ"lанию значение TabIndex
элемента управления соответствует порядку, в котором элементы добавлялись в окно проек­
тирования формы. Поэтому, если вы сначала добавите Label, а эатем
- Textbox, то для
Labe 1 значение TabIndex будет установлено равным О, а для Textbox - равным 1.

Для примера с помощью окна проектирования формы постройте пользователь­


ский интерфейс, состоящий из трех Labe1 и трех Textbox (рис. 21.3). Не забудь­
те оставить свободное место в верхней части формы для отображения элемента
Label, динамически создаваемого в методе CreateLabelContral (), и обратите
внимание на то, что здесь каждая надпись содержит подчеркнутую букву. Так вы­
деляются буквы, которые в значении свойства Text надпffСИ были помечены зна­
ком &. Вы, наверное, знаете, что помеченные знаком & символы обеспечивают воз­
можность активизации элемента пользователем с помощью выбора комбинации
клавиш <AJt+nnлteЧ€нный cuмвол>.

,. Забавы с I аЬеl н Тех!Во)( r_ ILQII'XI

I
[ _______~______ ~ ______.1
i

Рис. 21.3. Назначение мнемоник элементам управления Label

запустив проект, вы сможете перейти к любому из имеющихся элементов


TextBax, используя <Alt+п>, <Alt+M> ИЛИ <Alt+T>.

Элемент TextBox
В отличие от элемента управления Labe1, элемент управления TextBox (тек­
стовое окно) обычно не является достуШiЫМ толы-ш для чтения (хотя его можно
сделать таким, если установить для свойства ReadOnly эначение true) и часто
используется как раз для того. чтобы позволить пользователю ввести текстовые
данные ЩIя обработки. тип TextBox можно настроить на подцержку одной строки
текста ИЛИ множества строк. его можно настроить на маскировку вводимых сим­

волов (например. с помощью звездочки, *) и в случае многострочного ввода зтот


элемент может содержать полосы прокрутки. Вдобавок к возможностям, унасле­
дованным от базовых классов, Тех tBox определяет несколько своих интересных
свойств (табл. 21.2).
Глава 21. Исnоm:,зnвание ЭЛf!МВКТОI'> управления WindowsFor mS'867'

Таблица 21.2. СаОЙСТВ21 TextBox

Aco~ptsPi~turrj Читаю или За1Щет значеblие, ЯВl1яющееся ~lЩикатором необходимооти


перехода 1-Ia новую С'траку при нажатии <Ehter> в элемеНТ6" управлеt\ия
Техt:Брх, допускающем МflОГQСТРОЧНЫЙВВОД (,Wjаче нажатие <Enter>
актияизирует кнопку формы. испрльзуемую по умолчанИIЬ)
Cha.racterCas'i ng ЧJ.fГЭt)т или задает 3На'1е-ние j указывающее неоБХdдИМDОТЬ измеf\ен~
элем~том ynраале~I'1Я 'I'extBox. региerrра . символов ПрИ' их вводе

Ha'Ssw.o'rd.CГJ,ar Чит~ет или задает QИМВОЛ, примеНli!емЫй дпя маскировки вводимых


символов ~ од.НDСТРОЧНОNl элеМВffi6 управilенVlЯ TextBQx. ИСПОЛQJ3уе­
мам ДЛЯ В6О'да пеРDлей

S;;;roJ.l Ва::: s Читает или задает ЭНliЧ6Ни.в. указывающее необх.одимость Н,аличИf!. по­
лос I1РОКРУТК\1 в Эflеме1Jlте управления 'I'extBo~, допускающем МflОГО­
СТРО4НЫЙ ввод
Т-еж tAl ign Читае-т или задает .значвн-ие.• соответствующее DДtJОМУ ~З .значении
пере41-1Я HorizontalAlignw.erlt. и указывающее npввила еыравниаа­
НИR i'sKcra в ЭЛ6ме~lте 'У11равления TecXtBo)$

Ч'J'оБJd продеМОJfстрировать Вtшоз:'орые .воз'моЖНости TextBo.,X . .поместите в


форlV1Y три элемента :УТlравленпп TextB,Qx. Первый элемент TextBox (с дменем
t~tPa;HJworcH следуt::Т кастроить для ввода па.роля. т.е. символы. вводимые в поле
'fextBox, не дOJЩUlЬ,1 быть ВИДИ:МЫМИ, а BMecro них ДОЛЖJ-IЫ пон.вЛЯТhСЯ .СИМВОЛЪ~.
задa:FIJI:Ые эn.аqеJ::U!ем свойства PasswoI,dCr. .ar.
Второй <ЧJ,емент 'I's1<tBQX {с mieIicM txtMtJltilille)- это ОIЩО МRогострочFtIi>ТО
текста . ~oTOPO~ ДОЛЖНО допуснать обраб.vтну нажатия юtавиши ввода и отобра­
жат~ вер:тикалъ;ную. полосу пронрут:ки, .когда введенный текст не умеЩ1\етСЯ в рам­
ЩI,Х видимого ЦJюс:транства TextBox. НЗRанщ. третийзлемент ТехtБох (с :имеIi<~М
txtUp.pe.Tcq$e) будет настроен на п~ревод BBeД~1-tНыx символьных данных в верх­
ний реГИСТр.
СконфигурируЙте ЕаждыЙ эп:емеm Text.Eox соответствующим образом е 1101110-
щрю окна ~aujkТE. 1-IСИОJlli3УЯ в качестве рувоводства: следУЮII:Q!fЙ Фрагмеf,IТ реали­
зации ;I:nitiаl..i.;zеСОЛlрош:mt О.

priv.ate ,ТQid Initiali~eCQJТ>.po.(1ent (.)


{

/1 txti'&&sword,
/1
this _txt'P" $SW.Qrd. PasswordOl'iiJ.'r == ,
t '1:' •

JI txtмul ti1ine
//
thi$_t>{t.М!jJlt.i]'if1~.Мi.Jl:til-Lnе = true;
01'is. tKt1'1ul t i l ifle. 8cyollВ.ay" =
.sgs-l.em.Win(lоws ,УОТmБ. ScrollBa:r:S. Vecti cal"
.-,
1/tx.tUpperCaвe
11
O'\i в. t.xt!JpperCase. Cha:raaterCas'ing =
S,ys t.EUl1 . Nindo:we. Forrn&. Characterc:as1ng: _Оррет;

}
868 Часть IV. Программироваliие С помощью библиотек .NET

Свойству ScrollBars присваивается значение из перечня ScrollBars. который


определяет следующие элементы.

p'Jblic епит System. Windows .Forms. ScrollBars


!
Both, Horizontal, Ыопе, Vertical

Свойство CharacterCasing работает в паре с перечнем CharacterCasing. ко­


торый определен так

p'Jblic епиm System. vJindows .Forms. CharacterCasing


(
Normal, Upper, Lower

Сейчас предположим. что мы поместили в форму кнопку В,) t tоп (с именем


ЬtпDisрlауDаtа) и добавили для этой кнопки обработчик события Click (щеЛЧОI\j.
Наша реализация соответствующего метода просто отображает значения всех эле­
ментов Тех tBox в окне сообщения.

private void btnDisplayData_Click (object sender, Е"vелtArgs е)


(
// Получение данных всех текстовых окон.
string tехtБохDаtа = " " ;
textBoxData += string.F'ormat("MultiLj. n e: (О}\I1",
tхtМultiliле.Техt);
te.xtBoxData += string.Format("\nPassword: [Ol\n",
txtPassword.Text) ;
textBoxData += S tring. Format (" \nUppercase: {О) \ 11" ,
txtUpperCase.Text);

/ / Отображение полученных данных ..


МеssаgеБох . Slюw (textBoxData, "Вот содержимое элементов TextBox");

На рис. 21.4 показан один из возможных вариантов ввода (обратите внимание


на то. что вы должны нажать клавишу <АН>. чтобы увидеть мнемоники надпи­
сей).

Г~-.- 0"0 ' . "' ·,, ~ -- c

о KНQ веоо.а .о.ароля· i ~1!""~"7<II1C-

МНОГОСТDOЧНЬJЙ Te:t<:,cт. Iи ПОl<.dзыеающе~ nOf10CbI npOКPYТКI..1, А

-если текст оказывается


слишком Дf1ИННЬIМ .. ,

Г=Показать AaH;:i;J

Рис. 21.4. Множество "воплощений" типа TextBox


J\iJtJlfII.l""""~'~

..
ДШV~fI!i!I~а..
/~~.Н!)IJ3"~,
~';'сtWi.ItI~~1JQ~,"IJ'Щ~'
I!: .... "t~c." ~._T

~~~ib1trJI.'

~...,.~: .r~~III>_

~~ : 1ФAi!i>Щ\ ~Щ>S-~Qq('fiI; f'j».~oII:MAI

ЭJ1е~еtП Mas.kedТext,Box
:в .NEт2.1'11 I1РЩJlТlагnетсн "Тш<ЯН: ~{роеанноете1iСТrл!об QRНО.lюtорое ЛQ3Щ1.~·'
.
ет 'Зaдa,n IЮющцоватt"'яhИОСn" С'И,иВОIТЮВ. ДQrrycшМ}'1Q ]J,.'r1Л I1Icр!ЮUiТ.ия буфер.ом 1ЩQД:;l
{ато 'М61WJtI' m'>tlЪ 'fltiM~P ёОЩiram.иоPi СТрахОВКИ, lелефoнnый lfOM<:P 'С 14;ijJtOM рerIЩ­
на. IЮЧХОmJИ (Шдrotl21ifЛFr ЧТО-'I" $yr:gfi~. J'vI.a(,'RQ!,. с "Оl'О):JaЙ прdВ,Э1j()ДИ'te.R' "'Рa:JПIetf0е
(oon ~aeтcH :шй.БJi!l!)ft{).м' иJbl э·ыра.."ItImUPМ Шld(Щ. COMaeт~H € UОМОЩlbЮ специ­
;ВЛf,ю"tx 'Марк!;;'ров. ветрееш-J!Ы1'К в- ('трощ,в~ лпrерan. tJОЗД8.li1:l0:е энач~~r: въrpаже­
юш. J!i13:CRi% ~амс:я ®СiЙСТВ:У l'!ct~k. В ·wБJi. 21 .3 JЩ)ОтC;fl ОIШ!C'.а:ци,л .пеJ(JJ1'OV~.lX
м.аСDЧЮillX маrл{.еров.

Марк.ер
.

п реЩ!}faiiltOO'r 116J1~1<;~r$ Ul.4фры ХЗ~fа~6ГIi>;Я. IHlj.

ПРI!ф.DТ1!!fЛЯ~Т f'.ООБЯ:ЗQ1ЖIbliУ1О Цl(!фР·" \tfI1<1 ttpof)e.JJ


1. ПtJ8дt::таДl1'1еl tl>а.li~qИ'{1'6jtjl.т,1 ,l» ~шем' UlJilИIi!~~:lе""Р~М'rР!i!, .А.;2!\'
ПРщ\~8В'I1~ l·щФ6$\..~ЧЩQНУtй ~в,y (!} se!J%l,raM r1t1и· H!oUIQtE:~ 'PR.rl'\tlj:le; 'A·Z)
П(;Щi\~'fi!!rПlU?тg~ЗlJlffilifJVrfЬ tc~С!fЧ

ПРl'JfД,fi"IOO.llRoe:r ~Iffi&а~ елъ M~ttrE! '~&,J3Jlfi1ею'f'Я .ар5меми

j П~t:r:r Wii;зw-еIlli 'маGТЭЗ<IГlоJit.reI1I'1Я ДflfL>l.

f1реД;QJ:SБПЯe:v с,ИМООl1.:цен~ж~оiiе4l~нkI!Libl

Э"W!Jjllrt}1~, СiI'М&.пl!Ol~ д0л~"г'1мы8. Af\Я Y\bYJмьзовЗlil4st I! T~II1:~. Иа ·s k.eа.Т9'Х.~_.вQХ, 'fle !mOJ'Jfle t(\~
.(jЧ-~f!fсtevю' С~~(jКСИС)l ~r'r'.rт!j(pl'f+,l~ '~ыра*е>iИrt,. )(OT~ ,NEi Jl.t flpeMar:.IeТ "'~6~arr;O;Нl:I\e f\[,IQ"
J,7r1"~НЮВi!I ~1..reE! дm;i i"a:f'i(rrbI aD tJт~!3P'ТflЫMM ,)J"t)JJiя'р1'iЫ'МИ Й.t;jРа.>м!",~1Я.ми· (!П(l ПfЮстр~а
'ИМе", !i у ~tБJfJ ;TE:~ ·t , Р,Е .g:!1 1аr'Е~р1.->;J~5I si ЩI$ н Sув't ~ 'i!!,.~'еЬ. . ~ g·ц lа J:r~рrэ~ 5icns )"
TWI' jt1a$:~RJ:.tB,D;Jj, иtrf!jjяю~t GIMfl:all'.c,(C. ВНЭ!Jн:irr.t-iРJмl\i СI4~I'!1i~СIolС~ .::ФSМiilЮm! 1f\раnЛ:etI~
GQM в \t&),
870 Часть IV. Программирование с помощью библиотек .NET

Вдобавок к свойству Mask, тип MaskedTextBox предлагает члены, определя­


ющие реакцию этого элемента управления на ввод пользователем некорректных

данных. Например. BeepOnError (очевидно) заставит элемент управления сгенери­


ровать звуковой сигнал. если ввод не соответствует маске, и некорректный символ
к обработке допущен не будет.
Чтобы показать возможности использования MaskedTextBox, добавьте в имею­
rцyюся у вас форму дополнительные элементы Label и MaskedTextBox. Вы, конеч­
но, можете построить шаблон маски непосредственно в npограммном коде вруч­
ную. но в строке Свойства Mask в окне свойств npедлатается кнопка с многоточием.
которая открывает диалоговое окно с целым рядом уже готовых масок (рис. 21.6).

Inpu! /Ла5k Г?I~


:leIect а ~ rnask dI!~ mh.1iot beIOW ot sefect CUS!IIm 10 ~1inI! а wS\om mask.
---- ~-~~---- - --~-~--~---~~-- - --_· -~ ---------I

1 Мa$I< ~tIOO o..te format Valida~ т_


i Zlp Сodес - 91Юsнm (nor>e)
1,,",,, (US) ll:2O Svstem,D"teT,"",
Тiп", (ELКopeanIМn,tary) ZЗ::1J) System,Datf!T",e
I sod.1 secuпtу number QOO-щ!-l2З4 фоме)
i Snortdqreand bme.(!JS) l2/11/2!!О3l1:Щ System,DareT"'.
I 5hort date Щl1j2003 Syste."D.teT""e
i phone ,ш.r ncr or.. сМ. 555,(]123 {поме}

: NcJ11.ric (5-dg'l$) 12345 (попе)


i <Cus-tom> (попе)

еж i[ С;ancel

Рис. 21.6. Встроенные значения шаблонов маски для свойства Mask

Выберите подходящий шаблон (например. Phone number - телефонный номер).


активизируйте свойство BeepOnError и снова выполните тестовый запуск про­
граммы. Вы обнаружите. что теперь вам не позволяется вводить никаких буквен­
ных символов (в случае выбора маски Phone number).
Как и следует ожидать, элемент управления MaskedTextBox в цикле своего
существования генерирует различные события. одно из I{OTOPbIX - это событие
MasklnputRejected, возникающее при вводе пользователем некорректных дан­
ных. Обработайте это событие. используя окно свойств. и обратите внимание на
то. что вторым входныМ" аргументом сгенерированного обработчика события явля­
ется тип МаsklпрutRеjесtеdЕvеntАl-gs. Этот тип имеет свойство RejectionHint.
содержащее краткое описание 80зникшей ошиБI{И. для проверки просто отобрази­
те информацию об ошибне в строке затоловка формы.

private void txtMaskedTextBox~MaskInpl.ltRejected(object sender,


MaskInputRejectedEventArgs е)

this.Text = striпg.Fоrmаt("ОШИбка: (О}", e.RejectionHint);

Чтобы гарантировать, что эта ошибка не будет отображаться при вводе пользо­
вателем корректные данные. обработайте событие KeyDown ДЛЯ MaskedTextBox и
ГlfflВВ 2"1,. ИсполЬзование ЗJtемеtlтов уnра8леНИ!l' W i l)dо,WS FOHn'~ 871
pt".amblyihe в ОбрабОТЧИRе. еьБыl1я в"осстЭШ)влени:е эагОЛОIЦ,(а формы 1'( значению,
I1:p1ШНТому по УМШlча:ниlO,

p!:i'Jat:e 110id t.X"kMaS k.~QTN<.tВo~_IO~yD'O.wIl (obj ~'ct sелdеr, KeyEventA.rgs. ~)


{
th и . TeX't = "За!!jа~ы с I.,,:1,-b~l и TextEox"';
}

ИСХОДИЫК KQД. Проект l..abelsAf1dTextBoxe~ размеще\i в подкаталог~. соmветс11!YIOЩем nlаве 21.

Элемент Button
8адачей типа Sуstеm.Wiпdоw.s.FDrrns.В1.1ttоп ЯВJще.тся "'ГРЩlспрртировка" JШ­
формаций о вЫбаре пользо:ватeJIЯ. обычно 11 ответ на ЩeJI'IОR К!i(i)Пl:tи мыIии }ии на­
Ж3.Т:i1е RJlавиши ПОЛЬЗ0вател~м. Itласс В1.1ttол (iCНonкa) пo.тryчается нецосредственпо
из абстрактноrо 'ТИпа Е1.itt ОriБаsЕ'. об.еспечивающего ряд :кшочевых во:зможпостcit
поведения для всех производны.х типов (таких. J(aJ\ С ЬесКЬо к . R ,aIjiot;llJ,t,tOI'l п
вu ttоп). В табл. 21.4 о:п:исанbI не1'(оторы:е базовые свойства Butt'o nBase.

Таблица 21.4. СВQйс,-ва ButtonВase

Свойство OnИGaние

Flii!tstyle Возэрашае, или Зддает ;значение, СОDтнemтвующее 9дl-IOМУ ИЗ qЛ6ментqв Гfep&!flЯ


Fl,,'tStyle И' УКЭЗI>IВЭJOщее СТИЛЬ sнеШнеrо }jид.а элемента упраВ1l9НИfl ButtO!1
Image Указыва1:П (необя.затеЛЬНD&) изображение, которое будет Qтображаl'l.СS1 rде-то s
гpaH~Цax ЛРQизведноГ(} ОТ Butt.cn13a-s€ l'W1a. Напомним, Что КЛасс С опtl- 0 1
определяет СSОИСТВfJ Ба сkg rочлdIm.:igе,. кФорое исrшльзуетс'll ДЛЯ ВИЗУал'iI1за­
ЦИИ изобра~екVlЯ на всей t10вер)(насти ЭJ1емента

Im.;ilg'eA1ig!) Задает значение, GоответOiвующее одному Iдз элементов П~РI}ЧНR


.сопtепtАli gтrmепt ,И указывающее npВIJt.11IB выравнивания lltЭображеНИR Hq
ЭЛ,ементе управления Button
Тех tAl igл НозвР.ащает ИЛИ задает значение', С[)ОТВётствующее одному из элементов переч­
НА Cont,e ntAlign!ne.nt J/I' указывающее Т1ра~ила выравниgани~ текета на зле­
мёнте УПI)aВЛt1МИЯ B\1t t6Тl

СВОЙСТВО Т' ехtl'.ligл тиПа ButtQ[,) Base СИЛЬно упрощает задачу ПОЗИЦИОЩiро­
ванин соответствующ!::го текста. Чтобы позициоnнр.овать тенет JЩ nOBep~ocт.PJ
B'u tt..o n , ИСПОЛЬЗуЙТе значеl'fiiп! перечня соп tедtАlig tJ.m Н,t fопределеmюго в цро­
сТранст:ве имен Sy,g-tеТn.DI С!wiпg). Повже вы yвидl'IТe. ЧтО этот же перечень MO~P
иCrюЛ&зоватъ и при размещении на пов~ры-щсти Bu.ttcНl изображеЮUl.

f cubl ic ггJ,lIrl Sygt EJ IТ1, . Drawi tig . с'аntе'п tAJ. ignЛ1?r\ ,t


(
l<>ot tomC€m:er, EJOt.toIrtLeft, БQttOIТlRight,
Midcf1eC~!Jter., MiddleLeft, Mlcid.leRiCiIbt.,
']'",рСед l"'1:, "IojYLeft, TopRight

Другим ,объеf{ТОЙ н~шего интереса являетGЯ свойствоFl a t:S:t.y.1.e_ Оно исполь­


зуется для У'цра.вления общим внешним видомэлще.нта управлеEIИ.Я But ton. и ему
872 Часть IV, Программирование с помощью библиотек ,NEТ

может быть присвоено любое значение из перечня FlatStyle (определенного в


пространстве имен Sys tеrп. Windows. Forms).
public еnurп Sуstеrп.Windоws.Fоrms.FlаtStуlе
{
Flat, Рорир, Standard, System

Для демонстрации возможностей использования типа 81lt ton создайте но­


вое приложение Windows Forms с именем
Buttons. В ОIше проектирования до­
бавьте в форму три типа Button (с именами btnFlat. btnPopup и btnStandard)
и установите для каждого Button соответствующие значения своЙства FlatStyle
(FlatStyl,e. Flat. FlatStyle. Е'орир и Flatstyle. Standard). Также установите для
каждого Вllttоп подходящие значения свойства Text и обработайте событие Click
для кнопки btnStandard. Как вы вскоре увидите. при щелчке пользователя на этой
RHonкe позиция текста на кнопке будете меняться в результате изменения значе­
ния свойства TextAlign.
Теперь добавьте последний. четвертый элемент Blltton (с именем btnlmage)
для поддержки фонового изображения (устанавливаемого с помощью свойства
Васkgrоuпdlmаgе) и маленькую пиктограмму (устанавливаемую с помощью свой­
ства Image). IЮТОРая тоже будет динамически перемещаться при щелчке на кноп­
ке ЬtпStапdаrd. для свойств 8ackgroundImage и Image вы можете использовать
любые файлы изображений. и подходящие файлы изображений есть в папке с ис­
ходным программньtм кодом примера.

Средства проектироваяия формы создают почти весь необходимый подтотови­


тельн:ый программный код в InitializeCompol1ent (). и нам остается только ис­
пользовать перечень СопtеrltАligлmепt для перемещения текста на ЬtпStапdаrd
и пиктограммьr на btnImage. В следующем фрагменте программного кода обрати­
те внимание на то, что для получения списка имен из перечня соntепtАligпmепt
вызывается статический метод Enum.GetValues ().
partial class Mail1Window : Form
{
/ / Используется: ДЛЯ: текущего зна'lения: выравиивания:. '.l'eKC'.l'a.
ContentAlignment c~rrAlignment = ContentAlignment.MiddleCenter;
int СljrrЕпumРоs = О;

public MainWindow()
{
IпitiаlizеСоmропепt() ;
СепtеrТоSсrееn() ;

private void btl1Standard Click (object sender, ~vепtАrgs е)


{
/ / Получение всех значений перечня ContentAlignment.
Array values = Enum.GetValues(currAlignment.GetType());
/ / Чтение текущей позиции в перечне
/ / и цИItJJИЧ$СКИЙ возврат.
cllrrEnumPos++;
if (currEnumPos >= value,s . Lengttl) currEnumPos О;

l
Гnава 2:1 , ИСПQJIЬЗ0вание элементов управления Windows Foгms 873
1/ Чтение Te~гo sначекия; п~е"lUit.
C'.lr.f tUi gn:rrier, t =
(ContentAl igI1mе л t· ) Еrшщ. Раrзе(сurr~'\.lig.rщщtIt .Get Туре ()',
vaI ues. Ge't Va 1 u iЭ (f~1J.I f:'БгJПЩР~S) . То:>!: ring (J ) ;

11 ВJ.!во,ц'l'е1<JС'.l'а " ero аырав:низаниsН:& btnStalldard.


t)tn:S'tа,аdзr d. ::rext~.lig-n = С>JrIлli 9nте-сп:;
bttIS·ta.~cfu!:d. Te.;>tt: = t:u rrAl i qam<;>T,t • 'Тоо tr i U1g ()- ;

11 Ji'азыещение ПИl('.l'О%рu.tМJo1· иа btnI~ge.


Ь 1::.п I ilJ эg!:': . lm,iSgeA1lg.n = c;1JrrA.} i grlпнщ t;

Тепе,р'Ь .запустите свою прОгр.ам:му. При щелчне на средней Юlош{е вы УВ11д»те.


что '1'("RCT займет uрзицию и И8менит~ Е с.О0тве:готвии I!: теь.-ущим значен~е!'vf пе~­
ме~IJЙ СЧ r тА]; i-gл.m""Т!'t. Гlи:ктorpам.1\.t:a Е пределах b t JO.} magE=! тоже займет lJomщию.
СООТВе:{'ствувлцую этому энажению. На рис. 2] "7 показан вьтод программы.

'.~ klromш" _.. grвl~1


- - - - -

t{;?i {~~!~ . -" .~,, ' > . - ," ~~, :' ,~: ... ~ ,,' >1-·ч· ,,:
k

,
~ : '." • "", ..... ' 1'!~""_J'" ....

;:~':;:~,:"~~, ,~й,. ~~:' ~, _' '"~_ ' ·~iJ~~ >~. {~~ .~~:,:;-:~~~~.:~"
' ,; . ' , .-'.
.
.

РИо.21.7. 8ариаuии типа 9u:t.tO!l

ИсхО,дНlilЙ IФА. Проект Вuttопs размещен 13 подкаталоге. софrветствующем гдаае 21.

Элементы CheckBox, RadioButton и GroupBox


Простр'анспщ имеН $уstеrn . \~iJl'dOW5 . i'or .l116 опрер;eJIЯет целый ряд других ТИПОВ.
расширтощдх возможности ВЩ: t ..олВаsе. и это . В Ч8(,,"IВ.ОСТИ. тИп: Gh~ ,",kBdx (ЮI.OПRа
снезаписимой факсациеЙ" может подцерnrnнать ДО тp~ вазможиых МСТОЯЮIйj и
TmI RadioEMt t ,j!,. (щ-тотш с зависимой ф:fJН.CЭ.li!иеЙ, может Иl\fет'Ь два сост.оянn-я -
"ВI{Jlюченц" и "ВЬЩiD()'lен.о~J. ПодоБН9 типу But ton. эти типы тоже наследУЮТ за­
метную долю своих фув:кциая:альm.t.)( вовможноcrей от базового ЮIассн Солtrоl.
ОДН:utО I~аждыЙ кд·а.сС' определяет и С!'\о.и УН1'J:кальn:ые дололвительные DQЗМОЖНО­
('ТИ. Сначала ММ рассмотрим базовые. свойства элемеFl':rа управления CJ"JE:c'k p-о.х.
описанные в табд. 21 .5.

1
874 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NET

Таблица 21.5. Свойства Ch ec kBo:x,

Свойство Описание

Appeara nce Настраивает вид элемента управления Checkbox, используя значения


перечня Appearance
AutoCheck Считывает или устанавливает значение, являющееся индикатором необходи­
мости автоматического изменения значений Checked или CheckState и
внешнего вида CheckBox при щелчке на нем

CheckAlign Считывает или устанавливает параметры выравнивания по горизонтали и вер­


тикали для CheckBox, используя значения леречня ContentAlignment
(во многом анало~но типу Button)
Checked Возвращает булево значение, представляющее состояние CheckBox (вклю­
чен или выключен). Если свойство Thre eS tate paBI~o true (истина) , то
свойство Checked возвращает true как для включенного , гак и для не­
определенного состояния

CheckState Считывает или устанавливает зна'lение-индикатор включенного состояния


CbeckВox, используя значения перечня CheckState, а не булево значение

Tl1reeSt ate Индикатор поддержки в CheckBox не двух, а трех состояний выбора (в соот­
ветствии с перечнем CheckS tate)

Тип Radi oB utt on не требует пространных комментариев, ПОCRолыtу этот


тип представляет собой лишь немного модифицированный Chec kBox. Члены
RadioButton почти идентичны членам типа
CheckBox. Единственной существен­
ной разницей оказывается поддержка события Check edCh anged, которое (как и
следует uжидать) генерируется тогда, когда изменяется значение Checked. Кроме
того. тип RadioButton не nО,IЩерживает свойство ThreeSt ate. поскольку КНОIlliа
типа RadioButton должна быть или включена, или выключена.
Обычно наборы объектов Rаdi о Вuttол группируются вместе. чтобы функцио­
нировать, как ЦCJ'Iое. в соответСТВии с логикой создаваемой формы. Например. для
набора из четырех типов RadioButton. обеспечивающих выбор цвета для автомо­
биля , обычно нужно гарантировать. чтобы в каждый момент времени мог быть от­
мечен только один ИЗ этих типов. Вместо создания вручную программного кода для
решения этой задачи, можно просто использовать элемент управления Gro upBox
(групповой блок), гаРaI-IТИРУЮЩИЙ. что все соответствующие типы Radi oBl.lt ton бу­
ДУТ работать во взаимоисключающем режиме.
Чтобы проиллюстрировать работу с CheckBox, RadioButton и GroupBox. мы
создадим новое приложение Windows Forms с именем Са rCon f ig. которое будет
расширено в следующих нескольн:их разделах. ГЛавная форма позволяет пользова­
телю ввести (и подтвердить) информацию о новом транспортном средстве. которое
пользователь намеревается купить. Резюме заказа отображается типом Label по­
сле щелчка на Iшопке Подтвердить заказ. На рис . 21.8 показан исходный вид соот­
ветствующего пользовательского интерфейса.
Если для построения формы вы используете окно проектирования формы.
вы получите множество членов-переменных. соответствующих каждому эле­

менту графического интерфейса. Соответственно должен будет обновлен метод


In itia l ize Comp one nt() .
'\ r'i1I;mc.)
~
""'.00' '!<I!!I""И 1:- [(:111")(1
'-. - -

~" 3eA_I&~;~-'_~
.u-",~_!II.~$~

1,~bl·"lrotТ!'.~a1Ii. 1fJ.111р~К-""
~M"i31of.:r'ProQ~I.\eil?

Pкc...21.8~ ,It1сх~ lЩJ1.\JSOВЭТe.t1!,СIQl1Й· "~ис ФQрtiН-'/ С;.; .r;C"AOJ-!J ti9"

IIероой ШlD:I'~.3~чcif.~.[Щ~'I"':Я нwтpшiiEа ТIimA СЬе,'З~Вd~. Б:а...ч Еnс:щ~е JЛl!.lбо­


I!Q Щ3)'rQl'О npt;!ИЗ~ IП CO·!Jj:rc;} u.m.a., nfi,.::;u.ё ~I. BK.flliIIfeГI) Bцna эn(:меJ:lТ8.
ytrpaDIJmfИЯ его· ~Д.W~ дofiц.витъ l!.O ~'ШllDm 1«I'ЛlЛeJttJ;НЮ ЭЛt"_'I{ffi'I'l'D}! JlIP<r&IJеНЮJ
ф~- .

/! Chе!бjtFl00r1kt'$
j/
l.;b"i./i. ciьfШ'·'.FIО6tМа:J; в, N&!l~e = "":c·n-eckFill.::· :t!~t.$";
t",r,.;1 э • с11i::.с:k:П Du.r!'}a L1i> ., Tab,rp:dtox ~ о"
·tTH:t;.\, cll!:!,с1d!'lО"'~Ма.tэ ,'Тe~!;: = ~ ·Э"iliJа сныеl!."!'-И~Р.ll':КИ ц:п~ мэшины";
ilii s _C~u tn,~ ]..s . Ма (и~1:!: .~.f.Le~ k,FI1щх-меt.S'J:

J
8aTe~ ду.itiЩ'У' C-В;ОЕфmyрп:рова-r:ь 5.rc'l'JyE.D'x и СQД(jрm:.аш.и~1{:В nell'f 'J'ln;п.J:
Fi,adiaВot.te<n. Чтобы pa,1М~пI!mТъ элемент jЩJi1'i1J1t"f.Шfi В PaмRax G.ti:il't!f'B'O,x:. ~
ltP~IiWtr;'=~' .э.ыm.otСН'Г В}f.Оm:It'JЩШO t.алt r~i18' ~GrО1.гр~[J>Х IТQЧIiCi)ТaJ~ $"."щ:l~ ~;Ы Ai.1-
6~ эJrel',1евтоы1 }'I1:;Р,;i nЛСЁШЯ :в :в:оШI;ШЮ'.(йЮ С G[1! r: ;:01 s формы). "{'РoQы [NJЩ'"1о, Q1'T}1-
a:fЩI!:) ипrep~, :ш:.'lJOIi&эуil~(J}ШО свойств ~ :щцaiiin: обра.60Т.fi3' С()6ыnrй ЕI1 ke<-l' jf
L~ve lPUJ ~~a .Gr.o llpjjk~J!." 1tAIi. rtt'mазш1О ~mже.

p·rl va t.€ 1J"~,·id Ini t iali :keC~(f>o n.A..r.,t (),


!
.. ~

/I~c~
It
tl!liЗ'. r:adi·!JiR:eli!..N,iillIl1;< = Ura(;il.Q.F.-эd" :,
UJi,s ., !:adtL~d. .. s,::il.?;@ =r.ew :1:V5'U!m.D:r:'.а:,,'1IiJСj .Э,'j:?е!~4. 23)'/·
' .с,, · - ~.'" l' .,--с. .• ..J ,,",,=.ry t
j- '\;, .... = 1''''"''''~'c:o ~.,s. " .,• '. . . . .
_.I..!:! -~""
.. ~ •• ~,~" ,J == 1.!"t""~""';~":

/1
-/ I qцщp'Sb~СО1О11::"
ft
r
876 Часть IV, Программирование с помощью бибl1иоте к ,NET

t h i.s . g roupB oxColor . Con trols . Add (thi s . L'adio Red) ;


t h i s . g roupB oxColor .TeXt = "Ц в ет";
this.groupBoxColor . Enter += new
Sузtеm . ЕvепtНа п dlе г (th i s . g г оuрвохс.Оlог Ел t ег) ;

this . g ro upBoxColor . Leave += new


Syst e rn . Even t Handler(this . groupB0xC ol or _ Le ave) ,

ПОНЯТНО . что нет НИI<aRОЙ необходимости вьшолнять захв ат с обытий E o ter и


Leave в GroupBox. Однаво, Д1IЯ примера . обновите в обработчиках событий текст
заГОЛОВl\а Стои рВох , НаЕ ПОRазано ниже.

11 Индихация посещения группы.,


p r iva te vo id g roup BoxCo lo r _ Lea v' e (o bje ct s e nd.e r , EventArgs е)
{
grou pBoxColor . Text = " Цве т; спасибо з а п осеще ние этой гру п пы ... ";

pri vate vOl d gr oupBoxColor_ En ter (objec t sende r, EventArgs е) I


gr o u pBoxCo lor. Tex t = "Цвет: вы н а ходит есь в эт ой гр у пп е ... "; }

Посл ед ними элем ентами графичеСI<ОГО интерф е йса в этой форм е будут типы
Labe l и But t on . которые таюке будут с конфигурированы и вставлены в коллекцию
Con t ro l s формы с помощью I nitia liz eCompon e nt () . Тип Label и спользуется для
отображения информации заказа. формирующейся в обработч ин е события Clic k
кнопки B1J t to[) подтве рждения заказа . кан показ ано ниже.

pr i va t e void btnOrder Cl i ck (ob jec t зеndеr , Sys t em.Even t Args е)


l
11 Построение строхи ДJJя отображения информации .
.string orderInfo ~ "";
if (chec kFl oorMB t s .Checked)
o r. derInfo +~ " Вы х отите заказ ать ко в рики . \n ";
i f (radioRed . Checked )
ord erInfo += "Вы выбрали красный цвет . \[, ";
i f ( !'adio)' e llow . Chec ke d)
orderInf o += "Вы выбра ли же лтый ц ве т. \п ";
i f (radi oGx een . Chec ked)
orde rI nfo += " Вы выбрали зеленый ц в ет . \n ";
i f (radioPin k .Checked)
o r derlnfc += " А ГlO ч ему РОЗОВЫЙ цвет? \ п ";
11 Отnpавха строхи элементу Label.
infoLa bel.Text = o rderl nfo ;

Обратите внимание н а то . что как Chec k Box. так и Rad ioHLlt ton поддержива­
ют свойство Chec k ed . которое позвол.яет выяснить текуще е сос тояние эле мента .
Кроме того. напомним, что если вы сконфигурировали C11e ckBox с тремя состояни­
ями, то состояние элемента нужно проверять с помощью свойства Check State,

L
Элемент 'CheekedListBox
Тел~рь, аа:вер:tl:I.ИJ:! И-СQлеДQванйе- QаЗОВЪL't'8Jl'еj\{енJ'ОВ уnрав.пения. Е Ь t t .Qh. да ..
вайr:r,е рассМ,g:грим набор -ТиПов CI1tle-ка. 1J'-ЩСТRОCl~ Che с kedLls't.BDilL. Li 5 t E'I.oZ и
C0f11 bФE9~- ЭЛемeнr ::уnрШilле1iИЯ Chg,;ke d'Li stEo.x fr.щaо uтмеча_~l'.ЮГD mrn:сiШ)', позво-­
ляет еrругmиро:8аТЬ '(IО!9тветет.вующие эдем~нnn CbeekВo.x в enи;r;-...JШ/ Д;QП)'СI~а..'ОЩИЙ
UРСЖ:РУJК,У, Пре,цш.mmю;rм. 'ЧТо ВЫ .добэ.вшm 13 форму элем~ YIWIOmешш Са!! CGt'llig.
да[GIЦий. мл:ьзова.тerao вс>эмоw..нос,тъ У'кавать на выБQР ' fЩд хаР:ut.r.ep.иcrИR. "НоторЬ1'М'
~llШа УДОВЛВПlор.ятъ. система ЗВyJ\ОВt1спрощt:ведеИЩ1 автoм.oбшtЯ (рйr.. 21 ;9).

, ЭеГiOP'1!>t'I ._.II дl"! .... """*IЫ

!I~oт qV<~ ЭЗ П"~!Ш!f".,...; >TJ)\j '''fI!jI1I''''.

Ф.!ЩIIМ P<Iд~II""'"

'I:r~r~fJi-hbI.' 1"ItItlJiГPlft!OЗflVitlo ", .

o T'bII!<l!!SR АС
I~.. ~ .I

. ~ ~r

q;,;~~~~~;:·Ii:;~,.;,.,w~ a~,
I ~ Jl f.lW~ T~ni:lUJ.~I~
СО1l14"'PII!ИQ. У./1!>.,.р~-!j.с. (сo!i'.!(фер)

Ч'):'обы дuбацЩ"Ъ в Сl'нэ с'kе d Li Bt:Вox НOl!ble gлеМенты. вызаБ~ A o'd () .ДЩJ Иf1н<.f(О­
то :элемента NЩ1 0'еIЮ~JiЬ3Уйте метод Md'R,oi!IJg'.e!) с :м:аСOiRОМ об,,~RТО.~ (';:ЧJОК, еwш
б1;41'1> TO~ЫМ). I1редстfМn..я:ющиx веаъ lm:бор отмечаеМЫХ :ЭД6~Т~J:I удр,ав.пeюm..
('.1J€lдyeT знать о 'ТОМ, '"ЧТU· ·Б т).ежиме проекl'ИРОМЮШ дюбой тип CI'Ц}CI@ :мо.щ:н-о· яа­
DOЛЮlТ'ь (' rЩМ(IЩblO с~оЙt''JltW Jt-e.ms й!Жне свойств· tnpoc"!'co ЩeJIКF.rщ·е 1'!~ ю;ri:л:J:Ee G
ЮIOl'оточие:м H~lJ@дnтe под1tO~ ~ТРО1Ювъrе значения) . Вот ЧErСТЪ lJрQrраммлОГti>
"од.а J r\ i't i/ij 1 'L z~t&~~"'(1 Гl8Тr'Ц) • coO'I1!e.tc-rnующм Щl}Н;!Ц'lrypадlШ CJ!e.c:~.jLj $ tR-о,х..

privE .t e v. :}:ld T'Il :lti,'lli ;:-е.Сo:lЛф:Ьп еп t (j


.(

1/ cl1.eeked1!6XRadiOQption,s
11
j: :.й в . t'l1З i: k·e:dB0 xR·.:;'j·r,1 i o.Opti Q j~S .lt. e....rrт s . дt1dRшtg t= ( IЗ !1'\'/ з.Ьjвс t []
" ФрО аТctJfI:.?ia-@ AC ".r 'JЭ~:к:а..;i а.Тlb Н.ыИ ЗЗУ.К'! ,
'''CL'~ !J?О !1I'?ЫЗ.<:J те-л ь о. , 'О 'кас'efO' lPц:ъ& UF-:оиг,рЫВ <I''l'.Е:J:{;;' '' ,.
"'I:ь.щij'ь-ая А{; ". "1:" JJ ь. Т-Р 9:~б.а се "{.сабl1!уФ еР .I' О J 1 ;

J
878 Часть IV. Программирование с помощью библиоте~ .NEТ

Теперь обновите логику обработки события Cli ck для кнопки Подтвердить за­
каз. Выясните. КaRие из элементов CheckedListBox в настоящий момент отмече­
ны, и добавьте их в строку orderlnfo. Бот как должен выrnядеть соответствующий
программН:ЫЙ код.

private void bthOrder_Cl ick (object sender, Even tArgs е)

11 Построение строхи с информацией дnя отображения.


string o rder l nf o = "";

orderlofo += "-------------------------------\п";
11 Для каждо:rо элемента мз CheckedListвox.
f o r ( i nt i = О; i < checkedBo xRadioOptions. Items . Co unt; i ++ )
{
11 Отмечен ли элемент?
i f (checkedBoxRadi oOptio l1s. Ge tltemChecked(i))
{
11 Получение текста элемента и добавление к orderin~o.
o rderlnf o += " О ПЦИЯ радио: ";
orderInfo += checkedВoxRadi oO ptions.ltems[i] .ToString() ;
o rder ln fo += "\п";

в качестве эaкmoчительного замечания относительно типа CheckedLi st Box об­


ращаем ваше внимq,ние на то, что этот тип поддерживает много колоночное пред­

ставление, устанавливаемое с помощью унаследованн:ого свойства Mu ltiColumn .


Поэтому, если вы добавите в программный код оператор

checkedВoxRadioOptions .Mu ltiColumn = true;


вы увидите мн:огоколоночный CheckedListBox, КаЕ ПОRaЗано}ш рис.21 . IО.

ц Бет спat-ибо за посещение :этой r~I."

ОпЦII.Р"~_
iO ;Рpoнr~1Ь_АС
-- -- - -'- -' -l

10 8-канальНt>'Й ЭВ,!!, О т ыл""'я АС I


10 CD-nPQИгрЬ,,,,,,,ель
,. _~._, _ ..
~ __ ~ ~~ _ __ . ~ .
~ У"ьтра-6ас
_ _ ___ __ _
~ _ _ [сабв~",еРli
_ • _ _ .. _ . _ _--l
....,....... ~

Вы хотите З8kSЗ8ТЪ КQSpиIO\ .


E!t" выбра"" зе-М цвет .

о.ц.. Р.#40: 8-к!ll1"льный о .у.


OnЦ/'l. РОД"": Кассетный nPОИ!рЫ ••т.,..
Oni.t1я радио : Y1Ьтp~ ( соб.уФер)

Рис.21.10. МНОГОКО110НОЧНЫЙ тип CheckedListBox


Глава Z1" Исnользов~ние sлемеliТВВ управдеНИRWiпdЬws Form.s 879

Элемент Li'stbox
ка1< уже ynОМИННJJОСЬ выше, тип checkedListBox: наеледует больШИНСТВО сво­
их ВQ3МОЖНQj::те;й. от пша Ызtво.х (окно CIIИCIЩ). ЧТQбы продемоцстрироватъ ВО3-
~ожпост.и ИСЦОJJIsэования типа Li зхВ"о-х. д.аваЙте добавим а НЩD~ uриложен:ие
t:a гСоп fi aJ ~ОЩ:МОЖflОСТЬ выбора пользователем марки автомобиля (ВМW, Yugd и
т.д.j , на РИС. 21.11 поу..аэ8Н :внешний ВИД ТОТО пользо:вательCRогоинте:рфеПса. ко­
торый ~MЫ ХФТИМ ПОЛ)'ЧИТЬ ,

'-=" "'--~ - --- - --

м....- s.aoto
, .·~oe~·; Ц~T.

'QrЦ.t~"""""" W''Пi'OИfJJЫ·аТ~Rb

"Рис. 21.11. Т~П L i st..В.o:<

'Как :вееща.!'iачн:ите <: создания ~а-переменноЙ fI.11Я работы ТШЮl\j (В данном


случае ·Э'tО ТШ1 Lis tBo x). Затем скrmфигурируй:rе элем~Н'I' у;пр:;mдения в соответ­
СТВИМ' со слеД}'юЩIIIМ ФРiЦ"Мf;НТDМ из Irtit:LаlizеС!iJшроrtелt О .
priva t e v:aid !пitiаlizеСоm-ропеhI. ()
!

11 carМa!keblst
!I
t h i s- . Сд.IМ-аkеLiБt .НеIТLЗ. АddRа пgе
(I'!ew o'bj ect [] ,1
"PlМW", "Ci.1:t:a'!l8.JIl", "Ford", "GJ:;and Am" I
",J€€1?" I ".Uet ·t i;J", "З'ааЬ" ., "Vlper l1 r "'YU{[C" 1);

this . сопtrD1З·.. Аdd (t.rJis. саLМ'З!J<еList) j

Изм~епия @бр,gбОтЧика событий btnOf;d€r _ Cl ic.k () тar\Же Oq!':':H,I. просты.

private void btnOrde r _ Cl:i_ck ( Dbj€·~t S'E'fJder , Ev·el1tArgs. е)


~
tI Построевие С'1'роlCИ 7Q1R отображе.IOra цвфQРИЗЩШ.
str' in'9 orderInfti = ";';
880 Часть IV. Программирование с помощью библиотек . NEТ

// Dоnучение выбранного эnемента (не индекса!).


it (carMakeList.Sel,ectedItem [= nllll)
orderInfo т= "~1apKa: " + c arMakeList.SelectedItem + "\1'1"'-

Элемент ComboBox
Подобно ListBox. тип СоmЬоВох (комбинированное окно) позволяет пользова­
телю сделать выбор из вполне определенного набора возможностей. Однако тип
СотЬоВох уникален в том. что пользователю ТаЕже позволяется вставить допол­
нительные элементы. Напомним. что СотЬо80Х получается иЗ ListBox (а послед­
ний. в СБОЮ очередь. получается из Co1'1trol). для иллюстраЦии возможностей ис­
пользования рассматриваемого элемента добавьте в форму приложения Са r(; ollfig
еще один злемент управления. который позволит ввести имя продавца. с которым
пользователь предпочитает иметь дело. Если имени нужного продавца в списке
нет. пользователь может ввести соответствующее имя. Одна из возможных моди­
финаций интерфейса показана на рис. 21. 12 (можете назначить продавцам такие
имена, какие захотите) .

.. .
~ Пfжуrща HOBUH "МIUИНЫ ~J©J~
ПрQll,.....ц:
з~п~сные КOI!Iрики ДI1Я.МОWИНЫ

Ц_ет

о Эме"",й О Жеnrы~ О Розозьм

ОПЦII" pllAмocoocтeHbI: Марк ..:

10 q;рО",~nьн4я-АС 'BMW ",


О 8,каН~ЛЫ'iЫЙ ЗВijК ;Caravan
q CD·про"гр.,l8dТель y"-di
__е". J
Ford
I~GI.,ndAm v i

Подробноcr" "11"""":

Рис. 21.12. Тип СоmЬо80Х

Соответетвующая модификация начинается с настройки самого Со m ЬоВох. как


видите, используемая здесь программная логика аналогична лоmке Lis t Box.
private void Initial iz eComp o1'1e1'1t ()
{

/1 comboSalesPerson
1/
1
!
rJtaBa 21. ИGnО)tьзован~е эле М~kИВ уnра'8llеflИЯ WщiQw,g Forms 881'
tl'1i з. . c~lmb.'OSa lе БР€o1: 6'0[1. 1 t-еro'з, Ad,jRange (iT8 Vl ODj е ",t l J
" MilI~:a Би-Би " , "дЭ'!'J \"М~Ш>1р-а\'" I

"Джан KO/Ie-C;'E)м , " тr.a:МЩJ \!J.ap<I " 1);

Модифщщцмя обработчика соБЫтии lYtn:Or(;ier_C lickc(} снова ОЮ1зьmа.е'rСR


очень простой,

// ПОС'1'роеиие С'1'рО_ для отображения ииформации,


$1: ring 01: d€tInfc' = '''';

1/ Испол.эозани& с:а.QЙcПа Теп ~Rимени продавца.


// уха'заино1:О0 nom.soвaTe.neM·.,
if( СО!Т1Ьф~1 еs РеrSQп.1екt '1 = "")
.ал:l;;;;.!:':! п:Ео t= "Пр tq],р.веu: " + СОЩЬQS;; le sP'(;;r ~.\>I). T\O. X't_ -t ",\~, jl ;

·el~ fZ
.С> I'~!f:' r 1 Il f·o += " Вы не указ.а= ИМЯ U}'JDЛФ Bil.."i! " ":' ,, \ r'l " ;

Порядок пер.еходов ПО нажатию


Клавиши табуляции
Теперь. !tОГДа. вы создали ДОС"I'аточ.н:о интересную форму, давайте р(,,!<'СМОТ].)И м
rrpоfiлеJ<.ty nорялка переХQДОВ По на:;'Кат.иto Д.:IIfI.виши ТЗ'буляцин. Вы, НЩlерное., ЭJ:jа­
е-те. ЧТи ВТО'М С1Jучае~ Rщда форма содерЖ1<ЖТ множество элементов графичеСIЮI'О
ИН1:ерфеЙса.. ПQльзователь 'Может переместип,' ФОI\'УС ввода от ОДНQГОЭЩ'мента Е'
другсi'му. ИСПОЛЬЗУЯ :n:лавишу табуляции , Настройка ПОIИIДIШ переходцв по Тflбу­
ляции ДЛЯ набора ~леМefIТОБ yupab.1Je1-1ИЯ требует ООliИМания СУТИ Д1,1ух ключ евых
своЙс.'tI3: Tab8cDF Jir TabIndex.
СМИСТ~У TahStc,p .мОЖНО присвоитъ ~:щ~чеЮ!е tr tJe (и('Т'щна:) или f"l st' (ложь) .
вэ.аВИСИl\lIОСТИ от ТОГО, хо,тите БЫ .и.лц нет, чтобы соот.ветствуюпщЙ элемент гра­
фичеСRОro интерфейса был доСТyttetJ по Е,зжатюо кmщиши табуляции. ЕСЛJ,f свой­
~TВY '[а ЬЗ t Gp данного элеМе"lТil УI1ра.вДeJ-IИЯ присвоr;-н() t r 1.1е . ТID свой СТВУ 'т;:; bOrc!e.r
уо.1'анавливаетсn ;mачеrtНе (НАЧЩЩЯ r: нуля] ,CUОП:К""I'i:.'Т:ВУ fPщее- ЦОРJЩ~~' М'ТШll,\ЗU­
ции Этого элемента управления в lIoc.дeДOBaTeДЫ~O{,T!! I'!<lжатиfi ЮJавиш ТЦОУЛЯ ЦИИ.
Ра('сма'tрИ:Т~ С.'I.едуюlЩiЙ .пример,

I/ Rа:~С)й;ка СВОс)йСlL'В табymщии.


та di.C'~8d. TabIndex = 2;
l'a d iO~.e'~, 'l'аЬБtор = t. rЦ 6;

J
т
882 Часть IV. Программирование с помощью библиотек .NET

Мастер настройки переходов по табуляции


в VisuaJ Studio 2005 ЮЕ есть мастер настройки переходов по табуляции, доступ
к которому можно получить с помощью выбора View~Tab Order из меню [этот пункт
меню доступен только при активном окне проектирования формы). После активи­
зации мастера ваш:а форма в режиме проектирования будет отображать текущие
значения TabIndex всех элементов. Чтобы изменить ЭТИ значения, щелкните на
порядковом номере выбранного вами элемента (рис. 21.13). Чтобы выйти из масте­
ра ffастроЙRИ переходов по табуляции, достаточно нажать <Esc> .

_-_
. ...... - ~. ~-

~ ПОКУIlКu НNВUИ I "'ШИН.' Г:-IIБI~:

0--
п- --·
..._._ _ ___ _
-----
_ _. z-
-~

О"ет
l!iIелeныi< 1!I~1пыi< 1I!Jo3О11ый 111""011""
liJap",,:
lJiW ~ .--- - .~ - ~- ~,!
IС",,,,,ал :·:1
I Ford :
e,(il an9 Am ~ - - - _ ~

ШQ»DБНОСТ. зака .. :

Рис.21.13. Мастер настройки переходов по табуляции

Установка кнопки, выбираемой по умолчанию


Многие формы. предна.значенные для пользовательского ввода (особенно диа­
логовые окна), предполагают наличие кнопки, которая автоматически отвечает
на нажатие пользователем клавиши <Enter>. Если вы хотите. чтобы при нажатии
пользователем клавиши <Enter> в нarпей форме автоматически вызывался обра­
ботчик события Click для btn Order. просто установите свойство АссерtБ1lt tо л
формы так, как показано ниже.

// при нёUC&'l'ИИ <Enter> все буде'l' происхо,ци'lЪ так, 1Са1С будто


// пользователь щелкнул на ХНОП1Се btnOrder.
thi s .Acceptвutton = btnOrder;
Глава 21. Использование з-лемеtlТоа улраВl1ения Windows FогП't$ 883

3амечаНl4е. В форме МОЖНО так"ке имитировать Щel1ЧОК Щl n!OnKe· Сапсе! ('Отмена) при нажатии
1ТользоватеJlем I<!тавИши <Esc>. ДЛЯ iJj'fQrD НУЖRО НLlЗНеч\IIТЬ свойстау Са n с ~ 1 Вц t t Gn >1МА
06ъеlml But;.ton, прёДGтавляюсцего I<НCiПК\" Cancel (Отмена).

Р.абота С ДРУГИМИ элементами управления


и.ТaI(. МbI С BaJII~ ВЫЯСЦИЛИ. Еак работать болыпИ'н~1'ЦОМ ба:зо:в.ых ЭЛ~'\iI~НТОВ
управления WindQws Fbrms (Lзhеl. Te;;{'t Box, н 'La.). Следующейзада'it:Й будет рас­
см:отрение ~ле,ментов графичес<кого mперфеЙса. обладающих более Сд.ОЖНЫМl'1
Фyнюn1QНtз.лъНbl)\Пl ЕЮЭМОЖНОСТНМИ. R ~час'!'Ью. то.rtыю Т(\. '''11'0' эдемент управления
ВШГJlflДИТ "более ';:)J(З0тuческим:~. обычно означает не То. что с та:Ю'Й\.1 элемеmюм
будет трудно ре.ботать, а то, что в3..Ч потребуется немцого больше временв на eto
освоение. На CJ.rед..VЮЩИk нескалъюtt страницах мы ра(".смО1·РЦМ с.u~,lIУIOЩи!' эле­
меIп'ы1 графического FlНтсрфciiса.

• Мош: hCa l.erJda!"

• Тоо1Т:iр

• т.аЬСолtrol

• Pa!le l
• Элементы уцрЮЩ~I:П1Я t.1pDoo;r,
• E:r:::rorFrovi:der

Дм :6ачвща ДlllJаюе за-вершимnpооот С.а уС'(',ь f ig, р,ц'смотрен элементы yцpaJ'j­


леIЩЯ ~оn'tf}Сз lепdа r и J'o'JITip.

Элемент MonthCalendar
Пространство ИlIlен .вуз te..m. Wli,c(pws . F('r'П'.s nр'едлаг~е:r очень IlliлеаНЫl1 эдемент
упраЩlе~<я Мо Dt h С зJ :ела.з' r. tWторый дает ПОЛЬЗ0ваrrел~ возможность выбрать
дату (и.llЦ диanaЗOf'I дю). и.спользуя .цружествf'.ащ,IЙ интерфейс, Чтобы продемOfl­
стрироватъ ЭТОТ элемент ynpэв.лtlliИЯ. 06ншJИМ прило~ Ca.rConl i:g так. "fГобы
DОЛh30Вl:i'ТеlIЬ мо!' ввести дату доctавки XYWleHHQjQ :ГРfЩ,стюртного cpe~CTBa. На
рцс. 21 . 14 ПOIщз~а обновленная (и слеtна модИфицированная.в QТНо.uН!нии раз­
М~еНИЯ ~лементовJ форма.
Элемент yuр;;шлеRИЯ M,jhth.C~ieridar имеет весьма ширшше .возможности:, и с
его IЮМОЩЫО o~eHЬ Просто- I1pOI'p1iLqI\'lНQ выпom-JйТь "за..хват'· диапазона дат, ыыбран­
,ttь1X ПОЛЪ3QВа~ , Поведением ПО уМолчaдnю д.:1"{Я ЭТОГО' типа · яв.ляmсеа.втомаПi·
чем\ИЙ выбор (и выделение) теtt'УЩей (сеJ'ОДНЩШIей) да:.гы.
884 Часть IV. Программирование с помощью библиотек .NET
т
Onц•• paA.~~ Mapl<a:;
[ем\Г- ,
-' ---- -- -- '-- -- FQ'd
[дЖОН KOflecU
- .. .."- - .
_-- --- ~
[
,~T~!,d .~m _ _ , '

Цеет: спасмба э.е пасещ~"ие ЭfОЙ группы __

о Зе".,ный Ф ЖеЛТbI" О РоэоеbIЙ О К~СНblЙ

ПрОА~.ец Д>КОН Колесо


марка: С.I.,.", а и.,нь 2110& r О
&1 ХQ.тите3U~I!IТЬ кое:РИI(И.
E!bl .ь,бр8J1~ ж.rгг .... Ц'.Т Пн ВУ Ср Чт Пr С6 Ве
,', 1 2 3 4
5 6 7 S 9 ШJ 11
12.14 15 16 17 18
Машина ррnжн& eblTb АОСТ&D:леНIIJ
19 20 21 22 23 24 25
13.06,2006
2627282930 I

Рис. 21.14. Тип MonthCalendar

Чтобы получить выбранную в настоящий момент дату программно. можно об­


новить обработчик события Click для Button. как преД1Iагается ниже.

private void btnOrder_Click (object sender, EventArgs е)


{
I( Построение С11'рожи ДJ'IB О'1'ображеНИR информации.
string orderlnfo ~ rr ";

11 Получение дa'l'Ы ДОС'1'aJlЖИ.


DateTime d = monthCalendar.SelectionStart;
string dateStr = stгiлg.Fогmаt{"{О}.{l).{2)",
d.Day, d.Month, d.Year);
огdегlлfо t= "Машина должна быть доставлена\п" + dateStr;

Обратите внимание на то. что информацию о выбранной в настоящий момент


дате у элемента управления МопthСаlепdаг можно запросить с помощью свойства
SelectionStaJ~t. Это свойство возвраш;ает ССЫЛI~у DateTime. которая запомина­
ется в локальной переменной. Используя свойства типа DateTime. можно извлечь
нужную информацию в подходящем вам пользовател:ьском формате.
В настоящий момент мы предполагаем. что пользователь указал тол:ько ОДИН
день для достав1Ш нового автомобиля. Но что делать, если ВЫ хотите предоставить
пользователю возможность выбора целого диапазона приемлемой даты доставки?
В этом случае пользователю нужно просто ·nротащить" указатеJTh МЬПI1И через со-
[ЛaJiа 21, ИСnO.hЬ.зоааниеэлементоl3 Уllрilвлеl1Ия WlrJdows FDТ:ms 885
ОТМ\ТС'ТВ)ПQil1ИЙ дИапазон дат. Вы уже В1Щ('lЛi-L КЩi: МОЖБО ПOJJ)'Чl'lТЬ tJaчадо ДИaщI­
зона въrДеJleJiИЛ, PI:CПQЛЬ:ilУН ('ВОЙСТВо зеlесt1.0л9t"агt. J{онел лиanазона вмделения
МОЖно оцреJXе-J1Uть с ПОМОЩЬЮ CBOI{{>TBa S€'l~сtiог(Еmd. BO'r R:illi ДЩЩ{(;Н БЫГЛ~Аctъ
програ..~..1;1ЫЙ КQД. МСЩЙфШ1ированный СQответt'ТВУЮшm.! образом,

]Jcr:jJ<iate yoid Ьr.пdгЩr: Click (,,~bjE;::l s€:Dcie:r, Е"vе'г,'::Аи<Цj~) 5)


l
11 Построение c!!lpOlm д:mt О'2iОQР~ИИЯ; JoIНФорkaЦJiIИ.
s t.JL i rlg ·о,-еа ~X 1 о fб .= 111,1;

11 П€).:;:rучеJ:iИе ,ц1<Шttа.эО!lа: Д,;l.1I1ДОС'I!аБiCИ' . . .


Ds.'te1i,ll'Ie ,.,tartD = JflслtlIСаl:еnda.r ,SelectiDn:Sta.rt:
!Эа t еТ i те E'HdD = rrю'l ':1",1('''.1 End,<'.L .8eisc,t:l.onEnd;
st:r ~ flr;! c[iiI te'3t а rtS t~ 1 =
zt J: iiлч. ',о;rПlа l (П 10 I • j 11 .! 2 1" ,
~tE:r:t[l. ПрУ s!.iiI:tB .. Mb:n't.ts ~ $t(3..it[I,~Y,~~~);
'1

&U'L.'1CJ dвг.еЕndБtr =
s t r L'"ig . Fохrг/з;t 1" I С, I , I J r , ./ 2 I " ,
eJ'',{:iJi.DilY, f:'lidD,МQлtr1, endD, Уевг);

/1 '1'шJ Date'I'ims поддержива.ет перегружеииыs' операции,1


i г (dat.eS lartS сх ! = .:J.at еЕ,лсl.'Зtr:J

ti
a:r{t~.r::1 ГJ Ео += НМВILV11...ч.а _ГГ()ПЖ}.-f..п бьттъ na.~~ 'I'a БJг:е'Н:а \J!I
-+ lIё П+ d.аt.ез'!::.а::сt.;Э'tг J. 1'1 Т1(! тl ...,k l1ЕJ.tеЕnd.Бtr;

11 Когда. ВJilбрано О;ЦНФ Значение ;цa'.l'ы..


(I}: a~~Yl а fe1 +=" 11'.Ма.ЩИ-Нд до.Ji)ЮI;-fе б1::J'IЪ Дф:С:;Т'!d впе~_~ \ [1 t1
+ oot.e5tart3Lr;

Замечание. WindOW9Forms СОД8р'Ж.1Т 3I1ем.щп управленvw t\,э t€Tiroe F.i(:' k е r, который ПQЗВОllЯЫ
предложить Ио:пtЬ:Саlелdа r i! рвскрывающеМС!1 элементе уГ\равдеНИ>I Drоr;D'3'AlП,

Элемент ToolTip
в е$3И с рассматриваt:>МОЙ формом Са rcon:ti q мы доmЮIb'I I1РОДе.~он('триро'
BaJ:b еще одну. з<tItлючительную. в.озможн:оС'ТЬ. Многие современные инте.рфеЙСh1
I10ЛЬ:ЭQватедя llР~ДД:агают ~ наэываемые еСТlЛЬi6QJQщuе rfQikК:бl.ЗКt~. В ар()стран­
cт.в~ имеЕ эуst~m.Vifindоw'S.го:r:n\s эта ВОЗМОЖНQСТЬ тrpе1.J,СТЮJЛt;Щl ТИПОМ 'l'OolT'ip,
C()o:rBeT.t>TJ!Yl0Jд~e подСRа:з'&И :tIредс:гав,nя1ОТ собой неб(!).!]ъшие цдаваFФIIЩе ruша, в
lЮТОрЫХ отображэ.ютсл вс:цомotщ:елЫlьrе сообщенй'Я. Kor.Qa: нурсор 3<lдеРЖIШ~1:t'тея
:вблизи Д(lННЩ:;О э:.rrем:еl{'Г({ интерфейса.
Для примера добавь'те TaRyIO ЦОДСl\а:"шу ДJЦI каJreЩЩРЯ Ca'-':':оIJi'ig. Снач;) ; 1

перетащите НОВЫЙ элемент уцраВJI.eliЦЩ TOQ1'I'ip И(llщНеJrn .tшструмеfl10В' в,


прое.кХИРОБания форМJd и nереимеRyЙте :,)тот эдемент YIJpaRJ1(;;НИЯ Б' Г:(11'''Ф,Q.",
ВщIШ:Iдij ВИД ЭJ1e'меJ:fТ<l 'I'Qol 'J'ip :rvЮЖНО Зад'iЦЪ. uепо.%CJ,УЛ О]{IЩ rlЮЙСТВ, н.апрн'
886 Часть IV. Программирование с помощью библиотек .NET

private void InitializeComponent()


{

// calendarTip
//
this.calendarTip.IsBalloon = true;
this.calendarTip.ShowAlways = true;
this.calendarTip.ToolTiplcon =
Sуstеm.Wiпdоws.Fоrms.ТооlТiрIсоп.Iпfо;

Чтобы связать ToolTip С данным элементом управления. выберите элемент


управления. в котором должен активизироваться ToolTip, и соответствующим об­
разом установите свойство ToolTip оп (рис. 21.15).

mO,nth(:al"ndar 5yste!1'. WindoW5 .Forms, MonthCalendar

Когда следует доставить машиму? ..:J


• GrayToxt
Fals"

Рис. 21.15. Ассоциация ToolTip С элементом управления

Теперь наш проект CarConfig можно считать завершенным. На рис. 21.16 соз­
данная подскаЗRa показана в действии.

Исходный код. Проект CarConfig размещен в подкаталоге, соответствующем главе 21.

Элемент TabControl
Чтобы ПРОИJUПQстрировать остальные "экзотические" Элементы управления. да­
вайте построим новую форму, пщщерживающую ТаЬСопtrоl (элемент управления
вкладками). Вы, возможно, знаете, что TabControl позволяет селективно скрывать
или показывать страницы связанного содержимого с помощью щелчка на соответ­

ствующих "закладках". Сначала создайте новое приложение Windows Foгms с име­


нем ExoticControls и поменяйте имя исходной формы на MainWindow.
Затем добавьте TabControl в окно проектирования формы и, используя окно
свойств, с помощью коллекции TabPages откройте редактор страниц (в соответ­
ствующей строке окна свойств щелкните на кнопке с многоточием). ПОЯВИТСЯ
диалоговое окно конфигурации инструмента. Добавьте в нем шесть страниц и
установите свойства Text и Name страниц в соответствии с тем, I{aк ПОRазано на
рис. 21.17.
[ла.ва 21 . ИСПО'ЛЬ30'1i8ние элементов Уllра1iJJ6НИЯ Windows Forms ВВ7

"'!Ц>.".:
(ВМi/J- - ~. I
i ~;'lO!> ~. 1
_.- -
'!i'т~"d,Arn
, г -~- -~~

._i'b :1 . .:. .. - -,щ--, .. ; Y"I


--.,.., "~

1 :1 ~ 4
~ З UI 11
j2 j3 14 1·5. 16 1:7 18
19 ао 2'1 22~,:цЩ
2Ii :n :1fj 2'J зо

Рис. 21.16. Эг.€'Мент 'Гаоl 'I'ip в действии

Рис. 21.17. МНОГОС'fраничный ЭIJемент управления Tai)CC>1) t ГО1

При создании Злемента ynpавлен"'Ия Т;;;:ЬСоn tro 1. c.дr'дyeT учйтыuать 'JЮ, ЧТО R8.Ж­
дал страница nредставдлетс.!I объектом т а Ь Р а 9 е. содеРЖf.\ЩИ};lСfl во ВНУтре~-!Не-й
К1)JD'1eJЩИИ стрaroщ Tabcont.rol. Оконфигуриро:щшпbIЙ Qбъе.-т TabCofJt.rol (по­
добно любому ~py:['oмy элемеЕ'I}' rptIфичеCI<оrо интерфейса в форме) добавляется
n :ЮМJI.e:в:цию Сщн:rоls форм.w. Рассмотрите соответС'l'ВУЮЩц'й фра.r.:м.ен:'r ~eтaдa
1 гj i ti.a l iz(;'С'оmроnелt (:).

J
888 Часть IV, Программирование с помощью б"блиотек ,NET

private vo id InitializeComponent()
!

11 taЬControlExoticControls
11
thi s. tаЬСопtrоlЕхоtiсСопtr:-аls. Controls .Add (tlli s . pageTrackBar,s) ;
tl,is. tabControlExoticControls. Controls. Add (this. pagePanels) ;
this.lаЬСопtr:-аlЕхаtiсСопtrаls.Сапtrаls.Аdd(this.раgеUрDоwn);
this. tabControlExaticCon t rols.Cantrols.Add (this.pageEr rarPravider);
this. tаЬСопtrаlЕхоtiсСопtr:- а lS.Сопtrоls.Аdd (tnis.pageTr eeView);
tI",} з . tаЬСопtrоlЕх а tiс С опtr:- о ls. Cont.r:-ols. Add (this. pageWebBrowser) ;
tП is.tаЬСопtrоlЕхоtlс С опtr оl s.Lаса t iоп
= new System.DraWing.Point(1 3, 1 3);
t h i s .tabControlExoticC a ntrols.Name = "tаЬСопtrоlЕхоtiсСо п trо l s";
this.tаЬСопtrоlЕх о tiсС а пtr:-о l s.Sеlесtеdl п dех = О;
th i s.tаЬСопtr:-оlЕхоt i сСапt r о 1 s.Sizе
= new System.DraWing.Size(463, 274),
this.tаьсопtrоlЕхоtiссопtrоls.таыIdехx = О;
this.Сопtrоls.Аdd(this.tаЬСопtr:-оlЕхоtiССопtr:-оlS) ,

Теперь. имея базовую форму. по.zщерживающую набор вкладок. вы можете на­


строить каждую страницу Д1IЯ демонстрации остальных "экзотичеСI{ИХ" элементов
управления. Сначала давайте рассмотрим роль TrackBar.

Замечание. Элемент управления TabControl обеспечивает поддержку событий Sele c ted .


Selecting, Deselected иDeselecting. Это может оказаться полезным тогда, когда
требуется динамически генерировать соответствующие элементы в пределах страницы,

Элемент TrackBar
Элемент управления TrackBar дает пользователям возможность выбора иэ ди­
апазона значений, используя нечто. похожее на полосу пров:рутки. При работе с
этим типом нужно установить минимальное и максимальное значения диапазона.

минимальное и максимальное значения при ращения. а также начальное положе­

ние ползунка. Соответствующие значения можно установить с ПОМОIЦЬю свойств,


описанных В табл. 21.6.
Для примера обновите первую вкладку элемента TabControl. разместив на
ней три элементаTrackBar. Д1IЯ каждого из которых верхнее значение диапазона
равно 255. а I-шжнее
- нулю. При смещении пользователем любого из ползунков
приложение перехватывает событие Scroll и динамически создает новый тип
System.Drawing.Col or на основе новых эначений ползунков. Этот тип Color бу·
дет использоваться ДЛЯ того. чтобы отображать соответствующим цветом элемент
PictureBox (с именем colorBox) и соответствующие RGВ-значения в пределах
типа Label (с именем lblCurrColor). На рис. 21.18 первая страница окна пов:аза­
на в завершенном виде .
Глава 21 ИС!lОIl~зование ЗТlеМ8Н-ТОВ управления Wmdows Foгms 889
Таблиц, 21,6. Свойства Tr,a ckBa.:r

Свойства Оп~сание

LаrgеСh'зngе Число деnэний, на которое' изменяетQЯ положение ползунка TtackBar,


когда происходит событие, ПРВАполагающее " большое" 'изменение (на­
пример, щелчо!( g~опl<И мыши, когда указатель находитC'!i в о!5ласти на­
лf}8ВШllощей г.юлзунkа, или нажатие КlIaвиШ <PageUp> 14 <РвgэI:lоwп»
Ma.z i I:lUJP. Верхняя И\1ИЖ:НЯЯ границы диапJt.}ОНЗ TrackBar
Мinim ц1Т1

QJ:; ientation Ориентация ДIlЯ Ty·ackB,a:r. дейtтвительныии Я$J1ЛЮТОЯ значения из


пер~ни Gfriе ,\tatioll (ГОjJИ.зонтальная Или вертt1кальнаяориентa!J,Jf1Я)
ЧИОЛQ деЛ~I~,iIJ, на которое измвняеТСII п~ложен",е TrackВar" KOIM
rrроИсхо.l1.loIт СОб.ытие, nредполаraющее "малое" loIэмеНeJ.Iие (например,
нажатие клавиш со стре:л.каМIA)

TickFrequenc:y Влияет , на число делении'" КQто(Юе требуется изобра,зить. На,лример, для


Tra,ck Bar 1>'В6РJ.(tlИМ пределом 10'0 нераЦИО'нa/I,Ь-НО изображать 'все 100
деле,НИЙ дrlЯ элемента YflpaBJ1:eI-iИЯ' АЛиноiil 5 см. Если YCTaHoВffib ОВР}1'
стао Т1скFrеЧ'Jепс'У равным 5. ДЛЯ Tra<:;kJВar будет nокззаhю толЬКо
20 делений (одliо деление буДет ПРедставлять 5 ед,ИНИЦ)
TickSLjfle Задает Вlf8ШНИЙ ВIЩ .ЭJiемента управлеАИЯ Tra'ckBar. От етого- значения
(КОЩРQе ДОJlжt-lо ' соответствовать зНачеНияМ перечня TickS't ylej зави·
сит и тО, где ,(jYAYl ивображены делеНИR относительно ползунКа, L1 то, как
будет 8Ь!глЯДетЬ сам ПОЛЗУНОК
Читает Lo\ли устаиавливаеr значение. задающее текущее положение !Тол­
Эунк.а Tt.ackBdt. С помощью ЭТОiО СВОЙClва можно ПОПУЧIo1ТЪ чl:1словое
значеНl1В, сqдержащееся 8 TrackBar, чтобы ИСПОПJ:,З,ОВЭП, его в при­
пожеl~И"

I ,.___.
"~Тr$kE-..· ~ ?anOI i! Ijp6;;~:It_._
/ Е;r~iOI!iawJiТ~i;;'
-АWеЬ&;~,1
~ . ... ~~_ :l _ _
,
. -- :

КрасttЫЙ:
1_ , II I I

"~ ~ ---~ _ _._~---


'1 I

'~.' ' "' ''.;':'':'_'' '


. , I " • , I '1

--_.)-. I
:: . ~~ .•. __ ._ •• ~_...._..
,
' 1'

Зеленый': t

СIIIНИЙ: ,:::-'~. ':' ': .'} ~


' " -~ ~,:~.' . ..: ' ' ~, .,:. '_. !
i
1,--~_~_~_щ_и_е_Ц_Si_е,Т_OS_bIc_._:э_на_ч_е_НИ_Я_:_~_З._1_4_з,_6_5_}--<1 !
.... . _._._ _ ~ _____ ~ __ =_ : ~~.-:;.;..... . __ ,Jf
__

Рис. 21.18. СтраНliща T!'acJ<Bq.L:

J
890 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NET

Сначала. используя окно проектирования формы. разместите три элемента


управления Tra,~kBar на первой вкладке и назначьте соответствующим членам­
переменным подходящие имена (redTrackBar. greenTrackBar и blueTrackBar).
Затем обработайте событие Scroll ДЛЯ каждого Track:Bar. Вот подходящий про­
граммный н:од InitiallzeComponent() для ЫиеТуаскВау (программный н:од
остальных полос почти идентичен данному. за ИСRЛЮчением имени обработчика
события Scro11).
pri-Jac:e void InltializeComponent ()
(

11
11 blueTrackВar
11
this.blueTrackBar.Maximum = 255;
this.bluеТrасkБаr.Nаще = пы1 JетrасkБаrп
;
;
this.b:ueTrackBar.Tic~Frequency = 5;
this. tiue'I'.r:ackВar. TickStyle
= System.Windows.Forms.TickStyle.TopLeft;
this.blueT.r:ackBar.Scrol1
+= пе" Зуэ tern. ЕvепtНапdlег (th,i s . bllJeTrackBa r _Scroll) ;

Заметим, 'по минимальным значением по умолчанию для TrackВar является


О, поэтому его явно устанавливать не нужно. В обрабоТЧИШIX событий Scroll для
каждого TrackBar выполняется вызов вспомогательной фующи:и ПрdаtеСоlоr (),
fЮТОРУЮ нам еще предстоит написать.

private void blueTJ':iickHar_Scroll (object sender, EventArgs е)


{
UpdateColor();

Функция UpdateColor () отвечает за решение двух главных задач. Во-пер­


вых. нужно про'штать текушее значение каждого ТгасkБаr и использовать эти

данные для вычисления нового Color с помощью Color. FromArgb (). Имея но­
вый готовый цвет, следует соответствующим образом обновить член-переменную
Е'iсtиrеВох (с именем colorP.ox). чтобы установить текущий цвет фона. Наконец,
UpdateCo101 () комбинирует значения ползунков в строке. размещаемой в элемен­
теLa.bel (lblCurrColor), как показано ниже.
private void UpdateColor()
{
11 Получение HOBO:rO цвета на основе значений ползунхов.
Color с ~ Соlоr.FrоrпАrgЬ(~~еdТrасkВаr.Vаluе,
gree:1Trac kBar. Va1UA, Ы aeTrackBar, \/а1 ue) ;
11 Изменение цвета в PictureBox.
colorBox.BackCQ1o.r: = с;
11 УС'1'аноnка '1'еКС'1'а дnя надписи.
1blCurrColor.Text =
string. Fоrгпаt ("Текущие цветовые ЗНачения: ({ О) / (1), {2))",
redTrackBar.ValuE;', greenTr:ackI3ar.Value, blueTrackBar.Value);
Глава ~1. ИСПО)lЬЗО8а~l(Iе элемеfIТО,В управлеНИRWinQОW$ FОПI1S 891
Эан..юочительн:ым штрихом является у.ставовка началъных.эначениii: каждого
пол.эунка при нача..ThRОМ ПОJ1ВЛfНI1I1 фQРМЫ и отображеюre текущего цвета, как
по:казано ниже.

риЬН с МаiпW,iш:iоw()

ITci t iaii ~еСоrrrpQnеf'_t () :


,C€I1ter,!,'oScreen () ;
1/ УC'l\аИовха ИСХОЦИОРО положениil поnэу.нХО!!i •
.ted;T[ас kHar _V,a l 1,H~ = 1.0 о ;
grеепТ z а:сkEа.[ . \ia l n e = 2SSi
bl:u e T rёH: k.Ha'!: .Vэ.l'.iе '= О:
'Up d a te CploI () ;

Элемент Panel
как 'вы уже :видели, элемент управления G:rC)lJpBCix может использоваться д.ла
ТОГО:, "Л'о6ы логически объединить ряд элементо:в управления (наnpим:ер~ neреlUUO­
Ч'fi:Гf'лей) и заставктъ их фунь."'I:(ИОНttpова'1Ъ ВО взаимОС8ЯЗИ. Элемент управлеНИR
Р.шe.l в атом CМblC)Ш шщ.яе'Тся бш4.ЗКИМ к Gtou.pBox. 3лементы управления Panel
'I'оже ИСПОJIbЗУЮТ/;,:Н Д1fЯ группировки родствеlшых элементов управдез:ия в лаги·

ческие еди:mm,ы. ОДНИМ из различий является то.. что -ТИП Рапе 1 получается из
кпаесЗ. sсr э l lаыl·Qопt'l"с ,l • . ПI1Э'ТОМУ Раl1еl может подлержив<tТь подосы прОRpyt·
ки.. чего ' не'!' у G'I:OupBGx.
ЭдеМt>.JПЫ yuравлеии,н Ра'Л Еl могут тз..юне испО1rьЗ0ватЬСfJ ДlIЯ Wконсервзn:йи'" со­
держимого экрана. Например. если у вас t:CТb группа элементОв управления. ПО"
торы\:! занимают всю J-Шзtmtoю половину формы. -ВЫ можете Поместить эту rpyuny
в Б'а,l1е1 ло:ловиннЬtо раЭlWrepа и ycta1-iовитьзна:чеНИ(J т..ти:е (и:стинэ.) д:пя свойства
All t 0 '$ o.r: 01 1 . Тшда пользователь сможет использовать палосу (ИJШ ПОЛОС""1-) про­
крутки, чтобы просмотретъ весь :набор эле~lеН''Г()Б. К TQlVIY же, есJШ ДJ1Я свойства:
Brxrder3t,yl e Э.чемеШа Pan-el устанDвитьзнэчеНnе NOri'€>. 1"0 э1'ОТ ТИП МОJЮJО будет
иcnользо'вать для гpynnировкн набора элементов, КОТОРЫ'е очень л~rКQ ПОJ<азаrrь
:или C1tPIiITb способом. COflepiueHHo п)Jозрачным в ртношеюm кон~чного ПOJIьзо­
ватeJiЯ.

Для примера давайте доба:вйм на вторую страницу Та Ь с о с1 t r о1 дм тищr


В пtt or, (с имев:ами Ьt[)Sh а ",I?,~пеl и btnHidePa:nel) и QДИН ТИП Раnеl. ко-rорый
содержит шару текстовых 6510IФВ (txtNormalText :и txtUpp€rText) с инструкти­
рующим элементом Label. (1<,<шие именно Е\лемеI111::й нахоДЯТСЯ в Pa;nel, дця этого
прнмера. не очень важн.о.) На рис. 21.19 шmаэан ОI{ОН~'I:елъный 'ВИД (;OOTBeTC1'BY~
ющеЙстрающы.
С ПОМОЩЬЮ ОICНасооЙсТ.в Qбработmе событие Tex t Changed ДIUI первого 'элем;ен­
та Te.x-t.Вох. 'и в m·ен.еРИРDВанном обработчике собьпия поместите 8 txtUpperText
.преобрааованныЙ в Bepx.mm регистр ~екст. roзедевны}1 в tx tNcr::ma 1Тея t ..
pr i ~"a C е vpi d LxtNo n:Ili'!.JTe« t _ 'fext Ch~n.gfO'd ,( ob j -e.ct sendsr, EVe {\tArgs е)
j
't ,.; tQppe:rText. Tex .t = !:xtNormalText, T6.J-;t. ToUpper (-). ';
892 Часть IV. Программирование с помощью библиотек .NET

MainForm.cs [Des'gn]· Object Browser МatпFогm . сs'" Mвinform , DesiQner ,(5'* .. Х

TrackHar Paoel UpDown Е ГfOiP,avider T,eeV,ew Web8,Qw!e,


" -О:i±F:':'::--::~'.~-.'~-::::=':--= ·CC. :::"=:::.::::::'::::::::::JiJJ\5-·,
! По~_~~';~~;;-j J Введите lJЮбой текст ' !

. [ё!?~~_п;;;_l
,
,
ci
!

.i :'- - - - .- -. '-'- --~ ~o ·_ ·- · ---· " - -'-"_-_"'_ -,--- --Ь

Рис. 21.19. Страница Рап е }

Tt' llepb обработайте событие Cl ick для каждой кнопки . Как вы можете догадать­
с п. нуж но прост!) скрыть или пов:азать Pa.ne l (вместе со всеми содержащимися та.Г\1
ЭJlе Мt'птаюlof пользовательского интерфейса).

ral";,'.E 'Jci ·j i.JtлstlOwР а ПЕ: l_Сl iСk (obje ct se nder , EventAr gs е)

га.ле l Т ехtВ о хе з . V i з i Ьlе = fal se;

~(,ЛН теперь вьшолнить программу и щеЛlЩ:УТЬ на той или друтой КНОПRе в со­
отв етствующем окне, вы обнаружите. что содержимое Panel соответственно по­
казывается и СRрывается. Конечно , этот пример не производит слишком большого
впечатления, но я уверен. QTO вы смогли увидеть ero возможности. Например. вы
можете иметь ПУНКТ меню или ORНO безопасности, способные предоставить поль­
зоватеmo "простой" или "сложный" набор элементов. Вместо того чтобы вручную
устанавливать свойство Visible равным false (ложь) для множества элементов.
вы можете группировать их в пределах Рапе} и соответственно установить одно
свойство Visible.

Элементы UpDown
В ра~шах Windows FOпns предлагается два элемента. фУНIп~онирующие, как
элемеFtlПЫ уnравлеm!Я с 1lРQкруmкой (также известные. JШI( элемеfimы управле·
fiL!Я UрDошп). Подобно Сот ЬоВох и L ist Б о х, эти новые элементы также позволяют
rтолыювателю выбрать элемент из некоторого диапазона возможных элементов.
ГЛQва21, ИСПОЛЬЗОllа~еЭllеl.(ентов уnра:вле!/и~ Wir,dows F1)rms 893
РаЗНИ:Цii в том., чтО npи исполыюванци ~лемента ynpЮЦlеmlЯ DomainUpDowr)' и:.тra
N1lТneTic lJpDown варианты ВЬtбцраются: с UОМОIДЬ'Ю -небольши'!' ~трело~, направля­
ющих вверх и ВНИЗ, ВзrЛf.ltt»Те, НaJIpИJ\-tер, на рис. 21 . 20.
. .
'j:1 "ЭК3IJтн',вСl< ..С" -",,,,.j;! Нl.' У"РдП,1е "")1 1;'lfSIFX I
.I T;:"ci(B~J!'.. ~~ 1" U~D;~" I~t';,Pm~: fr;;"y~...1W~I-J..... ~ ~-. _ . - --..
I
1
: ЭЛЕ:I"It?нт ,DQmа lпUрDо,wn ~~~~,~~;ir ~~ _ ~ '~~ ,

Элеl"1ент Numеrlсl 1 dDО\ЩI ~.~ _', . _

Рис, 21.20. РМота с типами OpDt>w!1

с учетом Toro, Ч:ТО вы уже QСБОИJЩ раБОТ~: с ЩJДQбцьrми тиn.aмП, БЫ не доZIЖ­


ны впреrги,ТЬ особых СJ10жцостей при работе с эл е.ментами Up DOW !1 . Э лемент
Domai:nUp DoWI1' ;щет пользователю ВОЗМЩI\'JiQСТЬ сдеJЛ:пЬ ~ызoрp из набора строtro·
вых дащ-IЬЦС ЭдеМе1-П 'Nu me r i cUpDQWn поавощrет выбрать ПОДКUДЯ'LЦИе зnаqени.киз
диапазона 'ЧИСЛОВЫХ дaНJiЫX. К3жДhlЙ из Эnrx ,злемеНТОJ:l ЛВШlется ПРЯМЫМ n01'ОМ­
.коМ обrnего базового кпасса l.TpBo1N'.hBa:-sе . В 'Та:бл. 21 ,7 описаны некоторые щufi:ны(~
СВОЙСТЕ!а ЭТОfО класса ,

Таблица 21',7. свойства UpDDwnBa.s e

СаоЙс.тво Оnисанме

Iл cer,c:eptAr t owK.E'YS- Читает илИ устанаВЛИВ8еп ЗНа-4.ение, fщля\ОщеэСR' ИН;tJ,икатором ТОТО,


что пользователю разрешено ИСh0I1ЫЮвать r::тредки ilверх и вяиз
для ~JQoJJa значений

ReadOn l y Читает или устана{lл~вает эначени.е, flвлЯ-j(jщееоя индикатором того,


ЧТО 1el<C1 разрешаеl'СSl me\-iЯТЬ ТОЛbrО с ПОМОЩЬЮ стрелок ввер )!. и
вНиз, но fJe с ПОМОЩl>1O ввода в ,элемент упра5леЬJИЯ с клавиаТУР),1 (J

целью поиска данной строки

'r:ext Читает _ИI\И уЩ'анавЛ'ивает теКУЩИЙ текп. отОбражаеМ}I1Й ц элем.енте


ynрщmеl'lИЯ с лрокруткой

'f~xt Alig ь1 Читает или УОТВНЩlnИSа6Т аначение, задающее параметры выаени-­


агния TeKcia в ~леме.нте упрasлеltия с прщруrко.и

lJpDOv'lnAl1gfl ЧIl11Эет иЛИ уатанав~ивает ЗI-i"щ'!е'НИЕ), задающее rтapaMeтpы выравни­


вания mрело к вверх и вниз в элементе управлеi'1И~ С прокруТКОЙ" в
CDОТВ8ТCТJiIoIИ 1:;0 ЗНд4!ЖИЯМ\>1 первчшi w:ft.RightAl ign:me.nt
894 Часть IV. ПрограММИРОАание с ПОМОЩЬЮ библиотек .NEТ

Элемент ynравлеlШЯ DomainOpDown добавляет небольшой набор свойств, по­


зволяющих Rонфигурировать и обрабатывать текстовые данные этого элемента
(табл.2.1.8).

Таблица 21.8. Свойства DornainUpDown

Свойство Описание

Items Позволяет получить доступ к множеству элементов, хранимых в дан­


ном элементе управлеJ1ИЯ

Selectedlndex Возвращает индекс выбранного в настоящий момент элемента (от­


счет начинается с нуля, значение -1 указывает отсутствие выбора)
Selectedltem Возвращает выбранный элемент (а не его индекс)

Sorted Индикатор необходимости Уi10рядочения строк по алфавиту

Wrap Индикатор необходимости циклического возвращения к первому или


последнему элементу, когда пользователь достигает крайних элемен­
тов списка

Элемент NumericlJpDown так же прост (табл. 21.9).

Таблица 21.9. Свойства Nurne ricUpDown

Свойство Описание

DecimalPlaces Используюrся для указания правил отображения числовых данных


ThousandsSeparator
Hexadecimal
I,crement Устанавливает числовое значение при ращения для элемента управ­
ления при щелчке на стрелке вверх или вниз. Значением по умолча­
нию для приращения является 1
Minim\1m Устанавливают верхнюю и нижнюю границы значений для элемента
Maximum управления

Value Возвращает текущее значение элемента управления

Вот та часть
InitidlizeCamponent () , которая задает конфигурацию NumericUpDown
и DomainUpDown на этой странице.
р]' l va te '10 id 101 tial izeCompooent ( )

11
11 nwnericUpDown
11

thi.S.ГluшеriсUрDОwл.Масхiщum ~ пеw dесiщаl(пеw iлt[] {


500,J, О, О, О));
this. [llJmеriсТJрD®wп. Name = "ПUl1'!Е-гiсUрDоwп";
this.пиmегiсUрDоwп.ТhоusалdsSераrаtог = true;
1/
1/ domainUpDown
11
this . doroa.i. niJpDow[l. 1 tems .Add ("Второй ва.риант") ;
Глаза 21.. ИСnOnЬЗQl!дние злемеmов Уf1р;аВЛМ'\1S1 Windows FOTniS 895
tlu з. dо.шаlпUрD0WП. Н.ешS .дdd l "После:дний вариаr!!",") :
tb i s . ddmaiiOj1.Jp:DbWI1i. 1 tems. Add ("перВ"...JИ "В'а.р~аt<'I'·').;
tJ-li з . QOIroaiI1UpDo:vm • 1 tems , A..id (·.. ·У'ре"Тий ~ариа'ят'' I ;
till s. do:maintJ.pDeli1ti. l'Ja1JTe = :ndorna~!lLJpDown";
, J'"(i s "dошаiлОр'DQlVn. зо,сt еd ~ true;

Обрабо'I"ЧИR событии c lick дм 'rипа B1JttQl1 ~той стрщш:цы просто запраши­


вает у кажДого типа его Teнytцee 'Значение и размещает его в РаМЩ!.Х ПQдкодящего

типа l,3.bel (с именем IblCurr8.e]) В' ВИД~ форматированной строЮ:l., щщ покаяано


ниже.

privatE' '''1)1.С! Ьt.J1lS ет.$'i'! lе.сtiолs_С liсi'J( (abjet:Jt SiЭиdет I Е'\тепtl\:r.gs ,е)
[
/ / По:nучение ииФориаЦ'ИИ O~ э.леиewl'QВ U})bown .•.
1i~1С' ь п$8 i . Text. =
stTing . Forma1. I .. Стро ка: f о ·} \ J'1. ЧИС;Г! О ~ 1] f ",
dOJ!\", jJ'i!JpDowrl. Te:xt, Гluw ~r± ~llp-Do.wn. Va} LJe) ,;

Элемент ErrorProvider
в БшIы:IIпствеe npи..11:0ЖениЙ Windows Forms приходится. тэн или иначе:, про­
верн'гь правИЛЬНQСТЬ поЛЬзовательсRO.ТО ввода. Это особенно IiдCaeтCfl- диадФГQБhIX
окон, пQС'кольку.вы ДОЛЖНЫ ИНфОРМИРDвать пользователя Ь том. ЧТQ он еделал
ошибку. прежде. чем лользователь nPQДOJ1ЖИТ ввод. тип E:rr o.:rP !"p~1 ider -может И('­
IW-льаоватнG.Я длв' 'Гота. -чтобы обеспечи'ГЬ пользователю 'Виз}'алЫIые подска3~ .Б ОТ­
ношении, ошибок ввода. Т1редrtoло.жим. НaIJpимер. что у вае есть фQрма, содер;;ка,-­
щап элсмeк:r:ы ТехtБ-QХ и Euttmi. Если ПОЛЬЗQватещь введет 8 ' Тею:Вох бодее пяти
симвoлDв и Тех:tВоя утрачивает фокус ввода. м.ожио отобразить l1НфОРмацию. по­
ка:эmmую на рис. 2'1 .21.
-
"'!: "JI(ЮТl,'IРг.к",," з~е.I"'нты y"pa"~eH"-" r:-11'~If5(!

; T~ ~ PМl8l l' JJ~""", I EriiнfilW.de; ITreev;ew :, \{{l!bВroO;rer


.- _. - _ . _. ....... ~ - - ' .- ,;.., - -.,.- -...-.< • - - - --::-

С;пе,цуlOщее тексто_ое ОКНО ПDЗ_ОIU!fет


"еСТIII не 60пее.5 ·С"'.QlJО8.
ПОllрai6У'йте ._ести 601lkWе...


12'1456
L- c _ _ _ _ .... . _

РИС.21.21, ДеЙ' с,.,ви-е ErrorP:r:o"Jider


896 ЧаGТ~ IV. Программирование с помощью библиотек .NEТ

Здесь вы обнаруживаете. что пользователь ввел более пяти символов. и в отв ет


р а змещаете небольшую пиктограмму ошибки (!) рядом с объектом TextB ox . Если
польз ователь подведет указатель мыши к этой IlИ.Ктограмме. появится "всплыва ·
ющий" текст с опи санием ошибки. Кроме того, этот элемент Er rorPrmrider ClCOH-
фигурирован так. чтобы заставить пиктограмму "млтап,'· . '11'0 усилит визуальное
во здействие (конечно. без з апуска приложения вы этого не увидите),
Если вы хотите использовать такой вид про верки ввода. то вам. прежде все­
го, нужно изучить свойства класса Соп t со1. описания которых приводятся В
табл , 21.10.

Таблица 21.10. Свойства и события Corrtro1

Свойство или событие Описание

С3. 'U.sеsVаl id а t iол Индикатор того, что выбор этого Эl1емента управлени~ вызывает
проверку ввода ДЛ~ элементов упраВl1ения, требующих такой
про верки

V",lld a l:ed Событие, генерируемое тогда . когда элемент управления закан­


чивает выполнение программной логики проверки ввода

"" li cl dt .lП9 Событие , генерируемое тогда , когда элемент управпения прове·


ряет пользовательский ввод (например, когда элемент управле­
ния утрачивает фокус ввода)

Каждый элемент графического интерфейса может уста.новить дли свойства


CausesValid a ti o n значение true (истина) или talse (ложь) . причем значением по
умолчанию является [rue . Если вы установите для указанных данных значение
true, да нный элемент управления при получении им фокуса ввода заставит осталь­
ны е элемент управления в форме вьrnолнить проверку ввода. При получении фоку­
са ввода проверяющим элементом управления генерируются события Va1idating
и Va l i da.tecj для н:аждрто элемента управления. В конте1Ссте обработчика события
Va 1 idat i. п ч вы должны конфигурировать соответствующий Er Г О.гР rovider, Также
можно. HU необязательно. обработать событие Va l i dated . чтобы определить. Korдa
э лемент управления зан: ончит цикл проверки.

Тип E:r::r: orP rov i de r: пр едлаг ает очень небольшой набор членов. Для нашето
ПРИl\.ol ера самым вaжньrм является свойство Bli nkStyl e. RОТОРОМУМОЖВО присво­
ИIЪ любое значение из перечня ErrorB1 in kS t yl e , Описания этих значений даются
втабл . 21.II,

Таблица 21.11. Значения ErrorBlinkStyle

Значение Описание

Al waysBl,i 11 k Заставляет пиктограмму ошибки "мигать", когда ошибка ото­


бражается впервые или когда элементу управления l1азн.ачается
строка новой ошибки , а пиктограмма ошибки уже отображается

Вlin.kI fDiffе геп[Е rrог Заставляет пиктограмму ошибки "мигать" , когда пиктограмма
ошибки уже отображается , но элементу управления назначается
строка новой ошибки

Neve rBl i пk Индикатор того, что пиктограмма ошибки не должна "мигать"


никогда
Глава 21. ИспольэоВ~ftие элеме,t-JТО8 УПРВВЛе""iИ!l Wfndows Fprm~ 897
ДJщ примера добайьте на вкладку Е. Х r .o I Р r оvi d е r злементы управления
j1j1Zl't. t:'On,T€xt.B:ox и La.b€l. ка:к показано на р:nс. 21.2]. 3a-rем пере:гащи'J1~ в анно
проеКтРtрования формы элемент ErrcirPr:0vlder lf присвойте этому эдемewry имя
tо'оN.;зnуСЬаrасtетsЕrrО1:ргеvidеr·. Вот соответствующий фраг.мент программцо­
го ~Oдa l!1itiаlizеСёJmрсоепt().

private vOld IhitializeCQ!t\pdneI1t ()


I

/1
// tooМanyCh4ractersErrorProvider
11
tbis. tJ:>оиаТl'Усрёj.Iaс1l.еri>Е·rrqrР:rо'....idе,r . Е l.LnkRa·t е ~ 500';
thiJ3 . tооJ.l1aлуСhю.-а c:t..erS'EITQr?rbv lder. 131 i,г.kstуl е =
S yst.ern. w{ndc,w;;,;. FоnnS ..БrуоrВl inkS't1йе .}uwa y.sВl in,k..;
thi$ . t ·QoMa.l'lyCbar <'!ct'e ['sE:r:! OJ;·P:!ov..ider. С'эпt аi!1.еrСолt tQl = this;

После наС'Г[:)оiiШивнеmнего вида ErrQr]?rQv ide:r вы долщны вы:пшnrить nриваз-


1qI ошибн,и к Техt.Б6'Xi в контексте обрабОТЧJЩа СОбытия Vаlidatiл.g, lШХ DОказаво
ниже.

private vold. txtInput_ValidatiIJg '(abjeet sеп.dег, CatH::·elEv€mtAl'r;rs е)


j
/I Дnина 'l'e]!:C'ra seJtЬше 5?
i f (tхtlлрtl t . Text . Le ГJ:q.tb > 5,
{

еlэе /1 Все. nOPSДJC6, lIе mClха$JDа'1'Ъ IЩче:rо,


el:rorPr9v :tderl.Se:tEr:ro't':(,txtInput, nП);

Элемент TreeView
Элементы управления TreeVlew очень полезны тем, 'tf'fO они позволяют ·визу­
альна ОТ0бражатъ пера.рюlИ даН1{ЫХ (наприМ'ер, структуру КCJ,талогов или :ЛЮбуЮ
другую структуру, связанную отнощеRИ~ ~РОДИ'J'tЩb-:nОТОм(m"J. Элемент управле­
ния T:!:?eeView предлarает очень щирmше ВQЗМОЖНОСПl НCJ.C'I'ройки. При желaнIOi: вы
можете добавить Dолъзова:тельС1iие И;ЗОбl'ажен:и.JI. за.дат;ь Ц:В~T УЗЛОВ, элементы КОН­
'tpОЛЯ .Узла и дРyrие ВИЗУW1Ьные усо:верщенствования:. (8ЩIНТересQВанным 4ИТате·
лям за )J.Оl!оnнитеJПiНОЙ инфор~аЩ(еJ.i об этом Э.1'Jементе управле~я предлагаетел
обратиться к докумen-rации .NEТ Fram'ew:ork 2.0 SDK.)
Чтобы nродемо.нстрироватJ;. pcцoBlfЫe возможности >:I~.ПОЛЪЗО:Вa.ншr Tr.e eView.
на следУЮщей ctpaI-lИl'(е вашего Ta;bCor1t.rol мы npoг~o раЗМесТИМ э.11емент
Тз: eeView. оnpеде.IIЯ.ЮЩИЙ ряд узлов 8аив,ысшеГQ уРОВИЯ' , рредdтавJ1ЯЮЩИХ набор
ТИПО!! Сат (а1tI'омобилЬ). Каждый узел Ca.,r имеет ДJЩ прдчmtенныхуапа. предетав­
.nяющux те!tyщую (]1(OPOCT~ аВ:ГОJ'4(j)БИ.ая и люб»мущраДИОtJ'rанцию водителя. На
РИС, 21.22 обратите внимание па то. что выбрalф.ый: элемент выделен псщсйет:коЙ.
Также 38Метже, что в области элемента lapel нроме выбранного узла отобража­
Ются имена родительекого и сnеДУЮЩ~ГQ узло:В (есЩ1 по('..nедние име1C1I'CЯ).
898 Часть IV. Программирование с ПОМОЩЬЮ библиотек ,NET
т
l!!-асkвilf~IUPD~-Е~~ Т,Oevое~~~_. _ _ ____ __ ,
i : ~ с.,:О" ~i Вы Вbiбpas&<: Ci<oPOCТb: 1, i
'1 i [корость: 1 О ;~.;"~;.: I · Родите1JbСКИЙ y3eJt C~ 1
СneD.!lЮЩ~ узеn: Любi<мое рмио: ЗО.5' FM
i; Люб"мое Р.oD.ио- 90 FМ S;~ 'I
I i SI и.гl I
!! _5М'М., j,
11 .н с., ~юбимов рааИОI 90.5' FM t

Ii - CI<OPOCТb: 12 11

!I I 6i
I
Любимае раа",,: 91 FМ
См 3
.{'

ii Скаррсгы 13
I, I i±J.' car4Любимое p.wю, 91.5 FМ !
t

!l~;I~~._~, ., _____, _~]


L-- _ _ _ _ _ _ ,..-.. _ ...... _ _ .._ _ _ ~
.... ~ _ _ _~_ _ ~_._ .~","_,, ---t
!
Рис. 21.22. Элемент TreeView в действии

Предполагая. что соответствующий пользовательский интерфейс скомпонован


из элементов управления treeViewCars) и Label (с именем
Tree'Jiew (с именем
lblNodelnfo). добавьте в свой проект ExoticContr o ls новый файл С#. который
моделирует тривиальный типа Сау. имеющий Radi o .

лаmеsрасе Ex o ticCan ~ro ls


{
class Сау

public Сат (striлg РЛ т int cs)


I
petName = рп;
currSp = cs ;

p ublic string petName;


public int currSp;
public Radio У;

class Radio

public double favoriteStation;


public Radio(double statian)
{ favoriteStati on = station; I

Производный от Form тип будет llOддерживать обобщенный тип List<> (с име­


нем listCars), содержащий 100 типов Car. которые будут занесены в список в КОН­
структоре типа MainForm. заданном по умолчанию. Кроме того. этот конструктор
вызывает новый вспомогательный метод Bui ldCarTreeView (). который не имеет
никаких apryмeHTOB и возвращает void. Вот соответствующая модификация про­
граммного кода.
Гn,a"Вa 21,. !-1СПОЛЬЭО8ание элементов уIТJ}ЗВ!18НИЯ Wiпd'QWS FQrms 899
public partia1 cla&s MaihW'i ndow ; Fo:rm
(
/ / СО!lдаВИ8НОВОro Liat ,ДR_ хранении o&t.eК'J!oa Ca-r.
private 'Liзt<Саr> 1istCars = I).ew bist<Ca.r.>О ;

t:J'lwli(; l!1ai DWiпdо-.' ()


{"

11 ЗanOn!l'еиие я:чеех l.ist<> .иПОС!l'Pоение TraeVi8lV.


dOl1ble аПsе:r. = 0.5,;
for (int х = О; к < 10О; к++)
(
listCars.Add(uew Сат (s:t.ring.Forrn-аt ("Саг (Ь}"' , х), 10 -+ ХН;
.offs·€ 't += О. '5 ;
listCars [К) . ! = ne-w Radio (89. О -t off,s.et) ;

Bui1 dC.arT.r'eeVi~to; () ;
}

Обратите внимание на ТО, что petName ЕаждоI'О а:втомоб-илн зацае-гсн на ОСНО­


ве текущего зНачении х (Саг O/Car 1. Са!: 2 и т.д.). Текущая сноростъ образуется
путем одвШ'а х на 10 [от lО:КМ/Ч ДО 109 км:/ч}. а любимая радиостанция задается'
цдвигам ОТ начального зiIачения 89.0 на 0.5 (90. 90.5. 91, 91.5 йт.Ц.},
Итак. у вас есть (;ПИСОR С а т. и вам нужно dпроеnировать Э'Ги значения I{a
узл:ы anемеН'I'а управления TteeVie,w. Здесь самое важное, - понять. что 1сa»tдый
узел. как высшего уРовня. твх и подчиgенного. I1pедеТВ1UШе"ГСЯ объектом Systeirl.
WiliJ,jows .Forms .Tr,eeNod.e. полученным l-1e1IосредстоеШlО:ИЗ .Ma.::shalBYRefObject.
Вот :некоторые интереСНI>lе свойСтва 'l':reeNode.
public с'lа,5Э lreeNode : MatshalBygefOttject" IСIQлер.Ые, 1$e-ria1izab,le
!

РЧОНС' CdlQr Ba,c kColor { get; эеt:; }


pUblic Ьооl C'becks'ci { ;get; se,t; J
public ~irtual Сщ'!tехtМ"еl1\1 СолtехtМе,nu I get; .seti
риЬНс virtU81 COQtext,МenuSirip Cont,extMen".1Strip -get; set1 )
pllbli.c Colol' FGrеС<эlоr [ get; 5'et; f
publlc i.~t Лnаgеl)';н:!ех { get; set; }
public pool ISExpiOiI1ded f g€t.; }
public bool J sSelec't·e d { get; }
риЬНс bool IЭVis.i,Ые ( get; I
publlc s tJ:ing tilame ( gst; set; f
рмНс TreeNode N'extNode I get; )
public Font NodeFQnt;. I get; set; }
pubJic Trs€NodeCol1ectiofl 'Nodes 1 get; J
pulql.ic TreeNDde ~e"'JNDde '/ get; J
pu~lic эtring Te;xt { get..; set; }
p ubl ic st.rrn.g T,oolTipT~xt { get; setl

}
900 Часть IV, Программирование с помощью библиотек ,NET
т
Кап: видите, каждому узлу TreeView можно назначить изображение, цвет.
шрифт, подсказки и контекстное меню. Кроме того, TreeNode предлагает чдены,
позволяющие перейти к следующему (или предыдущему) TreeNode. С учетом этого
рассмотрите начальную реализацию BulldCarTreeView () .
private void BuildCarTreeView()
[
11 TreeView не оmображаеlJ!СИ, пока не созданы все узлы.
treeViewCars.BeginUpdate() ;

11 Очисmха TreeView 0'1' '1'ехущих узлов.


treeViewCars.Nodes.Clear() ;

/1 Добавление TreeNode д.nя XiUCДOI'O об'ЪеХ'1'а Сах кз List<>.


foreach (Car с in listCars)
(
11 До!Sasnеиие '1'ехущеrо Car в виде узла ВЫCИlerо уровни.
treeViewCars.Nodes.Add(new TreeNode(c,petName));

11 ПОJ1учеиие mольхо ЧIJ!О добавлениоI'О Car д.nи построении


11 двух nо,цчиненных узлов на основе схорос'l'И и
/1 BHyтpeRИero об'ЪеХ'1'а Radio.
treeViewCars.Nodes [listCars.lndexOf (с) ] .Nodes.Add(
new ТrееNоdе(str~пg.Fоrmаt("Скорость: (О}",
с.сиrrSр.тоstriпg()))) ;
treeViewCars. Nodes [listCar,g. IпdехОf (с) ] . Nodes .Add (
new ТrееNоdе(string.Fоrmаt("ЛЮбимое радио: (О1 fM",
c.r.favoritestation))) ;
)

/1 Отображение TreeView.
treeviewCars.EndUpdate() ;

Здесь создание узлов TreeView происходит между вызовами BeginUpdate () и


EndUpdate (). Это удобно тогда, когда заполняется "массивный" объект TreeView.
содержащий много узлов, поскольку тогда этот элемент управления Jie отображает
свои элементы, пока вы не закончите заполнение коллекцl:Ui Nodes. В этом случае
конечный пользователь не замеЧает того, что обрабопta элементов TreeView про­
исходит постепенно.

Узлы высшего уровня добавляются в TreeView с помощью простого просмотра


содержимого типа List<> и вставки нового объекта TreeNode в коллекцию Nodes
типа TreeView. После добавления уала высшего уровня этот уаел извлекается иа
коллекции Nо d е s (с помощью индексатора типа) для добавления подчиненных
узлов (которые также представляются объектами TreeNode), Как вы можете до­
гадаться, чтобы добавить подчиненный узел к текущему узлу, нужно с помощью
свойства Nodes просто пополнить его внутреннюю коллекцию узлов,
Следующей нашей задачей при работе с этой страницей TabCqntrol будет под·
светка (с помощью свойства BackColor) выбранного в настоящий момент узла и
отображение информации о выбранном элементе (а также о его родительском и
подчиненном уэлах) в поле злемента Label. Все этого можно сделать с помощью
окна свойств, обработав событие AfterSelect элемента управления TreeView.
Глава 21, ИспольэоеаЫИ'8 ЭJreмеНТQВ Уl1рав.деЮiR Windows Form$ 901
Это' событие тенерируется ПОС9Iе. Tor-o. КШ( польЗОватель вы:бцрае;г узел е Ш>МОЩЬЮ
щел'j:.Щl. мыши или кл.авиш навигации. Нбтг полная реaJ1ИЭЗ.ЦlW ОбработЧ"I(IИ.a собы­
тия J:\.j terSe le-c.t .
.p:rivate void treeViewCarS:....,lI.fterSe1e·c t ('()'b ject s'e nde-r.
ТrееVi еwD<tелtАrчs е)

11 Построе.ИJIt!'! C;:~OXК с иифQРма-циeU. о IWбраииом уз.nе.


node.In!',:) = 5tr iпg. Ротта !. ("Вы БЫО.рали; {(I) \.l'1." > e_Node, Text) .-
if (~.Npde.Pa:rent !'= 1jUJ1)
I1pde.lnfo += stIi.ug.F@·rmat ("Родительский ,узел: {О) \л".
e.Node.Pareht.Text) :
i f (е. N"ode . Nex'tNbde! = nи 11)
JюdеIrofо += s Uiflg.Format ('''Следующий узел: [О}".
е ..Node, NextNode. Text) ;

11 ~O~ J{Иформ&ци»f "" nОДСВ&'l'Хill узла.


lblNode lnio. Text "" П.оdеЙnf·о;
е .Node.Ea.ckColot = 'Ct,iOI _~lit;eBllle.·
}

Л~ТУДВ:ЮЩИЙ I!)БЪeRТ ТrееViеwЕ\:r.ел·t:Аrgs И:М~Т cBoit'cTBO N'ode. КО.торое воэ­


враща~т объе~ ' ТrееNоdе, пред(~тaвmnoщий выделенный узел. Вы можете извлечь
имя узла (с помощью ~войства Text') , как и иыена его родительского .и следУЮщего
уз.лов (е пОМОЩЬЮ свойств Pa.re.nt!NeJrtNo,de}, Обратите ВШ1мание на ТО. 'tT,o эдее;ь
,объекты Tr E'eNode', воавращающиес.я па Parent/NextNode. явно npоверmoтея на
равенство :пщчеli}IЮ null. пос.иОJ1ЪRУ пользователь МО&J.(ет выбрать IIервый узел
высше.го УРОflШI или последRИй noдчиненный узел (еCJIIИТal«ЭЙ проверки не будe'I~
может генер:и:роватъся NuilRefere'l"lceExc,e.p.t.ion).

ДобцлеlЧие графических изображений АЛЯ узлов


в завершеьще щш:rегD обзора тип.аТrееViеw давайте. добавим в:наш npиМер TP~
изображения ... brnp, ~oтopътe будут обозначать кажДР1Й йз типов УЗЛОВ. С этой це­
лью.Добавьте в O1lliО проекТИРования MainFcrm новый }(OМnOHeHT lm-аq'gL.!st (.на­
значцв ему J,J:М1J ListT reeView}, Затем добавьте в прое:кт три HOВqIX изображe.tiИЯ,
представляющщ [или хотя бы прИближенно напОI'vШНaIOщих) автомобиль. радио
и "СfЩРОСТЪ". в~бра,В PrOject.q Add New Item из меню (можете испольаоватьффы
... bmf1' ЦР1:!длаг~~:м:ые вместе с загружаемым программным кодом .примеров ЭТ{l~
книги). Каждый И3 йТИХ файлов. '" .Ьтр имеет размер 16х16 nиш:едей (чтр устщшв­
ливается через оюro СВОЙствJ. так что :е рамках 'rreeViEW DН.и будут ВЫГJщдеть .до­
статочно КОРQШО.

ПDсле соадашm: файлов изображений выберите ImageI.ist в окне цроект,иро1ilа­


ЮIЯ формы JJ поместите этИ йзображенил 'в свойство 'Ima.ges в том порядке, ~аЕОЙ
ПОЕазЩi ~a p~!z.
·2 1.23. чтобы rарантирова'I'Ь возможность nраВИЛЪ1JОГО нааначeнщr
Imag~Index (О. 1 или2J каждому узлу,
902 Часть IV. Программирование с помощью библиотек .NET

Irвag.. s (oll,,(ti,OI1 Ed,tor I'?IIXI

,i

'!
•... ___ ~ ... _____,_ __ .J

ок CaO=I

Р"с.21.23. Наполнение ImageList

Вы ДОЛЖНЫ помнить из главы 20. что при добавлении в проект Visual Studio
2005 ресурсов [таких. как точечные рисунки) автоматически обновляется соответ­
ствующий файл *. resx. Таким образом. изображеЮiЯ будут встроены в компоно­
вочный блок без каких бы то ни бьшо ДОПОЛЮiтельный усилий с вашей стороны.
Теперь. используя окно свойств, установите для свойства ImageList элемента
управления TreeView значение ImageListTreeView (рис. 21.24).

Рис. 21.24. дссоциация ImageList С TreeView

Наконец. обновите метод BuildCarTreeView(). чтобы при создании каждого


TreeNode Уl'rазывался правильный ImageIndex (с помощью аргументов конструк­
тора).

private void BuildCarTreeView()

foreach (Car с in listCars)

treeViewCars.Nodes.Add(new TreeNode(c.petName, О, О));


treeViewCarS.Nodes [listCars.lndexOf (с) ] .Nodes.Add(
new TreeNode (strinq. Format ("Схорос'1'Ь: (О}",
c.currSp.ToStrinq(» I 1, 1»);
Глава 21. /IIСПОПЬЗIJ:в.аНI'\'!З элемеНта!! Уl1РCl8леtjи~ WI'ndoW5· Forms 903
trе""ViеwСа:ц;. Nod~s ] . Nodes .Add (
[liS1;Ca;rs , IndexOf (с .)
пеw Tr.eeNo.d e (strinq, For,mat (ПJ1JOбммое -PSJU(Q: {О} FМ:",
с:' • ~ • .faVQri testat:ion) , 2 J 2»);

Обратите IЩИМa1;I:Ие на ТО·, чтQ ~а.ждъrй 1 т.а ge 11) de х указывается дважды.


Причина в том, чтоТrееN())d~ мщи:ет иМеть два уникалъJJЫХ изображения: одно длн
0т06ражениs тогда, коща узел В-t:' Bbleipaн, а .другое- коща .выбран. Чтобы упро­
стить cитyaди1Q~ мы ущшьцщем QДН()Я то же изОбражевне.дzr:я ·обеихвозможнОСтеи,
T-<m или иначе, обноВJJе.ндьШ тип Tr~eView nоказа.нн.а рис. 21.25.

. n.
~ _Ч( IОТН .... (ч.IО1f·
., I 'PtU"Hlbl '/пр ..НllL'НIo1Л
1. - '1 '-'
'~

I_ I

Р"с. 21.25.эneме~управ.nеI-lИ~ TreeView С PVtCYHl<aMi1

3neMeHTWebBrowsers
На IIО'СлеДНе.(I странице .в 9TO~ пр:имер:е будет щ:;по~зоватьея элемент управ­
леШIЯ Sу з tеm.W.ir:ld о ws.F'о r.ms.WеЬВrоwsеr, lfотарщ появился толЬЕЮ.li ...NEТ2.6.
Этот эдемент управленил пред€тащшет собо:if ошюмщш-обоэревател.н Web. встра­
иваемЬrо 11· дюбой тип FQrm и оfuтздmощеrо очень ШИРОI<ИМif. воз.-можностями на­
С троШш. I{aR и сдедУет Q$ИДc;l.ть, этот ЭЛ'е.м{ЩТ уцравл~:ЕЦiЯ определя-ет свойство
Пг 1. КОторому может быть црисвоено любое дейс;гвительное значение UЮ [U.nifoз:m.
Resouree Identifier- yнnфицировапН:~IЙ НiI{ентифщштор ресурса), Формam.'Но пред·
СТа.БЩIемое ТИПОм SY$tei!1~. Ori. На ЩшaдIo/ WebBrow,s,er до(18Вьте Элементыупр8.В-"
Л~НЩJ ·WIдЬЕrо.wsеr (е настро:(щаьш по вашему выбору), TextBo.x .{для ввода адРеса
URL) и Butt9il (для въmОЩlения: НТТР-зanpосо:вl.· J-Щ рис. 21.2~ показал вид eOD'J'-
ветствующеro окна в режиме въmоЛ»e.дИfI 11 момент назначеюm свойст.ву Пr 1 эна­
чения http://'www,intertec.h.tJ:q.ining·, c0m (~. это имеЯRО ·ТО, о чем.БЫ еей'часло-­
думали - б.еспаРДОН:На$ реклама 1\0МIIa..IlИИ. в .которой я работаю).
т
!

904 Часть IV. Программирование с помощью библиотек .NET

~\Т:.nE~H TМI~'MB
Th8 ,"oT/e.• 1 4.н/I."" be/llIto\
lеiи"ing .. "d d.i"g~

.NET WEB SERVICES


Tra"',,'g Modes

Рис. 21.26. Элемент WebBrowser, демонстрирующий домашнюю страницу Inteгtech Training

Чтобы WebBrowser отображал поступающие данные Н1ТР-запроса, достаточно


указать подходящее значение ДiIЯ свойства Url. как это делается в следующем об­
работчике событий Click ltноrncи Переход.

private void btnGO_Click(object sender, EventArgs е)


{
/ / Ус~ано.жа URL. соответствии со аначениек,
/ / ужаsаRIUJМ ,цм э.nекент& Textвox С'1'р&НИЦW.
myWebBrowser.Url = new System.Uri(txtUrl.Text);

На этом мы завершим наше обсуждение элементов управления из пространства


имен System.Windows .Forms. И ХОТя были рассмотрены не все ЭЛементы пользова­
тельского интерфейса, у вас не должно возникнуть проблем при самостоятельном
исследовании остальных элементов. Так что теперь давайте перейдем к обсужденmo
воэможности создания noльзовameльских элементов управления Windows Fbrrns.

Исходный код. Проекr ExotlcControls размещен в подкаталоге, соответствующем главе 21,

Создание пользовательских элементов


управления Windows Forms
Платформа .NEТ предлагает для раэработчиков очень простой способ создания
пользовательских элеМентов интерфейса. В отличие от (теперь уже считающихея
устаревшими) элементов управления ActiveX, для элементов управления Windows
Fbлnэ не требуется громоздкая инфраСТРУК'IYPа СОМ или сложное управление памя­
тью. Вместо этого разработчику нужно просто соэдать новый класс, получающийся
01aB~ 21, Иt:по:льэоваНt:lе элементов уnраВЛ6Н}ilfI Windows FО/IП$' 905
из UserControl. и наполнить э'Х'оттиа любыми аоДХОЩПЦИМИ своЙ(тва...м:и, мето­
дамИ й событиями. Для идлюстрации ЭТОГD npоцееса мы с noмощъ:ю Visua1 stщliо
2005 IIOcrрОИм пользовательский элемент управления. назвав его CarC'o!) t r ol,

Замечание, как и в случав любого .NEl'-ПРWlOже;ния ; вы имеете возможность nOCTpOI'ITb любоЙ


пол<>зователЬСiQ1й элемент управления Windows Forгns "ЗРУ"lНУIO", используя только текстовый
редактор и комшmятqр КDмаtt,ЦlЮ'й СТроки . Как 8bl вскоре убе.цитеОЬ r элементы управлен~я 00-
держатся в иомr:10Н!)Во'!Нl:ilх бл.оКaJ\ ".dll, по.этому В.Ы AO~I-1.bl YI$8aTb опцию /tar:get:dl.l
кеМПИl'lятqра С6'С.ехе,

НаЧНй:Те с зацуска Visuш Studia 2005 ивыберитедля новоroПРОeJtl'a рабочее I'IPO-


странст.во W1ndows Contro1 L1brary. ynaэав имя carCont!olLibr-аху (ри:с- 21-27) .

.P!\I~ tl'l*t
IJI' Vls~ С"
. ~
ii' Smer:~ Devlco
, !JetlbllS8
~~IrW
1ii' 00er~
!*' other PrOled Types

[ ott Il c.t)C81

Рис. 21 .27, Созд~ие HDsoro Рабочего nросrранстзв Windows Qontro!' LIDrary


После СОэДаИИ8 npоеК'Га аереименуй'ге ИСХО~1Й СИ-класс в CarControl. как и
в СЛ}"!ае проекта W1rtdows Appl1cat1on. ьаш аОJ1ьзоватeJIbC~ мемеит упращt'!fЩI
будет скомпонован из двух &r.l8.0COB, Файл "',De.s:lgne.r.cs СQдержит npoгp~
ХОД, .rенерltруемыЙ :инструментами проеЮ"ирова.ння, а. пер:sичыйпарцяальа:ый
IШS.СС опреДeJ1Re'J' ТШ1. па~аюIЦИЙСR из' Sуstещ.Wiдdоws.fО1;ms.ОsеrСоntrоl,

nатвэрасе CarCo·n t ro-l Libra,ry


(
public partial clas.s CatContrcl U•• %,'Cont~ol
!
public Са~Сiэn.tz:,оl О
{
InitializeComponent() ;
r
906 Часть IV. Программирование с помощью библиотек .NET

Перед тем как двигаться дальше, давайте рассмотрим общую картину того. что
мы ХОТИМ получить. тип CarControl отвечает за анимацию серии точечных рисун­
ков. которые будут изменяться в зависимости от внутреннего состояния автомо­
биля. Если текущая скорость автомобиля значительно ниже предельной скорости.
для CarControl ци}(ЛИчески будут отображаться три точечных рисунка, представ­
ляющие безопасное движение автомобиля. Если текущая скорость всего на 10 км/ч
ниже максимальной, для CarControl используется цикл из четы1ех риcyrшов, где
четвертый рисунок изображает автомобиль. медленно распадающийся на части.
Наконец. если автомобиль превысит максимальную скорость, циклы CarControl
будет состоять из пяти рисунков. где пятый рисунок изображает "обреченный" ав­
томобиль.

Создание изображений
в соответствии с представленным выше проектом первым делом нужно создать
пять файлов *. Ьтр для использования в циклах анимации. Если вы хотите соз­
дать свои пользовательские изображения. выберите пункт меню ProJect<=:>Add New
Item и укажите пять новых файлов точечных изображений. Если вы не хотите де­
монстрировать сваи художественные способности. можете использовать изображе­
ния, предлагаемые с ИСХодным кодом этого примера (но имейте в виду. что я не
считаю себя большим специалистом в области художественной графикиl). Первые
три изображения (Lemonl.bmp. Lemon2.bmp и LеmопЗ.Ьmр) демонстрируют вполне
безопасное и аккуратное движение автомобиля по дороге. Другие два изображения
(AboutToBlow.bmp и EngineВlown.bmp) представляют автомобиль. приближающий­
ся к максимальному верхнему пределу скорости. и его "безвременную кончину".

Создание пользовательского интерфейса


режима проектирования

Следующим шагом является использование редактора режима проектиро­


вания для типа CarControl. Вы увидите нечто подобное окну проектирования
формы. в котором будет изображена клиентская область разрабатываемого вами
элемента управления. С помощью окна Toolbox добавьте тип
ImageList для хра­
нения точечных рисунков (присвойте этому типу имя carlmages). тип Timer
(с именем imageTimer) ДЛЯ управления циклом анимации и PictureBox (с именем
currentlmage) для хранения текущего изображения.
Не беспокойтесь о раэмерах и размещении типа PictureBox. поскольку ха­
рактеристики этого элемента в CarControl предполагается задать программно.
Однако не забудьте установить в окне свойств значение Stretchlmage для свой­
ства SizeMode элемента PictureBox. На рис. 21.28 по казан желательный резуль­
тат описанных ВЬШIе действий.
Глава 21. Использование злеМ&НТCJII'УnРЭ:6ItеНЙR WJndow~ Fo.rm.s 9~1

РИС.'21 ,28. С(j3:цat!ие GUI режима проеКТИРО13аI:rИЯ

Затем, ИСIlО..i1Ь3УЦ 01$0 свойств. настройте IюллeJtцию lmages тиna JrnaqeList .


.добавив РИСУНЮ!f в едисоR.. При этом СОQТВt::ТcтвyIOш.иеэдеме1:IТЫ нужно добавдять
1:1 список последовате.лъНf) (Ьеm9'!'\1.Ьтр, Lещоn2. Ьmр. ьетоnЗ.Ьтр. AbQutToblow.bmp
11: EngineBl0Wn. Ьmр'). чтобы г.арантироваn. правильную цослеДQвател.ьностъ ЦИК'
ла aJ;IИМации. Т;щже следует У'Ч'иТЬШв'ТI:! то, что по умолчанию ширина 11: высота
фщ1:JIIi!В ." .Ьmр. встюw.яемъtx 11 Vis.ual Studio 2005, равна 47»47 IТИRCелеЙ. Поэ:гом.у
lЛ1аgе-Sizе длв: ImageLis.t тоже следУет установить paвнъrм 47х47 (лначе вы полу­
чин; нескоЛhf\O ИCIЩЛQеН)iОС изображение). Нанонец. настройте СБОЙ тип Тiш&r Т.:::Щ.
чтобы его свОЙствО InteJ:'val бьmо равно 200, ион 6:ьщ kiзначал.ыш О'I1ЮIIоq~н.

Реализация CarControl
После этой ПОД1'отовите.дьноЙ работы по соадЩIИЮ цодьзовательекого интерфей­
li;3. ВЫ :мощете rrристуцить к реализации членов т;ииа. Сначала создайте новый об ..
щедос'l}'IIНЬ1Й:переl:.~ень Anim'P'rames, 1(ОТОРbIЙ будет Щl.{етq "'Щены, лредстamшющие
RaЩ:IU>!Й :шемеgт ИЗ Imag€Li st, Этот переченьбудет И(~ДОЛЪ30ваться длн 011ределе·
НWJ 4еJ\yЩего фре11ма, предВ'аанач~lНОГО ДJ1Я. ВИЗУQЛИзаци:и в PictureBo,X.

11 .вспОIl!Q:t'а'1'е.nьВAtЙ перечеиь ~K изображеииИ.•


рщыltt ~num An~~Frames
{
LеШ0пl~ Lеmоп2, Lеmоп3·,
AboutTQBloW', ЕпgiдеВIQWП
}

тин Ca..r:GontrGl поддерживает достатоЧiЮ большой наБор npиватных данных,


необходимых ,цлн npед'GТавлени:в. npоrpаммной .лог:Ин:м анимацuи. ВОТ .краткое ОnИ­
саниei ШlЖДоrо из члевов.

f'llblic: РДL·t ial cl<"sa Ca.rCont:cC'l : U;s",r.Cont rol


{
1/ дa.и1&I8 СОС'1'ОRНИJI.
priva.te А.nitr(}';r.aЛlЕ'$ currFrame = дnlmFrаn'teS.Lещо.nl;
privat.e Аn.iЛIF.ramеs cllrcrМaxFr.ame = АJ1iIТ!Frarr~еs.LeroОDЗ;
private Ьосй I,sАйiш;
r
[

908 Часть IV. Программирование с помощью библиотек .NET

private int currSp = 50;


private int maxSp = 100;
pr:ivate string carPetName= "Lemon";
pr: i va te Rectangle bot tomRect = new Rectangle () ;
public CarControl()
(
InitializeComponent() ;

Как видите. здесь есть данные. представляющие текущую и максимальную


скорости. название автомобиля. а также два члена ТШ1а AnimFrames. Переменная
currFrame используется для указания того, какой из членов ImageList следует
отобразить. Переменная currMaxFrame используется для обозначения текущего
верхнего предела в ImageList (напомним. что в цикле анимаци:и CarControl ис­
пользуются от трех до пяти изображений, в зависимости от скорости автомоби­
ля). Элемент данных IsAnim используется для определения того. что автомобиль в
настоящий момент находится в режиме использования анимации. Наконец, член
Rectangle (bottomRect) используется для представления нижней части области
CarControl. Позже в этой части злемента управления будет отображаться назва­
ние автомобиля.
Чтобы разделить CarControl на две прямоугольных области, создайте при­
ватную вспомогательную функцию с именем StretchBox () . Задачей этого члена
будет вычисление правильных размеров члена bottomRect и гарантия того, что
элемент PictureBox будет растянут на верхние примерно две трети поверхности
типа CarControl.
private void StretchBox()
{
11 КокфиrураЦИJl окиа Иllобр. . . иИJI.
currentlmage.!op ~ О;
currentlmage.Left ~ О;
currentlmage.Height = this.Height - 50;
currentlmage.Width ~ this.Width;
currentlmage.Image с
carlmages.Images[ (int)AnimFrames.Lemon1];
11 ВWlСИ8ИИ8 раll1ll8РО8 НИlClt8rо nРJDIоуrrоn .. иика.
rect.bottomRect.X = О;
bottomRect.Y ~ this.Height - 50;
bottomRect.Height ~ this.8eight - currentlmage.Height;
bottomRect.width ~ thiз.Width;

После установки размеров каждого прямоугольника в рамках конструктора, за­


данного по умолчанию, вызывается StretchBox ().
public CarControl()
(
!пitiаlizеСоmропеоt();
StJ::'etchBox () ;
Глава 21. ИGПОЛliзование ЭllемеfПО8 УП,ра-nлеllk1Я Windows Forms 909

Определение П'ОnЬЗ0ватеnьских событий


тип ,СаrСоп t тоl обесд~чива.f:!I' поддержку .двух событИй., отnpавляемых содер­
жащей тип фQрме в зав:цс~ости от текущей скорости автомОбиля. Первое собы­
тие, AboutToBluW, генеРI{РУетс.л Torдa. RЩ1ДCl акоростъ C a·rCaГit.rol приближается 'J\
вepxнeJ\'fY пределу. СоБЫ',f.{<Jе ВlеwПр отправляеюся конте.ЙНеру тогда. 1I.щца тенущал
снорость станОВИТСЯ ()щ1ЪЩС n08вЬлеп.нш'О максимума. Каждое из этих событий ис­
nодьзует РОЛЪ3(i)вателъсщЦ1 делетат (CarE v el1tllandler). lЮТОрЫЙ может сод;ержатъ
адрес що50)!(i) метода. возврaIЦЗЮIЦeто void И получающего S ystem. str:]l ng в ка­
честве параметра. мы обработаеМ эти событИя чуть позже. а пеЩа что добавьте J(

Гpyrш€l QТl~~Tыx ~леNLентов Са.r:Солtг()l следУЮщие члены.

1/ СоБUPЮI и поm.зоа&~.nьоto:ИЙ ;IIe.neraw c.~.


publ ic c1elega<t;e void Саr~VеrltНq.пdlеr (stri rrg ПLэg) ;
pU:\)lic evan"t CaxEveDtHaudler АtЮlltТйВlоw.;
public ev_nt Са.rЕ"еr1tНапdlе:r BlewUp;

Замечание. HanDM.fH1M, что ·"настоя.u.u,;i1 14 палноцеННblЙ" д~легат СОМ. таву В) должен указать ДВВ
аргумента. первым из Koтqpbl~ дрлж-енбыть .system .ОЬ] е ~t (прецставЛЯЮЩI4Й QтnрввитеJ1Я) .
а ВТ{)РЫМ- тип. ПРm'l3S0АНЫЙ от Sуstеm.Еvепtлrgs. Однако дляна.шего примера ВПQЛl1е
ПОДОiilдв-т и ПР~Qженный выше делегат,

Определение пользовательских свойств


кa1t и любой друтой тип класса. элемент управденил можМ' опред~JIНТЬ Набо.,
свойств. с помощью которых внеnmие объекты смогут БЫnСНИТЪ (ИJП1 изменить) со­
стоя:виеЭТОl'о ЭЛeJvlевта. Нам понадобится: определить Т(ЩЬRO три своЙ"тва. СнаЧ<lJiа:
рассмотрим свойство Animate. Это свойство включает:или QТ.IGIЮчэ.:ет тЩI Tim.er.
11 Иcnо.пъгуе!llСЯ ~Я ltо ..~ащ.&И 8Яу~еив_I'Q !l'иnЗ 'l'ilII8r .
риы1 сc Ьооl Animate
{
get. I return 1 sAnimj 1
set

lsMim = vallle.;
;!:ma,qe'Timer . Enab..led ;:, I.sАпiJfll
}

Свойство Pe.tName выполняет то, что и следует ожидать. :исходя из его имени.
инетребует DОДрОбm:.Dt комментариев. Однаш> заметъ'Те. что при УGТЮЮВКf:' ПОЛЬ­
зователем соетветствующего имени вЫIIО.iIFIЯется. ВЫЗОйТnvаНdаtе О. чтобы это
~ саrС:Ьлtrоl Dтобразилосъ в нижней прлмоуго:льиой облаСТJ! эле:м:ента управ·
ления (сам это]: шаг будет сделан чуть позже).

/ / 8atбор Jl.Ql8JIК ~.
public st.ing Pe~Wame
!
get{ret·u:!'n carPetName; I
91 О Часть IV. Программирование с помощью библиотек .NET

set

carPetName = value;
Inval ida te () ;

Далее. у нас есть свойство Speed. Вдобавок к простому изменению члена


currSp. СВОЙСТВО Speed - это элемент, "стимулирующий" генерирование событий
AboutToBlow и BlewUp , В зависимости от текущей скорости CarControl. Вот как
выглядит соответствующая программная ЛОГИRа.

/ / Проверха currSp и currМaxFrame и генерирование соБы'l'й,,


pUblic int Speed
{
ge t { return cu rrSp;
set

/ / В пределах безопасной схорости?


if (currSp <= maxSp)

currSp = va l ue;
currMaxFrame = АпimFrаmеS.LеmопЗ;
)
// Вблизи взрывоопасgой си~уации?
if ({maxSp - curr Sp) <= 10)

if (Abo utToBlow ! = null)

АЬ оu tТ оВlо w("Чуть п о медленнее, парень!");


currMaxFrame = AnimFrames.AboutToBlow;

I
/ / Превwшaем?
if (cur rSp >= maxSp)
{
currSp = maxSp;
if (BlewUp != nul1)
(
B lewUp {"M-да
.. . тебе КРЬПliКа ... ");
c urrMaxFrame = AnimFrames.EngineBlown;

как видите, если текущая скорость становится JШШЬ на 1О км/ч ниже макси­
мальной допустимой скорости. вы генерируете событие AboutToBlow и сдвигаете
верхний предел фреймов анимации R значению An.imFrame s.Abo utToBl ow . Если
пользователь превышает возможности вашего автомобиля, вы генерируете собы­
тие BlewUp и сдвигаете верхнюю границу фреймов к AnimFrames.EngineBlown.
Если скорость ниже максимальной . верхний предел фреймов остается равным
AnimFrames .Lemon3.

L
Глава 21. ИСflользоваН)4еЭJlеМ~tjтое упрзвТ)еН·1411 Wi·лdоw,~ Form~ 911

Контроль анr.tМации
Следующей задачей нвля.етсЯ Обееиеч.еви.е гарантий того, чтО T1m 1':1 mer сме­
стит текущий фрейм визуализации в раJl.Шах Pi.ctureB.ox. Снова напОМНИМ. что
"кисло фреймов в цикле апим.ацми ·завиСит от текущей скорости авrомоби.ля..
НеоБХGДИМОСТЬ изменеНИfl1>l:зображений в pictu'teBox l'Iозmшает rОJIЫЩ 'Тогда. ~oг­
да СВQЙСтво AВimate равно true (истина). Начните с обрабоТitи u.oб'ЫТИЯ Tick для
ТЩJ.a Tiruer. шшользуя следУющий программный :код.

pri;!ate v-oid :Ltt1ageTimer_1'ick (QJ:;jjer;:t s-e1:1der, ЕvеЛ· j:.Args ~)


j
i f ,( IsAhim.)
c;urrentlmage _I(nage = .сar;rтач~s _Image$l (int) Gu.r.rFra:me] ,
11 Сдsи%' фpelixa.-
fDt neHtFrame = ((int)currF~am~) + 1;
~urrFr~me = (AnL~rames)~extFrame;
i f (.;.:urrFrarne > 'ии~ rMaxFramS!)
СЩ"rFramе = A!'_im,Frames _Lemoll~ ;

'Отображе"ие названия
Чтобы заверши1Ъ. СОЭДаЕШе элемента управления.. вы iЦОЛЖНhJ отобразить назва­
н.и~ Щ!томоБИJLff. ;Цлл этого обработаИте событие Paint ДЛЯ Cal'C.o nt.rol и в рамках
обрабртчщщ эт()го события ·ОТОбразите РеtN.а.пlе вашего Ca-rСоnt rol Е нижней npп­
моуголь1'IОЙ о&л:ас'Ти JWИента.

private 'J:oid СаrСОritrр l_Б'а1пt (object sender, Pai"tEvent:Arg$ е)


1"

11 О'Робра.:ение .иаS'84_...пmкиек :npIl.l:МОУ%'оm"ни:ке_


G'raphi cs 9 = е _G:raphicS·';
g _FilIF.ес:t.алgl е (Впц/hеs. G;r-е~:n:Уеllоw, bot tошRесt) ;
g.• D:t:aw§ul 1"1';; (PetNilme , rlew P·o:nt ("Time:s Ые,1 H01I',an-", 15),
Brus_hes .Bl~ck, l;юttоmRе·сt);

Ha~ТOM начальный эта:п построения CarCOI;Jtrol заверmаетм . Теперь ВЪП10ШI}!­


те ~цм:ai;lHOBHY своего проекта..

Тестирование ти· па CarControl


При зanyCI<е или отладке праекта Windows Control Library в VisщЦ ЗщМо2005 ис­
пользуется U.serCorrtral '!еэ! COlitail1er (испытательный конте[тер Щ).ЛЬЗователъс.~
Э!I1ементов управления). Это )'Пj)авллемый вариант Tertepp уже устаревшего Acl:l~
Control Thst Сопtainег (испытательный и.онтеЙН:ер эл-емеНТОJJ управления AcЦveX).
Этот ИНС1'румен.т автомаТИ'lески загружает Вад1 эдемент ynравлеtЩя в оь."]JYЖени~
иcrrытателыюго стенда реж:и.ма l1рректирования. ка1\ ПОRaзывает рис_ 21.29. ~TQТ
инструмент nозвшшет установи-ть для проверхи значение шоБОГQ польэоватедьсЩ)­
гО СВОЙС1'Ва (км 1'! лro50ГО наСЛ~дУемого).
912 Часть IV. Программирование с помощью библиотек ,NET

,," 'C~rCol)lrol' U~erCol)lrul Icsllol1'ailler G.-If'E1I~

Select U$Ol Control:

l':~~~r~iubra;y:~r~;;;ro\__ ~ ~-=-~ ~
Pre ~iew:
_, __-_ ~ ;R~ I L~

!;tJ {:-I,.A j. ,,_~;~_~,"~


l'i:., I 'L
; ""~j;"tТ~L~It-- N"a _... ,.,"~ (\iJ
[ ' useW,,"Curoor F. I.e
~,, ~~ '- ~ ", -;,;'. :~""
;" " AlloWDlop F;'lse
..
~'" AUloV.lidate EnabIePrevenlFoet.
Conte>rIМenuSlri (попе) iJ
EnabIetl Тг"е ' 14
v,~, ImeМode NoControl :t!
Е : ТаЫnd"Х О ~~j
~t._T ab5top Т rue --
i7: VIoibIe Тгие '1
Lemon !sf~-'" "".,," ',",,, '" ,,>. '1
f~ - - ~:' " ; Tr~
_ _ Lemon

50
~:~: '-'о ' ": - . , .- : ' ' ' - _ :.;.~.'" ~I
i

Рис. 21.29. Тестирование CarCont r ol в испытательном контейнере

Установив для свойства An i ma te значение true (истина). вы увидите цикл


анимации Car Cont rol с использованием первых трех файлов *. Ьтр. Однако с по­
мощью этой утилиты тестирования вы не сможете обрабаТЫвать события . Чтобы
про верить эту возможность вашего элемента интерфейса. нужно построить поль­
зовательскую форму.

Создание пользовательской
формы для CarControl
как и в случае любого другого .NЕТ-типа. вы можете использовать свой ЗJlемеит
управления в рамках любого языка, совместимого со средой CLR. Закройте теку­
щее рабочее пространство и создайте новый С#-проект Windows Applicatlon с име­
нем CarContro l TestForm. Чтобы сослаться на пользовательские элементы управ­
ления из Vlsual Studio 2005. щелкните правой КНОПКОй мыlПИ в любом месте окна
Toolbox и выберите пункт меню Choose Item (Выбрать элемент) . Используя кнопку
Browse (Про смотр} на вкладке .NET Framework Components (Компоненты .NEТ), пере­
йдите к своей библиотеке CarControlLibrary.dll, После щелчка на кнопке ОК вы
обнаружите в панели инструментов новую пиктограмму с названием. конечнО же,
CarControl .
После этого поместите новый элемент CarContr ol в окно проектирования фор­
мы. Обратите внимание на то. что при этом свойства Animat:e. PetName и Speed
тоже появляются в окне свойств. Точно так же, как и в случае испытательного КОН-
,
Глава 21.ИСПОn'l>аован~~ злементов УI11)ilНJ1ени'Я WindaWs Forms 913
тейнера:, ват элемент ynpамения ~ЖЩlет свое:й жизныо" !J режиме проектирова-
1:I.ИЯ. ПОЭ'{iому. ест! вы установите.дщI свойства Ani mcate .зна"'1сние t лш, вы }~ДИ~
те анимаjIИЮ автомобиля в (щи е проектироваНИ11 формы.
Сконфиrypировав начальное соетояние CarContJГo ;L , ДQб81Jъте дополнительные
элементы графичеоного интерфейса. которые ПQЗВОJЩТ увеличивать и уменьшать
CROрооть автомЬБЮl1i.. а пucже видеть T'~ СЦОРОЩ'ь авто'Мобиля.:и страховые
данные. посылаемые генерируемыми СQбытиЯJIЩ (д,ля этого ВПCJlIНe II'Qдойдyr эле­
менты управления l,a b e l ). О.nин из возмо>IЩЫX B<rPI:lWiTOВ шжазан на рис. 21.ЗО.

·,1":1 ФОР' \u Д1О тс crl<rtJ~d""Л ('II(m,lIol I-=-II!:J 11;;(


,- - - 1

Рис,21.3О. ГрафlllЧеский интерфейс клиента

Б npеДDOЛQжеli:ИИ' о том . что вы соадалn графичесиий ПОЛЬ30в8.ff.ЛЬСки.й iШТер­


ф(Щ:с. щхCiлогичн'ыЙ по1tазан.ному на РИcyRRе. npограм:мн:ый щщ в. рамках .ватера
тшщ. Form должен быть очень простым (здесь Я npедгюлЩ'aIO, что вы обработали
щ~щдое из событий Ca r Contro l спомощъlО о.кнасвоЙСтв),

p ubl i c par'tia l tla$s, Mai:nFor1!l : F'D .rm


{
public МainE'{)rm ()
(
1tJi t ia 1 i 1~ СОЩр'.о,меr1 t (1' ;
lblС).l r'rеJJ t Щ>'еJ!!d.Тех!: '= stri Тt 9'.FОImаt.(" ·! ев:у.шая СКОР,"-, СТЬ: [O t'" •
.t 1lis . ту СаЕСОП trol . Bpeed. 'J'bS'tI i)'щ () ).;
f1\.1 meri cUpDo-wn С а rS р ееd . Value= rnyCarcoot·rol , .speed ;

pri vate \! Qid I1tпnетi сUрDеwnс:аrSрееd_ Val ue.Ch~'IJ')ged (e'bj еС'!:. aen.der,
Evel1 t P.rg s е)

./ I Пр.,цпcmarае'1'CJr, ~'1'O .1 DIiOO(YII NшмriоUpDо'tПl р .... Н О I


1/ а M"~ - 300.
this .myCa.rCo:ntro l . Speed = (in·t) n .u mericUp·t)owncar5peed. У.а 1и е,;
lЫCU'r rentSpeed. Т€жt = эtriпg. Fdпnа·t {" 'I екущая CK<:JPDPT.b: [lJ}",
t.i:li s. m~ Ca:!~ntrol. Speed. TQ$tring О ) ;
}
рtiV2Фе vold my carC.Ol1·trol АЬоut!l·оБl0W (Btring mag)
f lblE,,~ntD.ata.TeKt. = striг,g. Е<Qrroа~{"JIанныесоI5ьrТИR~ ( О} " ., 1ttsg); }

private vcd:d :myCarCcntrol_BlewUp (.str:l.ng ms.g)


{lblEventDa:t:a..Te:.::t ;: зtriпg. F сrmаt("ДаННЬ1е С.QБЫТ!~": fO}j" msQ); }
914 Часть IV. Программирование с помощью библиотек .NET

Теперь вы можете запустить ваше приложение клиента на выполнение и про­


верить его вззимодействце с CarControl. как видите. задача построения и исполь­
зования пользовательских элементов управления охазывается достаточно простой,
конечно, при условии, что вы уже имеете достаточные знания об ООП. системе
типов .NEТ. GDI+ (т.е. о System.Drawing.dll) и Windows Fопns.
Вы сейчас имеете достаточно информации ДЛЯ того. чтобы продолжить исследо­
вание процесса разработки злементов управления Windows в .NEт. но здесь следует
учитывать еще один программный аспект: функциональные возможности режима
проектирования. Перед тем .как дать точное описание упомянутой проблемы, мы с
вами должны обсудить роль пространства имен System.ComponentModel.

Пространство имен System.ComponentModel


Пространство имен System.componentModel определяет целый ряд атрибутов
(и других типов). позволяющих описать то. как должны вести себя ваши элементы
управления в режиме проектирования. Например, вы можете указать текстовое
описание каждого свойства, определить событие, выбранное по умолчанию, или
сгруппировать ряд свойств или событий в пользовательскую .категорию, чтобы они
отображались вместе в окне свойств Visual Studio 2005. Чтобы выполнить указан·
ные модификации, вы должны использовать атрибуты, показанные в табл. 21. 12.

Таблица 21.12. Подборка членов System.ComponentModel


Объекты
Атрибут Описание
применения

BrowsableAttribute Свойства Указывает, должно ли свойство или событие


и события отображаться в окне обозревателя свойств .
По умолчанию могут просматриваться все
пользоватеЛЬСКllе свойства И события

Category Attribute Свойства Указывает имя категории, к которой отно·


и события сится данное свойство или событие

DescriptionAttribute Свойства Определяет небольшой фрагмент текста,


и события который будет отображаться внизу окна
свойств, когда пользователь выбирает дан·
ное свойство или событие
DefaultPropertyAttribute Свойства Указывает свойство, используемое для
данного компонента по умолчанию. Это
свойство выбирается в окне свойств, когда
пользователь выбирает данный элемент
управления

DefaultValueAttribute Свойства Определяет значение по умолчанию для


данного свойства, которое будет при мене­
но при "переустановке" данного элемента
управления в среде разработки
DefaultEventAttribute События Указывает событие, используемое для
данного компонента по умолчанию . Когда
программист выполняет двойной щелчок на
злементе управления, автоматически гене·

рируется программный код заглушки для


события, используемого по умолчанию

l
Гпав.а 2~ .. IAСПОЛl>зование элементов упрзвлеНИI\ Winduws Forms 915

Соверwенствование режима проектирования C8rCon.trol


Чтобыпродемонстрироватц И'сцользование ReRDJЮРых. из ЭТИХ ноВЫХ атрибугрв,
закройте npоект CarContrQl'restForm 13. l'!lOBa откройте проект СаrСопtrоlLiЬr<Jrу.
Дава.йте 'со:ЩВДJ-tм ПО.!IЬэова:геЛЬСIСУЮ 1Щтегорию (назвав ее МRонфмrypaция мaIllИ­
ЕПi(]. в Вdтаройбу;цут отображаться 'все свойСтва и СООЫТШl CarCo:Qtrol, Т1ЩЖе yrщг
жем "дружесt:венное" описание ДЛЯ КШ~ОГО ЧЛeJlа й значение по умолчанию ДЛИ
KCЫI{ДOГO сВоЙ~тва. для этого просто обновите ШlЖдое нз свойств и собы~ типа
C-аrСэпtrоl так. тп'обы Olffi поддер:жщш.ли атрибуты CCate.gorY1. [I1E~fault:ValueJ
п [DescriptiO:r.'J). КЮ:t 1]o-gaзЭ,Но нише.

11UЬ i i .c; раrtiсЙ с ~,a ·S.5 CarCQr:t:r:ol : U'$er.Co~tro1


{

[С'аtegоry("I(ОНфЮ'УРацаа 1I&1Ш!ИW") (
DеАCriрtiОn'("'ГеJ(8РИРуе~CR при приб.пииевии 1с npедещ С~ОРОС'l!И. "]]
pu:tlic ev€m t 'СаrЕ'VепtНашil.еr Ab.outToBlow·;

{C4teqQ1:}' ("J<:o_~aЦIU ЩlIIIМRJd") r


Desc.ltiptloJ). ("и... .аашВЙ иа1DlВlo1") ~
D~hul tVal.ue( "Le1non-" ) ]
,1 public str{ng PetName ( ... t
·1

1 Теперь ПО3ВОJIЪте npоком-ме.~тцро~щть ТО,· 'ПО Oi~начает присваива.ние свойству


змачеl-liщ rw умолчаftUЮ. поскольку, jI( j11Jep~, 'ЭТО не то, что вы моЖt}те (естествен­

I, но) ПРf;дIIолаrать. УupощеIO-lО говори, атрибут lDefa1JltValtJeJ не l"арантирует. что


соот:вет~щее Зl-lа-.:rен"е эnемент,а ДЭШ-IЫХ, npедставленного данным ,свойствоМ
будет автоматичеСЮ1установлево РЩJНЫМ э.н.ачеНI-ПО по УМОJIчанwо. Так, ~TH IJbl И
указали ;<JначеJ-ше по умЩГ{щrn:ю "U'.mon~ ДШ:I овойСТВ<\ PetName. члеlJ-rrе~Мt'нная
ca.rFetName не получит зна'If~Н'ИЯ: ·I..emon~, ПОJ(.З вы не устаирдите это значение с
помоIЦЬЮ конструктора nша ИЩ1 СИНТaRС.иса инициa.rшзациn ЧЛ~Ia ('111'0 БЫ уже на
самом деле сделали).

private string c.a r.Pe tName= "LemOI1";


Атрибут [Defanlt.ValueJ "вступает в игру~ тогда, когда upЬграммист "пере--­
V
устанав;щmает ;;щаче:ние данного свойства n- онне овоЙств. Чwбы переустано.вить
СВОЙCТlЮ вVisua1 StudiQ 2005, выберите интересующее Щ1С своИство. щemrnите-на
нем п,рaIlОЙlШОПftОЙ МЫIIlИ :и в появИВПlем.ся КOЫ:TeKCTHO~! меню выберите Res.et,
Обратите BHI-JМ<! ине на то. ~T() зtIэ Чf!ние- [D е S ~ r i р t i Q [1] при .~'f()M ПОяiшяетса в
Ю-iЖНей панели ():кна свойств (рис. 21.31).
Атрибут [catt?gory] будет ПРОНIIЛЯТЬСЛ тодыютоrд<J" когда црorраммист выби­
рает дlJЯ просмотра'в о:кне QВОЙc-rв ВИД, сrрynЩ:IрованнЪ1Й по .кат.erориям (в протя.­
воположн:ость PJ,1OCMOТPY по алфаВIIту. предлara.емаму по· умолчанию). рис. 2] .32.
916 Часть IV. Программирование с помощью библиотек .NEТ

Рис. 21.31. Переустановка свойства

Рис. 21.32. Пользовательская категория

Определение выбираемых по умолчанию


свойств и событий
Вдобавок к описаниям членов и группировке членов в категории вы може­
те настроить свои элементы управления на поддержку поведения. принятого

по умолчанию. Так. для элемента управления можно назначить свойство. вы­


бираемое по умолчанию. Для указания такого свойства используется атрибут
[DefaultProperty]. как показано ниже.

1/ ПОИ8'1'Х. о.оЙо'1'll., 8ыDИР.81101'О по уио.nчaиsao


11 .ц.п. ,I:I.ико%'о iI'n8М8И'1'а УnP ••'n8КИИ.
( "Anima te") ]
[D8~.ul tProp8rty
public partial class CarControl : UserControl
{. . .)

1
Гпава 21', ИСПОЛ,!'ЗОВЗ'liие элемеf!iТD8 упра'Вl1е1'lИЯ Windows Forms 917
Te..'W саМым вы гарантируе'I'е. тпо цри выборе пользоnателем '<Этого элемента
управления в реJюrnе rrpoеRТnраванця в QЮiе свойств автомаТИЧБСКИ будет ~дe-­
лена свойство !'>ni'ma-r:.e. Точно тт{ ~e Д.1J8 зл~ента упраЩIенЩI указыцается ~'f;>r­
fiиpаемое по умолчaI'tИю событие.

11 Поие'1'ka соб:wmrя:, I$Idбцрае,ИОl"О ао УИОnЧ&ИИIJ


11 .дяя: ;цаииоl"О ЭJIеи~а ~а1ШеRИ".
[Defau'ltEvent (n~.boutToBlown) 1

befaul tProperty ("A.IlJ,ima.te") 1


рulйi с parti·a l ela.55 CarCor:itrol : UserControl
j ... }

Тем c.aMЪJM ВЫ гарадтируете. что цри ДВОЙНОМ щеJ1'ЧRе llО..IlЬзо:вателя на ЭТОМ


элементе у:лравленця IJ режm-:tе npоеJ{ТИ;РQВщntя будет автомаDIЧeсЮl создан npo-
граммный код з-аглуnщи ,дm'! выбираеJ.V!:ОГО по умолчliFIИю события (теперь вам
должно бытl, нена. почеJ.1;Y' при ДJlОnИОМ Щ"'Jl'Ще на Eutton автоматически обраба­
ТЪШаетCJi еобыТ1tIе Cl±ck. цри двойном ЩfЩЧКе на Form- е:обытие Load и т.д.).

Выбор изображеttия АЛЯ панеnи инструментов


HaKOH~Ц. :непре.м.е:нным; атрибутом любоrо ~J:Jриличн.оГD" пользоватмьского
мемеНТ'а у:лр<UЗления. должно быть изобр~}Ще, .Представляющее этот элемент
yuразлеция :в o~'Нe П,ЦJi~ инструментов,\ В W'd.СТoiUЦиЙ момент при выборе ПОJ!Ь­
аозатеде,М C-а.:rСоntrоl среда разра.боnщ щ:щажtт 8'1'0'1' тип В naне.'iИ йНСтр:У1\-lентов
uo стандартной щш'r.oграммой RаубчаТЩI". Чтобы )'1(8Зan. пOlI.ЬЭОВ8те-m.CRое иЗо"
браженuе. первым шаго~ додЖН'о быть добавление в проект нового, фаЙла '0\'. Ьтр
{C.arControl.bmp). размеры воторого,цoJЩmЫ бытъ 16x16 t'IИRселей (у.стаяавлива­
Ю'I'ся. с ПОМОЩЬЮ cBo'i:iCТB Wi.dth и: Heigl:Jt). мы просто ИСIЮлъзуем изображение ta:r
из npимера TreeVie~""
llосле создания: по,щсодящеГQ и:зобр1Щ(eщtf[ испрльзУйте атрибут [To:olboxBi t'IТ1ap]
(Кt1ТорьПi применяе1'СЛ на ypoB;fJe тиn~). чтобы на~ачить это изображение' своему
элементу управле1ЩЯ. Первым аргумеЩ'O,liI IЮнструктора атрибутаДOJIжна быть
иgформаЦИR типа 'для элемента уцра.вленsщ, а втОрым аргументом - имя файла
... Ьmр без раСДЩРtlliИЯ.

(Defa:ul tE.verJt ,( "AboutToBl.OW"),


Defau.l ,tPropert~ ("AnirrJate lt ) ,
Tool.boJ$.1 tmap ( ty.p.o.f (CarCon trol), 11 CazCon,t rol " ) 1
public pattialclas:s Carcontroi : UserControl
j ." }

Эaкmoчителън,ЬTh:f щarом JПlJlвется зыt50Р эна~ения Embedd'eGiR:e13,ource д.nя


свойс::тва B,u ild 11.ct:.iOl1 (с IЩМОЩЬЮ OlQ-lа своi\стз). чтобы данные СDОТВетствую­
щerо изображения бы:ли: BCТP~.в 1(омпoflDвочный блок (РИС. 21.33).

3..........148. Причиной встраивания ф8~nа ..... bmp аруч\,!ую (8 Отnичие ОТ CJlУ'18Я испальЗОВSНИR Т\1па
l'mageList) ftВJ1PJ!ТСЯ ТО, 4ТО Bbl не НазНачаете фвЙЛСаrСЬntrОl.Ьmр ЭI1EiмеttТУ ПОЛЬЗ0В,!Ic­
те:льокого интерфейоа в режиме проектиро~ни:я,· ПОЭТОМУ Е;ООТВEl1СТВУЮЩИЙ файл, ". reSXHe
получает ооотвеlстаУID'ЩИХ обновлен~ых дattHblX,

1
918 Часть IV. Программирование с помощью библиотек .NET

Embodded Re,aurce
00 паt сару

Ac:tlbn
thCJ fta ",Iotes to the build and depIoyment processes.

Рис. 21.33. Встраивание ресурсов изображения

После перекомпиляции вашей библиотеки Windows Controls вы можете снова


затрузить предыдущий проект Ca rCo n t r olTestFo r m. Щелюште правой кнопкой на
имеющеися пиктограмме Ca rC o ntrol в окне Toolbox и выберите Delete (Удалить).
Затем снова добавьте элемент Ca r Control в панель инструментов (с помощью
щеЛЧItа правой кнопкой мыши с последующим выбором Choose Items). На этот раз
вы доджны увидеть в окне панели инструментов свой пользовате.лъскИЙ точечный
рисунок (рис. 21.34).

Toolbo. ®
"А RicI)TextВax
Щ Text80x
~ го<щ:>
~~:::-~ TreeV\ew

Рис. 21.34. Пользовательская пиктограмма на панели инструментов

На этом наше обсуждение вопросов создания ПОЛЬЗ0вательских элементов


управления Windows Fопns завершается. Я надеюсь . что этот пример вызвал у вас
интере с к разработке пользовательских элементов управления. Здесь была исполь­
зована стандартная для учебников автомобильная тема. но вы можете представить
себе пользовательский элемент управления. отображающий круговую диаграмму.
построенную на основе содержимого соответствующей таблицы из базы данных.
или элементы управления. расширяющие функциональные возможности стан­
дартных элементов пользовательского интерфейса .

Замечание. ЧтоБЫ узнать больше о разраБОТkе пользовательских элементов управления Windows


Forms, обратитесь к книге Matthew MacDanald, User /nterfaces {п С#: WindDws Forms and Custom
Contro/s (Apress. 2002).
ГlJавз 21, ИСТlQJ\ьзоваНl:1е' элементов у,ПР!lвлвнияWindо\'1Э Forms 919

Исходный код.Проеl(i СагC1DntГGtIU!Эraгу размещен В подкаталоге, СОQтвет~твующем глвве 21.

Созданиепользовател.Ь.СКИХ диалоговых оком


Теперь, }(оща вы I1Щ:l1iМаете роль базовых элементов YlIравлени.я Wlndo9,<S Fолns
И cyrгь процесса :построения пользовательских э;леме6ТО~ ynравле'ВИя. д.ава.й.rе рас­
смотрим вопрос Со3ДЭ-ЩШ ПOЛhзm'J:aТе.ТlЪСRИX диалоroвЬD!1 PR:OR. 3де.ёь хорошей вес­
l'ью, яв.nяетоо то, что все уже известное вам о Windows Fbn;ns оJtaзъmается Henocp~­
ctbeIO-IO прuмеRш.:fым и J( CJIY"lаю программиров~ диалОI'{ffiЫХОКОН. В ОБЩем
и целом, создание (и .отОбражейие)ди.алогьвых окоЦ J:Цr<wТЬ не более СЛQЖНО, чем
добавлеlШ~ в npоект #овой формJ.i.
В пространстве ИJII[illJ Sy'Ste.m.Wirldows.furms ддя этого нет CDеци.алыrorобэ,эо­
БDГО K.1IaCCa, Диалоrовое Olm@- это прое1'О "cnеrщaльщц( форма, Например. мно­
гме диanого'Вые ЩСl'Jа не позволяют MeIOlTh свои размеры. поэтому ДЛЯ их свойстоо
FormВorderStyle 1iIыбираетоя значение FortnВorder5,tyle.Fu'edDi.alog. Также.
в диалопl)вых анках с.воЙf.'тва Jv1iI11mi.z€Box и м'ахiщizе!30К обы:<fНО равны fal.''>!!,
,(ложь). В ЗТ6М случа~ ВИД диалогового ,окна вообще ЩJляет'С,в фИКСИРО!:!щщЫМ,
Наконец. еслн установить значение fal зе для свойства ShowIB Та s кЬэ Т. форме бу­
дет запреЩено П(Jпв-ЛRТЬСЯ в па:Ш'..ти -аа.цач Windows)CP.
чтоБы nроде~ОJJСТР}lроватъ воЗМОЖй.ости работы С диэлоrовыми ОКНalI!IИ. соз­
дайте новое WtndОWS~ПРRЛ.ожение с именеМ SimpleMO·da ):..Dia lO"g. lлавный тип Fщm
БУдет подttержИБать объект MeDll3't.rip. содер>щuций пуннты:меню ФаЙЛ~ВhlХОД и
Gе'Р~исqНао:гроЙка. ПостроИn: <1'1;01' ПQЛЬ30:QательGКиД инте:рфeii'с и о.бработctй:ге со­
оытие Clic'k ДШI :пущтов меню вы1одд и НаСТРОЙIЩ. Тащке определИте тvrеи-nере­
метryю стрOfфВОГО T~a (е именем usеrИеsэаgе) в рамках главного ТИIIЭ F'Qrm и
оtобразите cootnete-rВyIOщие данные в обработчике соб.bГ'rИ:f.r. ]'aint главной фор­
МЫ. ВОТ 1i'щ< вblI'JIящIт соответствующий проrp~ код файла Mai rtF'c-r:m. <:s.

pliblic partial class .МаiдWir,dоw : Fоrл!


f
p:riyate string llserMes5,age "Сш::ое"<Щ€'.J'iМе', заДaJ3:}!О'Е: ПG УМОJ1чанию~';

publ.ic Мa. inWir>do\" t)


j
tЛ'ltiаl:!..z·еС..,mропеп .t () ;
}

private vpid ~:r.:.itТоQlS.t.riрl!iеПJ,) Itеm...-Сliсk ·ldЬjесt sелdеr,


Ev-ецtArgs е)
{

private voia солfig\.lЦ~Т1) о lstriрМеj1lJItem_Сli,""k (object SЕшdе.r,


ЕvелtАrg$ е)

I
1
920 Часть IV. Программирование с помощью библиотек .NET

priva te vo i d MainWindow_paint(object sender, PaintEv entArgs е)


(
Graphi c s 9 = е . Gr a phi cs;
g.DrawSt r ing (userMessage, ne w Font("Times New Roma n", 24),
Brushes.DarkBlue, 5 0 , 50);

Теперь добавьте в текущий проект новый объеIiТ Form с именем UserMessage-


Dialog.cs С помощью выбора Project<=:>Add Windows Fomn из меню. Установите для
свойств Sh owlnTaskbar, MinimizeBox и MaximizeB ox значения false. Затем по­
стройте пользовательский интерфейс, состоящий из двух типов Button (для кно­
пок ОК и Отмена) . одного TextBox (чтобы предоставить пользователю возможность
ввести сообщение) и злемента Label с инструкцией. Один возможный вариант по ­
казан аа рис . 21.35.

~j,,[j.erМe~;g:o...oa~;=[~_'n] 'С ~~~~;:;1~~i}~~~:~~~~\Й~:J;f,;;~~~~;; ",:~'~~:;~'~"'


! ' I

I a~.~",
--'_
г--'_
· ..-'
,'j
____ "'i!,f'.;Й:( ~, ~~_~=-
§I
'- с

I
, . .'
. .
~J . < J 'Dki ' II ~'"'I".:;"~:
.,Iii' . :f." _ "- ;r - :< :'_"cт~ ,,<lf>t:" \'~ :>..

IL_.'__ __.. ____ ._._ _ .._ ·· _ _.__ .-,__ _" J


Рис. 21.315. Пользовательское диалоговое окно

Наконец, откройте доступ к значению Text элемента TextBox фор~ с помо­


щью пользовательского свойства с именем Мезваче.

public partial class UserMessageDialog : Form


(
public UserMessageDialog()
(
InitializeComponent()i

publiC string Message


{
set txtUserInput.Text - value: )
get return txtUse r Input.Text; )

Свойство DialogResult
в качестве заключительного заданиЯ' при создании пользовательского интер­
фейса выберите RНОПКУ ОК В окне проектирования формы и найдите свойство
DialogResult. Назначьте DialogResult.OK кнопке ОК и DialogResult.Cancel-
кнопке Отмена. Формально говоря, вы можете назначить свойству DialogResult
любое значение из перечНJ~ DialogResult.
Гnава 21. Иr;ПОЛЬЗl')вание злементр13 у.праВЛ'61-iItя W1ndows ~orms 921
рtфliС еDШn S,y stem . .\Hndows . Fопns . DiallDgRе:щlt
I
Abort., Can:cel, Ig11I.ore, No,
Nопе, ОК, Retty, Уез

Но что же рзна:ча:ет ПРlIсвацвание З'На'Ченин СВОЙСТВ), DialogRes.ul t злемента


Button? 91'.0 С80ЙСТВО МО1К.ет б1>lТЪ назначено ДЛЯ любoro типа 9utton (ЩЩ и для
самой формы}. и ОНО ПQЭВЩШет родительской форме опредemnъ. Кa.RyЮ ИЗ ЮfЩЩК
выбрал :60неЧJiЫЙ ПOJIьзо:в~.Дл.I=t примера измейИте обрабоТЧШ\ меюо Сервис'*
Настройка в Pa:мRax тида МаiпFоrrn так, как предлarаетсl'I :ниже.

privat.e vo i ·d confi .gu.teToolStripM'enuItem~Cli ck '(obj ect зепаеr I


EventArgs е),
{
IJ Соsдаки8 ЗХS8МlШlllpа tJBerмe8sageDialog.
US.blMessageDialog dlg = new Usе.rМеsв.аg'fШiаlоg 1) :

// Р&8~...иие !l'81Сущ8Ро СQQбще_• • teXtвoz..


d.lg .МеSSЕЩ€ = userMessage;

/ / Ес::ши ПОnЬ!tа-аi1'anь,.щeпxнyn ка JUlОПХ8 ОК, ~б~.-.. coodiцeuol •.


i f (DialogResult. ОК == .d lg. 'ShowDialog () )
'1
usеrt<.fе'$зэ-gе = dl g • Меэ'в ·аче;
Jnvalida.te О;
)

/ /JIyчJII1!, wоБJor O'&HCnY _.~.ИIUIХ !lи8lt_~оа 'ИШOJDUIJIО само


/ / WCQQ:r<OS08 OJUlO I ]18 ДQDIД"~ сбоpщlQtа мусора.
dlg.Dlspose();

Здесь UserMessageDialog OТdбраж.а.еТсЯ с помощью вызова ShowDialcg () , ЭтОТ


метод ЗЩIyСТИТ форму В влде м.oдa.rrbнoгo ДИвлотовоrо OH1iIa. а ЭТО. :как вы знаете,
означает. 'Ч1'о ПО1IЬэова-;гель не сможет перейти к глав1l0Й форме. ПО1W. диалоГоВое
01(80 не будет эакрь:rто. После закрЫТИn .диалоroвоrо окна ПО;llы~ователем (с ~ЩМО­
щью щедчка, на Rщ)Пке ОК илииа ннormе ОтМ6}Щ). форма CТ~T невидИl'(l.ОЙ, 110
все еще будет ост,а:ва'Ц>ся в памяти. Поэтому вы можe-rе запрос~'1'Ь у экземwщpа
UserMe'S'Q ageDialog (с именем dlg) информацию о новом ЗJ-Iачен~ MessC\ge в тОм:
случа~, Rоrда ЕОЛЬЗОвателъ щелкнул 1Ia кнопке ОК. В 3ТОм случа.е вы щображаете
новое СQоБЩelЦЩ, иначе не.делаe>rе ничего.

а.Меч.Н...·• Чтобы оro~рааиrt. неМ!'ЩaJIЬНDS' диалDГОвое ОКI(Q ()ф10·рое. позволяет переходить <JT ро·
дитenЬClCой ф.ормы l( .!3)IзлоroвоИ' и обратно), следует "ызвать Show (') , а ке SbowDialog (.) .

Наследование форм
Qдt'ШМ из наиболее ЦРИВJJеli~ аспекТО8 nocтроения диалоговых окон J3
Windows Fbnns ющя.етои насдедовцние ФОР/-i. ВЮ. несомненно. знаете. ЧТО насле­
ДОВание НВJJ.1IJе7СЯ ОДJ;iИМ из ба~08Щ ЦРIOЩЙПОВ оол, I<оторый ПОЗВDЛЯет OДНD1'dY
классу расшиpи'l'$ фующион~ность другого. оБы1но,' когда I'оворят О tiЗ.CJJеДО8а-

1
922 Часть IV. Программирование с помощью библиотек ,NET

нии. представляют один тип (например. SportsCar) без графичеСRОГО интерфейса.


получающийся из другого типа (например. Car). Тal{Же не имеющего графичеСRОГО
интерфейса. Однано в Windows Fопns ОRазывается вполне возможным получение
одной формы из другой. сохранив в процессе наследования элементы базового
RЛасса и их реализации.

Наследование на уровне форм является очень мощной технологией програм­


мирования. ПОСRОЛЬКУ она позволяет построить базовую форму. обеспечивающую
базовые фуннциональные возможности для целого семейства связанных диалого­
вых онон. Если связать базовые формы в RОМПОНОВОЧНОМ БЛОRе .NEт. другие члены
вашей номанды разработчин:ов смогут расщирять эти типы, используя при этом
тот ЯЗЫR .NEТ, RОТОРЫЙ они предпочитают использовать.
Для примера предположим, что вы хотите создать ПОДRЛасс RЛасса UserMessage-
Dialog. чтобы в новом диалоговом онне пользователь имел ВОЗМОжность уназать.
что сообщение должно отображаться RУРСИВОМ, ДЛЯ этого выберите Projectq
Add Windows Form из меНю. но на этот раз добавьте новую форму Inherited Form
(НаследУемая форма). наЗначив ей имя ItalicUserMessageDialog.cs (см.
рис. 21.36).
После щеЛЧRа на RНОПRе Add (Добавить) вы увидите онно утилиты Inheritance
Picker (Выбор наследования). RОТОРая позволяет выбрать форму из вашего тенущего
проеRта или форму из внешнего RОМПОНОВОЧНОГО блон:а (с помощью RНОПRи Browse).
Для данного примера выберите свой уже существующий тип UserMessageDialog.
Бы обнаружите. что ващ новый тип Form расширяет ваш тип диалогового окна.
а не базовый объект Form непосредственно. Теперь вы можете расширять полу­
чен:ную форму так. кан:захотите. Для проверки просто добавьте новый элемент
управления CheckBox (с именем checkBoxltalic). который будет доступен через
свойство. названное Italic.

Add New Item 5нпрlеМоdаlDi"log i?l~

dO$s

:?::
;~
II1terface CodeFr/t;' У'Лndows
fo,,"
Cuslom
Control
•• !лherrl<:d
\JSer Con!ro/

~
C",IDm
',!ieb
.Jj~
~
.,
Сomрооen! SQL Da!abase
~
'" , ;1
DataSet '(МL FIIe
~
XSlTAIe
lIJ
н'м.. poge
Cont'O/ C~ss

St,le 5heet Тех! F1le Bitтn.ap Frle CUI'Svr FiJe Imn File

Add J I CНrcel

Рис. 21.36. Добавление производной формы


Гпа~а 21, ИспOJiЬЭDваllие Э'heмеНТО8 упраllМ~И!l Win(,jQws FOfn1S 923
publi .~ part.ial cl<1:s:s ltаliGUзеrМеSS9.чеВidlоg
S±ЩРleмodalDiа1oq.US~S8.geDiаlО9

public ItalicuserMessa~~Dialog()
!
InitiаlizеСоmролеntl);

р$Нс b001 I -talic


{
set .{ ch€<~ кВо}! llal ic . Cf.ecked = va ltie;
get ( rеt.uп! сhесkБо~ТtQllс .' С!):ес~еd; J
}

Teдep~. имея ПЬДЮIaCD ба;З0вога типа 1Jsеrыез:sаgеЬi Q l0g. ~змеВИ1'еМа.LnF' Qrm


тан. чтобы довое своЙство Italic Можмо было исцользовать, Просто добавьте но­
в.ьr.й чл~н-переменную типа Вооlеап.цля ИСЛQцьзоваlllИКII,pИ построении объекта
ront. :предстЗвЛЮ<JIЦerа курсивный шрифт. и измените ~)бработЧИЕ еобытия Click
для .мeroo Gi;lP-ВИС9НаCJРОЙка Так. чтобы ИСРО;Jlli30Вался lta 1 j.cUserl-4€5sаg.еDiаlоg.
Вот как может выrл.ядеть охончатеЛЫIЫЙ варИafJТ nрограмм;наго K(jдa.

public part1al clas'& МilinWiпdьw : у оу n\

!
j:;>x:iV<1te . s'triл.g· DserMessa·ge = "Def aiil t _~Je.!i!,·sage";
pr!vaite ьооl textIsItaLic = fa~se;

p .r iv.at", ,,'oiQ сопfigurеТооlStriрМ€лultеm Cli.cJ«,obj-еGt s-ender,


E'le.!1·tArgs е)

Itil-li0'UfI~ssаqеDiаlо9 d1g =new Itаliс:t1sеrN&!fsаqеЮiaJ,.оg () i


d1ч .IIЩs:sаgе = 11serMeS$ёlge;
dlg. Jt.a.).ic ? t:ex'tIs-Itаliс;

/ I Е.ели пО.ш.Зов&rв,nь щеnрул Па ОК, Q'.l'оБРS9И'J!Ъ с::ооБЩевие.


ti (Di.a 1 бgRе5ult "ОК = dlg. ShowD'i а 1 bg ( .) )
(
J.Jsеr!1еS$аще "" cllg. JI-~€$$ag!e;
teztlsltelic = dlg,Italic;
lI'il1'.ali date () ;

/ j Л$-"Чillе, q:rоб;,J БЧИ'С'I'КУ внутренних Э ,Т1емеи'rОВ BъmOJ11i ,<tТi(", сам>,:)


1/ лиаЛО1'6воIE Ol<RO, lje ДОJ!(И,щ,аясь · с;Ьорши*а мусора"
fНЧ, DispGse () ;

р З:- ~'fj3tе vo:ld МаiПWjЛdа:w_РаiIit iobj-ect sелс:!еr, раiпiЕv8пt.~g-$ €)


l
GI".a phi Щ; 9 = е лr~Р!l-iсs;
FЪn.·t f = 111:111;
lf ·Ctextl Sltali,cJ
f = ТJew E'yl'1{ ('t!i.mes .New RciПlюi", 2'4, FOJ1tЭtуlе,Itаliс) i
e1se
f = newFont j" Times New ROlI1a.n·', 24) I
g. D.rаwstrlng\usехМеsэаgе, f, Brushes. DaJl-kВlu,е, 5;0, 50).;

1
924 Часть IV. Программирование с помощью библиотек .NEТ

Исходный код. Проект SimpleModalDialog размещен в подкаталоге, соответствующем главе 21.

Динамическое позиционирование
элементов управления Windows Forms
Чтобы завершить эту главу, давайте рассмотрим несколько подходов. которые
можно использовать для управления размещением элементов управления в форме.
Если при соэдании типа Fоrrп вы предполагаете. что элементы управления должны
отображаться с испольэованием абсолютных позиций. то это. ПО сути. 0значает.
что кнопка, раэмещенная в окне проектирования формы на 1О пикселей ниже и
на 10 пикселей правее верхнего левого угла формы, будет там оставаться в течение
всей ее "жизни".
При создании формы, содержащей элементы управления пользовательского ин­
терфейса. вы должны решить. должна ли форма ПОЭВОЛЯТЬ изменение размеров ее
окна. Обычно главное окно допускает изменение размеров. тогда как диалоговые
окна - He1.~ Напомним. что допустимость изменения размеров формы задается
свойством FоrrпВоrdеrStуlе. которое может принимать mобое из значений переч­
ня FоrrпВоrdеrStуlе.

public епит Sуstеm.WiПdоws.Fоrms.FоrrпВоrdеrStуlе


{
None, FixedSingle, Fixed3D,
FixedDialog, Sizable,
FixedToolWindow, SizableToolWindow

Предположим. что вы захотели изменить размеры формы. Тогда в связи с со­


держащимися в форме элементами управления возникают интересные вопросы.
Например, если пользователь сделает форму меньше, чем необходимо для отобра­
жения всех элементов управления, должны ли эти элементы управления изменять

свои размеры (и, возможно, расположение) в соответствии с размерами формы?

Свойство Anchor
в Windows Foпns свойство Anchor используется для определения относитель­
ной фиксированной позиции. в которой всегда должен пребьmать данньrй элемент
управления. Каждый проиэводный от Control тип имеет свойство Anchor, кото­
рое может принимать любое иэ эначений перечня AnchorStyles, описанных в
таБЛ.21.13,

Таблица 21.13. Значения AnchorStyles

Значение Описание
Bottom Нижний край элемента управления прикрепляется к нижнему краю контейнера
1eft Левый край элемента управления прикреn.nяется к левому краю контейнера

None Элемент управления не прикрепляется к краям контейнера


Right Правый край элемента управления прикрепляется к правому краю контейнера

Т'ор Верхний край злемента ynравления прикрепляется к верхнему краю контейнера


Глааа 2.1.ИСПОJ1ьзовзние элементрв управления Windows Forms 925
Чтобы закрепить Элемент в· верхнем девом уг.Т!у оина. ~ОЖНО связывать со­
ответствующие значения операцией ИЛИ [напр,;имер. Al1chorS~yles.'I'op I
AnchorStyle в. Left}. Целью ПСПОЛЬЗ0ВaIЦi~ свойства i\nchOir является указание
того. J(aRие расстояния от краез элементаynравлеlЩН до краев 1tонri:Й1reра долж­
ны быть фиксированы. Например. ес1Щэадать для ююпки следующ~е эначение
Аш:tlОr:

11 3axpeшt8JO(е ЗIJ8.1C81Р1'& О!l!КОCИ'1'9JDwНD ЛР_ОIlа краа.


ro.УВL1ttоп .Anchoz = AF!chorStyles .1\1qhti

то вы гарантируете •. что при переопределении размеров фор~ щцпraя кнопка бу­


дет сохраюлъ свое ПОЛOJi(ение QТНОСИтельное правого КрЩl формы.

Свойство Dock
Другой особенностью llpOI'раммированил WinQOWS Fom1S ~теЯ ВD8МO)fmOCТb
задать ~ поведение элементов управления. С помощью ·своЙства Doek
элеменТа управления можно указать. ЩЩоЙ: CT(i)POHI:!l' (J.JlllII Jl:акИХ СТОРОН) формы
д1'iЛЖeJiJ касаТhСЯ данный элемент. Значение. 1Ю1'орое вы цаЗБаЧJПе свойству ООСУ>
элемента управления. учитываетcg вне зави<;m.rости от Т~}\.."УЩИХ размеров Q1ffia
фор:м:ы.. Допустимые зиачеltИя описaн:ы .n табл. 2] .14. .

Таблица 21.14. З~ачеНJrJR DockS t yle


знач ...... Описание

BOtt0I11 'Г\ИЖJ,lИ'l1 ICрай элемента упраWlВНИВ стыкуется с нижним Краем i(ОI1Т6Й1iерноro


элемента УJ.lр<!В1JItfIИЯ

Fill Все кр!!я элемента ~правления· стыкуются со есеми' краями КОl1тейнерного Э]1~
Me~lтa УПРЩlления. и соответствующим образом IIIзменяеТCfl размер

Left Лesыйкрай Эll8мента Yf1равлеlU1Я стыкуется с левым кРаем KQHTemtepHorb зле.


мента управления

None Э:nемвнт упрasl1ени~ не .стыкуетС!! с кр!!ем~шпейнерного элем.вита управлеt-l14Я

"Rigbt ПрваыЙ край элеМенТа управления стыкуется с правы~ краем контейнермого


'Элемента управления

Тор Верхний l<pаИ элемента управnения стыкуется с верхним lC'Pa~M I{OHTeiilHepHoro


элемент~ ynрasлеi'iИЯ

Нацрим~р. если вы ХОТИТ~, "Чтобы данный эл,eiI.lе'нт Bcerдa распoлarалсл по лево~


му краю фор~. вы должны н.эли.щтьсле.цующее .

11 эwIoм it.п~ПI .сеl'.... ра~.l!ф. rto n..оку 1(p8lO Формм,


11 .И••UXalOlО 0'1' '1'.IC}IЩJC~ P&Ц8po.~pat·.
myButton,·Doak = DookStyl~.L.tt;

ЧТQбы ПОИЯ'J:Ь. вр что "зьшивatrCЯ" устанОЗJ(a свойств AnC::hor 1{ Dock. рассмо­


трите проeRТ Ащ:hоr;\'nq,Соntrоls, .который содержится в зarpужаЕ:.МОМ файле l'Iри­
~epOB ДЛ1J; этоЙ ~. После' RQМПQВОВКИИ запусна этоГО приложевия вы сможете
жщоJtЬЗО1lll.ТЬ его сис.т&му мeНJO ддя уст8ИОВКPi Р&ЭJ'IИЧНЬ1Х значений Apcho~Style ,s
и Dock5tyle, чтобы набmQ.ца,-ь измеаеНИR. происходsшwе при 3ТОМ; В поведещщ
типа B1i!tton (P1:lC. 21.Э7).
926 Часть IV, Программирование с помощью библиотек ,NET
т
Не забудьте переопределить размеры формы при изменеlШИ свойства Anchor,
чтобы выяснить, ltЭR на это отвечает Button.

ИСХОДНЫЙ КОД. Проект АпсhОГiпgСопtгоls размещен в подкаталоге, соответствующем таве 21 ,

Anct"a: Тор. Le/t


Dock: ~

Рис. 21.37. Приложение Anchoring Control s

Табличное и потоковое размещение элементов


в .NEТ 2.0 предлагается еще один способ управления размещением элементов
управления 8 форме - с помощью одного из двух администраторов размещения.
Типы TableLayou tPanel и FlowLayoutPanel могут использоваться в области ми­
ента формы с целью управления размещением внутренних элементов управления.
Предположим, например. что в окне проектирования формы вы поместили в фор­
му новый элемент FlowLayou tPa nel и настроили его на cтыквку со всеми краями
родительской формы (рис. 21.38).

p-.. -,~-_. ' -:-" "" , ' c..>~"""_ ~~.~ :':-"-'::;::"~;::"7"" _ _ _ "'~_ ... -- -~"" '~-''''''''..:-'' •-•~
-::""" ....:.-1:-'1':..:.-
I 'fIowL")'Dutform.cs [Оеligп] ~~t.,Page [ .:" " о' ·· .Х !
I :

!
I

I
!
I

с_. ~" .,_.,.,._ ~._~~ ,~c.


•• __ ___ ,. __ •• ~._.,_._ .... _,_ . ___ .. ___ J
РИС. 21.38. Стыковка FlowLayoutPanel в форме

в режиме проектирования формы добавьте в FlowLayoutPanel десять новых


типов Button . Если теперь вьmолнить приложение, вы заметите, что вати десятъ
кнопок автоматически распределятся в форме. и это очень напоминает стандарт­
ный НТМL.
С другой стороны, если вы создаете форму, содержащую TableLayoutPanel, вы
можете построить пользовательский интерфейс, который будет поделен на "ячей­
ки" (рис. 21.39).
Глаеа 21. и.СnОЛ~ЗОВfil1и·е элементов упрввпеНi-1Я Wlrtd,oW$ Forms 927

. - - ~ ~ . -
• 'ii, 'I<Ф1"t i!;lQl.ft~'<щltl . _ ci' х

М<!'!'<QIOI

~! ~e""'''e L<>SI Q,I<JmI1


I R;;itфв1.iiSt_
. . bli~Y:~~~~" ~ _;

Рис,21.39. Тил Tabla1:.a.yol1tPa.-пе 1

Ес:1IИ вы выберете иУIIКТ Edit Rowsand Соlumщ; (Реда:ктирова'IЪ строки и столб­


ЦЫ) ид меню вмроенното реЩlliтФра элемента в р:кв:е проентпрования формы (:ках
шжа~ai-ю на Рl'lс.21.З9). то вы сможете ИЗl\>fенить формат ТdblеLа'УQutРаnеl . ДЛЯ
'КЗЖДой ячейки (рис. 21.40).
Правда, еДIiНс"П}енвой ВО3МОЖIlОСТЫО YВMeT~ реаyJIЬта'I'Тa1tой настройюs 1'ипа
'таыlLауооtр.аnеl1 являетСя провержа вручную. Предлагаем заинтересованным чи­
тателям Elьmолвить $'1:0 задarmе самостоятельно.

;- - ~ -.-.- - -- - -~ -- =:3'
>110... 'Cdm:ls
r 1~ ;;,,~ ,_ _: _ _ _ ._ .- _ .~
_
,-- [ ..___
1'''''.
. ~~
y~
JO
е!ЩoalJ~
.,
Ca!ymt12 'Щ .!ЗtJ~. о Per=1 г-:-_~ , ,"

с.оUщ)
о AUlD5iZe
I i ' CGliпц~ ~"d!БW spanning,
I .~; if\!DJJwa"t.~..o<IIt<l11O <1'' ' f1Н1!tшl • ..,...,о1
~ иttloe'RO.61>.<''I .nd CoIumtt!;p.1!I
pfopblti~. 011 tIIe "'Л!ТJ>'.

1) .ttIvn!IJem 8I\d .Щt~"'.g: .


1 1f '~1I 'waт tD -8: IП~ § 9101Т'Q! ?titbm а C.eU; ~y:
. If УI>U ....<\t, <otttrol Ь" sl;rt!Ф .,;thJ~ "QI1,
us ..~ I;Ql!b'ar" д1!<bat proPeltjl.

I
...
~ ----_ .:....._ ~ .

ок JI 'C~

Рис. 21.40. Настроика ячеек типа 'l:a:.~leLaYOl1tPa nel


928 Часть IV, Программирование с помощью библиотек ,NET

Резюме
Эта глава расширяет ваше понимание пространства имен Windows Forms путем
рассмотрения возможностей элементов графического интерфейса пользователя. от
самых простых (таких как Label) до "экзотических" (таких как TreeView). После
изучения множества типов. соответствующих элементам управления, была рас­
смотрена задача построения пользовательских элементов управления, включая их

интеграцию в среду проектирования,

Также в этой главе вы узнали, как строить пользовательские диалоговые окна и


как ПОЛУЧИТЬ новую форму из уже сушествующего типа Form. используя возмож­
ности наследования форм . 3авершилась эта глава кратким обсуждением возмож­
ностей закрепления и стыковки элементов управления, а таюке новых менеджеров
размещения .NEТ 2.0, которые можно использовать для задания нужного располо­
жения элементов графического интерфейса в форме.
ГЛАВА 22
Доступ к базам данных
с п, Q'МОЩЬЮ ЛDО .NEТ

Е сшi вы не ЯБляетесь профессRoнальным: РCJ.зработч:ющм видеоигр. вас. на­


верное. заинтересует тема дос'rynа R базам ЩlliНЫХ, к<'щ И сцедует ожидат~.
платформа ,NEТ опред!:лJI'ет деJIЫЙ:р.вд npоСтращ:т~ Ю<J.e;FJ, обеслечивдющих взqи­
~'Io10действи:е с :т.ю:кальНhThfi! И удаленными хp;;urnлищами данных. Эти цро<,:транства
имен известны ПОД общим названием ADO.NI'.,T
В этой rлаве. после того как будет ·очерчена" J>9ЛЬ ADO.NET (В сле;:{Ующем раз­
деле.), -МЫ обсудим тему поставЩИЕов данных ADO.NEт. ПЛ<J.тформ~ ,_N E'f о~еспечи­
мет поддеР)llliy целого ряда постанrцmroв данных, Iiа.жд:ы:й 1113 которых оптиNIИЗJ.{­
рован , ДЛЗ доступа Е КОlШре'I'НЫМ сИстемам управления базами д!ЦiНЫX (Micr-osoft
SQL 8e:rvщ Огасlе. MySg,L и: т.д. ). lloC.!!le того RaR вы QСВЩIте дринциды вэ,аимодей­
сrnмя с RОНКРe'l'НЫМИ пос'НlВщикамИ данных. мв] с вами paCCMOTp~ НО'ВЫЙ ша­
блон постд.ВЩИl(аданных. предлагаеМЫЙ.nлwфор.·~1ОЙ .NET2.0. Исщщъзуя uшы из
пространства имен Sу:stеl11.Dаtа.Офmmоn (и фа:йп app.conf.i,g)., МОЖ;НО ПОСТРО1:!ТЪ
едm-IЬ!Й дрограммный ЕОД. с помощью :которого дnнaм.:нчeски выбираетСfl ~LЫj]
пocтaвu:tJfR данных. без неоБХ'ОДИМОСТИ перекамmmящш 1>1 поВторной инСталляции
npиложения .

В ocr<шшеЙСJ1 части rnam.r 'будет выяснено. как протраммно взаимодействовать


с ре.11if:!ционными база.1\ЛИ да:1юblX, испо.!IЬзуя наибо.ТIее подходнщий для вас поСтЗБ­
IЦИК данных_ Бы сможете убеДИтьсн :в том. Чi'Q ~I\DO.NEТ обеспещшает два раан:ых
YPOB~H взаимодейств:~m с истоЧI-nlliоМ данных, -часто назЫваемых СВЯЗНЫМ .Й не­
СlЩЗliЫМ blPQ8JЦl:.Мl1., Вы Y~Haeтe о роли ооъею:ов сое.ф1не1'lИЯ. объектов команд. объ­
е!С'щвчтеНЩi д.aннbIX. адаптеров данных и множества дРyfиX ТИПОВ из просТран­
ства имен System.Data ~ часТJ-ШС'rИ , D'a taSet, DataT·able. DataRow. OataCol.umn.
Data\liew и Dat;.p-Relation}.

Высокоуровневое определение ADO.NET


Если вы имеете onыт прим:енеНИIl дредPIДУЩе:й мо:це,'IИ МiсroзоП доступа ]{ дан­
ныM -- .модеJIИ ЛDО (ActiveX Dащ ObJects - об~екnц данныхАcti~). основанной
на использовании СОМ. - вы ;Ц;О.1JJЩibl донять, что ADO.NEТ имеет с ЛDО очеЮ>
мало общего •. RpOме БУКВ "А" . "О" и ·0". Хот," и верно ТО', что неноторая взаимОСВЯЗh
между ЭТИМИ двумя системами имеется (НЗПJЩмер, в RaЖДОЙ из систем ИCnоЛьзу­
ются понЯ"tИЯ объeк:rа (:оединени.я И объщсrа ~o:мaIiцы•. не:котррые ПРИВl>Iчные . д.r1Я
т
930 Часть IV. Программирование с помощью бибшютек .NET

ЛDО типы (например. Recordset) в ADO.NEТ больше не существуют. К тому же.


ADO.NEТ предлагает целый ряд новых типов (например. адаптеры данных). ROТO­
рые не имеют прямых ЭRвивалентов в "классичеСRОЙ" модели ЛDО.
В отличие от классической схемы ЛDО, которая была разработана. прежде все­
го для жеСТRО связанных систем RЛИентjсервер. технология ADO . NEТ была постро­
ена с учетом "разъединенного мира", на базе использования DataSet. Этот тип
представляет ЛОRальную копию набора связанных таблиц. С помощью DataSet
клиентское звено приложения получает возможность 'lитатъ и обрабатывать его
содержимое. будучи отсоединенным от истоЧНИRa данных, чтобы затем с помощью
соответствующего адаптера данных направить измененные данные обратно ДЛЯ
дальнейшей обработки.
Другим главным отличием ADO . NEТ от классической схемы ADO является
то, что ADO.NET предлагает широкую поддеРЖRУ XМL-представления данных.
Сериализация данных, получаемых из хранилища, выполняется (по умолчанию) в
формате XМL. Поскольку для обмена ХМL-данными между уровнями взаимодей­
ствия часто используется стандартный ПРОТОRОЛ НТТР, на ADO.NEТ не влияют
ограничения устройств сетевой защиты (брандмауэров)..

Замечание, В . NEТ2.0 сериализация типов DataSet (И" DataTable) может выполняться в ДВОИЧ­
ном формате (с помощью RemotingFormat). Это может оказаться полезным при построении
распределенных систем на уровне удален~IOГО взаимодействия .NEТ (см. главу 18), поскольку
двоичные данные оказываются гораздо более компактными, чем данные XML

Но. возможно, самым главным различием между классической схемой ЛDО и


ADO.NEТ оказывается то, что ADO.NET является управляемой библиотекой про­
граммного кода. RОТОРая, следовательно, подчиняется правилам. сформулиро­
ванным для любой управляемой библиотеки. ТИпы. ВЮIЮченные в ЛDО.NEТ, ис­
пользуют тот же протокол управления памятью CLR, 'I)' же систему типов (классы,
интерфейсы, перечни, CTPyкrypbl и делегаты) и так же открыты для доступа любо­
му язьmy .NEт.

Две грани ADO.NEТ


Библиотеки ЛDО.NEТ MOryт использоваться в paмRax одного из двух Rонцепту­
ально различных способов взаимодействия; на связном или несвязном уровнях.
При использовании связного уровня ваш программный код непосредственно сое­
диняется с соответствующим хранилищем данных (и отсоединяется от него. когда
задачи взаимодействия решены). При использовании ЛDО.NEТ в такой форме для
взаимодействия с хранилищем данных обычно используются объекты соединения,
объекты r"шманд и объекты чтения дaшrыx. Позже вы сможете убедиться в том. 'lTO
объекты чтения данных обеспечивают способ извлечения записей из хранилища
данных на основе подхода, допускающего только чтение в режиме однонаправлен­

ного доступа.

Несвязный уровень. напротив, позволяет получить набор объектов DataTable


(содержащихся в рамках DataSet). функционирующих, как клиентские копии
внешних данных. При получении DataSet с помощью соответствующего объента
адаптера данных необходимое вам соединение открывается и закрьmается авто·
матически. Bъr можете сами догадаться, что данный lJOДXOA позволяет быстрее
1
tmoo.БОДН1Ъооединение ШIir дp'}1'I1tt ~ЩИХ aOOl1eRТOB.lIoooe D()лr--ч.enия объе.п­
-Т~ !JatZ:Set клиен::I'OМ 3m'!' -юшен'I' JJIФ1!:СТ' проематрива1'Ъ n 1\{ео.щь ео:neРЖ:НМ()t~ ()бъ~
i:tI:trn. lIесозда:в~fI .ilJiШШelИ 1'larpy;aъ.'1'J на CeTeDoJt трафиt!.. Ч1'оБEt напраmt'I'b И$Ю8-
liшmr;rе jI;?J.нm.te 06p~o I!I xpm111JIШДf' Дf!l:l':1l'IbL'. 1,лие1-\Т МQЖet СЙQ1F8. пe.rroДР30$ТЬ
aдamе-JiI дa.IOIbLК 1:2 СОВОХУП!ЮСТkI С UHO!JIL:ec:I'1aOМ ПО~ tjrrepiЦ'(),pos sgL}. J1. ЩI­
OO~ OOnавi1lеЕ!ИЯ ЯС'1'Qtnmыа,цilШUЮi: СЩ:.1UШeI;L~ diНдШi пеме~lQ рattрьщаетf.$.

Поставщи'ки р-анных АОО.ЫЕТ


ЛDО,:\'fflТ:!:1!! ЦР~tltf'Щ:Т :СД;ИВ{),J'О ~1a:6(i1pa rшw:в д.~ ~И с1) J!~<61Щ'!: tfЩТ~.щ1'ЩЕ.'
.}"ffPЩ:П:rl~Я б'а.за~rfl даЮl~ rС~'БД}.ВiВ.~естО' Э!J1'u.rQ APO.NE'J;' ЯQМ~ЖI1.ВЗетм:Ef.().'Щf:­
'e'rno noеrпавЩ;1.LIoa!8 drщ-ШЬЩ J:ЩЩды1! из K-9TapЬr.1i4 ОЩ~,0;flавЩ:l ддн:В-3~.tt~Й~
СJ'ffifЯ е С~1Jr:Щ ~,II)1lК:pt"mf;'lro B~._ Qд;вим да ~рЩ'fМYЩOC'JfiI'I та:'Щ)1;Q ]J,DдJФдa JфJJneтрл
'то-, что .Ra'f'..ды-l! .Dое'h"f\.БЩИБI Jjl,щnn.~ МОiЖет L7Po~pos~C'.6- с }",,~'~:М YJdЩt~JI:Ь~
дщ l.:!сОО~mrDf'Т~j,jсшrrве1'(.'J~ующе~~д. Др!П'им ПРel4щущt::.t"r'Щ)М; ЯR.<I:fI,етсЯI ТС).
<0'70 ("I):e~а.i'r.Iiг.iJиРОВ;1RНЪЩ П()!..,~ ~:"щi1ных _~ ~ДИ'FШ'ТЬ~ '~peдC'Т;Eтtн~1
t' щ;qI01I'1 Q1БД, '006 !~ЛЫfOI!ШНjШ '~М~'О>1ЦЩro УРШJRН {)'mб~t"JПЩ1 рoJш.tеIцi:t­
'eblOrO I\I(~' ~ВЖ'iывакщw~сн J:"Л)pt'lRаМк.
1l:I1;РОЩtyИ1.), [ooo~., 11'C1l;fI'IОЩ.цш(' ~- ~'T~ l-liaИОР 1'ЛIIйВ, QТl'PtoДел~:В дан'­
БjОМ! I'IpQCf.rpaw~&~ YМefl J!i ~~ащ~"~ .щ:rn: ООЩ9ТЬqIc С. JtfJJiiДРeJ1П:ым И('''I'()~t
~ ЛЮlб~л:rnо.l,Ibi3-Уемf.>'tЙ lIa.мJl !iJl')<,>тa::ElЩИlt даз:!l~lhm .опреДtшяe'F BOOOp:ТmIOE.
~б~O:I:Wi~ OO:sЩ'!bltl ~l;l'IiQl'I~e .воm..iШЖНOO'I'и.. BТl!lfjJf. Т.2. ] .i:Л1Ц(IaНЫ :ве­
Бlп'аtщl\'l '~~~Щ:i!е \Dб'м'кIi'ы~ 'ИЪ~I;ШЫ€ JU~('cЫ (н"с' они L'1rJp€;Д~:rLR:ь6тcE в п:ро.с:гр;зн­
ItJt'Б.ft }{Мщr Syst9m.,Thait01J i ~~fI1!Щ){J 1. 1'1. $1OO,.'Ш.эО В Э'f'l F !1.IJРВ них Jште.РФf-,йсы (uдИ ~дe­
ДJIЮ'J['еЯ J3 'SY1l>t ~,Da;t Pi}.

PeatJиаОВDШlые
'мнтерФu'Ь!

ОБЬiМ1if ()f;;rI:jA-,'j:t'j1j!~IJ~Я'..
dбеtпеч'имe-r }Ю(lМO'ЖI4Q~ТЬ ею­
$l'IlifеfJИR
I'i! ~вН'иriИ!цfJlA .цa:IJH\;J.j(
~,crrКil~~!t!Я, "N 1o!8Г~'t а ~1Qil~
дооryn ~ СХЩ:'1Ве'ТОТВУЮЩ!1МУ
оБЫ:llrrу TpaH3a~

O~1f1: ~lCtМаl'(ЦЫI,
П~JJ:стга"М1..sQL-заrrpOQ; ...i1J>1
I~MI'I храf.1:ИМ@И flp~ypl!iI'. 1:1
так.же ,Ь6еt;lilвq,IIIВШй'r.Ю!С.yт;Ii к
ооъещ 'Il'eH~il' ДЗI\IIi\:IIJI {';IЩП1.'l6Т­
l,тsующtlго [1UС1Щ.jЩИI(В ШitIt\blJ(

IDJJI. t aR:ectd.E. г"' Об'!]e)q 4тelil~1I ЛllННЬW,


IDa-t.a:Rе6D:r>d О.беC'l!Еf!Иlilзет OAНOWlinPfIВJ'1;!L'I­
НЫlй, AD~J'n 1( J;i8fj~~blM в rm-*'i<IМEI
"WJibrtO д:п.я <tЮ1Ч\IIЯ'
т
932 Часть IV. Программирование с помощью библиотек .NET

Окончание табл. 22. 1


Ре8лизованные
Объект Базовый класс Описание
интерфейсы

DataAdapter DbDataAdapter IDataAdapter, Объект адаптера данных.


IDbDataAdapter Обеспечивает обмен объектами
DataSet между вызывающей
стороной и местом хранения
данных. Содержит набор из
четырех внyrренних объектов
команд , используемых ДЛЯ вы­

борки, вставки, обновления и


удаления информации Из храни­
лища данных

Parameter DbParameter IDataParameter, Объект параметра.


IDbDataParameter Представляет именованный
параметр параметризованного

запроса

Transaction DbTransaction IDbTransaction Объект транзакции. Выполняет


транзакцию базы данных

Хотя имена соответствующих типов для разных поставщиков данных оказыва­


ютс.я разными (например. SqlConnection, OracleConnection. OdbcConnection и
MySqlConnection). каждый из таких объектов получается из ОдНого и того же базо­
вого класса. что предполагает идентичность реали:зуемых объектaJV1И интерфейсов.
С учетом этого мы вправе предполагать. что. освоив приемы работы с одним по­
ставщиком данных. освоить остальные поставщИIШ будет совсем просто.

Замечание. В соответствии с соглашением о прИСваивании имен объекты поставщика Aa~IHbIX


должны иметь префикс. указывающий имя соответствующей СУБД.

на рис. 22.1 показана общая структура поставщика данных ADO.NEТ. Заметьте,


что в представленной диаграмме элемент Компоноеочный блок клиента может Qбо­
значать практически любое приложение .NEТ- консольную программу. прило­
жение Wlndows Forms. Web-страницу ASP.NEТ. Web-сервис XМL, библиотеку про­
rpaммнoro нода .NEТ и Т.д.
Конечно. в дополнение к объектам. показанным на рис. 22,1. поставщик дан­
ных предлагает и другие типы объеl(ТОВ. Однако указанные на рисунке базовЫе
объекты присущи всем поставщикам данных.

Поставщики данных Мiсrоsоft


в дистрибутив Мiсrоsоft .NEТ 2.0 включен ряд поставщиков данных. в частно­
сти для Oracle. SQL Server и ООВС. В табл, 22.2 для поставщиков данных Мicrosоft
ADO.NEТ указаны пространства имен и содержащие их компоновочные блоки .

Замечание. Специального поставщика данных, обращающегося непосредственно к механизму Jet


(т.е. к Мiсrоsоft Access), нет. Для взаимодействия с файлами данных Access можно использо·
вать поставщик данных OLE DB или ООВС.
~~ ..NfT
-
Обi,!Ю бошr6'С uщ! ~tr !J:вtг.i\~tю-

I
-
Tra1ls·attic,n: -! I IЮМSfIДa 'Sel e~\

~lliзвr1.
()бъm fuIIrue:cti йа
-
1!OOл~?taam",t~s,
[ Ji.»;.ia. iIjI1dв tt'
I
- -

I 00ъe1<Т 'Da:tiiP.ei;!.d~ 1:
- - - -
I<DМO!\Qa Dei~te
I

П()сtаащик ,цlUlныllt l1РОС'фQQТIIO lU(e~1 К4)М[10t,iiJ'ВО'iНiiЩ бrjах


--~----~----------~----~--~----~~~--~
OLE ОВ ~Yi'У:"~'!ТL •. j)Э:tа .ОlВШ, ~.-ste(f1. I;t:tt./З. .. olH
Mr(ttosuft SQL Serve, Sys t-e.lli.D'.3ta.. ~lClie.ТktSystem. Da't.a.... dll
МiCroMft SaLS1lNer &ytIl:;~II1.De+..21" ~Hql$erve.!'CB S:t.i:>tem. DS!.th . Sqlзе:t"Jе 1ГС.Е:. dU
'МQOi1e

ODBC $:.tsteJI"j_D~"ta~QdЬc ·5y"$lJ:e~ Da:\ii . dil


Q(E\OIe- Sy1!!;t em.•Th>rta, .QrQd::~Cli~j: 3У911:еrr"D<3.tе.Qz:.а1.:'1Jг:С:.l~:аfi1'Ц:Jll
------------------~-------------------
f11jс"Х8'DЩnК :J!а'ПR1:IIХ OLE па. хОО'ОрЬ1Й · t;:КOМПlЩоаа.в m umщ., апр~деде~ъm' ~
ПрОcтpa1W1'ве имен system,. D~ta ."I.:J:teO.b, ШJэвс.щВt'r ПQ.1trЩl'ij , д.QCТyti ~ ~, 1ЦO­
dtIFО~тmща ,ДEtRш-tх. 11QДДep~"UBa.JO:rцerf) мareЩЧе1!:ji:IШ fiРОТf3f!ЮJ[ ot.E Ь8 нз
(]саове C:Ol\1l. С ПОМОЩЫО,:Э"IIQ11trj постаJШtIf1i)а ДfЦ3НJ>:a' МQЖol'-rо ~a.~., fij ЛJ9б6Й БШ!оЙ'
,данных. OLE В8" !1рtЗCТо. n8..crро:ив ~eн:r" F!"cvid;~ r ~1·РIj)Ю~ СОeдtШ~. при ЭOF()~ ".
oДRaКI!I.. QЛ&".,цуе'1' УЧВТ.<)JВаn., ТО, что дщ::rа'.8щдi> OJ.E; DБ' :EI 'фо.litOВt.t.1Ia: ~e ~
д~"стэуе'Г с раi3.\1И~ .объеитэ.ми ООЪ1. а ЭТО МiО~1'}lJЩfl'1"Ь' 1't.Il; gрЩt:aМ~­
i!OС:ТЪ ПРИЛQженм, Б общем. nОfjГSЗОUU: ,.!IЗ.Itн:ьtx OLE D.В CI~~tcJ;1 IЮ.1Же!Ul:blМ
т:о:лъко в 'т,ом Ci1'Y'Ia:e. lCiQ.tJ:tQЩ>ЮЩ'!щ'l"C,I'I ~Qд.dtQ'Т'Щ)Э",Гh е C!lP'д, -I:!:~ tшpедещвю­
щей I«mЩ~ТНОГО uatтamциl~а дaю'П>D[ .:NET.
Thю1I'aВIШU( .ДвmПd!С Mlcresott:s~ S«ver np~iarl"i.eт' nf'ямо;1 ДОC'1)'JJ к ХрQiiИд;И"­
щам ДaIOJЫX M(tro90ft :5gt..Seтvcr :(:s'ерсий 7.О и :3Uilm~ :и fitrfJJ1;Ыф~ ~p8.fJИmп.n:aм~­
въш SQL Sem!Т'. npoc1'J1Ul~l"eQ имен S~ste'rn . .iЗаt:,а.s,qlС1i~Р~t ердt::ржиl'ТlШЪ1.. Ш!"
щt)Jъar~ые ,ПDЩ'~Оl'l! ~ BQL 'Э~ 11 дpe~e, "в Q!д:i'IJ.Sl>ШМ,. 're. ~e
934 Часть IV. Программирование с помощью библиотек .NET
r
функциональные возможности. что И поставЩИI\ данных OLE DБ. Ключевым раз­
личием является то. что поставЩИI\ данных SQL Server действует в обход уровня
OLE DB. а это обеспечивает ряд преимуществ с точl\и зрения производительности
системы. Кроме Toro. поставЩИI\ данных Мicrosoft SQL Server позволяет получить
доступ 1\ неl\ОТОРЫМ уникальным возможностям данной КОIЩретной СУБД.

Замечание. Если вас интересуют особенности использования пространств имен System.Data.


SqlServerCe, System.Data.Odbc или System.Data.Oracle, за подробностями обра­
титесь к документации .NEТ Framework 2.0 SDK.

Поставщики данных других производителей


Бдобавок 1\ поставщикам данных, предлагаемым компанией Microsoft, суще­
ствуют поставщии:и данных других производителей, предназначенные для самых
разных, как свободно доступных, TaI< и коммерческих баз данных. В табл. 22.3
указано, где найти некоторые управляемые поставщики дан.ных, не предлагаемые
в комплекте инсталляции Мicrosoft .NEТ 2.0 (помните о том, что указанные здесь
адреса URL MOryт измениться).

Таблица 22.3. Поставщики данных ADO.NEТ разных производителей

Поставщик данных Адрес Web-страницы

Firebird Interbase httр://www.mопо-рrоjесt.соm/FirеЬird_IпtеrЬаsе

IBM ОВ2 http://www-306.ibm.com/software/data/ db2


MySQL http://dev.mysql.com/downloads/connector/net/l.O.html
PostgreSQL httр://www.mопо-рrоjесt.соm/РоstgrеSQL

Sybase http://www.mono-project.com/Sybase

Замечание, Поскольку число поставщиков данных ADO.NET велико, в примерах этой главы будвт
использоваться поставщик данных Мiсrоsоft SQL Server (system.Data. sqlClient). После
освоения материала, представленного на страницах этой главы, у вас не должно возникать
проблем при использовании ADO.NEТ для взаимодействия с другими СУБД.

Дополнительные пространства имен ADO.NET


в дополнение к пространствам имен .NEТ, определяющим ТИПЫ Koнкpernoro по­
ставщика данных, библиотеки базовых классов предлагают ряд дополнительных
пространств имен. связанных с ADO.NEТ (табл. 22..4).
Следует понимать. что эта глава не предполагает рассмотрение абсолютно всех
типов из каждого пространства имен ADO.NEТ (для зтого потребовалась бы отдель­
ная книга). Но очень важно. чтобы вы поняли суть и возможности типов, предла·
гаемых в рамках пространства имен System.Data.
Главв 22.. Д!юул ~ базам AaH~ЫXC i1VМОUiJ:iЮ дDО.NЕТ 935
1аблица 22.4. ДРПОJ!нитеЛЬt'IJ:llе проtТРЭJ-lОТВё! имен, имеКJЩlI!е 011iсш),еfН.i1е к ADO.NEТ

ПРОСТРQНСТВО им е" Оmaсание

Новое n.рООТРЭfi[;Т!;i(} ~MeЪt .NEТ 2.0; ГiPflДJ1afaеТ' ти'rlЫ 1 по­


авоfJяющ.lе- с , ПОМОЩЬЮ управляемых !!.i3blJCQB СО,здаев'IЪ
)IIраНИМDге процедуры ЦNЯ 5QL Server QQ05
ОrtредеЛJ~ет базооые- ТИПВJ . АDО.NЕТ" ИОПQльзуемые ~Ge·
ми. JЮСУo±ВЩW(aМ).\ ,цaHHblJ\

СО~PЖVI1; типы , C05meC:r!-lOмпощ,зуемые r1QС:faВЩl1r-9-МИ


ДЗfJНЫIt., IW1Ю'I:tя nт1;\,. CDоrвеrСfВ)IIощие м~еЛ!il W;TO,+-
НИК-О nоСТ·6ЩlЦи' \$ A~!~HЫX.NEГ 2 .0

Ноео.е. пщютранс:гво 'Имен .NE.Т a.Oj 'ПР8Д)1агает раЭЛИ'i·


flые типы" И'Спеnьзуемые при Ii~Шф.9:Ике ЛОЛЬ31'JВ'зтеJur
· СК.l1)( ,",oMrlOH8HT(}B Aa~'H~~K iJ р!3~име 1''р~КТI~рi:i8а~IJIIЯ

f,'t>пое I1РО(П~tJСТ89 ИМ~ .NET 2: Р; П~дmlГает ,илы,


riОЭIЮЛЯloЩl't6 I'IЬ/ЯВЛП'i'Ь :зкэеМп.n>rры f'AicrosQft SQt Serve"
,установленные в ilОК<1ЛЫЮ.Й СЭТИ'

Содержит ·cDfulBeHHl>le~ птпъ\ дati~ы( Microseft SQl


Sщvвr. Хо;па вы всег;п,а мажете "icgмьэCIВа-т'ь c.oO-ТEI~П'­
QТВУЮЩIll!Р\1П1>1 AВHHbl)( СLЯ, nmы S.ql Туреs оПiиIYiИЗИ­
posattbl сrтe!.!~алЬНI)I для p8~01H '9 'SQL Servl!r

' Типы System.Data


ГТPOCТp;lH~T.вO имен Sy.s.tem . .GI.ata ,fIвщrетCJ'I. -:.ган tкaaaтъ, об:rщим ~еН'ше-лем,
для :всех np0C1'pa:aCTB имен ADO.NEТ. ВЬУ пр.ощ'о не можете постром::rъ ПРИ..JJ:IC)и,е­
юre ADО.NEт, He~ это проетра'НСТЩJ и..1It~ В пp.иJtожemm домупа tt данньтм.
Э17а пространство -имен сод.ержмт ТИJjJbl. совме(1ТНО 'иcrюлъ-зуемме- всеми ИQП'Ю3-
IЦИКa..'IП! данных ADO.NE1: веЩШIilГ'-WblO рт лtЩ>ащеro в -их DC1:l.0Be типа 2Срaнwли­
ща данных. в допол:неЮlе :к цltЛому ряду псщщruеу.ll'Ш- (1iill:J.u ) l All awedExcepti.O-r1.
ROW'Nc,'tlrJYa.'bl€:E'Kc",ptii,:>tJ.., Mi;osingPrirnaryKeyEKi:E!p'~l~!J и т.д.). CtIлаанпьnc: с ДО·
t:тynOM :к ба.3Ш4: даИНЫ1:. Эуй EC'!rc.Dat <а «oдep~ тJЩы. соответствующие .как раз­
ЛИ"ШЫМ :nPИМИ1'ИJ'lat.1 (та.б:дJiЩaМ., СТРОIШМ, :с.'1tl)л~цам. OIрa:н:tNёш,шм и т,д.1 €iазы
Дal-Шъtх.та:К н общим ИIJ;терфеJ-!сщ(. lэеБLiJifЗ,УеМЫ:М o6ъeIcraм:Rr посташцйЭtа д:amtыx..
В· табл. 22.5 rq:!еДilIмаю1'С1I' ОПИС3}-IИ;Я' l'lеJ<OТOРЩ бааоltЬix 'ГlШ'ОБ ЭТ-tJю npостранстда.,
ИМён. о' которых sa..",j сл~дует зН,ш.ть.

Т'оль пространства nм.eн :r1a.taS~t. а T~ n:аt~'Таblе • .Dа,t6Rеl.at.iО1!, D,aJ:aF.Ow 'Н'


Т.Д, будет расct.ЮТIi!ена в Э'I'OЙ rnfЩе-по<1Ж~ . НщUffЙ "б.гтИжвЙшеЙ задачей буд€т pac~
смотрeRиебановых Im:r:ерфеЙСQВ 5уst-ещ, 'Data, T8'1f' оиаэать, с общей 1'ОЧRИ <)р~нп:а.
чroбы.Ji)'ЧШе. nC)НnТЪ общие .фУЩЩЖ)l~а.;J;ЬПIi1!' вонмoж.iwсти . npед.лагаеt.lЬtе BceMJf'
'ПО€'Гi1ВЩI:iШJМй, ;д:am:tъш. КО1щретпые Дf>:т.ШЦ1 буJ\11' о{kуждат.ъс:я: в nрсщессе йБЛ0же--
1шл Мat~иa!IaЭТОЙ ГJ!aВЫ. а е:е'Йqa;С -!'лы ~ОСР~ДCIJ'ГОЧИМШl}-Ш rШIЦем поведении каж­
ДОЙ'! из имеющихс.я ТJiИ10Ii интерфейса.
936 Часть IV. Программирование с f10МОЩЬЮ библиотек .NET

Таблица 22.5. Базовые члены пространства имен System.Data

Тип Описание

Constraint Представляет ограничение для данного объекта DataCo1umn


DаtаСо1U!1Ш Представляет отдельный столбец в рамках объекта DataTable
DataRe1ation Представляет отношение "родитель-потомок" между двумя объектами
DataTable
DataRow Представляет отдельную строку в рамках объекта DataTable
DataSet Представляет хранимые в памяти данные, скомпонованные на основе лю­
бого числа взаимно связанных объектов DataTable
DataTable Представляет табличный блок данных в памяти

DataTableReader Позволяет доступ к DataTable в режиме однонаправленного курсора


(только для чтения); этот тип ПОЯВИЛСЯ в .NEТ 2.0
DataView Обеспечивает пользовательское представление для DataTable с исполь­
зованием сортировки, фильтрации, поиска, редактирования и навигации

IDataAdapter Опрвделяет базовое поведение объекта адаптера данных

IDataParameter Определяет базовое поведение объекта параметра

IDataReader Определяет базовое поведение объекта чтения данных

IDbCommand Определяет базовое поведение объекта команды

IDbDataAdapter Расширяет IDataAdapter С целью получения дополнитеl1ЬНЫХ функцио­


нальных возможностей объекта адаптера данных

IDbTransaction Определяет базовое поведение объвкта транзакции

Интерфейс IDbConnection
ТИп IDbConnection реализуется объектом соединения поставщика данных.
Этот интерфейс определяет МНОЖество членов, используемых ДЛЯ настройки со­
единения с конкретным хранилищем данных. а таКЖе позволяет получить объект
транзакции поставщика данных. Вот формальное определение IDbConnection.

public interface IDbConnection : IDisposable


{
string ConnectionString { get; set; }
int ConnectionTimeout { get; }
string Database { get; }
ConnectionState State ( get;
IDbTransaction BeginTransaction();
IDbТransaction BeqinTransaction(ISo1ation Leve1 i1);
void ChangeDatabase(string databaseName);
void C10se () ;
lDbCommand CreateCommand():
void Ореп () i
Глава 22, Доступ !<. бааам дaHliblX с ПОМОЩЬЮ ADO.NfТ 931

Интерфейс IDbTransactJon
RaR m~:дите, п~реrру>:кенный ме'11Од BegiI1TrartSacU.onO. определенный интер­
фt'.:й~О1\J IПЬСоппесtlоn. обесвечgвaет дощyn J{ абъеterrty транзакции поставщи:ка
данпъц. ИспоJIЬ3УЯ члены, oupC,I&JleНныe интерфейсам IDbТransaction. вы може­
те осуществля.ть прогрarvrюше !33аимодf3ЙСтвие с сеансом транэакц;ии ~И COOTDeТ­
ствующnм: хранилищем данньц!'.

public interr.ace IDbT.r:aniia:ct:it'Jn : lDisp'osable


{
IDbCQQhl?ction С~lщесtiОD { чеt: )
]; s{)lli t J.o.!1Lli!Vel 1501 atiol1 1€v-el{ ge.t; }
vфid COffi.'I1i t. () i
v'Oid Rol1backO i

Интерфейс IDbCommand
Интерфейс I .DbC o1t\mand. будет реЩШЗОВЩIобъекmо,м KoMal-to.ы. пос.тавщика
дaIlНhМt. Как и в других объeкntых моделщ доступа .к даI:IНЫ~.эдесь объекты ко­
-мwщ позволяют npoгрВММIЮ обрабатывать БQL-операторы, ~J'анимЫе npЬiJ;едуры
и параметризованные запросы. 1<.роме ТО1'о, с пом;ощью rrерегруженнаго метода
Execut.eReader j} объе.х.ты команд обеcnеч:цвaIOТ ДDС'I}'Л. R объеК'J)' чтения данных
поставщика .дaнньut.

'P1jpli~ intetface IDЬСornmаfiЗ : lDisposable


(
strJ ng ·СQ!'t1J'lliJ.щir~хt .{ g e:t; set; }
int СС!11Лlа!1dТ±mеоut ge.t; 5'et; j
(
Cormna.TKIType ConunandType [ get: set t
IDbConnecti БП C,o:nnectiofl { get; s-et; J
IDа .tаР:aramеtеrСэ11есt.iол Paxarneter$ { get;}
IDЬ":!'rалstlс'[iоп 'rrапза,сti 'ОП { g~t; бsti !
lJpd.a beRow&ource- updatedRowSD'I.U'i::Je { .ge~t I 9·et; .)
-v:oid Can.cel () ;
1 D.bD'a1:,a·P arameter Creat ePara!!(~t er 'о ;
.i:nt "tхее·ut.е1'JcщQu:е:rу О ;'
1 DataRe.ader Ехеси1: eRei:lder (~) J
IDаЦiRе'аdеr ЕхесU'tеRещiеr {Cornma.ndHe1\a.vi'QE behaviDr1:
objet':'t ExecutESc.a1a.r: () J
vaid Prepare o ·~

интерфеисыоьDаtараrаmеtеrr и lDataParameter
Of)paT~'I,"e ВЩIМaIOiе нз ТО,1!ТО свойс:rво Pa:rametexs ИН'Юрфейса IDbCo.mmand
ВQзвращае-r строго ТЩIИЗованную колленцию, реализуюIЦY10 Иllтерфейс ID:ataPara
mеtеrСоЦ.есtiоFt, Этот интерфейс обеспеЧИвает дocтyn кмножес'1'ВУ сQвм~стимых
с IDbDatliiParamete,r rщФВ кЛасса (например, DtrьeffГQB параметров).

1
938 Часть IV. Программирование с помощью библиотек .NET

public inte!:face IDbDataPar:ameter: : IDataPar:ameter:


{
byte P!:ecision { get; эес;
byte Scale { get; set; )
int Size ( get; set; )

Интерфейс IDbDataParameter расширяет интерфейс IDataParameter. предла­


гающий следующие возможности.

public iлtеrfасе IDataParameter:


{
ОЬТуре DbType { get; set; }
Par:ameterDir:ection Dir:ection { get; set; }
bool IsNullable { get; }
str:ing Par:ameterName { get; set; }
str:ing Sour:ceColu= { get; set; }
DataRowVersion Sour:ceVersion ( get; set: )
object Value { get; set; )

Как видите, интерфейсы IDbDataparameter и IDataParameter: позволяют пред­


ставить параметры SQL-команды (включая хранимые процедУРЫ) в виде специаль­
ных объеl(ТОВ параметров ADO.NEТ, а не в виде сложных строковых литералов.

ИнтерфейсыlDЬDаtаАdарtеr и IDataAdapter
Адш1ТТ!ерЫ данных используются для извлечения объеIСТОВ
Da taSet из хранили­
ща данных и отправки их в хранилище. Интерфейс IDbDataAdapter определяеr
набор свойств. используемых для помержки SQL-операторов в операциях выбор­
КИ, вставки, обновления и удаления данных.

public inter:face IDbDataAclapter: : IDataAdapte!:


(
IDbCommand DeleteCommand { get. ; se С;
IDbCommand Inser:tCommand { get; set;
IDbCommand SelectCommand { чес; эес;

IDbCommand UpdateCommand f get: set;

Кроме этих четырех свойств, адаптер данных ЛDО.NEТ наследует поведение,


определенное его базовым интерфейсом IDataAdapter:. Этот интерфейс определя­
ет ключевую функцию адаптера данных: способность переносить объекты DataSet
из приложения вызывающей стороны в хранилище данных и обратно. используя
методы Fill() иUрdаtе().
Дополнительно интерфейс IDataAdapter позволяет транслировать имена столб­
цов базы данных в более понятные пользователю дисплейные имена с помощью
свойства TableMappings.
public inte!:face IDataAdapter
{
МissingMappingAction МissingМappingAction { get; set;
MissingSchemaAction МiSЭiпgSсhеmаАсtiоп { get; set; }
Глава 22. Д(){;T~n к ~азам АЩIНЬ!* с nО"ОЩЫО АОО .NEТ 939
l1аыlмаррiлgсоllесtiопп TableMapping's { ge·t; f
int Fiil(System.Data.DataSet dq.taSet);
DataTabJ Э· [] Fi llЗсдета (D,a·taS;e.t data.set., SchernaTyp.e 5сh~-mаТуре);
IDa:taParameter rl GetFi1 lParamete:J;s {J;
1nt Update {l)ataSe.t d'ataS~t 1;

Интер'фейсw IDataReader и IDataRecord


Следую1Щ{М WIЮЧ~ВЫМ интерфейсом нвляетС8: IDаtа .R€!з dеr. который пред;­
ставднет р~щие ВОЭ'МОЖНОСТИ по:ведеНия объекта Чтен.I1Я да. нн.ЫХ. Получив
ЩаtаR~q.dеr-тиn от поста:в1ЦИКа данных AпO.NEГ. вЫ можете обрат.иться к. резулъ­
ТNPYЮще~ нэf:)DРУ данных в р.е.жиме однонanравленного доступа. U03Dоляющего
ТОЛЪ~О чтение.

poblic interface IDataReiideY : ID1sposabl e , П1;;).tаR.~еQгd·


{
iГlt Depth ( get, }
Ьооl IэОоsеd I ge:1::; }
in t ~еСбrdSАНесtеd { g~t;
VG id C1Gse () I
.D'a taTable Ge-т:.s .с.ьemатаы1 е tI J
bool Ne,xtReslIJlt () i
bool Re~d О;

Наконец. вы видите. ЧТО IDataReader: раqши:ряет ИJfi'eрфt>-Йс IDataR:ecord. опре­


дедщоllJ;ИЙ БQМЩQЙ набор 'ЧЛенов. IWТОРВд' позволяют извщчъ ИЗ ПОТОМ СТРОГО1'и­
пизованвое значеFЩе B~~eCTO общerо объекта System.Objeet. nредлагаемого пере­
груженДblМ. методом ИfrдeI~еаТОJ,>а оБЪе;!{Т~ чтеmtя дюm:ых. Вот часто проtpаммн.ОТ'О
1tода соответст.вуюЩЩi: ме.тодов GetxXX (). определев:н:ых в РШоncа.х IDa't.aRecGr·d
(вес.ъ .програ:ммныii код M0ilt80 Н~~ТИ' В документации .NEТ Frameworx 2Л' SDК).
-publi.c inb",rfa'ce lData'RecQ:td
r
int F;Lеld\:.".DI.шtl g.et.. )
obj eatth.ls [ 5 trlng QGlme j \ get.;
Qpj'ectthis L ipt i ] ( g-et;
Ьооl Gе:tВ(;ю~еаrlJiпt i);
byte GetByte (in t i) ;
сЬ?г GеtСh~r(iпt i};
uateTime GеtDаtе'Тiюе lint i);
Decimal GetD.ecim~l tint i1;
float GеtFlоаt(iпt i) i
S-hD:rt Get Ir,t16 (in t i) J
int GеtIцtЗ2Jiпt i) ;
10лg GеUпtб-4 (i:n:t i);

lb9bl rs~I1B1{ull (in t i) :


J
940 Часть IV. Программирование с помощью библиотек .NET

Замечание. Перед чrением значения из объекта чтения данных можно использовать метод
IDataReader .IsDBNull () I чтобы программно убедиться в том. что соответствующее поле
данных не равно rшll (и не допустить генерирования соответствующего исключения в среде
выполнения).

интерфейсы и абстрактные поставщики данных


к этому моменту вы должны иметь более конкретное представление об об­
щих функциональных возможностях. присущих всем поставщикам данных .NEТ.
Напомним. что, хотя имена реализующих типов у разных поставщиков данных
оказываются разными. у вас все равно есть возможность программировать эти

типы в аналогичной манере - в зтом И заключаетСя преимущество полиморфизма.


основанного на использовании интерфейсов. Так. если определить метод, имею­
щий параметр IDbConnection, вы сможете передать ему любой объект соединения
ADO.NEТ.

public static void QpenConnection(IDbConnection сп)


{
11 О'l'кpvrие 1Iходноrо соединеНИIII .цпlll 81о1.!1ЫВаацеЙ C'l'OPOНlol.
сп. Ореп () ;

То же можно сказать и о возвращаемых значениях. Рассмотрите. например.


следующую простую программу на С#. Iюторая позволяет вызывающей стороне
получить конкретный объект соединения. используя значение пользовательского
перечня (здесь предполагается. что вы указали оператор using для System.Data).
патеэрасе ConnectionApp
{
епит DataProvider
( SqlServer, OleDb, Odbc, Oracle )
class Program
{
static void Main(string[] args}
(
// Под учение соединеНИIII.
IDbConnection туеп =
GetConnection(DataProvider.SqlServer) ;
11 Требуе'l'СIII соединение с е&.!IОЙ Д&НКWX SQL Server Puba.
myCn.ConnectionString =
Саtаlоg=РuЬs
П
"Data Source=localhost;uid=sa;pwd=;Initial ;

1/ О'l'КРIoi'l'ие соединеНИIII с ПОИО~ IIспоиоr&'l'еn.ноЙ функции.


OpenConnection(myCn) ;
/1 ИСПОn • .!IО8акие соединеНИIII и ero ПОCllеДYJClЩ8е а&КРlol'l'ие.

myCn.Close() ;

static IDbConnection GetConnection(DataProvider dp)


{

l
Глав'в 22. Доступ J( (jазам nafjHblX t ПОМОЩЬЮ ADO, NEТ 941
1 DЬЕоппес t i фд COI1 n = n'и 11 ;
:;wit (;1'). ( ар )

!
с аЭе Dta t a PrQvi det. S.q1Se rver;
C Q!tJ1· =< new S ql'Co'nDec·t iод <) ;
bre.a k;
case ):Jat a Provid e .t.01eDb:
:::~JЛ'П .о; пеw .ОlеDbСО.'Ell1.е сt iол () ;
b ~eaK ;
ca se Data F rov lde r .OpЬ ·c :
canJ'! = Dеw Qc:Ib !l\ Соnп е.сtiQIl () ;
br~ak ;
Са$е Da t..a,P ro'Jide.r , Oracle:
СОD П = 'леw OraeleCQnnecti .o n () ;
b:reak;

..cetur·n со пn,;
)

Преимущеt·::гво ИСПОЛЪЗ()SI;!НИЯ оБJ:IUЦ щrтерфе.йс(}в Bys.tem.Dat.a закЛючается в


ХОМ, ч:'Го в этом случае у ва!:) больше ШCl}tСОВ создаТ1! БОJ1~.t'! гпб:кий прorpaммиый
ХОД. КOTOpьm долъше сможет ос.'гаваn.сЯ' ав;туа,льным. Напрцмер'. если ceroдн.s вы
построите пр.иложевие, испольЭ)'Н)щее M1crosoft SQL Senrer. то -ЧТО вы сможете сде­
лать. если через год-другой pyнoвoдc~o вашей J(O~ nPlЩет р.еiIIевие перейти
на ИСПОJIЬ30вание Oracle? Естl в прилож~н,ии "жестко' запрограМмированы ммeнs.
ТИПОВ Э}'.stеrn.Dаtа. SqlClient. Т.О вам. оч.евиДно, 1IpJ1дется снаЧЗJ"fа их 01'редаити·
pOBaTI>., а затем переl<омпилировать и повторно ~т~ировать !(о.мnОНО80ЧНЫЙ
блок.

Файлы комфигу, рации и ГИ' бкость приложений


Длs: ПDНЫЩ~;НИS гибкоет':! С1')о»х nPИJ1'ожeнW\ ADO.NET 1')ы' можете на стороне
1tIDte!-Жта аспольз0'ВI!I.ТЪ фэ,й)] * . соп~iч. '1') катором в paмKmc Мемента <appSetting.s>
МожнО' yt«\Эатр nc:щъао~атель~ие пары МЮЧt!Й и зна~tениЙ . ВСПОМНИте из главы 11.
что nОJ1Ьао.sателъские Д~н8Ъte можно прочитатъ программио с nОМQЩЬЮ тИПОв из
пространства имен S у э tеm. С ОI,l Ц gurаtiоn. преДnОJJCiЖИМ. что в файле нонфи­
~aJ:t~ вы ук~эали строки СО~ДинеRИn J4 поставщика ЩUIНblX так. как nОКаз&НО
ниже,

<: Qcn .t i gurBti.on>


чфр s е t. t i!"ig's>
«Cad'c;J ke}"""prcv1ch..:" vblu'e-"sqlsеГ/ еr " />
<a.dd key-"сnStz:" va l ие,..
"Dа t.С1 S0 ц r с·е.=оl ОСёl1,.}юs t; ~iq=.sa ;pwd-; lйi t:!.a 1 Cataloq=Pubs " />
<IappSett i ngs>
</cQnf i 9U ~ atian>

в этом случае вы можете добави'l'Ь в Main () cтpo~; обеспеЧИвaIOЩ"е nporpa.'\>f-


МRoe чтение а.ТИХ знв;qеJ:{ИЙ, В результате ВЫ.' пр существу, создадите Licml)ЧkUX (~e.
942 Часть N. Программирование с помощью библиотек . NEТ

генератор) поставщика данных. Вот как может выглядеть соответствующая моди­


фикация указанного метода.

static v o id Ma i n( s tring [ ] argE)


{
11 Чтение значении ICJDOча provider.
st r ing dpStr '"' Conf igur at i onManage r . AppSet tin gs [" provider" ] ;
Data Provider dp =
(Da taPro v ider )Enum.Pa rs e (t ypeo f (DataPro vider), dpSt r):
1/ Чтение значении cnStr.
s t ri ng c nStr = COnf i gu ra ti onMa nager . App Se ttin gs [" c n Str" ] ;
11 По.пvчение соединении.
IDbC o nnection т у Сп = G€tConnection(dp);
myCn .C onnecti onS tring = c n Str;

Замечание. Тип Confi g u r at ionMana g er появился в .NEТ 2.0. Не забудьте также устагювить
ссылку на компоновочный блок System. Co nfigu r ati on .dll И указать using ДЛЯ про­
странства имен System. Configuratio n .

Если предыдущий пример преобразовать в библиотеку программного кода .NEТ


(а не в .консольное приложение), вы получите ВОзможность создать любое число
клиентов , которые могут устанавливать свои соединения, используя различные

уровни абстракции. Но чтобы построить действительно полезную библиотеку , реа­


лизующую возможности источника поставщика данных. вы должны также исполь­

зовать объекты команд, объекты чтения данных , адаптеры данных и другие тИПЫ,
связанные с обработкой данных. Хотя построить такую библиотеку программно­
го кода будет не слишком сложно, для нее потребуется весьма большой по объему
прогрaммный код. 1\ счастью, что касается .NEТ 2.0. добрые люди из Редмонда уже
встроили все неоБХОДИМDе в библиотеки базовых классов .

ИСХОДНblЙ КОД. Проект MyConnectionFactory размещен в подкаталоге, соответствующем главе 22.

Модель источника поставщика данных .НЕТ 2.0


в .NEТ 2.0 предлагается модель источни.ка поставщика данных, с п омощью
которой, используя обобщенные типы, можно построить единый базовый код для
доступа к данным. Более того. используя файлыI конфигурации прилож ения (В
частно сти, их новый раздел <c o nne c tionS tr ing s » , можно получать строки по­
ставщиков и соединений декларативно , без необходимости пере компиляции и по ­
вторной инсталляции программного обеспечения клиента.
Чтобы разобраться в реализации источника поставщика данных, ВСПОМl:IИТе из
табл. 22.1 , что все объекты поставщика данных получаются из одних и тех же ба­
зовых классов. определенных в пространстве имен Syst em.Da ta. Common:
• DbC ommand - абстрактный базовый класс ДЛЯ объектов команд:
Гnааа 22, ДОСтУП ~базам данных с ПОМОЩЬ'Ю AOQNfТ 943
• DbCo.nnectio!1 - абстрli!1\ТНЫЙ бjJ..3QВЫ:Й RJIaCC длн объектов соединенuя:

• D,b-D,аtаАdа'Р't,е,r - абстрaкJnIый 'базовый КЛаСС ДЛЯ объектов адаптера данных;

• DbDataReade.L - аБСТРaRТR.ыа базовмй масс для объеитов чте'НИН да.нных.;

• DbPari31Т\i;;.ter- абстрактНЫЙ 'базовый классД!1'Я объектов параметров.;


• DЬтrапsасtiQ[! ~ абстраkrНьrй. базовый класс ДЛЯ объектов трaRЗа1ЩИИ,

.кроме тоге, ц.~vr 2.0 каждый поставщик данных от WСlVsщt предлаг?).ет СЩ'-
циальный fQJacc. ПOЛJЧ'ащЩИlЙСЯ:ИЗ буstеrn . .Dа:tа.Соmmоп . DbP.r:oviderFactory, Э'гот
базовый 1~..jIaC:;C оr.rpедел~ет р.яд методов, с IЮмощью жоторъ1Х извлекаlOтсяобъе~ты
данны;,х. спеЦИФИЧНb.Iе дщl дamюто nостcmщика. Во'г список соометст.вуюЩI:IX 'ЧJIе­
ЛОВ DbP!'QviderF'a~tory.

public abstrac:t classDb.J?rovider F<lctQry


f

public viпuа l DbCo!'Om~nd Сr~аt€-С()mrnапdО:


public; ','irttial DbCom,maIJdВLlilde'r Сгеаt;еСеrшпаndВ:uild&г ();
publi-c ,.. ir'1'..ual DbC •.mn-sсtiоI;l C:t,eat.,eConrJ €:~tioI1 () ;
public "i rtual DbCbnn;:,ctiClr\Stri!ig!,3'IJ il d~r
Cr!i'<ateC;O!m~<::f.i on-s't r i 'ngВuil der () ;
pu!::,lic: ,",'i:r:tual DbDat.aAdapter 'СrеаtеDе.t<i~dарtб:r (-) ;
рIIы1 ic vi rtua l I)рпе ta$ourceE'flUmerа tor
Creai: eData,5 0urceE1l 'l.ln1eLato'J!: () ;
]''L!blic ,,·i :ttша.l DbParameter C:reate1Parame ,t E'jCO;
)

, Для IIолученин тиr:Щ, npоизвоЩtОГО от DbProv.:voerFactory и подходящио МИ


:вашего поставщика ЦSЦЩ:blX, прострЩ'!lСТВО имен s уstеm.,Dа:tа .соmrtюп пpeДJIa1'a'eT

ТИ1'I ЮIaСса Db.Prov1detF'actories, Испольаун статичесний мeT()11 GetFac'tory (),


МОЖJIО получить КQ1'шретяый (и. ~(:тarrи. уюшaлънъriI) DbPrGvide:tFactory дщI ука­
занного вами ПОСТaDIIЩRа даmЦiIX. н;апример:

stati с void Мai..n (striш~ [) а rgs)


(
/1 Роn"е,щrе ИС'1'ОчИИJt& пос~авlЦllXВ' ;Ц-&JIRШ[ SQL.
DhPIoviderFact'ory sqH'a cto~)! =
DbPrcivi.tierF~ctorie$ ,Getl"E;lctory( l' System. Dat:a, SqlCl ient") ;

1/ IIOn)'че:иие ИC'J.'ОЧИИЕа. ПОО'1!авщих.а д:&ИН1.iX Ora,C,l e .


ПЬРrОlli derFact,Qry Qr;i3,cl eFacto:ry =
D$Provide'rFactori~s. GetFactory ("systel1i .Data. Oracl eC lief1t") ;

кщс вы, наверное. J:J до,ц,умали. вместо попучения исто:ttни1Cа с ПОМОЩЬЮ "жесТ­
KO~ эакодировщшой буЩlадЬНОИ СТро;ки. соо-rnетств)"ющу:ю mtФормaщtl9 можно
прОЧU'I'а'ГII из файла *.cOI1fig юmента (аналогично тому , как ЭIfО бъщо сделало в
предьщущем прдм:ере l'1yCOQr:1€ct iо.лFасt'orу). чутъ.ntiзж~:мы с Ш1ЮI TaR И сдедаем.
Но. 1'ах или шraче, СОЗда1! источник своеХ0 IlоставЩИRа дашrых. вы емощете по­
JIy.'UfГb объенты (соеДI:IНСЩЩ, команды н т.д.), 'соответствующие ЭТО~ поста:вщцку
Ц<JИНЬЦ:,
944 Часть IV. Программирование с ПОМОЩЬЮ библиотек .NEТ

Зарегистрированные источники поставщиков данных


Перед тем как мы с вами рассмотрим вполне закончеННЬ1Й пример использо­
вания источника поставщика данных ADO.NEТ, важно обратить внимание на то,
что тип DbProviderFactories (В ,NET 2.0) позволяет выбрать источники толь­
ко некоторого подмножества всех возможных поставщиков данных, Список дей­
ствительных источников поставщиков данных указывается в рамках элемента

<DbProviderFactories> в файле
machine,config вашей инсталляции .NET2.0
(заметим, что значение атрибута invariant идентично значению, передаваемому
методу DbProviderFactories .GetFactory ()),

<system,data>
<DbProviderFactori8s>
<add name="Odbc Data Provider"
invariant="System.Data.Odbc"
description=". Net Framework Data Provider for Odbc"
type="System.Data.Odbc.OdbcFactory,
System.Data, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" 1>
<add name="OleDb Data Provider"
invariant="System. Data. OleDb"
description=", Net Framework Data Provider for OlePb"
type="System.Data.OleDb.OleDbFactory,
Sys t em.Data, Version=2.0.0.0, Culture=neutral,
РUbliсКеуТоkеп-Ь77а5с561934еО89" 1>
<add паmе-"ОrасlеСliепt Оаса Provider"
invariant-"Syst8lll. Data, OracleCli8nt"
description=".Net Framework Pata Provider for Oracle"
type="System.Data,OracleClient.OracleC11entFactory,
System.Data,OracleClient, Version=2.0.0.0, Culture=neutral,
PubLicKeyToken=b77a5c561934e089" 1>
<add name="SqlC li en t ОаСа Provider"
invarian t ="SY8tQ , Data. Sqlcli8nt"
descr i pt ion- ", Ne t Framework Da t a Prov1de r for SqlServer"
type-"Sуstеm.Dаtа.SqlСliепt.SqlСl i епt Fа сtоrу, System.Data,
Vеrэiоп=2,О.О. О , Culture=neutral,
l?ublicKeyToken ..b7 7<1Sc5619 34e089" 1>
</DbProviderFactori es>
</system.data>

ЗIМ8ЧIНИ,. Если вы хотиrе использовать МОДВЛЬ источника поставщика AaHHblX ДЛЯ СУБД, не упо­
мянутой в Файле machine.config, то знайте, что подобная модель ДЛЯ множества постав­
ЩИКОВ данных, I<ак с открытым программным кодом, так и коммерческих, предлагается дис­
трибутивом Мопо ,NET (см . главу 1).

Рабочий пример источника поставщика данныx


Давайте построим консольное приложение (с именем DataProviderFactory).
которое будет печатать имена и фамилии авторов из таблицы Authore базы дан-
,
,I
Глава 22. До<;1уП к базам данны)( о ПQМОЩЬЮ AOO.~T 945
НЬi:X PUbs. создаваемой npи. устщювке M1erosoft SQ~ Serveт (Pubs представляет со­
бой пример баэьt даннмх. вымы1ШIСННОЙ иsдательс~ой 1tОмпa:mш).
Сначала укажИ<rе CCbl.nкy на КОМПОНОIЩЧЮ:>IЙ блОЕ Systern.Contigura·tion.dl1.
добавьте в текущий проект файл арр. carlfig 1:1. определите элемент <CippS,ettings>.
Помните о ТОМ. что "Официащ.lJЬtм"' форматом ;щач:ения поставщИка является пол­
ное ИМЯ пространства имetI постanщшщ Д8НrIЫX. а не та1Юе фШtEmное ИМЯ. юm то.
которое ·ИСПОЛЬЗQВалщ:ъ ВbIше в npимере MyConnec:tionFattory.
<confi.gur:at.ion:>
<appSet tingsJ>
<:! -- ~ ПGС'З!~? -->
<aqd key="provi.J::1.er" val ие=" SY'51tem. Data. ечl с liецt·" />
<: ! -- lCaxaк GII1pclП сое,цинеJD{1l? ~->
<addkey="cnStr" valu.e,..
"Data SОL:ы:i:е=lФсаlhо.s.t:Uid=S.а;рwd=1 In1t.ial Саtаl0g=ЕU!})э"I>
< / аррSеtting,З>
<"cOr1figur,.a tion>
Теперь. когда у B~~eCTb соответС'ТВуюIЦldi фm'Ь1 ... config, вы можете црочи:гать
из ;н;еrо аначе~fl provider и cnStr, ИСПOJ1Ъауя: метод Cbnfiquration.Manage:r,
AppSettings (), 3;начe.щl~ p.r;ov-ider буд~ передано методУ DbPro\ridQrFactorles.
GetFa:cto.ry (,), "Ч'f'об"ь1 дcmyчи'rh специфичю.m ДЛЯ даниогс ПОСТВВЩЮtа ТiШ ис­
точника Д~. Знач~ние cnStr будет использоватъСЯ: для устаков1Ш свойства
Connect:i;o.n_S uiflg типа DbConnet:tion. В предположении о ТОМ. что ВЫytalзали
using ДЛJJ ppc.kтpaнcn имен SувtеП6Datа. и: System.Data.Common. оБНОВiШе метод
мai m() ~, :как поJtазано 1IИЖе.

з · tаtiс· void Ма:1о (эtrih9 [) <tr9S}


{

11 nOlnуЧ8КИ8 C'2IpO. са.дмке.,,_ liI nOCJ». . . .a Д.~ • • фaйnа ... c~i;.


strfng ·ар ..
СоnfigurаtiолМDJu.gеl:. AppSet:t1nqs ('tptov1,deor" 1.;
strin.g cnSt1:' ..
Сс)'Лfigцrаtiоnt4аnаgеr .AppS·e ttings [''сnStr''] :
11 Сcl.~.ИМ. JlСФО'lJOfX& ПОСI'UЩИlCа.
Db?ravlder Fa.ctory df '"' Db.Pt"oviderFactor i аз. G~tFactory (.ар) ;

11 со.даки. оD'И· '!t'а СО8ДНII8JIИJI.


!)ЬСолце.сtiо,n оп, = df. Cl'ebt'.eConne~tion () ;
Соr!зоlе. Wr.it:еL1ле ("Qe1:teK'11 с;оеДИk~Н1ИЯ: {О) 11 I

cn.GetType() .Fu~~~me);
С;N, Con'n-е'с t iог.Stз;iпg .. cnBtr /
сп. Ореn {'} r
11 C:oaД8JlX8 o&м~a .o,,~,;
DЬСоminaЛd crod = df. Crea teCoromand () j'
Соnз:оlе. Ю' i teLine (HO~ъe.к'!:' 1<ом'андЬ\: (а)" I

cmd. ~e'l: Т'уре () • fullName) ;


cmd, Co·nr,·e otiofl .. .сn:
cmd. CommandText .. "Se1ec,t ~ From AlJtho-I'S";
946 Часть IV. Программирование с помощью библиотек .NEТ

/ / ВЫвод дaJIJWX С ПОNОЩloJO объекта Ч!I.'ения: да.ниых.


DbDataReader dr =
cmd.ExecuteReader(CommandBehav1or.CloseConnect1on) ;
Сопsоlе.WritеL1пе("Объект чтения данных: {О)",
dr.GetType() .FullName);
Console.WriteLine"'\n***** Авторы в РиЬБ ***.*");
whi1e (dr.Read(»
Соnsо1е.WritеLiпе("-> {О}, (l}", dr["au lname"], dr["au fname"]);
dr.C1ose() ;
}

Здесь для про верки печатаются полные имена объекта соединения. объекта IЮ­
манды и объекта чтения данных. Запустив это приложение. вы обнаружите. что
для чтения данных из таблицы Authors базы данных РиЬв был использован по­
ставщик Мiсrоsоft SQL Server (рис. 22.2).

Рис. 22.2. Получение поставщика данных SQL Server с помощью источника постав­
щика данных . NEТ 2.0

Далее, если вы измените файл *.config тю<. чтобы в нем для поставщика дан­
ных было указано System.Data.OleDb (и соответственно обновите строку соедине­
ния), как предлагается ниже:

<configuration>
<appSettings>
<!-- Whicb provider? -->
<add key="provider" value="System.Data.OleDb" />
<!-- Wh1ch connect1on string? -->
<add key="cnStr" value=
"Provider=SQLOLEDB; Dat a Sour ce=loca1host;uid=saipwd=; 1n1t1a1 Catalog=Pubs" / >
</appSettings >
</configuration>
то вы обнаружите. что теперь в фоновом режиме используются типы System.Data.
OleDb (рис. 22.3).

.L
Гла6Q 22. Доступ к базам AaHHblJ( G пQмDщыо ADO.NE:i 947

Рис. 22.3. пол"i'lеНИ8 ПDtтаВЩl>!ка даННЫХ OL.E ОВ с ПОМОЩl;,Ю И0тФчника поставщика


данных . NEТ 2.0

Н"онеЧF.IО, не имея опыта работы с ADO_NEТ. щ.I можеТе IiMeTh слабое представ­
ле~е о ТОМ, что именно де.пi.llOТ оБЪ6КТЫ соединения. 'Команр;ы и чтенин данных.
Но не беспокОЙТесь до поры до времени о детanяx (В конце КОIЩОВ. Б .этой "Главе еще
~е..f\!ало странЩ . .и вам толыш предстоит их прочеСТЬ 1!). На дa1rны'Й момеlfl' важно
ТОЩ;КО ПОНЯТЬ. 'Что в .NEТ2.0 8ПО.lП{е вааможно щ)'строить едицый базовый код. КО­
roРl!rИ сможет в деIU:IaраТЙБНОЙ форме принимать раs.щ.D;: I;IоСТавIЦИItов ДЭJ:-Il·n'IX.
Предложенвая. модель оказываеТся очень мощной'. но ВЫ долаш:ы: убеЩJТЬСЯ
в тоМ.' 'ЧТо ~аш баэовый ПРОflРамJИ1I.ЫЙ код ИСПОJ[bэует 'только "Iie 'тИПЫ и MeIТ()ДbI.
которые ОR-2зъmаются: обiЦИМИ ДЛЯ всех поставЩJmОВ . Поэтому при создан:м-и об­
щего програм.мнОJ'G кода ограничьте оеБJJ цсцол:ьзоваш,.ем чл.енОВ. npt;длаtаемых
DЬСоnлесt.iG Е . DЬ.Соmm апrd И ,дРугими типами из ЛрОС~0тва имеН5у-stеm.Dа'tа.
C,Q-mm'оГJ. t ,дрyrой CTOpo-Нbl. IШОJП-tе возможно, ЧТО такой. ~Qбобщt"ПНЫЙ" подход ае
ДО~ВОЛИТ исПольэават:ь пекO'ftJрые специфи-reсtще llOЗ~О1ЩiОL'Ти конъ.--ретн.ой СУБД.
так ~O Об.нзательно проверьте работоспособНQСТЬ c03ДiiЦIaeм.OTo вами проrpа.'\1МНО-
1'01ЮД3 В реал:ьных УСЛОВИЯХ.

Злем'ент <connectionStrings>
в рамка..х .NEТ 2.0 файлы конфигурации прИ'lIOЖeИИlI могут определять новый
,злемеltТ, ffa:mаиI:tЫЙ <(:'onrtection-String5>. В к(Д{тексте этото эл~ента вы може­
те олределит~ люБQе ч;nсла пар ~eli и значений. которые можно будет uрочи­
тать npограммными средетвамц. :ис;польэуя индексатор Сап! igllrаtiопМаi;1@gеr.
CannectionStrings . IЛав!1Ъ11\I1 ПРI;Щ\4)'П.I;еством этого подхода (в QТJlИ'lце О,Т ИС-­
l1ОЛЬ30вания ЭЛемента <:appSettings> ИI индексатора сопfigurаtiолМаnаgеr_
AppSet t i пЩ's, ) является то. что в ЭТОМ случае БЫ .можете определять :мt-Jo~eCТBo
СТрок соедП'Нений цля ОДНОГО np:иложении в единообразном стиле.
Для примера обtlOвите свой файл i:\pp. COiJ fi l~ Ta.I(, как ПОК8.Эано ниже (a:rм(,T&~
те. что каждая строка соединеЮ1~ здесь задаетcR атрибутамИ 'паn-lе и ссФnесtiQ!).­
String. а не ke.1 и"Vаluе. ЩU\ В случае <appS.et tings».
<CDll f i gч.rа tioI1>
<appSet,t ing:s>
< ! -- \ilhich pro,,-lder? -->
<.a.dd .k6y="provid.er'· vаlое=РSУ$:t.. еm • .Dаtа.SqlСli'е.лt" (>
</ appSetti1'1gs>
948 Часть IV, Программироваtiие с ПОМОЩЬЮ библиотек ,NET

<connectionStrings>
<add пате ="SqlProviderPubs" connecti onString =
"Data Sоurсе=lосаlhоst;uid=sа;рwd=;Iлitiаl Catalog=Pubs" / >
<add пате ="OleDbProviderPubs" connectionString =
"Provider=SQLOLEDB.l;Data Source=localhost;uid=sa:pwd=;Initial Catalog=Pubs" />
</соппесtiопStriпgэ>
</configuration>
Теперь обновите метод Main ().
static void Main(string[] args)
{
Console.WriteLine("*** ИСТОЧНI1КИ п"оставщиков данных ***\п"):
string dp =
ConfigurationManager.AppSettings("prov ider"Ji
string cnStr =
COnfigurationManager.Co пnecti o nStrings[
"SqlProviderPubs"j.ConnectionString;

На этой стадии нашего обсуждения вам уже должно быть ясно, ка}( взаимо­
действовать с источником поставщика данных .NET 2.0 (и новым элементом
<connectionStrings> ).

Земечение. Теперь, когда вы понимаете РОЛЬ источнико& поставщиков данных ADO,NEТ, в осталЬ­
ных примерах зтой главы будут использоваться типы из System. Data. Sq1client и "жест­
ко" закодированные отроки соединений, чтобы Сфокусировать ваше внимание на соответству­
ющих более "узких" задачах обсуждения.

ИСХОДН~IА код. Проект DatBProvtderFactory рвзмещен в подкаталоге, соответствующем главе 22.

Установка базы данных Cars


Итак. теперь вам известны основные возможности поставщика данных .NEТ, и
мы можем заняться обсуждением специфики программированиJt с помощью ЛDО.
NEт. Как уже упоминалось. в примерах этой главы будет ИСПОЛЬЗО811ТЬСЯ M1c:rosoft
SQL Server. В русле автомобильной темы. которая используется во Bce~ книге,
мы рассмотрим пример базы данных Сnсв (Автомобили), содержащей три свя­
занные таблицы с именами Inventory (Ицвентарь). Orders (Заказы) and Customers
(Заказчики).

Зем.чени •. Если у вас нет копии Microsoft SQL Server, вы можете звгрузить (беоплатную) ко­
пию Microsoft SQL Server 2005 Ехргезв Edltlon (http://lab.j!tsdn.micro во f t . coml
express). Хотя зтот инструмент и не обладает абсолютно всеми возможностями полной версии
Microsoft SOL Server, он позволит вам принять преДl1агаемую базу данных Cars, При этом учтите
то, что примеры данной главы соэдаввлись с помощью Мiсrоsоft SQL Server, поэтому для выясне­
ния всех проблемных моментов используйте документацию SOL Server 2005 Express Edltlon.
Глава 22. Доступ к базам ,Данных с помощью ADO. NET 949
Чтобы устанDВИТЬ базу дaнJiЫX СЩ'8 на своей .мaunmе. RaЧЮlте е запуска yт~­
ЛИТI:;I Query AnaJyzer (Анвли;щтор запросов). Пdставляемой в paMRax SQL Server.
Сое,циннтесь со своей. мaI1ЩНой и crПЩОЙ're фaй.Ir Са rs .БЧ1. npе.цлar3.емыЙ в папке с
.исхоД1lЫМ кодом примеров дlIЯ данной т.лавы. Перед 'Гем JШВ ВЫШJЛНИТЪ cцeHap~.
убе,цитесь 11 том. ЧТО путь,уtЩЗa.щ:IblЙ В SQL-фaй.1iе. соотвe-rстзует вашей ЦНС'1!ЗJ1.­
ляции М1сrosоft SQL Server. Ес;:л:и f{еобходимо, отредактируйте сл.едующие стрщщ
(вьщеленные полужирны:м шрифтом).

CREATE DATABASE (Cacrs] GN (.NAМE = N'Саr'Э_Dаtа'. F'IL,ENNdE


= N' С: \Proqr&tl l'ilea\l«ic:rQ50ft SQL Se.rver\МSSQL\D&t.a\cax8.))ata..МD"f' ,
S IZE ;: 2, L"IL,E,GRO,w'r}I = 10 %)

LOG ON (NAМL = N ' ,Cars_LQg', F'ILENAМE


= N'C:\Program Filеs\ИЦ:r080ft SQL SаrvlU'\ИSSQL\Dаtа\С&Z',*_Log.LDF" ,
81 Z'E '= 1, F~ГLЕЮRОWТН = 1 oq; )
GO
Тenеръ выполните сценарий. После это.го откроите ОШJО yтшm1ы sgL Servrer
E.nt~rpт:ise МaiJзgeг. Бы сможете увидеть там три сщrзанные таблицы (е некоторы­
~ уже ввt,ДeJl1I1>1МИ данными) и одну хравимую щхщецуру . На рис. 22.4110казаны
таБJIl'PЦU. формирующие базу данных Сагв.

'ис. 22.4. ' !)a~ дat{HЫX Cars

Соеди"ение с базой данных I Vi'sual Studio 2005


Ит8J(, база данных Cars создана. и зы може'l!e установить соедиЮ!вnе с Э'rой ба­
зоЙ данных из У1эиаl Stud10 20О5. Это ПО3ВOJЩТ npосматривать и редактировать
разJIи'чныe объекты базы дiuiных в среде раарабо'rJIИ Vi,Sua1 Studid 2005. Испо~уя
М~ VJew. ОТ.ttpaШt oRHoServer Explorer (Обозреватель серверов). Затем шелкните

1
950 Часть IV. Программирование с помощью библиотек .NET

правой кноmюй мыши на узле Data Соппесtiопs (Связь с данными) и выберите Add
Соппесtiоп (Добавить соединение) из контекстного меню. В появившемся диалого­
вом окне выберите в качестве источника данных Мiсrоsоft SgL Server. В следую­
щем диалотовом окне выберите имя своей машины из раСКРЫВaIOщегося списка
Serveг Name (имя сервера) или просто укажите localhost, а также укажите правиль­
ную информацию для входа в систему. Наконец. выберите базу данных Cars из
раСl<рываlОщегося списка Select ог епtег а database пате (Выбрать или ввести имя
базы данных). рис. 22.5.
После завершения описанной процедУРЫ, в рамках подцерена Data Соппесtiопs
должен появиться узел ДЛЯ Cars. Обратите внимание на то. что здесь же можно
увидеть и записи любой таблицы. если щелкнуть на ее имени правой кнопкой
мыши и выбрать Show ТаЫе Data (ПОRaзать данные таблицы) из nоявившегося КОН­
текстното меню (рис. 22.6).

Л;1d Соrюе .. LllIП Г?- 1)(\

o..ta~cr:

I I-ta_oso_
L -_· ft_5Q.:...-Serv_;"-,-~-,--
.",;'=----___ .-J (Qw1ge... I

o u...1:l)ndow. дuthl!nfica_

О u... SQI. Serv.,. ~tlcallon


;:;, '1
- - ----.1
., .,.,. , [.
'-- , ,~ __._ .. ~ _ ____ _I

1__. - а '\!i!11II><sR
G Sel!!ct oron!l!r '. _.-. пате: .. __ __ .
._ ---~ --~

Ii!

I~· .. I
,-~ -'--'- -'- . ~--;:=:=,~==:;
[ !б! Сoпnedion I еж I[ CiIraI ]

Рис. 22.5. Соединение с базой данных Cars в Visual Studio 2005


Глава 22, Доступ ~ б8'3ВМ Дi1IМ>lХ ~ ПОМОЩЬЮ. AOo..NET 951

J],-<~; ,•.!~}~,,,,~
';;' :.t:i .NfЭ CМni!<:tiO<i!.
';iJj ~~~ o.,,,,:ObJ:;
r..: ~ "~':~I
- -"fJ ~ ·,iI!P,3@#i
.4dd"li'~ r..ыe
.. .1! ~""'ffJ~ '
~ ]! О,""," l' ..\diJ *,,,Ttigger i
..:;:JV",,,. I ~."Quefy I
, 1 ..::;J $wro ~~.

+ .~ Ii'f~~iiarrl open таЫ.. ro..fiг;!юr1 I


.~ ~,,,,,r~jJ
". ..w uOO':I:.""'P'~te:·. bYtij· S!3aW~D..fa
'" 1f,Se<"...o. I..,";;i c:..cv
-4
,; . .~ llt""Cc1Dfidl'" i v - De/ete
l' '"
I~
[,,~
- "" L
~~ '~~ ~:~Г ""~~>

Ptto. 22.6; I1роомотр дa,*,!>IX та~ЛИЦI;I

СВ1lЗНЫЙ уровень ADO.NEТ


Нацо.м:ним. что свЯЗI-LЫЙ уровень ЛDО.NEТ позв()щtет ВЗЩИМQдеЙство.Вacrь с ба­
зой д-анНЪ)Х, иепользуя Qбъекты со.единения. комаЦД 1'1 ч-,геIППJ ДaF.IНЬJX BaUJero
ПОСТЭВЩИ1(<:I ДэднJ:>IХ. Вы уже JilcrJОЛЬ30Вали ЭТИ объекты в предъхд;ущем примере
Data.P:!'oviderFact,or y. но давайте рассмотрим соотвеТСТJ;1УЮЩИ:Й прoiJ,еrс -еЩе раз
более подробио . Чтоб~ соеДilниться с базой Даннъ1Х и uрочитать ее эanисй с помо­
IIU>IO объ~кта, q'ге:пця да:нн:ых. нeotLi:ОДИ-l\4.O :вьшолни:rь сдеЦУЮIЦljfе шarи..

1. Разместит}>. настроить 11. OTI!1PЫТb об:ье:!tr сое,динеFШЯ:,

2. Разместить и 1iастроить оБЪ~J(Т lЮманды. передав ему объеlfтсоеди.неmm JjJ

виде аргумеша lЩВ:СТРУК;ГОР0, и..'Ш с помощью 'свойства (: ~rmе.~tiс:щ.

3. ВыЗ'ВЗТЬ Exec uteR.e,a:deI () ДlIЯ СКОllфШУРИРОВанвото об:ьекта команды,

4. Обрабrцмъ каждУю запись. иcnодьзун метод Read () объента чтения дан­


IЩIX .

для fia'ЩJIВ соада-йте новое консодыще приложенщ~ с названием .СаrSПа t ё\Rе:аdez: .


нашей дель:ю я:вЛяе!ТСЯ отв:рЫтие соедииенuя (е помощьЮ объекта SqlConnectit.ltL)
и отправна SQL-93IJpоса (с помощью объект Sqlcommai'1dl для получения всех запи­
сей из таблицы Inventory бааы данных Cars. З'атеммы используем Sql Dat"'Rsoder.
ЧТО~ напечатать результаты с по1'оющыо IЦfД~caTopa ТfiЛа , ВОт соотвe-rствующ.ий
ПРQтраJ.\1J"ШЫЙ JЮД Мa:i пО , за которым следует его Щi3JТИЗ,

cll1'ss P r .Qgra'm
1
st.. tic v ,.)id Main (Stl':Lng [] Qr-gs)

11 Созц",иие и O'r1tpbl':l'К8 СОeдIoiW!НP,IЯ.


'S qlC,: «inect ion Сl1 = пеw .$ej LСОQпе:с tiоп () ;
['
,

1
952 Часть IV, Программирование с помощью библиотек ,NET

cn.ConnectionString ~
"uid=sa;pwd~;Initial Catalog=Cars; Data Source~(local)";
сn.Ореп ();

/1 Со~дание об~.к~а SQL-коиаиды.


string strSQL = "Select * From lnventory";
sqlCommand myCommand = new SqlCommand(strSQL, сп);

11 IIо.пуч.ние об~еJ(~а чтении даииwx в C'l'ИJIе Ex.cuteReader () .


SqlDataReader myDataReaderi
myDataReader =
myCommand.ExecuteReader(CommandBehavior.CloseConnectio n) ;

1/ Цик.п по ре~УШотатам.
while (myDataReader.Read())
(
Сопsоlе.WritеLiпе("-> Марка - { О }, имя - (l), Цв е т - ( 2).",
myDataReader["Make"J .ToString() .Trim(),
myDataReader["PetName"] .ToString() .Trim(),
myDataReader["Color"j .ToString() .T rim ());

11 Поско.пъку бwn ука~аи CommandВ.havior.Clos.Conn.ction,


11 д.л. со.дии.ни. и.~ иеобходимос~и . . но ВWЗlolJlатъ Close () .
ПlуDаtаRеаdеr.Сlоsе();

Работа с объектами соединения


Первым шагом в работе с поставщиком данных является создание соединения с
источником данных, для чего мы используем объект соединения (который, как вы
помните, получается из DbConnection). Типы соединения , NEТ получают на вход
форматированную строку соедuненuя. содержащую набор пар имен и значений,
разделенных точками с запятой. Эта информация используется для идентифика­
ции машины, с ноторой требуется установить соединение, параметров безопас­
ности, имени базы данных на удаленной машине и других данных, связанных с
конкретным поставщиком данных.

По предыдущему программному коду вы можете заключит.ь, что имя Initial


Catalog (исходный каталог) дает ссылку на базу данных, с которой вы пытаетесь
соединиться (РиЬз,Northwind, Caгs и т. д.). ИМЯ Data Source (Источник данных)
идентифицирует имя машины, поддерживающей базу данных (ДЛЯ простоты здесь
предполагается, что для администраторов локальной системы никакоrо пароля не
требуется).

Замечание. Чтобы узнать больше о парах имен и значений для той конкретной СУБД, которую
иопользуете вы, в документвции ,NEТ Framework 2.0 SDK найдите и прочитайте описание свой­
ства ConnectionString объеl<та соединения для вsшего поставщика данных.

После создания строки соединения само соединение с СУБД устанавливается


с ПОМОЩJ:>Ю вызова Ореп (). в дополнение к ConnectionString, ореп () и Close ()

1
Глава 22. Доступ к базам J1aWHbI)\ с ГlОМОЩI>IО ADO.NEТ 953
объект соеДИEl.ени.н npедлаг.аe'J' еще целый рид \]Ленов, хот.оРБJе позволдют IЩ­
проить ДОJ]ОЛНН'I€'пIЫU;Iе пара:метры соеДJПIеgия. НЩIример. та.1Ще, как BP~M:U
ожидания R С:ВОЙСТВЗ тран3а1ЩИН. описaI:tия: }leKOT@pbJX Ч.1lенов базового цласса
DЬGоппесti оn предлarаютс.н в табл. 22,6,

Таблица 22,6, Члены типа Db(:':onnec'tion


Ч.nеJf Описание

ВеgiрТrа.щ~асt i оn ' О Мтод, ~GПОJ.lьзуеМblЙ Д.л~ l'Iа4ала 'l:РQнзаrщии

Cha11g€Data.r~a6e (.) Метод, используемый дпя CMe~1 бsзы данн'Ых пр!/! открытоМ СОЕ1Дl'1неr~ии

eonnecti{inTimeour. ДОС1Упное ТЬЛI;КО Для ~теНI<lЯ свойство', 1!о;шращающев значение


времени ОJКидаlJияустанов·КИ соединения, прежде чем будт сгеflери,
Г)ОВ8на ошll1бка (значением по умолчанию я:зляеftR 15 секунд). Чтобы
V1зМеНЮQ 3f1а"lение, используемое по умол~анию. уl<1JЖИте в СтРоке
соеДИfiеНl'\!iI тpetJуеМ'оеЗНВ'I8Ние Connect Tlme.out (\1априЩ!р,
Солnесt Tirr.eoct<H))
DаtЗDаs-s СВ.ОЙСТiJQ, .сообщающее имя баз. ы данных, используемой объ6JCТОМ
СО eДVi.Нe н ИSl

Data 5.о '!;] !'се СвойсТlШ, ещ)бща,ющее информацию о месте размещения базы ~H~
НЫХ, ИСПОJ1ьзуемоVt Объектом о::оединеtlия

GеtSсhеша 1) М~тi:щ, ВQЗВРащающий объект DataSet. КGТоры'й содержи". схему баэы


данных. получеНIiУК;> от ИСТО'tника дaHtlblX

State СВОЙСТВО, УС1:аfiавливающеЕ! :ткущее ооотоя.Нl:1е соеДИненИiI в со.ответ­


I:;1'ВIIII1 со энЗ9еI1IМ1МИ)1S перечня СоппесtiОПStаtе

}{а}( видите, св@йства типа DbConpection в ООЩaПll\ЦIстве своем доступны: ТQЛЪКо


ДЛЯ 'ЧТения (11 CWJy своей uрироДJ;d,) и 0!Щ3ЫВaIOТСЯ: пщrезн:ымиталъко тогда. ]«jrAa
выхатите uо.т.tyЧИТЪ харв:кте.РИС1'йК'И С(i)едщiениg в среде выпэ1нен.ия,' -ЧТоБЫ перео­
пре.z(ел1'lТЪ ЗRaчеЮ!е. устан<tВJIиваемое:nо УМОЛЧ8.1ЦilЮ, B~ дОNЖНЫ изменить строку
соодиненЮI, Например. следУЮЩая С"грокц соединеНИJi yв~ae'I 1!рем.я ожида­
ния соединения с 15 до 30 секунд (пут~~ )'ЕМaRИя СОО'l'ВеТСТВУЮiЦerо знаqeния: в
сегменте CO!'l!llec'LTimeo\.1t строки соед.инения:).

$t.atic vo±d Мaiп tstri1'1g [] .arg5)


{
sqi Сопnе",: iол · Сn = newSql соn:nеc:tiоп () i
(';11:. C·on,Гoe.ct i0nSt r in:g' =
"u1d=sa:pwd.=: Initial Саtаl<щ.:.с;a:.rSI н +
"Dat~ Sощ'се= (1осаl) ; r;:onn.ot 1':l.ПI80ut-ЗСР';
сп . Open () ;
/I Но••• .аСnОХОl'аi.vem.иu фуМХWCJl (CaI. КJDI8) •
ShowConnect.i Q.nStat 1..1з (с.п) ;
... . ~

в этом фрю'менте программного НQЩ1 обратще ВЮD4а:ние на ТО. что теперь объ·
ект соецинении передается в в}Ще napaмe-rpa BOBO~ .вспомогатеm.нoму статйче­
СКnМУ метrщy Sh.оwСоппесt.iодst..аt;uэ О ·КIП:1.CGR P1;ogra.m, рe.am.fзованиому 'ta2(, юн(
показcuю ниже.

,.

1
954 Часть IV. Программирование с помощью библиотек .NET

static void ShowCannectionStatus(DbCoпnectioп сп)


(
/1 Оwображение информации о wекущем объекwе соединении.
Console.Wri·t..eLil1e("***** ИНформация о соединении *****");
Сопз 01 е . Wr i teLine ( "Размещение базы данных: (О)", сп. Оа taSource) ;
Сопзо1е. Wr i teLine ("Имя базы данных: (О)", сп. Оа taba зе) ;
Сопsоlе.WritеLiпе("Время ожидания: (Oj", сn.Согшесti.опТimеоut);
console. Wri·tеLiпе ("Состояние соединения: (О j \п",
сп.Stаtе.ТоStriпg());

Большинство укаЗа1-ПIЫХ свойств самоочевидно. но свойство State все же тре­


бует некоторого обсуждения. Этому свойству можно назначить любое значение из
перечня Corlrlec tionState.
public епит System.Data.ConnectionState
{
Broken, C10sed,
Connecting, ~xecuting,
Fetching, Ореп

но единственными действительными значениями ConnectionState являются


ConnectionState.Open и ConnectionS·t:.ate.Closed (остальные члены этого переч­
ня зарезервировa1-Iыl для использования в будущем). Также заметим, что вполне
безопасно закрыть соединение. состоянием которого в настоящий момент являет­
ся ConnectionStat:.e .Closect.

Работа с ConnectionStringBuilder в .NET 2.0


Работа в протрамме со строками соединений может оказаться достаточно слож­
ной. наПример. из-за возможных опечаток. которые не всегда легко обнаружить.
В рамках .NEТ 2. 0 все предлагаемые Мiсrоsоft поставщики данных ADO.NEТ под­
держивают объекты построuтеля строк соедUJ-Lе/-lUй. позволяющие создавать
пары имен и значений с помощью строго типизованных свойств. Рассмотрим сле­
дующую модификацию метода Ма in () .

static void Main(string[] args)


(
11 Создание с'1'рохи соединении с помощью объекwа ПОС'1'роиwели.
SqlConnectionStringBuilder cnStrBuilder
new Sq1ConnectionStringBuilder();
cnstrBuilder. USerID = "аа";
cnstrBuilder. Password = "";
cnStrBuilder.lnitialCatalog = "Cars";
спStrБuildеr.DаtаSоurсе = "(local)";
cnStrBuilder.ConnectTimeout = 30;

SqlConnection сп = пеw SqlСоллесtiоп();


сп.СолпесtiолStriпg = cnStrBuilder.ConnectionString;
сп.Орел() ;

ShowConnectionStatus(cn);
Глава 22. ДОСТУП к базвf.1 дамных о ПОМОЩЬЮ ADO.NEТ 95'5'
в ЭТОМ варианте IlpОГРil1\1:ЩIOГО ItClда созд<!.ется ЭRЗeмI1JlЩJ Sql С опп е с t i о t! -
StrlnqE!\Jil ,d e r. устанавливаются сооrrвеТСТВyJOщие свойrn:вa и с ПQМОЩЪЮ свои­
стваGо rl11:есtiолStr-.iЛJ} получается внутреюыm строн, а.. Заметьте. чтоздесъ ис­
пользуется HOHcrгpPCТ0p типа, задан:ныi} тю умолчаНИю. Можно также (юздать
экземпляр объекта DОСТРОJ,iтеля строк со~;ципеНИJI дпн пестав1ЦИКЗ ,данных , аере ­
дав уже суще.ствуюIЦyIO СТРOl<у соединеrщ:д в качестве исХодной (от.о може'1' шщ-
3.атъсн пОЛез.IIо тогда. ноrда соответствующие значения считываютея динам:иЧ~СlI'oИ
из файла app.config). ПолучИl3 ТЩ(ОЙ 06ъeK'F!i' началь.нымИ. СТРОЬ."ОВblМИ даmtЬJ.1yЩ,
в.&i можете измеНИIЪ пары Щl.fеи и ~наче}ЦiЙ с помощью соответстцующих СВОЙС"I;В,
ВaIIpИМер:

stat:ic -.)'oid Main (s.t ri ng [] ахчв)


(
Cons ole.WriteLine('(*""" Заба вы С; . чте.нмем данных ~",!k\n");

11 l1peдnonQDW, 'f!l'Q ~oxaci1Str no:n:учеВА из фаРш&, ... COl'1fiiq.


st ri!'J'g c-nStr = '''uid='8a;p'wd=;I ni'tia l Catal,og=Car$;" +
'O:Oata So\1rce= (loc:;al) ; Солспес;;t Тim@·!'Уt1. t=ЗО";

SqlCdnl1ection$'trin:gBuilde r спStrВui l der =


n ew SqlCol~rl..,C't ion.9t:tinqB1)11df';r tс:л Str) ..
cn'S'trE1J·il аеу . U,S€rID = .. sa'i ;
cO,5trBu;il d~r . r a's5woHi = "'i;
cnStrB.1Ji ld.er _Ini tialCatal 0<7 = "Car'g" t
cnS'trBuilder.DataSol.lI'ce = н (1сюа l ) ";

J1 Изиенени.~ ~'leккs времени о~ан:ия.


cnStrB'L1ilder . Со аг,ес:tТimео'lП. = 5;

Работа с обьекта,.и команд


Теперь, ко('да вы понимаете роль объекта еоеДnНeJiИЯ, M~ ВЫЯСНИМ, пав
uредЪЯDИ'Т'Ь SQL-зацрос базе даЙНЫх.. ТИп Sql'Co mm-аnd (который nолучаt"ТСЯ из
DЬ'Соtrlщапd) я:оляет\,:Н объектным предСТ;Е!влением SQL- запроса. ИМ~~Щ таблицы
Jf.]Ц1 хранимой р.родед;'рЫ. Вид. соо:rвeтC'rВ)t.ЮщеЙ ltoмa1Jды уна:зывается о .цомощъю
свойства i:оmщаmqТуре, ноторое може'I принимать· любое значение из р.еречн..я
CommandType.
pHbli c: eaum sуstеrn.Dэtа.СоmmапdТуре
{
sto re91'Tocedl.J.re.
Tabl eDi re ct,
'I' e~t 11 Значение I ИODоm.зуеное по УХOJAaИиn.

JJРИ создa:mш объекта ooмa.цдbl вы можете УЩС4эат~ sg,L-эапрос шш в качестве


параметраХDJilСТРУКТОРа.. или напрямую че~зсвойство СоmП\аndТехt. Также при
ЦIЗДffiПiIИ объекта команд вы ДОЛЖНЫ )'Rазать соeдщrение, .которое будет при ЭТОМ
ИGПОЛЪ3О1ШТЪtя. Это можно 'сделarrь либо 'l;reрез параметр к(шструктора, либо с Jl{»-
моlЦЬЮ свойства C:onnection .
956 Часть IV. Программирование с помощью библиотек .NET

static void Main(string[] argsJ


(
SqlConnection сп = new SqlConnection();

// Создание объекта команды с поио~ю аргументов KOHCТPYK~opa.


string strSQL = "Select * From Inventory";
SqlCommand myCommand = new SqlCommand(strSQL, сп);
/ / Создание другого объек~а коианды с поио~ своЙс~:в.
SqlCommand testCommand = new SqlCommand();
testCommand.Connection = сп:
testCommand.CommandText = strSQL;

Следует понимать, что в этот момент вы еще не предъявляете sgL-зanрос базе


данных Сатэ непосредственно. а только подготавливаете объект команды ДЛЯ ис·
пользования в БУдyIЦем. В табл. 22.7 приводятся описaJШЯ еще нескольких членов
типа obCommand.

Таблица 22.7. Члены типа DbCommand

Член Описание

CommandTimeout Читает или устанавливает значение времени ожидания выполнения


команды, прежде чем будет сгенерировано сообщение об ошибке.
Значением по умолчанию является 30 секунд

Connection Читает или устанавливает значенив DbConnection, которое ис­


пользуется данным зкземпляром DbCommand
Parameters Получает коллекцию типов DbParameter, используеМblХ ДЛЯ пара­
метризованного запроса

Cancel() Отменяет выол~lеliиеe команды

ExecuteReader () Возвращает объект DbDataReader постаВЩИК/i1 данных ДЛЯ доступа


к соответствующим данным режиме однонаправленного чтения

ExecuteNonQuery() Направляет текст команды в хранилище данных

ExecuteScalar () "Облегченная" версия метода ExecuteNonQuery (), предназначен­


ная опециально для запросов, возвращающих одиночные данные (на­
пример, как при запросе числа записей)

ExecuteXmlReader() В Мiсrоsоft SQL Server (2000 и более поздних версий) допускает­


CR возможность возвращения набора результатов в формате XML.
Данный метод возвращает System.Xml.XmlReader, который по­
зволяет обработать поступающий XML-поток

Prepare () Создает подготовленную (или скомпилированную) версию команды


для источника данных. Вы, возможно, знаете, что rOTOBblj:! К ИСПОЛЬ­
зованию запрос выполняется быотрее, и это оказывается важно тог·
да, когда один и тот же запрос требуется выполнять многократно

Замечание. Позже в этой главе будет показано, что в .NEТ 2.0 у объекта Sqlcommand появилось
несколько дополнительных членов, упрощающих задачу всинхронного взаимодействия с база­
ми данных.
~ла~а 2г. доmп k базаМ данЫЫХ с ПОМОЩЬЮ ADQ.NEТ 957

Работа с объектами чтения данных


После создания aRТИВНОГО соединеНИ$J и sQL-I<ом3.нды следующим m:arOM явля­
етен upеД'ЬНвлеRИе ,ganpoca .исто'1НИКУ данных. как вы, наверное. догзд'blВа..е1'есъ.
это можно сделать не.С:JtoЛЫШМИ способами. 11m DbDat.aReader (реализующий
IDataRsader) обеспе'tИ:Вает самрIЙ npocToii 1i самый быстрый способ noлучеfIИЯ
ивфор.мации и.з :х,раНИ.iJИПIЭ .данных. Нa.uоМI:tИМ, что объеКТЫ ~ени.н данных соз­
ДЭIGтодиоы:аправленnъrй 11 доC'J)l11щ,Ш ТОЛЬК() ДЛЯ чтеmmПОТОR данн'ЫХ, воавраща­
ющий по ОДЙОЙ эапиСIi за ОДИН раа. ЛОЭТQМУ должно быть 'Вполне очевидно. что
объекты t.гтення данных иеПQЛЪ<!)'ЮТШi для отправки хранилишу .дaн1IЬ1X только
SQL-оператороlЭ выборки A<fflHblX.
Объекты ~tпmия . данны,х OJщзываютс,Я полезЮiJМИ тогда. коща требуется ()чещ,
быстро прOCМn'1"реть болъJПРЙ объем данных :и IIpИЭТОМ нет необходимости забо­
титьСя об иХ Д!JедставлеIЩИ IJ Шll\lJЯТИ. Например, если вы запраuшвaете 20000
записей из таБлицы1' для того, чтобы GOхранить :их в TeкC'tOBoM файле. ТО при хра­
нении этой ииформаnН}i ц IlataSet ВQзfiИRает достаточно болъш~ uагрущ<а на па­
мять. Волее В'ЬJIОдньtМ решением 9каэыве-тся со3Дание объекта чтенИJi дщmых.
который будет обрабатыва:гь каждую Зa.JШСЪ нacrолыю быстро. наеКОЛЪRО::jТО воз­
можно. Но при этом следует учщтывать то. что объекты чтения ДaIШЩ (:в' ОТJЩ'Чi;lе
Q!I объектов аЩ1Птера Да1-ПЦJ:X, К9торые:мы рассмотрим позже) ttGl,цдерж'ИВЩот от­
I;pblToe сое.дnнerше С источником данных. пока вы нвш) не закроете сеацс сщщи.
ОБЪ,eh"ТЫ чтeцIOl ДЩlНЫХ получаю'ген из объекта команды с помощью вызова
Exe c uteRe a d~r(}. Прц BblB01i1e этого метода объекту чttIПIЯ данных МDЖJJО допол­
нительно дать ицструкцИЦJ автоматичecIOi. ЗаКрЫТЬ соответствующиЙ 06ъ($':I' Щ)­
единеllИf1. yI<.aзав CQmJrJandBehavior . CloS€.Con nectiCJn.
В следующем прим~ре j'fСЦQЛЬЗоВaни.tl объекта чтения .ц.aмн:r,r:x: дрименm:тcя метод
Eea.d () для ODpедедеНИf{ MQмeнтa достижения конца <1anисей (тщда воэврашдеr.wе
ЭRачetiИе ОЩ:lзъщается равн;ым fal.se~. для -Каждой пocтyn.aющеЙ ЭЩJИси ИНДe,Jссато ­
ру nmа дается укаЭaI:Ще нattечатать JШформацию о марке автомоБИЛЯ. его fЩ.звЗНии
и цвете. ТaI\Же обратlпе внимаН:Ие на то. что сразу же rюc.ne заверщешJЯ обр~оТRИ
зaпиr:ей ,~ызы:ваe':fСЯ метод C'lose () • чтобы освободиТЬ Объектсоер;иненин..

stcatic VQild Main(stri.ng(]a:rgs)

/ / ПФЛУчевиа ol$oJ;eX!l'~ ЧU'8JIJIR' даяcrюr JI. СI1'и.tl& ExecuteRead8r С) •


5 qlDataReader myDataReader ;
туDа taReader =
mYCOlJlJl1and. E'.ltecut:e..R.eader (CommandВehavio'L • ClcseCcmn.e ction) ;

1/ 11иJtI'! по р8.!1!y.Do!Иlp}'DЦ8ИV набору.


wl1ile (.myDa..taReader ,Read () )
(
CO.J'lS01 e.Wri teLiTJe("-> Марха . - [01, }Jаэаа';{ие - ilJ, двет - [~) .",
myDa.tEReader ["Make"] .T(,strin.g О. .Trim(),
щуDа. t аRеаd.е:r· (" .Р еtNalШЭ'· ) . T.oString tJ . Trim [) •
щуDаtаRе а d·еr [1' Color" ] . Toi>tri,og () • Tri m () ') ;
J
щуDatаRе:аdеr. О аэе ();
SЬоwС onл €: с t iоns tаt US '(G:>!I );
958 Часть IV. Программирование с помощью библиотек .NП
т
3амечaJotие. Обрезка строковых данных здесь используется только для того, чтобы удалить про­
белы в конце полей базы данных, и это никак не связано с ADO.NET.

Инденсатор объента чтения данных перегружен. чтобы ему можно быдо пере­
дать либо строку (представляющую имя столбца). либо целое число (соответству­
ющее порядковому номеру столбца). Поэтому вы можете немного "подчистить· со­
ответствующий фрагмент программы (избавившись от необходимости печатания
имен) и использовать вариант. показанный ниже (обратите внимание на использо­
вание свойства F'ie1dCount).
w!-,i 1е (туОа taReader .. Read () )
{
Conso1e.WriteLine(n***** Запись *****");
for (int i = О; i < myDataReader.FieldCount; i++)
{
conso1e. WriteLine (" {О) = {l} ",
myDataReader.GetName(i),
myDataReader.GetVall1e(i) .ToString() .Trim());

Console.WriteLine() ;

После Еомпи.iIЯЦИИ И запуска зтого проекта вы должны увидеть список всех ав­
томоБИiIей из таблицы Inventory базы данных Сагв (рис. 22.7).

Рис. 22.7. Объекты чтения данных


Глава 22, Доступ k баз-ам дакных с 'ПОМОЩI>Ю A'DO.NEТ 9$

Получе' flие множества наборов резуnьтатов


с ПОМОЩЬЮ объектов чтения данных
об'ъeJ{ты ч:гешm даННЫХ могут получать от 'QДFЩГО о/5ЪeJ{Та 'К6м:анд;ы множество
наборо~ резулът<j.ТОВ. Например . чтобы ПОJIyЧИ';I1Ь BC~ стрОки тa.бanщы Inventory и
всестррки тэ;БЛИ:U;Ы Customers. МОЖНО уназать оба сщ)'гвс~твующих SQL--операто­
ра. используя ВICaчесme ра.здели'теля тотщу с ;щUЛТQЙ.

striТJg lh'2S,QL ~ "Select " From 1I1ve.n:tory; Selei:t. -;- fTom Cus'tomers";

По~Ив объек:г чтения дam:rьдr:. 'Можн:о переходить от одного реЗУЛЬ"1'ирующеГ(j,


набора}{ дpyroмy с ПОМОЩЬЮ ~eToдa NextResu,l t О . При эггом возвращение R перво­
му набору происходит автоматичео.IOi. Поэтому. чтоБы lIpочитать CTPOКfl ШlЖДQИ
таблицы. МОЖНО IЮстрOlПЬ следующущ итерационную конструкцию.

do
(
whiie(l1\~DataReader.R~ad(»)
{
11 Чтsнке ИВфQрмац5Щ U'ехущеrо ваб'ора резузn.~а'1'оа.
}
'whiJ е (J1tybataReader. NextResult ()) :
Теперь вы ДОЛЖНЫ ,знать болыnе о воаМОЖНЩ.'ТЯХ объектов "пения )),aIiliPIX. Э~
объекты пре.дла:гaIOТ н Другие В03МQЖЦОСТИ, о щrтoрb.D!< ;щесъ Щ' ynоминалоеь (на'­
цример, ВЪ1I1O'Шiение скалярных и о,д1щстрочныxацроG{ш),' СЙО1:ветствующие под­
РОБНОС"ГИ можно найти в ДOl~ументации .NEТ Framework 2.0 SDK

ИСХОДНЫЙ КОД'. Провкт СаfSDаmReadеrразмещен в подкаталorе, Gоответствующем главе 2а

Изменение содержимого таблиц


с помощью объектов команд
Вы :rолыro что убедились, ч'ГО метод Exec1.1teR.eader (). И,3ДJ]eltает объект чтения
дaнnыx. DО3ВОJШЮЩИЙ проверитъ р.езультаты ВЬЩОЛН~НИff SQL-Qператора Sel€ct 'в
ОЩIонаправ,1lI~ННОМ ~ ДOC'IyIIНOM Т{JЛЫЮ для Чте'Ю1Я потоке. Но если 'Вы хотите при­
.мeHWГЬ SgL-ком;анду. в результате которой доmи:ва дроизодти модификauия та­
бтщы. въr.дРЛЖ,trr.I выавать метод ExeCiJ:t.eN,bnQU€ry () cпOTBeTCтвyн'1ll(eTO объекта
.
К0МЩJДЫ, Этот метод 'ВЫnОЛ1:IНeТ вcraiJКИ. оБНОDnения и vдаления в соответсТвии с
,

форматом сооПler.r~твующ~Й команды.


Чтобы ЦРОЦJШЮстрнровать ВОЗМОЖНРстъ МОДИФИКa:Q.lЩ (:ущ~.ствующclI базы
данных с помощыо вызова ExecuteNofiQUf!ry (). мы с вами uостраим новое Ш>Н­
С!ОЛЬНОе ПРИЛ0ше,н~е (CarsIJlvent:ory,Upclater). предоставляющее пользоваТелю воз­
можность :и:ще.uеДИJf дат1ЫХ табmщы lnvento'Y базы ДЭfЩЩ Ca1:S. как и в друпах
I1pим~рах. ~'eTOД Маiп () здесь отвечает за По.ilуче1lllе ОТ подыюватм.н ИFIСТру1ЩИЙ
по ПОВОду DЪШQJЩепия конкретных действий, что прщуа:ммно реализуетC1l с по­
мощью ОПt;ратора&w i tch. Программа: разрешает nOJIЬЗlJвarг~шЬ ввести следующие
J«)МЩi'ДЫ:
960 Часть IV. Программирование с помощью библиотек .NET

• [- вставить новую запись в таблицу Invento.ty;


• u- обновить существуюшую запись в таблице Inventory;
• D- удалить существуюшую запись из таблицы Invento.ty:
• L- вывести информацию об имеющемся наборе автомобилей. используя
объект чтения данных;

• S- показать эти варианты выбора пользователю;

• Q- выйти из проrраммы.

Каждый возможный вариант обрабатывается своим уникальным статическим


методом в рамках класса Program. ДJJ:я полной ясности вот реализация Main (). ко­
торая. как кажется. не требует никаких дополнительных комментариев.

static void Main(string[] args)


(
Console. Wri teLine (" ** * н Модификатор Inventory для. СаЕ * * '" * *") ;
bool userDone = false;
string userCommand = "";
SqlConnection сп = new SqlConnection();
cn.ConnectionString =
"uid=sa:pwd=;Initial Catalog=Cars:" +
"Data Source=(local) :Connect Timeout=30";
сп.Ореп() ;

Showlnstructions();
do
(
Сопsоlе.Writе("Введите команду: ");
userCommand = Console.ReadLine() I
Console.WriteLine():
switch (usеrСоmmапd.ТоИрреr())
{
case "1":
InsertNewCar(cn) :
break;
case "И";
ИрdаtеСаrРеtNаmе(сп) :
break;
case "О";
DeleteCar(cn):
break;
case "L":
ListlnventorY(Cnj;
break;
case "5":
Showlnstructions();
break;
case "Q":
userDone true;
break;

1
Гп&вз 22, Аоет~п к базам данных с ПОМОЩЬЮ AOO.NEТ 96'1
default.:
Соnэоl е. w:t.i teL.ine ("ЫеI(ФрреК'I'RЬ1е .1iIаъ"НЫе J Вве:цит!;!! 'nРУТ'И€.") ;
Ьгеак;

} while (!uэе.rDОIJе};
on.Close{);
}

Метод ShG)wIn~tгuсtiолs () Де,щ1.ет ТО', -.:по и следует ожццать.

pl'ivate .static voi,Q Sl'1.o.wInstruCtiaIl3 ()


f
Ссщ,sэlе. W.riteI..ine О;
СQпsоlе. Wri teLin.e ("}:. добаВЛЕние Й.оВQИ маШИFIbl. ") ;
CO!).501e. Wri t~Lill.~ ('''Ц: модифщ-::ащrя ИМЩQЩейся машин-ш. 11') :
С=801е. W'г.Lt€Ь.inе .("1 D: !"дале'ние имеющейсZl мaшI1D'iЫ.");
Cotls·ole. wr-i teLihe t "Ъ: С;ПИGDJ<: налиЧнЬР<: машин. п I;
Con.sole. WriteLine (-"13: BbШO'n, И:r1С'JJРУ1'-.ций.") ;
Сопеаlе. Wri teLin'6 ,( "О: выход из арограммы.") ;

Rap< уж.е ynом.иналос:ь, MeтoiЦ Li st In уе nt ату () печатает СТРОКИ 'fа:б'-лицы


Inventory с помощью объе~тэ. Ч,те}Щя ДaщIЫX (соответствующий nрогрaммный код
аналогичен црограм:мному ROДY ПР~ДыдУЩего примера ОаrsD\Э.tаRеаdег).

pr.ivate static vaid ListТп>:Zеntоrу (5ч1СО.fill.есtiGЮ сд)


(
зt,д1: 119 strSQL = "Eicele-~t ... Б'гоm
Itrvehtp-ry<1;
S.gIСоJliПlапd юуСQl1lщв.пd = new ЗчlС01nrnапd (st'r SQL, сп);
SqlDataReader mуDаtаR""аdщ::
myDataReader = mуСоо:tшaro..d. EKecuteВ.eader () ;
while (myDat.aReader.Read())
{
fDr (int i = 01 i < myDаtаRеа.dеr.Fiе1.dСО1:юtJ i+~)
{
C0l1so1e.WritE("{O} = (1} ",
rnyDataRead€r,GetNarne(i),
myDataRea.der.GetV~lue(i) ,ToString() .Trim(});

Cort.$o.le . Wri teLine JJ :


J
rnyDataR.eade:r:.Close();
}

ИТЗR. текстовый интерфейс ПОJlЬ:;lоватe:rJЯ ГOTDB~ И мы можем теперь перgйти к


более иFf1'ересным вещщ.!.

Вставка H081.IX записей


для вставки новой Эanи:СИ в таб.1IИЦY Inventory nужно (на основе IЮлъзователь~
схрго ввод.а) ооздать SQL-оператор вставки и вызвать ЕхесutеNопQщ"rу (), Чтобы
не эатроМоЖДаТь пpQгр~ ~OД, здесь из него удалена нео(5ходимая програм:~
962 Часть IV, Программирование с помощью библиотек ,NET

мная логика try/catc h . которая присутствует в загружаемом варианте программ­


ного кода примеров для этой книги.

p ri vate !:H_atic void I л s е r tN еw С а r (Sq lСолл е с tiо п е л )


(
// Сбор информации о новой машине.
Console. Wri te ("В в едит е н о м ер машины: ");
iлt newCarID = iп t .Раrsе(СОП501е.RеаdLiл е ());
СО[1501е. Wri te ("'ВвеД'ите марку; ");
5triлg [1ewCarMake = Солsоlе . ReadLine ();
СО П5 0 1е.Writе("В в еди т е цв е т: ");
5 tr ing леwСаrС оl о r = Со лsо l е . ReadLi ГJ e () ;
Солsn l е . Wr i t e (" 'В веди те н а з ва ние: ");
st riлg newCarPetName = Con so le.ReadLine();

/ / Создание и вьmоnнеиие оператора SQL.


s,tr'ing 5ql = st r in g . Format ("In5ert Int o I пvелtОL'У" +
n (CarID, Make , Co l o r, PetName) Va1ues" -1-
"('{О)', '{l,', '[ 2 }', '{З}')", леwСаrID, ne wCarMake,
newCarC010r, newC ar PetName);
SqlСоmmалd cmd = л еw SqlCommand (sq1, ел);
c md.ExecuteNonQu e r y () ;

Замечание. Вы, возможно, знаете , что построение SQ,L-операторов с помощью конкатенации


строк достаточно рискованно с точки зре~IИЯ безопасности (вспомните о возможных атаках
SQL-инъекции). Здесь зтот подход используется только ДЛЯ простоты, поскольку, конечно же,
предпочтительнее строить текст команд с помощью параметризованных запросов, обсуждение
которых предполагается немного позже.

Удаление записей
Удалить существующую запись так же просто. как и вставить новую. Но, в от­
личие от программного кода для IлsеrtNеwСаr(), ниже демонстрируется важная
возможность применения try / catch ДЛЯ обработки попытки удаления автомобиля,
используемого в настояIЦИЙ момент в процессе оформления заказа для покупателя
из таблицы Customers (сама эта таблица будет рассмотрена в ЭТОЙ главе позже),

pr i vate static v o i d Dеlе t еСаr(Sq1Соплесtiоп с п)


(
/ / Получение номера машины ДЛЯ: удалении и само удаление.
Console. Wri te ("Вв е дите номер машины для удаления: ");
int carToDelete = int . Parse (Солsо1е.RеаdL iл е () );
str i ng 5ql =
stri л g.Fоrmа t ("De 1e t e fro m I пvелtоrу wh ere Car ID ' {О} "',
c arT o Delete) ;
Sql Command cmd = rTew S ql С оmmа л d(sq1, с п);
try [ c md.Execu t e No nQu e r y (); }
catch
( СОЛ501е. Wri t е L,iл е ("Из вини т е, на эту машин у о ф ормляется заказ!"); )
Гпава 22. Аоатyn 1< 5азам A8Htt\;IX с пом.оЩЬЮ ADO.NEТ 963

Обновление записей
Если БЫ разобралt1Сl1 с ПРОГРClJ\1МНЫМ'1\ОДОМ ,ДЛИ nel~teCar'( i ~ ln!S,er tNe-wСаrО.
то и IJPОI'рС1ММН14Й КОД ДЛЯ Upddte ~;: arPetName () :не будет ДЛ'Я Ba~ сложным (эдесь
ДJJfl цро.СТОТ,bl .допrnа try/catc:h тоже "Исключеfiа) ,
priV'.a,t e st.<i!'tlс:.IГDid 'U,рdаt:еСа.r:Ре'сN'iiше (SqlСоl1t.1есtiол crj)
!
// nO~учениа нонера .~~ P.mI ~0.цифnk8.ЦИИ и вв,о,ц НОВОГО .lВUiШИII'.
СОIlSlЭl",. 'Writе \ "В ве'ците номер !Ji4аши1'!Ы.дJ"lЯ МО.дИфUlкаiJ,.ни : ");
5'!.!' i ng 'пеwРе~Namе ~ п п ;

i,пt сзr'I'о\Jрщ, te = ,c<irTolJpdate ~ in1.. Fa:rse '(С Qлs,оlе. ReadLine () ;


C:onsolE' . Wr:i te '! "'ВведЙТ.е INJSO,e наз'В ание: ");
J1ewFet_~ame =' C'c)J1.501e. R,e ad.Line ("\ ;

/ / Обl\lOВJ.lе.виа ЗiЦПfСJ!.
slriйg sч1 =
st :ri ng.FЬrUJбt("Орdаtе Im!~ntljry Set Fеt.ЩШJе='!ОI, · Where CarID=',{1} "',
t1~wРеtNа.ше, .ca.r'I'olJpda't.e) i SqlСоmшаnd сшd = 'rJE'W SqlCt5m:rrJand (sgl, СП);
t~,d . Е...ХЕ:f:litЕNО·(jQпе:rу () ;
}

На ЭТОМ соаДaнIifе црило~ за:вершаeтq.я. На рис. 22.8 DО:казан результат те­


стового задуска' этого ПРИJIожеНИfl ,

Рис. 2.2'. 8. В'ставка, О.БНОВJ1ение и у,даление за~сей Q паМОЩЬt0 объектов команд

Раб'ота соб-ьектами параметр, изованl'IЫХ команд


Показапная выше программна$ логика вставки, обномеEЦ?lЯ :И Удa.i,еJЦIЯ ра,бота­
ет ta}{. Raк J{ OЖИJ1аетс,ц. однако Qбрат~тt;. ВНИМaF..ие на то, ЧТQ кaщдый иа. SQL-за­
просов :щt\съ uредставлен ажеСТIЮ" ЗalЩДИРОВaII1'IЬЦlm С'l"РQfСО~ЬД\fИ .IЩТ~а.flа:ми. Вы,
964 Часть IV. Программирование с помощью библиотек .NET

возможно. знаете. что с SQL-параметра.~и можно обращаться, как с объектами, а


не с простыми строками текста, есди использовать naрамеmpu.зованные заnросы.

Обычно параметризованные запросы ВЫПОЛНЯЮТСR намного быстрее буквальных


SQL-СТрОК. посколь1\у они анализируются только один раз (а не каждый раз, когда
SQL-строка присваивается свойству CommandText). Параметризованные запросы
также обеспечивают защиту от атак SQL-инъекции (это известная проблема без­
опасности доступа к данным).
Объекты команд ADO.NEТ поддерживают коллекцию дискретных типов параме·
тра. По умолчанию эта коллекпия пуста, но вы можете добавить в нее любое число
объектов параметра. которые должны будУТ отображаться в Uзаместитель" параме­
тра в SQL-запросе. Чтобы ассоциировать параметр в SQL-запросе с членом коллек­
ции параметров данного объекта команды, добавьте к текстовому SQL-параметру
префшtс @ (это работает, как минимум, при использовании Мicrosoft SQL Server. но
такое обозначение поддерживают не все СУБД).

Указание пара метров с помощью типа DbParameter


Перед тем как приступить I< построению параметризованных запросов, мы:
должны рассмотреть тип DbParameter (который является базовым нлассом объек­
тов параметров, специфичных для конкретного поставщика данных). Этот нласс
поддерживает ряд свойств, позволяющи..х указать имя, размер и тип данных па­
раметра, а также другие его особенности, например направление параметра.
В табл. 22.8 описаны некоторые свойства типа DbParameter.

Таблица 22.8. Ключевые члены типа DbParameter

Свойство Описание
DbType Читает или записывает иНформацию о "родном" типе данных для источни­
ка данных, представленную в виде соответствующего типа данных CLR
Direction Читает или записывает значение, указывающее направление потока ДЛЯ
данного параметра (только ввод, только вывод, двунаправленное движе­
ние, предусмотренное возвращение значения)

IsNullable Читает или записывает значение, являющееся индикатором того, что па­
раметр допускает значения null
ParameterName Читает или устанавливает имя DbParameter
Size Читает или устанавливает максимальный размер данных параметра

Value Читает или устанавливает значение параметра

для иmпoстрации мы модифицируем предыдУЩИЙ метод InsertNewCar(), что­


бы в нем использовались объекты параметра. ВОТ как может выглядеть соответ­
ствующий программный код.

private static void InsertNewCar(SqlConnection сп)


(

/ / Обратите внимание на 'запOJIНИ'l'еnи' 11 SQL-заnpосе.


string sql = string.Format("Insert Into Inventory" +
"(CarID, Make, Color, PetName) Values" +
"(@CarID. @Make, @Color. @PetName)");
Глава 22. Доотуп k базам данных с nОМОЩЪ.ro ADO.NEТ 965
11 Напonaеиие 1C0Jtn!bцRJC дараие!t'pOJI· .
sql'command cmd = пеу,! Sq:leornmand (sgl, сп) i
Sqli'i\'rareetar раrаш = neirl SqlFar.ameter() ;
param.Parameter.Name = "@CarIO";
pa.r,am . Yal Щ~ t? леWCа.с ID;
pa~am . SqlDb~ype ~ SqlDbType.Iht:
ctr'.d. ParameteTS' ..]);dd (рахаn\) ;

ратат ~ new SqlParameter ();


p.aram. Paramete:tName = "@мak6":
рахат. Value = newC:arMa'ke;
pa,ram. SqlDbI'ype = ·S qlD,b lJ'ype. Char;
pa<r ат . S i z.e =2'01
о;;щd.J?аrщ:mеLеrs.,о,dd(раram) ;
раJJ1Щ, = new SqlPa rameter () i
p.a:ram. ParameterName '" "@:Color i \ ;
рауат. val11!.e ;: newCapColor;
р,а-тат . .8qlDb'I'ype = 'SqlDbТype,. Char;
param.Size = 2:0 ;
cmd. Farameters. Add (ратаm.) ;
рахarn = rne1l1 :Бql ,Р.аr:аmеtеr () ;
рат,ат ..ParameterNarne = i'@FetNa111e·" I
param. Уа:lие = newCarP'e t·Name;
ра:ram.БqlDЬТуре = SqlDbType. Char;
ра:rаm.Si2:З'" 20;
cmd. Para~te;r-s .Add (pa.r: am) ;
cmd. Exe,c uteNonQuery () 1

Хотя npисоэдании , параметр~эованно:rо 'запро.са требуется ВВОДИТЬ большой


объем програММJIОГО кода. {tоnечный резульrат акаЗI:ilвае'J'СВ более вытоДНЫМ с
точки зрения програмМJЩГО ис-цользовaюi'Н SQ1гопер-аторов. Улучшается также .и
общая ПрОИЗ1'lОДИТедЬ1-ЩСТЬ. Е!ы, Rонечно.можe-rе использоваТЬ предлагаемый под,.
~Д для всех SQ,l.rзапросов, но щmболее полезными параме:тризованНblе эацросы
оказbШalOТC8 при эanyс~ хра:цим:ых npoцe;wp,

замеЧа14ие. Здес& Д11Я Щjзданv.~ объекто.!;! п<'!рам(про-в IIIстIQJlЬЗОВЗЛI1СЬ различн.ыe свойства. Но


Сm:Jдуетзнать.и о roм, ЧТО объект~ naраметроа 't1сщцеР)i(~Вают L.I.!!nЫЙ ряд neрегружекных 1(011-'
CTpyI<TOpOS. также ПОЗВОЛЯЮЩИХ уотанЩ!итьзначения СВОЙСТВ (кроме того, в результате ПOJ1у­
чается более кампактftый БQЗDВ~Й ПРОJ"р8ММflоIИ lФА).

Выполнение хранимых процедур


с помощью DbCommand
.х;рmщмой процедУРОЙ наЗывается блок lIporpaMMfIorQ кода SQL, coxpaнeнны:ii
s базе д8.НJ'ihlN. Хранимые процедуры MOryт соадаватьс» .для тoro, ЧТ()бы :цозвра­
ща:гр .i;I~боры строн или скалярных ТИПdВ дaшtЬiХ. J] MOryт имеТl1 щобое число 1;fe-
об~эатеJtы-IЫX naра.~етро-в. pe-ЗУJШtато:м 1ШЛяется рабочая ~едшцща~, которЩl ведет
966 Часть IV. Программирование с помощью библиотек .NET

себя подобно тицичной фующии. С той очевидной разницей, что размещается она
в хранилище данных, а не в ДIlоичном рабочем объекте.

Замечание. ХОП1 обсуждение соответствующей темы в этой главе не предполагаеТСf1, CaMaf1 новая
версия Мiсrosоft SQL Server (2005) включает в себя CLR,xOCT! Таким образом, хранимые проце­
дуры (и другие атомарные единицы базы данных) могут создаваться с помощью управляемых
языков (например, С#), а не только с помощью традиционного языка SQL. Подробности можно
наЙТИ на страницах http://www . microsoft. сот/ sql/20 О 5.

Для иллюстрации соответствующего процесса давайте добавим в программу


саrlпvепtоrупрdаtе новую опцию, которая позволит пользователю выяснить на­
звание автомобиля с помощью хранимой процедуры GetPetName. Этот объект базы
данных бьш создан при установке базы данных Cars. и выглядит он так.

CREATE PROCEDURE GetPetName


@carID int,
@petName char(2D) output
AS
SELECT @petName = PetName from Inventory wl1ere CarID = @carID

Сначала обновите имеющийся в Main () оператор switch, добавив в него об­


работку нового случая "Р" для вызова новой вспомогательной функции с именем
LookUpPetName (). которая принимает параметр SqlConnect ion и возвращает
voi.d. Обновите также метод Showlnstructions (), учитъmая новый вариант вы­
бора.
Чтобы выполнить хранимую процедуру. следует. как всегда, сначала создать
новый объект соединения, сконфигурировать строку соединения и открыть сеанс.
Но при создании объекта команды свойству CommandText следует присвоить имя
хранимой процедуры (а не SQL-зэпрос). Также вы обязательно должны установить
для свойстваCommandType значение CommandType.StoredProcedure (значением
по умолчанию является CommandType.Text).
Поскольку наша хранимая процедУРа имеет один входной и один выходной па­
раметры, нашей целью является построение объекта команды, содержащего два
объекта SqlParameter в своей коллекЦйИ параметров.
private static void LookUpPetNamelSqlConnection сп)
{
/ / Получение номера маШИЮol.
Console. W:r:i te ("Введите номер машины: ");
lnt carID = int.Parse (Console.ReadLine () );

/ / Установка имени Хранимой процедуры.


SqlCommand cmd = new SqlCommand ("GetPetName", сп);
cmd. СОJТlIПдлdТуре = СоrпruапdТуре. StоrеriРrосеdше;

// Входной параметр.
SqlParameter' ратат = new SqlParameter () ;
param.ParameterName = "@carID";
param.SqlDbType = SqlDbType.lnt;
param.Value = carID;
param.Direction = ParameterDirection.lnputi
Глава 22. ДCJстул к '6}ааам Aal1HblX с nОМОЩЫ!) АОО. NEТ 967
сщQ. Fiaramet$rs. Ad.d (раташ) ;

/I ВlilXо,цвой параиеorp.
p.aram = new Sqll'",r.ёJrneb=r () ;
раrал).Рэrаmе-t.еrЫamе = "Щр.еtNаше"1
param.SqlDhType = Si;jlDbType.Chд;r;
Fс,rащ.Sizе = 20 ;
par-ё1д1.IJirесt i оn = }'aL:arneterD iTectiori, 01.1tJ>,'ot:
cmd . I"ararneters. Add (ра ram) ;

1/ 1IъПI'Cinиeuиe ~Р~ЙD;pацедУРloI.
c:rn,:l. EKetIJ teNonQu e ту .() ;

iI Печать выходного пара.нетра.


rQn'S('l~.WriteLlr(1:9 {"машина 10} J,Jазыв.аеl'(:Я {1] '1,
са:rП) , CrIid. 'Fa:rame.talrS ["@pёtNarncё:"1 .Value):

ОбраТft"Ге :внИМание на то, "<fI'O свойство Di recti ВЛ. 'Объекта параметра позволя­
ет указать з:ходnые и выходные параметры. ПО заверщении БЬ13ова хранимой про­
цедуры с помощью Exe.cuteNQnQuery () вы можете lЩЛУЧИТЬ эна'reЮrе ВЫХОДIJl)fО
параметрз, обраТ.fПЩJИСЬ к :кощ:rе:кЦЮ'I' параметровобъекта :команды . На рис. 22.9
II<lliаgаи оД}'ш иЗ БО3МОJl\.'нык вариантов тестового зацуека программы.

Рис. 22.9. Вызов храl'lИЦ'ОИ процедуры

Иф(ОДНЫЙ КОА. -Проект ОэrSlпvепtогуUрdаtег ра~мещен в roдкаталоге, сюотаетотвующем mаве , 22.

'1
,
968 Часть IV. Программирование с помощью библиотек .NET

Асинхронный доступ к данным в .NET 2.0


в .NET 2.0 поставщик данных SQL (представленный пространством имен
System.Data.SqlClient) усовершенствован с тем, чтобы он мог подцерживать
асинхронное взаимодействие с базой данных, используя следующие новые члены
SqlCommand.
• BeginEx.ecuteReader () jEndExecuteR.eader ()
• BeginEx.ecuteNonQuery()/EndEx.ecuteNonQuery()
• BeginExecuteXmlReader(J/EndExecuteXmlReader()
С учетом материала. представленного в главе 14, названия пар этих методов
можно считать "триумфом" соглашения о присвоении имен. Напомним. что в ша­
блоне асинхронного делегата .NET используется метод "begin ~ для вьшолнения
задач во вторичном потоке. тогда КаЕ метод "end" может использоваться для по­
лучения результата асинхронного вызова с помощью членов IAsyncResult и не­
обязательного делегата AsyncCallback. Поскольку работа с асинхронными коман­
дами моделируется по образцу делегата. простого примера в этом случае должно
быть достаточно (но не забудьте снова заглянуть в главу 14, чтобы освежить в па­
мяти подробности. касающиеся использованИЯ делегатов асинхронного вызова).
Предположим. что нам нужно выбрать записи из таблицы Inventory во вторич­
ном потоке выполнения. испольэуя объект чтения данных. Вот полный текст соот­
ветствующего метода Main () с последующим анализом.

static void Main(string[] args)


{
Console.WriteLine ("***** Забавы с ASNYC DataReader *--***\п");

/ / Соз.цание OT!CpW'1'O:rO сое.цинеНИR в асинхронном режиме.


sqlconnection сп ~ new SqlConnection();
cn.ConnectionString =
"uid=sa;pwd=;Initial Catalog-Cars;" +
"Asynchronoua Рrосеsзing=truе;Dаtа Source=(local) ";
сп. Ореп () ;

/ / Соз.цание об..ехта SQL-ХОlllaидJol, ожи.цaJOЩeГО ОICоло 2 с.


str:ing strSQL =
"WaitFor Delay '00: 00: 02' ; Select * From Inventory";
SqlCommand mуСоmmалd = new SqlCommand(str:SQL, сп);
/ / ВЫполнение чтении во ВТОРОН потохе.
IAsyncResult itfAsynch;
itfAsynch -
myComrnand.BeginExecuteReader(CommandBehavioI.CloseConnection);
/ / ВЫполнение действий во время рабо!t'Ы .цругого потоха.
while (!itfAsynch.IsCompleted)
{
Сопsоlе.WritеLiпе("Работа в главном потоке ...• );
Thread.Sleep(lOOO) ;

Console.WriteLine();
Глава ~2 . Доступ 1( -базам дafHfblX с помощью доо. NEТ 969
IJ Ее.. ~oonoso! Въш:ОЦКeJ!И8 циq&по ре!lУ.иь'l'.&'l'UI
1/ с ~ОМОIЦЬИ) обrъeX'l'A Ч'l'$НИИ ~i!lBвpjr.
SqlDataRe~cler ,royDd:taR~adR:r =
mYCQmmanci. E,ndE>te'GUteReader (,i t.fAsyoCfi) ;
while {шуDаtаRеаdеr. Read (j )
{
СdП501",. Wri teLifJe ("-> марка - {[)}, наз-вЗ-ние - (,1 J, uвe~ - J:2}.",
шу'DаtаRеаder r "М'аkг" ] . ToStriug(.) ,Trim () .,
mYDiiitE\:F..eade!' [·"PetName"] .'1'o,S tring(} • T.rl.m () г
myDa baRead~J; [ "СФ l,o·r" ] • Тоs-t:tiпg () . Tr1ro (J D;

mY:Di!1taReade][. Сlс>.Э~ () i

Первый интере.сным моментом вдесь является то. что вы долщнъ) рааре­


шить асинхронное взаимодействие .с ttОМOIlIЬЮ нового с~гмеи.та А'5 yn'c Ь rO!~ о и $
Processing в строхе соеДШlеflИЯ. Также отметьте, 'Что в тенст объекщ :КОМЩIДЫ
SglCommand был добавлеll сегмент Wal tFo.r L1elay д.п:sI и:ми:тощиа Дl;IlJJ'елъ~ото вза­
Имодействия с базой. данных.
Кроме этого обратите m-шман.ие нато. '11'0 ВЫЗОБ ВеglдЕ.хесutеDаtаR~аdеr (.!
ВО3Нрш:цает ОЖIЩаемыИ IAsyncRe's u1 t-совмести.l\IIый ТИП, крторый l!П~ЦОЛbllУется
дл.я синхронизации потока вызова (с помощью свойства IsC'o mpleted), а "ГaIORe для
ШJлучения SqlDataRead.E :r по saвeptueIOnl вЫIIОЛНem1Я запроСа,

Исходнlt1Й ХОД, Проект AsY"CCmdOЬ!ect размещен. а nOAl(aTMO~e, СООТI3f)<ТСТ8УЮЩем rnаве ~.

Несвязный уровень ADOJlNEТ


Кан вы у6еддлись. рабата со СВЯЗНЫМ слоем позволяет взаимодействовать с ба­
зой д<щных. ис.по.цъ:wя объекты соединеНШI. команд й чтения ЦaJПIЫX. С ПОМI;>ЩЬЮ
:небoл:ьmОl'(1J набора тйПов вы можете ПО желанию выбирать. вставляТЬ. обновлять
и удалять запИС;И (а также запуСJ(,ать храни'мы! процедуры). НО иа самом деле
вы пока ~TO У<JНали только noлавину того, что вам следУет Знать о возможностях

ADO.N.E;Т: ЛО3ТОl\-ty напоминаем, ЧТО объектнапМОДеЛЪ ADO.NEТ 'Может исполъэо­


ваТЬС!1 ДJЦl взаимодействия в «еС8J13НОЙ форме.
При работе на неС'ВНзно:м: уровне ADQ.NEТ вы по-npежнему должны .исполрзо­
.вать оБQекты со.единения и I{Оманды. Кроме того, Bы ДОЛ$I-IЫ ИCiШJ1lьзоватъ спе·­
ци~i1 оръект, наэываеМ}J1И ад.аптером дан.ных (и расnшpяющиi1 абетрахтцьrn
QbDataAdapt.er)', чтоб.ы извлекать 11 обновлять данные. В ОТ!1И'lие от свя~ноro
слon. дщшы-е, полученные с naмoщью адaпr'ера данных, не обрабатываются с по­
~ОЩЬЮ об~l{та -ЧТения дaнных.. Вместо этого для перемеще.нйя .да:ЮiЫХ междУ вы­
зъщающей СТОРОНОЙ и ИСТОЧНИКDМ данных объекты ~anтера дс'цщых иcnоз:u>зуlOТ
оБЪeI"'Щ D~tp.Set. тип Data:S,et - это нонтеi'Iнер, используемый ДJ-Щ любого 1Щсла,
<;>БЪill<Т9В DataTable, КаЖД:ЫЙ из которъш содержит ROЛJ1екциro объеЮ'OfI.DаtаRо.w и
!).i3-t аСо luН1Щ
О!:iъщст адаптера.Данных ватеro ПQСТавщика, данных обрабатыает соединение
с ба30Й данных ю;тoмa:rичесltИ. С целью расширеmtя ВО3МО)Ю1.0стеЙ масщтаб,ируе­
мостиадanтеры данных сохраняют соединение o-rxРblrJdМ Мill:IИмальцо :возмоящоо

1
970 Часть IV. Программирование с помоЩью библиотек .NET

время. Как только вызывающая сторона получает объеи:т Da-tаSеt. соединение с


СУБД разрывается. и вызывающая сторона остается со своей ЛOI<алыюй копией
удаленных данных. Вызывающая сторона может вставлять, удалять и модифици­
ровать данные DataTable. но физически база данных не будет обновлена до тех
пор. пока вызывающая сторона не передаст явно объект DataSet адаптеру данных
для обновления. В сущности. DataSet позволяет клиенту имитировать постоянно
открытое соединение. в то время как реальные операции выполняются с наборами
данных. находящимися в памяти (рис. 22.10).

Приложение-клиент

r г- -""--
.....
i'--- ...--' ..., 1'--
DataSet .J Адаптер данных База данных
~

'- ---
Рис. 22.10. Объекты адаптера данных передают объекты DataSet кли­
енту и возвращают их обратно базе данных

Поскольку основным элементом несвязного уровня является тип DataSet. на­


шей следующей задачей будет выяснение того. как управлять объектом DataSet
вручную. Если вы поймете, I{aк это делается. то не будете испытывать никаких
проблем при манипуляциях содержимым DataSet. извлеченным из объекта адап­
тера данных.

Роль DataSet
Упрощенно говоря. DataSet является представлением внешних Данных в па­
мяТи. Более точно. DataSe.t представляет собой туш класса. поддер:щивающий три
внутренние строго типизованные коллекции (рис. 22. 11).

DataSet

IDаtаТаblеСоllесtiоп
I DаtаRеlаtiопСоllесtiоп

I Рrореrtусоllесtiоп
Рис.22.11. "Анатомия" DataSet

Свойство ТаЫеэ объекта DataSet позволяет получить доступ н: Rоллекции


DataTableCollection. содержащей отдельные объекты DataTable. Другой важ­
ной коллекцией DataSet является DataRelationColle~tion. Ввиду того. что объ­
ект DataSet является "отсоединенным" образом структуры базы данных. можно
программно представлять родительско-наследственные связи между таблицами.
Например. с помощью типа DataRelatioo можно создать отношение между дву-
Глав8, 22. Дос>ТVI:I к Мзам ДЭfIНЫХ.О rЮМсОЩblО ADO.NET 971
ми таблиuами. моделирующее ограничение вНешнего 1tЛюча. Соответствующий
объеКiМQЖЯD затем добавить :в DаtаRеlаtiОhСОllВlZtiЩI с пОМОЩЬЮ свойства
Relaticms. После этого вы сможете осуществлять переходы Мe.'1tщу сое;nшенными
таблицами при -Поиске дaFшых. Кщt 3'1'0 реализуется на прЕЩТIЩе, будет ДОI<а.зано
неМного позже.

СВОЙСТВО ExteflcledFroperties обеС'llечивает достуд Jt объекту pyoperty-


Collecti оп. который llозволдет ассоциировать с De ta.Set .nюбyIо доIIолнитедыIfy1Q·.
шrформanиIO, иcnолъзуя пары ИМ~ и значений. Эта инфоp!lЮ11ИЯ может быть пра:к­
ТИ'iесШl любш'l. и даже вообще не иметь Н1iк<щого QTl-щшенйЕ к даИНbl:\I<l. Например,
:можно связать с D@taSe-t н~вание вашей IЮмп:ании. ноторое В этом сiIt)'Чае может
выступать в роди вюnQчснны1{ в иaмJlTЪ метаданных. ДрУГИМ~И: np:имерами ТЗJ~ЦХ
расширенны.~ св~й:С1'В M~ryт быть штамп Д;1тЬ!fвремен:и, ши:фроваНJ;IЫЙ пароль.
lЮl'ОРЪ1Й необходmю будет указать д.JlЛ доступа tt содержимому цат аЗеt. число, i;la-
дающее частоту ООНОБ.;·:rения ДШ:ЛIЫХ. И Т.Д.

3амечаниа. КлассDataTabl€ Т8.юке ILОДlJ.еР:ЖИВВ81 расШирение свойств G помощью овоостna


ExtendedPr.opertLe.(:' .

Члены DataSet
Перед пот руже1I1!lемв миогоч:исленвые детали програ:м.мирования ,иавай,'е
расемотри:м Н'абор базовых ч:iтеновDа:tаs-еt. Кроме свойств Tables. Relations и
Ехtелd",-dPIт\р.егl.i.€s. в табл. 22:9 опИсаны некот{JрыIe другие интересные свой­
C'J"Вa.

Таблица 22.9. СВОЙС1fВЭ D$.'[",Set

СвоАство О.lисание

CaseSe!1sit~ve Индикатор чувствитеЛЫЮСТId к perj,1CTPY· Символов Тlри cp.abH<Jt-fИи


строк в оБЪ6!(тах DaU'l'Rable
ПРe;!J.стаsляет понятн:ое ИМЯ данног!;) объектаDEJtа8~t.. ()[iычно это
значеНие устенэ.вливаеТСR Gпомощью napaM8Tp3 KO!-lсrrрУ1<ТОра

EJ1 forc",CoJ1:st l'ёJ iлt $ ПОЛУ'Н1ет ИЛИ устаНЭВ'i1ивает значение, являющееся иt;i·ДИI':aТОРО~(
I-lеобхоДPlМОctJ.f ТlРIIIМеtl8НИЯ заданных ограничений при любой
Qnерации а6новлеН(/,I~1
HasErLors Получает ЗН8'tение, ЯВЛЯ1Dщеес!\ \IIIЩИКЗТОРОМ НW1ИЧИЯ ОlJjибок В
люtJой 1113 строк обмктое Dato!Ta1:"le дпя об1>еJ<та I)E(taSet
Новое GВОЙСiвО .NET 2.0. позволяющее указать, каК должНа 81'i1пш1-
~rЯ1ЪСЯ Сериа.лиза,ция D.atakt (,ВДВQИЧНОМ .f1ли )(МL-формате) для
!Злqя удаленного взаИМQдеи(;твYlЯ .NEl

Методы BataSet воспрщщводят ~eI>OТ()pыe ф)'.fUЩИОНЩl'ЫJые возможности, обе­


спе-.umaемые 13ьпnеУЩilМЯВУГЫМИ свойствами. В доuрлнещ1е. и вааимодействию
с ООТ'Q}{ами XN[L. объект DataSet ~Iрещrагает :ме"I'оДЫ, ПО3ВОЩIющие нопировать/
КJIШ:t1'Iровать содержимое Dat a5",t, а 'ХЩQI<е у:~танавлива.ть наЧaJlliные :и R.QнеЧНЫе
точки пакетных обновлеЕИЙ. В табл. 22.10 ДЩОТСН описания ИСWDторпm И3 таких
метОДОВ.
972 Часть IV. Программирование с помощью библиотек .NET

Таблица 22.10. Методы DataSet

Методы Описа"ие
Accept.Changes () Фиксирует все изменения, сделанные в данном объекте DataSet С
момента его загрузки или последнего выЗова AcceptChanges ()
Clear() Выполняет полную ОLIИСТКУ данных DataSet путем удаления всех
строк в каждом объекте Dat.aTable
Clone () Клонирует структуру DataSet, включая все объекты DataTable,
а также все отношения и ограНИLlения

Сору() Копирует и структуру, и данные для имеющегося объекта DataSet


GеtСhалgеs () Возвращает копию DataSet, содержащую все изменения, сде­
ланные со времени последней загрузки или последнего вызова
AcceptChanges ( )
Get.ChildRelations() Возвращает коллекцию дочерних связей для указанной таблицы

GetParentRelat.ions() Возвращает коллекцию родительских связей для указанной таблицы

HasChanges () Перегруженный метод, который воэвращает значение, являющееся


индикатором наличия модификаций у DataSet, учитывая новые,
удаленные или измененные строки

Merge () Перегруженный метод, который выполняет слияние данного объекта


DataSet С указанным объектом DataSet
ReadXml() Позволяют считывать ХМL-данные иэ действительного потока (фай­
ReadXmlSchema () лового, размещенного в памяти или сетевого) в DataSet
Rej ectChanges () Выполняет откат всех изменений, сделанных в DataSet. С момента
его создания или послеЩlего вызова DataSet. AcceptChanges ()
WriteXml() Позволяют записать содержимое DataSet в действительный поток
WriteXmlSchema ()

Теперь вы лучше понимаете роль DataSet (и имеете неIЮторое представление о


том. что можно делать с этим объектом), и мы можем приступить к созданию но­
вого консольного приложения под названием SimpleDataSet. В его методе Main ()
определяется новый объект DataSet, содержащий два расширенных свойства,
представляющи:х название вашей компании и штамп времени (не забудьте указать
using дли system.Data).
class Program
{
static void Main(string[J args)
{
Console.WriteLine("***** Забавы с Dat.aSet *****\п");

/ / Создание объех~а DataSet.


DataSet carslnventoryDS = new DataSet("Inventory из Car");
carslnventoryDS .ExtendedProperties ["TimeStamp" J
DateTime.Now;
carslnventoryDS.ExtendedProperties["Company"J
"Int.ertech Training";
[лава 22. Доступ к базам даfiНь/Х с nОМОЩb'iQ АDО,Nп97З

Объект D,зtа$еt без DбъеКТDВ DataT'a ble чем~то рanоМ'инает рабочую н:еде.m{l
без ~ьIXОДНЫХ. Паэтому слеД}''10щеи нашей эадачей будет pa(;cмoтpeНl,e ВFiYтр~nНей
стр}"Кi!ypЫ Dat aTable., начи:наи с типа DataColumn.

Работа с DataCo,lumn
Тип Оа t а Со l u :ro 11 nP~ДС'rа1lляет отдельн:ый столбец 11 пределах Data;Tab.le.
Вообще говоря. Набор всех ТИllQВ "I)ataColaw:n в границах дaннnгo типа DataTa ble
представляет собой струкгуру т.абn:ицы. HaдpfI,Мep. чтобы преДстави.тЬ таБЛИЦ,V
1nventory базы данных Сагз . .вы ДОД.ЩRPI ~@Здать, четыретиnэ IJa taC'oll Шllд. по ОДНО-­
М)' для GaЖдого столбца этой таблицы (CarlD. Mak~. Соl0r- и PetN"i3.IiIE). После созда­
ния 6бъеI{Т()В Ii),a ti:iCoI L1l'!\fI они добавл:я:ютсSJ в коллекцию столбцов типа Dat.aTable
(спомощыо с:войства Co lumps).
ИМея определенну'JP подготовку в области Т~ИИ реляциQнных баз дaI-шых.. вы
ДОЛЖНЫ знать. Ч1'О €толбцу 11 таблице дзl;пIых МоЖНО назначить набор orpaн:ичеIOtЙ
(например. иcnОлъ:.юватъ L'1'олбец 'в Щl.честве первичного , ЮJЮча. задать звачение
ло умолчаnиro . потребовать. чтобы информацнл в столбце была доступна TOJIЬКO
ДЛЯ чтений и т. д.). Также K~ столбец в табmще Д~J1Жеи соотвe-rствовать за­
данному Д.JIЯ иеrо типу даннЬРС. Например. струнтура таблицы Inventory требует.
чтобызначеНйя столБUа саrID ' Былиедыми числами. а 'ЗНачения Маке. Colo.1" й
PetN'ame - наборами симвоJ.IРВ., Класс Dаtаl,JQl шnn имеет множество евоЙетв. ко­
торые ПОЗВОЛЯ'ют ВЫПOJUmть <:оотве~ствущщие настройки . Оhисания осповu.ьtx
своЙcr.в 9roго типа прx:mеде:ньJ F.I табл. 22.1 J.

Таблица 22,11. СвоиОтва Da,t acolumn


Ово'Аства 'Описан~е

Allio-wDВNП.ll Индикатор' того, LITO стр.ока в ЭТОМ С;ТDлоЦе (IЛ(}же;т ОOiO,ержа,ть Зliа'lБ!'iие
null. ЗначеtlИВМ ПQVМD:l'lat1ию' является true

Au"tclncrement Используются для настройки автоприраш6.Н\IIЯ' МЯ ДЗННGГО CТOll(jtJ/.a.


Лutоlпс:r-e.mеrLt s:'ее(] rorдa НУЖИQ гараНТИРовiпь УНlIIкалЫIDСТЬ зизчвнs-1и в AafiHQM объекте
А'Jtо l псrвщеnt s tеjJ DataCol 1J..Тrtrl (например. ДJ\Я IlВР~ИЧНОП) ключ3.) _ По умоЛЧ'внИJ!) 11
Data ColUl!\I1 Э6топрi-tpащеIiИ/,: не выполняется

Capt lOn Llиraет ИЛИ' устанавливает текст заголовка\ который должен QТo6pa­
жаТЬGRДЛЯ Aa~IHorQ столбца (наnрймв'р, текст. I(()ТQР~IЙ увидит КО~lеч­
ный" полЬЗ'авателЬ. в DataGrid'Ji e w)
COlu:m1".мappin g Определяет представление Da t.aC olufl1n I!1РИ сохранеliИИ
p at aSet
в sli1AeXML-Аокумеtiта с помоЩью мето,о.а DataSet .W.-.i·tеХml О
Со lа!i\R Nаш е Читает или, уtтвflaВЛИваSoт имя столбца в j(Оfl~IЩИИ Colblmr'ls (Т, 6 . -его
преДGТ<i!~nен'Ж! -в Dat. aTabJ.e). Еоли не ус;rановить 'Соl.UIIIпNam е Я8НО,
знаqеl'lием ПО умолчанию будет CollolffiD с номером c:rолбца
(Т.е. C61umnl, Сdlщаn2. соlumnз и Т,Д.)

Da ta1'ype Опре,ДеЛЯет тип Д8Ж'1ЫХ, "'ранимых 11 столбце' (лornческий" СТРОКОIibIЙ.


ЧИС""IЮВОЙ С плавающей ТQ'IKOii1 и т.Д.)

[16 .fau HVi'11ue ЧИ'тает ИЛи усrаf.tаВЛИ1!rIе-т ЗНQiеl'Ме, "OTO'P~ ДОлЖНО лрИriисываТI;ЮЯ
по УМО:Л'18НИЮ .n,.lЯ щ\ННDГО столбца ПРИ вставке HOB~)( crpOIl".
Это эwаЧ€l1ие используется TOr,na. lФiДЗ' не УКi.'lЭЩ10 ИЩJе
974 Часть IV. Программирование с помощью библиотек .NET

Окончание табл . 22. 1j


Свойства Описание

Expre ssion Читает И"ли устанавливает выражение, используемое для фильтрации


страк , вычисления значений сталбца или саздаНИI1 агрегированных
сталбцав

Or'd i na 1 Вазвращает числавую пазицию сталбца в каллекции Co1umns, под­


держиваемай абъектам DataTabl e
ReadO:nly Индикатор запрета изменеНИI1 садержимого столбца пасле дабавления
страки в таблицу. Значением па умолчанию является fa 1se
Та Ые Возвращает объект Data Table , садержащий данный абъект
Da t a Co 1umn
Un iq ue Индикатар требавания уникальнасти значений в даннам столбце . Если
сталбцу назначается аграничение пеРВИ4нога ключа, свай{;тву !Jnique
далжно быть назначена значение t rll e

Создание DataColumn
Чтобы продолжить рабату с проектом SimpleDataS e t (и привести пример ис­
пользования DataCo l umn ), пр едполажим. чтО' нам нужна представить столбцы
таблицы Inventory. Учитывая та. что столбец Са rI D явля ется первичным ЮDочам
таблицы. мы сделаем абъект DataCo1umn доступным толы оо для чтения, с ограни­
чением уцикальнасти и не ДОПУСI<ающим ввада зна чений пи 1 1 (испальзуя свайства
Re a dOnly, Un ique и Al l oWDBNu ll ). Обнавите метад Mai n () так. чтобы постраить
четыре объекта DataColurnn .

s t atic v oid Main( string [] args )

/ / Соэдание объех'1'ОВ Da taColumn, О'1'обража..оцих 'реалЬНldе'


/ / С'I.'олБЦJo1 таблицы Inventory из базы данных Cars.
Da t aColumn carI OCo l umn = new Da ta Col umn ( " CarID", typeo f ( iлt)) ;
ca r IDCo l umn . Cap tion = " Номер";
ca r I DCo 1 umn . ReadOn1 y = tru e;
ca rI DCo1umn. AllawDBNu ll = fa1se;
carIDCo l umn.Onique = true ;

D аtа Соlumл ca rM ake Co l umn = new DаtаС оl urtш (" Make " I typ eof (s t ri ng ) );
DataColl1mn car Col o r Co 1 umn = new DаtаС о l шnп ( " Color ",
typeof(str i n g )) ;
Dа t а Соlumл c a r Pe tNameColumn = new Da taCo 1'l1mn (" Pe tN ame",
t ypeof(s t r ing ));
carPetN ame Co,Lumn . Capt i on = "Н а зван ие ";
Глава 22. До[;туп К базам дз~Iныx G ГIОМОЩЫО APO.N~ 975

Р'азреwение аВТОП'РиращеНИI ДЛИ полей


ОДflDЙ из :ВО'ЗМО~(Jстеi1 Dаt, а{;'оlюnm,. -КОТОРая может:вам пона.цоби'I'ЪСЯ. будет
.вОЭ.МСУJКносrь авmoпр.uращенuя.. АвтоприращеНИf: используются для тоro. \lтобы
при добавJIеI;UUfJ;IО~ строки столбцу aIiтоыатически nPЯСЙЗ.Ивалосъ значе-нпе. вы­
численное на основе заданного' прирвще-ния. Это мо.жет OIЩзаТЬСЯ: полезным тогда.
когда В;Ы :-;:О1':ите, что!5ы{"годбец не имел повтоpmo"щихсЯ 3М~lений (например. RЮ{
перв-ичный !ЦIЮч).
Соот:веТСТ:ВУКJщи."hi Ц(i)ведснием можно управлять с ПОМО1ПЪЮ св.оi1стй
AT~ 1:. oi (u:хе т' е l'lt, 11.1) t'O 1 n-c r .ew..e [1 t Бее d и Ai1 to 1 I1C r e1lleJ1 t St.ер.3начени.е
АiltрIрсrеm~лt$ееd исuользуетсл ДJLН начаЛЫ-ЮГQ значеmш столбца, а зна~tение
AutoI:nc;rement.s;tep ,зад~ет ''ПЮIIO, lЮТорое сле,п;ye-:t добавить к Auto"lDC':remel)tSeed,
~ш::да Вр]п!).lЩяется лриращедие. Р;а1::смотрите шrедующую модифИВ\ацйЮ IФI-ICТРУК­
и.ии ОбъеI;:та c:ar1 DСОll;jПlЛ' ТИЩ!. Оа t аGo11l1J1Л.

$ .t 'a t J6 yo.id ,Маiл.(s. t :r iлg[) a .rgs)

Q'd tаС.оIU,!1Ю = "е,\> Dаt ёlt'iJl 1..!mл


.carIDC61umn ("CarJ п " / ,t ypeof \ iflt.));
сar:I DС,оl uюn . EeadOnly = tr-1.1e;
баrlDСоlшrrr'J_. С арtiо.л, = l' MDM€:P " ;
саУlОС<оl OOlТ'_ .Аl10W[JВЩull = fэ l:;;Е" ;
c arl ОСоl шtltl • ОГО q1J',," = t:r\J", i
сз (] DGoll.IlГ!J} .A\1toI-пc.reme1'lt = t:. T!J€ ;
с;;;,rJССоlьh1.П . АцtQ:Inсз:;-enеntSееd -=; О;
ca .tlDCo] ШJlл.Арtе!nc.rementstep = 1;

Эдесь объе-I{Т с ar1 D Со 1 U:Itl n св:о.нфиrури:ровщl'1 тац. чтобы rJ ри ДQб8.l'1лении


СТрОЕ в СОО'rВf":FС,ВyIOШУЮ таблицу 31Illчение . даннО'гО' столбца ув~ичивалось Ба 1.
НачaiIьным знаЧением является О. поэтому для стопбца будут выllJираться 'IИruffi О.
1.2. 3 и •. д.

Добавлеиие DataColumn. в DataTabIe


тип Л а t..aCol;Qmn обычна не сyrщест.вует Юn'(jIНQМIЮ. D. добавлястс~ в COO']·Bf'Т<..'ТВY­
юЩIiIЙ объект Dаtатаыl •. дlш uримера СОЗ;ЩЙТ(' НОВыЙ ТИJ;I Data"Table (ТЮ:ЦР6б:но­
ств будут предложе.ны чуть позже) и' всТ".шьте объекты. Da i~OIJCol eJ т 'л В' NОfiЛeIЩИЮ
СТОllбцов . испольэуя свойство Соl ,tПl1nS,

sta.tic Vuid Na..lо'(зttiГigIJ a:rgsJ


.~

11 До6'а-.лeщtе Dаt&Соl'\.Ш1n в Da"t.a'1!ab1e.


Da taTablE' i.дvгrit'оrуТаblе = 11ew Dаtа1'э.Ь.l е ("lrrvef1.t ory") ;
in",ец ,t, огуТаbl е. Colum(' $ . Аd'j[Щапrgе {I'Jew Da taCol ит-n (1
I са: r lDс ф lumf1/ са rМа.kе С О lumв,са LСаlо.r:С.с l'JЛiН. t:a.:rРеtNаще(;Ь 1Щ11.JI) J);
976 Часть IV. Программирование с помощью библиотек .NET

Работа с DataRow
Вы видели. что коллекция объектов Data Co lumn представляет структуру
DataTabl e . Коллекция типов DataRo w npедставляет факгические данные таблицы .
Поэтому если у вас в таблице Iпveпtшу базы данных Cars содержится 20 записей,
вы можете представить эти записи с помощью 20 типов DataRow. Используя чле­
НЪ! класса DataRow, можно вставлять, удалять оценивать и перемещать эначения

таблицы. Описания некоторых (но не всех) членов типа DataRow предлагаются в


табл . 22. l2.

Таблица 22.12. Основные члены типа DataRow

ЧЛ8.НЫ описание

HasErrors Свойство HasErro rs возвращает булево значение, являющееся


GetCol umnslnError() индикатором налИчия ошибок. В этом случае можно использовать
GetC o lumnError () метод GetColumnslnError (), чтобы получить информацию о
ClearErrors () члеli8Х , по рождающих проблемы, метод GetColumnError(),
RowError чтобы получить описание ошибки, и метод ClearErrors () , уда­
ляющий ошибки для данной строки . Свойство RowError позволяет
задать текстовое описание ошибки для данной строки

ItemArray Свойство, возвращающее или устанавливающее значения для дан­


ной строки с помощью массива объектов

RowState Свойство , используемое для выяснения текущего · состояния"


DataRow с помощью значений из перечня Ro wState
Table Свойство , используемое для получения ссылки на DataTable, со­
держащий данный объект Da t aRow
AcceptChanges () Эти методы, соответственно, фиксируют или отвергают все изме­
RejectChanges () неНJIIЯ, сделанные в данной строке с момента последнего вызова
AcceptChanges ()
BeginEdi t (} Эти методы, соответственно, начинают, завершают или отменяют
EndEdit () операции редвктирования для объекта DataRow .
CancelEdit ()
Dele t e () Метод, помечающий данную строку ДЛЯ удаления при вызове мето­
да AcceptChanges ()
IsNull () Метод, возвращающий значение-индикатор того, что данный стол­
бец содержит значение null

Работа с DataRow немного отличается от работы с Data Column. поскольку вы не


можете создать напрямую экземпляр этого типа, а получаете ссылку от данного

DataTable. Предположим, например. что нам нужно ВСТавить две строки в табли­
цуlnventory. Метод Da taTable. NewRow () позволяет ДОбавить очередную строку в
таблицу. а затем вы можете добавить в каждый столбец новые данные с помощью
индексатора типа, как показано ниже.

stati c void Main (st r ing[] args)

11 Добавление строх • табnицу Inventory.


Глава 22.. ДОС'rуп к базам даЮIЫ~ с ПОМОЩЬЮ ADO.NEТ 977
Dat.Q-.l\\о,w caLRow = lnvепtоrуТаРlе. Ъ1еwRоw О ;
C1'LrRGJW [ "Маке " 1 ~ " ':вмw~ i
carFlj!~w J "'Со ] or " ] = "черуli!Й";
"}iaml~ ,t ":
fl
car1<.o·w [ "P€'t.Name ) =
lй'vеn t(>r~''J'able. Rpws .Add {carRc.w} ;
с·<э.rl=i.QW = inv8ntoryTaD'le . [qewHow ();
ЦН:R'Owl"'Маk.е"j = ".saab" ;
carRow,["Colo.r"] = "J!.расН&IЙ"i
ca r.Row[\'Pe'tNa.'lle = "Sea Bre~ze";
l']

invent·ory1·dble .. Rows • Add (carRow) i

Обратите I3нимание на то. что Юlaос DataRo.. Qпределяет ИНДен'Сатор. ;КОТОР:Ы:Й


МОйtет йспользоваться ДЛ.R полученил ДОС1)'Па R данному объекту Data:Col'uffi!1 1Щft
тro ЧИ~JI()1J,Оl'lI)' iщде:ксу пОзИЦИи, 'ГаЕ Ji по ИМ.е.IjИ СТШJбца. Н результате 8ЫПО.1Пiенип
указанных стран @'OгpaммНoro кода вы ПOJIyЧИте ОДИН объект Dat~Table. содер·
жащий Д}3а ('толбца.

Свойство DataRow.RowState
СвойетвоFcwSt:ate QI<a3blВается полезным тогда, KOrдa необходимо прогрщ.rмно
идентифацировать на.бор всех 'Строк 'в таб.лицt:'. которая • .шшрllМ~р., была изм:еIiе­
на. ТОЛЬКО чТо Gоздан.а ит.Д. Это СВойство 1Vfо.жет прИRИ.Мa'I'Ь любое знэ.qеЮifе из
перечнн DataRowState. Описания этих аначений цреДJIагаются: в табл. 22.13.

Таблица 22.1 З. Эна4еlтlY1я пере"4НЯ bd.ta.RowSt~te

3наченке Оnмсание

Added Строка была ДQб!iВлена:в DataRo,wCQll~ction. но метод АссерtСhangеs'('j


не выl3Ы8ЭJl!>Я

!)е Leted Стрtжа была удалена с ПQМОЦlЬЮ метода Delete О om.e'qa D<!t.af<.o:w
!Jetacl1e<i СтроКЕ! -была создана, но неЯБляется 'IВetью коллекции Da tаЯОWСQ11 ect.iop,
Qfi'i:>6trr ba:taR.o'", находится в· этом С0СТIЖ!fИIII после своего соЗ'дан\l!11 ДО ТОГО .
кэк будет доБС!ВЛ8Н " коллек!:),ИИ (илk1 же П(J)Щlе уд;ItЛения этого объекта 143 ко.п,
лекции)
Modifie,d Строка была !Азмеkена. 1-10 метод AcceptC,haflges (:) не ВЫЗЫ8алt~
t1neh.ang,ed Стока не измеНялаCJ> со вреМеНи n-bCJIeДHeгo вызова: AcceptC!'mnge-s ()

Во Щ)емя ПР(JГР~.маНИПуЛЯЦ:ИЙ CTPOкa~ объе'Кта DataTable CilОЙСТВО


RowSt.a te устaromЛИJЗаетсn юrr6матиqески.
gtati c void МtIi л (str1i-.g[) arge )
{

DataRow .carRow = i Qv.e ntory]able. NewRow () ;


11 iWaО.ци'1' 'С6С'l"ОJQIИ8 c!t'pOlUf: De1:atched. r
Сопзоlе. И ;!;. i teLiTle ("с.ОСТQ!'l1'lИ'еСТРDЮ1: {а).", c.aJ:;Row •.Ro"JSta teJ;
c-аrRЬw [ "Маке" \ J => "'BNW";
,саrR{)w[ "с фl0r "] : " черный ";
ci'!r:R:ow l ".PE:tName"] '" "Rar[йеt" i
in v~n: t0ryTable . :RQWS . Add (c<1l'Ro.w) ;
978 Чассть IV. Программирование с помощью библиотек .NET

/ / В:ыsо,цит 'СОС!1'ояние С'1'роки: t-dded , '


Con s o le . Wri te Line (" Сос т оя ние с т роки : {О } . ",
i nven t o r yTabl e. Ro ws [ O] . RowS tate ) ;

Как виДИ'Ге , Dat a Row в ADO.NET является достаточно "сообразительным " для
того. чтобы контролировать текущее ПОЛОЖение вещей. Поэтому , имея DataTabl e ,
вы можете выяснить, какие строки были изменены. Эта особенность Data Set очень
важна, поскольку именно она при отправке обновленной информации в хранили­
ще данных позволяет отправлять только измененные данные .

Работа с DataTabIe
ТИпData T a ble определяет большое количество членов, многие из которых по
именам и возможностям идентичны членам Dat a Set. В табл. 22.14 предлагаются
описания основных свойств тшra Dat aTable, за исключением Ro ws и Co l umns .

Таблица 22.14. Основные свойства типа Dat aTable


Свойство Описание

С а s e Se nsi t ive Индикатор необходимости учета регистра символов при сравнен и и CTPOk
в пределах таблицы. Значением по умолчанию является fa l se (ложь)

Ch i l dRe lat i ons Возвращае т коллекцию дочерних отношений ДЛЯ данного объекта
DataTable (если таковые имеются)

Cons tra i nts Возвращает коллекцию ограничений, поддерживаемых табпицей

DataS et Возвращает объект Da t a Set, содержащий данную таблицу (если таковой


имеется)

De f au ltVi e w Возвращает пользовательское представление таблицы , которое может


включать фильтр или позицию курсора

Mi ni mum Capacity Читает или устанавливает значение ДЛЯ начального числа стро к данной
таблицы (это знвчение по умолчанию равно 25)
Pa r e ntRe lations Возвращает коллекцию родительс к их отношений ДЛЯ данного объекта
Dat aTable
Prima r yKey Читает или устанавливает массив столбцов , функционирующих в качестве
первичных ключей для таблицы данных

Re moti ng F'ormat Позволяет определить , как объект Data Se t должен выполнять С'ериализа·
цию соответствующего содержимого (в двоичном или ХМL-формате) ДЛЯ
слоя удаленного взаимодействия . NEТ. Это свойство появило с ь в . NEТ 2.0

ТаЫ е Ы ат е Читает или устанавливает имя таблицы. Это же свойство может быть ука­
зано в качестве параметра конструктора

Б нашем примере мы установим свойство Pr ima r y Key типа DataTa ble равным
объекту c ar I DCo lumn типа Dat a Co l u mn .
s ta tic void Ma in(st ri ng [ ] a rg s )
I

// УС!1'ановка первично~о ключа для таблицы.


inve nt o ryTable .PrimaryKey =
ne w Da t a Co lumn [] { iиvе п tо r у Та blе . Со lumns [ О ] };
Глава 22. ДOOТVl1 к баз'ам Aill'!HblX с ПОМОЩЬЮ ADG,NET 919
На Э'Гом созданйе примера ДдЯ 'Dаtат.аыlзсщерща~тся.. 3акпК)'tnггеl1ЪВЫМ ша­
гОм будет вставка DataTable в D.at.aSet-объе.кт саrsIn!18ntог}ФS'. Затем объект
Da ta.Set нужно -передать :ВСЦОМО1'атедълому методУ Рз; intData:Set () (который еще
npeц~тоит написать).

Hatk '"oid Mail'\ (·s t.r ing- l'J -з: rg5)


{

1/ Н_онец, добавление 'J:'Jlбmщ:i1 в D.ataSe.t .


carSlm:eoto ;ry т:JS. T.ables. Add (iпvепtо.rуТаblе! ;
I/ ~enl!q)> вывод даННI!IХ Da'taSet .
Pr:ir,tDa.ta5et ( с аr s InV\9 0tоз;уDS);

Метод "'r'i,n.t DataSet !) просто вьтодняет ЦJЦtII во воем Da,t aTabl.e из Da.taSe,t.
печат'"clЯ им.еыа ётолБЦОБ и значения строк с помощr;.ю 'инденсатора типа.

,static void PJ;'intDataS i?t (Dar,dSet ,ds)


{
'Cb l'1$(,l е. Write Line r "Табi1DЩ@ в Dat.aSet '1 О} , . \ ri", tЗэ. !:;Ja:ta5etNaтne) ;
fb):each (DаtаТд'Ыеdt iJ;] ds . Table5!
{
сопsоlе. W,r itеl,iпе ("ТaQлиl1а {О}. \п", dt. Tabl.el'lame) ;
/I ВJoIЗО;Ц 'ииеи С'Z'оnБЦDВ.
fo!' (, 1лJ: clIrCa l = О; c u.rCc.l <: dt.соl 'l1mnS.С'Ь\I:Пt., сиl" Са1++)
(
r
Ca:nso.le. i~1:1 ice (dt • Соl Umл,,s сцrСоl J • (:Q11.1thI1.Nате • TriJn О + '1\ t ") ;

:Соns,olе '. Wri te L·:h ae -СУ' \ п--- - - - ---~ ---- - .---- ------- ---- -" ) ;
11 !Шво;ц: DataTable .•
f o.r (i.пt curRClw = О; СЦ'rRоw < dt.Rоw,S.СОlшt; eurRQw+-+)
i
for (iлt ситСоl = О; c1;J:t;Col < dt.Со1UJIшs.СОtJпt; CLlTColH)
i
COrisQle.Write (r,;lt.Fc;).w-s [curRowJ [cu.rCG1] .ToString() ... "\t");
]
С o.rj $01 е ..Writ.eLl n.e (.) :
}

выI:Jдд арограммы. щжазанНа рис, 22.12.

Рис. 22.12. СО$ржимое объекта Dat,aSet примера


980 Часть IV, Программирование с помощью библиотек .NET

Работа с DataTabIeReader в .NET 2.0


ТИп DataTable предлагает еще целый ряд методов, кроме тех, что уже были
нами рассмотрены. Подобно DataSet, тип DataTable поддерживает. например. ме­
тоды AcceptChanges (). GetChanges (), Сору() и ReadXml () /WriteXml (). в .NEТ2.0
тип DataTable поддерживают также метод CreateDat.aReader(). Этот метод по­
зволяет получить Дa1'lНыe DataTable, используя схему, соответствующую схеме на­
вигации объекта чтения данных (только вперед и только для чтения). для примера
создайте новую вспомогательную функцию PrintTable (). реализованную следую­
щим образом.

private static void PrintTable(DataTable dt)


(
Cons o le.WriteLine("\n***** С троки в DataTable **** * ");
// Пonучеиие HO.O~O ДnИ .НЕТ 2.0 типа DataTaыlReader •.
DataTableRe.ader dtReader = d t. Creat eDataReader () ;
/ / DataTaыlReaderr работает подобно DataReader.
while (dtReader.Read(»
(
for (int i = О; i < dtReader.FieldCount; i++)

Console.Write("tO} = (l) ",


dtRea der.GetName(i),
dtRe ader.GetValue(i) .ToS t ring() .Trirn(});

Console.WriteLine();

dtRe a der.Clo s e();

Обратите внимание на то, чтоDataTableRe a der работает аналогично объекту


чтения данных поставщика данных. Использование DataTableReader может ока­
заться идеальным вариантом. когда нужно быстро прочитать данные DataTable
без просмотра внутренних коллекций строк и столбцов. Для вызова метода нужно
просто указать соответствующую таБЛШI.Y.

static vo id Main(string[] args)

/I Печатъ DataTaыle с ПОNOЩW) 'об'Ъеlltта чтении таб.mщ I •

P rintTable(carSInventoryDS.Tables["Inventory"]);

Сохранение DataSet (и DataTabIe) в формате XML


в завершение рассмотрения текущего примера напомним, что как DataSet,
так и DataTable предлагают поддержку методов WriteXml () и ReadXml (). Метод
Wri te Xml () позволяет сохранить содержимое объекта в локальном файле (или во­
обще в любом типе System. I O. Stream) в виде ХМL-документа. Метод ReadXm l ()
,
rJfa8a 22. Доступ' k базам .дaflHbJX с помощью ADO"NE1 981
ПО3ВОJШе7 праqитать И,нфрр:мацию о состоянии Da.taSet tшщ DataTaыl)) из имею­
щегОС'Я XML-Дf,)RyМ'е-tIта, ltpoMe ТОГО, шш DataS.et. так и D~tаТаЫе.110ддершmщют
WritеХIiйSсhеmа() и Re;аdXщlSсhem'а(J ДЛЯ сохранe:юm и загРУЗЮI файлов "'. xS d,
Чтобы зто npaB('pwrъ, добавьте в метод Main () dЛeДУЮlЦЛЙ .набор oд~aтopnв.

sta'tic vo.fd Мadn ,str.iщj[J args)


!

// СОХРАнение DataSet в ~i(Дe XМL _.


caT~ InvetJi~oryDS. :Wr i tеХшl (".c a,r8DataSet. =1") ,;
carsln,\;ento r :y OS,.WIi teXJH1S,cherna '{ " ca:t'5Da't.S:$<.;:t. ~;;O") ;
11 ОчисU'Xil Оа taSet 11 1IblaОД t;:,Q:цер~оI'O (,цОJIJIUIО бin'Ъ ЩС'1'Шi) •
ca.ts 'InventoryDS . С lе-ifй () ;
PrintDi'l-I;-"S,?t_ (carsIDvel'lt.oryDSJ ;'

1/ ,ЗarpузJtаи Iiе-чаwь DataSet.


carslrlventoryDS . ReadYJOl (" саrэ-D.аt:а8еt. ыfil") ;
PrintDa: t .. Set( <:-::iЗLзlr,vеll t'oryDS,} ;

Если oTRpыьь caxpaн~ файл car5DataSet .xml, вы увцците. ЧТО J3 нем пред­
атЭВдеНы все столбцы');аб;mц-ыI. -заоодцрrщанные в виде XМL-элементов,

<?xтnl versioh="l. О" ~iLancliЙQt1е=''уеs''?>


<C;a.r_х'ОО20 _1 DV'ento1;y>
<rnveIltory >
<C;arI,D>O< /C arID>
<Mв.ike>,вМW<!M a Ke>
<Со.1 Qr>черН ....IЙ<! С'о 11)I;-
<РеtNamе>Нamlеt. </ РеtNаше>
</1пvеп tOl"Y>
<In",;?ТI . tQry>
<C'a rID>l< !Ca:t:ID>
<1'1ake>'Saab</Make>
<СОlоr>К;:>Ctсный<fСоlоr>
<Ре !:.Nаше >5 е,а Breeze</PetNarne>
<!lnvent ory::,
<с/Сат _ х()О2'О _ Тл:vеntоrу>

Накодt';Ц, напомним. ЧТо тИП .DataColumI.l поддер~юmает СВQЙСТВ[) Col umnMapping.


которое мржно ИСIIOЛЪЗ0вать ДЛЯ ynр~ле-ния. преД~'<t~ением столбца в XМL-<фор"
мате. 3н;а"lением. устанавливае:м:цrм для этого CВO~CT.вa по )'Молчанию. ямяетс'я
MappingType .EJ.еm.eпt. Одиако :МОЖНо потребовать, ЧТDбы столбецСа:r-ID ПреДстав­
ЛЯJlCJl XМL-атр~бутом. КtШ 31:'0 сделано ниже' n обновленной версии объ~'Та c:arl0-
Соl l1 Пlд АЛЯ DataColumrl,

$-t -atic void Иаin(S.tri.r).g[j arg,5)

Dat..aColuffiIl с;аз:;-IDСеlUffiJJ = Q,ew DataColumn ("CarIO" j typeof (irlt) );

сзrIDСоl.Uffifi.СQlumnмаррing = !>'Jappir,g'l'ype .At~trib.ut€;

J
982 Часть IV. Программирование с помощью библиотек .NET

Тогда вы обнаружите следующий ХМL-код.

< ?xml v ersio o=" l . O" s t an·d alone=" yes " ?>
<Car_x0020 Inven~ o IY>
<Inventory CarID="O" >
<Make > BМW </ Make>
<Со l о r > че р ньri1 </ Соlоr >
<PetNa me>Haml et <!PetNa me>
</ Ir1ventory>
<l n ve n tor y CarID="l " >
< Маке >Ба аЬ</Ма ке >
<Colo r >KpacHbm<!Co loI>
<PetN ame>Sea Bre ez e</Pe t Name >
</Inven t o ry>
<!С ат хОО20 Inven to ry >

Исходный код. Проект SimpleDataSet размещен в подкаталоге, соответствующем главе 22.

Привязка DataTabIe к интерфейсу пользователя


Теперь. когда мы обсудили процесс взаимодействия с DataSets в общем. давай­
те рассмотрим соответствующий при:мер приложения Windows Fопns. Нашей це­
лью является построение формы. отображающей содержимое DataTable в рамках
элемента управления DataGr i dView. На рис. 22.13 показано окно исходного поль­
зовательского интерфейса проеIпа.

~ Та6n'Щd /.\дШ~Н r:-lrElrg:1


ClfID М"",е

розоеый

I свет /10'з~еный

Рис. 22.13. Привязка DataTable к DataGridView

Замечание. Для представления реляционных баз данных в .NET 2.0 злемент управления
DataGridView считается наиболее "предпочтительным", однако остается доступным и уста­
ревший элемент управления .NEТ 1.Х DataGrid.

Создайте новое приложение Windows Fопns с именем CarDataTableViewer .


Добавьте в форму элемент управления DataGridView (назвав его carInventory-
GridView) и Label с подходящим описанием . Затем добавьте в проект новый
С#-класс (с именем Сат ). определив его так. как показано ниже.
.,
ГlТaBa 22, Доступ к базам Д8t.1liЫХ с ПОМОЩЬЮ ADQ . NEТ 9ВЗ

publi.c cl ass Car


{
I! Зде~ public испom.ауе'1'eJl ,ц.п,I nP0C'1'O'l'JoI.
p ,uhlj.c st.tirlg carPet.Name., carMa1t;,e, ,ea~Co lo');";

p:ublic С а !' (s·tr iпg ре.tNзme, s t ri ng 11'.ake, string col.or)


{
.carP",tName = pe't-Nam,e;
cilrColQr c.GloJ1';
=
carМake = malte,;

Теперь в рамках конструктора ФОРМЫ. заданного ПQ умолча:нию. наполните


члеJ,t-переменцую List;:<> J\.ШОЖе<СТВОМ В:ОВ1;1Х объектов Сат.

рu rйiс partial ,::1 а3'5 MainForm Sуst€Jt•• Windоws .. Fо 'гшs.F'огщ


(
I! ~аш: СПИСОК lIIа1llИИ.
pr: iv ate .1is t< Car> ауТn'еСатв = Щ!W Li.эt<Саг'>О ;

РnbНс Mainf:D.rm ()
t
1 ni t,i alizeCompOn€'rit '(); Сел't' еГТ0&сгеел() I

/I ЗаnолнеlЩQ оnщ:ка.
i1.r'IheC'a r'Si . ~i1,dd (пеw Са Сl " Ch.iJckyn, "BMW", "эел",'нЬ!Й"» i
,сП Т hеС а l:Э . .1'>\dd .( r!E'W Са r I ,\ Т iny", "Y ag,o'!, "ав,т.т") J ;
в r'J.'he Са r s . ~dfl (ое..., Са r 'С"", "Зеер" I "к о ри'ЧневртЙ"»;
a rl1heCaxs.Ad.d(Гi8W CarC"PaiE lnci)1ce1"', "('.вrа:vад", "РОз6ВЫТ)Г')!;
а;тТnеса т в. Add (new Ca .r ("Fred", "вм.w·', "светло-з.еленыИ") .;
a:cThe.cars .Add~new Саи: ("Buddha" ( "Bмw· l , "черrrЬ!Й") J ;
атТоеСа r:s . /l..d:dl riew Са!" ( "Ме 1 ", " р i.rebird rт, "крас:нъ.tй "')) ;
BTTh,eCars. АсИ (new Сет ("'S'ara'11" r "Со1 t ", "черный",) J :
}

как и 13 предыдущем прщмере SimpJ.eIJ~ta$et. в прШlОЖении CarIJat aTableViE;Wer


будет создан объеJcr DataTable. содержa.щ1W четыре объехта DataColumn ДllЯ цред­
ставлeRИя С'ГОлБЦОВ ''I:аБлицы щveпtоry б<щы дав:ны:х. Cars. ТОЧН() тцже DataTable
БУДe:I' содержать мноЖе.ство объектовD!1taRow Д.1'11'1 npедставлеltия crш:ска RВТoмo­
билcii. Но на ЭТО'Т раз мы заполним строки с пом.ощыQ. обобщенного члена-пере­
менной L ist<>.
Во-первых, добавим в :класс F'orro НОВЫЙ член-nepемениую ~ йМенем in'll'ento:r:y-
таыlдл.я типа D.ataT6ble. 3~TeM добавим RО:ВУЮ вспомогательную ФУНIЩИЮ
CreateDataTable (1 и вызовем лот метод:В конструкторе. заданном по умолчc:щиJQ.
Прorра..1"IМН!ЬШ код. необ:хюДЦМblЙ для добавлени.п: объе1t'I'OВ Dat.aColomnB' DаtаТ<Щlе.
идентичен программному коду :r;rредыдущеГfJ п~ра. поэтому здесь этот JЮД не
пр:иводитс.fI (полный набор необходимых операторов имеется В загружаемом файле
соответС'1:вyroщеГQ примераJ, Qбратите внимание на ,О. что эдесь для пOCTP9ClЦrn
множества сТрО}{ приходи::rся В1>ЩО,щmть цикл ПD вСем членам Lis t<>.

J
984 Часть IV. Программирование с помощью библиотек .NEТ

private void CreateDataTable()


{
/ / создание обr.ьeJC'1!ОВ DаtаСоlшnn и добааление их 11 DataTable.

// цmc.n по элемеиi1'ак списха дли соsд&КИJI crrpox.


foreach(Car с in arTheCars)
(
DataRow newRow = inventoryTable.NewRow();
newRow ["Make" J = с. carMake;
newRow["Color"J = c.carColor;
newRow ["PetName" J = с. carPetName;
inventoryTable.Rows.Add(newRow) ;
I
/ / CIIИ8ывание DataTable с carInventoryGridView.
carlnventoryGridView.DataSource = inventoryTable;

Обратите вниыание на то, что в последней строке прогрэммного кода метода


CreateDataTable () свойству DataSource присваивается значение inventory-
т а bl е. Установка этого свойства и является тем единственным шагом. который
необходимо выполнить для привяэки DataTable к объекry DataGridView. Вы. на­
верное. догадываетесь. что указанный Элемент графического интерфейса читает
внутренние коллекции строк и столбцов. Теперь ВЫ можете вьmолнитъ свое при­
ложение. чтобы увидеть представление DataTable в рамках элемента управления
DataGridView.

Программное удаление строк


Зададим себе вопрос: как удалить строку из DataTable? Одной ИЗ возможно­
стей является вызов метода Delete () объекта DataRow. представляющего строну.
которую требуется удалить. Просто укажите индекс (ИJШ объект DataRow). пред­
ставляющий нужную строку. Предположим, что вы изменили графический интер­
фейс пользователя так. как показано на рис. 22.14.

РИС.22.14. Удаление строк из DataTable


Г1tавз 22. ДосryrY к базам данны" с 110МОЩЬЮ ADO , NEТ 985
След.V1OЩая. программпая ЛОГlЩа обраб()Т1J'ЦКа еоБЫТИ1l ,С 1 i ck НОВQЙ кноп­
ки обеспечивает удаление укаЗанной С1'})0RИ из нвходящегосн .в r,raмяти объекта'
Dа.tаТдblе;.

11 У,цa.n~ние ухи_кой С~QXИ И5 D;ataRowCo1.1ection.


pri vate void btJ\RerrioveRo"J_C1ick (obj ect serH:le't, .Ev1=ntArgs е)
{
try
{
invелtG>rуТ~Ые. ROW5! (lnt. Раrэе (txt.Ro<W'l'GReroave • ТехЧ )] . Delete ();
inven t'O ryi'abl е . "cc:eptChanqe-s ! J I
}
ca'tch CE,xcept,i'on ех)

\
,Меssа:'gе-Вщt, Srюw (ех ,Меэsаgе) )

Метод Delete (). моЖет быrrь. лучше наа;ВЭ.тъ J:I:1arkedAsDeletable{), посколь­


ку строка на самом дме не будет удален:а до те;Х. пор. пава не будет вызван метод
DataTable ,AcceptChanges (). В.деЙствительноGТИ метод Delete () цросто устаж.ш­
тшает МИ строки фдar. который сообщает ,ОТ Ш4ени строци: ·я готФва уйти в 1re-
бытие По первому же npикаl'iУ моей табдицъх~. ТaI(же следует понимать, 'Что да,же
еOJ1И CТPOI(B была поме<IеF!а для удаления, DataTable ]\ЮЖ~Т o'rМ~Тb 'реаль,ное
удаление с помощью RejectChang,es (,). как повааано ниже.

11 По~Щtа сщро.ar ДmII уд,а.пeикR споcnеДYJDЩВJ1 0'1'11880. иar.uaиеВИЙ.


pxivate voict btnRemo;veRow_Click (obj'ect sелdеr( f)ven,tArgs -е' )
{
i1'lvento.ryTable .:Rows 1 (±..Dt. 'РаоСБ8 (txtRemove .1ext) ) J • Del.ete () ;
/I ДPynJl pa.clO!l.'a •

.inve.J'btoryTable,. Есе) e'.ctChanges {); /I ВосстаНОВJl!:iние знаЧ'6НИЯ Ro.wS1:ate.


1

Применение фильтров и сортировки,


Иногда нужно noЮl::Jать 1I0nмнomec.тBOД8НJfЫX Data1'able. 3ЩОЩIетворяющее H~­
которому набору критериев фильтрации. Например, как пок~аатъ только onреде­
ле1-mые маРI(И автомобилей из ра:эмещeuной в JiIамяти табщщы Invt'ntory? Метод
Select () Kilacca DataTable обеспечивает тarqrю возможность. Обновите трафи ­
чес,кий интерфейс пользователя еще ра.з. чтобы ПОЛЪ:;lователь мог ,задать стро­
ку, представл.нюI1IyЮ ту марцу авто;мобиля, J(оторущпо.цъэовате.ль желает вцдеть
(рис. 22.15).
Me1'oASelect () является перe.rружен:ным и поддерживает разл,ичную ~ЩI1~­
ку выбора. Посылаемый методу Selec:t() параметр может быть, нanp~ep. стро­
кой. содержащей t;:оответствующие условии.

J
986 Часть IV. Программирование G помощью библиотек .NET

1Удалиrь cтpoк~ 11] г--:=-=-='="]


[ Выбрать ~ j ~~-=~=-1

белый

)Jeep KOPl-IчнееЬIс;.

Caravan РО3061;tЖ

Рис. 22.15. Создание фильтра

Рассмотрите следУЮЩУЮ программную логину обрабОТЧИRа события С1 ic k но­


вой кнопки.

private void btnGetMakes_Click (object sender, EventArgs е)


(
/ / Построение фильтра на основе ПOJJЬ!!Iова'l'еЗIЬсхоrо ввода.
string fi1terStr = string.E'ormat("Make= '(О)' ",
txtMakeToGet.Text) ;
/ / Выбор всех строх, СОО'l'ве!1'С'ПiYJCIЦИX фильтру.
DataRow[] makes = inventoryTable.Select(filterstr);
// Вывод того, Ч'l'о получилось.
if(makes.Length == О)
MessageBox. Show ("Извините, машин нет ... ", "Ошибка выбора!");
else

string strMake = null;


for(int i = О; i < makes.Length; i++)
{
DataRow temp = makes [iJ ;
strMake += temp["PetName"] + "\п";

MessageBox. Show (strMake, txtMakeToGet. Text +- " type (в) :") ;

Здесь строится простой фильтр на основе значения, содержащегося в соответ­


ствующем TextBox. Если вы укажете BMW. то фильтр будет иметь вид Make = 'BMW'.
Отправив этот фильтр методу Select () . вы получите массив типов DataRow, пред­
ставляющих те строки. которые соответствуют данному фильтру (рис. 22.16).

Map"d: BMW rxl

ОК

Рис. 22.16. Отображение отфильтрованных данных


Глаilа 22. Дщпуп J( баз'!!м данIчыx ~ ЛОМQЩ~Ю ADQ,NEТ 987
К3}( видите. nporpaммrlM лопща ф~!L;n,тра ИМ~Т стаНДарТНЫЙ синтакепё SQi..
Чтобы провер.ить Э1:0. предположuм. что требуeтcsJ представить р~эультаты пр~д­
:ь;пущего вызова Select,O в алфавитном 1I0ря.дне. В т~рМ,РЩах SQL это означает со ­
ртировку ПО столбцу PetName, Поскол.ь~ Me'fOД Se1ect (,) нвлиется щ-регрушелным.
мы :можем у.ка3аТЬ .критерn:Й: сортировки тан, ltal\ цо.казамф ниже.

11 COp~'pOB!tano PetName.
шаКеБ = i.nvel1t:oTyTabl е . Select, (fi1 t<llStr, "'р.tNаш.е" ) ;

Чтобы ynИдезъ реаулыгаты в ниr:-хощпцем ПОР,IЩКе, ~Ь!зовите Select; (). как по­
казана ниже.

JI Ворраща.е':1' резУ~'J!а~ з порцхе У'БЪП5анив.


makes = iл~е!J'tоrу'I'аыl.sЕlесtt (filb.e rStr, "PetName DESC");.

Вuобще г.оворя. строка сортироl3Юl должна с.'0дсржатъ:ю.щ С'l'o.nбца. зв: I\OTOPьц.iI


следУет ~ASC· (обозначающее порядок по ~о.~раставию. что ~ляетея ЗЩIЧением,
принимае..\!ым по умолчанию] или "DESC" (обоэна'чающе~ ЛОРЦД(lК по убывaщno).
При необходимостИ можно уЕазатъ весRолыю столБЦОВ. разделив их заnвJыми.
иакomщ. следУет понnматъ. Ч'Т'о в строке фWIЬтра можно иcnользоватъ любое 'ЧИGJ]О
,Qперaци:ii отношения, Например. ~литребуется найти В,се автомобили с HOMepa~
МИ, боnЪШИМй 5, то вот кап Должна выглядеть щ::помщ:ательная фyRh"ЦИЯ, Щ)Т1:,>paJJ
, сделает это,

РУ} v.at.€: void Si;H)WC", :c:SWi thJ t;J.Le'9$Тhan р' 1""9 ()


(
11 :&;u,;од назЗёUIИЙ ИA1IIИН с Н!i)иерами, бо.цъ1ШOdPi 5.
DataR,ow [] properIDs;
st.rinq ,nelй'ilte.rStr = "1D > 5";
рУ'оретТ Оэ = i11v.Ей,tоrу'таыl-.. Sel ~c t_ (tJ.e,wFil terst.:r) ;
s~r~fig strIUs ; оцlli
fG Y (int i = О; i < prQperJ[),5.Lengtn; i+")
j
DataRow temp = !9 'юр€rIU's [.ij I
s t .rfDs += temp["PetName"l
,. n 1з jD " + t:ещр 1 "1[I"J ' "\Н";
}
МеS.!>аqеБо.){. 811O'W ($'trТСЦ:;, fl Н аэва 1Ф}t /ij r>13JIIИН с:: 1.О :> .5 ") I

06НQвлен",е строк
Еще одной ЩJерацией. RОТОРУЮ БЫ должны о.своитъ" явлв:ется. изменеиие значе.­
ни.й еущесТllУЮщей в Dаtаt:raJэlе строки. С ~п.оЙ' це:лыо можно, например; с.ндчала
с DО.\lllОIЦPЩ метода Sel(O'ct (} П()]I)"'ЩТЪ строку, еоо'ГВеТСТву1ОЩУЮ нмеющемуся кри­
терию фИJ.Iьтра. Имея соот;ветствующи:й объект 'Qa·t.aRow, вы м.ожю:е 'СОQтветсrnую­
щим образом его измеЩilТ,q, ПредцО"л()~м, ЧТО в форме есть RНопка (nrn Бuttоп}.
при щел<те. н.а которой ВЫIЮЛН.J'\.ется РОИСR ТeJЧ стр<т В объекте .DataTable. для Кб­
TOPЪU!; Мак,: равно BМW. Идентифицировав ~11И '3лементы. Iiы изменяете знаЧtнnrе
М а ke с EMW н.а' Со1 t.
988 Часть IV. Программирование о помощью библиотек .NEТ

11 Поисх С'1'рох дли редахтироваНИR с помOЩЪJD фИЛЬ'1'ра.


private void btnChangeBeemersToColts_Click(object sender, EventArgs е)
{
11 Проверха вмеНRемости пользователя.
if (DialogResult.Yes ==
MessageBox. Show ("Вы уверены? '? BMW намного лучше, чем Со1 t! ГJ,
"Подтвердите свой выбор!", MessageBoxButtons.YesNo))

11 ПОС'1'роение фИЛЬ'1'ра.
string filterStr = "Ma]<e='BMW''';
string strMake = nul1;

11 Поисх С'1'РОХ, соответств}'JIIЩИX хритериJIИ фильтра.


DataRow[] makes = inventoryTable.Select(filterstr);

11 Замена буиеров на холь'DI!


for (int i = О; i < makes.Length; i++)
{
DataRow temp = makes[i];
strMake += temp["Make"] "Co1t";
makes[i] = temp;

Класс DataRow преДJIarает методы BeginEdit (). EndEdit () и CancelEdi t (). ко­
торые позволяют редактировать содержимое строки. временно приостанавливая

все заданные условия проверки ввода. При использовании предложенной выше


программной логики строка будет проверяться с каждым новым присваиванием.
(И если вы задали обработку каких-то событийDataRow, они тоже будут генери­
роваться с каждой модификацией.) При вызове BeginEdit() для DataRow строка
переводится в режим редактирования. Вы получаете возможность внести любые
необходимые ва.ч изменения, чтобы затем вызьmать EndEdi t () и зафиксировать
эти изменения или СапсеlЕdit() и выполнить откат к оригинальной версии дан·
ных. например:

private void UpdateSomeRow()


{
11 предполагается, что строха для редахтирования уже получена.
11 ВыполняетСя перевод этой строхи В режим редахтирования.
rowToUpdate.Begin Edit();
11 Отправха СТРОI<И вспомогательной фУНI<ЦИИ, возвращаnцeй Вооlеan.
if( ChangeValuesForThisRow(rowToUpdate) )
rowToUpdate.EndEdit(); IIOK!
e1se
rowTOUpdate.CancelEdit(); 11 Забудьте об эток.

Вы, конечно, можете вызывать эти методы для данного DataRow и вручную,
НО они вызываются автоматически при редактировании элемента DataGridView,
связанного с DataTable. Например. при выборе строки в DataGridView эта строка
автоматически переводится в режим редактирования. А при перемещении фокуса
ввода в новую CTP0Ity автоматически вызывается EndEdit ().
Глпва 22. :ЦОС1"упlC базам д.аflНbt.JC ",noмощью ADO.NET 989

Работа с ТИПОМ Dat'aVi'ew


в терминах базы ДaIO-1ЫХ nредсrrrщвлеlW..e - , это П'Qкq.э набора данных таблицм
(или множества таблиЦ) в определенном стиле. НlШp1l:lМер. с пtJмощыо М1ctOэoft sgL
Server на базе таб1iИЦЬJ lnventory МОЖНО создать .цредстааление. lЮторое воэвраТl1'
новую таблицу. содержaщyro ЗВ'rомобшJИ тол:ько эада:н,воГQ цвета. В .ADO.NEТ тип
D.at-аViеw ПD3ВОЛJ1е'r программно извле<JЪ подмнщ~е(:тво да:н:ных. ив DataTa.ble и
раэмес"I'И"ГЬ их в отдельном объекте, Одцим из, ~ преимуществ создания мно­
жеСТВа предстanлеЮIЙ одRОЙ и той ще 1'Qf)лWJiЫ щmяетс.вто, что. вы можете связать
эти npеДстав.rreния с различными зn.емептами графичеСRОТО интерфейса (таними,
КзR DataGridView). К примеру. QДЩ! ,элещ.нт Dat.aGridVi ew МDЖ80 связать с объ­
eld'OM DataView\ ПОЕаЭЫJЩЮ1ЦИМ все мвщщrы из таблицы Inventory, в то BpeмR Шlli
дрyrой элемент {)удет настроен на отображение тoл:ыtо зеленых автомобилей.
для LIЛЛЮC'r'рации добmН:fi"е -"текущему графическому :интерфейсу еще одй1i1'ю]
Data'Gt idV.!ew, :назвав· его 'd'ata,G ridColtsView и (:QnPОВОДИВ по.я.снmoщим Элемен­
том La:bel, Затем о~Д~лите ЧЛeR~пер~~:нную colt.sOn.ly Vi ew типа DataView.

рпЫ 1с partial с l a.sэ МoiinF оrш ; Рсн:т


>(
1/ npe~~JJeщr$ ~.DataTable.
j),э:lа\/ i.ew GoH.sOr.l yV iew, 1/ QтоБРQleJЩе только .:расных t(OJlЬ'l"OB.

1'enеръ ~оздЩwe новую вспомогательную фующию с именем Cre':H.eDat:.aVi,,"w О


и вы~ювите ЭТОТ метод в кон.струнторе формы. эадашюм По умолчанию.. сразу i.{Ш
после TOr(i), ~ будет создан тип DataTable:
pllblic Mail1For:m ()
!

/I СОJlАаи:ме !!Iаб;l1Ифol Дaщa.IX.


Crea± e OataTable О;
// Соsр,аиме праДСТaJ!Ineн:Jor.
Creat.eDataView (') ;

Ниже покааана реализация этой новой вспомогательной фyнкцm,L Обратите


вНИМ'а:ниена ТО, что 1Сонгтрywroру Dat'.a Vlew передаетCjf пm IJata'Ta'blE. н.оroрыЙ
будет испо:JIЬзоватъся {1llЯ uостро.оои-я J10JIblJователъского набора строн дam:rьтx.

"p.rivate \1oid Cre,ateDa,taView П


{
// Устаковха мб~ ДПIII ар.дс~n~.
со.], t.sOnly'Vlew = new DataV.i.ew (lnvелtЬryТаblе) ;

/I НАQ~ОЙU' i'lp&АО1Н8,п8ииа с ПQIIQJII;W) фwu.~_


te.: = "Make = 1 С о ;!. 1:''';
co1tsOt!lyView. ROWF i l
11 ПриаJl51t8. l( '_~!1'Y ynpa&Jl8 __ .
9эtаGr i dCol,tsV,i ew . D", t &Source= с01 ts-On i 'yView;
}
990 Часть IV. Программирование с помощью библиотек .NEТ

Как видите. ЮIасс DataView предлагает СIlОЙСТВО RowFilter. содерЖаЩее строку


критериев фильтрации. используемую для поиска соответствующих строк. После
получ:ен:ия объекта представления соответственно устанаВЛИIlается свойство
DataSource элемента управления. На рис. 22.17 ПОRaЗано окно готового приложения .

• Гоб ,.Ц" ,"'шнн Г-11t:1lr8J

I Yдм4rb CТjXIII~ 11] ! ,-==]


[ Выбpгrь",-" ] C:-:~~
все __ ... Ор1 abIe

i --
коричневьJЙ

т раэ~ый I Pдlin 11
t;ee1 lIо-зеl1еныi:i Fred .g



Рис. 22.17. Представление отф~льтрованных данных

Исходный код. Проект CarDataTabIeViewer размещен в подкаталоге, соответствующем главе 22.

Работа с адаптерами данных


Теперь, когда вы знаете возможности использования типов DataSet ADO.NEТ.
обратим внимание на адаптеры данных. Напомним. что объекты адаптера данных
используются ДЛR "наполнения~ DataSet объектами DataTable и возврата изме­
ненных объектов DataTable базе данных для обработки. Описания основных чле­
нов базового класса DbDataAdapter приведены в табл. 22.15.

Таблица 22.15. Основные члены класса DbDataAdapter

Член.,. Описание
SelectCommand Задают SQL-комэнды, которые будут отправлены хранилищу данных при
InsertCommand вызове метода Fill () или Update ()
UpdateCommand
DeleteCommand
FillO Заполняет данную таблицу в DataSet некоторым набором записей, за­
висящим от заданного объектом команды значения SelectCommand
Update () Обновляет DataTable, используя объекты команд из свойств
IпsеrtСоmшапd, UpdateCommand или DeleteCom.mand. Точная ко­
манда, которая при этом выполняется, зависиТ от значения RowState
для данного DataRow в данном объекте DataTable (данного DataSet)
[пава 22. Доступ ~ баЗl1М даНkЫХ с ПОМОЩЬЮ ADO, NEJ 991
в следующих примерах :не забывайте о' т.ом. что объекты aд~nTepa данных
упрaВJ1sпот соответствующим соединением с базой дaIfНЫ:X за ваС. та}!; ЧТО вам не
nРИДf'1'СЯ nВИQ 01J'РЫ1ЩТЬ или ЩI.НрывGIТЬ сеанс связи с СУБД, Тем 'Не :менее. ~aM все
равно нужно n.редостаЩIТЬ адаптеру да иных дclkтвнтелъный обыжr аQед11Н~ИИR
ИJЩ. в вцде аргумента fюнст:руктора. ~рсшу соединения (которая будет ИСl'Щl!ЪЭО­
Ba:rъC8 ддя DОСТРЩ~1Щ.!l в;цутре.ннето OOЪeRТa соединения).

ЗаПОЛJiение D'ataSet с помощьюадanтера данных


Создэ;fJ:те HOВQe 1\онсолъвое цриложепие с именем Fi11DataSe'tWithSqlDa1a-
1>.dapter. YI(азав в нем исщщыювшmе пространств ИМен System.Datca и $уstещ.
Di'ita .Sq1Ciient. Обновите метод Маiл () т.ак. :иа:кnpедлагается ниже (ДЛJ1 дрое-l'ОТЫ
здесь /-11': цоказЭJ-I блан 't r у 1CEJt сЪ!.1-

'M,.atic Iloid мain (str iлg []arqs)


1
о;;пs,оl,е. Wr i't.е'Liшв {."~ ,;< ".*;, 3аба'вы с адаптерами даннЫХ .. *. k" \ ОП ' ) ;
зtLing Cl'lStr =
~llid=sa,jpW C!=i r 'лi tial C.atalog=C'ars'; Dа'tз. 50urr;e= (lэс:аl)" i
11 ЗаnОJDI~е Da'tas'e t 80В_ Data'l'ahl.e.
DataSe!: шуDS = new Dаtа.S~t(flСаfЗ");
SqE1ata.Ad.<Jptex- dAda,pt =
I1~W S.glba.taAdapte-х ('}Se leet ~ Fr:.ош ];1J 11ento.ryfl. cr,str);
dAdapt . Fill (m yDS ,~ ПТаvеtJtо rу");

11 Orобра.~ СО;Qержимоro.
Prirlt'.DataS'et (myV.s};

0бра1'иrrе внимание 1Ia то. что ададтер дaннъrx создается с указанием SQL-
'Оператора Selec t. Это 31rn:ченuе будет ислоль~оватъt:.я: фШ внутреннего постро­
еш,ы! объекта команды. которую за.те,м МОЖF.\Q будет по:rry"iИТЬ, выбрав (;вой.ство
SelectCQ!11m.and. Далее. заметьте, Ч1'Q метод Fill 1, получает экземп,ляр типа
D.ata5~it и неоБЖJa,теЛhНое стрОВQJюе ИМЯ. КО'Г0р!;)е будет использоваться пря УсТа­
lювке свойmватаыlNаmеe нового Qбъекта [1ataTaы1e (если вы не у.кажете имя т-а­
блицы. адзnrrер данных ИCI10дьзуетдrЦ'lтабщщьr R,lМНTableJ.

Замечание. Метод Fi 11 () воЗвращает целое число, соотвеl'СТВУipщее чиt;оу отрок, затронутых


SQL -заЛРОGОМ ,

JUili и ,следует ожидать. при передаче Da'taS~t методу Е'!" i-r,tDаtаSе"t, () tpеали­
:,юванному И этой rлаве pa1!e~} будет подучен список .Jloex стрщr та,бщщы lnvent,ory
6айы д;лпtых Cars (рис . 22. i8),

Отображен.,е имен баЗbl данных в понятные имена


Бы. скорее 1ICeI'0. знаете. Q'j'() nДминистраторы баз данных СКJlQШIhI созда~
i3aТЪ и;мена таб.пиц и СТDлбцов. 'Которые недъзн назвать ЩН-IJlТНЫМИ ДЛЯ RонеЧFIЫX
ПОЛЬЗ0вате.леИ. Но' хорснпеи вестью яnллется то. -что об-рскты адаптера да:нных
992 Часть IV. Программирование с помощью библиотек .NET

поддерживают внутреннюю строго типизованную н:оллеRЦИЮ (Da taTableMa pping-


Colle c tion) типов Sy stem.Data.Co mmon.DataTa bleMapping. доступную с помо:Ш;Ью
свойства TableMappin g s.

Рис. 22.18. Заполнение DataS e t с помощью объекта адаптера данных

при желании вы можете использовать эту коллекцию ДЛЯ того. чтобы инфор­
мировать DataTable о "дисплейных именах" . которые должны использоваться
при выводе содержимого. Предположим. например. что вы хотите отобразить
имя l n v en tory. используемое для таблицы в рамках СУБД , в дисплейное имя
Ассортимент. Кроме того. предположим, что вы хотите отобразить имя столбца
CarID в виде Но мер. а имя столбца PetName - в виде Название . для этого в объект
адаптера данных перед вызовом метода Fi 11 () добавьте следующий npогрaммный
код (и не забудьте указать usirlg для пространства имен System.Data.C omm.on ).
static void Main(string[] args)

1/ Отображение имен столбцов Бд в имена, понятные ПОЛЬ$оватemo.


Dat aTableMa pp i ng cus tMap =
dAdapt.TableMappings .Ad d("Inventory", "Ассор т им е н т");
c ustMap. Co lumnMappings . Add ("CarID", "Номер");
c ustMap. Co lumnMappi n gs . Ad d("Pet Na me", "Название");
d Adapt.Fill(m y DS, "Inv ent o r y ");

Теперь. вьmолнив программу, вы обнаружите. что метод F'rintDataS e t () ото­


бражает "понятные" имена объектов DataTabl e и DataRo w, а не имена, заданные
структурой базы данных.

Исходный код. Проект FillDataSetWithSqlDataAdapter размещен в подкаталоге, соответствующем


таве22.
{пава '22. Доступ j( ба!l3М данных с nDМОЩЬЮ ADONET 9·93

ОбновnеКИ'е базы да, нных с ПОМО.ЩЬ. Ю


объекта адаптера данных
Адanтеры.щumыx :могут не тальм зanОЛШlТЬ 1IIJ1Я вас таблицы объекта DataSet .
.QНИ могут также шщцерживать НS:бор объектов ооновных sgL-.команд. используя
их ДЛЯ возвращения :моДиф1-ЩИРОВан.ных данных обраmь Б х.ранилище данных.
При .вhIзаве меТОДа lTpda;te (] aдanTepa ДaIШЬLlC. провериется свой.ствоR",w&tаtе
д.пя: ващцоЙ строки в DataTable и и.cnоm.зyIDТCЯ соответствующие sgL-II:ОМIЩЦbl.
npиtвоенные свОЙcтnамDеlеtеСоmmаnd. Inse'r.tcoromand И 1JpdateCoro.ma,nd. чтоб»l
ЭamIсать изМенения данного Dal:..aTable в КСТОЧНИR :дaRНыx.
Чтобьt проиллюстрировать процесс иcnользовШiИя адаптера дaIOiЫX.длв. еозвра­
щен.иg иэменениt! DataTabl е в хранилшце данных, в следующем примере мы пере­
рабэтае·м npиложение Ca~sIr1vertorIfUpdateI:. соаДafЩQе в этой главе ранее. чтобы
на этоcr раз ИСПOJIЬ3ОВать DataSet и объект aдarrтepa данных. ЛQОКОЛЬКУЗНВ,<Ш-reль­
наи часТЬ приложени.я останется той же. CKoнцeHTp~pyeM свое внимание на изме­
нениях. которые необходимо сделать в метоДах DеlеtEiСё\r () • ТJрd,аtеСаrРеt1~аП1е ()
и ln sertNoewCar () ('ttобы уТО . . ПlИТъ детали. про верьте TelCCT эатружа.еМОГQ про,
граммното кода Д,i'lЯ дашюrо пр:и:мера).
Первым ооновным изменениt>:М. которое требуеТСJJ Вflест.и в црщroжение, явля·
ется onред~ение двух новых стаТ}<IЧес~их члеНQR-uеремениых кла.сса. J;'rogram для
представления DataSet и объекта соединенил. Тажще. чтобы заl1O.1ПЩТb D:at·a Set
начaлыt1iIМJlданпы:ми., МQЦИФИIЩРУетс.я метод 1'1aih ( 1.
class Program
{
11 Об_х'1' Ра taSet, IIс.~y'пItыii ~a. урозке nPИJЮ:lteIUЦl.
рчt,liс зtаtiс DataSet dsCar!QV6ntorjl =
:n.ew DataSet ("CaJ:'&Datab,a .se ") :

11 ОбlЪUIl1 соед»ШЕЩИ. ,ДOCTY~ I!a }'ров!!е npипожениlil'.


pubLic .statj с SqlС'G:)пn&сtiоп спОЬ ) = rtew S::jlC@nne·c ti.on [
"1.lid=sa;pwd=; Initial саtаlоg=С.,rз;Dаt.а ЗОi,Jrс ? = (l ocaJ .). " );

8tatic void мain{st,ri.ng[] args}


r
// соз.цаЮfe а.Аап~ера. даиН5IX n hпоnИеИИl\I Dat:aSet.
SqlDataAdapte;r d.Adap1:ie'r ~
new ."J.qlDataAdapt&1" .( "S:elect." frorr. Invепtоrу". cnObj);
d.A dapter .F:tlJ (';il.lC.:\rInventory. "lrl',"ept;ory");
si10w.IПRtгtJсtJ.с.ns (.) ;

}
., .

Обрати.те внимание и:на то, что методы Li g t 1 nvento:t)' (.), DeleteCa r ().
Uрdаt.еСд.I'РеНiamе (:) и In.s.ertNewCar(') ТaRже бbIЛИ изменены (; тем, чтобы OlЦI
могли ПРИНII'l'Ъ Sч1 DаtаJ.Дарtе:r в .!taчест.ве параметра.
994 Часть IV. Программирование с помощью библиотек .NET

Установка свойства InsertCommand


При использовании адаптера данных для обновления Оа taSet первой за·
дачей оказывается назначение свойствам OpdateCommand, OeleteCommand и
InsertCommand действительных объектов команд (пока вы этого не сделаете. эти
свойства возвращают null). Слово "действительные" для объектов команд здесь ис­
пользуется потому. что набор объектов команд. которые вы "подключаете" к адап­
теру данных. изменяется в зависимости от таблицы, данные которой вы пытаетесь
обновить. В этом примере соответствующей таблицей является таблица Invепtогу.
И вот как выглядит измененный метод InsertNewCar ().
private static void InsertNewCa r(SqlOataAdapter dAdpater)

11 Сбор информации о ковой машине.

11 Формирование SQL-опера'l'ора Insert и подx.moчеиие Jt Da taAdapter .


string sql = string.Format("1nsert Into Inventory" +
" (СаУ1О, Make, Co1or, PetNarne) Values" +
"('{О}', '{1)', '(2)', '{З)')",

пеwСаrIО, newCarMake, newCarCo1or, newCarPetNanle);


dAdpater.lnsertCornmand = new Sq1Cornrnand(sql);
dAdpater. 1nsertCornrnand. Connection = cnObj;
11 ОБНОllnеиие 'l'абmщw Inventory с учетом новой строхи.
DataRow newCar = dsCar1nventory.Tables["Inventory"] .NewRow();
newCar["CarIO"] = ne wCarIO;
newCar [ "Ma ke" ] = newCa.rMake ;
newCar ["Co10r"] = newCarColor;
newCar [" PetNarne" 1 = newCarPetNaroe;
dsCar1nventory.Tables["InventoryfТ] .Rows.Add(newCar );
dAdpater.Update(dsCarInventory.Tables["Inventory"]);

После создания объекта команды он 'подключается" к адаптеру с помо­


щью свойства InsertCommand. Затем в OataTable таБJШЦЫ Inventory добавляет·
ся новая строка. представленная. объектом dsCarlnventory. После добавлеIШЯ
DataRow в OataTable адаптер вьmолнит SQL-комаиду. обнаруженную в свой­
ствеInsertCommand. поскольку значением RowState этой новой строки является
DataRowState.Added.

Установка свойства UpdateCommand


Модификации метода UpdateCarPetName () оказываются приблизительно та­
кими же.. Просто создайте новый объект команды и укажите его для свойства
Opdatecomma nd.
private static void UpdateCarPetName(SqlOataAdapter dAdpater)
{
1/ Сбор инФормации об измениемой машине.

11 ФормаТИРОllаиие SQL-onepa'l'opa Update и подx.moчение Jt DataAdapter.


s tring sql = string.Format
Глава 22. д,QСТУПК ~азам данны>: с ПОМОЩЬ!О ADO.NEТ 995
("U'f'Jdate Iлvгпtоry Set P.etName = '{()j" 'Wnere ~a:rID "" t (l i "",
newPetName, c.arToт,Jpdate);
8чlСоmmarld cmd =' 'f)'eW SqlСоrщщ:шd (эql, cnObj);
,dAdpater.tJpdate·C()!1U1W.hd ~ сщd;

DataRow[ 1 С;irR'оwТс;Юр(jа t е =
d,s CarI ov,eJ'1 tOI''j .1abl.e,s ["Iп:vеоtоrу" J . Sele.c::t (
string, Fcrmat (nCarID = • {О) , " ,~aTToUpGiQt€) ) ;
carRo,wTo1)pc;J;i;\te [О] [" l?e,t .}Jame" ] =' n-еw!1е'tNаmе;
dAdpater . UpdEl·t.e ('ds-СаrI nven:bory. T:able.s [" Inv<'Jri't.ory"·'] ) ;

в данном случае . .\ctоrда ВЫ выбираете строку (с :ПОМощью меТОДа S-ele'ct_ ()}.


для R,ow st a't.e уназаВ'НОЙ стро:ки abtomaTll-чеБСИ: устанавливается зltачеI-tие
DataR'~ wSJ'tate.Modified. ДругИМ З$:CJIJ1IЩJ!aIOЩИМ ВНИМ'afmЯ моме.НТОМ здесь Яl3~
ляетсн ТО. что M~OД Select С) .возврюдает массив объектов Da'taRo.,. ПО31'ому Bы
ДОЛЖНЫ YJ{аэаrь. RЮ<yЮ цменно строку требуется иЗМe.IiИ'l"Ь.

Установка свойства DeleteComman'd


Наконец, вы имеете следующую мо:цифина.цию. метода De ls-,tеС<J.t' ( ) .
рriva'\;е' stati(: \7oid DеlеtеСiП ('SqlDa,t aAdapt,e .r dAdpater')
(

striпg эql =
strlhg.F'orm-аt ("Delet-e Етот lдvsпtоrу where CarID = '{O"j' ,:.',
ci'irTGDelete);
SqlCornmand cmd = n.ew Sq1Cbmt1'1apd (.sql r CfI'Obj);
dAQpat~r.DeleteCommaТJd = ernd;

uataRow (J ca:rRowToDelete =
dэСа:r IrJ.veritt>ry .1ables [,"'Inven tory" ] . Sel.ect('st,rlng. Fш:mаt (
"Cё.rrID =' i(Q),1.t/ carToDelet:e»),
С& iLН,оwТзD еl et.e [О] . Delete О ;
dAdpa ter • tJpdaXe- (dsCarI mre.fltQry. Tables [ "lлvеrJt0rу 11) ) :

в этом случае вы находите строку. которую ttyЖНо удалить (снова с помо ·


ЩЬ.IQ -метода S'elect О), а затеМ устанaвлиsаe-:rе ДJUI свойства RoWStabe значение
DataRo'V/:St а t е. Оеl e-t..ed с помощью вызова Delet ~ (} .

jiCXQAНlol" код. Проект Carstnver10ryUpdaterDS размещен в подкаталоге, соотвеп;твующем maвe 22,

Генерирование SQ, L-команд с ПОМОЩЬЮ


типов построителя команд
вы ДОЛЖНЫ соrnаситьс.я с тем. 'ПО ддя работы с адапт~~ ДaJIfU>IX:может по.­
требоватьол ВВ'од довоm.на БОЛЬ1Darо объема проrраммного .I\-ода. ,а такЖе СО3Да-
6И~ всех че.тырех объ,ектов f(oMI:tН,!), 11 СООJ;ветству.ющеЙ строки соедин~1lИЯ [ИЛIf
996 Часть IV. Программирование с помощью библиотек .NET

DЬСоппесtiоп-объекта). Чтобы упростить дело, в .NEТ2.0 каждый из поставщи­


ков данных ЛDО.NEТ предлагает тип nocmpoumeля команд. Используя этот ТИП. вы
можете автоматически получать объекты команд, содержащие правильные типы
команд Insert. Delete и Opdate на базе исходного оператора Select.
Тип SqlCommandBuilder автоматически генерирует значения для свойств
InsertCommand, UpdateCommand и DeleteCommand объекта SqlDataAdapter на
основе значения SelectCommand. Очевидным преимуществом здесь является то,
что исключается необходимость строить все типы SqlCommand и SqlParameter
вручную.

Здесь возникает вопрос о том. как построитель команд может строить указан­
ные объекты SQL-кОМанд "на лету". Оказывается, все дело в метаданных. В среде
выполнения, когда вы вызываете метод Upda te () адаптера данных, соответствую­
щий построитель команд читает данные структуры ба:зы данных для автоматиче­
ского генерирования объектов соответствующих команд вставки, удаления и об­
новления данных.

Рассмотрите следующий пример. в котором строка из DataSet удаляется с по­


мощью автоматически сгенерированных SQL~операторов. Кроме того, для каждого
объекта команды соответствующая команда выводится на печать.

static void Main(string[ ) args)


(
DataSet theCarsInventory = new DataSet();
11 Создание соединения.
SqlConnection сп = new
SqlConn ection( "server=(local) ;User ID=sa;Pwd=;database=Cars");
11 Автоматическое генерирование команд Insert Upda te и Delete
I

11 на основе существующей команды Select.


SqlDa taAdapter da = new
SqlDаtаАdарtеr(ПSЕLЕСТ * FROM Inventory", сп);
SqlCommandBuilder invBuilder = new SqlCommandBuilder(da);
11 Заполнение DataSet.
da .Fill(theCarsInventory, "Inventory");
PrintDataSet(theCarsInventory) ;
/1 Удаление с'1'РО:ХИ на основании пonьзова'1'еnъского ввода
11 и обновление базы данных.
t ry
(
Consol e.Wri te("HoMep строки для удаления: ");
int rowToDelete = int.Parse(Console.ReadLine());
theCarsInvent ory _Tabl es ["Inventory"] .Rows[rowToDelete] .Delete( );
da.Update(theCarslnventory, "Inventory");

catch (Excep tlon е)


(
Console.WriteLine(e.Message) ;
)
11 Новое заполнение и печать '1'абтщы Inventory.
theCarsInventory = new DataSet();
da .Fill(theCarsInventory , "Inventory");
PrintDataSet(theCarsInventory) ;
iЛаеiI 22. Доступ к qззвм данЮ>lХ СПОМОЩIiЮ ADo,N'EТ 997
в зтЬм фрагменте программного кода QБJЩТИте внимание на то. что здесJ;, объеm
ПDстроителя кЬманд (В .дa!tHOM сдуча.е . Э1'О SqlСоmщалdВuil'dеr) передаетс5.I объек:ry
адаптера ДаНнь1Х в вцде параметра конструктора и fiO.!lblIie никак не ИCIJОJIЬЗуется.
Как бы стратlo ЭТО ЮI вЬП'лядсло, но 31'1;)· все. что вам требуется сделать (II мини­
мюIыJьD( усло&илх). УЬ"а..~ннЫЙ тип В фоновом рёжИМе СRонфиrурирует ДJiЯ <!,Дал­
тера Данных ocтa.trьнъre объекты комацд .
.хотя идея получить кое-что, не предrщ.пщ :ничего .взамен, понравитсл мноrи.м .
важно· ПОНЯТЬ , что построители команд ~еют некоторые (В,есьма важные) оrpшш­
ч:eIn1я. В чаcrrlroС1:И. постро.итель команд может' автоматически reяерироваn. sgL-
I~oMaнды ДЛЯ а.цаптера дюnц,IX только Б Т(')М случае. Korдa ВЫПOJШены все следУЮ ­
щие УСЛОВШI.

• СаотвеТcтВj10ЩaR :командаБе le ct :вщrn,модеiiствуеттол:ыш с ОДНОЙ таблицей


(так что. например. соеДИJreRИЯ таблиц не доIJYСкаютея).

• Дlщ Э'roй единственной таблиnы :назначен дер,ви'ЦIЫЙ ддюч.


• Столбцы , представляющи.е пеРВ~iЧНЫЙ :ключ. приеутет.вуют 'в ДЩЦIОМ SQL-
операторе Select.
Таь: или иначе, рис. 22 .19 деМОIШТРИрует. что уюазан.ная, стро·ка уд~яется из
физической ба3,Ы даннЬ1Х (при анализе програмшtото ~oдa этого 1JрЩ.Мера следует
ра3ill1Чать зна<щние CarID и UОрЯДROВJ:JlЙ номер стро!щ).

~C. 22. tg~ ИСПОJ1ЬЭ0!'1аН\.1& ~томатичеФ<.и генерируемЫХ команд SQL

Исхо,ц~ ... Й КDД, npO~1<Т MySqlComman.,c;tBuiIder раз-мещен в подкаталоге, СФОisетствующем


rJЩве22.
998 Часть IV. Программирование с помощью БИБлиотек .NET

Объекты DataSet с множеством


таблиц и объекты DataRelation
До зтого момента во всех примерах данной главы объекты DataSet содержали
по одному объектуDataTable. Однако БСЯМОЩЬ несвязного уровня ЛDО.NEТ про­
является тогда, когда
DataSet содержит множество объектов DataTable. В этом
случае вы можете добавить в коллекцию DataRelation объекта DataSet любое
число объектов DataRelation. чтобы указать взаимные связи таблиц . Используя
эти объекты, клиентское звено приложения сможет осуществлять переходы между
таблицами без пересьuши данных по сети.
для демонстрации возможностей использования объектов DataRelation соз­
дайте новый проект Windows Fопns с именем MultitabledDataSet. Ipафический
интерфейс пользователя этого приложения достаточно прост. На рис. 22.20 вы
можете видеть три элемента управления DataGridView, содержащие данные из
таблиц Iпveпtогу. Orders и Customers базы данных Cars. Кроме того, там присут­
ствует одна кнопка, с помощью которой информация обо всех изменениях направ­
ляется в хранилище данных .

.}1 МаннпуоЛIОР ба ,ы ДdHHЫX Сап r.:·lli21rx:


т a6l11t11a InvenlCII~

. __ ~, _ """..__... . о
_.~ - "_ .

2
----- ------tz·- ---.
'4 1

Рис. 22.20. Просмотр связанных объектов DataTable

Чтобы упростить ситуацию, тип MainForm будет использовать построители ко­


манд (по одному для каждой таблицы) для автоматичесн;ого генерирования SQL-
команд каждого из трех объектов sqlDataAdapte r . Вот исходная модификация со­
ответствующего экземпляра типа Form :

public partial сlаsз MainForm : Form


(
11 Объект DataSet .ц.пsr формы.
privatc DataS et car sDS = new DataSe t ( "CarsDataSet") ;
Глава 22, ДОСТУП ~ базам данных с ПОМОЩ):JЮ AQO , NEТ 999
I/ Прииен~е пос~иmenей хаиа1ljЦo ДJJЯ yupощеEJtR
/I _ас'1!РОЙХИ а.цliП'Rвроз ,1:IaRНiDC.
p!Tvat.'e 'SqlСОпiJIl.аnd:8uЦ deJ;' эql<;::ВInvеп t;,иrу;
:рт i vate 5q.lСQIIl.щап:dБuildеr sqlCBcu.s:t ,0тners;
ру i "а te Sql C:'Oll1Iflar>dB1'1i lde:rs,ql'C:BQrder~ ;

11 А.щ&П'1'еры данншс. (.д.nJil Ж~ОЙ из '1'аб.пиц) ;


pri"a.te SqlDаtа..д.dарtеr .Ln17"]'ab],eAdapter;
pr:i ",а t; е $qlDa t aAdap,t er cU5tT~bl eAdapte r ,;
privat~ sq.!.D.зtа.Аd:арt-еrоЕdеrsтаыlАdарtеr;;

/ / Об'ЪeJЩ" ооеДJЩе.нии дnи фор_.


priv,at.~ SqlСопneсtiО'n сп =
'пеw :sqlСоллес.-tiоtl C"'se r 'v er= (.local) ; uld=sa; pwd=; d",tebase=-Cars'l) ;

J
J\oНCTPYКTOP формы Вl>lПОJПИiет основную работу по СDЗданию чле.цОЗ-DеремеlI­
ны:х ДЛЯ данных и зar:!олнеJ-lliЮ
DataSet. Обратите Ta1GКe внймЗние.на ВЫ,ЗОВ при­
ваТ1jОЙ вспомогательной. функции B<J i 1 dTab lеRe:Lat. iо:пshiр ( ) .

publ i с Mai'hf·orm ( )
I
1 oi t ia l i zесоmРQле!.lt О ;
11 Создание ~an!l.'8pos.
ir1'\1'J'aibleAdapt er = пе'"
Sql DataAdapt.er ('''Select .. frош Iлvепtоrу", сп);
custTilbleAdёi:p t er =-n'ew
~.qlDаtаАdарtЕ:!: ("Belect " froIJ( C1!J' S1:~meIS", сп);
оГclеr-sТаblеА'dарtе'l = ре""
sqlDataAd,ap1':er (".Ss1ec:t * f rorn O:rdEors". сп);

1/ А.в'1'orенерироваИlllе Jl:онаИА.
sqlСВ1!Нl€лt о rу = new зчН:::оmmаПdВ·.Jil(lеr (inyTfJ;ЪleAdapter} I
sqlсвоrdеr з = new SqlC9 lPJnar.dBuildel' (oJ:de-х s1'аыlАdарtеrj;;
$чlСВСI:LSJ:оmегs = new SqlC'o!!uriC;!,n'dВuilder (ctJstTaGleAdapte.t).;

11 Добазление '.li.~бпиц 15 Data,S et.


{nVTable});dapter, F i.ll (caors.D::'i , .. InvEintoxy11.}:
c l1st'J'al;JJ eJl"dapt'e:t • F'ili (са! ;>.D9, "CU5 tbmers") ;
оп:lегsтаыlл:d9р'tеJ;'.F.:llll (carsDS, "Orders");

/ 1 ,Соэдание О'rНОШений МеждУ !l.'аблицами:.


BuildTableR<:!lat.ians1:tip О,

11 I1pивRз:ка Jt ЭllеъsеНJrIЦol :f~р_левии.


da taGr i dViewIpve.ntory. Data SOJJr се = carsI'S, ТаЬ,lе:'О' [ " Inveflt.oTY" J ;
d'ataG"ri,dVieWCi1stomets, D,atEiSou.rce = саrsШ;. Т.аЬ1е.$ ["C1).'st om~r:s- "J;
datз.Gr i dvlе""Оrd.~J" $.DаtаSоtlI'ое = caJ;Os DS; TaЫ€o; [ "Qrders'''] i
')

ВеUОМОГ<lтельн:ая фуmЩИJi B'J ildTai::-l еRеlаtiопз h'iр () д.елаетв точности то.


ЧТО ОТ аее ОiIЩД<1.ется.. HanaMI-IИМ. что .БЗ<щ данных Cars имеет ряд отноmеnий "ро­
iiИтеЛЬ-ПОТОМQR". ЧТО уч:итьrnзе'гся 11 с.ле.дующем фрarIl~~нте IIpCJJ'pal\iIl\>lНOrO кода:
1000 Часть IV, Программирование с помощью библиотек .NET

private void BuildTableRelationship ()


{
/ / Создание об'ltех'1'& О'1'Иошеиия CustomerOrder.
DataRelation dr = new DataRelation("CustomerOrder",
cars DS. Tabl es [ "Customers"] .Columns[ "CustI D"],
c arsDS. Tables ["Orde rs"] . Columns [" Cus tID"] ) ;
carsDS.Relations.Add(dr);
11 создание об'ltехта отношении InventoryOrder.
dr = new DаtаRеlаtiол ("InventoryOrder",
carsDS . Table s ["Inventory" J .Columns ["CarID" j,
carsDS.Tables["Orders"] .Columns["CarID"]);
carsD S .Relati on s.Add(dr) ;
}

Теперь. когда оБЪeRТ DataSet заполнен и отсоеДIшеи от источника данных. вы


можете работать со всеми таблицами локально. Можно вставлять. обновлять или
удалять значения любого из трех DataGridView. как только вы будете готовы от­
править измененные данные обратно в хранилище данных для обработки. щелкни­
те на кнопке обновления формы. Программный код соответствующего обработчика
события Click должен быть вам ПОЩIтен.
private void btnUpdate_Click(object sender, EventArgs е)
I
try
[
invTableAdapter.Update(carsDS, "Inventory");
custTab leAdapter. Update (car sDS, "Customers");
ordersTableAdapter.Update(carsDS, "Orders");

catch (Exception ех)


[
MessageBox.Show{ex.Message);

После щелчка на кнопке обновления вы обнаружите. что каждая из таблиц в


базе данных Сагв соответствующим образом изменена.

НавигаЦИОННblе возможности ДЛЯ связаННblХ таблиц


Чтобы продемонстрировать возможности DataRelation при программной реа·
лизации ДОС'I}'Па к данным связанных таблиц. добавьте в форму новый тип Ви tton
и соответствующий ему TextBox. В результате конечный пользователь должен по­
лучить возможность ввести идентификационный номер заказчика и увидеть ин­
формацию о заказе соответствующего клиента. которая выводится в простом окне
сообщения. Обработчик события Click этой кнопки реализован так.
private void btnGetlnfo_Click( object sender, System.Ev entArgs е)
{
string strInfo = П";
DataRow drCust = null;
Data Row[ ] d r s Order "" null;
Гhаза 22. AOCTyn К ба~ам данных 'с ПОМdЩЬiO ADO.NET 100,1
/ / ПОjJIуч~е :1I':каэахвоI'О CustID и$ Te~tвox.
il1 t theC'.Js't "" jmt. Par~e ttJ:l1s. tхt, СustПJ , Text,) i
/I Dопучешra ~Я:, CU~tID СОО'I!8е!1'С~уацей О'1'роJtи nб.-щцы ёu:atome~s.
d:r~t!.'5t. = Gar:sDS.Tables ["С1шt.оmеrs"] .Rо".з:,[tЬе;Сп'st]:
эtr Iпfl9 t= nзаRаз,ч~ N!" " drC"\J5t: ["С"ustIП"' ] .'ToStr:iri-g О + n \n;';

1/ ПереХОjl; О'.l!' ~a.бnицv захаа_ха. х табnице захаЗО!ll.


cU-sОJi:оег = drCus.t.. GetChild:.~ow~ (с.а;!:" $,DS. RelatioIl5 ["CUB !:omerO;r,de.r:"] ) ;

/1 ПОпуче.аие Rоиера saxU_.


f oreac-h (DataRo''j; r iп ф:' sOrder )
зtrln.fо += "Номер заказа: " + r ["'ОrdеrI.DИ} + "\п";

/1 Переход ОТ таб.JПЩw З.~SОИ х !t!абnИЦе &CcopR_e1lJ.1'a.


Da'taRow [J drSI-п'J' =
d:r,эОrder [О J .,GetR'a:r;ent'RQws (саХБ'О$. Rеlаtiол:!.J (" Iлvелtоrуо.rdег"] ):
11 ПопучeRи8 НИфОРNaЩНХ О IIUIИИ~.
foreach (DаtЩ'F1,О"l r iIl drsIЛI7)
{
s'trlnfo += "Марка: ,"' + :rr~Mak.e' ''' J + "\н";
strInfo +=: "ц&eT~ 11 + :r["Colc,'r"] + "\n";
strlDfD т= "Название: "+ r["PetName"] + " ' \п",

M~S5ageBo~. ЗЬО." (зиJп:Е'о" "Информация д.щ;!: данного 3dка:ЗЧИl</d") J

Как видите, КЛЮЧОМ к решeJIИЮ задачи д~р~мещенин ме-жду таблицами ~1-mы:x


:окааывается ИС:П:ОДЬЗОВaRие ряда методор, определеНН1>1Х типоМ Dаtа"fiдw, Давайте
рааоерем этот программный lЮД 1;10 ПОрНдку'. Сначала вм щ)'лучаете ПОДХОДRЩ:ИЙ
иденrифиr<адИОШIЫЙ номер э.аназЧИRa иа ТеКСТОВОГО б]l'ока й ИСПОЛl:Iзуете ЭТОТ по­
Мер ДЛJll'ОТО. 'ЧТобы nзйти соответствущщую строку в таблице Customers· (КQнечно
же, с помощью свойстэа ROWS), яан локавано ниже.

11 nOuучение vp.s_нкоrо CUs'tID иа TextВox.


ir:t. t..heCu.st = iпt.Р.8rsе(thi.s.tхtСU$'t.ID,Т~~t);
1/ Попyqеюsе ,ч'nJ! CustID соота&'l'С'1'Jlyuцeй С:'1'рОКИ' та.бn~ CU$tomert'l·.
Dat,aRO-I-l drC"s t = n lJll;
drO~$t = Ea:tsDS. Tables ["Cust$!1e~s \, ] . Rows [tbeCust ) ,
9t:tIllfo -+-= '''Зака~w-l'Ш; 11'" + drС 'uз't["',Cu'.stIО'''] . TbSt:.x-iоg,() + "\n(';

Затем Rbl JЮРfЦЮДИТ~ !iJT таёlлицы: Customers к таблице Order.s, Щ:Л0Лb;ly8 отНоше­
liИеСustоrn~rОп:1еL. ОБР;;J:m:те внимание на то, Ч'I'D Метод DataRow_Ge1:;ChildRow.s О
!103вOJ1Яет D~JJУЧИ'1'Ь дщ;тyu R СТРОl!ЭМ дочернеЙ .аблХ'щы. ПОСi1Те этого ВЫ :можете
nPОЧ~lТатъ информацию из этой Табmщы.

/1 ПерехGД O'J!' 'l'аб~ ~•• аз-.ихов 1( 1J.'аб;JЩЦe захаsos.


DataRow Т} .drsOrder = nul1;
dr$Order =drСuз.t..GetСhildRows (са!!: s.DS. R'elations]"C1,1st/)'meiI:Ord'e r" '] ) ;

If Получение' номера ЗАха!lа.


fnreach(DataRow r in ЦxsQrder)
stJ::IJ1:iQ += "I:i0М€Р эа1'<:аза~ " + r ["Orde:t'ID"] + "\n";
1002 Часть IV. Программирование с помощью библиотек .NEТ
т
8акпючительньrм шагом является переход от таблицы Orders к родительской
таблице (Inventory) с помощью метода GetParentRows (). После этого вы сможете
прочитать информацию из таблицы Inventory для столбцов Маке. PetName и Color.
как ПОl{азано ниЖе.

11 Переход o~ таблицы захазов х таблице ассортимента.


DataRow[] drslnv =
drsOrder[OJ . GetParentRows (carsDS.Relations ["InventoryOrder"J );
foreach(DataRow r in drslnv)
{
strlnfo += "Марка: " + У["Маке" ] + "\п";
strlnfo += "Цвет: n + r ["Col or "] + "\п";
strlnfo += "Название: " + r["PetName") + " \ п";

На рис. 22.21 поназан один из возможных вариантов вывода.

3aк_~1

НOI!eP
марк.,
"""138'
eot
I
Цвет : I)blJl<!1it
~: Rusty 6U<Qer

ок

РИС.22.21. Навигация по связанным данным

Этот пример убеждает в пользе типа Dataset. Поскольв:у DataSet отсоединяет­


ся от соответствующего ИСТОЧНИI<a данных, вы можете работать с копией данных,
размещенной в памяти. переходя от одной таБJШЦЫ к другой и выполняя все не­
обходимые модификации. удаления или вставки. По завершении этой работы вы
можете направить свои изменения в хранилище данных для их обработни.

ИСХОДНЫЙ КОД. Проект MultltabIedDataSetApp размещен в подкаталоге. соответствующем главе 22.

Возможности мастеров данных


к этому моменту HaIIIerO рассмотрения вы открьши для себя множество путей
взаимодействия с типами ADO.NEТ без использования мастеров. Но. хотя пони­
мание основ работы с поставщиком данных оказывается (определенно) важным.
важно и то. что от болыIlиx объемов вводимого вручную. в общем-та. шаблонного
программного кода могут болеть руки. Поэтому в завершение мы рассмотрим не­
сколько мастеров даЮiЫХ, которые могут вам при случае пригоДИться.

Здесь не ставится цель комментировать все мастера данных для интерфей­


са пользователя, имеющиеся в VisuaI Studio 2005. но чтобы показать их основ­
ные возможности, мы рассмотрим некоторые дополнительные опции конфигу­
рации элемента управления Da taGridView. Создайте новое приложение Windows
Foгms с одной формой. содержащей элемент управления DataGridView с именем
iпvепtоrуDаtаGridViеw. В окне проектирования формы aI<тивизируйте ВСТРО-
Глава 22. Досrryп к базам ;цаI-lНЫХ с nомощъю AIJp .NfТ 1003
eIO-lЫЙ редактор ЭТQГО злем~J,Jта, 11 в раснрывающемся СШIСI\е Choose Dащ ЗЬигсе
(Выбра:гь источ6ИJ{ дm;mыx) Щf'.лкmrrе ва ссылке Add Project Data Spurce (ДобаЩll'Ь
ИСТОЧJiИl( даиных в проект), рис. 22..22.
, f';.m:I .~ ~ji<"l_ . - - - -- '-- - "', ~ - - ~-- - - ,- - _.- -- ~ - ... х

~~- -- ~~,-~ , - -- - --~ . - --, - .. - ,- .- - - ._- - - -,-,

Ри~. 22.22. Добавлеl1l ие источника :цаflI:iЫ?(

в резyJtЬrзте будет эапуще,ff мастер конфигурации I-)СТ'I)ЧНИКОБ ,данных. На це;р­


ВОм пxarе просто RЫберите J1Иктограмму Database 11 щелюntте па кнопке Nex1. I-ta
ЕГором шаге ще.IfКНИ1'е :юа FЩоцн:е New Соппесtiоп (Ново,е соединение) И YCT-clЯщвцте
с,вязь с базой д.аниых Сатз (испоЛЬзуя ВЬiШеприведенные И}IСТРУКЦИИ из раздена
Тоединение с базой ДЭНЩ-.I4. в VisuaJ Stш:l10 2005" э:той rnа-вы). Трет.иЦ mа;Г :ЩЩВjJ ­
Лllет мастеру сохранить строку Соеди:вения. во внешнем файле App .~ OГJfig в рам­
ках должным образо~ ОROНфЩ'IЛrpoванн'ОГО ЭЛeJ.\iеRТа < c()nnectiol1$ t;r; ings > (~TO.
в общ:ем:~w. iЩJIfIе'ТС-Я ДОВОЛЬНО ХОРОПIИМ решение..'VI). На за:J{.mочительн~м шar~ 'Вы
получаете ВОЗМQЖНОСТЬ B~()pa'ГЪ объенты: базы даннblX. которые должны исrюлъ-­
зоватьсл ге:нерцруемым объ~ктом DataSet. и для Hamero примера это -будет одна
тaQлиIta m\'entory [рис. 2~.23).
ПО завершении раБОТI!1 мастера НВi УВИдите_ Ч'То элемент D'ata Gr 1 (IVi ew В ОЮiе
проектировщrnя фор~ ;щтоматически отЬбражаеТИiJI.>Iе,на столБЦОВ. И если ВbIПол­
J-ШТЬ при:ложеF,IИе Б ТОМ В1-mе, .в каком онЬ, Н8ХClДИТСН сейчас. ВЫ, УВИдиТе, -.все содер­
жимое табщщы lnve-ntory. отображmnroе Б окне укээав:ного эл.емевта ИН1·~рфеЙса.
Если paCC:»OTPeтl:! програм;мный 1ФД ддя соб:IolТйя Ь.оаа вашей формы. вы ООН'8.РУ­
Жйте. что элемент управления ЗfiD1:iлН'SrетСJI 1[; IlОМОlЦЪЮ строки nрограмм:нО{'О кода.
в~делеI-IlЩЙ: здесь nодужиркым :шрифтом.

public ;paт t i a]: ICLa,ss Mai nFo r m : Fbrtn


{
ji\ublic Mair'l.Fo ri'(1 (/
'{
:Li1 i !: i ;;il i 1.e CQmpQ o,e I! t {);
1004 Часть IV. Программирование с помощью библиотек .NET
l
private void MainForm_Load(object sender, EventArgs е)
{
// ТО DО : ~hi s l ine of code loads dat a into
/ / the I carsDa taSet. I штепtоrу ' table.
11 Уои сап move , or remove it, as needed .
this. inventoryT&bleAdapter.Fill(this.carsDataSet. Inventory) ;

Чтобы понять, что делает эта строка программНОI'о кода, мы должны сначала
выяснить роль строго ТШIИзованных объектов DataSet.

Рис. 22.23. Выбор таблицы Inventoгy

Строго типизованные объекты DataSet


Строго типизованные объекты DataSet (как и подразумевает их название) по­
зволяют взаимодействовать с внутренними таблицами объектов DataSet. исполь­
зуя для этого специальные свойства, методы и события базы данных. а не обоб­
щенное свойство Tables . Выбрав ViewC:>Class View из меню в Visual Stud10 2005,
вы увидите. что мастер создал новый тип CarsDataSet. полученный из DataSet.
кан видно из рис. 22.24, этот тип класса определяет ряд членов, позволяющих вы­
брать, изменить или обновить содержимое.
При вьmолнении мастером своей задачи он помещает в файл *.Designer.cs
член-переменную типа CarsData Set (именно этот член используется для события
Load формы).

partial c lass MainForm


{

private CarsDataSet carsDataSet;


Глава 22. дDСТУП ~ баЗIIМ данных с noмощьЮ ADO.NET 1005

<5d'dJ> • u '"
"" ~ w~"biAci:eu
'tJ са Prajed Rl!feo_e.

Э·~· ;С7:.
~ <~ CwSDa~t.~t4т.ыe
~ ~ CII"JOII~t.~VCI:!IQrr'l!ow
~,' ~ Qll'lDat.Sebln;..entor~~~\re!Ot
rliI .' c.rf[l.r.as.~lnv~Ior~IIE.tn!l'lll1(t..
liiI · ~ ..'
~~ ~Im
t,;; О WIlW«'''ed»Iit~,CМJt)Jl~tl'ТeЫeAc.oter.
,., lt Wj~~,~
-.,.: . "'~~"'=-~' ~~. ·_;.:'-"·... "'~:r.""::.. ~. :~ :--";::",,;..';. . :,1: ~, • ..
~ C.vlOllt8$e!O ,р(;
ISI C"_ _ Ia$et(s~~е . sen.Jil.JIoo' . SerIIIldoo'l!r\fo. $~\UI _.
·... 0-0
\ ~, G.~6~".ыeO.
.., Getт\'ре~Se~vttoem"xmI,sa-;)С:I\ISohlm!lSe.l)
[; )~- -

Рис. '22 ..24. Строго типиiэова~н"н.JЙ оlSъв~т DataSet

Автома,тически гене' рируемый компонент AaHHIaIX


ВДОПОЛН~R строго 1"иnизованному объt:R'Гy Dа:tа5эt, мастер rенерп.рует ЕОМ·
штент данных (В данном сдучае е 'Именем InventorYTabIeAdapter). IщRanсулиру­
ЮЩИЙ соответствующее соединеНJ:iJCi, !1Дй.ТlТер дaн1iых и оli-ьекты КОМЩIД. которые
исполъэуютс·я .при азВlЦ.fодеЙствии с таблйц~й lnventory.
ptl.bHc partial 'c lass lnVE!TltoryTa.blei\dapter
:System. Соmролеnт.r.М"Qdel •.Соmроnвлt

11 nOn,Jl Д8JПNX ДmI ,J:Ioc,""y13 10 ,l(aКIUDI.


pri vate sуз! etn. D",·t:в:. SqJ, C1ierrt. Sq,lDa t&1I,x;j,apt. er !1\_"'d·арtец
pt.1. va:t а .9уз tem. Ра ta. SqlClient "SqlCO.J:l11e.c t t 01J trI_ cOI1nectiocl;
pl i v-ate sузtеm. Daita. SqlClient. Sql CotmmanQ t J щ_ CO.ll\IТ\andCallect l on:

Также Э1"ОТКОМnОllент определяет ПОЛЬЗО1Ш1"eJtЬCюrе методы F1 11 () и up:d~ te 1) ,


){aCТPOeRНыe ;на работу с вашим о!h,ектом 'ОаrаDаtаSэlL. в.дОПdJIНенйе К мнаже·
~ членов,. иt::ЕOIIJbЗYемЪ1Х для в~.тавRИ. обновлени". и удаления строк внутренней
Тlll5л:lU1Ь1 Invent.ory. 8аиН't'ере.сованные чи:rатeJU1 MOryт ШIМОQТОllТ~О выяснить
д~та.ли ре~заЦ1Ц!l каждого из Э'1'ИХ членов. При зтФм можно надеяТhСН. 'ЧТО по­
сле· всех УCИJ1ИЙ. ,Щ/торые.:вы заТратили на ос.воt:ние ма~риала этой l'Лавы. СООТ­
ветст'вуюП1.ИЙ :пим членам nРО.rpаммНЫЙ КОД не ДОJJже» 1Ш~аться вам совершенно
неЭНaRОмдrм.

за ..... ч.Ни •. Бfll1l!wе 06 об"'ЬеI('fНОЙ МОАел~ ADO.NEr. з T8~ О соотвеТСТSУI9ЩИ~ мастерах Visual'
Studio 20051:abl y~a~Je ИЗ I<I1!1rJ.1 Сахила Малик~. Microsoft АОО. NБ7' 2. О для nрофесGионалов
(l'1ер. с QI>IГЛ, ид "в\'1лыi1&'\\ 2006 г,}.

J
1006 Часть IV. Программирование с помОЩью библиотек .NET
т
Резюме
ADO.NET является новой технологией доступа к данным, специально разра­
ботанной с учетом несвязных многоуровневых приложениЙ. Пространство имен
System.Data содержит большинство базовых типов, которые могут потребоваться
для программного взаимодействия со строками, столбцами. таблицами и представ­
лениями. при изучении материала главы вы могли убедиться в том, что в составе
дистрибутива .NEТ предлагается множество поставщиков данных. позволяющих
использовать как связный. так и несвязный уровни ADO.NEr.
Используя объекты соединения, объекты команд и объекты чтения данных
связного уровня, БЫ можете выбирать, обновлять, вставлять и удалять записи.
Объекты команд поддерживают внутреннюю коллекцию параметров, которую
можно использовать для повышения типовой безопасности SQL-запросов и кото­
рая оказьmается очень полезной при запуске хранимых проце.цур.
Основным объектом несвязного уровня является DataSet. Этот тип являет­
Ся размещенным в памяти "представителем" любого числа таблиц и любого чис­
ла их необязательных взаимосвязей, ограничений и условий. "Красота" создания
отношений на базе локальных таблиц заключается в том, что вы получаете воз­
можность программно исследовать ИХ, Отключившись от удаленного хранилища

данных .

Здесь же была рассмотрена роль адаптера данных. Используя соответствующий


тип (и соответствующие свойства SelectCommand, InsertCommand, UpdateCommand
иDeleteCommand). вы получаете возможность согласовывать изменения данных в
DataSet с оригинальным хранилищем данных.
1
ЧАСТЬ V
Web- приложения
и Web-сервисы XМL

I этой части..-,
Гл,ава 23. Web-еrраницы и Web-элементы упра!3ленияASР.NЕТ 2.0
Глава 24. Web-прило-жения ASP.NET 2.0
Глава 25. Web--с'ервисы XML
1
i

ГЛАВА 23
Web-страницы
и web-элементыI
управления ASP.NEТ 2.0

Д о сих пор все примерыприло)!(ений в -этой кн~гe !(аеались Ш>НСОЛЬНЫХ при­


. ложений и npиложений Windows Forms. В втой rnаве и далее мы выясним.
RaЮIМ образом платформа .N:E'Г ynpощаеl' за,да-чу построении приложений о интер­
фейсом на OCROBC бр~зера. Но сна..чала мы обсудим ряд мючезых для: Web поня­
тий [таних.. ЕМ НТI'P. HTML, 01l6нариn КJnteH1'a И сервера). а Тa.IoJre рмь Web-cepвe­
ра (ВКJIIОЧaJ1 сервер разработки ASENEY, WebDev.WebServer.exe).
На осяоэе полученной информации в оставwеt1ся части тоы мы сконцентри­
руемся fofQ комnо.неитэхАSР.NЕТ (ВКJIючая усовершенствованную MoдeJtЪ страви­
щ)] е внеmни.'I\f ХОДоМ поддержки) и на иCIIОJIЪЭОВании Web-элементов ynразлени.в:
ASP.NE~, Вы увидите, что ASP.NET 2 . 0 предлагает целый ряд. НОВЫХ элеменroв
управления. новую моделъ "шаблона" страницы и новые воаможиост-и настройки
WеЬ·страниц.

РоnьНПР
Wr!Ъ-ЦРЦil0жеНИl'J Qче5Ь сМьно оТЛRЧ8ЮТСЯ от традиц~оfP'JЫХ лриложений для
~асто.щ~rIЫX СJЩтем. Первым очевидюам О'Iщtчцем является то. -.пф .цюбое реалрнОе
Web-ЦpIOIожeцJtе преД!fола:гse-r IIспользоваяи-е, ~aK минимум:. двух соед~неfщы,х в
сеть,},jJEI.ПJИR (ИOlJеЧ}-IO, при разработке n,риложения ВПCIЛне возможно, ч.тобы рс;mи
нлиецта и c~pBepa ДТ'рr;ще. ОДf1а маш~на), 3Щ1ейст:воваННЬЦ1 машины должиысог.'1а­
совать ИРnOlЛ>Зовrom-е оnpеделсщнoro Сет.евого протокола ддн усдешного осущест­
ВЛ~НЩI i:YrrJ:равщ.J и приема д~. Сетевым дротокмом, соед~нщощим номлъю­
TIW~ .в рассматриваемом J'IВМИ ещучsе. ЯВЛЯ~СЯ щрото.кОд НТГР (Hypertext Transfer
Protocol- протокол передачи гищрте:кста).
Когда М8ШИFlQ-1tлиентзаПУС1iает Web---браузер ITaнo~. как Net$cape Navigator,
:М-оzШа F1~.fщ или MicrGsoft Irltemet EJj:pIQrer), rенерируетса}--f'I1"P-зsnрое до.отупа
Ц кшшрt!тиом:у ресурсу (ИiЩри.м:ер, к файлу ",аsрх или *.htm) Ка удален~ой ма­
шпнс-се,рвере. ПрQтОRОЛ НПР- это текстовый IJРОТОТiОЛ, построеННЫЙ на стаН­
дартноЙ l1sр<щи:гме запросов и ответов. Нацример. ;при обращеl-JИЩ Rh t tp:./ /www •
Iпtе-rtес:hТr~iпifjg, COJ'1"l дрограммное обеср-€чение браузера использует WeЬ~T.eX-
1010 Часть У. Web-приложения и Web-сервисы XML

нологию, называемую сервисом DNS (Domain Name ServJce - служба имен доме­
нов), которая позволяет npевратить зарегистрированный адрес URL в 4-байтовое
(32-разрядное) числовое значение (назьmаемое lP-адресом). После этого браузер от­
крывает сокет (обычно через порт с номером 80) и посьmает НТТР-запрос страни­
це, используемой Web-уэлом bttp:/ /www.IпtеrtесhТrаiпiпg.соmпоумолчанию.
Осуществляющий хостинг Web-сервер получает поступающий НТГР-запрос,
и указанный в запросе ресурс может содержать программную логику, способную
про читать значения, введенные клиентом (например. в окне текстового блока).
чтобы сформировать НТГР-ответ. Разработчин: Web-программы может использо­
вать шобые технологии (CGI, ASP, ASP.NEТ, сервлетыJаvа и т.д.), чтобы динами­
чески генерировать содержимое Н1ТР-ответа. Затем браузер клиента отображает
НТМL-код. полученный от Web-сервера. На рис. 23.1 показана общая схема цикла
запросов-ответов НТГР.

Web-cep8ep
брауэер клиента Входящий
НТТР-запрос Web-приложение
Визуализация
HTML-коАа, полученного Исходящий (любые ресурсы сервера,
из НТТР-ответа НТТР-ответ файлы
',aspx, *,asp и ',htm)

Рис. 23.1. Цикл запросов и ответов нттр

Другой особенностью Web-разработки, заметно отличающей ее от программи­


рования традиционньп( приложений, оказьmается то. что протокол НТГР является
сетевым протоколом, не сохраняющим состояние. Как только Web-сервер отправ­
ляет ответ клиенту. вся информация о предыдущем взаимодействии он:азывается
UзабытоЙ". Поэтому на вас, как Web-разработчика, возлагается задача принятия
специальных мер, обеспе"lИВающих: "запоминание" соответствующей информации
о юпrентах, которые в настоящий момент он:азываются зарегистрированными на
вашем узле (такой информацией может быть, например, список товаров в корзи­
не покупателя). В следующей главе вы сможете убедиться в том, что ASP.NEТ обе­
спечивает целый ряд способов обработки состояния, причем как стандартных для
всех WеЬ-платформ (это сеансовые переменные, файлы сооюе и переменные при­
ложенил), так и новых (визуальные состояния, состояния элементов и кэш).

Web-приложения и Web-серsерbI
Под Web-nрuложеНllем можно понимать коллекцию файлов (".htm, "'.азр,
*. азрх, файлы изображений и т,д,) и связанных компонентов (например, таких как
библиоте1Са программно,о кода .NEТ), хранимых в отдельном семействе каталогов
на данном WelJ-сервере. Как будет показано в главе 24, WеЬ·приложения имеют
специфический ЦИЮl существования и поддерживают множество специальных со­
бытий (например, события начальной загрузки и окончательного завершения ра­
боты), н:оторые вы можете обработать.
1I
,

ГЛ&8'<! 23. Web-Сiра~JИЦJ>1 и Web-ЗJ16I11енrы УfJравЛениЯ ASP. NIТ 2,0 1011


Web-cepaep- это проrраммн:ый ЛРО.lO''"J(т, обеспечивающиfj хостивг длн ВaIIIИX
Web-ПРИЛОЖItНИЙ. lit. как правило, tIрБДДЩ'аюЩИ'Й цe;.rrьUJ ряд СQnYТСТВУЮЩйХ сер­
висов. таких !Щи. :например. интerpированные службы 6еаопасноСТИ. подДержка
FТP (File n.an.sfet Protoco] - ПР()ТQКО-Л .ререда-чи файлов), с;.цу-Atбы обмена nОЧlЮвьt­
М:И сооБЩениями и т.д. Web- сервером проиэводcrвеНlщго уровня является се.рвер
119 (Iпtеrпеt Infoгmatlon Server - ИНфОр1'4QЦИОНны.й сервер Интернет) от Мlсгшюft.
который. как вы мажете ДОГЩЩJЪCЯ. npeдnага;ет ВВУТреJ;ПЦОЮ поддержку и ~ЕЛасси­
ческйХ" Web-npиложений ASP. и Web-npиложеroIЙ' ASP.NEт.
При создаНЩJ Web-npwюжеюrn ASP.NE.l· ВЩi1 дотреруетС'я взam.ЮдtШствие с IIS.
,
И эдесь важно подчеркнуть. ЧТСI еервер JlS по умолчанюо при УСТaнmiItеWiпdоwэ
I,. Server 2003 Й'ли
WindOW9 ХР Profession,al Editlon не ~crtLQ}t(1ВJШ.Ваеmся [а среда
"

Windows ХР Ноше Ed.itiot1 rlO.rr.дeРЖЕУ 11S '}Je пр~arает вообще} . Пuэтому. в Зави­
симости Ьт конфиrурации еашdJ мцшин:ы разработки, вам. ВQ3Можно. придется
установить 118 вручную, для этого откройте O1Ц;IP Установка 1>1 удаление программ
(Add/RemO\!e Рщgram). из пащш Панель у.правления (~Jitr:o] Рапеl) и выf:I.ерите в нем
Уст,ановк-а КОМПОFlентов Windows (1\dd/R:emove Wiпdоw_s C6mponents},

ЗамеЧ8име. Сервер 115 лучшеустаl:Ю5J.1ТЬ до УСТЦt1Овки .NEТ FГarnework. ~J1И yOTaHoвmъ 118 после
У{;Т~НОВJЩ . NEТ
'Framework, тО Web-арWlО~6I!ИЯ АЭР.NЕТ не будУТ выполняться J(oppeJqJ,tQ (B~
увидите тОлько пу~тые cтpaH~Цb') . 1( счастью. можно настроить 11$ на щrддержку .NП-припоже-
ниiit с ПОМОЩЬЮ заПуQk<!!. УТИЛИТЫ KOMaJ-IДI:fОЙ строки .as:p net regi i$_ еХе {о флагом /i~.

в npеДnОJIожеии:ио том. ЧТО 1:Щ вашей рабоч.ei1 'Ста:Б:Ц:Щ! сервер пs установлен


ДШJЖI-WIМ образом, вЫ сможете взаимодействовать с ПЭ {oIЭ пашщ Администрирование.
(размещенной в паIlliе П~нель управления). Дл~ работЬ] с материалом этой rла­
JШI нас бум-:г инт.ересоватъ т.ОЛЬ1\О узел Web-уэел по умол-ч;знию' (DefauJt Weh SIte).
рис. 23.2..

---- - -~--- , _ :..~ - - ,

~ !~nel rпtЪ'~n 5er~ ~,,' I'II!I 1пуп. .~ ~


!Н! ..8,S!ИR (~~~ep) ·i;:' . • ·П5Н~ <:,\w..oow,;\heIp\r..heIp
со ' !2а IIгбс-У'f1bl . :"!"ICl~_cfier:t
с ". ' I'Афpt4Q'Щi !!'IJ,~lp.фf
" ПSНsIp - l!J JisЩ!rt.а,~
~ D~t_diCnt
;i:i.'i if-t,a. ' Aw""-:<'QII~ ~...~ rм \I...,.b!.i.... ~ *' .i ~- ~-» .;.;.. '~~~~~~_4H )-
--,
" .
,
'" ~ -

Рис. 23.2. 01(1'10 оонастки 118

Работа с виртуальныии катал'огами ItS


Одна инста:ЛЩIЦЩ'I IIS способна абслущнваl'Ь :множес1'ВО Wеh-приложений, каж­
дое йз вoтopъn:: размещается в своем ВlLргrtУQЛън.ам кamaлО2е. Каждый виртуаль­
ВЫИ катало. проец:ируетея в фиэичеCtЩЙ ЩIТaJIОГ яа лов:алыroм Жecl1<оМ диске.
Тап. есл:Н вы Сo:iщадfIте' новый .Бf1]пуаД1!н::ыi1 катащоr с именем Ca x sRUs. внешние
поЛЬэоватe.!tИ смогут про сматривать этот узел, иlmолъзуя. нanрим~р. адрес URL
http .: J Iw-w·w. Саr~R-rJ. s, сom {вnpeДt10ЛQжении о ТОМ, что iP-адрес BaiIJe.o узла имеет
1012 Часть V. Web-приложения и Web-сервисы XML

всемирную регистрацию). В фОНОВОМ режиме виртуальный каталог отображается


в соответствующий физический корневой каталOl~ например. в C:\inetpub\www-
root\AspNetCarsSite, в котором находится содержимое WеЬ-приложенин.
При создании Web-приложения ASP.NET с помощью Visual StudJo 2005 вы имее­
те возможность генерировать новый ВИР1уалЫiЫЙ Ii:аталог для текущего WеЬ-узла.
Вы также можете создать lШРТУальный каталог вручную. Для примера предпо­
ложим, что нам нужно создать простое Web-rrриложение с именем Cars. Первым
шагОМ при этом оказывается создание на машине новuй пarщи (например, папки
C:\CodeTests\CarsWebSite). которая будет содержать RОЛЛСIЩИЮ файлов. компо­
нующих новый узел.
Затем нужно создать новый виртуальный каталОГ ДЩI узла Cdrs. Просто щеJJ­
Rните в окне IIS правой кнопкой мыши В стране Веб-узел по умолчанию и выбе­
рите СоздатьС:>Виртуальный каталог из появившегося контекстного меню. Будет за­
пущен интегрированный мастер создания виртуильных Itaталогов. Перейдите от
окна приветствия к следующему ОЮJУ и Уfшжите ДЛЯ ВЩllего Web-узла ПОДХОДЯЩИЙ
псевдоним (Cars). Далее вас попросят указать фиаическую пarmy на жестком дис­
ке, которая содержит файлы и изображения. ИСПОJIЬ:1уемые ДЛЯ этого у~ла (ДЛЯ на­
шего примера зто ПanRа C:\Code'I'ests\Car:swebSite).
На заключительном шаге мастер запрашивает информацию о правах доступа
R новому виртуальному каталогу (разрешение доступа н файлам для <{тении/запи­
си. обзора файлов с ПОМОЩыо Web-брауаера, ааnyска выполняемых файлов и т.д.),
Для HauIero примера вполне rюдоЛ:дет вариант выбора, предлагаемый мастером по
умолчанию (тем более, что вы в любое время можете иэменить эти настройки. от­
крыв окно свойств с помощью щелчн:а правой RIlОПКОЙ мыши В любом иа окон, ин­
тегрированных в ПS). По зааершснии работы мастера вы увидите В окне IlЭ НОВЫЙ
виртуальный каталог [рис. 23.3).

I<ожсм. ~ ем.. C~.

"f "... .•
~IFЖГх ~ ~ ~ [f .~

Р .. с. 23.3. Виртуальный каталог Car:s

Сервер разработки ASP,NET 2.0


Комплект поставки ASP.NET 2.0 содержит "облегченную" версию Web-сервера
под названием W(ЭЬDеv.WеЬSеrvеr.ехе. Эта утилита позволяет разработчику осу­
ществлять хостинг Web-приложений ASP.NET 2.0 за границами llS. С помощью
этого инструмента вы можете строить и проверя'rЬ Wеl)-страницы из любого ката­
лога на своей машине (что очень удобно при разраБОТI(t' сценариев в грутше раэра-
1 j
Глава п lNeb-страницы и W,eb-ЭJ1(!меНТЬi упрамения ASP, NEТ 2.0 101 3
~oтчИRОВ И при создании Web-npоrрамм A8P.NEТ 2.. 0 в среде ОС W.indoW5 ХР Ноте
Edition. которая не подДерживает nS).

З~е.чание. Сервер Wel)De-v.WеЬ3!;!rv еr.е х е Непьзя ИСnРЛЪ3ЩI3Т.Ь ,ДЛ!\ теСТ'1Р'ОВ6I'iИЯ "КJ1acclit­


ЧОСI<~х" ·Web-l1РИ11ОжениЙ ASP.

Лри построении 'W"el)-yaпa с помощьюVisшU Studio2005 ItЫ име~:.rе Щ>3МО:llCFlОСТЬ


исп~зова~ь. Wer-JDеv .-Wе.ЬS еrvеr . ехе ДЛЯ обслуживания со~азаемых <:Tpaнnц. Но
вы также имеете ВОЗМОЖiЮСТЬ в заимодейс,твовать С эmм инструм:ентО'М ВРУ'ПryIO
'Из RО!\ЩНЩ1!)Й строки .NF:r. Если ввес!fИ КОмандУ

WebDev.WehS e r ver.ex ~ - ?

вы УВ»ДИте окно сообщен:и:я.. в КQТop~ будут ОЩIсаны действительные опции 1<0-


МilНД1'tой с-троки. В сущности. вам ВУ'Ато ~азать неиспользуемь1Й порт С помощью
опфnJ /part ~ . корневой каталог Web-npилежещш с ПОМОЩЬЮ опции J pa'th : 11 ке-­
обязательный вирlyалы·Iый путь с ПОМОЩЬЮ ОUЦ.IЩ !vpatb : lес.r'lи BbJ не укажете
зваченйе !vpa't11:. цо умолчанию исшmъзуетс.н значение /), РассмотриМ медую­
щ-.ш 'I:фИмер.

WebDew . We:bServer , ехе / por·t: 12] 4 5 /path: " С : \ С о dеТ еSLэ\Са :r s We,bS i t e ""

Пощrе ввода этой -команды вы можете запустить свой люБИМ1>IЙ Web-бр~узер


для зацроса соответствуюш;их страниц. Так, если в папке СагsWеЬ S l t г содержится
файл <: :цr.reием MyP,ag,e ,as p x,Bbl можете 13Becrn сле.цующиЙ <Щрес uщ..

h t tp; / /lo c aH,QS':; i2 345!C" rsWe:bSite!MyPage . a's.px

Во многих цримерах Из ЭТОЙ:$i сдедУЮЩ~Й rnзв, We1DDev .We b8e:rv,er . ехе будет ис­
ПОЛЪЭ(!lВа:гься через Vlsual Studl0 2005. 'Сде.щует учитыватъ то, что этот Web-серве~
не предна.эна"Iен дЛfl ХОСтИЮ-8 Web-пр'ИложеI-ll!l.й; проиющдственно'Го уровня. Он
npедна.'ша'lен иcw.tЮЧИТедьно ДЩ:I целей разработки и тестирования .

Замечание. Пр~.ект МЬпо (см. тл~ву 1) f1редлагает беопnатное расШ\llр~ние ASP.NCГ ДЛ!'! Web-cep-
вера Apaohe. За б0лее ПDдроБНой }1НфОРМElцией обратитесь по адресу:
h ttp,_: ! /www·. ffi9л о-рr оj'е.сt , СQJП /АSР'. NE.T

Роль HTML
Сконфигурировав I<атЩJОГ длп ~Boeгo Web--ыриложеНин. вы ДОЛЖНЫ создать и
его содержимое. Напомним, что WеЬmРШtDженue-
' это просто теРМШI, ИСII()ЛЬЗУ­
емый для o{iознач~ния множества фаj{лов, обеспечиваю'-ЦИХ фуmщионировайие
узпа. ЭнаЧИ'I'eJJЬнав часть этщ файлов будет tюдержать €инта1tсические леl~Семы.
оцредел:еgные lJ рамкщс HТМL (RyPertext Markup Language - языiranертекстово.й
раз:метI<И) . ffI"МL - это етандартць!й fI3blR, используемый ДЛЯ описания того. lUU<
.в ОЮiе браузера юrи~та долж,:,щ В;ЬШОЛНЯТЬСfI визуалиаация БУI<Валъного текста.
изображений. вне1IПЩX С(.':blЛОК ~ р~ли"lных элементОВ графического интерфейса,
Этот специaJ1ЬИЫЙ аСПеКТ WеЪ--рззрабо:I'Ю:I ЯВШieТСЯ ОД1l0Й из главных UрИЧИн
столь распространенной }fеJJюбви ПрОГраъ!МИСтов . }(Q1'ОРУЮ они испытывают Е
раэраБОТI<е Web-протрамм. и ~ОТИ cOBpeмeннЪie средства Web-разр.аБОТЮI {ВКЛlО-

j
1014 Часть V. Web-приложения и WеЬ-сервисы XML

чая Visual Studio 2005) и платформы (такие как ASP.NEГ) генерируют большинство
НТМlrкода автоматически, сегодня для успешной работы с ASP.NEТ все еще ВЮЩIО
хорото понимать этот язык. Данный раздел, конечно же. ни в коей мере ие пре­
тендует на охват всех аспектов НТМL, но давайте рассмотрим основные.

Структура HTML-Аокумента
Файл НТМL состоит из множества дескрипторов, описывающих представление
данной Web-страницы. l{aн и следует ожидать, базовая структура любого HТМL-дo­
кумента примерно одинакова. Например, файлы *. htm (ИJПl, альтернативно, файлы
*.htшl) открываются и закрываются дескрипторами <html> и </html>. обычно в
них определяется раздел <body> и т.д. Следует иметь в виду. что HmL не чувстви­
телен к реtистру символов. Позтому для браузера <HTML>, <html> и <1-Itшl> оказы­
ваются идентичными.

Для демонстрации некоторых базовых возможностей HTМL откройте Visual


Studio 2005, создайте пустой НТМL-файл, выбрав Filec;. Newc;. File из меню. и сохра­
ните этот файл под именем default.htm в каталоге С :\Code'fests\CarsWebSite.
Наша исходная разметка весьма незамысловата.

<html>
<body>
</body>
</html>
Дескрипторы <html> и </htшl> используются для обозначения начала и конца
документа. Как вы можете догадаться, Web-браузер использует эти дескрипторы.
чтобы выяснить, с какого места следует начать и где следует ЗЮiOнчить обработку
при знаков форматирования. указанных в rnавной части документа. Почти все со­
держимое документа определяется в paMI<ax дескриптора <body>. Чтобы немното
"ОЖИВИТЬ" страницу, определите ее заголовок так. как показано ниже.

<html>
<head>
<title>Web-страиица Cars</title>
</head>
<body>
</bady>
</html>
Вы. наверное, догадались, что дескрипторы <title> ИСПОЛЬЗУЮТСЯ для обозна­
чения текстовой строки, RОТОРая при вызове этой страницы должна размещаться
в строке заголовка окна WеЬ-браузера.

Разработка НТМL-формы
Реальное действие в файле * .htш происходит в рамках злементов <fоrш>. IП'МL­
формn- это просто именованная грутша связанных элементов пользовательского ин­
терфейса, используемых для сбора данных пользовательского ввода, которые затем
передаются Web-приложению по протоколу H~ Не следует путать НТМL-форму со
всей областью окна браузера. Фактически НТМL-форма представляет собой логиче­
ское объединение элементов, размещенных между дескрипторами <form> и </fоrш>.
1
i

,лава 23. W!!Ь,~траниЦЫ и Web-элементы уrrравления AS'P.!ltEТ2.0 1015


<hl!n.l :;.
<head>
<tit le>WeJ:>-С!!'l"'>аl-l.I1Цоа Car$ < / t i t l ео>
< /heiHi:>
<b6dy>
<torrn id="de:fqultPage' [),:1m",="debl1Jl tPi1ge">
<! -- МeC!IJO ,ttllЯ" Web-со;цер_аго ->
<lfот.ш>
<-!tod:}':>
<Л,иnl>

ДЛЯ id и пате этой ФОРМЪt указано 3Н8чение defaultPage. Raк правило. от­
крывающий деСКJЛillТОр <form> задает также атрибут acti оп. указывающий адрес
URL. по которому сд.е.цуе'f передать данные формы, и метод переда qц этих да.нных
{POST или GET). Эrr-й ВО3МQЖН.ости дескриптора <.fo!'m> мы рассмотрим чуть позже .
ПQка что давайте БЫЯСЛИМ.RaIO:(е элементы могут раамещ,аn,сщ Б НТЩ-форме.
в панели инструмент'ОВ Visual Studio 2005 предлагается специальный раздел HTML.
в катаром сrpyпnированы tвя:.;Шнные с НТМL эдемеllты управлеlffiЯ (рис. 23.4).

ltjj~"И~~~:::1"~
о IrP.t, (~) '~
~ ]фJt (А-) ~
c ..

~ Jф.i:(SobQt.j "~
~ inI!<t (T~)
~Щ!Ut (~J
~ Jt,put (Pмswor<1)
o 1npur (Q1ecl!tioxJ
Ф lnput(Roc8o)
~ ,i; 1npш (1'~)

;~f::~~.~~~;-- - ~
Рис . 23.4. Раздел I-IТML в окне панеflи инструмеюоs

Создание пользовательского интерФ'ейса на базе HTML


Перед Дtilба.вдение,м lfI'1\1L-э~ме~то!'l в НТМL-форму ва,жно заметить. что Visu.al
StudJ.o 200ffi ПО3Dоляетредактирова:ть содержимое ф~ЙЛО!а i< .1уjc П1 с помощью ин­
теrриро:вщUIОГО НТМL-peдaK)'opa и окна свойств. Цри выборе DOCLIMENT Е ClffI:Ю
СВЬЙСТВ (рис. 23.5) вы получаете возмощность наСТРl)й~r ряда параметров HТМL­
страНИЩ>I. J-:rщrp~ер цвета ее фона.
Тепе.р~ из""ените раздел <!:юqу > фajiдаqеfаtJl t .lt"jLm так. Ч'J'оffiы отобразить
пользова.теJЩ> I1риглашение ввести ИЩl '8. паРQЛЬ . и; устаr-ювите ДЛЯ фона '1'ОТ $ет.
}<оторый J3ЭМ нравится (ВЫ может.е вводить и формаТJoJр~ша:ть теКСТОlюе содерЖимQe
Henocpeдeтв~o в окне lfГМL-редактора),

<ьt.шl:>
<h .e'ad>
<1.1 tJ e >Web -С'l"рающз\": аr~<1 t i tle>
< / Ь,е-а.d>

J
1016 Часть V. Web-приложеfiИR и Web-сервисы XML

<bod y bgcolor="Nav ajo Wh ite" >


<!-- Приглашение ввода для поnьзователя-->
<h l align="cen·t er " >
С т раница входа в сис те му Cars</hl>
<р align="center" > <br >
Вв е дите <i>имя поль зо ва';Г еля< I i > и < i>п а'РОJl ь< l i > . < /р>
<form id="defaultPage " n a me = "defaultPage" >
< /form>
</bo dy>
</html>

-
P,op"rl,t'S
.- -- - ~- . -

8ackgr'ound
~a"ajoWhite !d
8gPropertie$
BottomМ"'gin

Chor$et
'.-
о
a...._---
s.
'. _~ ,_.-
_ _ ....,. ,. _ ,,_ ._-
.,. _

lIQCoIor
Document bod<q"'Jnd сo!or.

Рис. 23.5. Редактирование HTML-Аокумента


средствами VS .NET

Теперь давайте построим саму НТМL-форму. Вообще говоря. каждый НТМL-эле­


мент описывается с помощью атрибута паmе (соотэетствующее значение исполь­
зуется для программной идентификации элемента) и атрибута type (это значение
задает вид элемента интерфейса. который вы хотите поместить в рамки деклара­
ции <form». В зависимости от того. с каким элементом интерфейса вы работаете.
в OI~He свойств появляются дополнительные атрибуты. при сущие данному конкрет­
ному элементу.

Пользовательский интерфейс. который мы собираемся здесь построить . будет


содержать два текстовых поля ввода (одно из KOTOPbLX- типа Password) и два кно­
почных типа (один ДЛЯ подачи запроса с данными формы, а другой - для сброса
данных формы в значения по умолчанию).

<!-- Построение фориы для получения информации от пользователя -->


<form name="defaultPage" id= "defaultPage" >
< Р аligп="сепtеr" > ИМR п ольз() вателя:
<input id="txtU serName " type="text" N AМE= "txt LJserName "><!P>
<Р аligп="сепtеr">Пароnь:
<inpu t name ="txt Pa ssword " type="password" ID="txt Pa.sslflo rd">< / P>
<Рalign="center">
<i nput name="btnS ubmit" type="submit" vаl u е= " ОтпраВ!I)ТЬ " I D="btnSllbmit" >
< input name="btnReset" type="reset" v а lu е=" Сброс" I D= "btnRes et" >
</ form>
1
Глава 2'3, WеЬ~'СТР:i\1ИЦЫ и WеЬ·зщщеliТЫ управления ASP:NEТ 2,0 1017
Обра:rцт€ Щiимание, на Т О . ЧТО' здесь АЛЯ 1Щ')1ц(ого:mеме:нта ваЗНj:l'Чен},) 0;0-
otbe'-,СТВУ1(Щ1i<rt ,3щtчеuия л ""mе :и id ( t )\tUS'(~r;Narne, tУ.tРаэ 'swоrd. btnS'ubJI1'i t ~
1;H.nRes€'t1, Еще более ваЖНQ ТО, что IWЖДЫЙ элементвводаJpdеет ДОUQЩIИТ€дbНЫЙ'
атрибут type, :&оторыи ясно I1.деНТИфИЦ}iрует Щ КзR ВЛОЩiе опредeлeнш.rе эдемен­
Трl Dоль:;юва1'е.[lliСНЩ·О интерфеi;rса. Например. type = r'r~~et" ук!'lзывет на@­
ТOJ'.ЩТИ~lееl(JЮ очи(;тну всех полей формы и лрисвоение им щ,Iч6льных 3f:щчений,
t.ype ="pas" 'WQrd" - Мi;lСI:tИРОI3ц,m1ЫЙ I;!ВОД uароля. а Еуре '" "subJt\it" - ОТДРaIЩУ
да:иньщ формыI Iюлучателю, На рщ~. 23,6 I;IOfЩЗR.F:I НЦД I1рлуч~шейс::я: стрш-щцы.

~ i:' _

,~траница входа в CII'C'feмy Can

- _._- - - - - ~- __ О!

ИМЯ ДС ЛFЗQВII:rеля: ,

-~.

г IЭmр;;D~Т;' j 1 РБРDt: j

Рис.2;3.6 , ,ИСJ<ОДНЫЙ вид страницы, оtщраненfI'ОЙ 'в фаиле 'd efa u 1 t.. h'tI!1

Роль сценар,иевкл,иента
Дroщый файл ~. )-Y't !111\южет содер~\'юъ блон кода сцtЩаJ;>}{J'!, Щ1ТОРЫЙ буд~т ПDме·
Щ~JJ !=I ответный j10TOj't и обра:ботШ1 брауверам, запРQС~ШИМ ЭТОТ ПОТОК" Есть две
'гдавные ПРИТfИНЫ. ~10 IЮТОРЫ)I.J I:Iсаолъзуются <.'цеН~'Р1Jj:I ~eН'l'a.

• Пров-еРliа )10ЛЬЭ(Щ'i:trещ.(:jtОГQ В8ода Тtt>ред отправкоц дaННblX Web;?.epBepy.


• ВЭВИ!\ЮДСЙ<:!'I\иt' с моделью DOМ пелевQto браузера.

В о:гношешш nepBOTQ ayтtтa. сл~Ц)tет fЮliИММ:Ь, что Цнаедt:дствеююй" rrробде­


мой WеЬ·uриложеяи:Й ЯВЛЯе"''Тсянеобходим;осп. частых обращений н серверу (на­
зьmаемых :вторичными, обрюцenиямиj ДШI обно.елеюш НТМL·яод.а. 'Отображаемого
в окнебраузера. И хотя вторичных обрaп:tенnй DолНоетыо избежать НeJЦ,3Я. всm'­
да нужно стремиТЬСЯ МИПИН:I:Iзировать ce!'eBO~ обмеR. ОДНИМ -WJ подходов, умень·
шающих lt()ЛИЧ~ст.во ЦИ'1\:ДОВ сетевого обмена. ~яетсн ИСЦО.lU,ЗОВaRИ€ сценарnя
Юllw.нта для проверR-И пр.an-и.,'u.аl)(",и ШЩЬ301щ:тедьск.QГО .8Ij.ода перед отдра,в~ой
данных формы We.b-cepnepy. Если. е>бnaруЖй8а:ется оmпбка [нanример. не ук'd3ЩIЫ
данные)З QДJЮI\) йз 06пз;rгеды/ых полей) . МOЩI:lО ареДЛОЖVIТQ польвоватещо и.сцра­
вить ошиБJ<.-У. не посылая Jtah:tfl,!eWe:b-серв't'ру напрасно . [в,кщ:ще IЩlЩОВ, нцm-o не
раздражает l1D.1Jъ;ювателя бщIЬШ€. чем oтnp.<UJI:I<a .!m:a:ныx по медденно;й СВ5fЭ,И тодько
ДJШ ТОТО. Ч'rобы uолyчuть в OT!Jf'T совет исправить ошибки .ввода!)
В ДОПQ.t1Нецие .R проверке uЬJlЬзоватеllЬсКОГО ввода. сце1Цl.РИИ .клиента MOry'T
также ИСnШIЬЗ0ват~сн ДЛЯ ЦЗQ.Имьде,Йе"l'1ШЯ (' объе~ТНQ.ti! ,Мсщелью РОМ (Dосumепt
1018 Часть V. WеЬ·приложения и Web-сервисы XML

Object Model- объектная модель документов) браузера. Большинство коммерче­


ских браузеров предлагает множество объектов. которые можно использовать для
управления поведением браузера. IЛавным раздражающ.им фактором здесь явля­
ется то. что различные браузеры предлагают подобные. но не идентичные объект­
ные модели . Поэтому запущенный вами сценарий клиента. вза.имодеЙствующИЙ с
DОМ. может работать по-разному в разных браузерах.

3амечаниа. ASP. NEТ поддерживает свойство HttpReques t.8ro ws e r, которое позволяет в сре­
де выполнения определить возможностИ браузера, отправившего текущий запрос.

Имеется множество языков сценариев, которые могут использоваться для на­


писания программного кода сценариев клиента. Двумя наиболее популярными из
них являются VВScript и JаvаSсЛрt. Язьш VВScrlpt представляет собой подмноже­
ство языка программирования Visual Basic 6 .0. СледУет подчеркнуть. что Мiсrоsоft
[пtеrnеt. Explorer (IE) - это еДIШственный Web-браузер . .имеющий встроенную под­
держку VВSCrlpt клиента. Поэтому если вы хотите. чтобы ваши НТМL-страницы
работали корректно в любом коммерческом Web-браузере. для программной логи­
ки сценариев клиента лучше VВScript не использовать.
Другим популярным языком сценариев является JavaScrlpt. Здесь следУет под­
черкнуть. что JavaScrlpt никоим образом не является подмножество языка Java.
Хотя JavaScrlpt и Java имеют не сколько схожий синтаксис, JavaScrlpt нельзя без­
оговорочно отнести к семейству язьшов ООП, поэтому этот язык окаэывается дале ­
ко не таким мощным, как Java. Но здесь важно то. что все современные Web-брау­
зеры поддерживают JavaScript. что делает этот язык естественным кандидатом на
роль языка сценариев клиента.

3амечаниа. Чтобы еще больше усложнить ситуацию, напомним также о JScript . NEТ - упраll11яе­
мом языке программирования, с помощью которого , используя подобный сценариям синтак­
сис , можно строить компоновочные блоки .NEТ.

При мер сценария клиента


Чтобы продемонстрировать роль сценариев клиента. давайте выясним, как
можно выполнить перехват событий. посьmаемых HTML-элемента...r.1:И пользователь­
ского интерфейса клиента_ Предположим. что вы добавили в свою НТМL-страниду
de fault.htm тип Bu t to n (с именем btn He lp). которая должна предоставить поль­
зователю возможность увидеть информацию справки. Чтобы въmолнитъ перехват
события Cl i c k длн этой кношш. активизируйте окно просмотра НТМL и выберите
имя кнопки из левого раскрывающегося списка. Затем в правом раскрывающемся
списке выберите событие опс lick. В результате этого в определение атрибута но­
вого типа But t on будет добавлен атрибут on c li c k.
<inpu t i d =l btn He lp" t ype = " but t o n" v al u e="Help " l anguage=" j a vas cr ip t "
onclick=" return btnHelp_ onclick ()" / >
Visual Studio 2005 также создаст пустую фунrщию JavaSCript. которая будет вы­
зываться при щелчке пользователя на кнопке. Чтобы отобразить окно сообщения
клиента. нужно в пределах этой заглynrки просто использовать метод а lert () .
,
Глава n Web-г.траницы )11 WеЬ"'3лемен1'Ы управления: ASP,NEТ 2,0 1019
<script langUage=" j <;З)"аsс:riрt ц t}1:.·e = Iltext/ j s\J'ascript ">
<1--
.fH1'lction ЬtnИеlр_оnоli.С!kО i
аlеrt :(n Э;rо fie так уж TPYДo!'ip. Про ете, ще.JfRните на ~HOI'!Ke Отправить!" J;
</ scr iFt.>
Обратите 'ВВИМaШtlf: на ТО, что БЛБВ: сценария помещен в рамки HT'ML-IWммев­
l'ария «! -~ -->). Прич.и.Н8 этш() очень проста. Если BaI!Ia страница щ\'.аж.ется в
браузере. не поддерживающем Jav11Script. программньtй 1ЩД будет интерпретиро­
ван инк RОММtffi'IЭрий И ПОЭ1'6му npоиrн.орйрован. J(OlJew.o. JJОЗМОЖНОСт.и вашей
стрaнlЩЫ будут более узкими. но з-аТQ ваша страница не бу:д~т ПQJnlOСТЬЮ OT~e.p­
гнута браузеро:м:.

КОJfrРОЛЬ допустимости ВВОДИМЫХ данных


Тепf"'РЬ давайте добавим в нашу страниnу 'd efaul t. btm IQIИент~ noддерщку
ROнтроJIЯ BB~ в форму Д'аБ.НЫХ. Нам нy1ЮfО. чтобы: при щелчке П0JIЪ30ваТtля
.на КflOC!Re Отправить. выаваласъ функция Jfl.va:Scnpt. которал провер.нла бы щlждый
теж'ТОВЫЙ блон на пустЫе зна-ч€нил. llрI1 наличии пустщ:о ЗН-аче,IJИв. ДQJJЖНО по~
j{ВИТlЮЯ o.1fHO соо.бщенин е YIшзanием. ввести npавиль'ВЫе .данные. Сначала обрае50-
тайте событие <Clli~1 i 'ck ДJШ КНОПКИ OmраЮllЪ.

<i:npm п а.mе= "Ьtп5uЬrni:t" tуре="sщ,rni t \', ~.аl1Jе-='' SuЬmi t" id="·btn'SuЬn1i.t"


lаЛ\Juаge= " j aV$.sc!·ipt·' Dru::1 ir:.k.= ".l"et urn Ы::пЕ'uЬmit _onl1: lick () ,. >

Этот обработ'lli:l{ ·событ) ре<UIИ3уйТе та'К. RaI( покаэано ниже.

fUn.cti:on Ьt: ПSоЬ1Iiit_QI1Сl i c k () [


1/ Е~доnьзова~&ць о чек-~о забwn, отобра~ь СОQ~НИ •.
if( (·default.Page.t.J!.tuserNarrje.value == "") 11
(dE:fault Ji'.age .txt Ра.s.6;..ЮД~. val '\l.e =="").)
{
.a1.-€''l:t.: I "Сле.ц,У8 Т УJ'<'аэат ь ИМ'Я П ОЛ.ЬЗQв,а~J;:Jт'l'! м nap:o)!ъ!~);
!'€:t.u rл f д ls.€ ';

Тt>llерь вы можете О'ГКРl?rIЪ I::BO~ любимый брауЗер. перейти к С'трани'uе


de.ta\.1 1t .11tщ в виртуалЫlOМ fШ-Fалоге C'a.r-s й проверить работу вашего сценария
к>шента.

h't t. Р : ! / 1L>11: a·lh о s t/ С атз / ое f а иН • IУt.п;!

Подача запроса формы (GET И POST)


Теперь. lютда у вас есть простая J-IТМL-страница. мы доJIжны1 .вылепить, ЕЗR п.е--"
редать данные формы обра'rНО Web-с~рверу .I1lСЯ обра(iiот~~. Il,ри построеНии JfI'Мlr
форм:ы1 а открывающем дескриnторt. <form:> обычно З<Щ;lется атрибут aci-i'СН1.
укааЬJВaWЦИЙ IIО'.ilyЧат.е..1IЯ ВВQДJDЩIX. в форму даВНN'!:. Возможными IlОJJУ<Jаw,лями
мотут быть поч.товые оерверы. ~J1e фU'ЙЛЫ НТМL, файлы ЛSР (ВЗR "влассиче -
1020 Часть V, Web-приложения и Web-сервисы XML

ские". так и .NET) и Т.д. Для нашего примера мы используем "массический" файл
ASP с именем Cla s sicAsp Pa g e.asp. Обновите свой файл default.htm. указав в нем
следующие атрибуты в открывающем дескрипторе <fo rm>.
<fo r m n arne= "de fa \Jl t.Page " i d=" d efaul t Page"
асtiоп="httр://lосаlhоst/Саrs/СlаssiсАзрРаqе . asp" method = "GET" >
</ form>
Добавленные атрибуты гарантируют, что при щелчке на Rнопке Отправить
данные формы будут отправлены файлу Cla s sic Asp Page,asp с указанным URL.
Указание metho d = " GET" для режима передачи означает. что данные формы при­
соединяются к строке запроса в виде набора пар имен и значений. разделенных
символами амперсанда.

htt p: //local ho st/ Cars /C l a s.si cAspPage. asp?txtuserName=


Andrеw&txtРаsswоrd=аЬсd12З$&Ьtп S uЬmit=SuЬrnit

ДРУГОЙ метод передачи данных формы Web-серверу указывается с помощью


me t h o d = " POST".
< form n ame="de faultPage" id="defaultPage"
acti on="http : )/ l o ca l host/ Ca rs)Class icA spPa ge. a s p" method == "POST">
</form >
в этом случае данные формы не присоединены к строке запроса. а записьmают­
ея в отдельной строке в рамках НТГР-заголовка. При использовании POST данные
формы будут невидимы для внешнего наблюдателя. Еще более важно ТО. что POST
не имеет ограничений на длину символьных данных (многие браузеры выдвигают
огранич:ения на длину запросов с использованием GET). ПОI<а что для отправки дан­
ных формы странице-получателю *_ asp мы будем использовать НТТР-метод GET.

Создание "классической" АSР-страницы


"Классическая " АSР-страница является комбинацией HTМL и лроrраммного
кода сценария сервера. Если вы никогда не работали с ASP, вам будет полезно
знать, что целью использования ASP является динамическое построение НТМL­
кода с помощью сценария сервера и небольшоrо набора классических СОМ-объ­
ектов. Например, вы можете иметь серверный блок VВScript (или JavaSCript), ко­
торый читает таблицу из некоторого источника данных. используя классическую
технологию АОО . и 'Возвращает строки в ВИде НТМL-таблицы общего вида.
В нашем примере АSР-страница использует внутренний СОМ-объект Requ est,
чтобы прочитать введенные в форму данные (присоединенные к строке запроса)
и возвратить их обратно вызывающей стороне в виде эхо (не слишком впечатляет.
но поставленная задача будет выполнена). Для сценария сервера мы используем
VВScript (что обозначено дирентивой language).
С зтой целью создайте новый НТМL-файл и сохраните его с именем
ClassicA s pPage . asp в той палке, куда проецируется ваш виртуальный каталог
(например, в папке C: \ Co de Tests \ Ca r sWebSite). Реализуйте эту страницу так, как
предлагается ниже.
1
Iмва 23, Web-страниu.ы \<1 Web':iMMeHThl yт:tравления ASP,NET ,2,0 1021
<.t;@ lапguag:е="VВSСЕз.рt" %>
<btml>
<Г!l:·зd>
<t i tl е>СтраницгСаrs</tit le>
<Jh",ad>
<b.ody>
<[й аl:igrl=u..:ег.tеr">Ь>о'J' 'Ч'J:-О вы йам ПРИ:СJC.аJГА;<f]й,>
<Р ~liqn=llcR3Jnt.'i!:rr~.> <_Ь·;>Ji11ul'Я Ц~Л1;;l'ЗQз,а'tgJ1я ~~~/Ъ>
<%= Request_Que;ry8tri.ng ('<t:;ttJJseJ"NdIfte"') "%> <br>
<t,>Парол!о': </1:;.>
<}~ = Request. Qч.еry,stJ;:.iпq ("' t % t Pas""wqrd" )1," <lbт>
< '/Ьэйу;'
<:!п t.n11 '.>

Здесь COM-об'Реv.,;г Rеquг",'t ASP ~1:СПОЛЪ3Уетсsr д/lнвыоваметода: ,Qu.eryStri.n.gO


с целью анализа 3J;iаче"lЩЙ. сод:ержащt1ХСJJ в НТМL-эдементах. и переданных с по­
МОЩ:ЬЮ Ii1etJ:-юd '" "G:ET". Обозначение <'t= '" %> Я1Iдпет('я COJ(parцeиneM xt.iI.Ii тре­
боваНИfI "вставить ука:щнное Н:f>rДJl"Рt:;дt:твен,но .в u:сходящ:ий 1{ТiP-,(')TB,e'1,b. Чтобы
ДОСТИЧЬ Qоль.Ше~ гиБJ{ОСТИ. ВЬ) могли QbI ~заИМlClдеЙСТf:lP.вать с СQМ-объе~том
RespO!lse В раьшах Б€егр блщщ сценария (общщачаемоro ЗЩ1FЩМИ: <% %». Б ЭТоМ
.эдесь необходи.~ос'Ги H~ од:нюю вот простой при.1\.!:ер_

<%
Dim pw;:\
pwd = Request. QueryString '! "-:Х:t:.FEl55WОГО")
'Rеs-роnэе, W~ i te( pw.d I
%>
ОЧевидно, чого !iJ0ъеКТЫ Т1.8 q 1.) est И RеSF(УПЗ~ f\:.ТЩС;СИl:-1еС.НОЙ схемы А$Р' предла­
гают цeJIЬ1Й рщ ДОДОЛНИТf"ДЪ8ЫХ членов, :кроме показанных ВblIП€. Jt т()щ ще. ц
раМЩIl!: ЩIассического ПОДХ9,ца ASP оцреДe.JЩетсн Ilебольшой набор доuолнитеJJЪ­
НЫiК COM--'Оt1ъе:rпов(SoeвSiОn. $~rv€:r-. Appli<:;ctti::'1o 11 т.д.), ЩIторые вы ~ можеТе
исI10ЛЬ31JRa1Ъ при ЛQстроении Web-ПjJl'1Ложенин.

3аме't81tИе.S ASP. NET эти COM-QбъеКТbj оф ..ЩИarJЬНt) н'е оуществуют. Однако вы1 увидите., 'Iт05азо­
вый класс 5'у в\:ет. WE'ot, ,1.1 1 .Р'аяе ОПfJе.целяе;r С80Й€;Тва с иденl'ИЧНЫМИ именами, ВЬ:fвраЩ8Ю­
щие оБЬf}ктhl (]ilнадОГИЧгIЫМИ ВОЗМОЖНОСТЯМИ,

Чтобы провеРИ1Ъ проtpаммыую лопШу ASP в нашем Gлучае. IIJ'ЮСТО загрузите


страницу <:Jeia.u 1 t•.. ]1tm ~ брау-~ер и введю'е в форму данные. После обрабQТКИ СООТ"
betctbyIO-щет('I еЦбnаРНfl на Web-сt'pвере вы получите ньныIи lДИнамичее:I01 сгенери­
РОБаННhIй) НТМ1гКОД (РИС, 23,7)"

Ответ t.a отправку POST


но наC'l'QЯЩИЙ :момент ДЛЯ 0ТПрав:wн данных формы Цfлевому файлу * .аар в ва­
шем файле def;;iult.JH:'l11 )ffiазан НТГР-метоД С+ЕТ, ПРИ ИСПОJIbзоваю:i:и ЭТО)"О подхо­
да значе:Н:РIЙ, со.р;ержащ;иеr:я вэлемента..'!!: ynрав.)Iе.'iI~Я графичесв:ото интерфеИса.
присоеДИНЛЮТQЯ в -конец стрщш запроса, 8деl!Ь важно подчеркнуть. что АЭР'~метод
Eeque.st.Q1J..eryS-t:r:iпg () способен из,влекэm, данные. передшшые JtWilb1CO с по!'vlО­
ЩЬЮ метода ПЕТ.
1022 Часть V. Web-приложения и Web-сервисы XML

ВОТ ЧТО вы нам прислали:

имв ПОJlЬзователи: Эндрю Троэльсен


ПарОJlЬ: абвr 123.J1&

Рис. 23.7. ДИнамически сгенерированная НТМL-страница

Чтобы представить данные формы Web-pecypcy, используя НТГР-метод E'OST,


можно использовать коллеIЩИЮ Request.Form. чтобы прочитать значения на сер­
вере. например:

<.body>
<Ы аlig п= "сепtег">Вот что вы нам прислали:< / h1 > < Р аligп="сепt е r">
< Ь>Имя пользо вате ля: </Ь>
<i1; = Request. Form ("txtUs erN ame") %> <br>
<Ь >Пароль: <!Ь >
<%= Request. Form( " txtPassword") %> <br>
</ body>
На этом наше обсуждение основ Web-разработки завершено. Надеюсь, что даже
если вы до сих пор не имели НИI<акого опыта разработки Web-приложениЙ. теперь
ВЫ понимаете основные принципы создания таких приложенИЙ. Перед выяснением
того, как шrатформа .NEТ совершенствует существую!ЦИе на сегодня подходы, давай­
те потратим немного времени. чтобы Uпокритиковать" классический подход ЛSР.

Исходный КОД, Файл примера ClassicAspPage размещен в подкаталоге, соответствующем главе 23.

ПроблеМbI классической технологии ASP


с помощью классической технологии ЛSР (Active Server Pages - активные сер·
верные страницы) создано очень много популярных Web-узлов, но эта архитектура
имеет свои ограничения. Возможно. самым большим ограничением классической
технологии ASP н:ак раз и является то, что делает эту технологию такой мощной -
ЭТО языки сценариев сервера. Языки сценариев. такие как VВScript и JavaScript.
являются интерпретируемыми. не предусматривающими определения типов дан­

ных и не способствующими созданию надежных объектно-ориентированных про­


rpaмMНbIX конструкций.
Второй проблемой классической те.хнологии ЛSР оназЬёВается ТО, что программ­
ный код страницы ".asp не является строго модульным. Поскольку ASP представ­
.1Я~ т собой комбинацию НТМLи сценариев в рамках одн.оЙстраницы, большинство
1 Глава 23'. Web-СтРаницы и, Web-3l1емеИТkiI управлен~я АБР. ~EТ 2.0 1023
Web-nрИ'ложений АБР 0Щli1~ается ~странной смесыо- двух coвc~ разных подходов·
в ПрQrpам:миро'ВаliИй.. ИХОТ'n М'Iшс('ичеСltaJl технОЛОГИ$! ASP JJ Q;шоляет раздел;ить
МНDrOJ~раТIЮ исuользуемый ПРQrpaмм}iblЙ код на отд~лъ.н:ь~е В!UlЩча~мые в проеtcr'
фай.!lы1 • ..цежаща:я J! основе TaкQJQ разделения @бъектнал MOдe.JlЪ не обес.печивает
ис:r:и::mюе ра~граничеJfИ~ обязанностей. Б идеадс каркас Web-ршrработки должен
ПО3I\(}.JЦl'Ть.nрограммноЦ JIоrщtе предстаВ.аеЮ!lЯ (те. десfiPШJТОРам fiТМL] существо ­
вать неаавлсимо ОТ rrpогр~о:й лоrид,и реализации (т.е . npограммного кода. реа ­
лизующего Фунюnщва.rrыще воаМОШRОСТИ npило:жениJ'l) .
Нш\О.щщ. еще QДЦОЙ проблемой .FJ]~ЩН:'1:сg то. "IТO ,1'Jтассическая технология ASP
требует иепOJ-д>ЗОВ'ания Qолъшоrо .количecrва mаБЛ{)JЮВ Ц .весьма и3(jъrгoчного про­
,раммнр,ГС! :кода сц~нариев. который может I.Iовторя:ться ОТ проеRта 'в: проехту.
По"t'ТИ реем Web-придожени,ffil.1 'Нwбходимо осуще('тВЛП'Т~ контроль ПCJльаователЬ­
(~oгo !щода. обновцять {;ОСТ{)JЩие НТМl.г~)ЛемеFfГОВ перед ОТПРаВКОЙ Н1ТР-о:гвета.
П:Е.ерировать НТМLстаблш.{Ь! дaнI'ТЫX и "l:,A.

Главные преимущества ASP.NEТ 1.х


Уже первая: г.даваая реапизация ASP.NEТ (версии 1.Ii) аредло:(Юvщ фантастиче­
c~e ВО3М0ЖНОС1'ц' преОДО.i1еНИJi оr'раничеНИЙ. присущ:их ктracСИ'~t':с]'(ой Т~ОJl(jJТИ;И
ASP, По сути. платфQpмо .NET дала начало испЩIЬ3f':!Ванцю следующих подх:qдов \,

• ASP.NEТ]..x преДiIага~т моцелъ. осноsaнuyю на И('ПОЛЬ30ЩЩШI вН€щнего крда


rwддep:нcкu и ПО3ВО./1ЯЮ.rц,yн> O1'1lеш~:r.ь .trогИh)' прмставлеmш O:II ДOI'}Ц{И реали"
зации.

.. Сrраницы ASP.NEТ l.x предс:..авляют собой CRОМIIидированные- Rомпоrшвоч­


ные блоки .NEТ. а не ш-гтерnретw-pуемъre строки язьm:.a сцеmtриев. которые об.
рабатъrваются значительно медленнее.

• Web-злем:ент:ы: управления ПОЭВШШЮТ программисту uтроить Web-приложе~


ни.н с' графи'чесЮfМ И1ГГе-рф.еЙсом приблизЮ'еЛЬНО тэн же. lШlt и при:лоmеЮfJI
Windows Fш:rn.s.
• Wep-:щеj'ol[efffЫ ЛSР.NEТ автом(}.тичес.liИ обновляют {''Вo~ состояние при вТо­
ричных запросах. ДЛЯ чего используется снрытое поле формы. имеющее имя
VI EW S TAТEJ'.

• Web-nРИJ10Жения ASP.NEТ ЯJЩНR!JТС.я аолностыо оБЪeJ\l'~о·ори,е],;1ТИРОIЩННЫМИ


1'1 ИСПОЛЬЗУЮТ CTS (СОО1Щоп 1)rpe System - . общая OklCTeмa ТRпщв).

• Web-приложt)1{ИЯ ASP.:NET лепю коt-tфиryрироэа:ть с ПОМОЩЬЮ ета}1дарт­


нык средств NS или с помоЩь.ю файла к,онфиryрации Wf'b-ПРИЩ;1жения
(web. con.fig).
Теnолоrтr АЭР.NF.Т 1,X была больпшм шагом в правиЛ.ьном направл~нии. но
ASP.NEТ 2 .0 ооеспeoqИnает дополнительные ПР~JШyЩttства.
1024 Часть V, Web-приложеНИR и Web-сервисы XML

Главные преимущества ASP.NET 2.0


ASP.NEТ 2,0 предлагает ряд новых пространств имен. типов, утилит и подходов
в разработке WеЬ-npиложений ,NEт. Вот их неполный список.

• В ASP.NEТ 2.0 для разрабатываемого и тестируемого Web-узла уже не требу­


ется хостинг в IIS, Теперь вы можете разместить свой узел в любом каталоге
на жестком диске.

• ASP.NEТ 2.0 поставляется с большим набором новых Web-элементов (элемен­


ты управления безопасностью, элементы управления данными. злементы
пользовательского интерфейса и 1:Д,). дополняющим набор элементов управ­
ления ASP.NEТ 1.X,
• В ASP,NEТ 2,0 поддерживаются шаблоны страниц, которые позволяют соз­
дать общий шаблон интерфейса для множества связaшn,lX страниц,

• В ASP,NET 2,0 поддерживаютс.я темы, которые предлагают декларативный


метод изменения внешнего вида всего Web-npиложения,

• В ASP,NET 2.0 поддерживаются Web-части. которые могут использоваться


для того, чтобы конечный пользователь мог настроить внешний вид Web-
страницы.

• В ASP.NEТ 2.0 поддерживается WеЪ-утилита конфигурации И управления. ко­


торая осуществляет управление файлами Web.config.
Если бы здесь ставилась задача описать все новые возможности ASP.NEТ 2.0.
эта книга была бы в два раза больше. посколыI~ тема Web-разработки не является
единственной темой рассмотрения, для получения более конкретной информации
о том, что здесь не представлено. обратитесь к документации .NEТ Framework 2.0.

Пространства имен ASP.NET 2.0


в библиотеках базовых классов .NEТ 2.0 предлагается не менее 34 пространств
имен, имеющих отношение к Web. Всю эту совокупность пространств имен можно
разбить на четыре главные группы.

• Базовые функциональные возможности (типы. обеспечивающие вэаимодей­


ствие с Н1ТР-запросами и Н1ТР-ответами. инфраструктура Web-форм, под­
держка тем и профилирования, Web-части и т.д.)

• Web-формы и Н1МL-элементы

• Web-разработка для мобильных платформ

• Web-сервисы XМL

8 этой книге тема разработки .NET-приложенИЙ (ни Web-приложений, ни ка­


ких-то других) ДЛЯ мобильных систем не рассматривается. но роль Web-сервисов
XМL будет обсуждаться в главе 25. 8 табл . 23.1 предлагаются оПИсания некоторых
базовых npостранств имен ASP.NEТ 2.0.
1 Глава 23. W&b-страli,ЩЫ и WеЬ·Эllеме нт ы упрiiвления ASP,NET 2,0 1025
Табnица 23.1. n'ростраНС1ва имен ASP.NEТ AfIЯ Web

II Прощрвнст_а "~H

s)" st.em. W€b


Ооиса/D18

Оr;pе,nеляет ТИПql, обеспеЧ~l;laЮщ~е I(ОММ~НИКд!ЦИIO брауае­


ра и (8 4д.ол:юрти, ВО;3МQЖНРОТИ эапр.аса И от­
!,
I
.Web·cepeepa
ае'Га, обработки фаЙJl(JВ cQokle и пере:дачи файлов)
i sу s,tеm.WеЬ.Сасhirщ Опр~еляет типы, обеСП\'!'!И6Зющие ВОЭМОЖflQСТЬ кэширо­
j В~I~IIIЯ АЛя WE!b-приложеfН1Я

Определяв'r ТИПЫ, t'IОЭВОJ1ЯlOщие ~троить ГlользователЬские


>Феты Д/rя среды выполнения ASP.NEТ

Sys t eal. W,еЬ.Ма nаgешеnt Опредмяет тиnы. об.есnечивающие· ynраrm8ние и контроль


правИ1lЬНОGТИ функциоыиРования Wеt:!~лриложения ASP.N.EТ

S уst ЮТ1 , W;e:b-. li' зm filе Определяет tИ-nЫ , исr:юльзуемы'' ДJlЯ работы с гтЬльзова­
тепьсl<~I\4И профилями АБР. NIIJ

Sys tel1',..Web . Se ctJJCJ.ty Определяв; ,ипы, позвопяющие програмМНО обеспечить


безопасность узла

Sys te m"Wе.Jл . Se..s sj GJlБtз.t 6 Определяет типы, обеспечивающ\.tе поддерЖКу ~f1jфОРМаци~


с,остшrния дnя каждого ПОЛЬЗ'Dвателя (например, на оскрве
)IJ';.ПОIJЬЗQвани~ ceaHeoB~lx rlepeMet'l'libl~ СJ;ЮТОflНИЯ)

sy,s:t е,!'1 , W!i= Ь . 1) 1 ОпредеI1ЯЮТ ряд 'ТипоВ ' лозвqляющих СОЗДЩЩТЬ ДЛЯ Web·
S;y ste.m,W\!:.tJ . Ul. W~b(: (mtrol s прилС)же-ний ПJ)ОГраммы клиеmа с графичес~и.., паЛЬ3ОВ.а ·
Эуs tеrп.wеЬ . (Г[ . Е t w. lCDп,tгоlз Т€il I>СКИМ интерФ·еWг.ом

Модель программного кода


Web-страницы ASP.NET
WеЬ~страниаы ASP.NEТ могут СТРОИТh'С& С :исшшь:юванием ОДНОГО из, двух под­
ХОД'О:В. ВЫ можете создать ОДИН файл CIi. . аер·х. К!!>l'ЬрыИ' будет (~Ьдержа'rь ~омбиНа­
цroo программвого коЦр. с ервера и HТМL (ПО примеру E..!IftcСИЧ €С1ЮЙ технологии
ASP). Для создания такогО' файла используетСЯМО;!l ел:ь ОДНОМОдy:llЬНОЙ страницы,
когда npогра..lУIМНЫЙ 'КОд сервера размещается в кояrrексте < з с riрt> . IЮ сам ЭТОТ
I1РОГРaммдblЙ КЬд непосредствеliнО не является протраммным ходом сценария (не­
пример. на яз,Ьше VВSc,ript ИJШ JаvaSсrфt}, Вместо Э1'ОI'О операторы npогрaммнorо
кода в рамх.ах блока < s с r ipt. > ЗanИСI:>IВaIOТСЯ на любом :из управляемых ЯЗВlКЬв
(С# , Vist('cJ.1 Вasic .NEf и т.д,) ,
Если соЗДаваемая с'i'pанипа оо.цержит оч:ень мало nPЬграммного кода. (и очень
M}10TQ НТМL-KOдa). модель одномоЦУ'ЛШЮЙ C~I окажется лучшИм вариантом
выбора. так как перед вами 11 ОДВОМ уни.фицарованном файле " , аsрхбудут и про·
грам!VIНЫЙ КОД. н разметlса. Размещение npoгpaммнoro И Н1"МL-кода . в одном файле
*. аэрJt обеспечивает и дрyrие преимущ;ества,

-Страницы., со3Данные в рамкю:: ОДНОМО;ЦУiIъНои мо:цели. проще инстammро­


ват.ь и npeдocтaвтrть другим разрабмчихам .

• ВВИдУ О'I'СУТСТ1ШЯ3aвJiIсимос:ги мещду фafuJами. Фд.номрд.vлъвую стращщу


прыi({~~ переименоваТЪ.
1026 Часть V. Web-приложения и Web-сервисы XML

• Подцержка и обслуживание файлов исходного кода оказьmаются более про­


стыми, поскольку все действие происходит в одном файле.

Подход, принятый в Visual Studio 2005 по умолчанию (при создаиии нового про­
екта Web-узла), использует так называемую технологию вн.ешн.его кода nоддерж­
/си (code-behind), предполагающую отделение программного кода от НТМL-логики
представления и размещение их в двух разных файлах. Эта модель исключительно
хорошо работает в тех случаях, !шгда ваши страницы содержат большие объемы
программного !{Ода или в процессе разработки Web-узла принимают участие много
разработчиков. Модель. основанная на использовании внешнего кода поддержки,
имеет несколько преИмYIIIеств.

• Ввиду совершенного разделения НТМL-разметки и программного :кода. ста­


новится возможным. чтобы созданием разметки занимались дизайнеры. а
созданием npoгpaммнoro кода С# - программисты.

• Программный код не предъявляется дизайнерам страницы и другим разра­


ботчикам, занимающимся только разметкой страницы (вы. наверное. дога­
дываетесь, что разработчики НТМL обычно не проявляют большого интереса
к огромным объемам программнаго кода С#).

• Файлы программнаго кода могут использоваться в множестве файлов


*.aspx.
Выбранный вами подход не влияет на производительность полученного ре­
зультата. Также следует подчеркнуть, что ОДНОМОдУльная модель *. aspx, которая
в .NEТ 1.Х провозглашалась неnрuемлемоЙ. теперь такой не считается. Теперь во
многих Web-приложениях ASP.NEТ 2.0 при построении узлов используются пре­
имущества обеих указанных выше моделей.

Модель одномодульной страницы


Сначала мы рассмотрим модель одномоцульной страницы. Нашей целью яв­
ляется построение файла *.aspx (с именем Default.aspx). который будет отобра­
жать таблицу Inventory базы данных Cars (созданной в главе 22). Вполне возможно
создать эту страницу только с помощью редактора Блохнот. но Visual Studio 2005
может упростить процесс построения с помощью средств IпtеШSепsе. автоматиче­
ского завершения программного кода и визуальных редакторов страницы.

Сначала откройте Visual Studio 2005 и создайте новую Web-форму, выбрав


Filer::>Newr::>File из меню (рис. 23.8).
После загрузки страницы в среду разработн:и обратите внимание на то, что в
нижней части окна проектирования страницы есть две кнопки, позволяющие уви­
деть содержимое файла *.азрх в двух разных вариантах. Выбрав :кнопку Design.
вы увидите окно визуального проектирования, в котором вы можете строить поль­

зовательский интерфейс страницы во многом подобно тому. как вы строили Ш{­


терфейс формы приложения Windows Form (перетаскивая элементы управления
на поверхность формы, изменяя настройки в окне свойств и т.д.). Если выбрать
кнопку Source. выI увидите НТМL-код и блоки <script>. из которых скомпонован
данный файл *.aspx.
Гпава 23 Wеl>JСТРэ.ницы 11 Wеh-элеменm уПравлеНИя дЭР.NЕТ 2.0 1027

- - .,l~i
,"'-- '..G-1.....
Рeiforп!ancе
S Web
\II5цi1I~,шr
с#
VJi
ViStJaI ('но
5cфI;

__ _ _ _ •_ _ -'=_с- _ ....;;:::...... i;....... ___ _

Эамеча"lU!. В оrnичие отболев РilНliИI{ версий VisuaJ Studlo', вид Source в 'Visual' StudJo 2005 nplaA~
ilагает П()ЛНОЦ~ННУЮ ПО):I,D.ержку IntelllSense и позволяет перетаскивание злемеюов пользова­
тельского интерфffi1са непосредстве~о в окно IrITML-', Кi'Jда.

В.панели' шrGipyмemOB (О1СНО Toolbox) Vi'Su:al Studio 2005 ОТi'{ройте раздел Standatd
и nepетащитеэлеМe:I-trы yпpaв;Iешrя Butt bQ, La'b el и 'Grid.Vie w' в ЬщJO Npоектиро:ва­
НИIl r:тpэницы (элемент ,GridlJ:Lew :можно найти в .разделе Dзtа окна TOOlbOx). Не пре­
небреI"aЙТе использованием ОЮlа свойств (или JпtеШSеl1sе дщJ НТМL) при устанОВ­
ке рэзлиЧНI:iIX СВОЙСТВ элемеl-!ТОВ :и:wrерфейса и укажите ддя 'Каждого Web-элемента
ПQдХод.нщее имя с помощью свойетва I D. На рис. 23.9 ПOlf"щан QДИН из в:оэм:ожных
вариантов ОфОРМl"lенn проекта (сдержашюетъ здесь ЦРШIВллетСn npeДEaмepeHHD,
чтобы минимизировэть объем генериру~моro кода рэзме';ГКИ) .

. ",.'
... "'" I'

_. ",.. . ..", , , " _..., .,.,...... _.. ,.. . ·0 ,,,


i 't:!о1umйD Сo1unшl Сu1ШDl12ii
" I
1Ibc аЬ(; аЬс (]
: аЬс аЬ с a'bQ f,
.'19
! r;aЪc аЬс 1I\,>с n
;;;

:ab~ аЬ.с .1IЬС


I . аЬс а1х: аЬ е ;1
: =и .~_· ,='~1=. ~,~~=,.,..:,,,:~'=---";.Q

Рис. 23.9. ПОЛЬЗ0вательсм.иЙ интерфейс Default.aspy.

J
1028 Часть V. Web-приложения и Web-сервисы XML

Теперь щелкните на кнопке Source внизу окна и найдите в программном коде


раздел <form> своей страницы. Обратите внимание на то, что Web-элементы
управле}шя определены с помощью дескриптора <аэр :>, там же вы обнаружите
набор пар имен и значений в соответствии с установками, сделанными вами в
окне свойств.

<form id="forml" ruпаt="sелтеr">


<div>
<asp:Label ID="lbllnfo" runat="server"
Техt="щелкните на кнопке, чтобы заполнить таблицу">
</asp:Label>
<аар: GridView ID="carsGridView" runat=" sелтеr">
</asp:GridView>
<asp:Button ID="btnFillData" runat="serv'er"
Техt="3аполнить таблицу" />
</div>
</form>
Подробности использования Web-элементов управления ASP.NEТ будут обсуж­
даться в главе позже. Пока что достаточно понять, что Web-элементы управле­
ния - это классы, обрабатываемые на Web-сервере и автоматичеСI{И помещающие
свое I-rТМL-представление в исходющrй Н1ТР-ответ (да , .вам не нужно создавать со­
ответствующий HTМL-код!).
Кроме этого основного преимущества. Web-элементы ASP.NEТ поддерживают
модель ПРОГРCLl\1МИрования, аналогичную Windows Forms. Iшгда имена свойств, ме­
тодов и событий имитируют их эквиваленты в Windows Fопns. Для примера обра­
ботайте событие Cllck для типа Button либо с помощью окна свойств (используйте
кнопку с пиктограммой молнии), находясь Visua1 Studio в режиме проектирования
Web-формы. либо с помощью раскрывающихся списков, размещенных в верхней
части окна просмотра программного кода (кнопка Source). В результате в определе­
Юiе Button будет добавлен атрибут ОпСНсК. которому назначено имя обработчика
события Click.

<asp:Button ID="btnFillData" runat="server"


Техt="3аполнить таблицу" OnClick="btnFillData_Click" />

Кроме того, в вaпr блок <script> добавляется обработчик события Click сер­
вера (здесь обратите внимание на то, что входные параметры в точности соответ­
ствуют требованиям целевого делerата system.EventHandler).
<script runat="server">
protected void btnFillData_Click(object sender, EventArgs е)
{
)
</script>
Реализуйте серверную часть обработчика событий так, чтобы использовался
объект чтения данных ADO.NEТ дЛЯ заполнения GridView. Также добавьте дирек­
тиву импорта (подробнее об этом чуть позже), которая уЕажет. что вы используете
пространство именSystem. Da ta, SqlCl ient. Вот остальная часть соответствующей
программной логики страницы файла Default.aspx.

<%@Page Language="C#" %>


<%@Import Namespace = "System.Data.SqlClient" %>
Глава 23 WеЬ·страниЦЫ и Web-эл'ем:еюы уПР86ле~ия ASP.NET 2-0 1029
<s,crlp!. run.at="'.se:rv.er'";'
рrоtесtещ void QtnFil.LData_Click (robj,ect a~.flde:r:, Ever.tArg$ е)
(
sqlConnectl:on sqlCfJnТl = new
ЭчlСоn·nееи оп ("Da ta S01lTC.e=.; Init ia1 Сlatаlоg~rэ ;ПID=sа ;'Р I!ifD = " ) :
sqlCc n n . Орег. () :
~qiСОI;tlf(J!:1:f)d cmd = nе",
SqlCammand ("5elect .. Fro.m Tn\1 ent o:ry~ ' , sqlCO!1n))
.c:arSG:rid'v'iew. Da-r.а$оtiт.се = crnd. ExecuteRea:dE r: () ;
ca:t'IiJGr-l aYi ew • [Ja t-аВd.дd: () ;
sq'lсолг.. .• Glоs€ I~.;

</s·cr.1pt>
<htJrll xmlns= "b'ttp: J ,'w,Ww. W3. org/ 1999 (xr,tmJ, 11 >

</html:>

Перед тем:как noгрузиться. :в детали: обсуждения формата этого файла *. а spx.


даваЙте ВЪШОJШИМ тестовый затryСl\ странИЦЫ. Оцройтв окво RОМ~НДНОЙ Сl'РОJ{И
Visual Studjo 2005 и запустите утилиту W~bD.ev. WebS-е:r:vеr .€же, yI<З"зав путь к СО-
1{;рсщеНFIОМУ вами файлу Dеf.аUlt .. д'Зр'Х.

WEbclev. w,ebs~rver" ы<:е I port:: 1 23<4 ') !pa·t b; "С: \Code TEst-s \8 ing l,r?Pa.ge'Мooel;;

Затем. o'rI;:pbд'J любой брауэе-р. введитеслецующий адрес URL.


l-it. tp: ( / .1 Q;Ca lJ'lO:S t ; 1 2 3 4501
Пр!:! затрузке страницы вы сначала -увидите ТЩIЫ\О ТШlb1 Label и BlJttK:JtJ. Но
к(>гда вы щеmrnете на кнопке. Web-серверу будет Нatrpав,леJ:I I:lТ0РИЧВЫЙ зanрос. в
реауцьтате IЮТОРОГО Webo-эдементы ynравлеШЩr получат обратно соо:rnет.ствующие
HTML-дескр1Ш'ТОрЬ1. На рис. 23.10 показан реЗУЛh'Тат ВИЗУaJ:lИэащии нашей страни­
цы в ОЮJе Mozilla Flrefox.

l'etNams

Рис. 23.1·O.Web-Ааступ R данным


1030 Часть V. Web-приложения и Web-сервисы XML

Совсем просто. не правда ли? Но. как говорится, все зависит от мелочей, так
что давайте рассмотрим немного подробнее композицию файла *.aspx.

Директива <%@Page%>
Прежде всего следует отметить то. что файл *.азрх обычно открывается набо­
ром директив. ДИрективы ASP.NEТ всегда обозначаются маркерами: < %@ ХХХ %> и
могут сопровождаться различными атрибутами. информирующими среду выпол­
нения ASP.NEТ о том. как обрабатывать соответствующие данные.
Каждый файл *.aspx должен иметь, как минимум. директиву <%@Page%>. ко­
торая используется для определения управляемого языка. применяемого в рамках

странш,:~ы (для этого используется атрибут language). Также директива <%@Page%>


может определять имя соответствующего файла с внешним кодом поддержки (если
таковой имеется). разрешать трассировку и Т.д. Наиболее интересные атрибуты
<% @Page %> описаны в табл. 23.2.

Таблица 23.2. Подборка атрибутов дирекrивы <%@Page%>

Атрибут Описание

CompilerOptions Позволяет определить любые флаги командной строки (представленные


одной строкой), передаваемые компилятору при обработке страницы

CodePage Указывает имя соответствующего файла с внешним кодом поддержки

EnableTheming Индикатор поддержки тем ASP.NEТ 2.0 элементами управления данной


страницы *. азрх
EnableViewState Индикатор поддержки состояния предстasленмя между запросами стра­
ницы (более подробно об этом г{)ворится в главе 24)
Inher its Определяет класс страницы, из которой получается данный файл *. азрх;
может быть любым классом, ПОЛУ'-lенным из System.Web.UI.Page
MasterPageFile Указывает шаблон страницы, используемый в паре с текущей страницей
*.азрх

Trace Индикатор разрешения трассировки

Директива <%@Import%>
в дополнение к директиве <%@Р а g е %> файл *. а 5 I? х может использовать раз­
личные директивы <%@Import %> . чтобы явно указать пространства имен. необхо­
димые для текущей страницы. В нашем примере указано использование типов из
пространства имен System.Data.SqlClient. Ясно. что при необходимости исполь­
зования дополнительных пространств имен .NEТ нужно просто указать несколько
директив <%@Irnport%>.

Замечание. Директива <%@Import %> не является необходимой, если применяется модель стра­
ницы с внешним кодом поддержки . При использовании файла с внешним кодом ПОДlJ,ержки для
указания внешних пространств имен применяется ключевое слово using С#.

Опираясь на имеющиеся у вас знания .NEТ, вы можете поинтересоваться. по­


чему в файле *.азрх нет УI<3Заний на пространства имен System.Data и System.
1 Гл~ва 23. Web-сtРЗIrlИ.цы иWеЬ-элемеюы упра;вления ASP.NEТ 2.0 1031
Причина/З том. что 1З~е страюiЦЫ *. азрх а,в,то:матичес;ки получают доступ Е ряду
клюqе.вых npocтранОТИ имен, включая CJlедyюIiIИе.

• Syst;:;;m
• Sу s tеm.СОllес:ti.олз

• System.Coll~cti.Qns.GeoeriC

• Sу.st;еm. С опfigurзtiоп

• &ystem,. iO
• System.Text
• S'Y'steтn .Text. RеgulаrЕхр:rеsэiоrL$
• Все прoecrpанс.'ТВа имен, С8Язанn.ые с syst-еJ)1.Wе,Ь

ASR'NET определяет ряд других дирef\ТИJ3, .которые мотут встречаТЬСII в ФaйJ1,ах


".азр.х :кан.да, ТШ<И после
<%@Page st:,> и < %@Iщ.ро:rt 'i.>. но:их .обсуЖДениеnредпо-­
цагаеТСII провеСти позже.

Блок <script)
в соответствии с моделью ОДНО);[ОДУ лъной страницы файл YI. а spx MQ}fteT со­
держать логшtY сценария серв.ериоЙ СТQРОПЫ. ЕОТ~)РъiЙ должен выполняться на
Wеь-сервере. Блони программного коДа. оцределеШlblе для сервера, должны въl­
llO:пняться на сервере. позто1V!у ДJ1Я.них nСПОЛЬЗуеТСЯ атрибут r·Lшаt="sе.r,'~r".
Ее:ли: атрибут :tunat=" s~.cv.er" не yщtзан.. среда ~ьшолвенин предпо.l1'aГаe"I: что со-
y~ ~
QТ.ветствующии l1ЛОR ,flВЛЛt!тся сценаРИ€l\ot K.;'!uel-lmq. RОТopblИ следует отправить с

~сходmцим. Г.JТrP-Oтвe:rOM.

<Е-сri1бt r.иnаt="зеrvеr" >


p:rotec1i:e(i void btnFi 11 Data_Click Cbbj<;\c:;,t $e;;ncter , E'\?e DtArgs е)
{
1
</ $c;Z;ipt>
.сигнатура этого ВСПQ.l\1юrатеlIъно.rометода ДОЛЖНЦ выглядеть оч.ень 31IШФМРЙ .
Вспомните, Ч:IQ при йзучеmm Windows FQrms товорЮJОСЬ (1 том. что 06работ<шк
соб~ долЖенооответствоБa'I'Ъ цщмону _определенному соответствующим деле­
ra:rOM .NEТ А КOJДa вы хотите ,обработать щелчок на кнопКе ео стороны сервера.
соответствующим делетатом mmяетсFJ Systen1. E'\7entHandler, RОТОРЫЙ. Юllс ВЫ пом­
ните, может выз.ват'Ъ ТОЛЬЕО Ме'{'ОДЫ. IlOJI)"laJощие в кз'чество первогЬ' параме:тра
Sys1;:errJ . .object" а в Rачеетве ВТОРОТО- Sуstsщ.Е\iсеnt Ап~.г5.

ДеклараЦИll311емента ASP.NET
Последним из рассматрйВаeмъrx З~(~Ь щшросов цвляется структура определе­
НИЯ17лементов управления Bu t Lon . l.a b€l и G:'J;" idView Web-формы. Подобно ЛSР и
JПМJ." Wеь-элемеmы ASP.NEJ размещаются в ьюнтеКС'1'е <fоrш>. Но н этом случае
ОТIqJЫJШЮЩИЙ дес&риrrrор <f и~m> соupовождае~аТРJoJбугом r u п а t="зеtlтеr". Это
оче.lД, важно. песю')лЫ'.:у тем caMbIМ деекриТJТClР И'liформпрует (феду:вьmолненЮf
MP.NEТ о том, что перед размеще.l;tи.е:м НТМL~Rода в потоке ответа соатветствую-
1032 Часть V. Web-приложения и Web-сервисы XML

щие элементы ASP.NET должны получить возможность обновить свое НТМL-пред­


ставление.

<form id="forml" runat="server">

</form>

Исходный код. Файл при мера SiпglеРаgеМоdеl размещен в подкаталоге, соответствующем главе 23.

Модель страницы с внешним кодом поддержки


Чтобы продемонстрировать возможности модели страницы с внешним IiОДОМ
поддержки. мы воссоздадим предыдущий пример. используя шаблон Web-узла
Visual Studio 2005 (при этом важн:о понимать. что ДlIя создания страниц с внешним
кодом поддержки использовать Visual Studio 2005 совсем не обязательно). Выбрав
FileqNewQWeb Site из меню. укажите шаблон ASP.NET Web Site (рис. 23.11) .

. . ASP.NET Web Per.onol W"b EmptyWeb ASP.NET


Ser\,/Ice Slte 5tarter Кit 5ite crystalf>.e ...

!
1_ I'1:и ",,!,pl,,t.,.

I .;}]
11 S....ch Onlin.
Templ.t", .. ,

I~ ы;к~~~~~'~~:~"~:~-.~:~~.==~~=:~=~" :::~_.~':_~:::::'~.,~.:=:=_.•'=~ . , ,.,,. ~'"-_'.~".'",.","_


""._".,,.\
~QC«Ion'
LqtЩIe:
I~~~~.~~~·" ·· ~- . "-~! [~\С~~~i;-~~~d~В·Е~~~р~~~~ ·"_. = ~~=.
[VI~~"c# -==- =~
". _.._... -~1 1 ~owse,., I

ОК JI C_ncel

Рис. 23.11. Шаблон ASP.NEТ Web Site в Visual Studio 2005

На рис 23.11 обратите внимание на то, что вы можете сразу указать место рас­
положения нового узла. При выборе File System ваши файлы будут размещены в
пределах одного локального каталога, и страницы будут обслуживаться с помощью
WebDev .WebServer.exe. Если выбрать FTP или НТТР, узел будет обслуживаться в
рамках виртуального каталога, поддерживаемого IIS. Для нашего примера нет ни­
какой разницы, какую из возможностей вы выберете, но для просто'Гы давайте вы­
берем File System.
..

Глаза. 23. web-сlраницы и W!7b-Эl1еtvlе~1Т101 упраIНIВН~~ ASP.NEТ 2.01033

3аМjlчаНllе. ПР\II соэдаН\II\II WеЬ-узла ASP..NE'Т .8 Visual Studio .2005 соотаетcrвующий файл решения
(".з10) 1"10 УМОЛ'-iанию P<i'змщцае.,.о~ в лапке МС'И дGкумен'l!ыv1з uаll studio. 2005\
Proj ec·ts, Файл'ы содержимого узла (такие !С_Ж, например, ". а spz::) будrr Н8ХQАиТЬСЯ В ука­
занном ЛО.КnЛЬ/-IOМ j(aTiW1or~ или (при I4СПОJ1Ь3GВaнV!И 115) в физ~чеCl.(ОМ фаЙ'ле •. отображающем­
ея в .виртуалi;IтIЫЙ каталог,

СНО:8а ИСDOJ!ьзyiiте 01<ВО про~~тирDВания для пос:троеk1ИН ПОЛЪЗ0ватеЛЪСМrо ИН­


терфейса. СОС1'Оящщ"О ИЗ Lab'el, But ton п GridVi,ew, и используйте OIO-ro СВОЙC'rВ
дЛЯ иsменения дaCTpo~. Теп~ръ щеЛКFlите на КН:ОIlJ{е Source в:низу окна. чтобы
увид~тъ ОЮIО црогра:ммното кода. и вы увидите сооидаемые деСlфШ1ТОРЫ <asp> и
</аэр>. Также обра'i:ите внимание rta то , что ДИРеЕтиnа <'%-@Page:%> в даннОМ слу­
чае Ц1Аеет два новых атрибута.

<" @ Page Lапчuа.g.е="С#" Аl1t . оЕ'ilепП·nrе\Jр=~t.Тl.lе"


СоdеFilе="Dеf'аЩt. ~вpx.. cs" Inhe:Jt'its="_D~fauIt," %>
Атрибут CQdeFile ЦСDQльзуется дщl укззfiНиJr GВЯзав:нGtо внетнето файла. со­
держащего программную ЛОГИНУ <:тра;Н:ИЦЬL По УМDлчанию имена файлов с внеш­
ним J\.\)дом ПОдДерЖ1iИ образуются ТJYI'~M ДОбав.леFJИЯ ауффикса . с s к имеЮl' файла
*.aspx (скажем. в наше:tvJ upимере ·это Defaul t .aspx, c.s). ЕCJIИ ~аtлянуть в ож«>
Solution Exp,lo.гer. вы ysидите ф~ е вщ:шиим RQДОм. поддержки в рамках ума Web-
формы (ри~ 2З.l~J.

;-~
. .1-."
5nhJon
-
~I~,;r
.... .
.17;т-- &Pber/~ d~_
__ , - - ___
О _ _ '- .~ ._ . _
J
Рис. 23.12. Файл с ВI~е.шним I(QoЦoM nоддер)((1о:И, аСС/:ЩИlrlрованный с файлом '*. азрх

3амеЧЗl(И8. Ат-рибут C'odeb'ehind, преДJ:ta"гавшиися в ASP,NEJ 1 , х, ~ рамках ДL1реКТ. И8Ы


<'%@Page%> баЛЬШ$ не поддер;.кивзеТС91,

Кроме ряда операторов usi пч ДЛЯ указания евЯ'занных с We.b ПРОС7рЫIСТВ


)Iмея, ваш файл 13дешн.его программно:г!) J{ода опредeJiяетI<Ласс с :модификато­
ром partial, DРОИЗВОДНЫЙ от Syst€m.Web.UI.Page. Обратите Щ-ШМaIO-Jf: на ТО, что
ИМЯЭl'ого КJI~с9З LDef.auJ.t) идентично значению атрИбута l aherits. JIЩЗaННОГQ
в pnмкax диреJ\'nmЫ ~'fi@Раg,еi1l > (IIDДробнее о Page_LQii'd () МЪ] nQГЩю.рим в этой
главе неМНQГО щ)зже),

p'.iIblic partial class _Default : SY$te1rl_Web .. ПI .Paqe


!
p'!:Qt.ected -"oid Page_Load(9bje-::'С sender, Even\:.Args е)
{
I
1034 Часть V, Web-приложения и WеЬ·сервисы XМL

Обработайте событие Click для типа Button (снова аналогично приложени­


ям Windows Forms). как и раньше, в определение Button будет добавлен атрибут
OnClick. Однако теперь обработчик события сервера уже не размещается в контек­
сте < script> фай.па *.aspx, а оказывается методом типа класса Default. В завер­
шение построения примера добавьте оператор using для System.Data.SqlClient
в файл с внешним кодом ПОilЩержки и реализуйте программу обработки в соответ­
ствии с предыдущей программной логикой ADO.NEТ.

protected void btnFillGr i d_Click(object sender, EventArgs е)


{
SqlCo nnection sqlC onn =
new SqlConnection("Data Source=.;Initial Catalog=Cars;UID=sa;PWD=");
sqlConn.Open();
SqlCommand cmd =
new SqlCommand ("Select * From Inventory", sqlConn);
cars GridView.DataS ou rce = cmd.ExecuteReader();
carsGridView.DataВind();
sqlConn.Close() ;
)

Если при создании проекта вы выбрали вариант File System, то при вьшолне­
нии Web-приложения автоматически cTapryeT WebDev. WebServer. ехе (очевидно,
что при выборе IIS этого не будет). В любом случае используемый по умолчанию
браузер должен отобразить содержимое страницы.

Отладка и трассировка страниц ASP. NET


Вообще говоря. при создании Web-проекта ASP.NEТ вы можете использовать те
же средства отладки. что и при создании любого другого проекта В Visua1 Studio
2005. Так. вы можете устанавливать контрольные точ1{и в файле внешнего кода
поддержки (и в блоках script файла *.аэрх), запускать сеанс отладки (по умол­
чанию для этого используется клавиша <F5» и использовать режим пошагового
выполнения программного кода.

Но. чтобы выполнять отладку Web-приложения ASP. NEТ. ваш узел должен содер­
жать правильно скомпонованный файл web.config. В главе 24 структура файлов
Web.config рассматривается подробнее. но. по существу. эти XМL-файлы служат
той же цели. что и файл app.config выполняемого компоновочного блока. Если
ваш проект еще не содержит файла Web.config, Visua1 Studlo 2005 это обнару­
жит и добавит такой файл в ваш проект. Соответствующим элементом является
< compi 1а t ion>.
< сопfig\паtiоп
xmlns=''http://schemas.microsoft.com/.NetConfiguration/v2.0">

<system.web>
<compilation debug="true"l>
</sys tem.web >
</configuration>
Вы также можете разрешить поддержку трассировки для файла *. а spx, устано­
вив для атрибута Trace значение true (истина) в рамках директивы <%@Page %>.

1
rrl8sa. 23. Web-с;траНk1ЦЫ и Web-элементы управления АSР,NП 2.0 1035
<%@ Pag€ Langu:age="C#" AutQEventWireup="true H
CodeFi le="D€faul t • aspx, C~" Inhe'T i 't.$=''''_ Oefaul t" Tra.ce=" true" !1,> ;>.

в резулнгате генерируемый H'fML,-1WД будет 'содержать l'УШОJ.'О"IислеllНЫе IIодроб­


НОСТИ • .кacaIOщиеся предыдущего ЦИЮ1З запроса/ответа Н1ТР (перемеllные еерве­
ра. сеанса. ПРИ]JОженил 'R11~Дo). Чтобы добавить к Hl'iM свои собствeНlIЫf" сообще­
нив: 1'Рассиров:ки. можете ИСIICшьзова.ть свойство 1'raee ТRIIЭ Syste'm.Web. tJl. Page_
В moбое Bp~. коrда вы JJож~аете зanиtать ПЬJIbЗЬвателъское сообщение (из блока
С11енария или файла исхФДНого Jiода с.#). просто Bbl3OВl!tгe метод Write О.
,P'l:otect,ed void btnFi llG:rict_ Сl l,z,k (oJDj ect send.er, EventA:trgsej
!

1/ ГеllерИровавие поnьs~а!l'enьсх<)rо сооб1Це~ o:topзс;:с.иро.жи "


ТiаGе.Writ.е(''''М'~,;;r катеГОf)Ия.", "Конец sanо.гmе1',ия та:бmщы"),

Есди теперь запустить npoeKT и нanpamпь вторичное <JбращеRИе Web-сериеру.


вы увидите сВdЮ IIользовательсtqтIO KaTeroplUO 11 щ!)льзова:reльское СОQбщен;ие бли­
же ,,:концу раздела трассироmtи перед разделом Control Тгее (рис. 23.13).

Ф<oin ~ I!>,A ~ Cel>eНE .сГЧW"''' ..

0'- . '" ~ [;1 ·Юt " ;) ~ ~ ~ е 1&- ~ g ~j I:r 11


;;I5p\(',j),a ge 6e\j11'! Ra:ise D,DЩ),О41
МРЯ К<irt1гtJ1'JИ51 )СItII'I!'IЦ s.aI1[]ЛнеНI1" та.fin}"ц,", 0,757695206430398 0,121<187
asP!! ,p.a~e Еni:l 'ItВ~ P09t!jilckEvBnt О, 159779579~9~7В4 0,002064
asp~,paga Вeqin LD<ldCuтpJete Q,7S9B222~01D~29 liJ ,'00 00 'J.3
a5p~...p.age End LoadComplete О, 7.s9,65а~1121$2бб [J,.tJЩfОЗ]
as~.page Elegln prel'!ender О.7S9.ев29435СlЗ!1 0,000030
aspx, page J;;nd ,PreRender О, 7S99ввзаi~М92 D,ООООН
aspx .реЧЕ! Begin РПlRвnd9r'Comрl8te О.1599В7б~7З9D972 О,ЮОD029
aspK.page Елd PreReflt;le~9mplet8 О,,7t5001б915БЫ~SЗ O,OD.I'I029
iiSp>(, page Вegiп 5a\l8State а,768ЗG577207Щi8Э О,{)()82В9
asp~,pa9B E'lId SaveSt"te о, 775."7'f9~В19<Щ;!i О,DО7Щj9
aspx"page Begin sа1lеStatеС'DtПPlet"l О,77S71SЗ29&57Ь.SS DJЩОО40
asp,y',.page Е:Пс:! 5~IIB5t<lteCctmpie~EI 0,77!i'I'4Б2317,7:IЗ4З 0,000031
ilspII.pal;j8 Begin Rs!1dl3r 0/775775101471419 0,00002'9
~

Рис. 23.13. з,anIllСЬ по.гn.зовательоких СО(l)бщений траССI3РОQI<И

Исходный 1(0'((. Файл примера CodeВehindPageModel размещеi-I в под~талоге \ СDотввтствующем


mаве2Э,
1036 Часть V. Web-приложения и Web-сервисы XML

Структура каталогов WеЬ-узла ASP.NET


Если У вас есть опыт создания Web-приложений с использованием ASP.NEТ l.x.
для вас можете показаться весьма удивительным то . что множество привычных

для вас файлов (Web.config, Global.asax. AssemblyInfo,cs и т. д.) новыйWеЬ-узел


не включает. Кроме того. шаблон Web Site предлагает папку App_Data. но. кажется.
в окне Solution Explorer отсутствует папка References.
прежде всего, следует подчеркнуть, что файлы Web.config и Global.asax. ко­
нечно же. в ASP.NEТ 2.0 поддерживаются, но вам нужно ЯIЗно добавить их в проект.
выбрав WebSiteqAdd New Item из меню.
В шаве 24 будет рассмотрена роль этих двух файлов. поэтому пока что о деталях
не беспокойтесь. Знайте также. что вы можете добавить для Web-узла любое чис­
ло ссылок на внешние компоновочные БЛОIШ .NEТ с помощью выбора WebSiteqAdd
Reference из меню (при этом, как мы позже убедимся. результат будет немного от­
личаться от интуитивно ожидаемого).
Другим существенным отличием новой схемы Web-приложений является то.
что в Visual Studio 2005 Web-узлы могут содержать целый ряд подкаталогов со
специальными именами. имеющими специальные значения в среде выполнения

ASP.NEТ. Эти "специальные подкаталоги" ОШj.:саны в табл. 23.3.

Таблица 23,3. Специальные подкаталоги ASP.NEТ 2.0

Подкаталог Описание
App_Browsers Папка ДЛЯ файлов определений. которые ИСПОЛЬЗУЮТСЯ для иденти­
фикации браузеров и выявления их возможностей
Арр Code Папка ДЛЯ исходного кода компонентов Или классов, которые вы
хотите компилировать, как часть вашего приложения. Программный
код из этого подкаталога компилируется при запросе страниц и ав­

томатически будет доступен вашему приложению

Папка для хранения файлов * .mdb Access, файлов * .mdf SQL


Expгess. XML-фаЙI10В и других наборов данных
App_GlobalResources Папка для файло!! *. resx, которые доступны из программного кода
приложения

App_LocalResources Папка для файлов *. resx, которые привяза~JЫ к конкретной странице

App_Themes Папка с набором файлов, определяющих внешний вид Web-страни­


цы и элементов управления ASP.NET
App_WebReferences Папка для классов агентов. схем и других файпов, связанных с ис­
пользованием Web-сервисов в приложении

Bin Папка ДЛЯ скомпилированных приватных компоновочных блоков


(файлы * .dll). На компоновочные блоки из папки Bin приложение
ссылается автоматически

Добавить любую из зтих подпаПОli в Web-прилож.ение можно явно. выбрав


WebSiteqAdd Folder из меню. Но во многих случаях это сделает сама среда разра­
ботки, как только вы "естественным образом" добавите соответствующий файл
(например, при добавлении в систему узла нового файла С#. автоматически в
CTPyRТYPY каталогов добавляется папка App_Code, если она в этот момент не су­
ществует).

Глава 23. W~Ь-С'rраf.t·ИQЬJ и Web-элеМ6Ь!ТЫ упРlJвлеlli'iА ASP.NEТ 2.О 1037

Роль лалки Bin


Позже вы }'"Видите. что Wеь"страницы A$P.NEТ в :конечном сч.ете IЮМJдtJДIpy:iOТ­
сп Б .номпоновочныЙ бдок .NE'Т. Щ)э't'ому не ДОЛЖНО быть 1:{еoжщraиност~lO ТО, что.
Web-узлы :могут С(.'1!lЛатьс.R на .JJ,Ю60е. :чием пр-:Иватных ИJПI общедрстуцных. K0МIIO~
НОВО'ЧНЫХ бло.ков. Б ASP.NET 2.0 метод указаниЯ внешних IЮМПОНQВОqщ,IX бло.КОВ 1
необходимых Д:П.Я- дащщrо узла, в ;корне О'I'.ЛИЧаетсfI от того. ч.то npедлar~.JI{)С:.ь в
рамках ASP.NEТ 1.Х. Пр~чи;ва такого itзменения в тоМ. ЧТО теперь в Visual 5tudio
2005 Web-ya'nbl траюуются в безлроеJCТ11НoU форМЕ.
ХОТЯ шаблон WеЬ S1te игенер1dpУет файл '1; .з1 п. с ПОМOЩblO :которого. MO~O за­
грузить файлы *.азрх в среду рааработки. связаНF!ОТО с НйМ фaй.Iщ".с~рrоj не
СУЩlК'твуе.т. Вы. воз."roиНО,Зl{ае:ге. что проект WеЬ~приложенЩI ASP.NET 1.х эа­
nиm.mал информацию оБQ всех 13Нец:uIИX компо.новочных блоках :в фай:J;r *. сэ proj,
Этот факт порождает резоЩ!ЬJЙ воцрос: "!Де храннтся информация о BH~ "ВОМ­
nОНОВОЧНblX блоках в ASP.NEТ 2.0?"
Когда вы с:сьmаетесъ на прив~ТНЬiЙ ~ом'nоновочный блок. Vlsual Stш:liо .2 005
авТQматичесfW соэдаеткаталог B,in в cтpyкrype КЗТaJlorов приложении. чтобы со­
хранить там ЛО1\ЭЛьную копи,ю ДВОI:IЧнш'О файла. При испо~ованJЩ вanщм про­
траммнЪ1М :кодом тщтов из СОО11JeТ{!ТВyIOщИх библиотек программнorо KOД~ они ав­
томатичесltи зarружаются по щ;рвому запросу. ДтiJl проверЮi антивизирy1iтe меню
WebSite~Ad' d Referenc~ и выберще 1Iюбой [НО не строго именовщmыЙ'} фaibr*.dll
из тех. которые вы создали в ПJ)ацессе .изуЧ.ения текста этоЙmmrи. и вы qбщруЖИ­
те, -что в 01tНe Soluti'on Explorer отображается папка Bin (рис . 23.14).

Риll. 23.14. Папка Ei n содержит КОпИИ


всех приваrкы-х j(·омпоново>lныx блоков, на
которые ссы:nается ПРИnОJК8ние

Если же BQI сСЫЛаетесь на общедосrymiЫЙ :компQново"<'uiый 'МОК, VlsuaI Stu.dlo


2QОб аВ1'ОмаТliЧескИ добавля"т в TeRYIЦee 'Web-решение фаащ Web. CGh f ig [~C~ его.
еще :нет) и зa.nисывает внеIПЩQЮ ссылку в рамках элемента <a5l$em.blies>. Так,
есЛй снова: ак.ТИЩJJзироваТI::! ме.Ю9 Web$JteE;1>AddReference. но на этот раз B~Pa.'Ть
общедоступный :КОЩ10НОВОЧНЩЙ' ~лqR (например. System.Drawing.dll). то J!Ы об­
наружите. 'Что ваш файл Web.conifig примет следующий В'ЙД.

<?хтl V'ersion="l. 0'''7>


<.;:()·tJfi.gш:аtiоi\ хmiПS="Ь·ttр; I /SCheroa,s. miсП)s·оft.'соm/ . Net.co.nfclgцrаtiоп/V:2 • О">
<:af'pS e.t t i 'n g 11 ">
<с::опn-есtiопString.s / >
<зуstеm.wеЬ>
1038 Часть V. Web-приложения и Web-сервисы XML

<compilation debug="false">
<assemblies>
<add assembly="SysteJD_Drawing, Version=2.0.0.0,
Culture=neutral, рuыlскеутоkеn=возF5F7Fl1D50АзА''/>>
</аssешы1еs>>
</compilation>
<authentication mode="Windows"/>
</system.web>
</configuration>
Как видите. каждый компоновочный блок описывается с помощью той
же информацию. которая требуется для динамической загрузки через метод
A.ssembly.Load (} (см. главу 12).

Роль папки App_Code


Папка Арр_ Code используется для хранения файлов исходного кода. которые не
привязаны непосредственно к конкретной Web-странице (как файлы с внешним
кодом поддержки). но [юторые все равно должны компилироватьсн для использова­
ния вашим Web-узлом. прогрaммный код из папки Арр _ Code будет автоматичес{(и
компилироваться в фоновом режиме по мере необходимости. После этого соответ­
ствующий компоновочный блок становится доступным любому другому программ­
ному {(одУ Web-узла. В этом смысле папка Арр Code во многом подобна пanке Bin,
за исключением того. что здесь вы можете сохранить исходный код вместо ском­
пилированного npограммного кода. IЛавным преимуществом такого подхода явля­
ется то. что оказывается возможным определить пользовательские Тl:ШЫ дЛЯ Web-
приложения без необходимости компилировать их независимо.
Одна папка Арр _ Code может содержать файлы программного кода. созданные
на разных языках. В среде выполнения подходящий компилятор сгенерирует IJYЖ­
ный компоновочный блок Если же вы предпочитаете хранить такие файлы про ­
граммного кода раздельно, можете определить множество подкаталогов ДJlЯ хране­

ния файлов с управляемым программным кодом разного типа (*.сз. *.vb и т.д.).
Для примера предположим. что вы добавили в корневой каталог приложе­
ния Web-узла папку Арр Code, содержащую две подпапки (MyCSharpCode и
MyVbNetCode). которые содержат фaйлъr. написанные на соответствующих языках.
После этого вы можете создать файл Web.config. который указывает на эти под­
папки с помощью элемента <codeSubDirectories>.

<?хml version="l.O"?>
<configuration xm.1ns=''http://schemas.microsoft.com/ .NetConfiguration/v2.0">
<appSettings/>
<c onnectionStrings/>
<system.web>
<compilation debug="false">
<assemblies >
<add assembly="Sys tem .Drawing, Version=2.0.0.0,
Culture=neutra1, РubliскеуТоkеп=ВО3F5F 7 F11D50АЗА" />
</assemblies>
<codeSUЬDirectories>
<add directoryName="МyCsharpCode" />
<add directoryN~"МyVbNetCode" 1>

Глава 23. Web-сrpЭliИЦЫ и IIVеЬ ·э.леli.1ентЫ управлэttИЯ д.sР. NП 2.0 1039


</ coc1esuЬDi.rectori:es>
< /-с оmр} la tlo t,:>
<а ut.h е пti раЦ ол m о de="W iШЮ W 5 " / >
< t'S узtеttL . web >
</ СОl1гigurа t i ОП">

Замечание. ПаПJ(д Арр _ Code \.iaCTO используется и для хранеНИQ файлов , которые не ЯРflЯIQТСЯ
ф~ла~и Q лроrpамМНЫМ кодом на конкретмом ЯЗЫIЩ но тоже оказывщотся необ)(OДIoIМЫМИ (на­
пример, файлы *.xsd. -;.wsdl и Т.д.) .

Цикл компиляции страницы ASP.NET 2.0


Неаависимо от l.'oro. накую модедь стрaвJЩЪ,I вы НC1Jолъзовали [OДНOMOдYJlЬнyIO
'CTPfi1:i'ilЦY или страницу с внешним IЩДОМ ПОДД~РЖЕИ) . ваши файлы *. azpx (.как
и любые связанные файлъl с ЮДОМ П0Ддерж,ки) динамиче~ки КОМIlЙЛируются в
действительный НОМПО1l0ВО'ЧflЫЙ бшзк .NEТ. ЭТО'l' kОМПОНОВОЧНЫЙ блои затем об­
раба'lЪ1Вае.тся в ра..чках рабо-qего процесеа ASP.NEТ (a's pne t _ wp. ехг) в пределах
собственного домена npиложения Iд.iUIL получеmm более nO;ЦРQбной 'Информации о
д~менах. приложений СМ. rnаву 13). Однmroметuд:ttомпи:ляц;и:и1ЮМ'I1ОНОВОчн.ого бло­
:ка Web-узла вЛSP. NEТ 2.0 ОJЩЗЪtвается совершенно :иным.

Цикл .компиляцииодномоду,nьных страниц


При использовании мод.ели uщюмодулыroй страницы. HTML-раЗМе1'Rа.. блоки
< зс:;т i p t> :и о:пределеmш W~b 'Meмw'rol! управле'НИН динамически RОМIlЙЛИРУЮТСЯ
в тип .класса. nPОИЗВОДНЫЙ отsуst ещ.Wе Ь.UI. Ргgе.
имя э'Того RJIаreа получается из имени файла "' . aspx с IЮМОII.,ТЬЮ пр:исоеДИНения
суффикса азрх 1t имеJm фal-iJШ (иапример. crращща Му,Ра9 е . аэ р'F. noрождаеттип
масса с именем MyPage _ asp x). На рис. 23,15 ПOlЩ·\ЩБа общая схема соответству­
ЮЩf:ГO процесса.

Аатоматически Сiеl4ер~рованныЙ
RОМnОНОВQЧ'flЫЙ блок в a'&p n et_ Ер.ЕХ!?
,
"
КОМhИnЯТОР
System.Web.U'I •.Page среды J:I1>mолнеНИfl
(базовЫй xnасс ДЛII !lC6X
кnaccoв *_IiSrж)
,...':"-: -
,
MyPage_a:8PJl "
" - M:yP.a ge .aspJC.
,
(Aмнat.l1N8CКМ oll~1i И '. <htm.l>
IfOМП~'rМП Юlщ:са)
.. .
<j htm1 >
- - 1"
,

PJlс. 23.111. Модель I(ОМЛИnЯЦИИ OДIiOMoДYnbHЫX СТРаниЦ


1040 Часть V. Web-приложения и Web-сервисы XML

Этот динамичеСRИ ~омпилируемый компоновочный блок устанавливается в


определенный средой выполнения подкаталог в папке <%windir%>Microsoft.NET\
Framework\v2.0.50215 \ Temporary ASP.NET Files\root. И мя пути после \root
зависит от целого ряда факторов (хеш-кода и т.п.) , но в конце концов там можно
найти соответствующие файлы * .dll (и файлы поддержки). На рис. 23.16 показан
пример одного такого компоновочного блока .

.. Ю'I, d',I', Г-lfПIrs<"1


FIIO Ectt VieW ~orIt.. T00I5 Нelp

IIJ Aenвme thls fII!t


~ Мove thiJ f'4e
1QСОрУ, thl.fje
• I'IJbU5h thI. fJie 10 см
Web
S~oofNIII tt>/.
f1te
,. k >Delllte u.s f1le

Рис, 23,16, Автоматически сгенерированный компоновочный блок ASP.NEТ

Цикл компиляции многомодульных страниц


Процесс IЮМПИЛЯЦИИ страницы, построенной по модели с внешним кодом под­
держки. подобен процессу компиляции одномодульной страницы. Однако получа­
ющийся при зтом тип. производный от System.Web.UI.Page, компонуется из трех
файлов (да, именно из трех, а не из ожидаемых двух).
Взглянув на предыдущий пример CodeBehindPageModel. вспомните о том. что
файл Default.aspx связывается с парциальным классом Default. размещенным в
файле внешнего кода поддержки . Есл.., вы имеете опыт работы с ASP.NEТ 1.Х, то
можете спросить. что же при зтом происходит С описаниями членов-переменных

для различных Web-злементов управлеl:lИЯ и с программным кодом в пределах


InitializeComponent (), в частности с программной логикой обработки событий. В
ЛSР.NEГ 2.0 все это собирается в третьем "файле". генерируемом в памяти. Фактически
это не совсем файл. а представление парциального масса в памяти (рис. 23.17).
В рамках зтой модели объявленные в файле *. аэрх web-элементы управления
используются для построения дополнительного парциального класса. определя­

ющего все члены-переменные интерфейса пользователя и программную логи­


ку конфигурации. которые в ASP.NET l.x обычно находились в пределах метода
Initia l izeComponent (). а в данном случае остаются для нас невидимыми. Этот
парциальный класс в процесс е компиляции объединяется с файлом внешнего
кода поддержки, чтобы в результате получился базовый класс генерируемого типа
класса _ aspx (в модели компиляции одномодульной страницы генерируемый файл
_аэрх получается непосредственно из System.Web.UI,Page).
В любом случае после создания компоновочного блока в ответ на исходный
НТТР-запрос этот компоновочный блок будет использоваться многократно для
всех последующих запросов. f-Ie требуя перекомпиляции. Вот почему первый за-
. .,.

Главе 2'3, Web-СТР\ilIiИЦЫ ~ 'Weo-элемеНТbI управлеНI!Я .ASP. NEТ 2.0 1041


npос страницы". aspx может заяима.:гъ 1\ШОГО времеfЩ. ~ посл~щие оБРащения
к той же страницы окаэьщaIOТс.я Н8МНщо бы.стр~.

I<омпиnятор
среды System.Web.OI.page
IIЫПОЛ11QНИя' (ВаэОSl,iйlФасс,для всех
КЛассов ._aзpJ()

MyPag.e _ dl!'P)( • СЗ Геl1ерируемый


(определение naрqи8ЛРНйi'Q парциальный кnacc нз б~
k118сса 11 файле внеLIJнеro k~) МуРаg,Э.81;рх

",
~"
••
ДВтомэтичееж,и сгенерированный ••,
комлоi'j08очl'lый блок .м~,Р.аче .аsрк
в' aspnet _sp . e')le <ht m.l>
...
Myi'age ...:i'iзрх. </html)
(готозыlй кnaco)

Рмс.23.17, Модель КОМПИЛЯЦИИ MH(jr{)MOдYJ1ЫHb/}! страниЦ

ЗIIМ~Ч8ние. 8 ASP,NEТ 2,0 тепвр'ь Щ)*~IO выполнить преДj(ОМПИЛЯЦИID всех (или He~OTOpoгo ПОД­
Мtlожествз)' страниц WеЬ·уэла ';: ПОМОЩью спеЦИa.J1ЫJОГО инатрумеwта J(оманд~ой СТРО ' КИ
a.spn,et cQmpil €1 r ,ехе, Более конкреl'tot8ЯИНфОРМация по этому ВОПРОСУ имеется в дo~y­
ме:нтациИ,NЕТ Framework 2,0 SDK.

Цепочка наследо'вания типа Page


1\aJ< вы 1'ОЛЬКQ что убед.иumеъ. ГQТОВhIЙ тенерируем1йЙ :класс, 1i!.редставляюЩИЙ
файл * .а;эр". получается из sy:st€m.W~b. UI . Раче, J10добно любоJ'dY ба60вОму классу,
Э1'07 тип обесдеЧИIJает по.i'lим.орфный ~ерфейс всем Р:РО}j:ЭВОДВЫМ ТJШЗМ; ОдНа.но
тип P&ge IIВдяе1'СЯ не единственm.щ членом в иера.рхии щ!следовЩщя. Еели найти
тип Page (В пределах компщ:щ!3рчного блока. Syste.m-.Web,..dll) в qкие оБQзрева.'1'е·
.тm объе8ТОВ Vis.ual Studlo 2005. то 'Вы увидите, что этот тип "принад;цеЖJiТR типу
1'smp:LateCdQt:tol, который. в (';Вою .очередь, "ПРщшд.lIе)IQff" .C·on"t~ol, а послеДЮIй
"ПРИЦад/1ежит" Object (рис.2З , 18).
ВЫ должны ДОI:адыватъ(:Я, что каждый ИЗ этих базовых ЮЩссов вн·осит в файл
... ;аэрх свой "'немaлый вкдзД- n оrношtшии функционады;l~ ~озмо~остеИ . для
больПIИНСТ.IЩ I1poexгtm вы буд~е использовать члеН!il, onpeд~eнf.lыe в рамвах ро­
ДИТeJ'1ЬCЮiX классов Раче 1:f Ссщt rоl. Вообще говори, фУНКЦИОfJаЛЫ;Iые :РОЭМОЖRDоТИ.
I1риоб~тенные ОТ масса 'S ystem.Web.UI .'r'ernpi.ateCo;n.t ;roJ: . могут представлять
для вас щrтерее 'ГЩIЫЩ при .~остроении ПQJIЪЗО]ЩТe;Ilbе:в:щ элеме;нтов управления
WebFbnn и при взаим(!)действии с прсщеССQМ виэуaJni3аЦИИ. с этими оrоворками
давайте рас~мf)ТРИМ роль типа Pa'ge.
1042 Часть V. Web-приложения и Web-сервисы XML

Тип System.Web.UI.Page
Первым интересующим нас родительским классом является сам класс Page .
Ниже описаны его многочисленные свойства, обеспечивающие возможность вза­
имодействия с различными Web-примитивамн. такими как переменные приложе­
ния и сеанса, запросы и ответы НТТР, темы и т.Д. Описания некоторых их этих
свойств привоДятся В табл. 23.4.

Табnица 23_4. Свойства типа Page


Свойство Описвние

Applic ation Позволяет взаимодействовать с переменными приложения для текущего


Web-узла

Cac he Позволяет взаимодействовать с объектом кзша для текущего Web-узла

Cli e ntTa.r get Позволяет указать способ визуализации для данной страницы в зависимо­
сти от запрашивающего браузера

I s Po stBa c k Получает значение, являющееся индикатором загрузки страницы в ответ


на вторичный залрос клиента (в отличие от первичной загрузки страницы)

MasterPageFile Создает шаблон страницы для текущей страницы

Request Обеспечивает доступ к текущему НТТР-запросу

Response Позволяет взаимодействовать с ИСХОДЯЩИм НТТР-ответом

Server Обеспечивает доступ к объекту Ht tpSecve rUtility, содержащему раз­


личные вспомогательные функции сервера

Session Позволяет взаимодействовать с сеансовыми данными ДЛЯ текущего вызы­


вающего объекта

Theme Получает ИJ1}t устанавливает имя темы. используемой ДЛЯ текущей страницы

Trac e Обеспечивает доступ к объекту TraceContext, позволяющему записы­


вать пользовательские сообщения в ходе сеанса отладки
Глава 23. web-страницы и WеЬ·алемвн'Ты управления ASP.NEТ 2.0 1043

Взаимодействие с поступающим НПР-запросом


BI:iC~e БИДeJЩ выше, 'Ю'о основной поток Web-.ceaнca начинается с рerистраций
IO,I~eHT~. В1!Р:ца прлыювателЬСRОЙ иНформации и щеJГIКа на .кнопке Отправить, в
~зущ.таre чего данные JПМL-формы вапРaRJIЯЮтСя Web-'Страшще для обрабm'ки"
Б БOJ:~'lJIЩii::'I1Jе случаево'ГRРЫВ~ дескриптор fO'I:1JI содержит атрибуты асt.iо:п
и met:l'H)d. укаЗывающие фЩbJ на Web-сервере, 1«>ТОРьЩ- должен обеспечить Данные
рвэ.JИfЧН.t:i.l!ll Н'ГМL-элемеНТ"dМ, И'метбд пересЫJIJm этих данных (GБТ WШРОSТ).

<fcrm :n .ame="de-:fаtйtРаg.еr, id="def.ou 1 tpe.ge D асtiоn="httр:lllосаlhФ$t/


·Cars/'Classi:cAsppage.asp"· .m ethod = iiGETi'>
</'IQrrD>

В 0ТJIИЧИe от класtшчес:коЙ технологии А$Р, в рамках АБР.NEТ объект с именеМ


Reque.st не поддерживается. Однако -все страницы ASP.NEТ насле.цylO'Г своЙс.тво
System. Weh. а1. Pag.e.R'equest. обеcnечивающее досl}'II 1': эиземп.tutpутипа класса
HttpRequest. в табл.23.5 прец.11ага.ются: описания нсноторЫ1Х базовhIX: чmшов ука­
занного 'I!ИI!а. и не уДИВЙтсЛЫlO. что вти члены предлагают возможн.ос.пr, андло·
l1ff'oIНble ВQЗМ0ЖF10C"FяМ -ЧЛенов. присутствующих в уте устаревшем объеl:!Те Rёquеst
:н.лассичееJtОЙ модели АЭР.

Таблица 23~5. Члеl'tы типа' HttpReqt.1e:st

Описание,

Аррliсаtiол Ppth Получа.GТ путь IC виртуальНому каталОI)i прlofЛ'ожеtJия ASP.NEТ на сеf!J.вере

Bro~ser ОбвспеЧИезет информацию о возможностях 'бра.узера кл'иента


Cookies Получает коплеlЩ~ltd файлов cookie, оmравлеНIIЫХ БР"аузерам клиента
f'ilePath УI<а,зывает виртуалЬНf:!JЙ путь текущего запроса
Fru;щ Получает коллекцию п.еременнblXфОРМЫ
НеасШта ПОJ1)"'\аij.Т I(ОЛЛSlЩJ>11О НПР-заголовков
НttрМеthщi Vказblвает метод переда\1И НТТР·данных, Иl;)пощ,ау&мый IUIllfеtпом
(GБТ, POSTj,
IsSееurеС;СЩ,Г1есt i оп Имикатор эащищенноGr.И НПР·соеДинения (т. е . использования НТТРS)
Q·uerYStri-ng Получает «WI61Щ11Ю строковыхпервменных HTTP-запраСfl
RawШ:l Получает ·'G/>IРОЙ·' URL твк}'щ~го заПJ,Jасj;!
Rеq1.lезtТуре УказЫ'вает метод передачи hnP-Аj3}!НblХ, ИСfll0льзуемый Кf1иентрм
(GET, РОБТ)
sеrJ'еrvаriщыlss nOJ1уча:еr tоллеJЩИIO nереМ'енных Web-серElера
UsеrНо:~tАddrе:8Э Получае:г IР-адресхостаудалеfiного КЛиентэ.

Userflos·tNam.e ПоIiучае,т DNS-имя удаленного клиента

в доfIо.лнение к этим свойствам 'l'ИП HttpRequest предnwает ряд ПQлезных ме­


ТOДQВ. ВlUlJОЧая: слt:дY1OщИе.

• Map-Patb (J, Отображает Виртуaпъ;l'IЫЙ nYТЬЗЩJрошещlOГО Mpe~a[)RL в физи­


'Чесюm путь на серщ,ре ДJUI текущего ~Щlр~.
1044 Часть V. Web-приложения и Web-сервисы XML

• SaveAs (). Сохраняет информацию текущего Н1ТР-запроса в файл на Web-


сервере (что может оказаться полезным при отладке) .

• Va1idateInput (). Если с помощью атрибута Validate соответствующей ди­


рективы страницы разрешена возможность контроля данных. то зтот метод

может вызываться для провеРЕИ всех вводимых пользователем данных (вклю­


чая данные cookie) на случай выявления потенциально опасных вариантов
ввода (из предусмотреНliОГО списка таких вариантов).

Получение статистики браузера


Первый интересным элемеНТОМ типа HttpRequest является свойство Br owser.
обеспечивающее доступ к базовому объекту HttpBrowserCapa bi 1iti es. Объект
HttpBrowserCapabilities, в свою очередь, предлагает множество членов, кото­
рые позволяют программно исследовать статистику браузера, отправившего посту­
пивший Н1ТР-запрос.
Создайте новый Web-узел ASP.NEТ с именем FunWithPageMembers. Нашим пер­
вым заданием будет построение пользовательского интерфейса. позволяющего при
щелчке пользователя на Web - злементе управления Button увидеть различную ин­
формацию о вызывающем браузере. Эта информация будет генерироваться дина­
мически и присваиваться типу Labe1 (с именем lblOutp u t). Обработчик события
Click для Вutton будет таким .

protected v o i d btnGetBrowserStats_Click(ob j ect s ender ,


System.Eve n tArgs е)

string theInfo - " ";


theInfo += String. Format ("<li>Это клиент AOL? ( O}"I
Request.BrowS8r.AOL);
theInfo +-
Striпg.Fоrmаt("<li>Поддерживает ли клиен'r ActiveX? (О}",
Request.Brows8r.ActiveXControls) ;
theInfo += St ring. Forma t (" <li >Это клиент Ве ta? ( О) ",
Request.Brows8r.B8ta) ;
theInfo +=
string. Format ("<li>Лоддерживает ли кл иен т Jav a ? {О} " I
Request.Brow.8r.JavaAppleta) ;
theInfo +=
string. Fomat ("<li>Поддерживает ли клиент c ook i e? (О }",
Request.Brow.8r.Cooki8S) ;
theInfo +=
Stril1g.Fоrmаt("<li>Поддерживает ли клиент VBS c ript: (О)",
Request.BrowS8r.VВScript);
lblOutput.Text = theInfo;

Здесь проверяется целый ряд возможностей браузера. Ка}( вы можете дога ­


дываться. очень важно выяснить возможность поддержки браузером элементов
управления ActiveX. аплетов Java и VВScnpt клиента. Если вызывающий браузер
не поддерживает какую-то из Web-технологиЙ. ваша страница 1< . аэрх должна быть
готова выполнить альтернативный план действий.

l

Глава~, Web-стрзыиц", \>1 Web-З11еМ ~НтЫ упраiIЛеflИЯ АSР,NЛ 2 '0 1045

Доступ к поступающим данным формы


дPyrим.и эдем.ентro,rn тщrа HttpResponse тщяются; CBo~{:твa Fo.n!\ н 'Q U€T yS t riпg.
эти два свойства ФУЮЩПОНИРУЮТ ана:rrогwпщ JUЩCРИ~С,КОМУ варианту ASP й .по ~
3ВOnЯЮТ а1:iа:дИЗИРОвать ПОСТУПa:IQщие Дa:El1llirе формы, ИСПОJIЬ3уя пары имен и 3На-
1JенI1Й. вспомди:те 11З J,щщего преды~еrо обсужде}-щ,я :классичеGROЙ 1'ехН'ол:огии
АБР о том.' Что upи OTIJpaDKe I:1IТP-дцнньщ с цомрщыо .GET данные формы будут
дocтyrntы черезСJlОЙС:ТВО<)\JеrуStriI1g, ТОЩа ~ ДЛЯ дoc'JjIJТa к данным, прf':Дстав ~
Лe.IO!ЫМ с помощью РОБТ. используется СЩ)ЙGТВО FO+ffi ,
Для .доступа к Д<UЦIЫМ формы~а на Web-сервере, конечно, МОЖШ> ШШQ.lIЪ'
зовать сволет:ва НttрН~чuеst.FО2-m и HttpReques t ., Qu еrУSlriпg. но ЭТОТ устарев ­
ШNЙ подход tB большинстве случаев) ,I,f e ,яЩJЯеТ~~R fП~0б:ходимым , ВвидУ того. ЧПJ
ASP.NET предлагает свои собственные Web-элементы упра:влениясерверной СТО­
pORht. У вас ееть ВQЗМ0jЮJОСТЪ обращ:;!:г~снс НТМL-элеменгзми :и:нтерфе-Йса. как с
flat"ТоВЩИМИ объектами. 'J1аким образоМ" .вместо ПОл:уЧени.я значения.текстово1'О
бл()}{з в' .варианте

proJ::ec'ted v o :! d Ьtл G е tFоrmDа ta _ С 1 i c 1<. (оЬ] 'Z.,jt S:i;шdеr, Even t ,A :rg s е]


{
/I J;Io.J.1УЧ81Ще з~а ..e!IИJI ,ц.пи э.nемВИ!1!а. с ro=t.xtFi.xstName.
s trin g fi r s,tName = Rе quе s t .F' о tm [ " tх t Fl r з tNamе " ] ;

ElЬT можете :нanрЯI1оIJЮЗВПрОСИ'ТЬ свойст~о Text серверното 'эпе.чен-га упра:вления.

protecle.d v CIid r)tл.GеtFо~rпDаtа~СliСk (Qb je ct s 'imder, E\rent.,/irgs е)


{
/ / ЦО.J.1УV.8вие зи,,"чеиия 'ДJ'lJI. 9'n81111.8JW8 с ID=:txtFi.rstName .
s t л. tJ,g f i r.st.Name = tKtFir s t Name . Тех ! ;
}

Этот ПОдХод ие ТQЛЪКР соответствует строгим 1Jрmщm:xам ООN. .но при этом во·
обще .не ПРИХОД~ТСR ,забо-rитъе.в: о том , шm ~СТНШLЯЮтсg даmn.tе ,формы (GE7 или
~O ST) . К тому же J;IеПQсре.дствеffi:{3Я рабо-та с 6.ц~мeнтOM упра:вленин "Гораздо б.оль­
!Де соотве1'ствуеттребовани:нм типовой безопасности, поскольку здесь возможные
ошибки ввода будут выя5лIjLы ~e да ~тanе :крМIIИJ1fJЦИИ,' а не в. среде выполнения.
Конечно, это не значит. что J\ЗМ в ASP.NEТ вообще 'НИКЩ'да не придетa.t1 иtпоJIЬЗО·
:эатъ свойства F o r ,m ~ Q,Щlr УiSt .r i ng. ~O he-оБХQДИМОCТh в йх исп.ОlIЪзовании c:ylЦe­
ст.веннР уменыцится .

СвойствоlsРоstВасk
Еще одним очень важным членом }fttp Reque-st mmя.eтся своЙ'етво IsPe s t'Вa c k.
Напомним. что ·postbac~· обозначает ВТQрично~ орращение :н JЮНJ{ретffiJЙ Web-
стрaIЩце в ходе ОЩlого сеанса свдзи с сервером. С учетом ЭТО1'о долЖно быть "'nо­
J-"fRТНО, что CB.oik'FRO I'S'Po stBaek возвращает true l14стинаJ. если теь'УщийНТГР­
запрос oтapaв.т.teH уже зареrИСТРИРliШа.нным .Nfl.стоя:щиЙ момент пользователем. и
t alse (ложь). если это первое вза:щ.юд;ейс'твие пользователя со с.траницеЙ.
ОБЫ'ЧflО необходимость в оцредщrении тРП) , -.:rгO текущий НТТР~запрос ввляет ­
с.я :вторичным. ВОЗЦДIЩет тоща, ~oгдa нщroтор~ 9ЛOR npотрвммно,о шща должен
выполняться только при первом о.бращеЩiJ1: ПОЛЬ::JQватешr R странице. НапрИмер,.

J
1046 Часть У. Web-приложения и Web-сервисы XML

при первом ДОС'ry1Iе пользователя к файлу *. азр х вы можете запОJПIить некоторый


объект Data Set ADO . NEТ и поместить этот объект в кэш для испольэования в даль­
нейшем. Когда вызывающая сторона снова обратится к той же странице. вы може­
те избежать необходимости нового обращения к базе данных (конечно. некоторые
страницы могут требовать, чтобы Da t aSet обновлялся при каждом запросе, но это
уже другая проблема).

p ro tected void Page _L o a d (ob j e ct sender, Even tA rg s е)


{
/ / DataSet зanОЛНJJ:ется только при первом. обращении
1/ пользователи к данной странице.
if ( ! 1 sPo stBa c k)
{
// Заполнение DataSet и отправка в кэш!

/1 Использование Da taSet из кзша.

Взаимодействие с исходящим НТТР-ответом


Теперь вы понимаете, как тип Pa g e взаимодейс твует с поступающим I-1ТТР-за­
просом, и следУЮЩИМ шагом должно быть выяснение того. как реализуется взаи­
модействие с исходящим Hтrp- oTBeToM . В ASP.NEГ свойство Resp on se класса Рач е
обеспе чивает дос'ry1I к экземпляру типа HttpRespo nse . Этот тип определяет ряд
свойств , позволяющих с формировать Н1ТР-ответ. отправляемый обратно браузеру
клиента. Описания базовых св ойств этого типа предлагаются в табл. 23.6.

Таблица 23.6. Свойства типа Htt pResponse

Свойство Описание

С а сЬе Возвращает семантику кэширования Web-страницы (например , время


ожидания , параметры конфиденциальности, различные описания)
Cont en tE ncod ing Читает или устанавливает набор символов выходного потока НПР

Conte n tTyp e Читает или устанавливает МIМЕ - тип ВЫХОДНОГО потока НПР

Cookies Получает коллекцию HttpCooki e , посланfiYЮ текущим запросом

IsCl i e ntCon necte d Читает значение , являющееся индикатором продолжающегося соеди­


нения клиента с сервером

Outpu t Разрешает пользовательский вывод в поле содержимого исходящего


НТТР-сообщения
QutputSt ream Разрешает двоичный вывод в поле содержимого исходящего НТТР-со­
общения

Sta tusCode Читает или устанавливает КОД состояния НПР-ответа, возвращаемого


клиенту

Sta t u s Desc ription Читает или устанавливает строку состояния НПР -ответа, возвращае­
мого клиенту

Suppr essConte nt Читает или устанавливает значение, являющееся индикатором отмены


от правки НПР-содержимого клиенту
Глава 23. Web· страНИU;61 И We:b-элементЫ управlIеНИR' ASP.NH 2.а 1047
РаССМЩР:И'1'е 1:'aкJ1{e ОПИСЗRия некоторых методов ТИПа Rt tpRespons е. преДСl'ав­
ленные 11 табл. 23.7.

Таблица 2Э.7. Метояы типа H1:tpResponse

Метод Оnисакие

AddCa'CheDep!i!BdeflCY () Добавляет объект !3 кэш лриложе.IИI1 (см. главу 24)


Clear() УДаляет вее эаголов"КИ 1!1 со:цеРЖ~·'lМое вывода из буфера потоК:i\
End "O ОтпраМiЯ8Т все содерЖИмое буфера вывода к..lиен1У, а затеМ
завершает соединение ДЛЯ данного СОКЕНе.

1'1 uзд () Отправляет вое содержимое буфера ныводаКЛИlенту


Retiireet(} ВЫПQЛ/'lj;Jе'Г перенanРавленив клиента ло новому URL
Wr1te () ЗаТlис.ывает значения в ВЬ,IХQДНОЙ пС/ток HTTP-С<:1ДEiDЖИМDГО
WriteFile () Записывает файл RеПОСDе.дствен~,ю в ,I1ЫХОRfIОЙ лоток
Н:ГТР-содеРЖИМJ)ГО

Генерирование НТМL-содержимого
Пожалуй, саыОЙ известной сферой прйменен:иятипа RttрRе5роп.sеНБ.iIЯетс.fJ З8:­
пись cuдержимото непосредствеmш в выходной t10ТfЖ Н'ТГР. Мe-:roд fjt'tpRespons~.
Write (} позволяет передать J-IТМL-дескрппторы или вообщ€ mобые строковы€! ли­
Teparrы, Метод НttрRезроыsе.WritеFilе () раешир.яет эти воэможности с 'тем. ч'Iо­
бы вы МО:rJIИ yRaэать имя фиаичеСRОГО файна на We'b-cepвepe. содержащего данные,
напpawmемые 11 ВЫХОДНОЙ потои (это оказывается очень удобным в том случае, ког­
да требуется отпрmштъ СQдержимое уже сущеСтвующего фafтa* ..t'JСЛI).
Для прим:ера npедnолоw.им, что вы добавНiIИ в свой фaiш '*. аврх еще один тип
BU't.tOD, ROторыйреалИзует обработчик соББI'I"И".Я,СliсК сер:вератан.

:pro,te,Gted vbid btnHttpResp.O!~.se _Cli,ck (,obj'ect sender, EV8L1tArgs е)


[
Respafise. Wri 1:е ("<Ь>1'<.10е имя l<Jb><br:>") ;
Response. Wri.te (this'. ToStr irig(J ) :
ReSPP!1SS. W!:ite ("<br><bx><b>BpT 'Ваш nОCiJ1едний ЗС1:f'Iрос: < /b><b1i::>") ;
Respcmse, Wri.teJi'ile ("MyRТМLPage. ЬtnJ".);
}

Ролъ э'Т'ой вспомогате.rrы:щй. функции (:RОТОРая может вщэ;ыватъс,я некоторьЦос{


оПработЧИROМ соБЫ'гия на сторо,Ве сервера) очень пр()ста. Единственным заелу"
живающим внима:ни,ц моментом здесь является TQ. что метод Ht t'pResPQ:nse,.
WriteFi1e () теперь отправляет СGдеРЖИМDе фай:ла '".htrn €epBepa иа норневото ка"
талога Web-узла.
Снова подчеРIШем, ЧТФ ВЫ, цон~ч}ю, :мwкeTe исш>лъзовать rю.ЦХод "старой ш;ко"
JIbl", чтоБЪJ: oroбраящ;гь HTML-дескрщn"Оры и содерщимоf'. ИСrд:JЛЬ3';VJ'I метод \f~rit~ (),
Щ) этот ПОДХОД в pmщax ASJ3.NEТ IIpщ.хев;яетG'! гораЗДG реже, чем в, рампах ютасси·
чесной текнолоГ»И ЛSЕ: Причина здесь (снова) в наличии серверных \Vclэ-ЭJ]емеJ;lТОВ
управления, CRWI.teM, ЧТФбы отобразить блок текстовых дaJtшых ~ браузе;ре. ДOCTa~
точно просто присвоитъ подходтц~ эначение свойству TeJi'..t элtJМmIТ'd. Label.
1048 Часть V. Web· приложения и Web-сервисы XML

Перенаправлениепользователей
Друтой возможностью типа HttpResponse является перенаправление пользова­
теля по новому адресу URL.
protected void btnSomeTraining_ Cl ick(obj e c t sеп dеr, Even tArgs е)
(
Rеsропsе.Rеdirесt(Пhttр: !!www. IntertechTraining.c om") ;

Если ЭТОТ обрабОТЧИR событий вызвать с помощью вторичного обращения I(ЛИ­


ента к серверу, пользователь будет автоматически перенаправлен по указанному
URL.

Замечание. Вызов метода HttpResponse.Redirect() всегда влечет за собой обращение к


браузеру клиента. Если нужно просто передать управление файлу *. aspx в том же виртуаль­
ном каталоге, более Эффективным будет вызов метода НttрSеrvеrUtilitу.Тr:апsfеr()
(доступного через наследуемое свойство Server).

На этом мы завершим обсуждение функциональных возможностей Sy stem. We Ь.


UI.Page. Чуть позже мы рассмотрим роль базового l(Ласса System.Web.lJI.Control.
однако нашим следующим заданием будет исследование цикла существования
объектов, производных от Page.

ИСХОДНIoIЙ код. Файлы примера FunWithPageMembers размещены в подкаталоге, соответствующем


главе 23.

ЦИКЛ существования Web-страницы ASP.NET


Каждая WеЬ-страница ASP. NEТ :имеет свой "жизненный цикл". Когда среда вы­
полнения ASP.NEТ получает входящий запрос для данного файла *. aspx. в памяти
размещается соответствующий тип. производный от System.Web.lJI. Page. Д1IЯ соз­
дания ноторого используется конструктор, заданный по умолчанию. После этого
среда обработки автоматически генерирует серию событий.
По умолчанию сгенерированная в Visua! Studio 2005 страница с внешним ко­
дом поддержки определяет обработчик собьпия Load стра.ющы.
public par:tial class _Default : Syst em. Web .UI. Page
{
protected void Page Load(object sender, EventArgs е)
{
}

Кроме события Load. тип Page может вьmолнять перехват любого из событий,
указанных в табл. 23.8 в том порядке. в котором эти события возникают.
..

Глава 23. Web-ст.раНI-ЩЫ и We,b-зле.ментЬ1 управлеми,я ASPoNEТ 2,0 1049


Таблица 23.8. События типа Pag6

Оп~оание

PreJnit ИЬГlОльэуется и'нфраструl<lVPОЙ .. NEТ ДnЯ размеЩВl1ияWеЬ-элемеНТDВ ~прав­


лени!'!, пр:именвния тем, соэданИQ ша(!)JЮНa стр.аниЦЫI и у.становки ПРОфИЛR
пользрв:атв:rщ ВЫ МРЖiпе· пере?Щатить это СО(!)ЫТИЕ!, чтj'J~Ы ВНеСТИ измен~ния
в сооrв~ТСТВУЮЩI4Й прОЦ/1СС
lait ИОПОЛЬЗУ~ТС51 для УСТ;:IНО8'kИ овойств Web-Э.\1вмеНТОIi упрaв.nенwя 1'1 предыду­
щее состояние с помощью :ВТОРИЧНDГО запроса или про.смотра AaHHЫXCOCIO~
>lНИ1'1 (ПОДfJOбгlее об ,ЭТОМ гшщрJAТСЯ в главе 24)
Lc<ad Возникает тоща, когда шраницз и ееЭТlементы vпрввле~LИЯ полностыо ИНИ­
циаЛИЭИРОВafLЫ, а их пре;цЫДУЩjllS 3ffi1L"IЩf\/iЯ восстановлены. С:; этого МDмеюа
вполНе' безопасно На'ЩтЬ В<!l:iимодействие с любым из Web-элементов
i'соБы1'.е•. вЫ.звав.­ События С таким имеыем. коНечно же, Не существует. l'alt здесь 060зна'(еI10
IJJee вторичный любое СОбыт-ие. заставившее браузер отправить ВТОРИ·IНЫЙ запрос Web-cep·
запрос" веру (это может бьгrь, например, щелчок на ЮiОПl'iе)
'р r.eJ>:e ДQ еУ ПРИВIШL<a ДаННЫ!( и I(онфигурациl'l ПОЛl>зоватеЛЬ<i:кего интерфейса :щвершена,
и Э!lементы уf1(.Jнвле.н'ия готовь\ отnраВl41Ъ свои данные в поток исходящего

HТТP-OTBeT~
Unload страница и ее ,элеМеНтЫ управден~я з.аверLL!JИjJИ пjJОL\асс передв'4И даНных, и
объект страницы готов ~ yt:IloL·поженмю. Взаимодействие с ИСХОДЯЩИМ НТГР­
CJТBвJOM В ,ЭТОТ момент породит ошиfiКIjI среды 8ЫПОЛ,неНИR. Можно выполнить
ЗЩ(fЩ1 этого Gобьпия ДI1я"уборки м,(сорэ" На YPOBWB страницы ('IтоБы за­
крыть фаi1J:lЫИ бащ,1 дafoIt1bIX, BbI[1DflIH·m. процедуру выхода из систеМЫ, осво­
бодить ресурСЫI и т.д,)

Замечание. ВСе соБЫТИR ,ипа Page работают!: делвrат"м SYSteln. Eve.ntHar,dJer.

Роль атри6утаАutоЕvепtWirеUр
Чтобы обработать события дmr страницы:, нужиодобавитьв б.поЕ <sc.ript> шш
файл с внеuшим RQДОМ llодцер:lf>.'lШ rroдходв:щий обрабоТЧШt событнн. В O~ от
ASF.NEТ 1.х. тшшръ:ае требуется ВВОДИТЬ всю праграмм:ную логику соБЪiТИБ вруч­
н:ую. Нужно толыto оп:реде1lИ':fь соответствующий ме:гОД, ИCIIOлъзу.в: след.У1ОЩИЙ ша­
ШЮН.

prc.tect~d Ра'Эе_tl<tlmеОfТh€ЕV6'лt (object. sender, EventArgs е)

Например. с:.оБМТfJе I.1hiQбd МОЖНО обработать так.

pLlblice partial с-lшзs ~f.aul t: Sуз.tеm. Web. О1. Page


I
protected 'юid РаЧIе _LQщ:1 ,( Qbj.ect seJ'lder, ЕvепtАrgSl Э)
{
}
jpJ1·iэtе.сtеd voicl Pag-e Пnl0аа (obJecc. .зеl1dе'!:, Event.A rgs е)
{
}
1050 Часть V. Web-прилож:еНИR и Web-сервисы XML

Этот метод, как по волшебству. вызывается при вытрузке страницы (несмотря


на то, что вы не применяли синтаксис событий С#) . поскольку атрибут
AutoEve ntWireUp устанавливается равным true (истина) по умолчанию в дирек­
тиве < %@Page %> вашего файла *.аврх.

<% @ Page Language=" C#" AutoEventWireup="true"


COd e File="Default.aspx. c s" Inherits=" Default" %>

Как подсказьmает имн этого атрибута, при его активизации будет создана не­
обходимая оснастка событий в рамках автоматически генерируемого парциально­
го класса. описанного в этой главе выше. Если установить этот атрибут равным
false. не будутвызваны обработчики событий ни для Lo ad. ни для Unload страни­
цы _Default (вы можете проверить это непосредственно. установив контрольные
точки в пределах обработчиков событий Page _Load () и Page_ Unload ().
Однако, если вы используете стандартный синтаксис событий С# для обработ­
ки событий Load И Unlo a d , как показано ниже:

public partial clas s Default: System.Web.UI_Page


{
public Default ()
{
/ / ЯsНI.IЙ переЮlат соБW'l'ИЙ Load и Unload.
t his.Load +=new EventHandler(Page_Lo ad);
this.Unload += new Ev entHandler(Page_Unl oad);

pr o tected v o i d Pa g e Load( object sender, EventArg s е)


{
Response.Wr ite ("Сра бо тал о событие Lo ad! ");

protected void Page_Unl o ad (obj ect sender, Ev entArgs е)


{
/ / Направить Д8.ВJlJol8 в НТТР-ответ здесь невозко_о,
/ / поэтому :въmОnН.8ТС. запись в nо.anьНWЙ фaйn .
Sy stem.I O.Fil e . Write AllText(@"C: \ MyLo g.txt",
"Выгр узка страницы.") ;

pro tected v o id b tnPos t back_ Click(object send er, Eve ntArgs е)


{
// Здесь НКЧ8r-о не происходит, но это r-арантирует
// :вторичlWЙ запрос Jt С!1'ранице.

то эти события будут перехвачены вашей страницей независимо от значения. за­


данного для AutoEventWireup.
В качестве заключительного замечания напомним, что с момента вызова со­
бытия Unl o ad вы не сможете взаимодействовать с подлежaIЦИМ отправке HТfP­
ответом (еслц вы попыаетесьb вызвать члены объекта HttpResponse , среда вы­
полнения сгенерирует соответствующее исключение) . Поэтому здесь обработчик
события Unl o a d просто отправляет строку текста в файл на локальном диске С.

Гпа&а 23, Web-страниыы и WеЬ~злементы vrjpаВЛ8НИЯ ASP,NEТ 2,,0 1051

Собьrrие Error
Еще одним собьrrueм. которое МQжет nРQИСХОДИТЪ в цmще <:уЩествовмщя: стра­
FIИЦJ>I, ЯВЩIетса еобытие B:rror. 1tompoe такще работает .в паре с делегатом Вуз (е!'!1.
E,1~h.t.H<Hldler_ Это событие ВОЭЕЩRaеТ в том С.JJyЧ<lе. когда метод nPОИ;3ВОДЦОГО ОТ
page тmrn генер.ируt:Т ИСдлЮЧецие. оставmееся: без ЯЩiОЙ обработки. ПрftДnОJlОЖ:ИМ.
ч.ТО вы обработали событие Cllck для типа But tOE на странице. }<I в преДелах обра­
ботчика собыТИSI: (эдесь од IIа3ывается btI1GetFile _ tlick) вы пытаетесь заIЩсать
содержимое лшщлъного файла :6 НП1'-ответ.
Та1tже преДIЩЛОЖИм.. '1ТQ вам не удалое.,. ПРQвери:ть присутствие э;rого файла
с UОМОЩЪ1Q c-r:щщартiНОЙ технологии стр)'КrypИРО:6ЗНiН()Й обработки ИСКlIюченйй,
ЕсJIИ при ЭТОМ ВЬJ предусмотрели обработку события Er ror СТРЗ!:IИЦЫ. вы поду-чи­
те шзr).'С решит}> воанщсmyIO проблему. чтобы по,ПЪЗQватель не yJщдел безобРЗijЦУЮ
информацию 06 ошиБJCе. РаеСМОТРИ1'е следующий програм,мныЙ код.

[)ШЙ.iс partiaJ сlав-э Dе.fаloЙt.: Sуз'lero.Wеh.Ul.Раgе


{
p1.lplic _IJi:fault. ()
1

1/ Соз~ание O~K~a дnк соб~ Error.


tr,i s. Erro:i; += new EvelTt:HEHydl ех: (_ Deiaul t _ Ey'rc:r) ;
}
1I'oid Default_Er;ror (ob]ect 6ende.r, Е:gеn'tA.cgз е)
{
11 УIUtЧ![Iо*еиие 'reJtYЩero O'~Be'I'.a, оообще.аие об O'IIIИбs:е
1/ и ИНфОРМИРО8ание Cp&ДloJ ВШIOлиевиs о '1'08,
/I ~TO о~б1ta обработана.
RеЭРОllsе, C,1~ar () ~
Respo.nse . W;r '[ te ( "И::!ВЙЮ-fТе. •• немоту наити необходимый файл. ") ;
5erver .ClearE!''tor () ;
}
protect.ed . . 'oi d btnGet'Fi 1е_C1ick (obj'ect sешiеr r EventAT';J S е)
{
/I ПОDW'1'1l:& O'1'JtPvn. весущестз~ ф&ЙJl.
11 ~O nO~OQaeT собиwие E"or ДllИ данной СIRpаИИцЫ.
System.IO. File. Re adltl 1 Te.xt (@"С: \IDопtБхi$t. :rxt") ;

J
3дееь обработчик события E.rror Н8.'ЧШIаетС.я с 01ПIСТКИ всего содержимого
имеющетося нтrP-oтвeтa и вывода общ",го сооБЩ~JI 00 OIIIИб:ке" Ч;rооы ПD.lI)"fИ'I'Ь
доступ К ROН1tpетвощ объекту ,Sуst.еш.ЕхсерtiОIJ. вы можете использовать метод
Ht:'tpServerUtili.ty.GetLastError{). доступ к Rоторомуобесuечиваетунаследо­
яа:в:н:фе свойство 'Server,
v,oid Defs:Ult_Error (object seI1der, EV1entj'l.rgs е)

Rеsр<М1S<!. Cliaar () I
R:еSРQлsе, Wri te ("Извините, .. не могу най'j'·~ необходимый файл. <Ь;>" );
1052 Часть У. Web-приложения и Web-сервисы XML

Rеsро пsе. Writе ( striпg. Fоr mа t("ОШИб ка : <b>{OI</b> ",


Server .GetLastError () . Мessage ));
Serve r. Cl earErr o r();

Наконец. отметьте. что перед выходом из этого общего обработчика оllIИ­


бок с помощью свойства Server явно вызьtВается метод Ht.t pServerUtility.
ClearEr ror (). Это необходимо. чтобы информировать среду выполнения о том,
что проблема вами решена , и дальнейшего вмешательства системы не требуется.
Если вы забудете сделать это . конечному пользователю будет предъявлено окно
среды выполнения с сообщением об ошибке. На рис. 23.19 покаЗaIi результат вы­
полнения нашей процедуры обработки ошибщ{.

!I httр:IIlОСdlhоsl : 1()З'.iIРаgеl ifeCycleldefault.aspx - Micr()soH IntcГll . 1_-" Ll I ~1


»

ИзSlПпrге . "" не могу найти IJсо6ходимый файл.


Qшибка: Could not f1nd Ше 'C:lIDontExist.txt'.

Рис. 23.19. Обработка ошибок на уровне страницы

в данный момент вы должны чувствовать себя довольно уверенно при работе с


типом Page ASP.NEТ. Имея такую основу, вы теперь готовы перейти к выяснению
роли Web-элементов управления ASP.NEТ.

Исходный код. Файлы примера PageLifeCycle размещены в подкаталоге, соответствующем главе 23 .

Природа Web-эnементов управления


Возможно. самым большим преимуществом ASP.NET является возможность
КОМПОНОВI~И пользовательского интерфейса страниц с помощью типов. определен­
ных в пространстве имен Sу s t е m. We Ь . UI . We Ь С о n t r о 1 з. Соответствующие этим
типам элементы управления (для которых могут использоваться названия сервер­
н.ые элемен.ты уnравлен.ия. Web-элемен.mы управлен.ия. или элемен.ты управлен.ия
Web-формы) оказываются чрезвычайно полезными в тОм. что они автоматиче­
ски генерируют НТМL-код. необходимый для запрашивающего брауэера. и пред­
лагают набор событий . которые может обработать Web-сервер. Каждому элемен­
ту управления ASP.NEТ соответствует класс из пространства имен System.Web.
OI. WebControls. поэтому такой элемент управления может использоваться в рам­
ках технологии ООП как в файле *.aspx (в блоке <script». так и в файле внеш­
него кода поддержки.
,
Гл:аеа 23. Web-СТРЕ1~ИЦЫИ Web-эnемвМfЫ упра,влеНI!; ASP:NEТ 2.Р 1053
Вы уже видели. что при }Iастр()йк~ Web-злемев;та уцравления в окне свойств
Visua1 StudIo 2005 вmnи изменения заnи:сыв&ютс,я в ОПр'едеJ,l~ние ЭТО)"О элемев­
та в файле ". а spx в Биде набора пар имец :и значений. liапрИl\lreР, при добавле­
нии навато TextBbXB окне проекmрованил файла *.a,BP~ ~ изменении свойств
ВопierSty1е. gorderWidth. EaC':kColor. Bo·r tierColor и Тех]: средствами ЮЕ откры­
ваюЩИЙ д€"Jtриnтор <asp ·: TextBQX> может измениться '.Гах. ~aн пtJ1.taзвно нюке.

<аэр; T$!xtBOx idF'fljyTeKj:Box ru,n a t=" sегvеr'" Вo~d.rsty1..=·"Ridge"


n
Border1ridth="Spx"· ВaQkColor="P"leGtee:n BorderOolor=="I)arkblive-Grееn"
'rext = "[фивет, tT:a:PJ.H~!" :> </:а5р~ТsкtВоЮ>

ПоCJroЛЬКУ НТМL-декдарация WeJ;J~ЭШ~~НТll управления в хонечном счеrе (В ця­


юre .цинзмнче-€КИЙ КОМI:lИl.lЯЦИИ}СТaJД):В~'l'CЯ члено.м:-переменНоЙ ИЗ пространства
имен Sуs·tеm.WеЬ.IЛ:WеЬСОI1tr~15, Щ:iI можете :взаим:аД~'ВОDать с членами СООТ­
ветствующего типа в P81.~ блOR~ <sqr lpt> сервера шш файла о В~ШНИМ кадРЫ
подцеРЖЮJ страницы. например :

p'Ubli,,;; par tial cla.ss "J)efauJt ; Syst.ern_Web.OI.Page


(

р1'ё>tесiеd void btnCl"Ja.flge'TextBoxCi:J l,t>1:_Click (.oojec;t senqer, Ev.e·! )t.A.rgs е·)


J
/1 Иэм~вение дакиux BТТP-o~eтa ~nя~аВИОГQ элеМ.И~4.
tfli s. mY'rext.Bo)/;..E-а.с.k.Со l о r = ~yste!n.. Draw i!1g. Ce,l "'Т. Red;
}

Все Web-элементы упраruIeЙМЯ ASP.NEТ tюсходя:т к обще~ б'<РОво:му классу с


именем Sуstеm .WеЬ.L1I.Wеьс (;шt rаls.WеЬСЬJ'JtrОl. Класс WеЬС·опtrollДЩУчается из
Sys't.e.ro.Web.UI. Cor!trol (который. в с-вою очередь. получается из 5ystern.Objeot).
КШюсl!oJ Cont 1:cl и WеЬСQпt:rсй оnpедещtЮТ QВои наборы ~O~(,TB. об-щие для всех
серверных элемеfI1'ОВ у.правлеJJИ5J. Перед тем КtЩ paCCMQTPMh наследуемые фу.нн­
циоъшльныевозмож:ности эл~ментов упрапдrеJ-IИЯ, давайте выясним. что формалЬ­
но оэна чаftТ обраtJоnа cepвepныx событий.

Обработка серверных событий


с учетом ceToд~тnнeтO COCTOll1UU!World WideWeb ие.ЛhМ не принИмать во вни­
мание пр1>IpОд.У взаимодействия брау:зера щ Wep-~рвера . В еснове такого взаимо­
дейСТВИЯ ЛeжJJТ щ.r.IOl запросов и PT~eTO.Ц НТГР. 'в vропессе БЫПОдВfШИЯ. которых
состояния не сол-ран,юотсл. И ХОТ:я серверные эле~енты управ.пеиин ASP.NET де­
:ЛrllOТ все воЗМОЖНое, чтобы nаба.вить разрабо'F<цШВ: от необходимости непосред­
ственного CI:бращe:RИJ1 к настройкам протокола НТ1'Р, НИRоrдa не заБЪ1Вайте о ТОМ.
ЧТО трактовЮl Web в терминах yupавления событlШМИ -.это ве,дшtOJrеIШЫЙ "фокус"
CLR, дa.lIeKO не 9квивадент!iЫЙ модели ynpавле1фя соUы'I'mJМИ ПОJIЬзовательоRоrо
интерфе~са WindowS.
Поэтому. :МОТЯ простраНС'Fва имен Sys t em. Win-dоwв. Fот.mЗ :и Sys.tem. Web,.
UI;WebContr-о1s оtIр~деЛfIIQттшrы с ан.a.r.rОJ'}iЧНbl1\m имепа.iW1 (B1Jtton. 'l'extBox.
GIidView, IJаЪеl и т;д.). они пред.rrагаютрroщ:ые наборы событий. нanример. I(OГдa
пользователь помещает уназа1'едъ мъtIци на с:йтерхность в LlttO-n Web-формы. у вас
нет возможности обработать событие Mo·u seNo'l1e ыа стороне сервера, И это, оче-

J
1054 Часть У, WеЬ·пр~ложеНИR и WеЬ·сервисы XML

видно, разумно. (Кто обрадуется перспеI<тиве ПОСWIать вторичные запросы серве­


ру при каждом движении мыши?)
Поэтому Web-элементы управления ASP.NEТ предлагают ограниченные наборы
событий. результатом которых. в конечном итоге. оказывается новое обращение к
Web-серверу. Для обработки событий клиента вы должны создать соответствую­
щие элементы программного кода сценария JavaScIiptjVВScIipt клиента. которые
будут обрабатываться механизмом обслуживания сценариев соответствующего
брауэера.

Свойство AutoPostBack
Следует также подчеркнуть то. что многие Web-элементы управления ASP.NEТ
поддерживают свойство AutoPostBack (это очень важно для CheckBox. RadioButton
иTextBox. а также для элементов управления. получаемых из абстрактного типа
ListControl). По умолчанию это свойство получает значение false (ложь). что
означает отключение автоматической отправки серверных событий (даже при на­
личии соответствующей настройки в файле внешнего кода поддержки). Во мно­
гих случаях это оказывается именно тем. что требуется. Но если вы хотите. чтобы
какой-то из элементов управления обращался к обработчику события на сервере.
нужно установить для AutoPostBack значение true (истина), Это может оказаться
полезным тогда. когда дaннъle одного элемента управления должны автоматически

становиться данными другого в пределах одной и той же страницы.


Для примера создайте Web-уэел. содержащий один элемент управления TextBox
(с именем txtAutoPostback) и один ListBox (с именем lstTextBoxData). Затем
обработайте событие TextChanged элемента TextBox и в серверном обработчике
события добавьте в ListBox теI\YЩее значение TextBox (уследили за идеей?).

protected void txtAutoPostback_TextChanged(object sender, EventArgs е)


{
lstTextBoxData.ltems.Add(txtAutoPostback.Text) ;

Если выполнить приложение в таком виде, вы обнаружите. что в процессе вво­


да вTextBox ничего не происходит. Более того. ничего не произойдет и после ввода
в TextBox при переходе к следующему элемeнJY управления по нажатию клавиши
табуляции. Причина в том. что свойство AutoPostBack типа TextBox по умолча­
нию имеет значение false. Но если установить для этого свойства значение true.
как показано ниже:

<asp; TextBox ID=" txt.Au toPos tback" runat=" server'" АutоРоstБас]с="Тruе"


OrJTextChanged=" txtAu toPostback_ TextC}-Jangеd" >
</asp:TextBox>
то вы увидите. что при выходе из TextBox по нажатию клавиши табуляции (или
при нажатии клавиши <Enter». ListBox автоматически получает текущее значе­
ние из TextBox. Без сомнения. кроме случая добавления данных одного элемента
управления в другой. необходимости изменения состояния свойства AutoPostBack
в других случаях не возникает.
1I

Г:лава2З,WеЬ,стра~lи·ltы и Web-ЭЛGМSIiТPI УГ1iJ8впения ASP.NET ~,D 1055


i] Тип System.Web.UI.ControJ
Б~зо}lblЙ масс Systel1J.We.b .U1.Control оnреде.lIЯ~1' различные 'С):iОЙСТВа. -мeТQ­
дщ, Ji события. -которые позволяют взаимодействовать с базОВl;U>Ш членами Web-
эл~еата управления (@БЬГlНО не атнослщимися JI: tр;;\фическом:у интерфейсу).
В табц, 23.9 предлагаются miисания неко'Хорых тахшх qдщюв.

'l'аблицs 23.9. Подборка члеl-fОВ Sy~tem. WеЬ. Ul.Co1)tr~Il


Член ОПJ4С8ние
.,, Cont_Iols {:ВОЙСТJ.щ получающее объект 'опtrоlСоllааtiоп, nредстаВШIЮЩИЙ до·
черние эле-мщrгы ynРЩWIВНИЯ в PBMКf1X дaHHOr.O 'элеменrrа упрSкпения

!11aLaBirrd ( ) МетОД, ВЫПОЛНЯI9ЩИЙ ПРИS~ЗI(У источника ABHHblX .к яыэваННDМУ серв.ерно­


м,/ элементу ynравлени'" и воем его .дочерним злемен-гам управлеtl:ия

En.abJ.eThemeing Свойсrnо, укаЗblВalOщее В,О'зМОЖf/QСТЬ поддеРЖkИ тем,Щ1Я даННОГQ э'пеМ~1-t­


та управления

li!asr.Jontrols () Метод ДЛЯ опреде:ления FillЛИLIИЯ .Дочерних элементов уГiрав:nения у,д-о1НflО­


ГО cepB~pHoro ЭllемвнТ'~ упрааления

ID Свойство, \!И.rаroщее ИЩ1 УlЛaнаВЛИВЭЮщее значение npограммнога иден­


тификатора .о;ля, сер13ерного Зl1емеl-!Та УГlравnеf-lИЯ
p.age СвоИство. получающее ссыпку на экэемП'лЯ,р 'JlНfa Pqge, содержащий сер­
веРный элемеtrГ улрввленир

Parerrt Свойство, ПОЛУЧЭlрщее ссылку на po-дител'ЬСКИЙ э.Реме~lj уnр~вл'еыи~ дa~­


~1O'Го сервеЩЩГО элемента управления в иерархии элементов управления
CTPaн~

SkihID СвоиtтВ(i, '-IитаКJЩее или yc-танаВnVlВllющеij параметры скиннинга зпемент~


уттраалеНИR. Это дает воЗможноО1Ъ в дSP.NEТ 2,0 уста~;lВJlИ,l3ать 'ВнеШ~II,'Й
lijИД элемента управления динаминеСI<И

ViэiЫе C6~CTBO, ЧИТaJOщ~е или устанавлИвaIOJЩJ8 3НЭ"lание, указывающее не­


~одимость обрабОтки .сервернorо эдементц упра8пе~IIIЯ. 1ffiJ( элемеtiтз
польэователъс~оrо IИl-перфейса отраницы

СПИСО,k вложенных элементов управления


Первой из рассматриваемых зде<ъ ОС6беI:i:НOстей S}'s.tem. Web .IП .:Control явля­
ется TQ, что lIсе Web-э~е~енты управления (.это также отноои1'СЯ и К Pag€) насле­
дуют коллtlJl:цmо поль~оват~киХ але:ментов ynpавления (доступную с помощью
свойства CQntrols). Во многоМаналоти.'ЧНо случаю приложеНИ-И WindQ~ Forщ:s,
11 .данном случае с~ойство ~Q.n :ll·;ol s обеспечивает дocтyn ,н строг() ТИПИЗoв-aRНой
Jl:оллеJЩИИ объеI<ТОВ WebCootrol. Подобно moбой коЛ1l~ .NE1: вы имеете nоз>-­
мощность ДИR:aМЦЧеf,::JtИ добавJ1Н'J'ъ и удалять элементы этой КOJD1еIЩИИ в среде вы­
долн:е~DIИ,

Хотя добmщнть Web-элerменты ynpaвлевия в Р·аче-тfт моШlЮ и непо.средСТВ6Н­


до. на,много' :проще r~ безопаснее) JICnОДЪ30ва'ГЬ дЛЯ ЭТОГ() адеме:.нт упра.вле1UUi
Panel, }{ласе S-уstе1Т1.W\зQ.uI.WеЬС'опt.rDis.Ра:леl предст.авляет I-юнтейнер ЭЛемен­
тов управлеJЦiЯ. liO'fОРЫЙ может бють ВИДИМЫМ или невидимым для, -конеч:ного
ПО!IЪ30В~IJ'€ЛЯ (В аq:в:исимости от ЗRa-че:ний свОЙСт.в \iislble и Богdеr'S.tуlе)_

!
J
1056 Часть V. Web-приложения и Web-сервисы XML

Для примера создайте новый Web-узел с названием DynamicCtrLs. В окне про­


ектирования Web-страницы Vfsua1 Studio 2005 добавьте тип Panel (назначив ему
имя mуРапеЦ, содержarций элементы TextBox. Button и HyperLink с произволь­
ными именами (учтите, что режим проектирования требует. чтобы при перетаски­
вании внутренние элементы помещались в зону интерфейса типа Panel). Б резуль­
тате элемент <form> ваш.его файла *. aspx должен принять следУЮЩИЙ вид.

<asp:Panel ID="rnyPanel" runat="server" Height="50px" W1dth="125px">


<asp:Textвox I D="TextBoxl" runat="server"></asp:TextBox><br />
<asp:Button ID="Вu ttопl runat= "server" ТеХ1:="Кнол~а" /><Ьт />
Л

<asp:HyperLink
ID="HyperLinkl" ruпаt="sеrvеr">Гиперссылка</аsр;НуреrLiпk>
</asp: Panel>

Затем разместите элемент Label (с названием lblControlInfo) вне контекста


Panel, чтобы отображать соответствующий вывод. Учтите в Page Load () то, ЧТО
мы хотим получить список всех элементов управления, содержащихся в Panel. и
присвоить полученные результаты типу L.abel.

public partial сlаs э Default: Sys tem.Web.UI.Page


(
protected void Page_Load(object sender, Event-Args е)
(
ListControls InPan el() ;

pri vate vo i d ListControlsIn Pane l()


f
string tr,elnfo;
thelnfo = Str ing. Format ("Присутствие элементов: (О) <br>" ,
myPanel.HasControls(») ;
fore ach (Control с in myPanel.Controls)
l
if (c. GetType() != typeof(System.Web.UI.Lit eralControl))

t-helnfo += "** ***"* ;, ** ** ** "* i<-k* * ** * * * * k **<br > ";


the l nfo t = St-ril1g.F о rrnаt("N. amе = (Oj<br>",
c . ToString()) ;
tr,eInfo += String.Format("ID = {O)<br>", c.ID);
thelnfo t= String. Forrnat ("Visible = (О }<br>",
с. Visible) ;
thelnfo t = String .Format( " ViewState = lO j <br>",
с. EnableViewState);

lblControlInfo.Text t-helnfo;

Здесь выполняется ЦИКЛ по всем типам WebControl. поддерживаемым в


Panel.
и осуществляется проверка того. что текущий ТИП не является типом System.Web.
UI.LiteralControl. Этот тип используется для представления буквальных НТМL-
- - - - - - - - - .

r.na.!J8 23. WеЬ·стран~щы \11 WеЬ·элемеНТIJI управления ASP.NET 2.0 1057


дескриптороl'i':И содержцмого (Н'эдример. <ЬР. Te~eTOBЫX литералов не Toд.,j. Ееди:
вы .не ВblПОЛЦите тав;ай проверlШ, вы. с УДИ'вдением моmете обцару»tИТЬ в ROHTeJ('
(:'1':е ParJel U'e.il;blX семь ТJШОВ [дЛЯ' укаЗaIШЩ'@ выше оnределециn •. as·px). В пред­
nоложеНЯй о том. что ТИП не wшя;ется: брв:а.JJЫIЫМ I-Л'МL-['одеРЖЦМI;>Щ. BblВOДЦТ'
сл определецная етar:~1i:::тичеСR.(1Л информацnл. Пр:имер такого ВЫВОДа ПОRа3illI на.
рж.23.20,

»
~ L~ !~;'! floИo; ."~.). m.бражое ~)
=
J')

"'A!<,,:[j),~~P:I~t,,;~h;$t:tQ$~~~;""t(~~:.· r.,pe:i.oд. С',,,,,,"," 't!,

. l K,lo~1(o I
Г.f(n.~j;>c(;blJU(a

Лрщутств:;n JlЛ"lYjе}J'1'''Б, Tr\!E


~~~Ж~Ж:*-:~Ж~**

N~= SjЩetn. Weh m w t'bC<1.!ttrc.ht l'е::1&о);


lD=Те$щ1
ViJiibl~ = 1,..""
'liewBtт ='Тлl~

, Namt; = B~ W ~Ь:Ш l,;Ile!:;CQw<::.kButt,)j'),


ID=ButtQDl
VlgLbl~ = Тш"
VrБW StJ1te = ТПlС
~n'-:t:~,~"t.~ _I I ~~*

H~e = S~emW~Ъ.UГN~ЬС\>nl:с"Is.нуре(LirJlc
ID = ц,&.егLШk 1
V;s.iJ:,le= Tfue
• ~TJewS~afe =" Тше
..
"'A1'1''''''''''''~НТ1'ac-«Тb

Рис. 23.20. Перечеm, вложенных элементов

Динамическое добавление (и удаление)


элементову'правлемия
Но что делать. есШ! Ь1-ужно изменить содержимое Раг&l в среде выI1JIнения??
СоотвеТСТВУЮЩИIi процесс должен ПОRазатъсн вам очень знакомым. если вы вн:има·
тельно про:чит3.J'IИ материм h'IШГИ. посВЯЩенный работе с Windows Fапns. Давайте
добавим в текущую СТраницу ююrmy (с назваю{ем ЬtПАddNl,dg,е.t 5 j. Которая будет
динамичеей:й добаfu-rлть в Р.ап-еl пять HOВJ:j!X типов '}'62{tBo.x, 1'1: еще одну кнопку.:КО­
торая будет в.ы:UОШ-'JIТЬ ОЧИ:c-I'Ii)' Panel от всех -элемe:trrов ynpавленил. ОбработчиRИ
l'обытий Cli.ck ДЛН ЭТИХ kI-IOПОR приБедены ниже.
1058 Часть V. Web-ПРИJ10жения и WеЬ-сервисы XML

protected void btnAddWidgets_Click(object sender, EventArgs е)


{
for (int i - О; i < 5; i++)

11 Назначение имени. чтоб~ поз*е получить соотвеТСТВуЬЩее


1/ техстовое значение с помощью метода
/1 HttpRequest.QueryString().
TextBox t = new TextBox ( ) ;
t.ID = string.Format("newTextBox(O}", i);
myPanel.Controls.Add(t);
ListControlsInPanel();

protected void btnRemovePanelItems_Click(object sender, EventArgs е)


(
myPanel.Control s.Clear();
ListСопtrоlSlлРапеl();

Обратите внимание на ТО. что каждому T.extBox назначается унии:альное зна­


чение ID (пеwТехtВоХl. newTextBox2 и т.д.), чтобы можно было программными
средствами получить содержшцийся в этих элементах текст, используя коллеI<ЦИЮ
Ht tpRequest. Form (шш будет показано чуть позже).
Чтобы получить значения этих динамически генерируемых типов TextBox, до­
бавьте в пользовательский интерфейс еще один тип Button и тип Label. В преде­
лах обработчика события Click для Button реализуйте ЦИКJ1 по всем элементам,
содержащимся в рамках типа HttpRequest.NameValueCollection (доступного с
помощью HttpRequest.Form). добавляя полученную текстовую информацию к ло­
кальному типу System.String. По завершении обрабОТIШ коллекции назначьте эту
строку свойству Тех t нового элемента Label с именем lbl TextBoxText.

protected void btnGetTextBoxValues Click(object sender,


System.EventArgs е)

s t riпg textBoxValues = "";


for(int i = О; i < Request.Form.Count; i++)
{
textBoxValues +-
string.Format(" <li>(O)<!li><br>". Request.Form[i]);

lbl Text:BoxText. Text = textBoxValues;

Запустив приложение, вы сможете увидеть .как садержимое тексто.вых бло.ко.в.


так и дово.льно длинные ("нечитаемые") Сl'роки. Такие строки отражают вuзуал.ъ­
ное состояние элементов на странице и будут рассматриваться позже. в следую­
щей главе . Также вы заметите, что. после обработки запроса новые текстовые окна
исчезают. причина опять кроется в природе нттр- этот протокол не обеспечива­
ет сохранения состояния. Чтобы динамически созданные типы TextBox сохраня­
лись после вторичных запросов. вы должны сохранить СОСтояния этих объектов.
1
Глава 23. Web- СТ1Jан~цы I-f wеь·элементы упраВl1ЕНIIdЯ ASP.NEТ 2.0 1059
и~пощ.ауя с@ответствующие приемы программирования ASP.NEТ (эти вопросы Т<Ш­
же рассматриваются в следую~ей r.лавеJ .

"~ходныИ КОД. ФаЙilJы примера DynamicCtrls ра:змещеfjы в поw:аталоге, сратвеТСТБующем таве 23,

Основные члены типа


System. Web. UI.WebControls. WebContro,1
Можно СJЩэать. что тап Co:ntrol :предлагает воЗ,МОЖRОСТИ ПОВt.'дениSi. не относя­
щиеся. к графичеСIЮМУ интерфейсу.. С др~ОЙ стороны . базовый :к:щ,н;:т WеЬ С сп \: rо]
Qбес:tIеЧИЕ~'Г ЛОJЩМорф~rn графичесЕиИ m-Iтерфейс ДЛЯ всех Web-алемеИТQН по­
ведеюш. ЩJ.R ПО'Raза:но в Taf)JI. 23.10.

ТабnИ:ца 23.10. Свойотва баэоtюго :класса W еl1Сошtrоl


Cвo~cтвa Описа~ме

1>a'c kCblor Читает ми устанавливает цвет фО!l8 Web-элемент<\ управления


в OJ.." deJ.' C ol от Читает или устанв.liЛивае-т цвет гранины Web -элемента УnЩЦIления

Bord e.r$tyle Читает ИРИ устанаВJ1ивае-т стиль границы Web-элемента УI1рЭ:8I1ения

Rord·e rW.idt h Чи:та~ или ycт~a~nIllB;a.eT WИРl1lну границы Web'-элеМеН1а ynравленl-IЯ


Еn:аыldd "Iи.та6Т 10111" у.станаJi\лиааеl значение, ЯВlШJOщееся J,fi1дикатаром ДОСТУJ'lkО,СТИ
Web-элемент<'l Уr1равлеlЫl'I
C:iHJClass ПDзаоляет наЗН'а<iИтЬ WE!O-элемеН1У управления .класа, определенныJ.\ в рамках
CSS (Gascading Style Sneet - КЗdlЩЦная· табnиЦа ОТИI1~Й)

E'o.nt Читает информацию О' шрифте дляWеЬ-ЭiiемеfШl У11равления

РохеСо10!: Читает или YCTВ:tIa!iDJ1Bi'ler цвет изображения (обычно цпе11'вксrа} ДJJЯ Web-зnе·
Мента }II1РaQления

He~ ght Читает или устаliавливает · ~ыcoтy и ШИРИIЧУ 'М:1Ь-~лемента ynра8л.ениЯ


Width
TabIndex ЧJ/lтаеп МИ yc:гaHaaJI~Ba~T индекс перехода по-твбуляции ДЛЯ WвЬ -элемента
управлеl<iия

Тооl Tip Читает или устанавливает з~чение-ИАдикатор, УI(;3:;:'!ыв~ющее FteG.БХQДИМ-ОСТЬ


отображения ПQдсказ~и при задержке укаэат-е:л~ М\>IШI4 H.~ иЗОбражении Web-
элемента УFфаалени.R

Скорее всегC'l. эти свойства 'б)WYТ JVi.fl вас ПОН:НТИЫ. ТЭR что BJ\recтo примеров их
использования давайте немнОI'О сместим шщенты и прО:Верим в дейсТВии ряд 'эле­
ментов упра8леНШI Web- формы ASP_NEТ.

Категории Web-зnементов упр: авления ASP.NET


ТИпы в SУ5t:еm,WеР ..1..11.\'I'Е'1ЬСQпtIоl s Можно разбить на иеСJl;ОJ!Ы\О больших :ка­
тегорий.

• Простые элементы УПР~:ВЩ:НfI.Я

.. Элементы yrrрaв.п:eнnя с рэ.С'IЩipенны:м:и во~оЖ'IЮСТЯМИ


1060 Часть V. Web-пр~ложения и Web-сервисы XML

• Элементы управления для работы с источниками данных

• Элементы управления ДЛЯ контроля ввода

• Элементы управлеяия для входа в систему

Простые элементы управления называются так потому, что они являются Web-
элементами управления ASP.NEТ. отображающимися в стандартные НТМL-элемен­
ты (кнопки. списки, гиперссылки. контейнеры изображений, таблицы и т.д.). Далее
мы имеем небольшое множество так называемых элеменnwв управления с расши­
ренными возможностями. ДЛЯ которых нет прямото эквивалента среди НТМL-эле­
ментов (это. например. с,з.1епdаr. TreeVi ew. Wizard и т.д . ). Элементы управления
для работы с u.cmoчн.икамu aaнHblX являются элементами, для заполнения которых
обычно требуется соединение с источником данных . Лучшим (и наиболее интерес­
НЬПvI) примером такого элемента управления ASP.NEТ является, наверное, GridView.
Друтими членами этой категории являются так называемый "ротатор" и элемент
управления DataList. Элементы управления для контроля ввода являются сер­
верными элементами управления, автоматически генерирующими JavaScript-код
клиента для про верки вводимых в форму данных. Наконец. библиотеки базовых
классов ASP.NEТ 2.0 предлагают целый ряд элементов управления, связанных с
решением проблем безопасности. Эти элементы интерфейса инкапсулируют все
особенности регистрации доступа к узлу, предлагая. в частности. сервис ввода и
получения пароля и по.цдеРЖl()' ролей пользователей.

Замечаtlие. Поскольку в этой книге не предлагается обсуждение системы безопасности .NEl;


здесь не будут обсуждаться и соответствующие новые злементы управления. Если вам потре·
буется подробная информация по проблемам безопасности ASP.NEТ 2.0, обратитесь к книге
Domlnic Se//y, Andrew Тгое/Беп and Тот ВагпаЬу, Expert ASP.NEТ2. 0 Advanced App/ication Design
(АргеББ, 2006).

Несколько слов о System.Web.UI.HtmIControls


Вообще говоря, есть два разных набора Web-элементов управления. предлагае­
мых в рамках дистрибутива . NEТ 2.0. В дополнение к Web-элемента.м управления
ASP.NEТ (из пространства имен System.Web.UI.WebControls), библиотеки базовых
классов предлагают также элементы System.Web.UI.HtmlControls.
HThfL-элементы управления представляют собой коллекцию типов. позволяю­
щих использовать традиционные элементы управления HTML на странице Web-
формы. Однако, в отличие от ctaI-щартных НТМL-дескрипторов, НТМL-элементы
управления являются сущностями ООП, которые могут быть настроены для выпол­
нения на сервере и поэтому подцерживают серверную обработку событий. В отли~ше
от Web-элементов управления ASP.NEТ, I-IТМL-элементы управления по своей при­
роде очень просты и имеют не слишком широкие возможности. аналогичные стан­

дартным дескрипторам HTML (HtmlButton, HtmlInputControl, HtmlTable и т.д.).


НТМL-элементы управления предлагают открытый интерфейс. "имитирующий"
стандартные НТМL-атрибуты . Например, чтобы получить информацию из области
ввода. вы должны использовать свойство Value, а не свойство Text, как в случае
Web-элементов. Посколы<у НТМL-элементы управления обладают не такими бога­
тыми возможностями, как Web-элементы управления ASP.NEТ, далее в этой книге
,
1
!

Глава 23, Wab-стр:аниuы li1 Wab- :щемеНТ1>IУПРJa.ВJ1ВflIo1Я ASP.NEТ ~O 1061


}ПМL-элементы управления упоминаТЬСJl1-Iе БY7lYТ- Ещш вы захотите изучитьэ'1'Й
ТЩIЫ. обраТIПе-дь 11': ДОl{)Щell'ГЭIUIИ _NE'tFгamework 2.0 sпк.

Создание npOCToro Web-узла ASP.NEТ 2.. 0


ОграничеН1:lЫЙ объем щшrи IIC' ЦОЗВОJlЯет здесь ОIIИсать Qсобенн.ости всех. Wt;'b-
<'i:лементов управлеliИ.f{_ щ.;:од:!IIILИX в ЦОСПWRУ лSf'.NEТ 2,0 (,lJ;IIЯ этоrо -:r:pебуется от­
д,еJrьная и .ДОВОЛЬНО объеМJJ<J,Я кни.га)_ Но чтобы прDИЛ'ЛЮСТрйрODaТЬ работу с Р<'lз­
m-rчН'ыми Wе'ь - э.цемен.тC!М1J управлеrпщ ASR NEТ. следуюпulМ нашим зэдwием в
этой главе будет соз~анне W~Ь-узла. демокстрирующего испоnЬаовалие следущщих
В.оs'мо~IOСТей.

.. Работа с шаблонами стран:йЦ

.. PtJ.()oTa с .элемеш-ом управления Me:nu.


• Работ.а: с эле~Iентом управлtmия Gз:ldV iе-w

• РаботG). с зщ:ментом управления Wizв rd

При работе t' при:мероu.не забывайте о том, ЧТО элементы управления Web - фор­
мыI иnиапсу.uирywт ВО3МОЖН'CIсти Т'енерирования СQОrnетствуюu.щx 1-.ITh1L-десЩi>ИЦ­
торов и сдеЦ)'](}т модели Wщdоws Fonns. дi1a начала создайте вов.ое Web-прmю:иre­
а:и:е ASP.NETc Н<W.lщmщм AspNe t Ca.r 55i..te ,

Работа с шаблоном стран.,ц


Вы, l-IеСОМ1:Iе:щю, зн~ете. что мнотие WеЬ-узлы пре,цлагают СТРЭ:JUiЦЬ\, Bь~дep­
жаRные в одном СТИ'JIе (такие страницы имеют общую си.стему ме1:lЮ . общие эле­
меlП'Ъ! оформления DеР)Ц'lей и Ш:fжней чаС'FСЙ етрающ , неnpе..'l'IеrПJО СQдеРilЩТ фир­
меIШ;ый ~ШЩ l\омпании и 'F,дo, )_ в ЛSР.NEТ 1.Х разработч:mcи m.иРОКQ использовали
lJБеr сопtrоl и WeD-;ilл ементы )lПpавления. чтобы опре.це:.iIИ1'Ь WеЬ ~еодР.рЖИМ(){', :кото­
рое ДОЛЖПО бьшо }iСПОЛЪSОЩtться:на мног:и:х страющах. И ХОТЯ Usеr С апt:rоl и Wl;'b-
3i1IeMel;lTbl упр!Щлен}J'Я о.стЩQТСЯ Д@С1}'IIНЫМИ для исполъэовaНJfЯ в ASP.NEТ2.0. те­
пер>ь ДЛЯ решешm )[ffi.'ICUlliЫX задач upeдлагаетса использовать шаблоны Cl11JЮЮLЦ-
Упрощенно roворя.• rЩl.блон С'I'раницы О'I'личае:тся от обычной страниды ASP.NEТ
поч'J',И m::юnо'ПJТелыю ПШЬRО 1'еМ.Чт.о ОН разм€щаетсл 11 файл е *. [M ~ t e r. Сшщ- по·
оебе щаблоны ('траниц не ЯБЛЮОТ{'Jj .видимыми ДJlЯ браузерв, ЩIиента (фак:гичесКИI
среда })Ь~UОilliltШ;ш ASP.NET не обслуживает ЭIIY часть Web-сомржи..~roГQ)_ Шаблоны
стр.аниц оцреде{ШЮТ ([)t)щий Rapкac пользова'fельсв:ого :интерфеЙса. ('~:ш:м:еcrн@ ~C~
пользуемый всеми страница.'\Ш (или подмножеет:еом страниц) узла_ Кроме трго.
страница'" _таз t~Jr определяет различные ,цескрmnорьр-ааполн,nтеди. получающие
дощщпительное. сqдершимое.в фай'ле .. "a-в,Р'М. В реЗУJIiЬтате получается общий. уни­
qщцнровв.н)ilьЩmЩЬ:;IOВате.ThСкиИ ЮlТерфt'IЙс_
Добавьте в с~ой Web-узел НDВЫ:И mаБJrон С'1'раНИilЫ (выfjpa.в Web Site~Add N~W
"tem из меню) и рассмотрите ето :исжщное определение.

<%@ Иаstе-r Language='"ct'" AutoEvent'i'ireuy.=,·true"


CodeFi 1е= "Маз terPage .l!IAS t 'e r . cs" lnhe:ri ts=~мast.erPaqe" -%>
< ! DOCT'tPE ht-m l Р!.ЛiLJС "- //W3С//D'j'D XFJ TМL 1 . l/./EN" " btt p : // www _w3 .or g/
TK/xhtmll l /H'I'D/ k!"Jtmll1 , d'Ca" >
1062 Часть V. Web-приложения и Web-сервисы XМL

<html xmlns="htt.p://www.w3.org/1999/xhtml" >


<11ead runat="server">
<title>Untitled Page</title>
< / head>
<body>
<f orrn id="forrnl" [unat="server">
<div>
<asp:contentplaceholder id="ContentPlaceHolderl" runat="server">
</asp:contentplaceholder>
</di v >
</form>
</body>
</htrnl>
Первым интересным злементом здесь является новая директива <%@Master%>.
По большей части эта директива поддерживает те же атрибуты, что и < %@Page %>.
Например. обратите внимание на то. что по умолчанию шаблон страницы предпо­
лагает использование файла внешнего кода поддержки [который, строго говоря. не
обязателен). Подобно типам Page. шаблоны страниц получаются из специального
базового KТlaCCa, которым в данном случае является MasterPage.
public part i al class MasterPage : System.Web.UI.MasterPage
(
pr ot ected void Page Load(object sender, ЕvепtАrgs е)
(
}

Важно знать о том. что атрибуты, определенные директивой <%@Master %>, не


"перетекают" в связанные файлы *. aspx. Поэтому вы мажете. например, исполь­
зовать С# в рамках шаблона страниц. а ДЛЯ связанного файла *.aspx использовать
Visua1 Basic .NEТ.
Другим интересным злементом является <asp: contentplaceholder>. Эта об­
ласть шаблона представляет элемент пользовательского интерфейса в связан­
ном файле *. аарх. а не содержимое самого шаблона страниц. Если обработать
файл *. aspx в рамках этой части шаблона, то контекст дескрипторов <asp:
contentplaceholder> и </азр: contentplaceholder> окажется пустым. Однако
при желании вы можете наполнить зту область различными Web-элементами
управления. которые будут функционировать в Rачестве элементов пользователь­
ского интерфейса. используемых по умолчанию в том случае. когда данный файл
*.aspx узла не предложит свое нонкретное содержимое. Для этого примера мы
предполагаем, что все страницы *.aspx узла предоставляют подходящее пользо­
вательское содержимое.

Замечание. Страница * .rnaster может определятЬ столько заместителей оодержимого, сколько


необходимо. Также страница *.rnaster может содержать дополнительные вложенные стра­
ницы *.rnaster.

Как и следует ожидать, в VisuaJ Studio 2005 имеется возможность построить


общий интерфейс файла * .rnaster с помощью тех же инструментов проектирова­
ния, что и в случае построения файлов *.aspx. для своего узла добавьте инфор-

Глава:23 . Web-'страницы .и W~Ь-элементы уnра;аl16f1f1Я ASP.NEТ 2.0 1063


мирую1ЦуЮ Щ1ДIЩСЬ Label (чтобы иrnользС),ват:ь ее для общerо nриветственнргЬ
СООбщения). элеМenт _vпp~еция AdRotator (который будет случаj1ным обрааом
отображать ОДНО из двух и~t)бражений:) и элемент ynраfшеюш мел].1.fчтобы позво­
.iIИ:t:Ь ПОЛЪзовате.1lIО перейти к другим частям узла).

Работа с элемеmом управnеFtИМ Menu


ASP. NEТ 2. О цредлщ-ает неСНрдЬFЮ н-о:вых Web-элементов управления. ~OTopыe
позволяют реаЩIЗо:uать ВОЗМ@Щ(-lOСТИ наВШ'a:iJ,ИИ В пределах УЭЛа.. Это Si t eMapPat.h,
TreeView й Мели. как Е!Ы Mo~reT~ догадатьСя. ЭТИ Web-ЗJlемеНТbl MOгyr быть настро­
·etIыl множеСТ80М СТIOСОЦQ'В. ИадримеР. :каждый из эТItt элементов ynРfфJ[ения мо-­
Жет динамйчесЮ! генеРИPQВа:ть. своистроШl с помощью внеUШеro ХМIrфаЙДI<I щтlt
иt1'QЧНИК ДaJ!Нl>:m, Но ДЛИ JЩ.ШeI'0 типа Мет) мы просто Yl--ащем три .значения не­
посредственно.

В режиме ПРQе:ктировaщtя Web--cтpa1:i.ИЦЫ. выберите элеМ'е-нт управленип Меп Ll.


а:в;тивйзирy}iте BcтpoeHHblji редактор ЭТQГО :;Шем6нта (испольвУ9: маркер в ~epxнeM
yиryэлеМe.нIа) и выберите Ефt M~nlJ IteQ1s. Добавьте тpn корневых элемента Начало
ОБЗОj:,а. СQЗД .. ТЬ маmину и Ассортимент. Перед . закрьrгием диалorо,вого ощщ уста­
навитедл.н свойства N.пrigаtеllr11IЮКДОТ() элемент ССЪ1ЛRИ на следующие ~трани­
цы (Iюторьtе еще не С'озданы1-

• НtiЧClIrJJ обзора:. De f p.-u 1 t. зарх


• Создать ,Nli1.ШW-/,У: BlJildCa r .asp"'(

• Ассортllмент: Iпvеаt6r)'. аэрх

Этorо ()удет достаТI)ЧНО. чтобы ваш эдемент r1eTJU IЮ3ВQЛЯЛ лер.е.Йти н дРугим
с:гра:вицам УЭJJGi. ВblПDJ.II-ШТЬ Дi:шолнитeJtЫn,lе действцn в случае выбора ПОJIЫЮБа­
тедем дално:ГО DytfШТЭ меню можно. с помощью обрабоТЩ1 со,быти.я MeoultemClick.
Для нщцего при:мера 'в этом необх:одимости нет, но цы; должны знать. что. с ПОМО­
lЦЫ9 постуш:iюще.rО параметра MenuEvEj о tArg.$ МOOfЩО оцреДt"1IН1'I,. «акой пуюп­
Me:f:!.JQ бьш выбран.

Работа с AdRotator
Роль элемента AdRotat.Cir ASP.NEТ защrючаеТqI в случpJ%ном отображ.ении 000-
брaжtJ~ в Нf)}\\ОТОРОЙ llШIИЦIm В окне браУ;Jq>а. Н~посредСТвенВо п.оме РЗЭМбще­
PlИ:Я AdRotat0r в шше проеК1"ирования оltотоf5раящетс~ в Биде nycто,о·заместите­
ля ·ЭJ.IeJ\l~НТа.. ~он.ально этот элемент yupqвлени)'l не сможет :ВЫПШI1-J,ят"!', свою
~адачу ДО п;х пор, лова ВhI не наэuaчит.е СВОЙСТ.еу АdvеI'ti s еmелtF'l-lt;, ОСЫJIRy-на
фa'ЙJI, {)цисывающий все изображения. Дли :mцщ:го примера источн:иком данных
будет дроtтоЙ XML-фaйJ1 С именем Ads. x.rnl,
Добавив этот новый XМL-файл в уЗeJJ. укажите Ц нем уни:калънъти элеменТ <Ad >
;ЩЯ JtaЖДОГо. изображения. :кcrгopoe требуетс:а О'Fобраз.итъ, }\.юf минимум. каждый
щrе-м:епl' <Ad> до.л:Жен указать изображение.для ото[)рЩfreНИ.R (Imag,eUr.l). адрес tJRL
дм пер~ода rrpи выб0Р~ данного изоБРaJI,еFЩЯ jТа rgеШrl ), те:кст, ПОЯIШЯЮЩИЙСН
при ра~мещеюm указатели м:ыши на I1зображеmrи (A1.t e rл.аt~Техt -, J'I ":вес" изо:бра­
жеШlЯ ' l;Jrrpre·ssiO DS).

<-Adw.:ert i s:E:merl t _~ >


<A:d;>
1064 Часть V, Web-приложения и Web-сервисы XML

<ImageUrl>SlugBug.jpg</ Imageurl>
<TargetUrl >http ; / /www.Cars.com</TargetUr l >
<Al tеr'паtеТехt>Ваша новая машина ?</AlternateText >
<Impressi o ns>80< / Impressi ons >
</Ad>
<Ad>
<ImageUr l>car . gif</ ImageUr'l>
<TargetUrl>http://www.CarSuper·Site.com</TargetUrl>
<Аltеrпаt еТехt >Нр авится эта машина?< / АltеrпаtеТехt>
<Impress i ons>80</ Impressions >
</Ad>
</ Advertisements>
Теперь можно связать ХМL- файл с элементом управления AdRotator с помо­
щью свойства AdvertisementFile (в окне свойств).

<asp :AdRotator ID="myAdRotator" runat="server"


АdvеrtisеmепtFilе="- / Аds.хml" />

Позже, когда вы запустите это приложение и направите вторичный запрос


странице, вам будет показано одно из двух изображений , выбранное случайно. На
рис. 23.21 показан исходный вид IШJ,блона страницы ,

'Добро пожаловать в Саrэ R Us! Г~ ~dRolalor

Рис. 23.21. Шаблон страницы

Определение страницы Default.aspx


После установки шаблона страниц вы можете приступить к созданию индиви­
дУальных страниц*.aspx. определяющих в совокупности с дескриптором <аэр:
contentplaceholder> шаблона содержимое пользовательского интерфейса. При
создании HOBoro Web-узла среда разработки Visual Studio 2005 автоматически соз­
дает начальный файл ".аврх, но в исходном своем состоянии зтот файл не может
быть соединен с шаблоном странйЦ.
ч

r)'J~l!a 23. Web-страницы и wеь';iпемеюы ynравлgJ+~R ~SP.N:ET 2.0 1065


Прli'lmrn 8 ТОМ. Что имец:но фajiл ... mаэ te.r ,определяет раздШI <f.orm> резуль­
тирующеи HTML-страЮinЫ. Поэтомусущестsую:rnyю область <form> в файле
.,.. aspx J-tyJIt..НО заменить на <: а$Р: con·tent>. ПереЮIЮ~есь 11 режим $ouгce ДЛН
Hefaul t. аЕРХ И замеwитf' щ.шющуюся размеugт следующей.
<%@ Page L,a nguage="C#" masterpaqeFile=I'-/МаsterРаqe.ma.ster,1
А ,itcEven tWir'eup="ti;ue n Code'F'i l~=n Dehul t . aspx. сз" lnheri ts=" [)е,f-аtй t"
Т i tllOi="U-о t-i t l,et:! Pag,e " %;>

<-а- зр; Со!] te:n t I D="Солt еоtl "


ContentPlaceВolde:rID="'Contentpl:aCdlQlderl" J',l.lла t="'Server"'>
< f азр; COrJter,t>
Прежде всего обраТ1'Iте .внимание на ТО. -что директива <% @
'i?age%> здесь' полу­
чает новъ-1Й атрибут Мв s1:-erРаgеFile, КОТО'ромулрнсвоено значение BameI'O фаЙil:а
*.mаstэr. ТаНЖе отметьте. что зна:че~ Corite:fltPlacef!.olde-rID идентично 3I-Iaче­
:пию элемента <.asp: aO[Jt,ent;p.lace·helde~> из файла шаблона.
После переключени~ рбратно в режим ПроеКТИрования (РеЖИМ Oesrgn.) вы об­
Jщрущите. ЧТО польз()вательс'RИЙ ИБтерфеис шаблона crраницы1 стал ВИ:ДF!МЬ1М.
Область содерждмоrо также ВiЩf[Ма. ХОТИ в даннЫй момент онаnyста. У tiac нет «t:-
обхо~мости строить СДIJ~ьrй ивтерфейс длн .области содерЖИМОГQ DеfаIЛt.а;;рх:.
ПОЗ'ГО'М)' дЛЯ примера UPOQTO добaв::r;те В6сЕолыю типов Label. чтобы .отобразить
осн(Шнъrе ЩIстру;кц.щ:~ узла (рис. 23.22).

. указанные ссылки
IИnг создаНЮ; ~iiшы 'с~ооЙ мечты'
...
. ... ""....... -t- ,~ ~.. , _ _ "" "",,,,, - - - _ ~ ___ ---4 ~ О
_ _ _ _ _ _ " _ ... :

1-L"-"-~"L rrpOCM01pa ассортимента НaIlIИX маmин.

Риа.23.22 . Страница содержимого Defa11J..t .aвp~


ЕсЩl теперь ,зацусти:гъ upoefj;'}' на вЪП1олнение! то вы увидите, ЧТО содеРЖИМQе
1ffiтерфейео~ фаЩ10В *.fl19ste,r' и реfа-Ult..азрх c.J1ИВается в еДННОМ НТМL-noтоме.
ltaK IЩДВО из рис. 23.28. В:ОЦe-чFrый ПОЛЬЗQВат6.71Ъ не увидит даже ПРИЭВaRОВ' TotD.
ЧТQ существует шаблон сТраницы.
1066 Часть V. Web - приложеНИR и Web-сервисы XML

] UnliHed Page - Мicrоsоft 1,)lerllel [xpllJГer Г-Jlt:11I}<I

;\il""~' i :l htlp,!flocaJ,asfJ'lЗ6S/АSPNetсarsSitВ/~fauJt.~~х -
~ .,..-- _.'-- -~- . . " _. - - -~ - -- '~- ' •• - toд: , й
0 - • - ,;~; с] ПерехО4
r~ 'i:'Z"'Фt ....,..==,::;;;;;:-~~
(,Ы""~ .. ~ ~
_ _ _ ---::.......-......... J.-""":-__ ~=_

Добро пожаловать в Cars R US!


Хотите SLUG BUG CIПIеro цвета?
Заходнrе на Cars,com!

Используйте указанные ссылки


для создания машины своей мечты
или ПРОСМО1ра ассортимента наших машин.

Рис. 23.23. Страница Cars R Us, открываемая по умолчанию

Создание страницы Inventory


Чтобы добавить в проект страницу содержимого Iпvе л t о r у . а s р х, откройте в
cpeд~ разработки страницу 1< .maste r и выберите WebSiteC::>Add Content Page из меню
(если файл * .mas t e r не является активным элементом , этот пункт меню не пред­
лагается). Роль страницыInve ntory заключается в отображении содержимого та­
блицы Inventory базы данных Cars в рамн-8Х элемента управления GridView.
Бы. наверное, знаете, что элемент управления GridVi ew не ограничивается
представлен ием данных только для чтения. Этот элемент можно настроить так.
чтобы поддерживались сортировка , перелистывание и редактирование данн ых .
При желании вы имеете воэможность выполнить серверную обработку с ерии
событий . добавив соответствующий программный код ADO.NEТ. Этот элемент
ASP.NET2.0 заменяет соответствующий элемент ASP.NET l .x. с его "менталитетом
нулевого программного кода".
С помощью нескольких щелчков кнопки мыII.Iи вы можете настроить Gr idVi e w
на автоматический выбор, обновление и удаление записей соответствующего хра­
нилища данных. "Нулевой" программный код сильно уменьшает объем вводимого
шаблонного программного кода. но важно понимать, что эта простота сопровожда­
ется потерей контроля и не может быть рекомендована для использования в при­
лошениях производственного уровня.
...

Гдава 23. WSb-СТРf.lНИЦЫ и Web-элемеИfЫ ynpaBne~~r~ ASP.NET ,2.0 1067


Тем не ue~, для и.llШО~ТРации возможностей использования GrldV1ew в этой
де1СЛаратиmюй форме ДООЩlьте J:fОЦУЩ С'I'рающу' (InverrtO.ry.aspx]"tii соэдaй'I'e 11 об­
ласти содерщимого I-ШфоРмиpytQЩИЙ ТИП Label и тип Gi·idView. ИсtI0ЛЬ3УЯ вСТро­
eIЦiblЙ редщ~'FОр. выберите New Data Sошсе иа ршжрывающеГоея СПИСК~ Cho:ose
Data SourGe. Будет запущен м,з!'=Тер, "Который за i:Iernолъко шатов поможет' вам со·
еДИНИТЬ ItOМnOlleRT с соотв~тству1РЩИМ ИСТОЧНИКОМ данных. Вот тати. которые
Ii}'ЖНО СДМ1:!-ТЬ Д.i'IЯ uaшеrо пример~.

1. Выберите ПlШТOграмму DаtаЬаsе' И унщки.те Cёl!ТsDa tаSG.urсЕ' .Д.1lЯ .идентифика­


тора источника данных.

2. ВыберИте базу данных Сзrэ (ееnи потреб~ется.. создайте ДJlЯ этого новое со­
единение).

3. Е<щи хотите. сохраните данные СТРОIЩ сое,!Iинения в файле WеЬ.сопfig.


Нanо;минаем (см. rJlill3Y 22). что ADO.NEТ теперь :поддерживает элеме.ljТ
<GопnесtiопStхings>.

4. Укаяmте SQL-оJIератОр SеlесtД1lЯ .выбора все.хэanкееЙ и3 таблицы lnventory


(ри.с. 23.24).

i(~ CrilJligute the 5elect SI:.atSmВnt


$'01,
Я_' _dd}ICIЫ г .... 1O Iddв"" diQ " - " ' " d,*Qbwe:7
О SiЖfУ"'~:iQI. $I:iIInent ~ _aitprосю...
@ 'Sj)ecfyCDiu1ll'io lюnI • t.1IbIe st _
НQ;

~. __
~;; --- -- .-.----:1
... _,---- ~ - -." -..
~,
~_. . " о ~nontv_ r.-

I[ У!1ВЕ. .. ]
Oмake
О Color
I[ QвOER ВУ... )

O~-

~CT stetвmest,
J
_ [ Po:tr..-.:ed." J

r~"Rt~~nVll\l:rxV] . ___ _ ... ~ __...-.-..J

[;<~ ][ ~> I ."""" ~'

Р.. с. 23.24. Выбор таблицt;J !ПVЗl1tогу

Еели теперь проверить. содержимое от:кры.вающего деСКриптора элеМ.еи~а


управJlе;ния: Gri.d\liew, !iЫ увиди;те, что свойство DataSQurce-ID получило ТОТ тип
SчlDаt.аSОlJrс~, .который 'Вы "ГО.ЦЬКIJ что щrpедеlIИUИ.

<asp: G,r: LdVie'\~ 1 D= "Gr: i ·c!Y:1 e,w l" run<>. t=" зе.rVеr '"
АutоGепеrа:tеСО]I.lrnns'= "F~lse 1. CellPacЬ'1ing=' " 4 n Da taRey'bla1JLe's= "СагТ О"
DataSourCt!ID="Ca.rsDataSource" F.o.r·e Colo= " # зз.З·ЗЗ3" Gr i 'dLi nes="None":>
.. .. . 4

</ asp: G:ridView>


1068 Часть V. Web-приложения и Web-сервисы XML

ТИп SqlD ataSo u rc e (новый в .NEТ 2.0)- это KOМIIoHeHT, инкапсулирующий ин­
формацию о хранилище данных . С учетом знаний, освоенных вами в таве 22. сле­
дующие атрибуты должны быть для вас понятными:

<аар : Sq l Da t aS ourc e I D=" Ca rsDat aSour c e " runa t=" s erver'"


Connec ti onStr i ng=
.. Data So u rce=lo ca l ho s t ; In i tial Са ta log=Ca r:s; I n tegrated Secur i ty=Tru e "
Pr ovide rNarne=" Sy stem. Data. SqlCl i e n t"
Se lect Comma n d="SE LECT " FROM [Inven tory ] " >
</ asp: Sql Da taSo u rce>
Теперь вы можете запустить свою Web-программу, выполнить щелчок на пункте
меню Асс ор т им ент и просмотреть соответствующие данные (рис. 23.25).

)} Uotilled P<Jge Micro.nft "lte'l1et fXlJlor"r /':"j[gJ[g]


Фойn прast<. ВИА И36рвнное Серв"" Спровка
»
О Н3заА ~ •" ) ~ ~ {~ ; :' помск -~~\' ищ,••"",,, е ~ . ~

Хотите SLUG BUG синего цвета?


Заходите на Cars.com!

Бот чго мы :имеем на данный момент:

СarШМake Color PetNarne


О BМW 1Срасный Chucky
BМW з еле ный Snake
2 Viper 1СрасныйZJPPY
3 BМW розовый Buddha
4 Colt ры ж ий Rusty Burger,

Рис . 23.25. Элемент управления Gr i d Vi e w без дополнительного программного кода

Разрешение сортировки и перелистывания


Элемент управления Gr i dVie w можно легко настроить для сорт ировки (по ссьщ­
ке на имя столбца) и перелистывания страниц (по номеру или гиnеРССЫJ1ке на еле­
дующую/предыIyIцyю страницу). Для этого нужно aIпивизировать встроенный ре­
дактор элемента и отметить соответствующие пункты меню (рнс. 23 .26) .
Когда вы откроете страницу снова , вы сможете отсортировать отображаемые
данные с помощью щелчка на имени соответствующего столбца и листать данные
с помощью ссылок на номера страниц (конечно . при условии, что ДЛЯ этого доста­
точно записей в таблице Inventory).
4

Глава 23, Web-отраницы иWеЬ-ЭЛ8меl'ifЫ уnра,влени~ ASP,NET 2.0 1069

cfюм. Dat6 SoIgC81


аЬо ее
C~. ~~SOurC8 ...
аЫ: аЬо аЬс R.Fr~S~~
аЬс ilЬС . аЬс Etit Caklмl .. ,
аЬс аЬо аЬс дdd NeW Cokт1n.,.

w. аЬо .аЬо W Enable.PllQlnQ

а:Ьс аЪt' ~o ~~ .~
аЬс .aЪ~ .b(i r EI1eb"ftiti1g ~
аЬс аЬс Г En6bJe DeIelIng
.г ШЫе:И!aIon

Рис. 23.28. Разрешение перели· стываНИR \11 сортировки crpal-lIi1Ц

Раз,ре,шение редактироваНИIi на месте


3аключитеЛЫ-IblМ штрихом оформления этОЙ стрa.:ниц,.I будет ПОДдержка эле­
ментом управлении GridView ред.aJ(ТИрО1ЩНИЯ д.~. Для этого откройте встро ­
еНныЙ редактор ддя Зчl DataSOU'1!:CE и ВЫберите Configure Оаtз Sdurce. П'рonyстите
I1ервыIдваa оШ!а этап) мастера. а на щarе ~ щeщtНще на 1ШOJlltе Advatlced и отметь­
те первыи вариант наетро~и (рис . 23,27) •

.~ lNSER1'. iJ>DA'!E.,d РЕ1ЕТЕ Jt~ tNI ье Qen8rDd ~


Ц1d.Itгtl1ъ detaJOurte.

Е!~~т. ,~п.andDВRЕ~
Gвr.r_INsr.RT, 1,IPOA'Тf,"~~'b8$ed.1Ih \IOUr
SEIZC1~ , ~ou musth8V8,.J:II\м'!,~IIejd~ ~
фt(on. to~~,

О 'Ikй gPI:.IЦd8tJi:: ~
lIblfteOI U;I)A~ !I!'d 1EiElFIit.~,I8ItH" dlit'8I:t wh!lth8r' tII8~·
~~si'laldw.=·_~~tМ~, ТI'4$ 'heJps
Ph!I , ~. • '

oкll ~ ]
Рис. 23.27. Дl'lт.омат~ческое генерирование SQL-'Dператаров

'Бсли теперь рассм;отреть НГМL-оnpеделение элеМеИ'Т8 управления. то вы уви­


ДИ'1'е., что.для SчlDаtаSоuп;.е определены De.leteCornmand, Insеr'tСОЩЩ'аnd и
Updatecomm.a.nd.(C П0МОЩblO naраметризовaJ.ШЫx: зanpосоs).

<;'аЭБ":,.Sq1Dа ;t а S'оu :rс€:. ID= IICar sData.Sour.c e·' щtц;,_t=" serve r"
COnl}e~tiOJ1·St;r ing=
"Dat a SOI,u:::-е=-lосаНШS!:J lnitiai Cataloq=-Cars/ I.I1tegrated Sec-u.r it у='Тruе-"
1070 Часть V. Web-приложения и WеЬ-сервисы XML

Pro viderName="System.Data_SqlClient"
SelectCommand="SELECT * FROM [Inventory]"
DeleteComma.nd="DELETE FROM [Inventory] WHERE ICarID] = @original_CarID"
IпsеrtСOПDDaпd= "INSЕRТ lNTO [Invent ory] ([CarID], [Make], [Col o r],
[PetName]) VALUES (@СаТ1О, @Make, @Color, @PetName)"
Uрda.tеСОПll1\&пd= " UРDАТЕ [Inventory] SET [Make] = @Make, [C o l o r] = @Color,
[petNarne1 = @Pet Name WHERE [CarID] = @original_CarID">

</asp:SglDataSource>
Также будет присутствовать компонент Sql DataSourc e. который обеспечит до­
полнительную разметку. определяющую объекты параметров ДJШ параметризован­
ных запросов.

<DeleteParamet ers>
<as p: Parameter Name="original Ca rlD" Type="Int32" />
</DeletePararne ters >
<Upda tePar'ameters>
<a sp:Pararneter Name="Make " Type="Str'ing" 1>
<as p:Parameter Name = "Co lor" Type=" S tring" 1>
<asp:Parameter Name="PetName" 'Type="String" 1>
<as p:Parame t er Name="original CarID" Type="Int 32 " /> </
Upda te Parameters>
<1 D.sertParameter' s>
<as p:Parameter Name="CarID" Type="Int32" />
<asp: Pa rameter Name="Make" Type="S t ring" />
< аБР: Parameter Narne="Color" Type="String" 1>
<a sp: Parame ter' Name= "PetName" Type="Stri ng " 1>
< / In se rtParameters>
Заключительным шагом является разрешение поддержки редактирования и
удаления данных с помощью встроенного редактора GridView (рис. 23.28).

choose Dal:1I 5cu:ce:


ее
c.ortQJe Dat~ хиса ,. ,
аЬс аЬ< .Ьс P.efrosh 5chemo!J
аЬс асс ' аЬс EdI: CoIums .. ,
.Ьс аЬс аЬс дdd New Сobnn , ..

аЬс аЬс асс ~ ENbJeP_

аЬс аЬс аЬс ~ EnobIe~

аЬе аЬс аЬс ~ Et1oblo ЕdtП)


аЬс .Ьс аЬ,С ~ ENbIe DOIea-OQ ~~~ ..

.Ьс .Ьс аЬс r EnohIo-=t1on ~ ~ 'j


1.\ •

1~
EdI: Тerфiotes -.'+:J~~
...
,.

Рис. 23,28,. Разрешение поддержlU1 редактирования и удаления данных


Глава 23. wsb-ираkИЦЫ и Web·eJle.мefIT~ уnp/tвnения A5P.NEТ 2.0 1071


Дрстаточно очевидно. что пр!'! новом обращеци,и' К странице IГlven tory. а sрж вы
сможе'rе редактировать и удaшrгь ЗaIll!СИ tрис. 23.29).

O H~ ~ о ~ ~G . ;) f»!<a il ~ e e · ~ .~ . :,',; I~ (1
"11>"'[; i~~~l~~~~~~~r;;~ ,.~ . _= ~_._~ - -rli G~- ctь;п,,,» '~.
- *-- - ~- - --.".. ~, - - - - -.- - - -=: . . -
Добро ПОЯQ)ЛОВЗТЬ В Саrз R U's! "'"
XoТ1rIe Slug Бug красного ПВe'fIl.{
3ах,щип: да bu-Supe.tsite.СОm1

•"
вмw Ull.em.r! Snakе
Vlpet lq>acJ!lМi!: ~P)' "

@Мw- --- ' ]\leili,~~r~ __ ~--:- j L~~~-d-


h~-_~
, ~,

Colt рJ,lжиi! Rшty Вшger

;fJI'
_ _ о ~~~~TЬ -- '--'--
'

Рис. 23,.29" Та(inица' всех таблиц

Создани'е стран].1ЦЫ BuildCat


nоследнuм наш:им заданием в этом примере будет соэдание страницы
Добавьте ее в свой npоект (въtбрав Web Site9Add Сопtелt Рауе из
BuildCa.r.aspx.
мerню).Эта страница будет С(!lДержать WеЬ~эле.l\tеН!Гynpa:вnеlfИll Wiz'i'tIdASP.NET2.D.,
l\10ТОРЫЙ обеспечит ПРОСТQМ способ прrnmжде'НИН 1юве"tН()Г(!) noл:ьэоватс;"ЛЯ через се­
рию СВlIэаннык I1ЩГЩ!. Здесь соответС'Iвующие ша.ги будут ~тиrэов<!-ть выбор .nо­
ь.-упат.елем а:втом.оБИдЯ: с НУЖНЫМИ f'!1!IfY хараю,еристюш\odИ.
Памест:ите в обдасть сОд!':ржимo:rо Шiформирующую надпись и эдемinrt ynрав­
ленилWizаrd. Затем aR'I'J:Шwщру'Йте вс:rpоеЮfliIЙ редактQР дл:я Wiza'!"d и щел.к:в:ите
на сrЬ1Лке Add/Remove WizardSteps (Добавить или удалить шати Ma~'Тepa) . Добавьте
четыре шага. Шffi ПОК<:lЭЩiр на рис. 23.30.
После оnpеделеыин :пих шатов вы увИДИте. что w± za rdопредеJШ~Т области с цу­
стым содержt-Th1hIМ, нуда 1Jbl можете перетаЩИТЬ элемеНТJ>l ynрющения. необходи­
мые .ДJ!fI выбранного в На'столщm'f J\iЮмеitr шага..
1072 Частр V. Web-приложения и WеЬ-сервисы XМL

I 8ыеритеe цает ш
2 У<ОJllиrе Н_.НИ_ [~J .' T~I. Выберит<! моАeJIb
З Ук"",ите АОТУ АОСТО"'И
~_~" <~~;t;i!у-)чr:~;r~~: /'-.<- .. j
!.''' AllowR.turn True
I ,. Enebl.Тherrтlng True
\.,. En4bIeViewSt!te ТПJe
5t.pType АUlО

~"rtI·······
.;.,)
~~
, ,"'
,
,1
ОК .1 I C.ncel 1

Рио.23.30. КОНфигурация шагов Wizard

В HaIIIeM случае добавьте для шагов следующие элементы пользовательского ин­


терфейса (не забудьте указать соответствующее значение ID для каждого элемента.
используя окно свойств).

• Выберите модель: элемент управления TextBox


• Выберите цвет: элемент управления ListBox
• УКQЖите название: элемент управления TextBox
• УКQЖите дату дocmaвк:u: элемент управления Calendar
Элемент управления ListBox является единственным элементом интерфейса
Wizard. требующим дополнительной настройки. Выберите этот элемент в окне
проектирования (убедившись. что перед этим вы выбрали ссьuшy Выберите цвет)
и укажите для элемента набор цветов с помощью свойства Items в окне свойств.
После этого в контексте определения Wizard вы обнаружите разметку. похожую на
следующую.

<asp:ListBox ID="ListBoxColors" runat="server" Width="237px">


<asp: ListItеrn>лурлурный</аsр: ListItem>
<аsр:ListItеm>зеленый</аsр:ListItеrn>
<аsр:ListItеrn>красный</аsр:Listltеrn>
<аsр:Listltеrn>желтый</аsр:ListItеrn>
<аsр:ListItеrn>светло-зеленый</аsр:ListItеm>
<аsр:Listltеm>черный< / аsр:ListItеrn>
<аsр:ListI1;:еrn>лимонный< / аsр:ListItеrn>
</asp:ListBox>
После определения каждого из шагов вы можете обработать событие
FinishButtonClick для автоматическигенерируемой кнопки Finish (ThToBO). В сер­
верном обработчике события получите параметры выделения от каждого элемента
интерфейса и постройте строку описания, которая будет назначена свойству Text
дополнитеЛЫiОГО типа Label с именем lblOrder,
1
Iлава 23. ~t}оСтраНИЦbll и WеЬ·элементыупраlUlени~ ASf'.NEТ 2.0 1073
pr()tect'ed\'qid ca·r~.i·'Zard_ FinlshBut tonClick. (.obj ее\: эепdеr r
WizзrdNаvigаtiог.Еv-епtA:оgs е)

11 Пo.nyт.iеиие ~в.чеиий,
string order = striЩ:I,Fоrrr.аt(I1[ОJ, в-аш {11 [2}т Суде';) доставлен ,[3} . '1/

txtCarPetName .1ext.I Li~t:So~CCllpJfs. S.el~etecjVa.1lle, tхtСз!'Моdеl. Text,


саrcБ Lendar. S€>lectedDate. ToShO:t;tDateStr:lng!) ) ;
11 Прнс. . . .ви. ~На._.ЮI. ~nИСИ.
lbl'().rder. Тех"!: = orcler;
}

Итак. :ваш узел AspNetCarSite :готов. На рис. 23.31 noказан элемент Wizдrd з
деЙствйИ.

Создайте :маши:ну своей мечты


С ПОМОЩЬЮ FIaIIterQ мастера

Пн ВТ ер 4 ... hT Сб ВС
22ШJш.12~'±
i.li1..i!i.шll
J.21Зl1.J.S1illJ.fl
.12.:2.0~ШnМ~
Шi.1Z~п ~m 1 ~
Э. .:1.s.~1~2
- - .
I ~D1'IY.T~t :l\" ti~ _1

Ba1da, ваш светло-зеленый BМW, будетдоста:влен 30.06.2006.

Рис.23,31. Wiz.ard в деЙI':'11II.1·~

На ~томзаверroаетСR нащ обзор Web-элементов упрroмения. Не СР'МНеВ8Йтесь.


что иМеется Qчен}, Миого других элементов. которые эдесь О:IOJач:ены не быJtli.
Однако теперь вы должны чувствовать себя довольно уверенно при исполъэовании
ОСНОВНЫХ приемов ДaJIНОЙ моделР1 ПРОТРaI\dМ1'Iрованн.в:. А в завершение этой главы
мы рассмотри-ч элементы ynраэлеНИJl .. свЯSa.нliblе с контролем ввQдимых Дa!i~

ИСХОДНЫЙ kOA. ~айлЫ AspNetCВrsSlte размеЩены 1'> подkаталоtе~ соответствующем главе 23.
1074 Часть V. Web-приложения и Web-сервисы XML

Роль элементов управления,


связанных с контролем ввода

Заключительной группой рассматриваемых здесь элементов управления Web-


формы являются так называемые элементы конmpoля ввода. В отличие от осталЬ­
ных рассмотренных нами элементов управления Web-формы. элементы I\.РНТРОЛЯ
ввода используются для генерирования не НТМL-кода, а JavaScnpt-кода клиента
(и. возможно, программного кода сервера). предназначенного для проверки пр а­
вильности вводимых В форму данных как показано в начале этой главы, контроль
ввода на стороне клиента полезен тем. что в этом случае вы можете обеспечить вы­
полнение различных ограничений для вводимых данных на месте, перед тем как
возвратить данные Web-серверу, в результате чего число ресурсоемких обращений
к серверу уменьшается. В табл. 23.11 предлагаются описания элементов управле­
ния ASP. NEТ, связанных с контролем ввода.

Таблица 23.11. Элементы контроля ввода ASP.NEТ

Элемент управления Описание

CompareValidator Выполняет проверку значения одного элемеliта управле­


ния на равенство фиксированной константе или значению
другого элемента управления

СustошVаlidаtог Позволяет построить функцию пользовательского контро­


ля ввода для данного злемента управления

RangeValidator Проверяет принадлежность значения задаНIiОМУ диапазо­


ну значений

RegularExpressionValidator Проверяет соответствие значения соответствующего


элемента управления заданному шаблону регулярного вы­
ражения

RequiredFieldValidator Гарантирует, что данный элемент управления не останется


пустым (т. е. будет содержать значение)

ValidationSummary Отображает резюме всех ошибок проверки ввода на стра­


нице в формате списка, списка с буллитами или формате
единого абзаца. Ошибки могут отображаться "на месте"
и/или во всплывающем окне сообщения

Все элементы контроля ввода. в конечном счете, получаютсн из общего базо­


вого класса Sy stem.Web.UI.WebControls.BaseValidator, поэтому они должны
иметь множество общих свойств. Описания ряда таких свойств предлагаются в
табл. 23. 12.
Чтобы продемонстрировать осно:ВЫ работы с элементами контроля ввода. создай­
те новый Web-узел с именем ValidatorCtrls. Сначала поместите на странюw четы­
ре типа TextBox (с четырьмя соответствующими информирующими типами
Label).
Затем по соседству с н:аждым полем разместите типы RequiredFieldValidat or,
RangeValidator, RegularExpressionValidator и CompareValidator. Наконец. до­
бавьте одну кнопку (Button) и надпись (Label). рис. 23.32.

l
""

ГЛ1!ва ~З. Wеli>-стран~щы \.1 WеЬ·элементы y.npa.вne.lH1R A~P.NEТ2.0 1075


Таблица 23.12. 06Щttе СВОЙСОВЗ anемею-оs контроля ввода AS~NET
Свойство Описание

Control ToVa lid.ate Чит-ает или устанавливает имя элемента yn,равпениJt, который необ·
жщимо контралирOlJ'ЭТЬ

Display Читает ИЛIo1 устанаsливэет эначеН1<1е, характ~ризующее вид отображе­


Ния сообщени!'l об ошибке,ф1~ ЭiJIТемеНТ;lКо~tТРоЛЯ ввода

EnablеСl.lапl:.Sсriрt Читает или у.СТ!lAЭ6Ливает ПРИЭ~lаlC активизации контрlЭЛЯ в:вода на


стороне КЛИe!ffi1

fficrarMe.ssage Читает 1IIЛli\ YCt3t-tаВЛl/lвает TeKDТ сорбщения об ошибке


ForeColor ЧИ"fает илj.1 уста,fJнвливвет цвет сообщения, отобра)КаеМQГО при отри­
цатеЛЫ·IQМ исходе проаерки авода

11оЩ! об!,IIТf!J!ЫIIП'О IUtOдfC


~l!lOД'lt.'e 'СВОВ' I1l.iЯ I':'I!I ]ле.~ нy;t.,i:.,. ~C,>! Jц,", п:st е

:JeryJlll]lШ1В _РIrЖВНИI!;
r-- .- ~;Бi',~;l1'f д",йt'ТЕШ1\!:]{Ы,,~ ~!Н\"tЫИL\" '(!:Ь 2БN

Рис.23.32. Элемеиты. КОТОРЫе гтридется I(~НТ'рОЛИРОВ,пь

Тепер:ь у вас ест!'. IIОЛЪЗ0ватeiЛI>ский интерфейс, и .мм с вами lI.lOЖем занятьс~


настройкой :каждого из его эле:меВТОв.

Элемент RequiredFieldVaHdator
ffi:iСтроИть RequiredFieldValidator очень просто.. В окне СВОЙСТВ Vis.ua! эtшi1а
2005 установите для СВОЙСТВ ErrDrMe.sBage и CGntrolTaValidate 'IJYжыые значе­
НИЯ . Определение *.asp.x ДОЛЖНО быть 'ГаКИМ.

<~эр: R-eq'u i геdFiеldlJ:аli da'tc.r lrF"Re'q U.iredFieldY.alida t C> r 1 ..


t'unat=" server" ContxolToVi&l.i~te=i'txtR.~edF~e1d' "
E:rrо.rМевsa.ge='\Qй! Здесь нyиmо Bвe(:1m! iЦ8!RИble. ">
<} а.зр: fI,egu{ redFiel dValid'a t ot>
1076 Часть V, WеЬ·приложения и WеЬ·сервисы XML

Одной приятной особенностью RequiredPieldValidator является то, что этот


элемент поддерживает свойство I nitialValue . Это свойство используется для
того, чтобы гарантировать ввод пользователем любого значения, отличного от на­
чального значения соответствующего поля TextBox. Например. если пользователь
обращается I< странице впервые. вы можете настроить TextBox на отображение
значения "Введите свое имя". Если не установить свойство InitialValue эле­
мента RequiredField\'alidator. среда выполнения будет предполагать. ч:то стро­
ка "Введите свое имя" является действительной. Для гарантии того, что данный
TextBox пройдет контроль толы<o тогда. когда пользователь введет CTP0I<y. отлич­
ную от "Введите свое имя". вы должны настроить элемент тан, кан показано
ниже.

<asp: RequiredFieldValidator 1 D='"Requi redFieldVal i dator 1 "


runat="server" ControlToValid~te="txtRequiredF i e l dH
ЕrrоrМеssаgе="Ой! Здесь нужно ввести данные."
InitiаlVаluе="В.еДМТ8C1I08 им.">
</asp:RequiredFie1dValidator>

Элемент RegularExpressionValidator
Элемент RegularExpressionValidator может использоваться тогда, ногда тре·
буется сравнение введенных символов с некоторым шаблоном. Так. для гарантии
того. что поле данного Tex tBox содержит действительный номер социальной стра·
ховки США (US SSN). можно определить элемент так. как предлarается ниже .

<asp:Regu 1arExpressionValidator I D="Regu l arExpressionValidatorl"


runat="server" ControlToVa1idate="t:1{tRegExp"
ЕrrоrМевsаgе="Введите действительное значение ИS SSN,"
VаlidatiоnEхрrеааiоn="\d{З}-\d{2}-\d{4J">
</asp:RegularExpressionValidator>
Здесь обратите внимание на то, что RegularExpressionVa1idator определя­
ет свойство ValidationExpression. Если вы никогда не работал» с регулярными
выражениями, вам для понимания этого примера нужно знать только то. что ре­

гулярные выражения используются для проверки соотвеТСТВИR заданному стро­

ковому шаблону. "Указанное здесь выражение " \d {3 }-\d {2}-\d {4}" соответствует
стандартному номеру социальной страховки в CIUA имеющему вид ХХХ';<Х'ХХХХ (где
х означает любую цифру).
Это KOНI<peTHOe регулярное выражение вполне очевидно. но предположим. что
нам нужно проверить соответствие действительному телефонному номеру Японии.
Выражение, необходимое в данном случае, является более сложным - оно имеет
вид" (O\d{ 1, 4) -1 \ (O\d{1,4) \) ?) ?\d{l, 4} -\d{ 4) ". Поэтому очень хорошо. что при
выборе свойства ValidationExpression в окне свойств вы имеете ВОЗМожность
выбрать подходящее значение из встроенного набора часто испольэуемых регу­
лярных выражений (рис. 23.33).

Замечание. Если вам действительно нужны регулярные выражения, то знайте, что платформа
.NEТ для программной работы с регулярными выражениями предлагает два пространства имен
(System. Text. RegularExpression s и System .Web.R'e gula rExpressions).

Глава 23 . Web-страниuы иWеЬ-эле:ментOj уnравленИ'я ASP. NEТ Z.;O 1077

Stondartl""Фl'Мs/on$:
,~~~;t\XL~-~d~-- '- " --- -- -- -- -- ---- - .- ---~1
! R.~,C. pt,;пe nlJmw ' :1
,Я.R .С. D()S~~I code I
IР;!! .С. 500.1 setш~.,· t!шmber (JD "urт1ber) iIII,:,i!,J
tni1&' ..,;~~

=_ __
J.l.5,

\f~1OO о.""я1tJn :
;~{~t:\df21-\df<I} -~ =--_-~ -~_: .. ___ ~ J

РИI!.~З.33' С'О:Щ/j-ние регулярмого выражetJIII.Q с пQмощ.ыо ViSШlI Studto 2005

Элемент RangeValidator
Б дол{)лнение k свойствам Mi I1.i ffilШI Vаlu е и Мах i ni u тУ а } u ё. элементы
R аngеVэ lida tor имеют своис'l'ВО Туре. Чтобы :ВblIЮЛНИТЬ npOBep!ty ввода ПОJ!Ъ30ва­
тем на соответствие ЗВДaJ-ШОМу дйa.naзону целых чисел. ДЛЯ этап) сво.йства.нужш;i
указат.ь Iпt е g еt (что не ЯВЛЯе'Гся эначеl-шем по умолч:аниюIJ .

< а$р : RiJi.fJgeValidat o r JD"," Rёщ g9Val idaJ:: o r J " r'LТ:Г1 Ю t = " s e rve!:"
Со.] t r o;l'1'~Vali.d3 Ье =" txtRang e '.
Е!т оr!1<:'!ss аg'е=l'IВ:&едите з:н.а'У'еН Ие между О и 10 0 ."
И21.:в:iInumУа1uе="!ОО'·' Мiцim:WnValue="O" 'r~" Lnteqer">
<'" а зр: fl.angeVal ic"lalt.cr>
Элем~нт HangeVa 1 idator можно иёпальзовать и 'Тогда, жогда нужно npовери.тъ,
что введенное знаЧе}пiе при.иaд.iIеооfТ заданному дnапазоцу денежных значеЩf.Й,
значений дат. чи· (;е,)1 с IШаваЮЩеИТQЧКОЙ ИЛИ CTpOKOBъ:rx. Д'aшJ:ЫХ (это 11 еСтЬ значе­
ние, уст<Ыавлимемое пО' умолчанию) .

Э.nемент Compar:eValidator
Наконец. обратим ваше внимание на то, что СоШра.lеУа lid a.t Ci r поддерживаe-r
свойство Opera:t ar.
< азр: Comp<J.,!:' ",'Ja 1 idцtоr ID= "'Compar<;rVal idat-orl" r .1J1 a t=" serv€·r" 1

,C<:;rfl tJrc. .lT~ \]", lica i;;e= '.' txt C-ompa.r i .soJ.:l "
Еr r·Сirм:еS.5,аgе="ВэеД lilте зна че ние , Me f1:bln.ee 20.'"
OpQ.r atorr="lieSS!l'han" Val ueToCQmp«,re=" 20 '.'.:>
</ а sp : С ошра l.tVal .Lda tGI:'
Поеволъку задачей это.го элемента «онгроля :ввода является сравнение 3На­
че-НИЗ. в текстОвом блоке с другим значением при помощи бинарного оператора.
не удивителыш ТО', ЧТО свойству Opera tcr можно назнаЧйтъ такие значеюш. как
Les,s'Tha о (:менъ111е) . GX'E!cJ te:c1' h.a 11 (Ciолъше). Equa l (равно) ~! No t:.Eq ual (н:е равно} .
Также ЗilllJе ':гьте, -что ДЛЯ ув:азания значения, с RОЛDР:ыМ nPОИЭВОДИТСЯ сравнение .
испо1lЪ3Уется Val HeToCO!I1f"are.
1078 Часть V. Web-приложения и Web- сервисы XML

Замечание. С помощью свойства ControlToValidate элемеНт CompareValidator можно


настроить на сравнение со значением из другого Зllемента управления Web-формы (а не с кон­
кретным "жестко" заданным значением) .

Чтобы З8I!ершить соэдание программного кода для страницы, обработайте со­


бытие Cl ick для типа Button и проинформируйте пользователя о З8I!ершении ра­
боты программы контроля.

protected void btnPostback_Cl i ck(obj ect s ender, EventArgs е)


[
lblVa1idati onComple te.Te xt = "Вы пр ошли контрольную проверку!";

Теперь откройте созданную страницу с помощью любого браузера, Никаких за­


метных изменений ВЫ не увидите. Но если вы попытаетесь щелкнуть на кнопке
Подача запроса после ввода неподходящих данных. появится ваше сообщение об
ошибке. После ввода корректных данных сообщения об ошибке исчезнут, и соот­
ветствующий запрос будет ОТПР8I!лен .
Если взглянуть на HTML-KOA. отображаемый браузером. вы увидите. что эле­
менты контроля ввода генерируют JаvаSсгiрt-функцию клиента, использую­
щую специальную библиотеку JаvаSсгiрt-функций (она содержится в файле
WebUlvalidation.js), которая автоматичеСIЩ загружается на машине пользовате­
ля. По завершении про верки ввода данные формы напр8I!ЛЯЮТСЯ серверу, где сре­
да выполнения ASP.NEТ снова БЫnОЛНИТ те же проверки уже на Web-сервере (для
гарантии того. что не произошло НИRаких искажений при передаче данных).
В связи со всем сказанным. если Н1ТР-запрос был послан браузером, не под­
держивающим JavaScript клиента, весь контроль будет происходить на сервере.
Поэтому вы можете программировать элементы контроля ввода без учета возмож­
ностей целевого браузера - возвращенная НТМL-страница напР8I!ИТ задачу КОН­
троля ошибок обратно Web-серверу .

Создание отчетов по проверкам


Заключительной из рассматриваемых здесь задач контроля ввода будет ис­
пользование элемента \'аlidаtiопSumшаrу, В настоящий момент каждый из ва­
ших элементов контроля отображает сообщение об ошибке в том месте, где это
было определено на этапе разработки. Во многих случаях это будет как раз то, что
вам требуется. Однако в сложных формах с многочисленными элементами 13вода
вы не захотите видеть неожиданно всплывIoщиеe то тут. то там яркие сообщения.
Используя тип Vаlidаti опS umПlвrу, вы можете заставить все ТИПЫliOНТРОЛЯ ввода
отображать сообщения об ошибках в строго отведенном для этого месте на стра­
ниде.

Первым шагом при этом является добавление Vа lidа t iОП SUПlmа rу в файл
* .аБРХ. Дополнительно можно установить свойство HeaderText этого типа, а так­
же эначение DisplayMode. которое по умолчанию задает представление сообще­
ний об ошибках в виде списка с буллитами.

<аsр:Va lidati onSummary id="Va l idationSummaryl n


style="Z-INDЕХ: 123; LEFT: 1 52рх; POSITION: absolute; ТО Р: 320рх "
runat="server" W i dtГ1 ="З5 Зрх "

Гlrэsэ 23. Web-стрзниuы и Web-зnеме1iтЫ ynр«в:ле JJj.j\\ ASP.Ntт 2.01079


Hea~ext= "Эле-ме"ты "Ввада, ТРI:!'ОУ ЮЩИ@ КQрре:К·ТИРОВ J<.и . " ;'
<.1 а вр :'.Ja lidat i (шБ шmnа rу >

Затем ДЛЯ :каждого э-лемента контроля ввода на странице (дrш RecpJ.iredFie l d-


Va li dat'oI, RangeValidat cir и т; д.) нужно установить свойство I)i splay р<UШым
знэ-че1IИЮ Nопе-. Это rарантирует. что БЫ не увИД}l.те ЦУБЛИRатов сообщений об
ошибках для одного й ТОго »re случая неудэ"пюго завершения npовврttй: (I{ОГД;! ощю
сообщение отображается в резюмирУ10щем списке. ii другое - в месте размещещm
ЭЛемента контроля ввода).
.Наttонец. если требуется отобразить сообщения об ошибке с помощью
Mes s ag:eBo;x клиента. то установите ДЛЯ свойства Show'MeS's.ageBox знаЧ'ение true
(истина), а для СRойства S.h ow-SumПl.агу - значение [а1 5 е (ложь) .

Исходный .I:IA. Проект VаlюЭ!.{)тCtrls размещен е подкат'моге, соо'Т'serс;аующем tлаве ;2~ .

Резюме
Создание WеЪ-приложений требует иного подхода по сравнению C1ieM, 1ЮТор'ыЙ
испощ,6Устс,я д;пя создШЦlЯ " 11'здициqдщ,щ" приложенИй:. В начале этой тлаяы: быn
преЩIQЖен краrnий обзор фуццам:е;нталъвщх ~оставлюощих Web-разрабоТRИ, :н; КО­
TOP:J>n.t можно ОТНе<?ТИ НТМL, НТГР. сценарии :клиента .и сценарии сервера ripи ис­
ПОJIЪЭОISЗНЦИ клаСЕ;;ичесщэй 1WQIОЛОТИЦ ASR
3вачитеJil>Е.аЯчастъ f1l8Вbl б:ьща n:ОСНJiJlЦeJ-Щ рассмc.r.rревию архитектуры страви­
ЦЬ1 ASJ'~NE't I$I увидeJПf. -что с' I~a:щдым файлОМ ~ . а $'РХ в пр~~те связан нeJrowoрый
!ti1acc, производньтй от System.Web.UI.Page.. С помощью 1'ажото подхода ASI'.1\ТEТ
поаволяет строить более рриroдные ДЩf мнШ'окра'~-ното использования системы.
соответс~ующие:npинципaм ООП. в этрFj тл~е раесматрив:ал.оеъ TaIOll:e ИQПодь­
З0в;;щие щаблонов стрaJ:Ц'Щ щ Рa3ЛW'JДЫХ WеlrЭЛ5ментов управления (ШLПЮЧая но­
вые типът ,G.r i d\view и Wiz.a r d). B~ моти убеДКIЪCЯ 1'1 ТОМ. что эти элементы rp.aфп­
ческоro щггерфейса отвеЧJ'Щ)Т За создание ЩJДХОДЯЩИХ деснршггоров IП'МL-:кода,
l.Iапрaщrшмo.rо клиенту. Элементы контроля ввода ЯВЛЯЮI'ся серверными элемен­
T~, :tra которые ВОВJЩГается щ!д!!ча подroтовки Java&tipt-Jtoдa :к.nиента длн .вы­
полнеJЦIЯ apoBeplU,r дoцrcтимостi1, введенных в форМу да:tшых. чтобы уме;ньШить
КQmlЧес.тво н~бходимых обраще.l;1И.Й к ct';pBepy.
ГЛАВА 24

Web- приложения
ASP.NET 2.0

П редыдущая глава была посвящена номпозиции страниц ASP.NEТ и поведе­


нию содержащихся в них Web-элементов управления. На основе полученных
знаний в этой главе мы рассмотрим роль типа HttpApplication. ВЫ УВидите, что
фyrrn:циональные возможности HttpApplication позволяют ВЫПОЛНЯТЬ перехват
ряда событий, что дает возможность рассматривать Web-приложения. CI<Opee, нан
связную единицу. а не нак набор автономных файлов *.азрх.
В дополнение к исследованию типа HttpApplication в этой главе танже об­
суждается важная тема управления состоянием объентов. Здесь вы узнаете о роли
данных состояния представлений и элементов управления, о перемею-fЫX уровня
сеанса и приложения. а также о связанной с состояниями конструкции ASP.NET.
которая называется КЭШ прuложенuя. После основательного изучения предлагае­
мых шraтформой .NET приемов управ.ilения состояниями, в коще главы мы обсу­
дим роль файла Web.config и различные приемы изменения конфиrypации при­
ложений.

Проблема состояния
В начале предыдущей главы было yRазано, что НТТР является сетевым протоно­
лом, не обеспе"-швающим сохранение состояний. Именно этот факт делает процесс
разработки Web-приложений столь отличающимся от процесса построения вьmол­
няемого компоновочного блока. Например. при создании приложения Windows
Forms вы можете быть уверены в том, что любые члены-переменные, определен­
ные в :классе формы. будут существовать в памяти до тех пор. пока пользователь
не прекратит работу выполняемого файла.

public partlal class MainWindow Form


{
// Данные СОС~ОRНИR.
private string userFavoriteCaL;

Однано в WOI'ld Wide Web вы не можете делать тание роскошные предположе­


ния. Чтобы не быть голословными, давайте создадим новый Web-узел ASP.NET
1082 Часть V. WеЬ·приложения и Web-сервисы XML

(с именем SimpleStateExample). содержащий один файл *.aspx. В файле с внеш­


ним кодом по;:щержки опредег1ИМ строковую переменную уровня страницы с име­

нем userFavoriteCar.
public partial class Default Page
(
/1 ДаНИilе состоRИИR?
private string userFavoriteCar;

Далее, построим Web-интерфейс пользователя, показанный на рис. 24.1.

Рис. 24.1. Пользовательский интерфейс для страницы при мера состояния

Обработчик события Click сервера для кнопки Указать ... позволит назначить
СТРОКОВУЮ переменную в соответствии со значением TextBox.:
protected void btnSetCar Click(object sender, EventArgs е)
(
11 Сохраиение ииформации о машине.
userFavoriteCar = txtFavCar.Text;

а обработчик события Click для кнопки Прочитать ... будет отображать текущее
значение члена-переменной в поле элемента Label страницы.
protected void btnGetCar Click(object sender, EventArgs е)
{
11 Присваивание тексту иадписи зиачения члеиа-перемеииоЙ.
lblFavCar.Text = userFavoriteCar;

При построении приложения Windows Fопns можно предполатать, что после


установки пользователем начального значения это значение будет сохраняться в
течение всего времени работы приложения. Запустив наше Web-приложение, вы,
к сожалению, обнаружите, что при каждом вторичном обращении К Web-серверу
значение строковой переменной userFavoriteCar снова возвращается в своему
пустому начальному значению, так что поле текста Labe 1 будет постоянно оста·
ваться пустым.

Снова подчеркнем, что НТГР не имеет никаких внутренних средств автомати·


ческого запоминания данных уже отправленного Н1ТР-ответа, и именно поэто-
Глава 24, Web-ПРИЛОJКеН\ltя A$P.NEТ 2,О 1083
-м:у объект Ha ge H~м.~mцtO униq10жается. Когда клиент повторно обращается к
фaЙJIY * ..азрх" СОЗДаетqr аовый объе1СТ P.a'ge, переустзнавлйвающИЙ все -"шены­
Jlepeм~e уровJ:iЯ страницы . 3то, очевидно,. Е оказывается rлавной проблемоЙ.
Представьтесе~. ~аким бы неудс;б1-1Ь1М бьm npоцrсс:зшаза товаров. если бы каж­
ДЫЙ раз при обрэщt;'НИИ R Web-серверу вен введенная вами информация (напрй­
мер, о ТОМ, что вы хотите нynить) npопада:llа. Если вам Необходимо помнить ИВ­
формацию о ПCl.7IbqQва:телях, которые реги(.'трируются на вашем узле •.вам пРИДетс.я
исдtщъэоватъ раэ.тllfчные приемы сОХранения COCTOнl:UIН объектов,

Замечание. Эта проfiлема касается не 1'рЛ!>КD ASP.NE1 Geрв'Петы Java, COI-, " класСические" ASP-
и РНР-при:ложения - !tceM этим технолоtИЯМ таr<жe hриход\ltтC1I решат!> проблемы управлеt'lия
состоянием .

Чтобы сохранить ав:ачение СТрОКОБОro типа 1JserFavor ite.Ca!: в uромежуТКI::


.ме'ЩДу повторными обращениями k серверу, можно эапОМВИ'1;ь значения <!то.го
"ГИDЗ в сеавсовой п.еременнОЙ. Сооrrветствуюшие ПОjфобвости обработ1\И сос'{о­
ЯНИ$l сеанса БУ1I!трас.сматриваться в следующих ра;ще.л:а:х. Но здесь для ПОiЛltо­
ты мы npююдим сьотве1"СТВУЮЩИЙ llpOI'рaw.mы:й 1Щ/l. nеобхо,дl'tIvIЬJЙ МЯ тенущей
страницы (заметьте, что здесь больше не используется .цриватный ЧJ"lен-перем~­
liWI строкового· типа. таЕ "ГГ() не забудьте закомментировать ИЛИ просто удали:х:ь
его определение).

p r o t€-c t .ed vQid ,b tnSe eCar...:t::11 C' k (Qbj E:c t S е'л d е. у, Б:V·~п.t A.rgз е,)
{
Se s·!!'j:Qn ! "-Us~rFаvС а r"' J = tхtFэ\rC,3 :t. Tex·t ;
}
р!' ,з t e-c ted 'lfQ l.d btnGetC a r'_ С1 i c k (.o bj ect sencle r ~ Еv еп tAt g s eJ
{
lblFavCar.Text == \st r iпgISеs s iоn["!Jsе.:rhvС аr "J;

Если Вь1ПQ)1НИТЪ приложени.е теперь, то. информация о любимой машине в


npомежутке междУ обращешsя:ми R серверу будет сохра:нятьея благодаря объе~­
ту @ttpS e s .si o n S tate, обрабатываемому с помо[цью унаследованного евой;ства
S·e s s i oJ'I .

Ис:ro,цнwй код. Файлы IlРl1мера SjmpleStateExample размещеНbI g nо.:цкаталог'е , СQОТВ6l'Cтвующем


I'лаве24.

Технологии управления состоянием ASP.NEТ


ASRNEТ .npедл:arает целый рЯд м:еханиЗМОВ. KDTopыe 'Можно ИСПОJlьзовать ддя
UОДд.ержки информации Dостоанйя g Web -прИ'ложеШi.НX. В частности. у вас на ~ы­
бор есть следующие варианты.

• ИcnОliЬзoВaI'.IИё данных состояния предстаВiТений ASP. NEТ.


• ИСПОЛЬЗQвание данных СОСТOfIНИН элементов у.правл.енин A....~NEТ:

• Определение nepeMeJ!НЬЦ{ УРОВЮI пр:иложе;ния;.


1084 Часть V. Web-приложения и Web-сервисы XML

• Использование объектов кэширования_

• Определение переменных сеансового уро:вня.

• Взаимодействие с данными сооЮе.

Мы рассмотрим детали каждого из указанных подходов по очереди, начиная с


темы состояния представлений ASP,NEТ.

Роль состояния представлений ASP.NET


Термин сосmoянuе nредсmавленuй уже упоминался множество раз здесь и в
предыдущей главе без формального определения, так что позвольте демистифи­
цировать этот термин раз и навсегда. В рамках классической технологии ASP
требовалось, чтобы Web-разработчики вручную ун:азывали значения элементов
формы в процессе построения исходящего НТГР-ответа, Например, если поступив­
ший Н1ТР-запрос содержал пять текстовых блоков с конкретными значениями,
файл *. asp должен был извлечь текущие значения (с помощью коллекций Form
или QueryString объекта Request) и вручную поместить их в поток Н1ТР-ответа
(ясно, что это было весьма неудобное решение). Если разработчик этого не делал.
то пользователь видел пять пустых текстовых блоков.
В ASP.NEТ больше не требуется вручную извлекать и указывать значения. со­
держащиеся в НТМL-элементах, поскольку среда выполнения ASP.NEТ автомати­
чески создает в форме скрытое поле (_VIEWSTATE). которое включается в поток
обмена между браузером и соответствующей страницей. Данные, присваиваемые
этому полю. представляют собой строку в кодировке Ваsеб4. состоящую из пар
имен и значений. которые характеризуют каждый элемент графического интер­
фейса на данНОЙ СТРa.fшце.
Обработчик события Init базового класса sуэtеm.WеЬ.UI.Раgе отвечает за чте­
ние значе:fШЙ. обнаруженных в поле VIEWSTATE. и заполнение соответствующих
членов-переменных в производном классе (именно поэтому по крайней мере ри­
скованно использовать доступ к параметра.'\<I состояния Web-элемента в контексте
обработчика события Init страницы).
Точно так же перед исходНIЦИМ ответом браузеру данные VIEWSTATE исполь­
зуется для заполнения элементов формы, чтобы текущие значения НТМL-элемен­
тов оставались такими же, какими они были до повторного обращения к серверу.
Очевидно, лучшим аспектом поведения ASP.NET в этом отношении является
то, что все это происходит без вашего вмешательства. При этом, при желании, вы
можете изменить или ОТRЛЮЧИТЬ такое приннтое по умолчанию поведение. Чтобы
понять, как это сделать. давайте рассмотрим конкретный пример использования
данных состояния представлеНИЙ.

Демонстрация использования состояния представлений


Создайте новое Web-приложение ASP.NET ViewStateApp. В исхо­
с названием
дную страницу *.aspx добавьте одинWеЬ-элемент управления ASP.NEТ ListBox и
ОДИН тип Button. Обработайте событие Click для Button. чтобы обеспечить поль­
зователю возможность вторичного обращения к Web-серверу.
1

Глава 2~.WеЬ·ПJ»1l1Оже(,lИЯ ASP.NEТ 2.0 1085


:рrptе с'tёd 'J~, Jd ЬtПDОРQ'$tВа еk_Сl i сk ( ~bj-ect sе:иdе-r. J E:vl1!l1tArg.s eJ

1
11 д,ля в'l"ор~чвоХ'о оБРЦUЮJJ: серверу.

Тбl1ерь, ИСПОЛЫJyИ OJ{fiO свойств V1Sual Stu..dio 2005. ПОлуЧИrz'е доступ к свойству
:Ltешв и добавьте:в l,i s tBo'X чеmрезлемента Llstlt.em. Ре.зультат должен быть
примерно Т3RИМ.

<d Бр ; L i stEQX 1 о= "!Тi yLi$ tBQx N. l'Uцаt = " .serve.: r"'>


<;;, $р : L·iз. t 1 t E':!l"'> ЭлеN!еrrr 1 <: I а sp ~ Li s tI·t ет>
<01 5р: Lis·t 1 t еш:'Э:л.еМЕН-{W· 2 < ! asp: Listl"tem>
<щsр: Li5 tlt еm>эл~е:Н'т З < lаэр~ 1i strt--8iD>
<"
<а s;p : :Liз II t еm :>'ЭлемэF.rТ .IJ аБ-Р : L:i st l ,t em>
< / i9 5р ; 1,i.5"tБ0Х"'>
Заметьте. что здесь элементы L i .s !.B,QX в файле *..a spx yIOiза:ны НВ"Н<'у. Выуже
знаете . что все эл~енты <asp::>. обнаруж.еЩlые в пределах НТМL-формы, авто­
маТJI<reC'ШS об:fЮв.пmoт с!Зое HTML-пр.~С'1'авление Пf~ред отцравкой Н1ТР-ответа (KQ-
He"IН'O, е.с.л:а ОНИ при ЭТОМ имеют атрибут rIЩа.с=1. SE!.):v'er·'I).
Директива <.%@Pag"'%> имеет необя:щтельЮ>IЙ атрибут ел.аы1vj"".wstаtе,' "ото­
РЪ1Й rro умолчанию установлен pa.вньrM .t rL1e. Чтобы отме.ПJ1ТЬ талое поведеШlе,
просто измените содсржим@е Д0:рeJtТшшr >( '" @J?age-%':> ТЦ.К, КЗJ> !Iрсд.лагается ющte.

<'jj@ Page EnableVie1'lStat~ =~:fal8e"


La l1g·u a@e= "c1t .. АJJt БЕvе" tWi ~e up: nt.rue"
' Code~11E="D€!fa1J.lt.a5px.c!3" J n.hel'i ts=" Deiault" %>

НО ЧТQ же ~ д~йств:wrел:ыюсти овна <щет {)ТЮllOчеmre учета состо1I'НИl'[ npедстав­


ЛeIщffl Опе:!, на этот БОПРОСЭа8ИСl\fТ рт многих фmtrQ,ров. В соdтветст.вии с преДJIо­
жеf:i~blМ "Выше определением данного термИна вы моrn:и бы прeдnОЛОЖWIЪ. что при
QТЮI!OЧ'eF.IЩJ j"ieTa состояния ilредстаипе:ни:й .цля файла .... гэрх значения l,cLstBox
не бу.цy-r СОХРЩJJIТЬСЯ при B1'O~ aaIIllOCax к Web-cepвepy. Однако, вьmолнив
пр:щло~еJ;ЦIе "в том Биде. в каком Э'l'Q приложеНне НaжJДИТ~ ~еЙЧас. вы можете с
удmщeн:и:ем обнаружить. Ч'I'О информа.ц1Ш В List.B.ox сохраняется независ'Имо от
торо. рюл:ько раз вы пОвТОРНD обрaщae:r'еill>Н странице. ФактичесКи. если рассмо ·
треть исходный HТМL-KQД, "Возвращае:мblЙ БРGWзер}'. Bьi увИдиге. ЧТО С1<рытое поде
_VIEWS1ATE там все равНО.J:1}IИеуТ-СТ6у'ет.

<irlpu t t уре=" h i
dden" name="VIEWST1\.TE" l d='; 'ilIEWS'I'A ТЕ" \Та 1 це= A/wEPP-..у
~:1Мjсmс2!NnЩkOXDNIW5+R2VDbNWtEtНl!Of+-Уfutvu=" 1>

Гlри~цщо:й, тю Щ')'J'орой строка состоЯНИЯ предстaшrений. не ш::чезает. яIШfIе'ТСЯ


то. что, фaiiд ... , а $ рх ЯВНО' 9Предел.яе:l' ЮIементы Liвtilox в контексте Jl'TML-десирип­
тора <fQ:rш> . ПОЭТОМУ эл~менты Li.9tBox будvт' автоМJ:trИЧес:ItИ .геп.еР1>'J}Jова'1'ЬСЯ при
EtaЩ:ЦОМ ответе Web-С~'рвер~ клиенту.
Но цредцоложим, что значеНi1Я REi.rIIeгo типа Ьiэt.Вох дИНамически указываюТ­
cff В фajpre ~шешнего кода поддержки. а :неи рамках НТМL-оriределенип <: form >.
Dщ"'ЩJ1a удащпе деRJIарEiЦИИ < аБр-.L.i.stlt€n;> из име:ющегося файла .... aspx,
<:a,:;;p':'Li;atBox ID="n:tуL iэtВох " runat="serve.r">
</ аэ р :1 i 5t,ВОХ>
1086 Часть V, Web-приложения и Web-сервисы XML

Затем задайте элементы СJШска в обработчике события Load в рамках файла с


внешним кодом поддерЖRИ.

protected void page_Load(object sender, EventArgs е)


{
i f ( ! IsPostBack)
(
/ / ДИИaюlчеСltое зanоnнение ListВox.
mуListВох.ltеms.Аdd("Элемен'I' l"~;
mуListВох.ltеms.Аdd("Элемея'I' 2");
mуListВох.ltеms.Аdd("Элемен'I' 3");
myListBox. Items.Add ("Элемея'I' 4");

для обновленной страницы вы обнаружите, что при первом запросе страницы


браузером значения в ListBox прясутствуют и учитьmаются. Однако после вто­
ричнщ'О запроса ListBox становится nустым.. Первым правилом состояния пред­
ставлений ASP.NEТ является то, что они учитывается только при наличии элемен­
тов , чьи значения динамически генерируются программным кодом. Если ВЫ явно
укажете значения в рамках дескриптора <form> в файле *.азрх, состояние зтих
элементов всегда будет восстанавливаться при вторичных запросах (даже если вы
установите для enableViewState значение false для данной страницы).
Данные состояния представлений оказываются наиболее полезными при ра­
боте с динамическими Web-элементами управления, которые должны обновлять­
ся при каждом вторичном обращении к серверу (примером TaROrO Web-злемента
является элемент управления GridView ASP.NEТ. заполняемый при обращении к
базе данных). Если вы не ОТКJПочите учет состояния представлений для страниц.
содержащих такие элементы. состояние таблиц будет предстаВllено в скрыто:м
поле _ VIEWSTATE. Сложные страницы могут содержать очень много Web-элемен­
тов управления ASP.NEт, поэтому вы можете себе представить. насколыщ большой
может быть соответствующая строка. При этом содержимое НТТР-цикла "запрос­
ответ' может оказаться достаточно объемным. что может стать определенной про­
блемой ДЛЯ коммутируемых Web-соединеНИЙ. В таких случаях может быть оправ­
данным отключение учета состояния представлений для всей страницы.
Если идея отключения учета представлений для всего файла *.азрх кажется
вам слишком радикальной, вспомните о том. что каждый потомок базового класса
Sy s t e m.Web.UI.Control наследует свойство EnableViewState. с помоIЦЫO которого
очень просто отключить учет состояния представлений для каждого конкретного
элемента управления в отдельности.

<asp:GridView id="myBugeDynamicallyFilledDataGrid" runat="server"


EnableViewState="false" >
< / a sp:GridView>

Замечание. Страницы ASP.NET резервируют небольшую часть СТРОКI1 VIEWSTATE для вну-
треннего использования. ИмеltНО поэтому поле _VIEWSTATE появляется в браузере клиен­
та даже тогда, когда учет состояния представлений отключен для всей страницы (или для всех
элементов управления).
,
[лвва 24. Web-nРИJто*ения ASP,NET 2.0 1'087

добавление пользовательских данных


состояния представnе"ий
в дО'ПО'лне'Efяе к ~вой,СТВУ En i3 bl @View S.ta t Е" ба~овый клас.с 8 ув t е m . WЕ' Ь .
1JI .Солt.rоl предлагает :нш.;л{Щуе.мое СВQЙСТВО
Yie!fJState. Э,И'QВoiiство в фоновам
реЛQ1Ме обеспе~ает дос"ryп ~ T~Y Syst€:ffi,Web.UI .StateBa g. представляющему
все дaнщ.r~ ПОДЕ _VIEW&TA'j:'E. с' 'n01l40ЩЪЮ индексатора ·типа $ta,teBag вы можеcrе
встрощ:ъ ПШ1ЬЗователъсцую ЩjфОр1;1ацию в скрытое поле V1 ЕWБТАТЕ формы, ис­
пользуя ДМ этога u.ОД2ШДНЩИ;Й Щlбор пар нм:ен и значений. ВОТ простой пример.

р.п~ t.есtеd veid btnAddTtNS~Click (pb:Je.c.t send-er, Even'tAJ:'g~ е)


(
Vi e:w$ta 11. е ["CustomVieIllStateI'tem" J = "110ль!:lо&ателы z:и€€ даЮfые!';
lbl VSValue ,Text = (~з:'f; rinq) Viеwstэtе ["'·сuэtОi!lViе.w;$tа·t",I ёет,"] ;

тип 9уst.ещ.WеЬ. О1 .,stateBag М;()Ж~ рабртатъ с любым ТИПОМ,. праизвоДilliIМ {}Т


Зуstеm.ОЬjес·t, ПО1:)ТОМУ. чтобы получить ДОС'ryll R значению данного ключа. вам
нужно явно. ЦРЩJ6равwватъ его jJ правильный тип данных (':в данном случafJ это тип
S·ystem. s't riпg). При ЭТОМсдеду~ учесть, что размещаемое ~ поле \'IEW:8TA7E
~начеН,Rе не может 6ыть аБСlЭлюmнр люtj'ым объектам,· ЕДИНС'IIВев:вьrм.и действи­
т~дънPIМИТИПамв в .АЩfНОМ случае двлюотс;я строки, целые числа. булевы значе­
ния. ~ AIrayLi·st и Ha:shtable-, а з::"dНЖе массивы этих тяnов.
НО ~СJIИ страницpr *. а s рх могут вставлять полы:!uваТ'ельские фрагменты :ин­
фОР;Мадии R строку _VJE\'iISTj\. ТЕ, было бы неПЛохо .выясни lЪ. ка:кЭТ(J сделать. как
правило.. пользовательские дaH1JЦI€ СОС'rоЩ{и,я npеДставл-ен:ий исполъауюТСR ДЛЯ:
УСТЩЮRJ\И HaCTpo~ IЩJIЬЗОВа,телн. н.щример, вы може11е саэдатьэлеме'НТ да:ннЫ&
предст~щ;ш. YRвяываюlЦИ.Й 1'0, Ю:.L.КИМ .i1Ьльаователъ желает :видеть Gri-dViеw (на­
пример. с точки зреют JJDРЯДJЩ СОРТИрОшm). Но данные Састояния I1pfЩСТавлений
не оченъ хороша IIОДХОДЛТ дn:я: "разllернутых" ПОЛI:i30В8Тe.JIbCi!:И:& данных, Таких :кан
oQ1>е:!{ТЫ в и;орвине цo.кyцa~. noмещениые 11 юш типы Dаt:аБеt или накие-ТDдРУ­
I"И·е сгreциaльfIЫе тидыI. ~гдa тр~буетсл aanоМнить слОх<НуЮ инфОР:МНЦИЮ • .JIY"Illlе
paOOIТaTЪ с сеа:нЩЩЫМ1;f д.анными. Но перед тем как переЙтин. со(н:ве'I'ствующе!'.ry
раэдеду щuцt:ro Qбс.Уждения, мы дол:жны: выяснить роль файла G..l'abal.asax.

И'сходныи КОД. Файлы примера VlewStateApp рc;tзмвщен:ы в ПОДI(~талоге. соответствуtOЩS~ таве 24.

Несколько слов о данных состояния элементов


в ASP.NEТ 2,,0 пpeдлarается поддержка C.DС1i'WлнuЙ ?JлемеНЛ!DВ уnpaвлekt..lя. а :не
ТОЛЪRо (:остоf1НRЯ представлений. Эта технОЛОГШl ~жазьmа~тся очень удобной при
работе с созданными вами Web-элемента.1I.Ш' управления ASP.NE'Т. ноторые .должны
СОХРaFlRТllсваи данные Между поС1ШДОвательными ЦикJ:1a.м1-r заnpосав. Для этm:а,
Rdнечно, моЖiiО 8cIJользавать 1:1 СООЙС·J.·во ViewS:tate. на если учет саQтая:ни.q nP'ед­
ставлений будет откшочен на уровне страницы. пользавательснИЙэлемеlf.Г уцрав­
лtlliиЯ работать не будет. Именво по этой причине Web-эяемевты управдtfiИЯ те.­
перь поддерживают свайство COf1trolState.
1088 Часть V. Web-приложения и Web-сервисы XML

Состояния элементов управления используются аналогично состояниям пред­


ставлений. но учет состояния элементов управления не ОТRЛючается при отнлюче­
нии учета состояния представлений на уровне страницы. как уже говорил ось. эта
возможность оказывается наиболее полезной для разработчинов пользоватeJIЬС:КИХ
Web-элементов управлеRИЯ (обсуждеRИе дa:tfНой темы в нашей книге не предпола­
гается). За подробностями обратитесь н документации .NEТ Framework 2.0 SDK

Роль файла Global.asax


к этому моменту у вас может сложиться впечатление, что приложение ASP.NEТ
представляет собой простой набор файлов *. аэрх и соответствующих Web-элемен­
тов управления. И хотя вы можете построить Web-приложение с помощью просто­
го связывания несколь:ких Web-страниц. вам. скорее всего, хотелось бы иметь воз­
можность взаимодействия с Web-приложеl-mем, RaR с чем-то цельным. для этого
в Web-приложение ASP.NEТ можно ВRЛючить необязательный файл Global.asax.
выбрав WebSiteqAdd New Item из меню (рис. 24.2).

Add New Item - C:\Chdpter 24\Code\ViewSlateJ\pp\ E1IIfl1


Тeщ>lates:
10 1(3

A~
1".
Class Style 50eet

Web XMl Fil. XMl 5ch~ma Text Flle AssembIy 5Ql D.tabase DataS.t Generlc
configUrati ... Resource F~e Handler

~ ~ ~ .~c:'~ ~\
Site Мар
l, Cry<tal Report Mobile Web
Form
VBS<riptFile Report JScript File Moblle Web
US;er Сопtrоl
Mob;le Web
Ccrlfigur dti,.,
r А~~J;;'t;;;~-';~~А~~;'е;;=,=с~t'=-"'~- ~.,,-,с~,,,с,,,='=-=""-"'" =,.=-'--~... =~ . .=,c~,. =,=- -'-""С'С· -,,-с.%~,'З .,
.•."" _ _ ~~ " _ _ _ _ ~ _ . _ .,,. _____ ",,, __ __ ~.,,,. ,,,_" ~ ' ''''' _ ' _ _~_ _ .,._,.
,
~ ~. . ~_._-e......., _ __ ---,,-,.; ~ , . -, ~,;, _ "-".....< ,, ~"':""'

______ ____ __ • _ _ _ _ _ _
,_ " _~.~ , _. _ ,_" ~ . ,, _~.

• __ __ _ ..
~ ,,_. ~_
г
~
~
-
~
с
.
.
с
-
-
-
-
-
-
~
-
-
-
-
-
-
~
. Global.asax
{ .. __ ".. :~ •.• _~~ ~ .• __ , .,. ~~. ~ ..........."'-,,.;. ..... ~'''<, .• ,_,c _---''-" . ~ ,'~ ~," ~ ~~ _'- _
г-,~ - -- , -"",---,~",~,- , ~ , - - -_...-
i Virual
...
I . _:~'
с# ~;~_·:~. ___ •. _ __ . __.,•• __ • .о _ _ с _____ с __ .. . ..

Add I[ Сапе.1

Рис. 24.2_ Добавление файла Global. аэах

в ненотором смысле файл Global.asax в ASP,NET аналогичен традиционному


файлу * .ехе. поскольку представляет поведение Web-узла в среде выполнения.
После добавления файла Global. аэах в Web-проект вы обнаружите. что Этот файл
на самом деле представляет собой блон <script>. содержащий набор обработчи­
ков событий.

<%@ Application Language="Ct" %>


<script runat="server">
1
Глава 24Wе'Ь"Г1РИ'110же~l~н ASP,NET 2.0 1089
vt;Jid А'ррliсаtiоц_s'tа:r:t !Object sender, EV611 tJu:Y8, e)
{
I j KOill, .iшо.лJulе!DolЙ npи запуске придozеми.:,

IIQid AFplioa-tJ: ол_r.nd(О.Ьjе.;::t ?'e nder, E:v~t1tArg'l\1 е)


(
/ / ХОД, ~OJ,l_~ npи а_ерщеfЩJ( ltpиnо*еliНЯ.
)
void Applic~t:tOll_ErYDr (Obje:ct s8fider, EventAIgs е)
{
1/ Код, ЗIUIОПН_NIiIй при nоU.пeюrи веобрабо.,ввиоЙ' ошиБJЩ,

v'(;;~i d. $еэ s i 'o,n _ Sta.rt (ODJe,ct s.mde r" E've.n tArgs е)


r
11 Ход, 1WE10~,и.еИЫЙ 1IflId' ~ХEblWЮf ИO!IОГО сеаиоа.

void Ses'SlO>il_Ehd ('Object. 's ende:r, Even-t;Z;..rgs .е)


{
/1 1\:О!д, ВЫnomlА:8ШIЙ ПРИ' ааверше.иим сеаиса.

</s-zrjpt>

Однаво первое впечаLЛенм:е может ОЩJ.3~ТЬСЛ обманчивым. В среде ВьnЮлнею'ш


проrpаммнъlЙ код зтого блока <.s.cr ipt ~ пр~образу~гся в тип мэ.сса. получающий­
ел i'l3' System. W.eb. HttpA:pplicati.on, E~ вы имеете (ДIЫТ работы с ASP.NET];
Х, ТО ВЫ(. наверное. в(;помните, ЧТО там Фа~ с БF]еumим }{ОдРМ l]оддержки ДЛЯ
81obal. аэах бут<вальн:о определял класс. I10ЛУЧЩОЩИЙСЯ не FJt.t.pApplici:\tion.
как уже БЫ,JIQ сказано. 'Иены. определе<щые в Gl Gba l .a$a~. содержатся 11 обра­
боТ'-lШtClК событии. ПО:ЗВОJl.filощихвэаимодtШ'ствоЩiТЬ с событиJ'ThЩ На уровне npи­
ложeюm (а тЩ'оое сеанса). Описания этих членов предлагаютCI:\ в табл. 24.1.

Таблица 24.1., Обработчики событйй Global ,а5ах

Обрабо-,:чмк соб'IТИJI Описани~

8ыbll3aетоя только при аапуС\(е Web-ПРИПDжения, поэюму ге.яери­


pyen::f1 ТОЛЬКО ОДИ/'i раз за B{)~ время аыnлliенIIJ~wfi:ь-t1pJил'ожени~•.
Являетr;я иде8.Л~НЫМ меСТОМAf!Я оnределеНI4Я,)l8ННЫХ уровня ПРИ'
ложеНVljIi, до.ОТУПFI~)( '11 любой ТО4.Ке \tI1eЬ'IlРj1Лt)жt'1Н~IЯ

ВЫЗЫf!аетс~ при 3аВ'ершени~ раБоты nр~)1ьжеlil1Я, .,.а:пример. вслед­


ствие пре'вышеliИ}1 времени ОЖИЩ1НИЯ дм последнего пользователя
W1и при завершении рабmы r\РИЛОJкеI1W!Я вручную с пФМОЩI:!JO 115
&;S'.si 011 Start О выз>iв<lется ПРИ регистраЦии нового 110льзоват.еля в lТpипожении.
~дeOb можно установюъ tтapaMeтpы, СВliзанf'lЫ~С I(ОНIФетным rrол(,­
зователвм

8bfЗЫвав,ся' при з.аверШеliИИ 'оеанс-а ГЮЛЬЗQВi'!Теля (оБыIноo ' В резущ.­


тате преаЫШ81fl1Я установлf;1ннаго времени 4)Ж\-1дaf;tия)

Аррl :iJca tioГl_Error () Глобальный Dбраf'JОТ'lIllК ошибок, КQТQР~И 6!>.lЗываетоя TorAa, IФгда
Web-ЛРJМJожени~ геf1ерирует необработанное ИСk<.I1iCчеНие
1090 Часть V. WеЬ·приложения и WеЬ·сервисы XML

Последний глобальный шанс для обработки исключений


Позвольте указать на роль обработчика событий Appl i са t i оп _Етто т () .
Напомним. что страница может использовать обработчик события Еттот ДНЯ обра­
ботки любого исключения . сгенерированного в контексте страницы и оставшегося
без обработки. ОбработчИl{ l>.pplication_Error () оказьmается последним пунктом
возможной обработки ИСК7IЮчеНИЙ. которые не были обработаны на уровне страни­
ЦЫ. как и в случае события Ет ro r на уровне страницы. вы можете получить досryп к
конкретному объекту Sуstеm.Ехсерtiол. используя наследуемое свойство Server.
void Application_ Error(Obje ct sелdеr, EventArgs е)
{
Exception ех. = Server.GetLastError();
Response.Write(ex.Message) ;
Server.ClearError();

Обработчик Appl ica t iол_Еrrоr () является ·последним шансом" обработки


события ДЛЯ вашего Web-приложения. где вы. вместо предъявления сообщения об
ошибке пользователю. можете записать соответствующую информацищ в журнал
регистрации событий Web-сервера. например:

<%@ Import Namespace = "Sys ·tem. Diagnost i cs" %>

void Application_E rror (Object: sender, EventArgs е)


{
11 Запись последнего соБJol'1'ИR в *урна.п соБJol'l'ИЙ.
Exception ех = Server.GetLastError():
EventLog ev = леw EventLog ("Аррliсаtiол") ;
ev.WriteEntry(ex.Message, ЕvелtLоgЕпtrуТуре.~rrоr);
Server.ClearError() ;
Resp ons e. Wri te ("Это приложение "зависло". Извините!");

Базовый класс HttpApplication


Как уже говорилось. сценарий G1 оЬ а 1 . а s а х динамически преобразуется в
класс. который получается из базового класса Systero.Web.HttpAppli c ation и
обеспечивает те же функциональные возможности. что и тип Systew.Web.UI.Page.
ОписаниЯ соответствующих членов предлагаются в табл. 24.2.
Таблица 24.2. Ключевые члены типа Systero.Web.HttpApplication

Свойство Описание
Application Позволяет взаимодействовать с переменными уровня приложения, используя
доступный тип НttрАррliсаtiолStаtе

Request Позволяет взаимодействовать с входящим НТТР·запросом (с помощью


HttpReques c)
Response Позволяет взаимодействовать с НПР-ответом (с помощью НttрRеsролs е )

Server Получает внутренний объект сервера ДЛЯ текущего запроса (с помощью


HttpServerUtility)
SessioH Позволяет взаимодействовать с переменными уровня сеанса, используя до·
ступный тип Ht tpSessionState
1
Глава 24. WеЬ~приложения ASP. Nп 2.0 1091

Различия между приложением и сеансом


в ЛSР.NEТ состояние ЦРДЛО$е:Е:УИЯ yчJ1'rвшает·ся зкзеМПJIЯpОМ типа HttpAPFli-
саtiолStаtе . ЭтОТ класс да!"Т воз~жиость сделаn. глоБЭЛI,НУЮ информацию до­
ступной ДЛЯ всех полъзоваТfЛеW (и всех страниц). зареги:стрироваННЬL'1: в "Вашем
IIpиложеl1ИИ ASRNEТ. При Э'FОМ :МQЩВО Ее толыщ открыть даннме при:ло:женнз для
всех пользователей вашего у;ща. HQ I'I сдела'!.'!!> так. чтобы при изменении знаitении
УРОВНЯ приложевия ·ОДНИМ из ЦО;Тl?зо:вателеи Э:Щ uзменецИЯ становило{]ь видимы­
ми д,ДЯ всех остальных UOJIЬЭЩШJ'елей при С..11едущmих обращениях к оерверу.
С ДругоЙ сторан:ы. данны-е СОСТОRJ:ПЦj ce4Flca .используютсп длн сохранении ин­
формаЦии, :касающеИСII lЮШРf:ТНОГО Щ).lJЪзоваТ~R (например. то:варОБ в корзине
покynaтеля). ПраКтически СОСТОНI;Пi"е сеанса пользователя представляется ТnПCfМ
iti"IaCCa И:ttрSеssl00п$tаtе. ПРJII. Р",;ГJJСТРации ~OBOГO пользователя в WеЪ-прило­
жении ASP.NET :среда выпо:tnreН.ИЯ РВТОI.щтичеС1(И НазНаЧИТ этому ttолъзоватеJrn)
новый Iщен1'ИфИКатор (1Щ .сеанса. время -использования ItOтoporo пр умолчанию
истекает после 20 минут Н~aRТИ.8НО(~ТИ: пользователя. Если на ЩЦJ1ем узле заре·
t"ИСТРИРУЮ:ГСЯ 20000 пользователей. ~hI б.удете иметь 20000 отдельных объектов
Нttрsез.siоп2tаtе. каждо:муиз :кoтoplil;X. будет на~начен СБей yниRмьньiЙ иден­
тификатор ·Сеанса. Схема вааимо(:.вязеЙ M~'n"V Web-ПРДЛQ7I1"ением и We.b-сеан-сами
Показала на рис. 24.3.
Вы. возмоЖНО. знаете. что в paмK~ I\,JIасси.:rе(.']{оЙ техдОЛОГИИ АSРщuшые со­
стояния пРйлож.еНИfl И СDСТОЯН1'iЯ сеанса пр~дставляютс:я разны.мn СОМ-объек­
тами (например, Аррl icati~f) и Se$sioQ 1. в A8P.NE'Т типы.• I,rpОИЗВQщtые от P<'lge.
как и тип Attj:>App1:i c: ati·oll, 'Испояьзу~т свойства с Т2ЩИМИ же именами (т.е.
Applicati:oT, и Sе э s:iоn). которые npедос:гaвл.moт· дос'J1'Ц к СQ'ответетвуюпщм TnЦaм
!ft:tpApl?licatiQnState и ИttрS еs:siо:л$tаt·е.

Web·nplUlo*~н.lie (RttpAppliciI tion)

( BttpApplication$t:ate·; Т11P(janыtая ~(»о1аI4И!\, общая Д!1Я всех сеансав, )

;, .....

СеансА
(Ht,t pSessiOn$!;atef ·
"
CeaflC
(BttpSe.ssionSt&te)
n ,

(- Сеанс В -~
(HttpSessionSta.tej ,
,

I Кливит Д I I Кnиент f:J I I КnиеlП л


I
Рис. 24.3. 8~мосвязи nриложения 1'1 его C~HCOB
1092 Часть V, Web-приложения и Web-сервисы XML

Поддержка данных состояния лриложения


ТИп HttpApplicationState предоставляет возможность совместного исполь­
зования глобальной информации для множества сеансов в приложении ASP.NEТ_
Например. можно иметь одну строку соединения. используемую всеми страницами
приложения. один общий тип DataSet. используемый множеством страниц, или
любой другой фрагмент данных. доступ к которому требуется обеспечить на уровне
всего приложения. Описания основных членов типа HttpApplicationState пред­
лагаются в табл. 24.3.

Таблица 24.3. Члены типа HttpApplicationState

ЧлеН"1 Описание

AllKeys Свойство, возвращающее массив типов System.String. предотавnяющих


все имена в рамках типа Ht.tpApplicationState
Count Свойство. возвращающее значение числа объектов в типе
HttpApplicationState
Add() Метод, позволяющий добавить новую пару "имя-значение" в тип
HttpApplicationState. 3тот метод используется достаточно ред­
ко, поскольку предпочтение обычно отдается индексатору класса
HttpApplicationState
Clear() Метод, удаляющий все злементы из типа HttpApplicationState.
Функционально эквивалентен методу RemoveAll ()
Lock () 3ти два метода используются тогда, когда требуется изменить набор пере­
Unlock( ) менных приложения в реентерабельной форме

RemoveAll () Эти методы удаляют конкретный элемент типа RttpApplicationState


Rепюvе () (по имени строки или, как RemoveAt (), с помощью числового индексатора}
RemoveAt()

Для создания членов-данных. которые должны быть доступны всем актив­


ным сеансам, нужно создать множество пар имен и эначениЙ. В большинстве
случаев наиболее подходящим для этого местом является обработчик события
Appli ca tion_Start () типа, производного от HttpApplicatioD. например:

void Application_Start(Object sender, EventArgs е)


{
/ / УСТ&ИОВJ:& некоторых переменИШt ПРИЗ10жеиия.
Application ["SalesPersonOfTheMonth"] = "Chucky ";
Appli cat ion ["CurrentCarOnSale"] = "Col t";
Аррliсаl:.iопr"МоstРорulаrСоlоrОпLоt"] = "черный";

в течение всего времени существования Web-приложения (т.е. пока Web-прило­


жение не будет закрыто вручную или пока не истечет время ожидания последнего
п.ОЛЬЗ0вателя) любой пользователь (на любой странице) при необходимости может
п.олучить доступ к этим значениям. Предположим. что у вас есть страница, кото­
рая по щелчку мыши должна отображать в поле Label информацию об автомоби­
ле. предлагаемом со скидкой в данный момент.
..

prQt,eote'd vе>idЬ·t.J'1,31ю~атDi5с~Wlt~С:li6k (oJ.:rject i>end.m~, E,,",ea.·tArg.s е)


{
j j вое·аР!lЩ!liекШi 'Systeш. Objeot cne~ првобраЗ:oaamь,
1/ J Sy.atem. st~iD!J!
lbl Cur.rCa J:Or,5Gle:.1.e.»t =
{'$tringJApplicatiof! ["C1lrr.entC.ar-ОhSаlе"j ./

Как n.вe.nyчa.e СВОЙСТВ;! -llie~,,5t a te . обратJI'IТf: .!3lm.мaНЯе на 'То. Ч."Го вы долшпhJ'


преоБРi:iЗО"'ВЗ:ГЬ значение. ВРЗl3раllЩ.~мое NШОМ EttpAppliCEltiDt1S-tаr.е. в ПСЩХО"
дящи:И l'ИI1.I10ClЮ.JJ.Ь:КY ТЮI Hti:p.~'ppl i.cationStp.te можещ (1t:Щ~ржать любой тип,
должно бh:m:ъ o-чевиДiW. ч;тс; R ра:ъ!щщ Д;;JНJ'JJ:ffi\( <:ОС'IIОЯ.НИfI JIридржеJ;W.Я УЗJ1а вы мо­
жете ра.зм.tШ(а-:гь II!!:11Ъ3oВa:rf::ЛЫJКИ{~ TJ.<ЦЦi! 111 .вОQбще JПQБPJ~nШЫ .NEТ).
Дли npим€pа ИGI1O:IJ:pЗ"а-в1iнИJ:J 9ТQ.й" Щ>ЭМОЖНОСТ'И соэдай'тt' новое WclИIIшложев:ие
ASP.NEГ с нaanaни.6М. AppS't,a-t"i> r:J~ДI10Jii~1. что "ТреБУe-fСВ: IШ-д;reрживать три 1'€7
иyrф1е перемеI:lНl:i1l! npи.nhже.в:и.в ВpalIфКЩ crp(i)ГO ~ИnWdОВа,шющ· об'Ректа. с именем
СаrLоtlп:f,·о.

pl'ibJ.ic с 1$.3$ Саrtю:~:rnft;


(
publi c саrL.оtI пf"о· \з[.I1Щ 3-/ 5triI1g <;;, 's trlJ1'<,j ц;)
1
sа.l".в:Рer81'JJ,j('jf~IЪе.М\:1i) th. = з;
C'D.rJ:"eI1tСЭ1тQпs:аJ.,е ~ .С;
m~stРФlliJLtl ar~, oJ.{)rGtil,o,t. = щ;

j I ~ ;и;Q npodit<oorы ~oc·тyna .


риЫ ic s Ц .[ [19 3""] e~.Pe.t' S>f),nбfТhеМа'r,t}-,;
public эtJ!"iIЩ .'Щ.l".r eD::t С;и;Ош;а: 1 е ;
]J14bl iC' (5 trinq .таз tl'ор1.11аrСQl.о:гОnLr.it;

с Э'1:IDf:вспомагатeJrIii:iн~lМ' щrае(;'.оц вы можете снаЧз:1rа 1(IЭМeFIИТЬ обраб~ИК 'СОа


EiЫТИЯАррli g:аti.orl_Stа:rt О тщ{. H~ пр~д.m.п·<1ется 1iШже:

prt:ite-cted V'"Q:!.d Applioati611 stю: t (GВj",ct 5:eftd.er., Ei~'fЧj1:..~Ig·~ ..е 1


{
/J P~B J.lШlЬ~em.!Лtоr61 oC~~a
1/ • oh!rope ;ЦАи:inDt пpIOIO'.RJIйJr.
J!P111 iea:t,i.pfl [ "'~.&T$~ tE' lnf(Y"J =
r,e:w сатLQ .tr'пf~ . . ("'С Ьu·с:J.1", · "C o.1t~ , "чеРШ<!Й"');

iJ:з~1'еМ ШJлy:<rИ'IЪ д,OCтyn J( соотне--гeтfiующей и:вформадии еn.dМaЩЬЮ' t'.Y.Г.RpЫТЫX r:ro-


л,е-и ~ rJ оt3рС!.бо~е событий сервера. ..
ргаtе"tе;:] vo.id. bt !'fБЬEJ'oIiА.рр\tахi.aj:J1 e~_~ Н.ск. (.)bject ~e,,-der. ]t.,·€:t1t-Args a 1
{
~.,а'rLp t J t1 ,[.DflррVаrз =
.( (СаrLО tIn.fG iАррl.:l!i::З:НО)l ["Car·Sit:e.JpfQ" J);
str:!;ng, 3.ppStiJ t e =
.J'rt r. i·o'g ·. Yatrna-t ( ,,<) ±:> Предд.аг-~еМВJij мащу~'<Iа:: [О j </1 '!. > ",
:':!'ppVa:r';:; .,CD:r rel11.С"..l:О!"iБ:а 1~} i
1094 Часть V. Web-приложения и Web-сервисы XML

appState t=
stгiпg.Fогmаt("<li>Наиболее популярный цвет: rO} </ li>",
appVars.mostPopularColorOnLot);
appState +=
string.Format ("<li>Наиболее успешный продавец: {O}</li > ",
appVars.salesPersonOfTheMonth);
lblAppVariables. Text = appState;

Открыв сейчас эту страницу. вы обнаружите, что в поле типа Label страницы
отображаются строки каждой из переменных приложения.

Изменение данных состояния приложения


в ходе выполнения Web-приложения. с помощью членов типа HttpApplication-
State вы можете npотраммно модифицировать или удалить любые или даже все
члены уровня приложения. Например. чтобы удалить конкретный элемент, нужно
просто вызвать метод Remove (), Чтобы уничтожить все данные уровня приложе­
нил, вызовите RemoveAll () .

private void CleanAppData ()


{
// Удаление о'l'дenыlrоo э.nемеи'l'& по строховому имени.
Application. Remove ("SomeI temIDontNeed") ;
// Удаление :асех да.ииых npи.nо*еиия.
Application.RemoveAl1();

Чтобы изменить значение существующей переменной уровня приложеJЩ:Я, нуж­


но присвоить новое назначение соответствующему злементу данных. Предположим,
что ваша страница поддерживает еще один тип Button, который дает пользовате­
лю возможность изменить имя наиболее удачливого продавца. Обработчик собы­
тия Click в этом случае выглядит так, как и ожидается_

protected void btnSetNewSP_Click(object sender, EventArgs е)


{
// Установха "ово1'О имени продавца.
( (CarLotln Ео) Application ["CarSitelnfo" ] ) . sales PersonOfTheMonth
= txtNewSP.Text;

Запустив это Web-приложение, вы увидите, что переменная уровня приложе­


нин обновлена. К тому же, поскольку переменные приложения доступны для всех
сеансов пользователеа. если запустить три или четыре экземпляра Web-браузера,
вы увидите, что при изменении имени лучшего продавца одним экземпляром все

остальные экземпляры браузера после вторичного обращения к серверу тоже ото­


бразят новое значение.
Следует понимать, что в той ситуации. когда множество переменных уровня
приложения должно обновляться как единое целое , есть риск нарушения целост­
ности данных (поскольку теоретически возможно, что данные уровня приложения
могут измениться в момент получения к ним доступа со стороны другого пользо-
Гпав~ 24. WеЬ.ГlРИJlщке~J-ИR ASP:NEТ 2.0 1095
вате.ля). Можно.. Rоие'UIО. пойти ДJlИ1ilНЬ1М путем и BP~ бло~ировать доступ.
иецолъзун примигивы иOTOIWВ иа пространства имен $уstеm.1'.hrеа:d' iпg. :НО тип
HttpApplicationStale предлагает д;ва Мflтода, LосkЛ и Оnl ock О, }СОТОРЬ1,е авто­
матически rарантируют потоновуюбезоnaСНQСТЬ .

/ 1 Еезопаа.lWЙ;цос.~п· J!: СОО!I!Ве~~вуоцим ;цанНЪ1М I:IpИ110*е1«ИЯ,.


Applicati оп. Lo(~k( ) ;
l;ppli c:.ation ["Sai(,:sPersonQfTheMont.h·"] = "Maxine";
Appl icat ipn [ h(:u'rrentBonu.sedE;mploy..:;,e"' J =
_I>,ppl icat i·ол 1."SalesPe.rsoDOfTheMNJ tл~' J ;
Appli с;3 Ц оп. Urlloc·)z П ;

Замеlfsкие. Во МНОГОМ аналогично оnер.атору la.ck Б С#, если после В.ЫЗ0ва Loc·k () , но перед
ВЬJзt1ВОм Ппlосk () ВОЗНJJII(нет Иf!КIlЮ L lеff\<1е. БI10КИРОВf(Э буде'! <)~щ'матичеСJ(И удалена.

Обработка завершения работы WеЬ .. приложения


Тип HttpAppJ.icatiQnStat.e npеДНf!.знач.ен ДЛЯ ПОДДt;pЖКИ ЗR~ещr.й содержа­
IЦИXСЯ JI нем элементов до наС1ynлеlUШ однота из следующих соБЫТ,ИЙ; ззвершеНйЦ
cef'.tНca доступа послеДНЩ:'Q полъзоватeJ1Л узла (-вследствие прe.n:ы.щеmщ времени
ожидания ИЛЕ аакры1:'ИЯ: сеанса самим ПОill>зователем) 11/Щ црекращеJ;IИЯ работы
Wер"узш! вwшую с nOМ(I)ЩЪЮ пs. В любом случае будt:т автемдт~ески ВЪJЭЩLН ме­
'ГОД Applieatior\_Exit () объекта HttpApplication. в этом ФбрабQТ'Iине события
вы MCY~Te выполнить любоЙ неоБХоц:имъrй:вам программ;ный код "уборlU1"_

protecte,j void Арр1iсаtiОГ1_Е:ndЩЬjес't $~пd:Е-r, EVentArgs .е )


I
11 Запись '!t'ехущих .Sна1iеИНЙ .nеремеи.кwxпpиnozении
/1 в ба..зу Дa.иJQIX кци ~yдa~~ e1U. _ •

И'СХIiIД"WЙ КОД. Файлы пр~мера AppState раЗМБЩЕ!НЫ в ПОДRаталоге. СООТfjеТОТ8ующем главе 24.

Кэш прилож:ении
AsP.NET предЩlГЗет \7Щ~ од.ин. БРлее r:ибкий MC:tOA обработRи данных уровн.я
прmюжения. Вы, KOHe~o, ПОМFI.И'1'е. что ЭН:э"{ени.n оБЪeRта НttрАррli' оа:ti'О1lS,t;;зtе
остаютсЯ В паМятц ДО тех пор. покаработэе.т wе-Ъ~цриложение. Но иногда требует-
00. чтобы хавой-то фр~ент данных приложel:1ИЯ :подцерживался только в тече­
ние определенного ОГрЩlИЧе1ЩОГО периода времени. Например. вы можете 'поТре­
бова:rь. '!!ТобbI создaнн,blЙ ТJjfЩ QataSet оста:валсядействителъным ТОЛЬКО в течение
пяти ми:вут. После ::П'ОГО вы предполагаете nOJJyЧ}1ТЬ новЫЙ тип .D.ataSet. ~Iи'ты­
"Бающий возможные ДОЛЬЗQвательсЮ!(! мьд:ифииаци:и. Конечно. ВПОJlПtJ вОзможно
построить тai'УЮ структУРУ с применеnи:ем fJttpApplit:ationSta,te 11 некоторого
"ручного~ управл.ен:ия. но С'оответс-rвующая задача СИJI1>НО упрощается. если И'с­
полъзовать в:эm rфиложеnия .ASP.NEТ_
1096 Часть V. WеЬ·приложения и Web-сервисы XML

Как следует из самого названия, объект ASP.NET System.Web.Caching.Cache


(доступный С помощью свойства Context.Cache) позволяет определить объект. ко­
торый будет дocтyneH всем пользователям (всех страниц) в течение определенного
периода времени. В простейшей своей форме взаимодействие с объектами кэша
аналогично взаимодействию с типом HttpApplicationState.
1/ Добавление эnемеиori!l. а хэш.
/I Эorоor эnеиеиor *не* ycorapeeor.
Contex t . Cache["Some String ltem"] = "Это строковЬ!Й элемент";
string s = (striпg)Сопtехt.Сасhе["Sоm еSt riпgItеm"];

Замечание. Чтобы получить доступ к Ca che из


Global.asax, необходимо использовать свой­
ство Context. Но в контексте типа, производного от System.Web.UI.Page, вы можете ис­
пользовать объект Ca c he непосредственно .

Следует понимать, что в отсутствие необходимости автоматически обновлять


(или удалять) данные уровня приложения (как в нашем случае), объект Cache не
дает особых преимуществ. посколы<у тогда можно непосредственно использовать
тип HttpApplicatio nState . Но когда вы хотите. чтобы некоторые данные по про­
шествии определенного времени уничтожались (и, возможно, об этом сообщалось),
тогда типCache оказывается чрезвычайно полезным.
Класс System.Web. Cachi ng .Ca c he вне рамок индексатора типа определяет весь­
ма ограниченный набор членов. Например, метод Add () можно использовать для
добавления в кэш нового элемента, не определенного в данный момент (если ука­
занный элемент уже имеется, Add () ничего не делает). Метод Insert () также по­
мещает член в кэш. Однако если такой элемент уже определен, Insert () заменит
текущий элемент новым типом. Поскольку именно это и требуется чаще всего, мы
рассмотрим подробно только метод 1 п s е r t ().

Кэширование данных
Рассмотрим простой пример. Создайте новое Web-приложение ASP.NEТ с назва­
нием CacheState и добавьте в это приложение файл
Global. asax. Подобно перемен­
ным уровня приложения. определяемым с помощью типа HttpApplicationState.
тип Cache может содержать любой тип System.Object, а его значения часто за­
даются в обработчике событий App l ication_Start (). для нашего примера целью
является автоматическое обновление содержимого DataSet каждые 15 секунд.
Этот тип Data Set будет содержать текущий набор записей из таблицы Inventory
базы данных Cars, созданной нами при обсуждении ADO.NET С учетом этого об­
новите свой тип класса Global так, как показано ниже (сразу же после листинга
предлагается анализ этого программного кода).

<%@ .l\pplication. Language="C#" %>


<%@ Import Name s pace = "S ystem.Dat a .Sq l Cli ent" %>
< %@ Import Namespace = "System.Data" %>

<script r una t= "s e r ve r" >


/ / Опредenаиие сori!l.тичесхоI'О чnена-переменной типа Cache.
static Cache theCache;
Глава 24, Wеь-приnожеНИIl ASP.NE'1 2.1> 1'097
void A.ppli.catlDI1_Starl1..(Object 8.ende.J;, Е\lеn.t.J~rgз е)
{
11 ПРИСВАИ8&.t!J1ei!lit.ачeию;r с.фаorичеСЕDЙ перемеиноJt 'thetache' ,
theCac.;he = СОГltгхt. СасЬ:е;
11 При sa.uycxe 'npкnо_еJIИII tQG!~R' ~ 8arIИС-
1/ .'1'абnиЦw Inventory баз.» ДII:НJWX СИ'в.
~qlсQnпесti.оп сп = Dew Sql:ConnectiQ.11
("'data S"Dlux·oe=lo.calh·o,s·t ;'iлi tial cata lаg=,Саr:э; ЫБ:ет id = 1 '3д 1 ; pwd= ' "11 I;
~Чl Dаtа1Иарtе!" dAd~pt ~
T,ew SqlOataAdapte r (" Se-lect .. F:r9Щ Iлvеn't,Qrу", сп);
D",ta5-e-::' the-Cars = new ~ataSet () i
dAdapt. F i 11 (tЛеСаУб, "1.rнт€пt 6ry" );

1/ СОХР<щеИИе DataSet в х!tШEl,


theCa'C1"Je. In-ser·t. ("AppDataSet ",
thI::Cars:, П1.1l1 J
J1)q:te'Iilrt·e .,}low •.AddSecon.d s (1-5) ,
Cache.No$1idingE~pj.rзtion,
ОаС!IзItэ,щРr i ori.t у •.Оеfal11 t,
D€W С" cheI temRe.mo·vedCallЬac:k (Upda teCa.rInv€rltory 1. ) ;

f 1 Ц~ЛЬ ;IIe;nel"a~i! C~cheItemRemovedCallba.c~.


Bt<Jtic yoicl UpdateCarInventorY(.5tring key. obj.ect l.t em,
СасЬе 1 tеrttRemоvеdЕЕ/аэ>(,!'J Х'еазО!J I

11 Заао,nвекие DataSet.
SqlСбniN!сtiбn ·ел =' n~"" Sq1СогшесtiQ!1
("da t IЭs O\:Jr'ce=loc,a,lhogt: initia1 catalgg=Cq.rs: ив е !' id ='sa' ;:p~д=" ");
Sql DataA.,japter dAdдipt =
nе .... 9qlDa ta.AaapteJ: (" Select '" ~'rom rn veI).t bry'·. сп j ;
Da'ta $e.t :t.rl.eCa:r:; = new DataSet () 'i
dAda p t. F'ill (t.hеСа,rэ' , т. Inven'u;;,:y"') ;

If C~~aa"lpfe а хэше.,
t.heCa lZ.he .. Iпsеrt: {".AppD,H.aSet ".
t-hеСаrs' , Г1\111.,
DаtеТimе , .Nо·w . .Ad!:JSесоndз (15) г
Ca,C[I·e. N.05>1 idi.ngE·X piration,
CachaItemPriority.nefauJt,
1'J!S!'w Ca.cheltemRemovedCallbcaek (updateCar ln'lentoxy)) ;

</script>
С:наЧ2lЛа 'обратите внимание на то. что тип Gl"ba l onp:eдмяe't статическ-ую П~­
ременную типа CO!ctl€. Причина в том, Ч,тоодредеЛiIетCJi та.н.же С'те.тичес.кая ФУНК­
ЦИЯ (i]pdateCa rlnventory () j. которой тpe6yeтc~ дaeтyn 1с Са с:Ье (нanОМИйI.1. что
Сl'<\т:и,:ческие Чllены :не имеют AocTyna к насл~дуе~IМ ~Haм. поэтому в да:ннрм
случ;ае ВЫ не сможете использовать свойство 'Соnt 'ея t).
Внутри обработчИRa событий AppliQ.Q:ti,:;n_Start (/ заш:mнле1'СН ТИТI Il8.ta.Set.
RОТОРЫЙ' затем помещается в кэш приложенил. ДОЮ1WО БЬiТЬ пена. что метод.
1
I
1098 Часть V, Web-приложения и Web-сервисы XML

Сопtехt .Cach e . Irlsert () перегружен. Ниже объясняются значения каждого из


параметров этого метода.

// Сохранение в хэме.
tfJeCache. 1 nsert (" AppDa taSet" , 1 / имя дли иден'J!ИфИICЗЦИИ элемеН'1'i!I.
theCars, 1/ Об<ихт ДnИ отnpuJCИ В КЭШ.
n и 11 , /1 Зависимос'J!И ДnИ об..еJCТi!I.
DateTime.Now.AddSecollds (15), // Длительность npеб-ываиии вхэше.
Cache. NoS1idingExpi ratioll, 1/ Фиксированное или CJConьзищее время.
CacheltemPriority.Default, 1/ Приоритет элемента .
1/ Деле~i!lТ дnи соБЫ'1'ИR СасhеltemRешоvе.
new CacheltemRemovedCallback(UpdateCarlnve ntory));
Первые два параметра просто формируют пару "имя-значение" злемента. Третий
параметр позволяет определить тип CacheDependency (который в данном случае
будет равен null, поскольку у вас в кэше нет элементов, зависЯIЦИХ от DataSet).

Замечание. Возможность определить тип CacheDependency ДОВОЛЬНО привлекательна .


Например, можно указать зависимость между некоторым ЧJlеном и внеШНIiIМ файлом, и если
содержимое файла изменится. соответствующий тип автоматически обновится, Все подроб­
HOCТIiI можно наЙТIiI в документации . NEТ Framework 2.0.

Следующие три параметра используются для определения приоритета элемента


и времени. в течение которого элементу позволяется оставаться в кэше приложения.

Здесь указьmается доступное только для чтения поле Cache. NоSlidiпgЕхрirаtiол.


которое yкa3blBaeт, что указанный промежуток времени (15 секунд) является абсо­
лютным. Наконец, и это самое важное для данного примера. создается тип деле­
гата CacheltemRemovedCaJ.lback. IШТОРОМУ передается имя метода, ВQIзываемого
при очистке DataSet. Как следует из структуры метода UpdateCaI;ln ven tory().
делегат CacheltemRemovedCallback может вызвать только методы со следующей
сиrнатуроЙ.

st atic void UpdateC'arInventory (st ring key, object i tem,


Cachelte~~emovedReason reason)
{ ... }

Теперь при запуске приложения тип DataSet будет заполнен и помещен в кэш.
Каждые ]5 секунд DataSet будет очищаться. обновляться и снова помещаться в
н:эш. Чтобы увидеть результат этих действий. мы должны создать тип Pag e. кото­
рый будет поаволять некоторую степень взаимодействия с пользователем.

Изменение файла *.aspx


Обновите пользовательский интерфейс исходного файла *. аэрх так, r{aк пока­
зана на рис. 24.4.
В обработчике события Load страницы настройте GridView на отображение со­
держимого помещенноro в кэш типа DataSet при первом обрcuцении пользователя
к странице.

protected void Page_Load(object sелd еr, EventArgs е)


{
i f (! IsPostBack)
Гла!!а 24. Web-приложеНИjj ASP.NEТ 2.0 1099

c,arsGtidView. DataSource .. (Dаtдsе.t) С.а.сУн;; ["A,ppDa.ta:Set "] ;


с атвGridViеи , Da t aIlind () ;
1

tтраюЩа добаВ:flени:н НОВо!1 ~

i!.IoMcp,r

~eTr

,
1" I 1, , 1 ' , ,' , , ' ,), ,"

аЬс ~bo

Рис.24,4. ГрафИ4еСlG1Й Иlf'!'eрфейс ПОЛЬЗ(ЩQ:теЛА ЛЛI1 ПрИЛQ~ения с кэшщюванием

В обработчике соБЫ'tияСliсJ<. JШоПЮt ДобавИ'iЪ эту машину вставьте, новую <!а­


rIИСЪ в базу Дa1tНЫX Сагэ. nсполъзуя. для Э'ГО1'(> объект ADO.NEГ SqlСоmmащ:i, IJосл~
.добaвлtШИЯ зanиСИВЫ30ВИ'I'е ВСПQМогатедьную фушЩию F-e f reshGri d О. к!;Уropая
обнu.вит интерфейс с ПОМОIдЫo. типа $qlDi:J,taReade!: (поэтому не забудьТе УШl3.атъ
using для пространства имен S'уstещ. Data .БчlС} ie.nt). Вот как ДОЛЖН;Ы выrлнд~ть
соотв~тствующве методы.

PI:otE:t:ted' vc>id ЬtпДddi;:а]'_ CJ.:i<J k ('obj e.ct sende:r, E",e,n 'tArgs е')
r
1/ Обиos.оиие "a~ :tn\!'entory
1/ и Jlызаa
Ref'reshGrid О .
SqlCo'H fI,ection сп = new SqiCt:lnnectlOlJ () .-
СП • Connect iот,stтiцg =
'tT)ser .lD=sa; Pwd=; Il'litial 'Catalog=Cars i ,Da ta зоuгое= (10c,al) ";
.сп
_Ореп (). ;
string s~l;
SqlСОШll'аnd сшd;
1100 Часть V. Web-приложения и Web-сервисы XML 1
/ / ВС'1'&IIха Ho.oro Car.
sql = string.Format
("INSERT INTO Iпvепtоrу(саrID, Make, Color, PetName) VALUES" +
"('jQ)', 'jl)', '(2)', '{З}')",
txtCarID.Text, txtCarMake.Text,
txtCarColor.Text, txtCarPet Name.Text);
cmd = new SqlCommand(sql , сп);
cmd.ExecuteNonQuery();
сп. С1оэе () ;
RefreshGrid() ;

private void RefreshGrid()


j
/ / Заполнение табnицы.
SqlConnection сп = new Sql Connecti o n();
cn.ConnectionStri ng =
"User ID=sa;Pwd=;Initial Catalog=Cars;Data Source=(local)";
сп. Орen () ;
SqlCommand cmd = new SqlCommand("Select * from Inventory", сп);
carsGr:idView.DataS ou r c e = cmd.Exe c uteReader();
саrsGridViе w .DаtаВiпd();
c n.Close() ;

Теперь, чтобы проверить использование кэша, запустите два экземпляра ва­


шего Web-браузера и перейдите к этой странице *. аэрх. Вы должны увидеть, что
оба типа DataGrid отображают одинаковую информацию . В окне одного из экзем­
пляров браузера добавьте новую машину. Очевидно, что в результате вы увидите
обновленные данные GridView в окне браузера, который инициировал обращение
к источнику данных.

Во втором зкземпляре браузера ще.ruшите на кношее Обновить. Вы не увидите


новый элемент. поскольку обработчик события Page Load читает данные непосред­
ственно из кэша. (Если же вы увидели новые данные, это значит, что уже истекли
15 секунд. Либо печатайте быстрее, либо увеличьте время. в течение которого тип
DataSet должен оставаться в кэше.) Подождите несколько секунд и снова щелкни­
те на кнопке Обновить второго зкземпляра браузера. Теперь вы доJLЖНЫ увидеть
новые данные, поскольку время пребывания DataSet в кэше истекло, и целевой
метод делегата CacheltemRemoved.Ca11back автоматически обновил тип DataSet.
помещенный в кэш.
как видите. главное преимущество типа Сас Ь е заключается в том. что вы полу­
чаете возможность ответить на удаление члена. В этом примере вы, конечно, може­
те избежать использования типа СасЬе путем чтения данных в обработчике Page_
Load () непосредственно из базы данных Cars. Однако теперь вам должно быть ясно,
что кзш позволяет автоматически обновлять данные с помощью делегатов .NE'f.

Замечание. 8 отличие от типа HttpApplicationState, класс Cache не ПО,DДерживает методы


Lock() и Unlock(). Так что при необходимости обновить связанные злементы вам придется
использовать типы из пространства имен System. Thread.ing или ключевое слово lock С#.

Исходный код. Файлы примера CacheState размещены в подкаталоге, соответствующем главе 24.
Глава 24,'Web-(1риложеНИIl A:'SP.N!;T 2.Р 1101

Обработка сеа_нсовых данных


llОГQВОIЩВ (} данных уровня npиложениа. давайте перейдеМ к обсуждению ДЩ!­
ff.hIJC. ~o;здaвaeмыx на уровне пользователя. Ках уЖе упоминапоеь, -сеацс ца C~OM
дeд~ n:реДQТавляет собой процесс вэаи.модеИствИ.F1 ПОЛЬ30В!rrеля с Web-ПРИJlОЖ~­
ем. преДСТ~jJенныйnrnом Нttр5еэsiоnStаtе. ДJm ПQмер:J'f\."НQ информ~ сеанса
.конкрет;ного пользователя объект Ht.tpApplication и щобьхе другие типыSуstem.
Web. Ul.P.age МОгуТ ИСIiольз.оватъ доступ J< СJjОЙСТВУ sess i оп. Класс.ич~с~ ДРШ4е­
ром не0бходим:Ьсти .rюдцерЖRИ полъэователъских дaюn.ц является корЗJlЩ!. по~-упа­
теля: при noднлю'Чеюrи Дес.IП"КО13 посетителей К странице ИнТернет-мarазшщ ДЛЯ
~~oгo посетителя должен поддерживаться ую.щалJ;,щ,rЙ qшсок товаро!;!. которые
етот цщ;етителъ собрался купить .
.при регистрации новогО ИОJlbэователя в Web-приложе.flИИ среда ВbIIд)лненин
.NEТ автоматически назначит пользователю уникальный ИДeRтифюm';J."DР сеанса.
~щrолъэуеМЫЙ для идентифишщюr ДЮ-IНоrо полызовате-'Зя. С каждым идентифика~
тором сеанса ассоциируется ПОЛЬЗQваТeJ!ЪСюШ ЭRземщrв:р тица Rtt:pSessiQГaSt.ate.
1J'оторый БУДет содержать данные СОQтветс,ВУIOЩето Пощ>зоватeщr. Тt:хноло:r:и.н да­
бавления и ЧТIШldВ сеЮ-lСОВЫХ данных сИ1tТaRСWSески иденг:ична работе ·с данньrми
црил.ожени.н~ l-ШnРИМер:

11 Добавn.кuе/тw:~е се~совой перемвииой д.пll ~IUlRОl'О поm.зова'l'е'nll.


Sesisi 'Эг! (" De~i redcarColor" J =
"·зе.17€НiЪ1Й";
s!:rir\g =1.0 !: '= (sLrir<g) S'e&5~0l1 ["De'2iredCa~Color"J;

ПРОИ8ВoдJ'lьn1 от HttpApplica:tion um ttозвОЛНет выполнить IIерехва:г событиjj


начада и зэвершенил сеанса с помощью обрабDТЧИЖОВ событий Sessip!LSt?\rt ()
и Sesзiоn_Еnd (). в пределах Б:езsiол_Start (} вы можете создать .1IЮбьJe :щемен­
ты ДIO-I.'Щ>IX Щ:ЩЬ?lователя.а 13 Вез!; iG'П_Бnct О МОЯiliIO ВЫполнитр. .rpoбые Д~СТВИJi\
веобх.од1<IМЫ'fJ при аавершеюm сеанса ПОЛЬ3013а:rеля.

<%@ Applic.ation LCщguаgе="С1/-;' ?~ >


<ЗG.сlрt 1':1.1.1101 t_=" эеtvеf">

vcJid SеS5iол_Stаrt (Object &епdег, E'i'entArgs .е)

'УО idSessi:Ori_ End tObj ect зеn.Цбr, Еvе.пtАrg.s. е)

<J.scIipt>
Подобно типу HttpApplir:ationState. тИП Ht,tpEes$ionSta-t.е может оодержать
люБQЙ ТИП. ПРОИЗВОДIiЫЙ от System.Object. ВRJmi)Ч'ая пользовательские Шlассы.
Предположим, например. что у нас есть ROBIiJC Web-приложение (Sessi"-'I'lЕ;tаtе]. RQ-
TDpoe определяет ВСПОМОN:rедМ-lЬ~Й I<ЛaСС с имвдем UserSboppingCa.r:t..

p'JЫic c:lass' userSh"ppii1GJCar t


{
p.ublic stringdesiredCa:r.;
RubliG striI'lg des;J.redCarCa.l<Jr;
public float down.Paym.ent;
publie' Ьооl iзLе'а15iлg:
puЬHc DateTirne dateOfPickUp.;
1102 Часть v. Web-приложеНИR и Web-сервисы XML
public override string TaString()
{
return string.Farmat
("Машина: {l}<br>$ кредит: {2j<br>" +
{О}<Ьr>Цвет:
"Аренда; {Зj<Ьr>Доставка:
(4j",
desiredCar, desiredCarCalar, dawnPayment, isLеаsiпg,
dateOfPickUp.TaShartDateString()) ;

в обработчике событий Sessian _ Start () можно назначить каждому пользо­


вателю свой экземпляр класса UserShappingCart.

v a id Sеssiоп_Stаrt (Object sender, EventArgs е)


(
sеssi ал ["UserShappingCartlnfa п j
= new UserShoppingCart();

При про смотре ваших Web-страниц пользователем вы можете взять экземпляр


UserShoppingCart и заполнить его поля данными соответствующего пользователя.
Предположим. что у вас есть простая страница *.aspx с н:абором злементов ввода.
соответствующих каждому полю типа UserShoppingCart. и кнопкой (Buttan). ис­
пользуемой ДЛЯ установки введеюiых значений (рис. 24.5).

tеансовые состоЯния

fJapx~! f'

itре~иг.r

:t- .АревдВ!
~aтa достamc.и: I

Рис. 24.5. Графический интерфейс пользователя для ПРИЛDжеliИR с сеаНСDВЫМИ данными


Гпава 24. Web-Л'Р\llilоже~IИR ASP. NП 2.0 1103
Серверны:й обработчик события click Д~CTByeT весьма np.ямоли.иеiШо (счи­
тывает знаqения элементов Te «.tSQx и отображает поступающие значенй:я в поле
типа LabeI}.
p r ot i?ct e ,j " 1Ji d bt..nS'ubmi t._Cl i·c;:.k (object s€Ader , E,'e Pt Ar g s е)
(
j /Ус'ПИОвха npеферes:цr.Щ ~ • .lCyщerо ПОJlЬЗОВi!l.'1'e.пJI.
US~IShoppingCa ~ t u =
<;iJserSnG.ppingCar't) Se S'$lorl ["U s E-rSh оррi o gC<ir t Inf(') "] ;
u,.;J.a teOf'P i ck1Jp = rnУGаl е лdar.S еl есtredD аtе ;
1:1 • d'e 5i redCa.:r: ~ txt"CarMake. Text;
u .de s ired'CarCo lo r = tj1t Ca rCble>r .. Text;
u.c!own~Pa y.rne f) t '= fl o at. Parse (tхtDоwпР.ауmел t. 1'е х t );
:.J.is l!ea.si.ng = СhklsLе.аэ l пg.Сhес kеd;
JJ:Й1.1S е rInfО,Техt = Ц,'rоЭtring();

З е-s;е i о n ["Usе:r: shорр i!lg Сar,tlЛ'f О "] = и;

в Sessiol':J_ Ещ:l () вы МОЖете. сгапример. сохранить значещlЯ полей Usе rSlюррiп,щr­


С а r t В базе· данных ИЛи 'ВЪШ0ЛНИТЬ blюще-то 1oJ.llble ~:Dl(..'ТВ'f,l~ . Тан или иначе., если
вы запустите два или три t)НЗebll1Л}JР<J. своего брау~ера. вы должны у,видеть, ""'О
каждый ПОЛЬ30Вa!rеЛь Mam.eT создать СБОЮ корз;ину ПЩ'УРзтеля.('вязавную с его
yнишmьным экзеМJ1.JIЯРОМ EttpSessionSt a t e.

Дополнительные члены HttpSessionS·tate


:Кроме индексатора Т'иnа. юrас(" НttрS е э slОIls t а,tе (}предедяет ряд других инте­
ресных членов. Во-первых. СВОЙСТВОS,-"ss l орID возврщццет УПИШlЛыrый :иденти­
фm~тйр теъущеtо поЛЬЗоваТеля .

l bi U..!ie :rID . TeX't = s .t r i..лg . ]i'o цoat ("'3 1+9 ',.ени.е .в.аше го l D : I O}",
3е ;s: &i О D . .5 еsз i.оо IЩ;

Методы Re'mov,e () и
Remove All ~ 1 можно ИСПОЛI:,3Qвать д!.Щ удален~.~ элементов
из экземпляра Ht.tpSes.sicmState пОЛЬЗОВаrrеля.
ffi.€.·ss i o1'•. Rerno-v'e [" н е.ко 'Горы:еУ'жеНен~жныеЗЛ'ем€'нты" ) ;

1).ш EHpSe s s ioJ;lst.ate опреде;п:яет Также набор членов, управляющих значени­


ями времени ОЖИЛ:aJ-Ц1.Я ДЛЯ текуще:го сеааса. Снова IIодч~ркнем. '-пО' по УМOJ)'~lа­
ИИЮ · Iсаmдому nОЛЬЗЩJатедю п{)'а~ОJmет('я; 20 МИJIy'I' беЗ,nеi;l.ствил ДО того. как об'l>­
ею HttpSessi onstate буде'!' уничтожеJi . Пр:;этому если llI:i.iThзовате.щ; вой;1:tет в вarnе
Web-ПРШIOЖeJФ,е (и Щ)IЛ)ТЧИт В результате этого свое уникальное значение иденти­
фикатора c~ca1. но Не· 6удетобр~аться к узлу в те"Шние 20 минут. среда выnол­
несlИЯ ·'реI1lйТ". 1}То ПOJlbЭоватem, fio,m.ше не инт~ресу:етС'.}J узлом и yt-ш-tIТQжает все
сеanсовые дан:дые 'Э'FОГQ ПОДР30вателя. вы имеете вОЗМОЖНОсть изменить это прfl­
пятое по умолчанию 2O-МинyтRQе З1Щ.чение ДЛЯ .в;ащцorо по.лъзоваТ(tJiЯ в отдельно­
ст. используя свойство Т1 щ@ щtt. Чаще В<::СТ'() Дi1Я 'ta.Itoro л.';JменеНИЯ используется
KOНТ~CТ меТCiда 'GlоЬаl ,.Sе.s'э i С !1 _ S'tart () .

protect ~d voi:d S еЗ'S .i О!J_stа:rt ((')b j ect s e Dd er, Еvеn tЛrg~ е)


(
1104 Часть У. WеЬ·приложения и WеЬ·сервисы XML

// ПО~ЗО8а~~ разрешае~с. 5 ИИИ~ бездеЙс~.ии.


Session.Timeout = 5;
Session["UserShoppingCartlnfo "] = new UserShoppingcart();

Замечание. Чтобы не менять значение Timeout каждого пользователя, вы можете изменить при­
нятое по УМDлча~IИЮ 20-минутное значение для всех пользователей сразу с помощью атрибута
Timeout элемента <вessionState> в файле Web.config (структура и возможности это·
го файла будут рассмотрены в конце главы).

Преимущества ИСДОЛЬЗ0вания свойства Timeout заключается в том, что вы


можете назначить свои значения времени ожидания для каждого пользователя

в отдельности. Например. представьте себе, что ваше WеЬ-приложение позволяет


пользователям заплатить за некоторый уровень членства. Вы можете потребовать.
чтобы "золотым" членам устанавливалось время ожидания. равное одному часу. а
• деревянным" - только 30 секунд. Такая 'возможность порождает следующий во­
прос: как реализовать запоминание соответствующей информации пользователя
(например. текущий уровень его членства) между обращениями к Web-странице?
Одной из возможностей является использование типа HttpCookie.

ИСХОДНl,IЙ КОД. Файлы примера SessionState размещены в подкаталоге, соответствующем главе 24.

Данные cookie
Последней из рассмотренных здесь технологий управления ДaJЦiъrми состояния
будет использование данных cookie. которые часто имеют вид обычных текстовых
файлов (или наборов файлов). сохраняемых на машине пользователя. При реги­
страции пользователя данного узла браузер проверяет, есть ли на Manrnнe пользо­
вателя файл cook.ie для данного URL. и если такой файл обнаруживается. данные
файла присоеДШJЮОТСЯ к НТТ'Р-запросу.
Получающая запрос Web-страница на сервере может прочитать данные cookie.
чтобы использовать их при создaнщi графического интерфейса, учитьmающего теку­
щие предпочтения пользователя. Уверен. при посещении своих любимых Web-узлов
вы эам ечали , что узел как будто знает, какого сорта содержимое вы хотите видеть.
Например, при регистрации на странице http://www .minJstryofsound. сот мне
автоматически пр~ъявляется содержимое. отвечающее моим музыкальным вкусам.

Причиной (отчасти) mшяетсл то, что на моем компьютере бьщи coxpaнeНbI данные
cookie с ШJформацией о том типе музыки, которую я предпочитаю слушать.
Точное место хранения файлов cookie зависит от используемото вами браузе ­
ра. При использовании Мicrosoft lnternet Explorer файлы cookie по умолчанию со­
храняются в папке C : \ Documents and Sеttiпgs\<имяпользо:sателя>\Сооkiеs
(рис. 24.6).
Содержимое конкретного файла cookie, очевидно. будет зависеть от URL. но это,
в конечном счете. обычные текстовые файлы . Поэтому вариант использования
данных cookie нельзя считать удачным для передачи конфиденциальной информа­
ции о текущем пользователе (например. номера кредитной карточки, пароля или
1
Глава 24. Wеь·приnожеtfия ASP.NEТ 2,0 1105
дрyrой анадогичдой информЦДИ:И). Даже если данные бу.lJYТ зашифро.ваны. какой­
Шlбу.nъ хакер Mon~tT ра('ПШфроватъ сооrnеТС'ГDующие Эl'Iaченин и использоrщ:rь юс
в злонамеренных целнх, Но. та:н mm иначе, фаЙлы cookie :играют B3iK!iY}O роль в
разрабо:wе, Web-прилощециЙ. поэтому нам ваЖ1:Iо выяенитъ. Ii'.ai( эта специфиче­
ская ТeJUIОЛQГия. ynравJJЩЩJl ,состоянием отражается в ASRNEт.

si- l.ookil>5 г.:-11't1II~1


ffl Ed!: V~ F""""te., ТooI> НOIO;>

"~~ вacli.
'W - , , 1>;,
~!1
.1
1..
~) SeэIW
.г'
I i r--olders ?!Г.
~

1>t.\Jм",>, fCsG~\PQO~~~:JNTmTEo.;\p;'~;S -
- FjI~<1IIdFlildeJl;,sk~ ~Ii_ 1c :a~---EI - -ti
.::)_ ~ ~,,_~.. f",~r
d
-
~~t<*fe.·'h.
' J:МIXeb

~ ~1'!:h/9 tЩ,

' Р.ис.:24,.6. данные oooli;ie. сохраненные ' БРa)lзером Мicrоsоft tпteгnщ E)(plurer

Создание данных cookie


Во-первых. важно nO~. что в ASP.NET дaшrые cookie MOryr быть леР1'NfaНеат­
ВЪ1МИ ИЛИ временными . DеjJМQ.1'l€J-IЛ1ные данные cQokle обычно ра.ссма:ГРJ!ЦJaIотея
n смысле кщ:н;сического опреде,Лен:ия дaн:вьr.x co:okie. т.е. как МНDЖество пар имен
и ООВЧеЯИЙ. фи,эически co;xpCJНeнныx на жеспюм ДИСI\'е nОЛь;зоваТ&JIЯi. ВреМЩIJ-(Ы.r?
Д<il:ПfЫе cOp'IQe (:которые также ваэываютсн сеансовыми дзmn;IМИ oook1e) содер:;ю;lТ
те ~ д,arllще. что и перманeнтнъre cookie. но в этом случае пары ИМСЕ и зна чений
не СОХР!iННЮТСЯ: на JIoЩПlИRе пол:ъзовате,ля:, а сущес'Гвуют только в пределах ззто­

. ловка НтгР-сообщения. При отш[(очении ПCr1IЪЗ0Ватм.Ii от в.ашеl'О узла вее щщннr€


CeaIICOBblX cookie уничтожаются.

Замечание. БОЛЬJШ.fflСТВО БРl!у~еров I1Qддержив.ает СТрОkИ cookfe ДЛИНОЙ ДО 4096 б'аYtт. ИЗ-3З Т8-
КОГО ограlfИ'lения отроки сооlt.iе лучше всего ИСПОЛЫЭО~ТЬ ДЛЯ :за!Т"1МИНэ.ttия небольших фраг­
MeNTOB данны •• например идеНnjфlllка~юров пользователей, С ПQl)ЛОЩЬЮ КОТОРЫ1( затем МОЖ~IO
ПОIlУ'IИТЬ допdЛн~тельные дaWHble 143 базы Aa~IHbIX,

1)m Sуэtеm.Wе.\),НttРС;:Q,оki-е я:в-шrетс.я классом. предс'iавляющнм серве'рНУЮ


часть дашIЬ'1Х C06kie (nерманентн:ых.или IфемеНШ,;IXj. для СО8ДaIOU1 новых дaHRЫX
cookie :щ::полъэуетсн СВРЙС'ГВо RеSРОПS8' .Сооki,еs. После вставки HOBOI' O объе1iта
IdttpCookie ~o в.н:утренн:roю коЛлекцию соcrтвеТCi'ВУЮЩйе пэры имен и значений,
uanравлтгпс,я обрр.тно брауверув UPf>.дe.nах загОЛОВка НТТР.
Для прим~ра использования данных cookie создайте новое Web-ПРИ:ЛQ1R'ение
ASР.NЦ (Co'OJ<:ieStp.t, ~li.pp) с llольэОватеm.с1СИМ интерфейсом. из@бра~ен;ным ца
рис. 24.7.
В обрабОТч:IЩе соБЪтtин Cl ick trnопки СО8Дайте :новый.UП J-JttрСооk::Lе и 1iCТ8ВfЩ.!
его В IWллеlЩ]'Щl Cookie. досry:пнyю, с ПОМОЩЬЮ свойства ,Ht tpReqHest.COGKies.
1106 Часть V. Web-приложения и Web-сервисы XML

Рис. 24.7. Пользовательский интерфейс приложения CookieStateApp

Следует знать о том. что данные cook:ie не будут сохраняться на жестком диске
пользователя, если вы не укажете явно дату истечения срока действия этих дан­
ных. используя свойство HttpCookie.Expires. Поэтому в следующем фрагменте
программного кода создаются временные данные cook:ie. I<oтopble будУТ уничтоже­
ны. когда пользователь прекратит работу браузера.

protected void btnlnsertCookie_Click (object sender, EventArgs е)


(
// Создание новых (_ремениwx) данных cookie.
HttpCookie theCoo kie =
new HttpCookie(txtCoo kieName.Text,
txtCookieValue.Text) ;
Respon se.Cookies.Add(theCoo kie) ;

Однако следующий фрагмент программного кода генерирует перманентные


данные cook:ie, срок действия которых истечет 24 марта 2009 года.
private void btnlnsertCookie_Click(object s e nder, EventArgs е)
(
// Совдание новых (периаиен'l'JВIX) ДiUlВlOlX cookie.
HttpCoo kie theCoo kie =
new HttpCookie(txtC o okieName.Text,
txtCo o kieValue . Text);
theCookie.Exp ires = Dаt е Тimе.Раrsе("О З/ 24 / 2009");
Resp onse_C oo kies.Add(th e Coo kie) ;

3с:щустив это приложение и добавив некоторые данные cook:ie, вы сможете про­


верить. что браузер автоматически сохранит эти данные на диске. Открыв соответ­
ствующий текстовый файл. вы увидите нечто. подобное покаЗaRНому на рис. 24.8 .

. . . ~ ~T ' _ с:...-.
лобиwыйнiIПI<ТOК D3 еЛеный- ча.йо 1 ос а 1 ho,t-/iiiО24 о ]0·5'33181.44 П29993 98БО 34]-89557ZBIi2 -9794 207 D· iI- -

Рис. 24.8. Перманентные данные cookie


Глава 24, Web-ПРИЗlcrжеfН:lЯ ASP,N'ET 2.0 1107

Чтение поступающих данных сооюе


:Нanoмним. ЧТО именно брауэер от~чает за ВОЭМQЖЩ>СТЬ ДОGтyDa :к перМ8неит­
НblМ данным cookie ВО время обращеFJ:lШ к ранее ПОСeJiЦэвmеися странице. Длn вза­
имодеЙствия с поступаюIЦИМй данными cookle в .ASP.NEТ предусмотрена свойство
НttpRечuеst.Сооkiеs. Например. если IllilХQТи;Fе обновить ПОЛh30вателъсКifЙ ИН­
терфейс Bamero ПРЙJfОЖttНИЯ: е тем. чтобы молrnо .fll:,ыю отобразить текущие . даНI-Iblе
cookIe с помощью элемента ynpавленJ;rЯ Вuttщ"., вы можете реaDИЗ0вать цикл по ·
всем пара.~ -имен и анач:еШfЙ. ар~ставля,!l сощве"l1СТВyto1I!yЮ информаЦиЮ в поле
элемента Label.
prQtec:ted v.oi cl btnSrn-оw6ооkiеs_Сliсk ('.object Э<1лdеr, ЕVE"iлtАrgs е)
1
зtring coo]cieData = " " ;
fore'ach Cstring s in Request .:Coakies)
(
cQokieJ;)ata +=
string.Е'е:r:mаt("<li><Ь>ИмЕ</Ь > : jO}, <Ь>Значение,,/Ь>: -[l)</Н:,:." ,
~( Hequ~st. Соо)::} ез, J э]' .. VэJ.1.1е) ;

lblCOf.>kieData. Text = cookleData;

Если теперь· запустить црилщке,ние и щелкнуть на новой кнопке. ВЫ УВИди'rе.


что ДiШнЫе eookie вашим брау$ером действителыю посылаются (рис. 24.9).

un ~~ fItIA ~• •_ C8I:iМ:
, ~

Оf1юМ ·0' [3~. fQ: p~~ ~r~


.tщ>ocl41 ~;IJ~ht;j06e(C~~ ~_-IiD~А ~~ "1'

Данные cookie

г. - . _ -~ ~--- - ~

З'начени ~ _Cc('>Qk!ejA !

. _~- ---- ------~._- -----._-~-- --- -

• Ими: Дюбю.п.!:й1lаП8:rоК, Эira"lевие:с3eUllыi'гч:m


• Ими: Проверка. Зипвние' А

Рис. 24.9. np.oCMOTP дaHНWX cQokiE)


1108 Часть V. Web-приложения и Web-сервисы XML

к этому моменту мы с вами рассмотрели множество способов запоминания


информации о пользователлх. Вы видели, что данные состояния представлений
и данные приложения . кэта, сеанса и cookie обрабатываются примерно одинако­
во (с помощью индексатора :класса). Вы также видели. что тип HttpApplicati o n
часто используется для перехвата и обработки событий. происходлщих в течение
всего времени сутцествования Web-npиложения. Следующей нашей задачей явля­
ется выяснение роли файла Web.config.

Исходный код. Файлы npимера CookieStateApp размещены в подкаталоге. соответствующем главе 24.

Настройка WеЬ-приложения ASP.NET


с помощью Web.config
При изучении компоновочных блоков .NEТ мы с вами выяснили. что приложе­
ния клиента могут использоватЬ ХМL-файл конфигурации. содержащий инструк­
ции CLR о том, как обрабатывать связанные запросы. где искать необходимые
компоновочные блокц и что еще нужно учесть в среде выполнения. То же можно
сказать и в случае Web-приложений ASP.NEТ. но в ДaIOIом случае файлы конфигу­
рации (впервые упомянутые в главе 23) всегда называются Web.co nfig (в отличие
от файлов конфигурации *.ехе, имена которых зависят от имен соответствующих
вьmолняемых файлов клиента).
При добавлении файла Web.config к файлам узла с помощью выбора WebSiteQ
Add New Item из меню создаваемая по умолчанию структура выrлядит примерно
так. как показано ниже (чтобы не загромождать структуру. комментарии здесь
были ИСКJlЮчены).

<?xml version="l.O"?>
<соп figuration xmlns=''http://schemas.microso ft.coml .Net.Confi gura tion / v2 . О" >
<appSettings/>
<c onne c tionStrings />
<sy stem.web>
<compi lation debug="false"/ >
<authe ntication mode="Windows"/>
</s y stem.web>
</co nfiguration>
Подобно любому файлу *. соп f ig , в файле Web. c o nf ig определяется корневой
элемент < configuration>. В его контекст вкладывается элемент <system.web>.
который может содержать множество дочерних элементов. с помощью которых
осутцествляется управление поведением Web-приложения в среде выполнения.
BASP.NEТ файл Web. c onfig можно модифицировать с помощью любого текстово­
го редактора. Некоторые элементы. которым позволено присутствовать в файле
Web.config. описаны в табл. 24.4.

Замечание. Чтобы выяснить подробности формата файла Web.config, выполните поиск разде­
лов документации ,NET Framework 2.0 SDK, соответствующих ключу поиска "ASP.NEТ Settings
Schema".

J
ГлаВfl 24. We-Ь'nРI1J10жения ASP,NEТ 2.0 1109
Табnица24.4. ПодборкfJ. элемеl'п:оа файла WеЬ.солflg

Э:пеllенl' Оомсанме

<арр&iе'ttiпgs> Исполы:!уется для .создания Г1011&зоаатenЬСКI1Х п~р имен и ЗНВ'Iени"Й,


которые МОЖНО програмМНD С4ИтЬ1.13а<ТЬ 8 память ):1;J1Я И.СПОЛЬЭОВCi+jИЯ
на страницах в ДaI)ьнеишем

<au:thent ic:-ation> Связанный с бе;30llасносты(Jлемент, ИФlопьзуемый ,!1,ЛЯ OJТIpeдelle­


ни!'! режима вутвнтифrllКВЦИИ да~IНОГО Web-ПРИЛОJКенk'IЯ
<au'thCH:i z.at iO!J> Еще QДИН связанныЙ.С t"5еЭРпаСf.lОСТI>Ю эпемент, исrnользуемый ДЛЯ
QпределВtIИЯ прав пол'ьзов3телей при ДОf;lУпj3 " ресураэм сервера
< C'!iJrop i 1 а [ i.o n;. Использ),j3ТСП:ЦЛЯ разреl1JеНI4Я (.илиэапретn) QТJЩДI(~ и опредеПeflИR
яз:ыка .NEI, ИСnОj1ьзуемqro .данныМ Web-прилоЖetlием по умолча-
HI1IO, а таКЖе (-необязатеЛ!,IiО) ЩIR Оf1р~дerьения множеСтва внеШних
.компоновочных блоков' .NE"!; GC--blлКИ на которые ДОЛЖНЫ I/IСПОЛЬЗО­
ваться звт.омаТl1чесll.И

<СО !Jщ!с-tiоnS:t1:iпgs> ИспОЛЬЗУетСЯ дnя хранеНИR строк внешН~'Х ООМlIIнен~й даННОГО


Wео.,)'З118
<clJstQIPE:rrors> Используется АЛя И'fIСТРУКЦИЙ ср!!де ВЫПОI1нения по поводу того. как
сообщать 'об ошибках.· nроисходящlo\J! Б прсщессе работы Weь~при'..
ЛО)l(В'f-J ИЯ

<glФЬаl izatiQT1> Испt)IIЬЗУе:гся ДЛЯ настрОЙКI1 параметр.ов глобализцции данногО'


Web-nРИЛОЖЭf-JИЯ

ИСП!:lЛ&зуется ДrU'I ~ОНТРQЛЯ тшо. как И где среда ВЬ\ПОJ1Ь!6НИI\ .t-.tЕТ


,цьлжна хранить данные состоnния сеанса

I.:fсrюлЬЗуS1'ОЯ для раэреШ8\=1ИЯ (или DТКЛЮЦ6НР1Я) трассировКlo1 данНо­


го Wee-ЛРИ1ТожеНИА

Файл Web. config может содержать допотmтелъпЫЕ' элем'еI-IтыI. рМ1'4ещеИНI!Iе


хан ДО. T.alt И посл:е ~леJ'l,JеН'Тов, npедетмленны.х-:втабл. 24.4. БолыnцнсТl!ОЭТИХ эле­
ментов связаНО с безопасностью,
. а QСТальньre
- ОR.аЗЫВaIOТСЯ nолеэн:ыми толыш для
"'

Построении достаточно сложных сц~нариев ASP:NEТ. предполarающих. н:щр~~р.


соэдание полъзовате.лЪских H1TP-З~ОЛОВ"1{О1il или DОЛЪЗОlli1тель.СЮtX НТТР:"модулей
(эти вопросы здесь обсуждать не ПJl;:u:wpyется). Если вам -иужен: ПOJП-IЫЙ номрлект
Э..'l1емеНi'ClВ. ДОЦУСТ-ИМЫХ ДЛЯ ИСПОЛЪЗОВЩfИЯ в файле WеЬ.соrЙ ig. поищите по КJ:ЦGЧУ
"ASp.NE1' Settings Scherna" в системе операТИВIiОЙ справЮ1.

Разрешение трассировkИ с помощью <trace>


Первым элемен:гом фаЙlIЭ WеЬ _солfig, который мы собираемся здес:ь рэ.ССМО ­
треть. бy-l\e-I: элемент <tra.c.:e:>. Этот XМL-деекри.птор может име-ть лю.бое ЧИСЛО
атрибутов. задающих особе1-ШО<:ТИ его llовеДени.а. как покаЗа}{О в СJiеД\rющем при­
мере.

<ttaoe·
enahled=" t I f,c.l se·f'·
!:1J€'
J ocalQr)ly= "tr1.,le l:f а 1 'Эе.ll
page.output="Uue lfalse"
rечuеst1iIni t='" iпсtеgеr"
tt'<:)СБМОdе="' $оrtВуl'ime I S.оr .tБуС.з te:q.Q ry" j >
1110 Часть V, Web-прилож:ения и Web-сервисы XML

Оrrn:сания этих атрибутов предлагаются в табл. 24.5.

Табnица 24.5. АТРИбуТЫ элемента <trace>


Атрибут Описание

enabled Индикатор разрешения трассировки для приложения в целом (значением по


умолчанию является false -. ложь). Как было показано в предыдущей гла­
ве, можно разрешить трассировку селективно для данного файла *. aspx,
используя директиву @Page
localOnly Индикатор отображения информации трассировки только на Web-сервере,
но не в системах удаленных клиентов (значением по умолчанию является
true - истина)
pageOutput Указывает вид представления результатов трассировки

requestLimit Указывает число запросов трассировки, сохраняемых на сервере. Значением


по умолчанию является 10. ЕСЛI1 достигается предел, трассировка автомати­
чески отключается

traceMode Указывает соответствие порядка отображения информации трассировки


порядку ее обработки. Значением по умолчанию является SortByTime
(сортировка по времени), но можно также указать сортировку по квтегории

Вспомните из предыдущей главы, что с помощью диреl<ТИВЫ < %@Page %> можно
раарешить трассировку отдельных страниц. Но если вы хотите разреnmть трасси­
ровку для всех страниц Web-приложения. измените <trace> В файле Web.config,
как показано ниже.

<trace
enabl.d="tru."
requestLimit-"10"
pageOutput-"fаlsе"
traceMode="SortByTime"
localOnly="true"
/>

Настройка вывода сообщений об ошибках


с помощью <customErrors>
Элемент <customErrors> может использоваться для автоматического перена­
правления всех ошибок в пользовательский набор файлов *.htm. Это может ока­
заться полезным тогда. когда вы хотите построить более понятную для пользователя
страницу информирования об ошибках по сравнению с той. которая предлагается
средой CLR по умолчанию. В общем виде элемент <customErrors> выглядит так.

<customErrors d-e faul tRedi rect="url" mode="On I ОЕ f I RemoteOnl у" >


<error stаtusсоdе="код состояния" redirect="url"/>
</customErrors>
Чтобы предъявить пример применения элемента <cu5tomErrors>. пред­
положим, что Web-приложение ASP.NET имеет два файла *. htm. Первый файл
(genericError.htm) функционирует, как страница перехвата ошибок, Эта страни­
ца может содержать лоrотиn вашей компании, ссылку на адрес электронной почты

J
Глава 24. Web-ПРИЛDженloUI ASP.N:Ei 2.0 1111
администратора GИ~'rемы И. например. еообщение (; из.винениямиэа доставлеННЫе
ПОЛDSОВ;:tТелю Н:еу~()бс:гва. В'J'ОРОЙ: файл [Er rQt:,40·4,ht.m) - э1'(J полъзовательс:кi1Я
страйица ошиБRИ. !(оrrорая должна nоявлятъсsж Т(i)ЛЪRотогда, когда среда выnoд­
Ii~ЮLЯ о:бнарУЖl.ffiает ошибку с номером 404 (rpО3;ЕЩЯ ошибка ~не()бнаруженного
ресурса"). Ес:л:и ВЫ х-отяте.чтобы все ошибки обрабать1Ва.1ШСЬ этими пользователь­
СКИМИ страницами. вы МОЖe'I'е изменить файл Web_config тш{, I<a1( предnarаeJ1СЯ
:ниже.

<?xml verblon="l. 'O·' '?>


<-соnfi.gtJ.rаtiоn шnlwз",,-Пhttр://sсhemаs.miсrОSоft_с;щn/.N~t:СоnНgur.аtiQ.п/v2 . О">
<app'S ettings/>
<.cCl.nnect;i..on;,31;:ring'S / >
<system.w.eb>
<tЮ!!iрilаt ion debug= "fals.e.·' /:>
<а'\.l'!:.hаш:..:hQаti0fr m.ode="Win:dow.s'" />
<austoшErrоrs defaultRedi~c:t =' "'qЩ1еJ;i(:J:rror.htm n mode='IOn">
<ежrо!; statU8Code"-"fО4"' rеdireсt=."Иrrоr404. btт A 1>
</cu.stomE:rrors:>
-< /зуst'еm. web>
<Iconfiguration>
Обратwгe ВНИМ8.1;fЩ~ па то, что .корневой элемент <ё:.1J,5,tощ:ЕrrоJi'-S> :испo.n:ьзyется
дщ:I у.кааания ИМeJЩ общей страницы ДJIЯ :всех необработaщu,rx omиВо.w. Одним. из
атрнбутоn. доторы;е MOryт 1IpИСУТСТВQвать 11 ОТ!(J)ЫВaIOЩем :д,еСКРИIД'оре., smляетс.El.
атрибут rn.'j)d~. Значением. устанавливаемым для этоrо атрибута по }"мОлqа:нию.
ЯВi1шется RеШQtеОnlу. дающее уКазание среде ВЫIЮдnеНWI не оmoбрqэн:am:ь ШU[Ь­
ЗОЕ)ательсдие стран:и:цы oши:бo:it. ec.тm Н'I'Т.P-зanpос поступает с той же матины.· rдe
'fIЭХОДИ'1'СЯ Wfi"-сервер (это очень удобно для раараБD'ТЧИIЮБ. IЮТРРЫМ требуется ви­
де-:гь .все подробдо(''Ти). Если уст.мtоВИ"IЪ для атрuбута щрdезначe:нJ;lС ~оn". ЭТО по­
зволит видеть ПСЩЬ;30ВВ'rеJIЪские ошибки на ВСе::& маI1ЩНах (ВКЛЮчaf{ Ma.r.mmy разра­
б.ОТКИ). TaIOf(e зцметъте, что элемент <с,ш:ltоtп.Е.trоrэ> может поддерживать любl').е
ЧИсло вложецнь;щ элементав <error>. с IIDМ0ЩЬЮ КOТOPbDl; МО1'ЕНо YК<lliaТb,:кaItaН

страница ДОJDю=ra испольэоваться ,Д.тI ошибки с тем или ищ,m кодом .


Чтобы лро;веритъ рабmy П'ользоватeJlЬCJ«JГO переНЩIравления ОIIDIбoR. создайте
страницу ... a.spx с двумя элементами упрaвJ1ШJИJI But ton и обраб()'J'aй'I'е:их собы­
тЩI СНск так. как цре;цпагаетtя. ниже.

pI1.Vate '\7oio. ЪtnGе.nerаlЕrrрr_СliСk (o.bj'ect sende.r:, ЕvепtАжrgs е.)


r
11 Э!l'о 7'еВерируе'l' 0IIDdSЖ)" общеI'O вида.
thro.w ne·w Ехсерtiоn("ОщИбка общ~!'о вида., .. ");

pr:ivate V'oid bth-404Еl-rоr_СliсХ{оЬjеr.:t sещ~еr, :Еvелt:Args е)


{
11 Э!1'О :геверирv_ OIIIИбху 404 (цри O'l'cYWC1]!JJ_ ФlU"шаНу~9'е. aspx.) .
Respohse . Redj rect ( ''МуРаgе. азрх" ) ;
1112 Часть V. Web-приложе~IИЯ и WеЬ-сервисы XML

Сохранение данных состояния с помощью <sessionState>


Наиболее мощным элементом файла <sessionState>. По
Web.config является
умолчанию ASP.NEТ запоминает данные сеансового состояния с помощью *.dll в
рамках рабочего процессаASР.NЕТ(аsрпеt_wр.ехе). Подобно любому файлу *.dll.
положительным моментом его использования является то. что доступ к информа­
ции оказывается настолько быстрым. насколько это возможно. Однако недостат­
кам оказывается то, что в случае аварийного завершения работы этого домена
приложения (по любой причине) буlJYТ потеряны все данные пользователя. К тому
же, когда вы храните данные в *.dll внутри процесса, вы не можете взаимодей­
ствовать с сетевой Web-грynпоЙ. По умолчанию элемент <sessionState> файла
Web.config выглядит примерно так.

<sessionState
1IIOde="lnProc"
stateConnectionString="tcpip=127.0.0.1:42424"
sqlСоппесtiОПStriпg=Пdаtа source=127.0.0.1;Trusted_Connection=yes"
cookieless="false"
timеоut=П20"

/>
Принятый по умолчанию режим хранения оказывается вполне подходящим
только в том случае, когда ваше приложение обслуживается в рамках одного Web-
сервера. Но в ASP.NEТ вы можете дать указание среде выполнения обслуживать
*.dll сеансового состояния в суррогатном процессе. называемом сервером сеансо­

вого состояния ASP.NEТ(aspnet state .ехе). Тем самым вы можете вывести *.dll
изaspnet wp.e.xe в отдельный *.е.хе. Первым шагом при этом должен быть запуск
службы aspnet_state.e.xe Windows. С этой целью введите в командной строке

net start aspnet state


Запустить aspnet state. ехе можно и по-другому. с помощью оснастки Службы,
доступной из папки Администрирование панели управления Windows (рис. 24.10).

Desc:фtion :
PrQ~ lIЦ)P(!rt for out~f?OQOS$
SeSSIOn sliJtn for ASP.м:r. If this
Иf'oIia Is~, QJt-<>f~
r.Q.Jes~'" notbo prOCCSO<!d. Iftris

Рис. 24.10, Оснастка Services Windows


Глава 24. WеЬ'ПРИllожеыия A'SP.NEI 2.Cl1'1·13'
Преимуще-с'ШiОМЭТОro подхода llВi!I1!:eтca ТО. что с 'пО)(р~ OIШ3 СВОИС'l'.В вт
моЖе.те, Ra'СТРОИТЬ aspn et _ state . еке наasТ@ма1'И'Jeекийс~арТ ~И- 8агруШi.е ма.­
щ!ШЫ. В~БОМ муч.ае. эапустИ)3 сервер с~тоrom:н сеа.щ:~, и3мени''tе 3Ле1.нmт
<s.e-ssio!1-5tate > ,в файле weD.conf1.g так. RaК nORазано~.

<sessiet1S'tate
JDOa.=" Bt.a~8erv.6r"
s-t аtеСОIl1'.€<сt:LоnS t,r i:пg="t'срiр= 12 ~. 'О' . О.1 ; 42~ 2 4 '1
sql C p.nnec;-t-iорS-t ): iлg="-<:Jа t.a зоu :r:се=1.27 . (!. 0.1: 'l"ru.sted_ Conn scticm=yes"
CDD'k.iel еs-з="-fаl$е"

f '>

Здесь щ:грибут:rrюde yct-a:вa.влliIВВ.ется равным Эtаt.еSfjr'\1fвr. Этр' им:еюю- тР'. что


требуется, 1)щ~рьCLR обcnyживает даняы€ сеаIi:~а В рампaQ(а.sрn~t_'Stз tj1! .-~~~,
Ватам СЛ~-Ч~. есЛИ дОмен.лриложенил:. содержшций camo' Web-прм.лQiЖ'eние. ~a­
.верШИ".I'C;f.{ a.вap~. c~aBыe Дa:filПllе caxpaн.wrся. Такж~ ~e''l'l:ire. ч::го Э~eJlirеит
<: 9еs _s щns,tq.~Е.<> МОНТ содер1Ь"8ТЪ a'I'p1I{бут зtаt..е€оnцеаt_iоnst..r.:in~ . Т1рJ.fЯjrI-ОО ПО
~ro.щчащпо Щi,а;чешre ,(127.0-.0.1) ДЛЯ адРеса тер /JPyNaвыва:етпа JlORaJП'iИYJ(:) ЩШIИ~
,ну , ЕCJЩ~~1'О 'Э11ОГО БЫ ХЬТ'ИТе,1 "С'Л'обы среда ВhIполие.вия , NEТ иоо~сер­
:вис 3SPl1et_ $ t.ate. ехэ ва дРУГОй машине и сети (снова по~е (J Web-;rpynnej.
вы ИМ~!n,е возмtiЖНQc1lЬ измени"~ъ ЭТО 'энаqени~
НанОИЩ. еCJш -требуется Н8ИВЫСШа.н CfJ!en~ измироlЩ1Щ(Ц:11И J.ol усТоЙЧ'иЗQC'IJИ
ДI"Щ WeQ~n:pилoжeниa. вы можете "эaC'rttВRТЬ'" cp~ :въпюЛ'R'e1ЩЯ, c~ 'Вce.цaн~
ИLJ..[f: СQ~'JОfшин сеанса 11 MJcrosoftSQLSen--er. Соо.т:ветствy1QЩЗЯ модЦфикaциJiF фвй~
лЩW е р.r;сшиg сНова очень npoc:ra.
<·s.es,g.;! Ofls 't a te
mod8=тtS~erve::-"
9.Ьаtе'с "IП[jесtiQnЗtriпg="tсрiР~1 ~7'. 0 • .0 .1 ,424 2~"
s ql-Connect iQIl atri[)g~" data
~Q 1д :!:~е=127 ~ О. О, 1; Ti: I1-s ,tеd_ СQnn~tiQ!j=уез"1
,сор 1c,ie 1 e.ss =>" faLвe·'
t.imeoll!="2 О iI
/>
(}ДflaRO перед зreМ .шцt вы ПОIIbl'Тae'l'eсь вЬnЮJПtЯТЪ соответствующее Web-прв­
дo~e . .вы Д~1ihI ощ.,спеч-ить :npавильную 'Ъtar:тpойку целевой .машины (ука­
эаннаЙ . а:т-рцбу-,!'ом sч~соr1flесtiО1ilString1. ПрJa установке .NEТ FrameW0rk2,O
SЩ{ (ИJЩVfвuа::! S'1:цdtе 200$) создаюТСJl Два ФaJtШi.. тnзtаllSч:1Stа:tе. ",чl ~
f)п i !1S't-а115t;jl$.t:a't:е . s-qJ,., иоторыеno' умолча:нию помeщaюrcяв п~<,%windir,-t>\
Иicrоsоft.NE'Т\Fr-Q.J'МWOIk\<верt.:иJ:I::>. Нв.. II,елев()й машине вы ДOJlЖНЬJ ~поЛнить
фз.ibl Ins,tal1SqlSta~e.~ql. испыn.ауя. например.sgt Server Q'llery Analy7;e;I' ('ыо­
тоpblЙ вхо;дит :в rIOC<J:aиi<yМ1стоsofi: Sgt. Serrer).
Посде :ВЬПIО.llН~ :yXaзaНRoi'О SQL-cnепарил. вЫ обнаружите в.o~ баэу. дЩ-r
.ЦЫХ SQL $erver (~ ~~M ASPState). содержащую наБQР хрa:пm.noж щроце.I!Y:P.вpr­
зы:~аемых среДQ~ ЩOQIоiЛПеfmЯ' АЗР.NE'Г. и множество -таблиn. 1!СП(i)т.зуемых тщ
хра:нени;в. ceaBCOB~ д~юn.:rx (кроме тошо. в базу даниЪ1Х rempdb бу,д.ет доба:вдено
1\'1'НQжеСnQ таt'lЩm, ДЛЯ обмена данными). Вы Д1'>lI-SШЫ ПОf-IlШа.ть. ~n't), настрО~и.ц
~Ь.прилоВtщlИ:fci JШ зЮ1ICiМйНаиие сеансовы:х~врaJi4БaX SQL Эе~т ЯВJЦlt':тf:,fl
~r,blМ Meдд~ из всех .БОамnжныx вapиaв:roв. Преи:мущество ЭТОТQ иар~&ц~ ~
1114 Часть V. Web-прмохения и Web-сервисы XML

том, <tTO пользовательские данные при этом сохраняются наиболее надежно (даже
при перезanyсне Web-сервера).

Замечание. Если для хранения сеансовых данных используется сервер состояния сеанса ASP. NEТ
или SQL Server, то любой ПОльзовательский тип, размещаемый в объекте HttpSessionState,
должен быть обозначен атрибутом [Serializable].

Утилита администрирования узла ASP.NEТ 2.0


в завершение этого раздела улавы следует упомянуть тот факт. что ASP.NEТ 2.0
теперь предлагает Web-yrилиту конфигурации для управления множеством уста­
новок в файле Web.config узла. Чтобы активизировать эту утилиту (рис. 24.11),
выберите Web Site <=> ASP.NET Configuration из меню Visua1 Studio 2005.

:l ASP Nct Wcl) Лррllс"lюл Аdm1Лlsuаl10П М" , o<o[tlntelnet!xplo,.,1 !_ ': ~ i1.~ !


FIe Edt \/1e\OI F.~ar~es 1001< НeIP

Welcome to the Web Slte Admlnlst;ratlon Тоо'

AppliClltion:/CookieStateApp
CurтBnt Usеrl'l1аmв :INТЕRТЕСН\Д TROELSEN

РгоУЮег Enables уа.! to speclfY where and how to s10re admlnlS!ratlOn


Сопtlgur ation data used Ьу yaur Web slte.

Рис. 24.11. Утилита администрирования узла ASP.NEТ 2.0

в большинстве своем функциональные возможности этого инструмента связа­


ны с безопасностью вашего узла (режим аyrентификации, пользовательские роли,
поставщики безопасности и т.д.). Однако. кроме того. этот инструмент позволя­
ет устанавливать параметры npиложения, детали отладки и страницы обработки
ошибок.
OI,aB6I 24 W~Ь-ПРИПQ)кения ASP:NEТ 2.0 1115

Наследование коНфигура'Ци'и
Последним из рассматрива.емых в ЭТОЙ г.лаве вопросов будет .нaC:Jreдoвa.н.иe JCDtt-
фuгураЦ1Ш. Из преДЫдУЩей гла.эы вы узнали. ЧтО Wеь,.nPИ1I(J~ен;Qе МОЖНО· cmp6де­
лить, КаЁ множество файлов, содержзщихсн 11 КOPI:IeDOM в:атзлЩ'е 11 любом ЧUСЛ~
н.еобязameльны:хnoдм:а~в. Все nPИЛО'""'е:ния примеров , ЭТОЙ Ji np~ды;цущеЙ таи
"Находились в ОДНОМ корневом :&аталоге. управlJН.емом IIS (с u.еQбя;~теJIьным :ката­
логом Jlin). Одна1Ю ПОЛ}ЮМlilсштабные Web-upиложен:ия о(5ычно опредeляIОТ в кор­
невом xaтaJioгe -r.шожество подкаталогов, нaждъrй из I«YJ;'0PЫX сод~жит некоторое
под'множество !свнзa.шtых Между собой файлов, Подобно ot;iЫ'-шому nриложен:ию д.пя
наc:roпъиоЙ системы. это делается ДЛЯ -удобства раэработчИIЩВ, поскольку .иерар­
хичеСКaIiI струнтура может сделать БОl1Ъшое мдощество фащщв БО:1J~ ПOFI.JI'1'НЫМ.
Если,у:вас есть WеЬ·IIpйд:оже'fIИе ASP.NE'f. сод~ржэ,щее необязаТeл:blП:iJе ПЬДlшта­
л.оги в корневом каталоге, m.I с удивлением обнаружите. что ка;недый такойподка­
талог может иметь собственный файл Это
Aaecr ~~оЖНостъкаждому
Web.con'' fig.
подкаталогу переопределйть установки родительского Jta.T;'I.IIora. Если пOДt:щтал.оr
не .имеет CBOёrO ПОJI!baо;аателъсщ)го файла Web.config. катало!' tЩСЛf'i..дует'установки
сле~ фafu-ш. WеЬ. со п fig, размещешlOГО выше по CТPYJ:nYpe Шiтмат-а. Такой
ПОДХОД , Е8$ это .НИ СТРIilШО айуЧИт,. поз:воллет перенести оцредещшные Dp):П;щиm.1
ООП на CTpyКrypy 1Шталоrов. Соответствующая концеnцця щm:юстрируется схе­
мой, npeдставлен:нdЙ сШ рис. 24.12,

Корневой IМ1lrкaTanQf Oqpедenяет КОНфигурацию

W'eb,, config .I по ytЮnчаммlO для

1 I WеЬ · [lрмпожен~

Подкаталоt 'Переonределяеi" 'fC11U'IQВ1IИ,


4 I заданные B ' l<Opнeвoм
I ttleЬ,config
I катШ1оге

Рис . ~4.12. ~СЛfl:цование к{)нфиtyрации

Конечно. х;оrя; ASР.NEТ:поз:вOJ'.rJreт определить множество файлов, Web. с:r:щf ig .дл.я


оДlЩro Web-n;риложеюm:. это делать совсеМ не об~зательно. В БОflbI:JДJНстве случа­
ев web-прилоЖ(".J:IИЮ для нормального ф}'ЁIЩИонировa!:tИЯ буJLе JШодне достаточно
оДНОГО файла Web.eonfig, размещенного в корневом виртуалъfit»4. 1ШТaJJOг{'! ИS.

,ЭамеЩlние. Вспом ните из тавы 11, что файл ma сhiле. cGrlf ig опре-деnяет раЭ)JИЧiiые установ"
ки на у;ровне маWIAНЫ, многие из kQТОРШ CЩlзаtlы о ASP. NEТ. 3Т01 файл ЯВЛЯErrqя наивысшим в
иерар)l.И~' наСJ1iЩОfЩНИЯ конфигурации.

На этом наш обзОр ASP.NE'I' завершается. как уже Ul!);цчерки~алосъ 'в rnаве 23.
полное и исчерIIl:olвающее рЗССМО"Грение A8P,NEТ ~.O требует отдельнqй и доВQЛЫЮ
объ'СМНоИ I{НИТИ . Но .11 надеюсь. 'Что~еперь вы ЧУJlСтвуете себи дос'raТQЧRО уверенно
в paмrкax соответствующей I1potpaмIOiой модели:.
1116 Часть V. Web-приложения и WеЬ·сервисы XML

Замечание. Если вам требуется более глубоко изучить ASP.NEТ 2.0, Обратитесь к книге Мэтью Мак­
Дональда и Марио Шпушта, MicrQsoft ASP. NEТ 2. О с гтримерами на С# 2005 для ПРОфессионалов
(ИД "Вильяме", 2006 r).

в завершение нашего путешествия в последней главе будет рассмотрена тема


создания WеЬ·сервисов XМL в .NEГ 2.0.

Резюме
в этой главе вы имели возможность расширить свои знания в области ASP.NEт,
рассмотрев возможности использования типа HttpApplication. Вы могли убе­
диться в том, что этот тип предлагает целый ряд обработчиков событий уровня
приложения и сеанса.

Значительная часть этой главы была посвящена рассмотрению различных под­


ходов к управлению данными состояния. Напомним. что дaшfыe состояния пред­
ставлений используются для автоматического обновления значений НТМL-элемен­
тов при вторичных оБРЗlЦениях к Web-странице. Затем были выяснены различия
междУ данными уровня приложения и сеанса. рассмотрены возможности управле­

ния данными cookie и изучен кэш приложения ASP.NEТ. Наконец. был рассмотрен
набор элементов. которые могут присутствовать в файле Web.config.

L
ГЛАВА 25
Web-сервисы XМL

Г лава 18 информировала вас -о сдое удаленного вэанмод~йствин .NEт: Вы СМОГ­


JIИ: убедиТЪCSI в том. ч:ro эта технолотия позволяет любой rpулпе l(омпъютеpQВ
с ПОДд,ержко~ .Nbl осуществлять обмен информацией через границы .машин. 31'0.
копе'ffiО. прекрасно. но ОДНИМ из ограниЧений слоя удалеНПОN> взаимодействия;
.NEТ оказываe'rСя то, 'Что для 'ка.щЦоЙ из участвующих в обмене сторон требуется
установка .NEТ Framework, поддерЩfta CТS и использование ОДlШaRОВОГО форма:та
сетевого обмена (например" ТеР).
WеЪ-сервисы XМL предлarщот более гибкую альтернативу в деле построеRИ,f1
распредеJ!енных ПРИДОЖСIШЙ. IЪВОРИ npосТbIМИ монами. WеЪ-сервuc XМL - это
е;циmЩa протрaIOdНого кода, об~луЖп:ваемАя Web-сервером и доступная :в рaм:rщx
'стапдартшIX npoмьrщлсRных теПIQЛО'ГИЙ, НШIpимер. Таких как НТТР и XМL. }Зы,
йаверное, дoг~a.eTecь. ЧТО блaroдаря ИСПОЛЬЗОВанию "нейttральв.ых" ~ол()г.tЩ
Web-сервисы .x;м:L преддarают ТЭБоИ: уровень сОвмесl'ЯМОСТИ и взаимодействия в
ОТlIошении оп~рацио~ QИстем. тmатформ и ЯЗЫКОВ. :который ранее бъrтI Пlюq1'О
Недоступен.
Из э'Гой посл~дней глав;ы lЩ:иги вы узнаете о том. как создаются Web-сеРIЩСЫ;
XМL в РаМЩ)Х платформы ."NEТ. в п.роцессе обсуждения основной ~~ мы paGCMO-
-трим ТЩORе ряд свnз~ вопросов. в часmости с.iIужбы, ПОИСКff (UDDJ :и ЩSСОJ.
язьш WSDL и протокол SQAP. ВhIfJ:CНИВ, как cтponть Web-сервисы Xl'ЛL. ~1ы paCCMO~
ТРИ1\.:! раз:IIИ'U{ые ВО3МЩКН'QС'1'и rенериpnвaюm агентов клиента. способных В~:З,bI­
ваn' 'Web-~еТ0ДЫ* в сднхронно:м и а.синхронноМ режимах..

РО,ЛЬ Web-сервисов _
XML
с точки ЗРения самого ВЫСО1(ОГО уроВШI вы можете опред~ WеЬ-сервис XМL,
-кш: ~дивицу проrpaммlЮГО кода. ДОС't)'ЛНYЮ Д11JI вьrзо:ва с ЛРJlo{ОIЦЪЮ l:lTTP-запросов.
Однако , в отЛИчие от -градиционного Web-приложенил. Web-СервИСЫ XМL моЖно
ИСПОJlhзрватrь не T01IЬКO fЦЛSi того. 'Ч."Гобы возвращать 6рауэеру HТМL-кoд r: целью
визуа.цизации. Скорее наоборот, Web-еервис XМL чаще всего npедоoraшшет фym<­
~ональf,[Ые воамОЖпосrn. анwrо.гичные возМХ)жностям стандартной библиотеКй
ПРОгpaмюibrO 1ЩЦа .NEТ ()iапример. специалъщте ВhJЧJJсJ;t;еНИЯ, выборку данных из
QataSet. чтение ценна акции и 1:,ц.).
1118 Чаt."Т~ V. W~Ь-ПРI1ЛОЖ1iН>ИЯ 1.1 WeJj-сер~ио.1:iI XМL

ПреИМУЩ~СТВ'а Web-сервмСОВ XML


на l'IерВЫЙ ВМШIД. Wf.!b-~fi:РВ~"I XМL ~щгут nо~са;-:1QТЬСЯ просто ОLfередной НОВ(l:И
1'еХНодоI'ИeЙ уд8лен:в.ого Взa:lШ"ОдейС1"ВИЯ_Это. U0неЧНD. 'ГаН я e~'Ih. но давайте рас­
СМО'I'рИМ эту -технологию чуть ;цодроб~.ее, ИС'l'Qриче(Ж}iJ ,lJ,.'1n дос'IYfi::1 «- удаленm.rм
ooъeIt"l:'aМ Beerдa треоо8ЙJЩС:Ь umщиa.~В1bIe зa:впcющre 01' Dла:rформы (а -часто :и от
Я3ЫRa) tIpОТOlФЛЫ {ПСОМ. Java RМI и- т.д.}. Пр_оБJТеиа Taкoro ио,цхо:ца а~етсл
не в J!I~I I! еГООcmiпе тeJIНОlIО1'ИЦ. а в ЩМ. ЧWO ЩlЖДВ.."J I'IЗ еТорон эaм:ьrnгетея
зеваем специф:ичеС!ООм еетеJ3Щ\! фориа'I'е-. Поэтому при попыт.ке ПОО~.оe:mtя рас­
npеделенноИ-сиttтем'ы. в К1)ТО~Й; И:('Illj)J~уе1'С~ II-Iможer-,БО оперш~A"I'JВ::ыx ~!J'i'eM.
RaЖДОЙ Мai.ШП-Iе ПРИХОДИ'J'eJt <'.QrлacОВЫВЮ'Ъ фQр.'IOШ1' ПaJrera,II,ВJiЮ>IX. протоволиере-­
ДflЧJi' И 'J:д.. -с Щ1ШЬЮ ynpощеmm сит)'arJ.U]'[ Wс:Ъ-аерви:с,ы XМL nОЭ80ЛдЮт 1'!.bl3ыватъ
методы ИСМЙС'ma удмеiШv1'О ~па r.: ЩiМI)ЩЬЮ cmщдарт1lЫX НТГР-зa1IpOсаR. Из
всех np(j'['()ROJ]OB. сущеr.тayIOJЦик на c;e~ день. НТТР Яll..1JЯе'И:н мииствеи~
вьrм сетевым npoто:н:олом. с ЩУ.Гор)ill:.1 ~l?огла~!lЫ" 8се Ilла:ГфоРl\>tы (в вонце концов.
ЛГIр- 9TOOCНQBa Wotld VI.'lde W",pJ,
Друю'й фуцц~альной .про6.Л~9Й ЦС11()ЛрЗQJjщmaчас:rв:ы:x:: аpxrпеюур уда­
л:е-нвоrо 1i3CtИМОдейсТ1lИ.8. лвл.шrn.я: то. Ч'I'P щ:е тт требyIЩ'. "I'I10бы otrt.pa-lШ1'eJrЬ и
ПО.1IyЧaТe:m. "поНИМ:a.l:IИ" одну ту ~ Clf{'тeмy базQ1ШIX ТЩIОВ. ОДИaRо, 11 ВЫ С этИм:
ДOJIЖНЫ сог.лаCИIЪСЯ • .а rrayUst J.a,V-<'i ИjVf~ ~ало общеro с А! !:if1L,:Lat .NE1', и. v1ra.
QНике И-1idею-т -ничt:tо оБЩеГо с ".ЩСС:i'f8\:)М Ci;>!', Web-еерDRL'Ы XМL обеспечивают
sозмош..юCTh rnрмоиитшьrо Обмt:ЩJ. J-щфUРJ~ для lIееовм~ i:IЛaТформ.
011ерацв.оюIых СИётеМ: и яэЬmов J1J РI;JJCf;lШoIIМUРО)lаНWL B!\lU\CTQ тоrn Ч't'Oбы BЫН)~­
в:ьх.зы:ваК)lЦ)"Ю сторонр IWВИМатьсrreциалъ~1J1) систему 1'ШIоtJ. ШIформaцmr МеЖДУ
системами, передаеоЕСН В. виде ;(МL"J@J:П-nЩ: (кOТQpьre на tt~r оказЫВШО1ССS- ~11pa­
DИЛЫJO~ формаТИРОВАНFfЫ:МИ СТр6l'11'1МИt). Основю.хм1IpSJПUIОМ здесь ЯВJD1~..я следу~
ющее: t'..сли ваПiа опе~онная; си('тt'ме, nЩnCЦUJ:r.:т оrrepa:rивкый доступ й аналИз
сw.moльных ~, она спосОбна .взaш.ro~'I'ВQв~,т.1;> м. с Web-серIШСОЫ XМL.

~мe. Web- серВIIIС XМL Мl&rщ.рff .NEТ npa~gSoдc:raeH~oro 'J'рnaня .оБCJtужтцtеroя {;epB~pOM
118 В' pc1f111Ka" ~ЩWlbt-lРГО !lирryалЬНОrti I(ffiQJJora. Однак.G" ICЗ.к П~SОfWIЛО~Ь В ГnBBe 23. с 'nОМtlЩblO
We'bD.e'l.'. WebServe.r ..е.&е в .NEТ 2 _О теnер.l;>МOЖI:Ю ззгруж~r" Wеь-сод~ржимоо н иэ lJО!ШJtь­
:tlОn;:O . ~6rз (ПРИ разработке и ~еС'U1РОВШiIОII·

ОпреАеnение клиемта Web-сервиса XML


Одной особею-IQСТЬ)й Wе:tн::щm:и.сои XМL. к!Эторао может снаЧaJm Rа.э<!:тPf'.J;I цe.~
nORm'Rf;\Й, lI:в.J:fНeтcц 'ТО. что *Щl'греБJ;'iТe:J:rем~ Web-с.ервисов XМL ЯВШI:ID'"Гt'JII Ire то.l~ЫЩ
Web-!!трВВИi:J;Ы. :КoHC01IЪ1il:>,l'e, КЛ'Иент.ы }i lап-ref]ТЫ WIndcтw.s 1'Ь:тзs w:же моiyr ИСJJ~lrьс
зовать web-сёрвиcrl:iI. в Jll9БРм мучае- ПО1'ребит~ Web"cepb-иса XМL веЯвНG взая:­
ьщп,ействует р. УДaJIe1llJbl1!{ WeiP-серв:uсо~ XМL с IIOМОЩblO I1pом~щуточвorо пшu
агента (proxyt.
Лrет WеЬ~рвиеа ~ 1:!Б!Т1lff,l(И"J' ~ ведет себя в to'tHO-С'1'И так. К.aR на.croИIЩtЙ
yдвJЮнвыИ объе;к'г, пред:тrаг~ П]J'E ~.;\М 1'01{ :Jt~e ffil.6QP "ЧЛеной. Qдна:ко ~эа ВУЛ:И€ЗМИ"
прorраММI-IЫЙ под агента Иапра6JIЛСТ sanросы Web--еС!:рвису Xl\:fi., ИCne.lI't.ЗУНСТ;;Щ-­
дар!I'Rые воамОЖНОC"N\ ит.гн Aгr,wr T~~ отображае::r IJD~ающий ПQ'J}Оу" XМL­
А31п1ых в соотвеТGтвую'щие 'ТЩIl:h[ даю:iЬ1.Х •МЕТ (;или mоб;у"Ю д.PYry1Ю m:C"I'e1J.W ТWlPЦ,
Глава 25. Web-~J!)ВИСbl XML 1119
понятиых npиложеъщю ПЩ'Ре!бите;щJ. На рис. 25.1 по~азана базовм с..хема взаим.о­
де:ikтвия W.eb-сервйсов XМL.

, Прмmм.ceнмe.NЕТ
liЗ,nnатформе
WlhЗ2

:=-.:: 8
'~Java {ИIJИ :NE:t}\.

т" "/"
НПР &XML
-зre~ ,
.. - .,
НПР&XМl.

Web~p "
нттр & XML
• (на :ЛIC~' nnaТфоpмlJ)"

Рмс. 25.1. WetJ-сервисы XML ~ дейотвии

КомпонентыWеЬ .. сервиса XML


в дополнщ-ще н бибmютеRе управляемого дрограммно.ю кода. обеспечивающей
пре-длaraемые сервисом функ~оналQНЫе воом:ожцocrи. д!ш Web-серви:са XМL тре­
бу.етсн: опредед~ннан инфраСТРYIO'YPа иоддеР)lj;ИИ . В ча.стности, Web-сервис xмL
ИCl10:n.ьзyе:r ~Jreдующи.е базовые технолоr;ии:

,. слу.жба ПОИСJ(а (позволяющая нлиентам вьmсниrь :м;есто нахождения Web-cep-


В:ИсаXМL);

• слу-.кба описания (ПОЭ,SОlJЯЮЩая RлиеН'1"ам узнать. чтО' MO;r,кeт преДЛQЖИТЬ


WеЪ-сервис XМL);

• транСПОjYГнъm nPQТОЩ>Л (ПО3ВОJЩЮЩИЙ обмe.нnваться информацией междУ


ЮIИeНТОМ ti WеЬ~ервИ/)о:м XМL) .
мы: рассмотрим ПОДJJ()бн() каждь.(Й элемент инфраструктуры в продессе изуче­
нин материала этой ГЯ~Ы_ НО для fJ;aчRлaобсуждеnин: ниже npедлагаетсЯ RратЮIй
обзор унаэa.нF1ЫX -техцодогий mэддержки.
1120 Часть V. Web-приложения и Web-сервисы XML

Служба поиска Web-сервиса XML


Перед тем км клиент сможет использовать функциональные ВОЗМОЖНОсти Web-
сервиса, ему нужно узнать о существовании и месте размещения зтого сервиса.

Если вы являетесь создателем и клиента, и WеЬ-сервиса XМL, фаза ПОИСRа оказы­


вается очень простой, ПОСRОЛЬКУ вы сами являетесь источником нужной инфор­
мации. Но что делать, если вы хотите сделать возможности Baruero Web-сервиса
ОТRРЫТЫМИ дЛЯ всех?
Для этого вы можете зарегистрировать свой WеЬ-сервис XМL на сервере UDDI
(Unlversal Description, Discovery, апd Integration - универсальное описание, поиск и
взаимодействие). Клиенты могут послать запрос к каталогу UDDI, чтобы получить
список всех Web-сервисов, соответствующих заданным критериям поиска (напри­
мер, "найти все Web-сервисы, связанные с получением метеорологических данных
в реальном времени"). Идентифицировав подходящий Web-сервер в списке, возвра­
щенном в результате UDDI-запроса, вы можете выяснить все возможности этого
сервера. Если хотите, можете назвать UDDI "белой книгой" WеЬ-сервисов XМL.
В дополнение к UDDI-поиску, Web-сервис XМL, построенный в рамках .NET.
можно найти с помощью DISCO - ЗТот несколько искусственный акроиим рас­
шифровывается, как Discovery oJ Web Seгvices (поиск Web-сервисов). Используя
файл статического поиска (*.disco) или динамического поиска (*.vsdisco), вы
можете "афишировать н набор WеЬ-сервисов XМL, размещенных по конкретному
адресу URL. Потенциальные Iiлиенты Web-сервисов могут перейти к файлу * .disco
Web-сервера. чтобы проверитъ связи всех опубликованных WеЬ-сервисов XМL.
Следует учитывать то, что по умолчанию динамический поиск отключен, по­
скольку имеется потенциальный риск нарушения защиты, если позволить IIS от­
крыть весь набор WеЬ-сервисов XМL всем интересующимся объектам. Б связи с
этим службы DISCO здесь обсуждаться не будут.

Замечание. Если вы захотите активизировать по,пдержку динамического поиска для Web-серве­


ра, прочитайте статью аЗ07303 базы знаний Мiсrоsоft на страницах ht tp : / / s ирро rt .
microsoft.com

Служба описания Web-сервиса XML


Итак. клиент знает, тде размещен Web-сервис XML. Теперь клиент должен
узнать фУНI<циональные возможности этого сервиса. Например. клиент должен
иметь возможность узнать. что сервис имеет метод GetWeatherReport (), предпо­
лагающий использование некоторого набора параметров и возвращающий некото­
рое значение. чтобы клиент мог вызвать этот метод . Бы. возможно, догадьтаетесь.
что это предполагает использование некоторого метаязыка, нейтрального в отно­
шении всех платформ. языков и операционных систем. XМL-Meтaдaнныe, исполь­
зуемые ДЛЯ описания WеЬ-сервисов XМL. создаюТСЯ на языке WSDL (Web Services
Description Language - язык описания Web-сервисов).
Бо многих случаях WSDL-оnисание Web-сервиса XМL автоматически генери­
руется сервером I1S Microsoft. если поступающий запрос имеет суффикс ?wsdl.
Вы увидите, что первичными потребителями WSDL-KOHТP8RTOB являются инстру­
менты генерирования агентов. Например, утилита командной СТрOI<И wsdl.exe

1.
rJ!~ j!a 2~. Web-с;ервиObl XМL 1121
(ее обсуждение бу.дет цредлоЖ~О Цf.)З~) гeH~p»PYeт клиеит.cюm C:fI-хласс агента
на OCgOBe l'iМеющегося WSDL-ДОку!4ентц.
В более сложирух С:ЛУЧ~ (обы'ЦlО с Ц~ЬЮ гарантии совместимости) .при nо­
С.троещш Web-сервиС'ов многие ;разработчики используют ПОДХОД, в рамках кото­
рот:о сначала :вручную оцределлетсJ:'l WSDL-дqкумещ ПОСI<dIIЫty упомянутая выше
УТИJIМта КОМaидl'lОЙ СТРОI<И wsgl. ехе может генеРПР0Вать оnиса:нип интерфеШа.
AlUl Web-еерви~ XМL и на оснщ,е W5DL-сщредвтiения.

Транспортный протокол
Uoeдe создв,дия ПЩа агtЩТа Д1Щ "ВзЩ!Модействия с WеЪ-сер:висо-м XМL НlI.Иент
может выbJватl:! доступные -методы. l}<Щ уже подчерJЩВ-З:JIОСЪ, соответствующие
.дmщъre пеpt:дaIOТС,l'l с ПОМОЩЬЮ C~eВOГO црqтqкола J1f'IP. В частности, :и.ля оБМt:йа
информацией между потребитещши ~ WеЪ-Сервжт.m можно НClI.OЛЬЭGВатЬ НТТР­
M~TOДbI GE·T и POST или ЭОАР.
в общем, ОСНGJВ:ньпrt варинитом выбора !!iбьrчно ОRазывается .SOAP, поскольку.
:как вы вскоре убедитесЬо. СQобщеНЩI SОЛР' могут содержать XML- ОIIйсания слож..
Hых ТИПОВ (ВЩ1]Ю:Чан ПОЛЬЗрЩ:lтeJ]Ъс,к;ие ТЦIIЫ: и типы из бибЛИQТек бшю:въrx классов
.NEТ), При ИСПОДЪ30В/:lНИИ hrrr-ПРОТЩWЛQвGЕТ и POST вам щщдетс:к OI'paНИчнтъся
более узким мвоществом Т1'Ц10~ XМL-cXeМ:ы.

Пространства имен .NEТ ДЛЯ Web-сервмсов XML


Теперъ. когда у ва.сеС1:Ь база ДЛЯ ПОНИМaIO:lЯ ПРШЩШJOВ фУНЮЩОШЧJDВaIO!Я Web-
сервиса» XМL, мы с вами можем заняться построением '{'ЗI~ого объсRТa В p~
ru:raтформы ,NET. ВИблиотеки: базОБlЮt Kjlaccon определтот целый рнд пространст:в
имен. обеcnечива.юЩШ( ВЗ:llIМ(jДействи€ С любой И3 доступных техиолоrий исцолQ.­
аования web-сервисов (табл.25.1).

Таблица 25.1. Простраксща имен для РllБQТbJС Web-сервисам.и XML

rJpocтpaнCТBO имен Описание


Sys :tem..VJ·e b .3erv iсез СодерЖl1Т баЭОВБlе типы (I!КЛlОЧ3!! очеf/Ь важны1
атр'ибут [we bME:tl'lO d])~ необходимые ДЛЯ 110-
строetlИЯ lIюбоrо Web-сераиса -XМL

Sys: t e rтi . Web. S:e:r:vices.Configu rat ion Содержит типы, ПОЗВО1ТЯющие ttастроить по­
щщеМИ' 8 Web-сервиса XМL 8 ореде ВЫПQIIН,*,ИЯ
ASP.NEТ
З уэ tem .We b.se rv iсеЗ._Dе~ ~х ip~ ion Содержит 1илы' об6СЛEt4иваюЩие програММНОЕ!
взаимодейсtвие с WSDL-Аокум~нтам·, предлага­
ЮЩИ~ ОПИ~НИ6 данногр Web- оервиса

&уз tеш .Web, Serv.Lces. D i sCQVВ.c y СОАерж~т lИПЫ , позволяющие потptjбител~м


Web-сеРВИС:ОEl выпалн!!,\' nР@гр!iММНI;JI~ nOlo1clC
Web-СервPlСОВ на СDотвerствующей машине
5уs·tещ.WеЬ . Se ·r v i ce$.Pro.to c:pl$ Определяет ряд типов, предстаВlIяющих ;'атомы'
РEiЗПИЧН~Х протоколр13 СВЯЗИ weЬ·се.рвиоов XМl
rHTTP-МЕ!!t:lДЬ! GEiТ и POST, а также SDAP)
1122 Часть V. Web-ПРИЛОJКения и Wеь-сервисы XML

Замечание. Все npостраliства имен, связаНliые с Web-сервисами XML, содержатся в компоновоч­


ном блоке System.Web.Services.dll.

Пространство имен System.Web.Services


Несмотря на богатые функциональные возможности. обеспечиваемые все­
ми пространствами имен .NEТ, связанными с Web-сервисами XМL. подавлщощее
большинство ваших ПРИJIOжений потребует непосредственного взаимодействия
только С типами, определенными в System_Web.Services. как становится ясно из
табл. 25.2. количество таиих типов достаточно невелико (что уже хорошо).

Табl1ица 25.2. Члены пространства имен System.Web.Services

Тип Описание

Web Met hodAttribute Добавление атрибута [WebMethod] в метод или свойство


типа класса Web-сервиса обозначает возможность вызова
соответствующего члена средствами нттр и сериализацию
в формате XМL

WebService Опциональный базовый класс построения Web-сервисов


XML в .NE1 При использовании этого класса производный
Web-сервис XML будет иметь возможность " аккумулиро­
вать" информацию состояния (например. переменные се­
анса и приложения)

WebServiceAttribute Атрибут [WebServi c e] может использоваться для до­


бавления в Web-сервис информации, например. такой как
строка с описанием функциональнbIX возможностей серви­
са и соответствующих пространств имен XML
WebServiceBindingAttribute Этот атрибут (появившийся в .NEТ 2.0) объявляет связыва­
ющий протокол, реализуемый данным методом Web-cep-
виСа (НТТР-протоколы GET и POST или SOAP). и уровень
функциональной совместимости (WSI) Web-сервиса
WsiPr o files Этот перечень (появившийся в .NEТ
2.0) используется для
описания спецификаций WSI (Web Services Interoperability-
функциональная совместимость Web-сервисов). которым
должен удовлетворять данный Web-сервис

Остальные пространства имен. показанные в табл. 25.1. мотут быть полезны


вам только в том случае, если вы захотите вручную взаимодействовать с WSDL-
документом. службами поиска или соответствующими сетевыми протонолами . Все
подробности МОЖНО найти в документации .NEТ Framework 2.0 SDK.

Создание Web-сервиса XML вручную


КЭ.R и любое другое приложение .NEТ, Web-сервисы XМL можно создавать вруч­
ную. без использованияинтerpированной среды разработки. такой как. например.
Visual Studio 2005. Чтобы прояснить возможности использования WеЬ-сервисов
XМL. давайте построим пример простого Web-сервиса XМL вручную . С помощью
текстового peдaRТOpa создайте новый файл с именем HelloWorldWebService.asmx
Глава 25. Web-GSРВЩ:blХМL 1123
(00 умодчанию для ооезначенпя файлов Web-сеРВИСОJ:j .NE1' ИCllOЛ1!3уется расшщр~
вне *.а.з,rnх). Сохраните файл в ПОДХQДНЩ~М месте на своем жеспщм: дисщ} (нщ;rри­
Мер. В nanкe C;\BellQWorldWebService), дрбави,в следун:>ще<! определение типа.

<%@ XsbService Languaqe="C'" Сlаss="Ие!lоWebSеrv.iсе, .НеllОSеxviсе" %>


psing !';ystem:
u:sing- $yst ет. W::;b. S~гviФ~s',;
I1amesp.aro€ H.elloWebSeJ!Yice
r
pubIic c1ass Не'1l0Sеп-±ое
i
[~thod]
puD1ic strlng E:el1 oWo:rl'd {}
{
retHrn "Hell o !";,

},

в оqIQЩI0М, фaйJ:I-k.д:5mх вЫl"JrЯДi:lт аналОгично mобому другому определению


цроС'.гранствJjI. имt;R С'#. Первым дtJстоЙJ-rым внимания отличиеМ яв..'LfIеr<. я
. то. <по'
ЗдеGЬ ~Dользуется диреR'ГJiВtt <'%@WebSe:z;vice%.>. Jroторasдолжна, как минимум,
~~ .IЩmJ!ЦiИе ynpfш.ляeNiotо языка. используемого Д,IJЯ oi:I:peдe.1Ieни.я С()()'ГВеТству­
ющего ющ:сса. И ПOJrnое имя этого класса , Б ДОПОJIНeJDЮ :н. атрибутам Lапg"\l аче .и
Class диреRТИDil <%@WebS,e rvice%> может ' таюке содержатЬ мрибут DebiJg. инфор­
мщ>ующ1Щ :КОМIIИЛЯ'rtiр A$F.NE'Т' о необходимоC'rИ тенерирова1Шя СИМВQJ10В отлад­
~. И необ.язателыюе значение CodeBebind, идентифицирующее CВJШанньШ фai'lЛ
прогрвммпШ'о кода IЩЦЦержки в пределах необнзателъного кa:raлШ'а Арр_Соое (см.
1'лаву 23). В этом :примере мы не собираемсJl' исшшьзовать внеtJJНИЙ файл кода под­
дерЖЮI, а встроим:вСю :неоfuroдиму:ю программную ЛОГИR,Y непосредственно в фai'lЛ
*.asmx.
Кроме испоllЪЗОВанпя дирентивы <%~ l'1ebServ ic.e %>. цpyroй особенноШ'1ЬЮ .,зm-
1'0 файла *. аэrnх ЯВJUIeтсяиCn01lli3OВfl1:iИe щ-рибута t web,M ethod). информирующ~го
,среду вьшшmeния .ASHNE1' о том, Ч<nЭ ЭТОТ метод будет доступен для поступающих
H1TP-ЭaDpОСОВ и должеR позво.nлТь сери:anизаЦМ1о возвращаемых значений в фор­
мaтeXМL.

Замечание. В rщмка.х ttПР могут БЫТЬ ,ДQG1)'nIiЫМ~1 mлщ{() члеН!)I., имеющие атр~бут [W€bMethod].
Члены, не обозн~....енныв атрибутом [WеЬМеtl,(]Iщ1 J, не могут .В!>iзываТЬСI1 а1ентом МИ6Jна

Тестиров'ание Web-еервиса XML


е ПОМОЩЬЮ WebDev. WebServer.exe
Напомним (скова СМ" глзву 23). что WehDev.Webse rver .Е!;хе .JIВЛЯется серверам
Web-разрабоТН'И A5P.NE'f; поставляемым 11 составе дистриС5утива .NEТ Framework
2 .0 SDK И хотя WеЬDеv.,WеЬS'аг..7еr .ex€' Jle предполагается ИСЦОЛЬЗОIЩТЬ для обслу­
)ЮШМШВ Web-сервасовХМL .прои.ЭВQДC'I1ВООНОro уровня. это,т инструмент позволяет
запуеТИ'IЪ Web-еодерЖ1!МO€ непосредств€шю из Лt)Калънщ-о ка;галога при o~e.
Для npовеРЮ1 своего cepB~ca С помь!ЦЫО этот!) ц:нструмента откройте ОIЩО крманд-
1124 Часть V. WеЬ-приложения и Web - сервисы XML

ной строки Visua1 studio 2005 и выполните следующую IюманДУ. указав свободный
номер порта и физический путь к каталOlУ. содержащему ваш файл *. asmx .
WebDe v . We bSe r ver /port: 4000 /pa t h : " С: \ He ll oWor l dW e bSe r vi се "

После запуска Web-сервера откройте любой браузер и укажите в его окне имя
своего файла * _а smx , используя соот ветствующий номер порта .

hctp: // l oc alhost:4 000/ HelloWor ldWe bServi c e. as mx

Вам будет показан список всех Web -м еТОДQВ. доступных по этому адресу URL
(рис. 25.2).

!i HelloServicc W. Ь Service Microsoft Inlel'neJ Explorer I::;][ЕШ~I


Flle Edit lIIew F.varltes Tool, Help
»

HelloService
The follo wfГI g ope,"ations are supported . For d fo rmdl d еf iп i ti О П J plea se re'lI e w th e
S e rvice De s qJ.ptit!'t .

;'!.
< . ~ ;, ,
~ -_ .

Рис. 25.2. Тестирование Web-сервиса XML

Если в окне браузера 8Ы щелкн е те на ссылке HelloWorld. откроется другая


с траница. которая позвол ит вызвать [We bMe thodJ . ТОЛЫiО что выбранный вами.
В результате вызова He l loWo r ld () будет возвращена не буквальная строка . NEТ
Sуs t е m. S t r iп g . а ХМL-представление текстовых данных. возвращаемых Web-Me-
тодом He lJ.,oWor l d ().
<?xml v е rs iоп="l. 0 " encoding= "u t f - 8" ?>
<s t ri ng хmlп s=" httр : //tеmр ur i . org/ " >Hell0! </ st rin g>

Тестирование Web-сервиса XML с помощью 115


Теперь. когда вы проверили свой Web-сервис XМL с помощью Web De v.
WеЬ Sе п'-е r .ехе . перене си те файл * . asm x: в виртуальный каталог IlS. Используя
инструкции . предложенные в главе 23. создайте НОВЫЙ вирту альный каталог с
именем He ll oWS. который будет отображаться в физическую папку. содержащую
файл НеllоW о rldWеЬSеrviсе .а эm х. После этого вы получите возможность прове­
рить свой Web - сервис с помощью ввода следующего значения URL в строке Web-
браузера.

b t tp : / /loca l host/HelloWS/He ll oWor l dWeb Service . as mx


Глава 25, Web-сервисы XML 1125

Просмотр WSDL-документа
"Как уже упомщrалОСI!, WSDL двляется метаязыком, ОIIИс~ающцм шюгочдс­
денныеособе}-I1-ЩСТИ Web-методов. дос'1ущiык по Да.Е!ЕР"муадресу URL. Обраггите.
DНИМЩ-1И(':uа ТО. что при npоверке Web-серв:исщ XМL .aвтOMaT]J~eclG1 гeJ;Iерируемая
страница теСтирОВaIЩЦ предлагает ССЫJЩУ Seгvice Dеsсгiрti оп (ОШJCэние сервиса).
В резулътате: щелчка на этой ссЬt1Ше к теКУЩеМу :аanросу :прис:оt;ди:н;яюrrсн СИМВОЛЫ
?-ws.d l. Когда среда БЬЩО,lJНеIЩЯ ASP.NJj;Т получает Зa:IJрОС ДJJ;Я Файл:'). • .аsЛ1~ с. та­
ким принреnленIIblМ qффИШ:ОМ. ОН4 Е!:Бтома')'ически ВОЩJращает соответствующий
WSDL-код.. ошrnывающий. ца.ждый доступный Web-метоД.
Б настоящий МD1',ЩНТ ВЩ\IC не следует беспоROИТЬС~ о прирqде WSDL-Кi)Аа mm
формате WSDL-докумеита. Пока что важно ТОЛЪRО понимать, ЧТО WEjDL-ксщ onи­
еьша~ то, кан Web-метоАbl MGryт ВЫЗЫБа'I1ьея с ПIi)МОЩЬЮ ИМеlОЩИХс,я: ПРОТШЮЛ(JS'
евяш WеЪ-сер1!иса XМL.

Автоматически генерируемаSl
страница т'естирования'
как вы TO:iIЪкa что убедились. работоспособность Web-сеРВИСQВ XМL МОЖНО про­
верить с nОМОЩI:;Ю автомапмесЮl rehepJ-lруемой HTML-стршnщы n. браузере. Когда
обнаРУ.lКИВается l4ТГP-зanpос. указывающий на данный файл ". azUtx. среда ВЪПJОJl~
нения ASP.NEТ :использует фз:йл с именем Dеf.аUltW"Э cllНеlp.GеГJеr а tor . азрХ. чтобы
создатъ НГМL-стрafIИЦy. позволяюIЦYIO вызвать Web~MeTOAbl. доступные поданно­
му URt. Этот файл * ..азр}\ можно найти в сmщующем 'ШlТaJJоге (З;1есЬ" щшеqно. блOR
<.версия> следУет заменить на номер вашей текущей версии . NEТ Fram.ework).
С: \ Windbws\.Mi CI'bsof t. NE'1' '. Framewor k\ <в-ерс:и:я> \CONFI G

Создание пользовательской страницы тестирования


Если вы хотиrre, 1.JТOбысреда ВЫIIOJIНеНИЯ ASP. NEТ Iфименяла полъзоваТС.iIьскИЙ
файл .... аВ, РЕ ДШL.IlРОВерНИ ваЩИХWfJо-сервисов XМL~ вы можете встроить n эту
с:гр.а.ющу дополнителънуюинформацию (например. фирменный знак :компании,
доnеJIНШ'eлыiые ,опИсания сервиса,. СC'bl!IКИ на файлы справки 11: т.д.). Чтобы упро­
стить себе задачу. БDЛъtТtиНcтво разработчиков сначала l<ОПИРУЮ'I' существyIOIЦИЙ
файл D.е.fa.ultWsШНеlрGепеrаtоr.аsрх в цроеК1'.а затем. ИСПО.;IЪ3У.я: этоrr файл в
качестве исходноl'О~ НуЖ1-IblМ образом И3.\-Iенmoт оригинальный HTML-ДОКУЫен"f и
npограмМныЙ JWД С#.
СкопируЙте файл Defau.ltWsdlRelpGerierator,aspx в Hs,TailOT. содержа­
щи.ii Неll' ОWб,rldWе Jt<Sеr v i се . а sm х (tIaпример. С: \Не.llОWюrldwеЬ'Sе r'" .ice).
nереимеRyЙте полученную нo.nию в MyC'UstomWsdlHelpGene~a~or.aspx 1<{ измени­
те кююи-ни15удъ фрагмент RNL-кОда. CKWКeM. в облас1'Й дескриnтор:а <title.>.
iianpимер. измените имеющийся КОД ра6Метки

<tit l~><%,*Servi сеЫате + ... " + GetLo c al i zect-rехt (j'W~'ЬSегvi се"" ' ) %><; / ti t J e~
на с,rщцующЦЙ.

<t.i ,t le>l!toii собопeвsый:


<%.#(> е.Л!i.сеNаmе + " " -+ GetLocalizedTex't. ("WebSer v i<;:e"') '%>< /ti tle>
1126 Часть V, Web-nриложения и Web-сервисы XML

После изменения НТМL-содержимоro создайте файл Web.config и сохраните его в


текущем каталоre. Следу-ющие xML-элемеlпыl дают у-казание среде вьrnолнения ИСПОЛЬ­
зовать ваш пользовательский файл *.азрх. а не Defa·ultWsdlhelpGenerator.aspx.
<! - - Здесь ухз,зwвз,е'I!СJl пользовз,тельский фa.йJI *. з,sрх - ->
<configuration>
<system.web>
<webServices>
<wsdlHelpGenera tor hre f="МyCustomWsdlHelpGenerator. aspx" />
<./webServices>
</system.web>
</configuration>
При запросе своего Web-сервиса вы увидите, что строка заголовка браузе­
ра изменится в соответствии с указанным вами пользовательским содержимым.

Кстати, если вы захотите отключить генерирование страницы помощи для дан­


ного Web-сервиса, вы можете сделать это с помощью злемента <remove> в файле
Web.config.
<! -- Orмеиз, :reиеРИРОВёUlИJI страницы ПОМОЩИ -->
<configuration>
<system_web>
<webServices>
<protocols>
<! -- Этот элемент О'l'l>IeRJIе'l' геиерироваиие WSDL-ДОJtуиеН'l'а -->
name="Documentation"/>
<геmоуе
</protocols>
</webServices>
</system.web>
</configuratlon>

ИсходtlЫЙ код. Файлы примера HelloWoгldWebSeгvice размещены в ПОДкаталоге, соответствующем


главе 25,

Создание Web-сервиса XML в Visual Studio 2005


Создав Web-сервис XМL вручную, давайте посмотрим. как зто делается в Visual
Studio 2005. Выбрав FileQNewQWeb Site из меню, создайте новый С#-проект Web-
сервиса XМL с именем MagicEight8allWebService и сохраните зтот проект на
своем локальном диске (рис. 25.3).

Замечание. Как и в случае Web-узла ASP,NEr, файлы '*. sln проектов Web-сервисов XML, создан­
ных в
Visual Studio 2005, размещаются в папке Мои документы\ Visual Studio 2005\
Projects.
rJ1ai3& 2:5, web-i:6рвиоы XML 1121

T~,

~l~ '~~."ll!,_~t!Пфl~

• мр,rю
Cr~&taI·Fl.S~, j

~""J ~~ , ."'3..1.t.~
. :.,\~ . •~~____~___ i! 1 ~-.
_'.•~.,__~_'_.~'~~_'~'.~ I

~ lYf!o.8I r;, . __ ' [.!


С"'_' _,_ _ _ ~ __ ~ __ ~ __ ~ _______ ~ _ _ _ _ _____ , _ • • 0 ___ _

Рис-.25 . 3. Проект W6ь.-сервиса XМl в V1sUal StudiQ 2а05

После Щ~ДЧJ(а Н!). кнопке' ОК


s ОЮiе срзюrни,я с-ервис!il. Vi'$LЩ] BtudlQ 20Щ), бу­
дет с:rенериро:аЗlf файл Service .аS:ШХ. опре,целяющий сдед:У10ЩУro ЮIPеI{ТИВУ
<G'%@WebSe-rviС'е%>.

<:;'%',@ WebSer;u.Lce La,r,;guage-= "'011"


СоdеВеhiпd=-" ~ /Арр_ CodeJServi се:. 0(1;'" Сlа.i6:;=ПS-еl:1riсе" ~:>

Qбратите ВriИМш-mе 1щ то. 'что 9~CЪ :В:СПOJl'ЬЗуете.я: атри:бут'с.о·dев'еыlL. чтобы


ytШ3Q'rЪ им.я файла с IJ]Ю:rpамМRЬ(М :кодом С.#,. OI!Iр~еляющим саответствующнй
тип 1щасса (ЭТlJтфamr ПО умолчанию размеЩается в КЮ'aJIоre fI.:pf"--.:Соdе проента).
по УМOJr"lamrю Se:c-"ice. '1:'$ ОРР~д.Мllетсн T~

1Jsing ..1.~ystem:
using s.1~t;e-m. We,h.;
\»:sing Sp'tem. Web. serv:i..ce;;J ;
·I..lpin'i!! >SY5"t€!111. И'еЬ. $eI-:ч:Lcеs .:Р:rоt:о.еЩs;
['WcebS,eJ:-viсе (Ыа.m2s:рш::е =r'}Jt tp: / / temp.uri.@Ig{"T J
[WеhS~rVi.'СеВi.rrdil'н;r(с:.Щ-Ji't"rms';Г.Q
"" Ws;i.Рr0Цlе';з.ВавiоРrоНlеl 1) j
P1lbHc сlаs,з ~еr,,;i.О'е .~ E!y~t-el'\1, Web, Servi.ce~ •.WebServic:&
(
рдьНс ServiceJ)
{
1
( Wel1lo1e,t J"юd')
:p.lJblic string f)'el1owo:):,ld ()
{
:it!rt1,ll·f). :"Ней.о w.orld n ;
1128 Часть V, WеЬ·приложения и WеЬ·сервисы XML

В отличие от предыдущего примера HelloWorldWebService, здесь класс


Service получается из базового класса System.Web.Services.WebService. Члены.
определенные этим типом, будут рассмотрены чуть позже, а здесь достаточно под­
черЮIJ'ГЬ. что получать класс Serv ice именно из этого базового масса совсем не
обязательно.
Танже обратите внимание на то. что класс Service имеет два (та«же необяза­
тельных) атрибута. [WebService1 и [webServiceBinding]. Роль этих атрибутов
тоже будет рассмотрена немного позже.

Реализация Web-метоАа TellFortune()


Ваш Web-сервис XМL МаgiсБightВаll будет имитировать классическую гово­
рmцyю игрушку. сообщающую предсказания. Для этого добавьте в класс Service
следующий метод (существующий Web-метод HelloWorld () можно удалить).

[WebMethod]
public string TellFort\.Jne (string ЕопросПользователя)
(
string[] answers = ( "Будущее неоднозначно", "Да", "Нет",
"Вряд ли", "Спросите еще раз", "Определенно" 1:
11 Возвращение случайного оorзе'l'а на ВОnPОС.
Random r = new Random();
return string.Format("(O)? {l}",
вопросПользователя, answers[r.Next(answers.Length)]J;

Для проверки нового WеЬ-сервиса XМL просто запустите проект на выполнение


(или для отладки) в Visual Studio 2005. Поскольку для метода TellFortune () требу­
ется один входной параметр. aJ~томатически генерируемая НТМL-страница тести­
рования обеспечивает необходимое поле ввода (рис. 25.4).

1,",у" l' W"h ,е,у;"р, Mir.rn"uft 1"[('Гпеl ~xploreJ Г:;:-lrпJ~1

..
...
Service
Click ft~:,rj'~ 'for !!I comp~ete list of opelratl0l15.

TellFortune
Test
То , ..st <ее "per.'lon u"ng th. нттр POST prota~ol, Qlick th. ·InVDk.' bUtton,

!lОnРОСПОГlЬ-ЭОl!jатеГН;~j - будttТ IlИ a.тpeMOHT~.paeI5H еадостак .~y ~t;.~ходу~_ь~е7

I !nvoke I

Рис. 25.4. Вызов Web-меТОАа TellFortune ()


ГЛ<lва 25 , Web-сер8l.1сЫ XMt 1129
ВОТ воэ~ожный отпет :ыa воцрос "Будет ли отремонтировав водостои. в выход­
нpre!J"

<?ХЛ\1 vелsi&JЛ=" L. О"


eJ'JCOOing= " tJt t -8" ?>
<:;stl: i .!"_g xml !1З= "}Htp: / Itempuri . org! ">
Буд'е Т ли oppe""OH'I'j~p OBaH. В.Q ДФ;!tО 1< в ВblХОДJ-lblа" Эрял J1йt
</Btril1g >
Итак. к этому моменту вы 'соадаЛи два простых Web - <;:ерви(щ XМL: один .вруч­
ную. а д.рyrоЙ - с ПОМОЩЬЮ VisuaJ Studio 2005. Теперь у вас !'iCTb хррошщr: основа
для углубленноrо оОСуждения С(ЮТflетству:юш;их всшроСов. и наЧJ{ем ~l это обсуж­
Дение с рассмотрения роли базового Юl8Gса ~€h.sЕ'l''~iсе_

IIIСХОДНIiIЙ код . Фa:WIы фl1ме-ра МаgJtЕighШаllWеЫ>еrvlсе размеЩ8Н.Ь! 8 fI одка1 ал аге , COQrneTCTBY-


IQщем таве 25.

Роль базового класса WebService


в npоцессе. разраб отки сервиса Flе ll оWоrldWе11Sепгlсе вы:имели возщ)ж~оС'ть
убедиться. 'в том. что We.b-сервнс можно· щщучи:гъ IreJIосреДС'ПI.е нно из S ys te т.
bbject. Но по умолчанию 'Wt!b-с.ервисЬ1, с'{)эданные.в Vlэuai Studlo 2005; @ВТQмати­
S>y.ste:nl. We)::f. $er viee .We:bSerov ice·, ОПИСPJ-:IOН
чески П·О.i!JЧ'aIO"J'СЛ ив базового ltiШс.сз
осн.оиllых ч.n:енон эroго ТИJjа :kJlac-dа пре..п,лагшо'l:СЯ в таБЛ:.25.3.

Таблица
. 25.3. OO'-lOв~,ые ч·nеНЫТl-1па S'v'$tе!J).W~Ь.
- Sег\!iсеs_ W·еЬSеrv iс е

Свойство Оn~саi-tие

l.'фр1iс.8't.i оп ОID!:J0.пеЧlIIвает доступ i'\ объекту Ht t 'pAppl iсаtiоиS t а'tе Mfi. Тf:!ft(УЩегёJ
НПР-заnрсса
Contaxt Об.еСfreчивае.т доступ 1( типу HttpContext , инкалСУJmРУlOЩ8МУ все НТТР-со­
держимое . ИСnOflьэуемое Web-СерВ€рОМ ДМI НТТР-заriросов

Server ОбесnеЧL4вает доступ ~ объекту Bt.tpSe:tverUtil i t.y дnя текущего запроса

S.essi.oil Обеопе~щвает досту, п к типу F!t:tp S~sSlOI1St a te jj,;lЯ теКУЩего залРDса

Читает вероию протокола ЭОАР. ИGпоnьзуем'I~ Д!1J1 SОАР-эа"росов ~ Web-


оервиQV XML: э;го сво'ЙстSO ПОЯВtfЛQОЬ В .NEf 2.0

Вы, возможно. )"!l{t' ПОНЯЛИ. ЧТО для построения Web-серв:и-са. способного осу­
:щ~с:rщ:щть IIодцерзь.-Ry С'Боего СОСТОЛFlИЯ с ПОblОЩЬ'lО переменных Dpиложения и :се­
анса [см . главу 24}. nbl должны Подучить СОQТВетсТВ'уЮЩНЙ ~Иn из WеЬ$ел' ice , по­
СlЮ.lJЬнупоследдиЙ оnpe.делнет свgйм:ва App licat.ion и S~si.O'n. С др,Уг6Й стороны.
если II~! строите Web-сервис ЛМL. дЛЯ которото:не требуетсн ~ПОМНИ'lЪ" информа­
ЦИI(j) о внешних ШЩЬЭЬватt>Л'n"Х.не требуется 'и расширение WebService_ МЫ снова
P~CCMOTP~ процесr- прстроеЩ'[f)' Web-сервиса XМL позже. в ходе нашего обtужде-­
lЩЯ СВОЙ("I'ва Ел"й;> }еS",zsi О.h атрибута (WebMet.hod) .
1130 Часть У. Web-приложения и Web-сервисы XML

Атрибут [WebService]
Класс Web-сервиса XМL может быть помечен пеобязательным атрибутом
[WebService] (не путайте его с базовым классом WebService) . Этот атрибут под­
держивает ряд именованных свойств, первым из которых является Namespace. Это
свойство можно использовать для указания пространства имен XМL. используемо­
го в документе WSDL.
Вы. возможно, уже знаете. что пространства имен XМL используются для созда­
ния контекста применения пользовательских XМL-элементов в рамках конкретной
груrшы (точно TaR же. как и пространства имен .NEТ). По умолчанию среда выпол­
нения ASP.NEТ назначает для файла *. азrnх фиктивное пространство имен XМL
http://ternpuri.org. Аналогично по умолчанию Visual Studio 2005 назначает ДЛЯ
Narnespace значение http://ternpuri .org.
Предположим. что в Vlsual Studio 2005 вы создали новый проект WеЬ-сервиса
XМL с именем CalculatorService, определяющий следующие дна Web-метода с
именами Add() и Subtract().

[WebService (Narnespace = "httр:l/tеmрщ:i . OJ;'g 1") ]


lWebServiceBinding(ConformsTo = WsiProfiles.BasicProfilel 1)]
public class Service : System.Web.Services.WebService
{
[WebMethod]
public int Subtract(int х, int у) ( return х - у; )
[WebMethod]
public int Add (int х, int у) { return х + у; }

Перед публикацией своего Web-сервиса XМL вы должны указать соответствую­


щее пространство имен. которое обычно представляет собой адрес URL узла. об­
служивающего Web-сервис. В следующем варианте программного кода обратите
внимание на то. что атрибут [WebService) позволяет установить именованное
свойство Description. предлагающее описание вашего Web-сервиса.

[WebServi се (Description = "Чудесный Web-сервис калькулятора",


Namespace ="ht tp! Ilwww.lntertechTraining.com/ .. ) ]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1 1)]
public class Service : System.Web.Se~vices.WebService
( '" )

Свойства Namespace и Description


При запуске этого проекта вы обнаружите. что теперь автоматически сгене­
рированная страница тестирования не отображает сообщение с предложением
заменить http://tempuri_org. Более того, если вы щелкнете на ссылке Service
Description. чтобы просмотреть содержимое соответствующего документа WSDL. вы
увидите. что атрибуг TargetNarnespace соответствует указанному вами пользова­
тельскому пространству имен XМL. Наконец. WSDL-файл теперь содержит элемент
<documentation>, соответствующий указанному вами значению Description.
ГЛ81!ti 25. WеЬ·сервисtlt )(ML 1131,
<,w~cll : (~~C' IlТТlentatiojl Km1 fJЭ : w:s'dl=" littp': f /I9СhеИ:lа~. ~mlSQар.'О.r'!1/'Jlьd1i")'
Чу.цеcиШi W'lЮ-сеРJlис~.,:~qр21.
".!7iI·sdl ; t!lоиОThO'лtаtiсн>

Бы. наверное, уже цоrадались> что впоdiRе B03Мn1l!:В:0 IiоCТphЯ':tЬ 'l1oлъзuва~­


скую у-rюrи:rу (,lIзлри:мер. объеКТIJЫЙ li'jрщаер Web-сервиеа ХМЦ ДЛЯ ~:Ч-"'1'ы..вar
НИЯ:.Зйа"rr~mm, содеpmа1ЦИХСНВ 1\.ОнтекеТе;дI'ШiUlта. <d<Н::l:lшеrltа-tiQrI>_ ОДН<iЩО J3
большинстве случаев С()'J()J'fВетствующее З1:IaЧение б:Vil1,ет l1fСIШЛb8tiват~~ фаj;)JЩ!,!<[,
D:e ta1J ltt~s:ctlHelpGe:n.e ratt'r ..aspx.

Свойство Нате
llоc;uедиим йзр.асоматрl.ffiШ1:МЫХ здесь ~ОЙСТВ '1"Иtш WebS~rv i r;eA t,t,r i,bu t.e: i!В~
.ляe:I'СJI.ОВQЙСТВО Ndme, кот()рое: 'ИCIЮ:1IblIyetri'Ш,wllI укааamrя. нмеi-Щ Web-серllщ:а XMI"
ВИЛ1iIМOго nнеlIПD!lм nользоватеJU1м. 110,yьroлчанию 'ВНешнее ПМS!. Web-~I;epв:!il~ щек,
1'НЧНО имеН:И сюО':Г.ветс.ТВУЮЩffi'Q ·nша ЮL-"1:Сса lkOТf>pblbl. , в свОю Gчере~. 'ПО умщrча­
НИlO тШЯе'!:сfi И:МН Servic~). Однако еСЛИВЫjЮтmе ~отдe:rtИть~ JIМjfl1t,'1~GGCj..NE'1' Р'!'
~оо'r»етС'ТЙУJOщего wsDl,...,иМени. вы M~e й3мевить a~ [W8b6.~r·vici'lJ 'J<!J(,
Jiau RОI{а~Ш1О tJИЖe.

rW~~:~rvi <~ e 'lDeaaip'tiвn = '''-W JlЕi':~ We~) - ~ep:e~c R3.лы.."У:пяторг",


NamвaPAce = 'Ъ'ttр: i ;'~"""'~'C I'n 't e.,t't.ен:Ji'1тr;з' iTtin.g. oom/" I
·Naшe ~. ЦС31Сiдlаt.D r.w.еh&e:rЛ7iD'е" ) 1
1li1€дS,e l'v.:l с<iВi!Jф:r')g JCe'n form$!O = .~ LE'ToIill;fs,. Ba,;;;i clJ:!:;Q.fll~J.._iJ ]
pabli" ~1аБЭ iIe.Tvla~ : $1Pstarn~ WеJJ _ :S8Лl.i.ае-",. W~p$'e;r;v.iQe
~ '.' ]

Н" ри~, 2'5.~ nOKa~auft стрaниnа тестироваи:ия. ан.гQма'r'И't~СIШ сгеНерированная


.с :ормощыо tf~f'аijltWэ.d1!:lеlрGеhЕ'r,а'tРt . a.spx с учетом унааанного значе.нив а:г.ри­
бута r~...bS~tviceJ.

(1

-.(1-
-
".'. __ ""
."U
. I ~~ '~ ..~
b::iJ 1&1, 'tJ:! ,
. _. -- -'-
{1_,,~ ..л...,"",~_~
> .....,. 1;..,: -.,....,...е
"".~ ~ ~-
...,.,. 0;, ."Ъ3 ~
~
-~-
~
,'a .
~
. !=
а;;1 "-I~'" •
I!д ~ :"".,~ 11f<" ~

I§I
fJ

Са IclJlatorV\febService
~: ' 1,
•!

. й~~~~<,;1~~;J~~.~~!&r;;~rf~"J~~М~J]",)~'J~~=-~:fi~r.~~r~~~i~~ ~j-J.W't7Jf
.~.,.
1132 Часть V. Web-nриложения и Web-сервисы XML

Атрибут [WebServiceBinding]
в .NEТ 2.0 Web-сервис XМL может содержать атрибут [WebServiceBinding].
Среди прочего этот атрибут используется для того. чтобы указать соответствие
данного Web-сервиса XМL " базовому профилю совместимости Web-сервисов (WSI)
версии 1.1". Но что это значит? Если вы активно работаете с Web-сервисамиXМL.
вы должны знать. что спецификации WSDL в процессе развития этой технологии
изменялись. Вследствие этого вполне обычной оказывается ситуация. когда один
и тот же элемент (или атрибут) WSDL имеет разные интерпретации в разных си­
стемах разработки (ПS, WSAD), Web-серверах (IIS, АрасЬе) и архитектурах (.NEТ,
J2EE).
Очевидно. для Web-сервиса XМL это оказывается проблемой, поскольку одной
из задач разработки является упрощение способа обрабоТ1tи информации в меж­
платформенном мультиархитектурном и многоязычном окружении. Чтобы решить
проблему. для ПОВЬШJения уровня межплатформенной совместимости Web-серви­
сов организация WSI предлагает использовать открытыIe спецификации. В .NEТ 2.0
свойству ConformsTo атрибута [WebServiceBinding] можно назначить любое зна­
чение из перечня WsiProfiles.

public епит WsiProfi l e s


{
/ / Web-сервис не делает нихахих ЗаRВJIеЮIЙ о COBM8CТJOllOCTH.
NOГJe,
11 Web-сервис оБЪJПIJIllет о совместимости
1/ с базовым npофил8М WSI версии 1.1.
BasicProfilel 1

По умолчанию Web-сервисы X1v1L. генерируемые с помощью Visua] Studio 2005.


предполагают согласованность с базовым профилем WSI 1.1. Конечно, простое
npиравнивание свойства Confo rmsTo к WsiProfi1es .BasicProfilel 1 не гаран­
тирует. что каждый Web-метод будет действительно совместим с этим профилем.
Например, одно из правил ВР 1.1 гласит, что каждый метод WSDL-документа дол­
жен иметь свое уникальное имя (т.е. ВР 1.1 не допускает перегрузку Web-методов).
Но хорошей вестью здесь является то. что среда выполнения ASP.NEТ позволяет
определить различные уровни соответствия ВР 1.1 и сообщит об этом во время вы­
полнения.

Игнорирование проверки соответствия правилам вр 1. 1


в .NEТ 2.0 Web -сервисы XМL автоматически проверяются на соответствие спе­
цификациям базового профиля WSI версии 1. 1 (ВР 1. 1). В большинстве случаев
это хорошо. поскольку позволяет создавать программное обеспечение с самыми
широкими возможностями совместимости. Однако в некоторых случаях бывает
нужно пренебречь совместимостью с вр 1. 1 (например. при создании внутренних
WеЬ-сервисов XМL, когда совместимость не столь важна). Чтобы дать указание
среде вьшолнения игнорировать нарушение правил ВР 1.1. установите свойство
ConformsTo равным Wsiprofiles.None. а свойство EmitConformanceClaims - рав­
ным false (ложь).
ГП!}fI.(1 25, Web-ое'р:еисы XML 1133
ГWеЬ,SеЛ1i се ( DesCT± ptiOr1 ~ "ЧУIIеСНblЙ Wеt'-С,ервис каЯЬfrVЛR спора" ,
на:юезраСЕ' "","Ъttр: i /www.I11t.еrtеС.ЬТтщiпit.g.~·Ь!!1j .. ,
"Calc,Ilatc.r'Web:lierVH1t")]
NJJ.!Jl€ =
lWеJ.)SеrVi,СёВiщling- IConfo:cmsTQ = \'jsil'rofi 1е.з .NQ:ne,
Emi t-СоnfоrmanсеСl ai.m.s = f а 1 $,е") ]
publie сlз'Sз Se:n!i се : Зу,;; tem. W.eb. 5'eГllices • WebService
{ ... )

Ка:Р: и следует ожи.цэть,. от :шачt':}JИЯ, прис.ВоенноГQ с.'lЮЙСТВ:У Ew:lt'.CCH1 fО):'ф6 псе­


Clаiшs. заВ~СИl~ будут ли при публи:кации WSDL-описamrn WеЪ-сер.виса учиты~
ваться ссю6щения ~оответСТВ;ИЯ CBo~cтвa Сап fOT 1118'1"0. В .данном случае 'l"ш.рущеЮ'lе
пра9RiI вр 1, 1 раЗ'jJешается, но автоматичесЩ'l генерируемая страница тесUIPОВЭ­
Н:ИЯ l'Jce равно будетотображ:ать преiЦYIIрещдеНИБ.

Отмена проверки сооте8ТСТВИSl правилам вр 1. 1


ЧТQбы лолвостьщ ОТНЛЩ'1и-rь провер:ку соответ('твия ВР 1,1 ДЛЯ Web-
сервиса XМL. о]]реде,дите:в ео!(!твеТСТl3ующем фа.ИЛt' Web, со D f ig ·элеме:нт
<confOTman'ceWq.Ini щ~§>,
<.сслЕigпrаtiОll>
<.$ystew.. web>
<Iti!еЬБет\,j ces'>
<confOy'miiDoe:W"'IГl iTlgS>
<rе:пюvе naJDe;:' Ba:sic:1?rofilel_1 i />
</CQfI,t'orma'I'I ce'Wa.r: rJl Y.1gS>
<1 wer,S<;1i r'vi е;8З>
< (5 ' 1" З tem. wet,>
</СDпfigul'аti.ОГl>

Замечание. Атрибут I rlеhJSеЛl'iсеВi !1dlщj] можно Т<tIОК1Э ИСМЛ6зОВать Для QпределеНИ,А пред­
полагаемых qВЯЗ6Иконкреrнь.!х M61'OAOB по ЗН<I'IеНИЮGВРlI1ства Ыа те, Coot-веТСТElующие Г10Д­
РlOБНос:ти МОЖhlО blаитИ в до~умеНl'аЩIlI\ ,NET Fram.e.work 2,0 SDK

Атрибут [WebMethod]
Атрибут .L WebM~etl"jod] ДQлжен указьшаться::для каЖДОГQ метода, :который .вы хо­
тите Сдt'.J1ать досryпным в рамках данного Web-сер:виса XМL, как и большинство
ДР)'ГИХ атрибyroв. ТИП wеьм'еth':'dАttIiыiеe мФкет иметь це.1'lЬJЙ 'РЯJ'I; необяаатель­
ных имеНОВaI-Пrьti СВОЙСТВ, Давайте рассмотрйм IULii'дУЮ из 'НмеЮ!цИхся здесь ВОЗ­
мо1Ш-lOс-гей по оч:ереЩ1.

Описание Web-метода с ПОМОЩЬЮ 'сво:йства, Description


ItaK и в случае атрибута IwebS8Tllic.e]. свойство Dез.с:r:iрt ion атрибута
[WеЬИеt hod] позволяет описать фym:uщона:Jп,ные возможности Web-меТОДа.

[W,ebMethod, (DeScriptiOn = "БЫЧli1т.а:ни:ё делых ЧМСел.") J


1134 Часть V. Web-ПРИЛQжения и Web-СерВI1Сbl XML

public int Stlbtract (int х, int у) ( return х - У; )

[WebMethod (Description = "Сложение целых чисел.") ]


public int Add(int х, int У) ( return х + у; }

При указании свойства Description в пределах атрибута [WebMethod]


в WSDL-документ в контексте имени метода Добавляется новый элемент
<documentation>.
<wsdl:operation name="Add">
<wsdl:documentation xmlns:wsdl=''http://schemas.xmlsoap.org/wsdl/">
'ВiII10.пнllе'1' с,,"ТоJtеиие ЦeJDOIX чиееа.
</wsdl:documentation>
<wsdl : inpu t message="tns: AddSoapln" 1>
<wsdl ; output message='1 tns: AddSoapOut" />
</wsd l:operation>

Устранение конфликтов имен WSDL


с помощью свойства MessageName
Одним из WSI-правил вр 1,1 является то, что каждый метод в WSDL-документе
должен быть уникальным. Таким образом. если вы хотите. чтобы ваш Web-сервис
XМL удовлетворял спецификациям вр 1.1, вы не можете использовать перегру­
женные методы. Однако для примера предположим. что вы bce-т<Щи использовали
перегрузВ}' метода Add (). чтобы вызывающая сторона могла передать как два це­
лых числа , так и два числа с плавающей точкой. В результате вы должны увидеть
следующее сообщение среды выполнения.

Боth Single Add(Single, Single) and Int32 Add(Int32, Int32) иsе the
message пате 'Add'. ИБе the MessageName property attribute to specify
unique о! the WebMethod cust ommessage патеs for the methods.
(Сообщение гласит: Single Лdd(Siпglе . Single) и Int32 Add(Int32. Int32) используют
одно и то же имя 'Лdd' для своих сообщений; используйте свойство MessageName
атрибута WebMethod. чтобы указать для этих методов уникальцые пользователь­
ские имена сообщений.)
Здесь лучшим решением явдяется отказ от перегрузки метода Add ( ). Если же
перегрузка необходима. для устранения конфликтов имен в документах WSDL
можно использовать свойство MessageName атрибута [WebMethod].
public cla ss Service : Systero.Web.Services.webService -
{
(WebMethod (Descri pt ion = "Сложение чи с ел с плавающей точкой.",
ИеззаqeName = "AddFloats")]
publ ic float Add(float х, float у) { return х + у;

[WebMethod(Descript,ion = "Сложение целых чисел.",


МеsзаqеNamе = "AddI n ts") ]
public int Add(int х, int у) [ return х + у; }
Глава 25, Web-сервиCbl XML 1135
Пuме ЭТОГО тенерируeмъ:di WSDL-докумен:r будет В'l1утренне €СЫлаться на каж­
дую из перегруженны:х ~ерс.и:Й метода: Acld (j по YJЩI(адЬным именам (AddFl oats и
AddlDtS)o Но:с Т<ТЧRИ зрения ш'еНТ8 Ji,la стороне Iшиен-tа будет существовать только
один перегруж,енный метод Add () .

ЛОАЦержка данных состоянияWеЬ-сервисов


с помощью свойства EnabIeSession
Вш, :fщ:верное. ]]Щ4НИте из главы 24 о том. что свойСтва Applicat,ion и SesslQ'n
ПО3В(,)ЩIЮ'Г WeЬ-ириложени:ю АЗР.NEТ поддерживать ДанНые-состоя:ния. Web-с~рJIfJ­
сы XМL об~cnе"ЩВают Те же :вОЗМОЖilОСТИ с помощью базового , класса 'S'Istem. Web.
Ser'1 ices. We1;1Service, Нanример. nреДIIOJIОЖИМ. что ваmWеЪ-сервис шшъ:н:ул.ято­
ра rюдд~живает пере:менную УРОВНЯ IIp.иложения (которак, таким образом, ДО.JDl(­
на быть ДЩ:1)Т:Iffiой moбому сеансу'). содержащую эна:ч:ение PI. кап по:казщm H~e.
pnr., lic ~ lass CaloWebZecvice: System, Vleb. Service$. W'epServi~e

/1 ЭтОТ 1Web-И91l'О;Ц обеспечиваew д.оо~уп JC переиеваой ЗiшplеРI


1/ у:розии при.пО1l!еllИR.
[WebMec:ho d (Itеэсriрti ОП = "Получение знач,енив: Рl . ") )
public float GеtБiщрlеРI()
{ retu~·n (ПО-dt) Applic.ation [1tSiJDplePI "] ; ]

На"шльн.ое значение переменной SiПlрl е 'f'I уровня приложениЯ' можно устано­


ВИТЬ в 06ра:БО"I'"Чике Арр 1 i-cd ti.on _Б t -art (), QпределеJЩОМ в файле G 10Ьа 1. asax.
Добавьте в свой про.ект rлобальНБfЙ ющсс IIIщложения (ще.1Дщув правой кнопкой
МЬ1Шй на mппограм.ме lIpоекта в окне оБО3ревателя реmенШi: и. вь(брав Add New
Нет из ПОJlВИl3шегося менюl. а :затем изменате A,pplication эtаrt () так. кш_
предлагг.e-rся: 1tиже.

<'j;@ Appl.±cation L&tlgt.1age="C#" %>


<ВСС ip.t :runat= "servex"''>
void АррliсаtiОП_5t.аrt (Object seJ'\l.;i er, ЕVеrltАr:gз' е}
i
АррИсаиог. ["SiтТlplePI"] = 3 .1I1F;

8доб'.UIО}( н поддер1Кне перемен'Ных уровня n 'рlPIощедия МОЖ;НО ИСЦОЛЪЗ0вать


СВОЙСТВО SеssiQП':lJдJl поддержки, ееансо:вой информацщ,:r, Дrщ примера решmзуИте
метод Sessi .on_Sta.rt () в файле GIQb:al.asax таж., ЧТQбl,il R'эщдblЙ;'!арег.иетрйра.
ва:mIblЙ nО./IЪзователь :идентифицировался случайш>IМ эиа ~Jение!\1'_

<%@ Аррliсэtiоn Lа:ЩР;1аgе="с4t~ %>


<S'c:ript: ГЙГliJt=" S'eTu·er" >
. -"'"
I><oid 5essi ,>n _ Зt.аrt (Qbj ect sender, EV€ritA!.°g s е)
{
1136 Часть V. Web-приложеНИR и WеЬ-сервисы XML

/ / Ч'J!обlol сдenаNo дос'J!УIlНlollOl сеаисовые ДiUIЮoIе Web-сераиса,


/ / присаОЙ'1'е каждому поm.зоааТemD c.nyчaйJtое ЧИCJIо.
Random r = new Random () ;
Session ["SеssiоnRалdоmNumbеr"J = r.Next (1000);

< /sc.ript>

С целью проверки в рамках класса Service создайте новый Web-метод. возвра­


щающий присвоенное пользователю случайное значение.

public class Service : System.Web.Services.WebService


(

[WebMethod (EnableSession = true,


Description = "П олучите ваше случайное значение!") ]
public int GetMyRandomNumber ()
{ return (int) Sess ion["SessionRandomNumber "]; )

Заметим, что здесь атрибут [WebMethod] явно устанавливает для свойства


EnableSession значение true (истина). Этот шат необходим. поскольку по умолча­
юпо ДЛЯ любого Web -метода контроль его данНЫХ сеансового состояния отключен.
Если ВЫ теперь запустите два или три экземпляра браузера (чтобы сгенерировать
множество идентификаторов сеанса). ВЫ обнаружите . что каждый зарегистриро­
вавшийся пользователь возвращает уникальное числовое значение. Например ,
первый пользователь может получить следующий ХМL-код:

< ?xml version="1.0" епсоdiлg="utf-8" ?>


<int xmlns=''http: //www. IлtеrtесhТrаiпiпg. com/WebSe.rvers">93 l< /int>

в то время как второй может получить значение 472.


< ?xml version="l.O" еЛСОdiлg="utf-В" ?>
<int хmlПS="httр:/ /w ww.Iпtе.rtесhТrаlпiпg.соm/WеЬSе.rvеrs">472</int >

Настройка данных сеансового состояния с помощью Web.config


Наконец. напомним. что файл WеЬ.солfig можно изменить с тем. чтобы в нем
указывалось место. где ДОЛЖНЫ запоминаться данные состояния ДЛЯ Web-сервиса
XМL. Для этого используют элемент <sеssl0ЛStаtе> (описанный в предыдущей
главе).

<sessionState
mode="InProc"
stateConnectionString="tcpip=127.0.0. 1 :4 242 4"
sqlConnectionstring="data sou.rce=127.0.0_1;Trusted_Connection=yes"
cook ieles s= "false"
timeout="20"
/>

Исходный код. Файлы примера CalculatorService размещены в подкаталоге, соответствующем


главе 25.
Гnа:ва 25 . Web-0ер:~исы XML 1137

Язык описания WеЬ"сервисов (WSDL)


в пос,леiUЩX }-]~(О.nЬД-ИХ црим:ерах 8Ы могли ВЩДе:Тъ ОЩeJIbllЫе фрагме1-lТыWSDL~
кода. 1-la:rLOМНИМ. что WSDL- это оонованная на XМL 'Грамматика. предназначeI-l "
Ra:n ДЛЯ оuиcmпщ ВОЗМОЖностей В3a:I'L~одействил: вuешш·lX клиентов с WeD-МtlО­
дами, дt)стушlыми по дanнощу адресу URL в рамках ~OTO ИЗ подцерживаеМ1iIX
nРОТОП:О!JОВ СВЩН1. Во Многих О1'.ноx.nеFiШIX WSDL-документ может рассматриваться .
I'ШК Рконтракт" m,eJ-Кд.У I<лиентом We.b- серви:са и са:мНм We.b-сеРВИСJ'М' Это еще один
м(','таязьщ_ В частно('ти, WSDL ИСПШll!~ует.ся ДЛЯ оrmсан:ия слt'J.JYЮП1ИХ хара1.тери­
стик любого доступnот-о Web- метоАа.:

• ИМЯ Web-Metтoдa X1\itL:


• число, Т~Ш И порядок <:ледrш'ави.я параметро'В (ecffi') таковые имеются)';

• !
тип ВО8вращаемOl'О зна ЧСНЩj если ТW;:ОБое предУCJ\!Iо;rpено} :

• условия вызова НТП' \; Е:Т. HТIP РОБ'I' и ЗОАР.

В (jолыттинстве случаев WSDL-ДО~УМ:e,l'IТI:Ц генерирyкrгся авТОМ,а;гщчеСIШ соот:ве,т­


ствуюrЦИJ\:{ \Veb-с<:,рвер~м, Н.ацомннм. что при добавJ;leниJiI суффИНС,а ? wsd 1 надресу
lТRL, у.казьцщюще1Щ7 на ф щш ". asmx . Web.-сервер г~нерируе''( WSDL-Аохумент ДJ1JJ
уи.азалнОГ<;l Web-серв~('а XМL.

ht tp- : ,' / l f3ca l bDst / Б оше WS.l t hеW$ . aS ffi)i?'wsd l

Но если IIS а.в']·омаmчес:ки т ен(!рирует WSD~-:докyмeFlТ ДJIJ:J данного Web-сервш:а


XМL . aa~ тогда нужно г.11Убокое понимание синтансиса ге.нерируем:ых WSDL-дан ~
.ных? Ответ обычно .зависит от тото. как BailI сервис БУДет ИШlОльзова:гься внешни­
ми I1p:илож.ениями. В случае WеЬ~сервисов XlVIL. nредяазнач:енr'lыхляя "внуrреюre­
го' использования. стенерированноl'О WёЬ-сервером WSDL-кода буде'l': нах правило.
достаточно.

Между тем. ВПОJlне 1ЮЗМОЖИО начать разрабоТRУ Web-сервиса XМL с соадaюm


WS])L-ДОRумент.а вруЧ!Iy1b (об этом уже rоворилосъ вьпnе') . Thaвная идея нача­
ла разраб(Р'IQj с с~даН1fя WSDL· до:«.умента связана с во:щ:юсами соВмеС ·Тимосrи .
I3сш.омните о ТОМ, ЧТQ до поя:вл:ения. cnеUИфинаций WSI ра3.11ИЧНhLe инструменты
построе,,1ИSI Web-сервисо,U передую генерм:ровап-и несовмеСтимые WSD~оrшсапия .
Если иачщтать разработку с WSDL-НQда. вы можете пос-троить до«ументтак, как
требуется.
\'Са.н: вы можете дога;даты::я, Д.rJ.Jf, начала разработки Web-сt;рвиса XМL с соЗДaюm
WSDL-ДОRyМeflта треБУt:ТG1 очень хорош~е з:нanие граммат.иЮl WSDL. обс)'ждеЮlе
:КО'l'QРОЙ в 'контексте этой гnа,вы Щ:' предусмотрено. Но r-tы рассмотрим ба.зовую
CТVWYPY WSDL-донумента. Рвзо(Sравmщь в ОСН0вах, вы сможете ОЦеНИТЬ ПОJiЪЗу
УТl:fДИТ1>1 RОМаНДElОЙ строки w "di. . ~ze .

Замечание. Самую свежую I>tнфор.мацию РЯiЭыке WSDL MO*fi~ Найти на С1'раНt.щах


http: //www ,vo. org/ tr /wsdl .
1138 Часть V. Web-приложения и Web-сервисы XML

Определение WSDL"документа
Действительный документ WSDL открывается и закрывается корневым эле­
ментом <dеfiлitiолs>. В открывающем дескрипторе обьгпю определяются раз­
личные атрибуты xmlns. Они задают пространства имен XМL, определяющие раз ­
личные подчиненные элементы. Как минимум. элемент <definitions> должен
указать пространство имен, где определены сами элементы WSDL (http://s c hema s.
xmlsoap.org /wsd 1). Для того чтобы быть полезным, открывающий дескриптор
<definitions> должен, кроме того, указать пространства имен XМL. определяю­
щие простые типы данных WSDL, типы XML-схеМbl. элементы SOAP, а также це­
левое пространство имен. Например. вот как выглядит раздел <definitions> для
нашего Web-сервиса калIiliYJlЯТора.

<?хт 1 version="l.O" encoding="utf-8" '?>


<wsdl :dafinitions xmlns: soap = ''http:/ /.s chemas .xmlsoap. org / wsdl/ зоар/"
хmlпs:tm="httр: /l mi сrоs оf t .с ощ / wsdl/mimе / tехtМаtсhiпg/"
ХПllпs: sоарепс= "rlttp: // schema s. xmlsoap. org / з о ар/ encoding /"
xmlns: mime=" 'h t tp : ; / s c hemas . хщ 1s аар . o .r:g /ws·dl / mi.me 1"
xmlns: tnS="l1'ttp: //www.lr1tеrtесhТ.r:аiпiлg . сош-j"
xmlns: s=''http: / /www . w3. o.r:g / 2001/XMLS c hema"
xml пs: soap12=" http://schemas . xmlsoap. o r g /wsdl/ sоар;12/"
xmlns:http=''http:/ / schemas.xrnl soap.org/wsdl/ht t p/''
targetNamespace= .. http://www.IntertechTraining . c om/ ..
хmlпs:wsdl="httр://sсhеmаs.ХПi1S0ар.оrg/w.sdl/">

< /wsdl:dаfiпitiопs>

в контексте корневого элемента вы можете найти пять подчиненных элемен­


тов. Общий вид WSDL-документа должен быть примерно таким.

<? xml vers.l on="l. О" encoding= "utf-8"?>


<wsdldefinitions ... >
<wsdl;types>
< ! - - Список 'l'ИПо:а, доступных дпя даиноI'О Web- сер:аиса - >
<wsdl:/types> <wsdl:me ssage>
<! -- Формат сообщений ->
<w sdl:/message> <wsdl:po.r:tType>
<! - - ИНформация: портов ->
<wsdl:/poTtType> <wsdl :binding>
<! - - ИНформация связывания ->
<wsdl:/b ind ing> <wsdl:service>
<, -- Информация о саном Web-сер:аиса XМL ->
<w sdl:/s e rvice>
<wsd1:/defin i ti on s>
Как и следует ожидать, каждый из этих подчиненных элементов будет содер­
жать дополнительные элементы и атрибуты. уточняющие описание имеющихся
возможностеЦ. Давайте по очереди рассмотрим наиболее важные из допустимых
узлов.
Глаза 25. WeЬ·сер·висы XML 1139

Элемент <types>
Сначада мы рассмотрим элемент <tУ'Р·еэ>. который <;:одержит О]]ИС'.анин.ВСf'Х
ТИПОВ данных. npедпarаемых Web-сервисом. ~ы. возможно, апаете. -что :я.зьш. XМL
сам определяет Рпд. ~ба:зо:вых" ТДI!ЮВ дaнн:.l:im(, И BC~ ою-r определены.в рамЮl'Х про­
стра:нст:ва: .JIMeH XМL b.ttp~! !-w:w·w.W3.- DrЧ (,200] !XMLSGh~ma (которое доШЮiо бьtrъ
указаtiо n lюнте«сте Kop:t:teвoro элем:eJJТa <de:[ i n i ti ОЛВ ». Возъмем. например. ме­
ТОД Sub1:ract _\} нашего Web-серВИса калъ-кyrщтора, ИlVlt>ющИЙ два входных параме­
тра ЦeJJОЧНС.iК!itНОГО типа. В терминах W5DL тип SY$~e:rn .lnt32 среды СLП опИсы­
вается в нонтенсте элемента ~сс rnрl ехТур.е:>.

<s:elernent паюе="StiЬti.асt">
<9 ;·comple.xType>
<s:sequence:>
<"$: el em-er1t тninOpcur::;="l" lTLaxOcc-UI5~"1" nалхе="х" t ype="s:int-" />
<s:eleHl\S:nt шi.nОс:сurэ=,11{\ rnaz'()ccUJrs=·'ll'f ni1l1n€="y" t:ype="s:i.nt" 1>
</ s : seglI€Tl,ce>
</S:С: Gmр.lех'Туре >
<:,'$: еl'€!щеl1t>

Целое число. возвращаемое методом S1,lbtr:act О. тщuке описывается в рамках


элемента <types>.
<"5: е-1 e:men t лаmе~ "SUbuact:ReSj?onse" >
<э: (;DmplexT1j-р!ii~
<8 ; seqUej"1.,~e>
<'8: е l -erne.nt
miIlOC.C'UJ:s= "l·" maxOceur:s="l'" паmе=" S.ubtrесt1teзu·lt" type="s: int"- />
</s:sеqщmСЕ>
<1 s ~ compl 'E''IТype>
</ в: ",}ешг:IJ t>
Если вы И1Ioreете WеЬ~ме't(}д. возвращающий :илй ПОЛУЧ-aIОщ,ий польэоватеJIЪCRИС
типы двнных. они также появя'Гся в контекСте элемента -<сощр].ехтур е> , Детали
ТОГО. Н.aJ{ С ПОМОЩЬЮ Web-ме,тотl,Э. сдела:т.ь доступными nОЛЬЗО1Щ,eJIЪCкwе типы да:н­
нщ .NEТ'. мы рассмотрИм позже. для npим,ера предnоло!ЖИМ. что вь'! определили
Web-мcrод. возвращающийструнтуру С именем РЬiл.t.

publJ t; str\J.ct Point


I
рclэlJG iлt )/.;
publi,= int }";
publiC str-1ng p ·oi lJ t.NCiJ1!e;

<З : cQrnpl -е.х'Iуре name="Point">


<'5: sеqu>=П'с'е>
<~ ~ ,elemeГl:t m1:rTOcCUIs='lf't I(lз"о.ССUI Э~
"1 ~ f1aJlJe="x" t уре'" "з: i-Clt n 1>
<'5 ': e lement m.l!1.0CCL1rs= "1" rnaxOcclJrs='''l" nате="у" t ype~ "~~ int" />
<s :еlеmелt
min Qс.сurз=-"О" md.xOccurs=·" l ·" /Jа;ще="рointNаше-" type="s:string" 1>
<./в :sеquелсе>
</9: c_omplexType->
1140 Часть V. Web-приложения и Web-сервисы XML

Элемент <message>
Элемент <message> используется для определения формата обмена эапросами
и ответами данного Web-метода. Поскольку один Web-сервис позволяет передачу
множества сообщений между отправителем и получателем , одномуWSDL-докумен­
ту поэволяется определять множество элементов <mеээаче > , Кая правило. в этих
определениях используются тиuы' указанные в рамках элемента <t.ypes>.
Независимо от количества элементов <message >. определенных в докумен­
те WSDL. они обычно "upисутствуют" парами. Первое определение представля­
ет входной формат сообщения. а второе - выходной формат того же сообщения.
Например. метод Subtract () Web-сервиса CalculatorWebService определяет сле­
дующие элементы <message> .

<wsdl :message name= "SubtractSoapIn">


<wsdl :par t n ame= "paramete rs " element="tns:Sub tt'a c t" />
</wsdl :message>
<wsdl : гпеssаgе паmе="SubtrасtSоарОut">
<wsdl:part name="parame ters" element="tns:Sub t ra c tResponse" />
</wsdl :message>

Эдесь вы видите только связь SOAP соответствующего сервиса. Как говорилось


в начале этой главы. Web-сервисы XМL могут вызываться с помощью SOAP или
Н1ТР-методов GET и POST. Но если вы разрешите связь НТГР POST (соответствую­
щие объяснения будут предложены поэже). генерируемый WSDL-код должен про­
демонстрировать следую.щце данные <message> .
<wsdl : message name="SubtractнttpPostIn">
<part name="nl" type= "s:st ring " />
<part паmе="п2" type="s:string" />
<ws dl:!message>
<wsdl: message name="SubtractHttpPostOut" >
<part name="Body" eleme nt="sO:int" />
<w sdl : /message>

Элементы <message> сами по себе не слишком полезны. Однако на эти опреде­


ления сообщений ссылаются другие части WSDL-ДOIо/Мента.

Замечание. Не все Web'MeToAbl требуют и запроса, и ответа. Если Web-метоА является "односто­
ронним", для него необходим только элемент <ще.ssаgе> запроса . Обозначить Web-метоА,
как односторонний, можно с помощью атрибута [SOapDOCllmen tMethod] .

Элемент <portType>
Элемент <portType > определяет различные связи. которые могут возникать
между клиентом и сервером. и каждая такая связь представляется вложенным эле­

ментом <opera tion>. Несложно догадаться. что самыми типичными операциями


здесь должны быть SOAP. Н1ТР GET и НТГР POST. Однако есть и другие операции.
Например. односторонняя операция позволяет клиенту отправить сообщение дан­
ному Web- серверу. но не получить ответ (зто похоже на вызов метода без ожидания
возврЭ1Цаемого значения). Операция "требование-ответ" позволяет серверу отпра-
Глаеа, 25. Web-сеР8ИСЫ XMl 1141
В;ИТ~, аапрас во Bpe&UI ответа юшек'га (что М@ЖUQ рае~ТРИ1Jать , ЕЦ8 д~шо;лнен:ие
оперщин ·'запрос-ответ';).
Чтобы npоилдюстрировать формат необ:язаТtЩЬJ{QГО вложенного элемента
<opel.""3tion>. рассмотрЮ1 WЭDL-опред.еление дш'{ tdетода. $ubtrac1; О.
<w5dl : р со !Т:ГуРе n'am.E!= ItCa.lcu1ato~WebS.rviceSoap">
<wSdl: op.era t i ОТ! l')amе="StШtrасt" >
<w.s:dl ~ iпрut mеssаgе=Пtю,s; SUbt.J:actsoapIn'· / >
<~эd l :,;)utP 'LI't m1:'!·ss.age="tDв:SoЬtractSo.apOut" />
< / w$dl:GperatiGn>
<WS'dl. /port TYJ?E\>
Обр.атите внимание.Щ1 ТО. }Сан злемеНТ~ <input> и <outPlJt> есылаютсл на со­
ОТветствующее имя сообщения. опредеJJенное в рамках элемента <litеSS.а gэ>. Если
бы для метода S"Ub'trq.~t () быдl разреше~ НТТP~Meтoд РОБТ • .вы бы УВЩ!iели сnелую­
щий Дополнит~.l1ЪНЫЙ 9демент <: ор ед at iOJl >.

<wэdl: p Or t Type паmе="GaJ cula\:'orWe'bSetvicefft!:pFQoSt">


<-wsdl ,оре r,a:.t iоп паmе=fl.SuЬtrасl;.":>
<wsdl: iПР\J t meS Sf1ge="sO :$ubtractнt·t,pPost.In'· />
<wsdl :outpu t I!"r€эsa.tjе="sО:ЗUbtraоtвttpРОltOu·t" / >
<ws;дl:!-Qреrаtiоn:>
<wэdl: /port Туре:>
Наконец, УЧЦJте Tf), Что если даНный Web-метод описа» с помощью CBO'J:;"
м;ва: I:)е ,sс-riрт.iсn, ЭЛt:lМt1нт <G1H,ration.> будет содержать· вложенный элемент
<doc1:1me n ta·tio ц:>.

Элемент <binding->
отот элемеl;IТ yJШзыва{'т точный формат обмена GET, POST И SОЛР. ЭТQ самый
··Jl.lliоrословНPIЙ~ И~ всех эле.менТОВ.• содержш:цихся в :КОНТексте lФрневоrо элемеата
<defini tion>. Вот, l-Janfщмер. ШIpeделеНйе ·9леме1-l11З. <Ьiлdiпg> с ОIШсщшем того.
KaJtвызьшающа:я: стороц_а МdЖет взаимодеЙствовать с Wеь'-меТQДОМ Myr-1et.h od ().
используя SQAR

<wsdl :Ьi пdiл g- пате =" ·Са l си} atorWeb5.erv'i lDeSoap12"


type= n trtliI : CalcUlatorWeЬS~vioeSoap" >
<s"Oap12: bindiJ1g t rЮlsрсrt= "bttp! //schema.s . xnU1Ioap. org/sQ.a:p/http" 1'1>
<....'s·dl :о:ре rа ti.QЛ nэ.mе="SUbtr.асt">
<s·oap1..2 :
.орегаti= soapA<:,ti,bn="h·ttp: I/wvи. IntertechTraJ..ning.com/Subtract."
stу lе=~ dОСШlleI1t" 1>
<W5c!l: i Olput>
<БО'ар 12 :Ьody uэе ="l.i;terаl .... ,'>
<./w.$dl; ir,p\Jt>
<I~$· cll : \)utput>
<soёip12:body '.lsе=Пliterа!" ) :>
</wsdl ~ ОJ.1tрцt»
</wsdl:operatl.oJ"!>
<i v1s·dl: bi l1diпg>
1142 Часть v. Web-приложения и Web-сервисы XML

Элемент <service>
Наконец. у нас есть элемент <service>. который указывает характеристИRИ
самого WеЬ-сервиса [например, его URL). Thавной задачей ЭТОl'о элемента являет­
ся описание множества портов, открытых данным Web-сервером . Для этого эле­
мент <services> может использовать любое число ВJюженных элементов <port>
(не путайте их с элементом <portType». Бот IЩК выглядит элемент <service> для
CalculatorWebService.
<wsdl:service name= "CalculatorWebService" >
<wsdl: documentation xmlns: ws,:il=''http://schemas .xmlaoap .org/wsdl/">
ЧУдесный Web-сервис хальКУnRтора
</wsdl:documentation>
<wsdl: po):-t name= "CalculatorWebServiceSoap"
bind_ing= ,. tns :CalculatorWebServiceSoap" >
< зоар : addres s 1. оса t i on="http; / / localhost: 1109/CalculatorWebService/
Service.asmx" 1>
</wsdl:port>
<wsdl:port name="CalculatorWebServiceSoap12"
binding=" tns : CalculatorWebServiceSoap12" >
<soap12:addr e ss location=
''http://localhost:ll09/CalculatorWebService/Service.asmx'' 1>
< /wsdl :port_>
</wsdl:se rvice >
Итак, как видите, WSDL-код, автоматически возвращаемый сервером IIS. не
является сверхсложным. но. поскольн:у WSDL представляет собой грамматику на
основе XМI" этот код достаточно "многословен". Тем не менее, теперь вы должны
лучше понимать роль WSDL. таи что давайте- рассмотрим немнOl'О подробнее про­
токолы связи Web-сервисов XМL.

Замечание. Напомним, что пространство имен System.Web.Services. De scription содер­


жит множество типов, которые позволяют nporpaMMHo читать и обрабатывать "сырой" WSDL-
код (можете проверить сами, если вас это интересует).

Снова о протоколах связи Web-сервисов XML


Строго говоря, Web-сервисы XМL могут использовать ДJlЯ коммуникации любой
RPС-протокол (например. DCOM ИЛИ CORВA). Однако большинство Web-серверов
встраивает соответствующие данные в тело НТГР-запроса и переправляет их адре­
сату. используя ДЛЯ этого один из трех базовых способов связи (табл . 25.4).
Хотя каждый из подходов обеспечивает один и тот же результат (вызов Web-
метода). от :выбора протокола зависит то, какие типы параметров (и типы воз­
вращаемых значений) могут пересы.а.тьсяя между заинтересованными сторонами.
Протокол SOAP предлагает наибольшую гибкость, поскольку сообщения SOAP по­
зволяют осуществлять обмен сложными типами данных (а также двоичными фай­
лами) между:вызьmающей стороной и Web-сервисом XМL. ОДНaIiО для ПОЛ1юты да­
вайте выясним роль стандартных НТГР-методов GET и POST.
Глава 25 . WеЬ·сервисы XML 1143
Таблица 25.4. Режимы щtязи Web-сервVlсов XML
~)I(ИМ СВR3И Описание

НТТР-мет(!д 'GБТ В режиме обмена БЕТ пара:метры дЬбавлпЮТGЯ к строке запроса дflНM9T';> URL
НПР-метад. POST В режиме обмена E'OST данные BCTP!1-иваЮТСR в ЗВГО1l0ВО~ HTTP-сообще.I'IИЯ.
а tie добавляются к отроке запроса

SOAP SOAP .5ЦщяеТ!JR ПРОТ()t(ОlЮМ CB~~, опреД8)'lIЩ')ЩИМ лраВIA'ла передачи' данных


и 'Sbl30вa метоДОВ ~ сетИ с: помощыо XМL

Связь нпр GET и НПР POST


ХОТЯ GET и PQ8T кажутся I1pИБЫЧНbINIИ JЮRСТРУХIЩЯМИ, етот метод пересылки
JЩl10СТШОЧНО mбо:н ДЛЯ оБCлyЖивa:fIШl1'aRИX слt.JЖНЫ.е эдементов., 1{ак СТpyRrypы И
классы, пр.и ИСПОЛЪЗОБаНии GET и' РOS Т вы можете взаимQЩ"ЙСТ1Щ13ать с We'b-M eтo­
цамп, используя только типы, уБазанные в 'Табл. 25.5.

Таблица 25.5.• Типы данных, подцерживаемыe м~тодами GET и POST


Типы данных Описание

Перечни GET 11 POST поддерживают лередв"tу ТИГЮ6 3 ys t ero .EnUrn. .NEi. fЮ.CК.ОЛЬКУ
ЭТИ ТИni:/l лреДстаВ!1ЯIPТ<;:Я в .~идs стаТИ'iеских CТPOlli08bJX КОНОЩI1Т

ГiPOCТbJ8 М6 GС'ИВ'Ь1 Вы МОЖЕпе' ИQП\>ЛЬ30Е\ЭТЬ масс>ивЬ! любых ПРИМИТФ1ВНЫХ T,wn'OB

('тро",и GET и РО И осущестапяlPТ nepeAa'lY люБы( 'IИСЛОВb,lХ даНIiЫХ в виде crpo·


ка8.ЫХ M~pl(ep08. Строка ЭJJ,6СЬ на самом· деле обозначает СТРOJ<Овое пред­
ставление ореды CLR ДJН! T8kL<lX riримитиеов , как tnt 16, I nt J 2, r n t ·C4.
В ооl-е.ал., ':::i ngle, D ou'ы,. lYec ill,al и Т. Д.

По умолчан;щ{) НТГР-евлзь GET и :PO'ST не разрещ~на дmr удаленного въ1З0ва


Wеh-сервисов XМL. Однако lПТP-сsязь POST щс.тивиэирована д;ля вы0в3a машиной
локальвых Web-сервис @в (-па самом доое именно ЭТОТ режим испольаует автомати­
чески генерируем:ан страцица теСТИРО:вaRИll). Эти устаlЮНRИ yItaзьшаю:гся в файле
шасГЙ п е . с· ф !) fi.g С пщщщыlo эяемента <ptCltoce,ls>. ВОТ кап выг.шщит соответству­
ющий фра:гМ~Jf1.~

<! -- В фaйJпе maChine. conПч -->


<·web;se r v l ces->
<pr:otocol s >
<add n аще="f/t tр8@ арl.2" / >
<а. аа I'j·ame= "Ht tpSo a p" />
<.a:cld ГJ. amе="D осшnепt ·аt.i <УП" 1>
<!,-- ~ ~/РО$Т О!1'юwчеНк! - ->
<!-- <add ~"зttрРоst~/>->
<! -- <add rlЗme=ПНttpGet"I> -->
<с! -- ИСI101lЬЗ~Сsr страR:$Щей '1"есшировaвmr WеЬ~Сервиса -->
<add frюtlЕ ='1Нt tpPQsU,vo::.al hQ'$,t " />
< Iрr.Q tQ С(:;й.s >
</lNeJJ'~:r:v ic~$>
1144 Часть У. Web-приложения и Web-сервисы XML

Чтобы снова разрешить использование :НТГР-методов GET или POST для Web-
сервиса. добавьте имена HttpPost и HttpGet в соответствующий локальный файл
Web.config.
<configuration>
<system.web>
<webSerVLces>
<protocols>
<add name="HttpPost"l>
<add паше="НttрGet" />
</protocols>
</ webServices>
</system.web>
</configuration>
Снова напоминаем. что при использовании стандарТНЬLХ НТГР-методов GET и
POST У вас нет возможности строить Web-методы, допуснающие использование со­
ставных типов (например, DataSet ADО.NЕТили пользовательский тип струнтуры)
в начестве параметров или возвращаемых значений. ДJIЯ простых Web-сервисов
это ограничение может быть ВПОJIне приемлемым. Одн<Шо при использовании свя­
зи SOAP вы можете строить гораздо более совершенные Web-сервисы XМL.

Связь SOAP
Полный аналиэ возможностей SOAP выходит за рамни этого текста. однако сле­
дует понимать, что SOAP нельзя назвать специальным протоколом. который может
использоваться наряду с другими существующими протокола.1\оШ Интернет (ИТГР.
Smp и др.). Общая задача SOAP. тем не менее, остается той же: обеспечить неза­
висимый от языка и платформы механизм вызова методов, использующих состав­
ные типы. Для этого SOAP преобразует каждый метод в сообщение SOAP.
Сообщение SOAP состоит из двух основных секций. Во-первых, есть конверт
SOAP. который можно понимать. к<ш абстрактный Rонтейнер для соответствую­
щей информации. ВО-ВТОРЫХ. есть правила, которые используются для описания
информации в сообщении (размещаемой в теле сообщения SOAP). Необязательная
третья секция (заголовок SOAP) может использоваться для того. чтобы уназать об­
щую информацию. насающуюся самого сообщения (например. информацию без­
опасности или транз<шции).

<soap:Envelope хmlпs:хsi="httр://www.w3.оrg/2001 / ХМLSсhеmа-iпstапсе"


xmlns:xsd=''http: / /www. w3.0rg/2 001 / XMLSchema''
хmlпs;sоар="httр://sсhеrпаs.хmlsоар.оrg/sоар / епvеlоре / ">
<soap:Header>
<! -- НеобllsатеJIЬиаll ииформаЦИR заronовха -->
</soap:Header >
<soap:Body>
<! -- ИиформаЦИII в~зова ME!I~oдa -->
< i soap:B ody>
</soap:Envelope>
Глааа 25. Web-сеРIIИСЫ XML 11'45

Просмотр сообщения SOAP


Х9ТР при созда~ WеЬ,сервиоов XML в рамках платформы .NET от вас не тре­
буется понnм:а.ния БС.ех дtта.л:d\ SOAP. вы lIIю~к~е увидеть фОРШtТQООбщения БОАР
ДfIЯ RЭ!ЖДО)'О доступного Web-меroца с ЦОМQПnUO автом;ати-.ееl\И генерируемой стра­
ющы тестирования, Например, если щелIЩYТЬ .на ссылке ДmI меТОДа Add \.) WlШеro
С'аlсulа:t.бrWеiJSеr-"iсе. вы УВИДитесле,цvroIЩIЙ запрос SOAP 1.1_
<З'Фвр:!];rпт;;?l:оре =lлs l.xsi="ht t"p: / /www.w:Э _org/2'O 01 iXМL,Senema - Lrlвtа'псе"
xml!l8'.: ~.sd="')1.ttp: / !W!If/7.!. wЗ. o.(g /20'0 l/ХИLSсhеmа ~
X1111r,'13'; 5с>ар,= "t.it tp: / / .'>сЬетаз. хrrЙ$-оар. org /soapierJve] аре/ ">
<.soap:Body>
<Ада XЛI:1Тl,g= п-Ьttр:'/ Iw:w·'", _Iд:tёrt~сfiТrQ ini 119. сот П)­
<>:',) iл L,: ! х>
<у> Lr"t <I у>
< !сАоо]<
</sпар:Есн::lу>
<! аоар; 'РГ( \ze] аре>

СоО'!'вететвующий ОТБ.ет БОМ 1,1 ВЫГЩIДИТ Так.

<заар; Елvе lope x,mlrls ~ xEi="htt.p: I /WWW.]лl3 _ org/ 2Dj)ljXМ'hScrjema~ i.rtEltance"


Xml.'1E: х s d = "FLt tp ; j " 'IlWW • W3 • org/2 О О 1 / XMl'& ch ета "
хmlr,з;вoqр="'ht t;p; I /s~heL11a $ . ХIпl<щар. отс! /:soDp/emvE>lope/ .,>
<. SJJ8'P: BIJd у>
<.li1dcLR еэt>аti.5€: xmln,,= "httр://",tW;w.lлtегtеСТ1Т:rаiпiпg. сщ ":>
"3\ddR"'$u:! t:> iл1: <. /A(idJ<f:,s111 t:>
< /f,(Е1L~еSРGПS'е>
</э®р; Воду>
<1 ЕОдр :ЕЛ'ilеl0ре>

УТИ,nИТа командной строки wsdl.exe


Теперь. когда у вас есть базовые з~r.aв:ия. о WSDL и SDAP. давайте выяс:н"им. шut
с nOMoIЦЬ1O такого ин(.трумеНТClltOмаFlДНОЙ е.трокй. как wsdl_exe строиггь npограм­
мы нлиента. взаимодействующие о удаленЮ;tми WеЬ· сеРВИiЖ.II.Ш XМL. :в gt'ЩЕ.ОСПl.
1i/$.dl_exe рещает две тлавпых :задачи.

• ГeIiерирование файла сервера. фуннцианирующето в качестве каркаса ДЛЯ


реаcrизации Web-сервиса XМL,

• Генерирование файла lUШента. функцИ()нирующе;го в RfI<reCTBe ате1'гга удален­


ного Web--сервиса, XМ:L.

Утилита wsctl.€xe поддерживает ряд флатов lюмандlЮЙ СТРОЮl, список В:ОТОРЫХ


МОЖНО увиде:r.ь,. указ'ав при Bbl3UB€ ЭТОЙ утилиты опцию -? в коман:Дпой C'l'poxe.
Описания некоторых apIyмeНТOB wэdl.ехе приводятся в табл. 25,6.
1146 Чаоть V. Web-приложения и Web-оервисы XML

Таблица 25.6. Подборка опций wsdl.exe

Флаг командной строки Описание

/аррsеttiлgurlkеу Дает указание wsdl. ехе создать агент, не использующий ''жест­


ко" заданные значения URL. Вместо этого класс агента будет на­
строен на чтение значений URL из файла *.солfig клиента

/language Указывает язык для исполрзования в генерируемом классе агента:


CS (С#; это значение используется по умолчанию)
VB (Visual Basic .NET)
JS (JScript)
VJS (Visual J#)
/паrпеsрасе Укаэ.ывает пространство имен для генерируемого агента или ша­
блона. По умолчанию сам тип в рамках определения пространства
имен не определяется

/out Указывает файл, в котором нужно сохранить программный код


генерируемого агента. Если файл не указан, имя файла будет со­
ответствовать имени WеЬ-сервиса XML
/protocol Указывает протокол, используемый в программном коде are~ITa,
по умолчанию это SOAP. Но можно также указать HttpGet или
HttpPost, 'нобы создать агент, использующий для взаимодей­
ствия НТТР-методы GET или POST
/sеrvеrIлtеrfасе Генерирует интерфейсные связи сервера для Web-сервиса XML на
основе WSDL-Аокумента

Замечание. Флаг /server утилиты wsdl.exe в .NEТ 2.0 больше не используется. Теперь базо­
вый программный код для сервера генерируется с помощью /serverlnterface.

Преобразование WSDL-КОАа всерверный


программный код Web-сервиса
Одним ИЗ интересных вар,йантов использования утилиты wsdl.exe является
генерирование серверного программного кода на основе WSDL-документа (с помо­
щью опции / sеrvеrlлtеrfасе). Очевидно. если вы начинаете разрабоТJ<у Web-cep-
виса XМL с создания WSDL-документа. эта опция должна быть Д1Iя вас очень важ­
на. После того KaR файл исходного кода будет сгенерирован. вы получите хорошую
исходную цозицию для реализации каждого Web- метода.
Предположим. что вы создали WSDL-Аокумент (CarBizObject_wsdl). в котором
описывается единственный метод DeleteCar (). получающий на вход целое число
и не возвращающий ничего. Этот метод предлагается Web-сервисом xrvIL с именем
CarBizObject, который может вызываться с использованИем связи SOAP.
Чтобы сгенерировать серверный файл программноrо кода С# на основе это­
го WSDL-документа. ОТI<ройте окно командной строки .NEТ и вызовите утилиту
wsdl.exe с флагом /sеrvеrlлtеrfасе. за которым должно следовать имя соот­
ветствующего WSDL-документа. Заметьте, что WDSL-документ может содержаться
либо в локальном файле *.wsdl:
wsdl /serverlnterface CarBizObject.wsdl
глава 25. Web-се,рвис.ы XMl 1147

J1Цбо по~атьс:я динв-мичltски по дан..Fiому ИRL :с .помощью указания суффикса


?wsctl:
wsdl .1 aerve..r:JI-IL€'L [а ее httP1/ /loc a.l ьо.., t iCa:!:'ServLce.!C03xEh zojuj.ect. a;>mX?wscЦ
После ТОТО вак w-s·tП . е х.е обработает соответствующие XМL-элементы. вы ЦOiЦY­
чите ОШlС<UnТН интерфеЙОillJ ДЛЯ каждогu Web-меТОЩI.

J$,Y 5tefn •. Web .5.erv i.~ S. WebServJ с еВirнДi П 9А tt.ri111,1te (


J\lame= " Car!5izD.b j ·e ct 13,о,;ор" ,
Narne.spc<ce=" rltt p : / / Irlt егtеt: hТrа i !'li гщ. c ()m!,j)· i
риЬ Нс pil ct ial i"n±e :r; fa ce r~UВ'];%Щ'JjесtSo&р
1

void ReD!ovеШ (.i.n!:; carID) ;


:j

ИСIlOльзуя эти Иi-IтерфciiСN, Bы можен опреl1еJЩТЬ 1ЦJa:oe·, реа:пизуюЩJiЙ разл,ич­


ные :методы Web-сервиса XМL.

ИСХОДItЫМ код. Файл :CarHlz."Object.. w.sdl размещен в подкаталоге, СОO'fВетотвvющ.ем Гf1эве 25.

ПреобразованиеWSDL-кода в лроrраммный
КОД агента ДЛЯ клиента
XOTil это и вежелателъuа. 110 .lЦIОJШ·е ВОЗМQ'ЖНО' построИТD базовыи протраминый
-:код :клиента, который будет вручнуоо от .кp~1ВaTЬ H1TP-а.'тединеВИе, СТРОИТЬ 50АР­
сообщец;ия. вызыват.I'! Web-меroды И· В1illlОJЩЯ1'Ъ обратВfЮ трансляцию постynаю­
щегоXМL-цотона:в 'Типы данны;х СТ$, Нац:ного более предпОЧТИТ.едrЬныМ подХодом
ОRазываетси чсполъзованме \·i,spl.e~e ДiJ1,Я генw-рирования класса агента, который
будет предстэ.ЭЛЯ'FЬ Web-методы. оцредеЛfП:шые данным фаШюм. .... asm:.:.
ДЛЯ 3Т1;>ГО ужrщите Скан МИНДМУN:J ИМЯ геRерируемorо' файла атента (с по­
мощыо флаг·а i oU, t) И место рщзМ'~:ще:fJ.ИЯ WSDL- документа. По· умолчанию
wsdl"e:xe г-енерирует цporpа..1-vfМНЫЙ l-Ц1Д агента на Я3ЬJ::Юе Cit. Однтсо если вы Хо­
тите иметь прогрaммный коц aTeнт~ ~Ш друто.м яэьmе .NE1: вы MO;fKeTe ИСПОЛЪ30-
Мть флаr /lёФgu.аgе.. СледУет ТaJЩtt' ::щать; что по умолчанию "Wsd.l.exe генерирует
llpoгpaмМlP>lii EQ)). агента, о'рeдnрлагающего ·связь (; удаленным Web-серВJiСОМ XМL
с IЮМОЩЬЮ SOAP. Чтсбы еоа;l'f..щный агент испЬЛЬЭовал НТI1?-Me-roд G.EJT ИЛИ .POST.
следует JRaa2.TP соответствующий протокол СВЯЗИ с помощью Ip:t at.D:col.
Другим важН:ЬfМ моме.том в отцqшеп= генерировав:йя програММНото пода
агента с ПОМОЩЬЮ w s d 1 . ехе явШI('ТСЯ ТО, ЧТО этё>му .инструме:wrу действите-лъно
требуется WSPL-доку.М!!Ю1l Web-t:.'ервдса :xмL. а не просто файл с :именем '*. asrnx .
С учетом этого следует понимать , "tfI'O если д.1JЯ разрооотни и. тестировa.ни.tI. We.b-
серш:tс-а вы щ; uользуете WebD$v,Web&e.rv er.E'xe, то· nеред ген.ерированием npo-
rpaммдorQ КОЩI агею'а ЩI'Я 1ЩИента .вы. сн:орее всего. захотите скопировать со.де.р­

жимое проею:а:в виртуа,.'1Ь'fIЬLЙ кща,JЮГ nS.


Дл~ Пpи1'll(;:рEi!. rтреДПОil10.ж.цм. что БЫ создали новый Iп,rртуалъ:ныJ;t 1Шталог I1Б
("CalcSe rv ice), содерж~ ДЩЦIые nроекта CaJ.cu1ato:rSer'ii i oe .. После .этого вы
можете сгенеРИРОD~ прогр~ньЩ код атевта БЛиента так .
1148 Часть У. Web-приложения и Web-сервисы XML

в качестве замечания подчеркнем , что ws d l. ехе не определяет простран ство


имен .NEТ для упаковки генерируемых типов С#, если вы не укажете в командной
строке флаг /п.

wsdl /out :pro x y . cs /n : Cal cula t orCl ien t h t tp : //loc a lhos t /C a l c Service/
Service .asmx?wsdl

Программный код агента


Если открыть сгенерированный файл агента, вы найдете там тип, который по­
лучается из Sу st е m. WеЬ . Sе r v i сеs.Рrо t осо l s . Sо ар НttрСl i е пt Рrо t осо l (если, ко­
нечно . вы не указали другой протокол связи с помощью опции /pr ot ocol).
public partial class CalculatorWebService :
System . We b . Services.Protocols . So a p HttpCli e n tP rotocol

Этот базовый R.пасс определяет ряд членов, используемых в рамках реализации


типа агента . Описания некоторых из этих членов предлагаются в табл. 25.7.

Таблица 25.7. Основные члены типа Soap HttpCl ient Protoc'o l


Унаследованные
Описание
члены

Be gi nInvoke () Метод , инициирующий асинхронный вызов Web-меТОАа

Ca ncelAsync () Метод (новый в .NEТ 2.0), отменяющий асинхронный вызов метода Web-
сервиса XML, если вызов еще не завершен

En dInvo ke () Метод, завершающий асинхронный вызов Web-метоАа

I nvo k e () Метод для синхронного вызова метода Web-сервиса

I nvokeAsync ( ) Метод (новый в . NEТ 2.0), предлагающий более предпочтительный ва­


риант асинхронного вызова метода Web - сервиса

Pr oxy Свойство , получающее или устанавливающее информацию агента для


запроса Web - сервиса через брандмауэр

Timeout Свойство , получающее или устанавливающее значение времени ожида­


НИR (в миллисекундах) для синхронны х вызовов

Url Свойство , получающее или устанавливающее базовое значение URL


сервера ДЛR запросов

Use r Agent Свойство , получающее или устанавливающее значение ДЛR заголовка


польэовательского агента в запросах

Конструктор, заданный по умолчанию


Заданный по умолчанию конструктор агента "жестко " определяет значение URL
удаленного Web-сервиса и эапоминает это значение в наследуемом свойстве Url.
Глаез 25,WE;!b-сеРilИСЫ XMl 1149
.pl.1b,lic Са l.с;ulаt.ш:WеЬSеrvl De ()
i
thie .Пхl = "Ы::Ьр: 1/ 10саl l ю-s, t/Саlс3еrv-iсе}Sеrvlсе, азЦ\х";

Очеющнь!М недостатком TaKOr.o подхода я:вляетсн ТО, ..!то РРИ щ:~р~именовании


или перемеще:ни.и Web-еервиса ХМ!. ЮIaСС аг.ента II]i>.ИХОДI1'ГСЯ обновлять И пере­
kо~шилировать. ДJ:I:Я. построенип более ГИ:бного типа: aiГeJ-Iта wsdl.exe предлагает
ИСПOJIbЭо.ваl'Ь флаг !appgettin'gtalkey (RОТОРЫЙ можно СОЩЩТЦТI:! ДО Iчrlk~у).
Если yRaзать .э команщш-й строке З.тотфлаг. :конструктор ~reH'Fa будет содержать
программную л.оги.t<у ДШJ ч.тения URL с до:мощъю lЩЮча, CQДерщащ~гося в файле
"' ..cotJ f i9 к..1Ие!Н1'а.
wsd1 /:J1! t;·prc"xy .cs !n :Cal'cCI<Lent luI1keY1 CalctJrl
ht.tp:// l ",саНюst /Cal·c Service/S·e tvj се. asmxtwg·d l
Если теперь проверllТЬ .{\онструктор агента, за.данныЙ ПО умолчанию, В;Ы об­
наружите следующий IТpоrpам~IН.b1Й КОД (заметьте. ч.то есlШ ПОДХ{)ДЛЩИЙ i\ЛJQЧ де
БУl1~:r найден, в качестве резервного будет nспользоваться заданное .кОIШpf;>тное
зкаче.ние URL).
public Са l С\11ёltоrW<WSег"i<;~Л
t
s,t ring bl !:lSettir::g ""
SузtеJn . CO'J"If~gurat.ion . Сопfi '3'Ur.atio-nМапаgе!" ..1I.ррSеtt· iпgз [·.. ·Calc:Ur 1" J ;
d.f ((t:r15etT. 1f'}.g != щЙ.l.J i
{
'Lhi$- , Orl = u:rl$ettlng';
J
el.sE'·
{
't.his.o,l ' '11 ttp: I /lc,cal Ьiфз·t.! Са] c.servi ce.j serv ice. а.s1ТШ" i

Соответствующий файд app.config И,Э. стороне l\:JIИента буд~ примерно та,щш.

< :)ЩI1 V,ersiOrf=I,J.. О" encodi.rlg= " \)·tf-8" '? >


<с ;;;nfigш: i3t 1 ог..>

<аррЗе t .t ihgs:>
<.add keY=" Gl!-lсurl" valu8'=' '' bttp·: I (Iocalb OBt /Са1 сSе'r viсе/Sелтiсе .21smx" />
<J'аррSеt tiпg-s>
<! солf ig:Urat.i Qn>

Поддержка СИНХРОННО'ГО вызова


!енерируемый агент (!Щределяеттакже ШОдlЦeрщну j::ИНХРОННЬГО Вbl30Ba Web-Me-
ТQДОВ. Напр'нмер, CIi1BA-РОИНЫЙ варИЩlт метода Subtract О реа:л:изуетCffТ~.

publlc .1.n'i БI.1btrаоЦi8t .х. , int у }


{'
obj'E:C't т1 1'6$й} tS = t.h:l$ . l n""oke C"Subt..r:!ct " r n.ew al~ject [ ] i х, у}};
:ret'JTn О (ilJtl' (r.es,t;jlt s [ О] JJ;
1150 Часть У. WеЬ·приложения и WеЬ·сервисы XML

Обратите внимание на то, что вызывающая сторона передает два параметра,


"упакованные" в массив System,Object. Используя динамическое связывание, ме­
тод Invoke () передаст эти аргументы методу вычитания, размещенному по ука·
занному aдpecyURL. По завершении этого (блокирующего) вызова будет обрабоп!Н
поступащщий XМL-KOД и результат будет возвращен вызывающей стороне в виде
System. 1 пtЗ2 после соответствующего преобразова:ния.

Поддержка асинхронного Вblзова


Поддержка асинхронного вызова Web-методов в .NEТ 2.0 сильно изменил ась по
сравнению с .NET l.x. По своему предыдущему опыту вы можете знать, что аген­
ты .NEТ 1.1 использовали методы Beg i пХХХ ( ) /EndXXX () для вызова Web-методов
во вторичном потоке выполнения. Рассмотрите. например, следУЮщие методы
BeginSubtract () и EndSubtract ().
public Sуstеш.IАsупсRеsult ВеgiпSuЬtrасt(iпt х, int у,
System.AsyncCallback cal lbac k, object asyncState)

return this.BeginInvoke("Subtract", new object[] {х, у},


callback, asyncState);

public int EndS ubt ract (System. IAsyncResult;. asyncResult)


{
object ( ] results = this.Елdlпvоkе(аsупсRеsult);
return «int) (results [О]));

Хотя wsdl.exe все еще генерирует эти знакомые методы Begin/End. в .NEТ2 . 0
они считаются устаревшими, поскольку заменены новыми методами ХХХАs у пс () .
public void SubtractAsync (iпt х, int у)
(
this.SubtractAsync(x, у, null);

Новые методы XXXAsync () (как и связанный с ними метод CancelAsync ()) ра­
ботают В паре с автоматически генерируемым вспомогательным методом (явля­
ющимся перегруженной версией некоторого специального метода ХХХА s уп с ()),
который обрабатьmает аСИНХРОffi'iые операции, используя синтаксис событий С#.
Если рассмотреть программный код агента, вы увидите. что wsdl.exe генерирует
(ДЛЯ каждого Web-метода) пользовательский делегат, пользовательское событие и
полЬЗОВательский класс "event args", чтобы получить соответствующий результат.

Создание приложения клиента


Теперь. когда вы лучше понимаете внутреннюю композицию генерируемого
агента. давайте попытаемся его использовать. Создайте новое консольное прило­
жение с именем CalculatorClient. добавьте в проект файл proxy.cs с помощью
выбора Project q Add Existing Item из меню и добавьте ссылку на компоновочный
блок System.Web.Services.dll.. Затем измените метод Main () так, как предлага­
ется ниже .
Глава 25.Web-сеРВИQЫ XМL 1151
cl.a·s'S program

static void ~1ajn (stringl] a.rqe)


!
Corcs:Gle.1f.J t it·eL.ine{ ... " k ..... 3а:бавы с; .aгeHT.:)~ WS *"" '~"\n");
1/ COSAa!U1e &rеиwа.
Calclj la.t o.r~ebSe<n·ice ws= iTE:X~ CalculClt.orWeb~rvi ce () ;
11 CmlxpollIlЫЙ ЗЫ90~ . метода АддО .
C~hg·b le.W'riteLi n.e("lD + 10 = [ п}" , ws .Ad<'i(1.Ct·, 10»;

11 АcйЮlpОI:ПШЙ ЗШlОВ метода Subtracu с n6МOЩЬJ)


11 иавО.РО ПО~О.1\а· .JIEТ :2. О ка осиозе со~JoI']1ИЙ.
WS .StJJ:'Jtrac tCompl e tect += oew
Suibt r: асtс.оmрlеt.еd.К\Тепt.наtнЛ е!: (w"'_ S:Ilbt ri\lctf:o)11pleted) ./
ws.SljЫ: r·аctAэynс(50, 45);

/ 1 ПрcmО~lUIе раБQoni lI1()ИCOJDs ДnR rарa.w.t'Иlf nОЛУlI.еиия


11 реЗУJJЬ~!1!~' SJ.\ЧИIl'аиия: •
.С:ШI::,.оlе. ReadL,ine () I

st.aci.c
. \ IG)id W'S - S'Ubtr.асtСоm;::lеtе.d
- ·(оЬ 'i €-сt_ sелd.еr,~

SаЬtтасЕС'!:щурlЕ-tеQEvеntАrg:.>Е )

(О Dз 01 е ·. W:r i tetirle ("Ва'!!! 9TBe!I' : ГО 1", е. RG$ tJl t) ;


}

Обратите ЦН8:Мзrrnе нато. что новая логи]{8. aC~Iнxpoнвoro ВЫЗQlЩ D.NET 2.0
непосредствеliНО ОТО'Вражаетоо в ('..ннтаисис' событиЙ'С#, IЮ'rОРЫЙ. согласитщ:ь,
mшяется: более 8..fЩYpатпым:по сравнению с использо.ван:ием метс!Дов BeginxXX () I
.EJndJ;XX (), JЦfТl:'рфейса IAsYHcF_€ sulL, и делег.ата. AsyncCallbac'k.

ИСХОДНЫЙ ко.д. П"РQm:т CaletilatorCI~nt размещен в ПОДКЗТaliоге f СI1JО1в'е,стзующем maВ,е 25.

Генерирование программного кода


атента в Visual Studio 2.005
УТИдИ:Та wsdl ,€>&8 , нС!не~ш(). предлаг~t"J' це-JII;I;Й РflД apryмeHTOB N,ома..JIДНоЙ стро­
КИ. которые ПОЗВОЛЯЮТ КОl;[Т{:юлировать результат генер:ированИf.J масса атента. но
V1sua1 Studio 2005 позвQЛДет быстро сrенерировать файлаген:rа. ИCIIодьзуя д1taло­
гоное окно Add Web RеfеГелсе (Добавдение Web-СёыlIКИJ. ш)тарое можно в:Ызвать из
меFDO Proje'ct. .КaR щщн.о lЦ рис. 25.6. в этом OIте вы можете получн::ть ссытщ на
СУЩОС1'вующие Web-сервисы XМJ..,. раЗМещеmiЫе в самых разffЫX м.eC'l'ax.

3амечаНlfе. ДИВJJоговое OIQ-IQ Add' Web ReferenoE: не ПОЗВQIrяет ссыrrЗl'hСSI на Web-серв.ИОЫ XMl.
которые обслуживаются WabIJev. ~bSe rve .t. ехе.
1152 Часть V, Web-приложения и Web-сервисы XML

ф О" , ф 'Й' @) ~
URL: [Т - __ О • -" ~ -' . .. . - ,, , • • _. ~~_ • • • , c .• _'" ___ .. _ _ .. .. .___ ......~ ] ... Go
_ _ __ , _~_ "" ---,,_ ..;....~--'.:.. .. _~ ___. __ • _ _ __ .. _~·_ _oo3 ....

fbr Web Services


Use thls раое ••• stortlnQ po1nt (а find Web servfcМ . Уаи сап click the 1;00
beJow, о' type • known URL Inta the ~ddres$ bar.
Browse to:
• Web 5ervices iп фis sоlutiоп
• Web se.rvices qп фе 'О[)II1 rnacllllle
• Browse УОРI Servers оп фе 'ОСII' lleН'югk
Query yau, Iocal п.t\IЮ,i<for UOOl se,ve,.. • .

• ill2Q1 OjrectQr\'.
Que,y th. UDD! businos. '09lst,y ta F;nd "omp"Г1Ies ond p,aductlon Web
servic:es-.
• Iest MicrosQft уро, Oirectorll
Lont. t •• t W.b service. to use durinQ d.".lapment.

!
!
L _ ... _. ._ _ _ '- - - _ _ О~. - ' - . -_ _ - ••• ~ j
-
с.ncгi

Рис, 25.6. Диалоговое OКlio Add Web Reference

Обратите внимание на то, что вы имеете возможность не только получить спи­


сок Web-сервисов на своей локальной машине, но и запросить различные ката­
логи UDDI (соответствующие вопросы будут обсуждаться в конце главы). Так или
иначе, в результате ввода в строку URL подходящего значения, укааывающего на
действительный файл *. wsdl или *. asmx. вы добавите в проект новый класс аген­
та. Заметьте. что пространство имен агента (зависящее от URL источника) будет
вложено в рамки пространства имен вашего клиента .NEТ. Так, если для клиента с
именем MyClientApp добавляется ссылка на Web-сервис, размещенный на вашей
локальной МaI!IИне, вы должны указать С#-директиву using следующего вида.

using MyClientApp.localhosti

Замечание. В Visual Studio 2005 диалоговое окно Add Web Reference автоматически либо добав­
ляет в проект новый файл арр. config. содержащий значения URL ссылок на Web-сервисы
XML, либо обновляет уже существующий,

Доступ к пользовательским типам Web-метоАов


в заключительном примере этой главы мы с вами выясним. как строить Web-
сервисы. предлагающие доступ к пользовательСI<ИМ типам, а также к более "зкзо­
тическим" типам из библиотек базовых классов .NEТ. ДЛЯ примера мы создадим
новый Web-сервис Xl\1L, который будет способен обрабатывать массивы. пользова­
тельские типы и объекты DataSet ADO.NEТ. Сначала создайте новыIй Web-сервис
XМL с именем CarSaleslnfoWS, размещенный Б виртуальном каталоге IIS.

1
Доступ к массивам
Со'здайте Web-мето.ц GetS alesT a.gLi nes () . .кОТQРЫЙ в()звращает М~СИВ строк.
предста:вшl,ЮЩИХ .данные текущих прод
аж раЗЛИЧIlliIХ щвтомобилей , и ме<год
S or t Са rMa ke s () . .котарый ПОЭВоЛИ'Г вызывщошей croPQHe
передать мэ:ссиg не­
сортирФванных строк чтобы обратно получ:цтъ новЫ
Й массив отс-сртированных
строк.

[WеЬ Sеп'iС.е ( N ад\;i!sрас:е = " b t tp: I IIл t е~·t·е.


сr1ТrаiI).in.g. сфm / " I
De-sc .ript lo!! = "А'ВТ0Мо.t)и;ль·'н~ Web-серви.с",
Nаще = "CarS a]esln foWS ") 1
[Web.ge.r 'v'lc~НiCidi.IJ g (CQn:fo:r'msT'6 = WsiP rofile
s :Ba.s'! cPrG filel_ 1 J 1
риЫ1-с 'cl a~s serv ic e : 9y'ste ro.W eb.5e "vice s .. Web5e :rvice
1
rWebHetl10a (D.e s·cr.-\ ption = "ПОJiу,щ<{ие :Р:6'КЛаынbl.'<:СКИДОК"))
publ ic strin g (1 Get:8 i31esT agLln 6s О
{
atri ng [) Cllzr en·tb eals = {"ЦенЬ! на Соltсниж-еНЫ' на 5О %! "т
"Вс е' EМW :ксмп леКТУЮ;ГС>I 8'-в:a.rrалъ"ЫМ ЗВУ!<'
ОМ",
"'Саr:аvал беСiJJJа:VIiЮ ••• спросите у дилер
а !"};

, retu ~ a

( Vl e'bМ $ t h Cid
curD entD eals;

rDe.s cript. i оп =< "С 0РТИР ОЗ'КИ с л ис к а ~ap';) K") ]


риЬ} i·::> &и 1(1-9' (] $о,rt С·tilr.мз,k еs (5!tri ng rJ I:heGa :r$T·o Sott.)
f
Аттау . Sqrt ctы carsTo s Qr 't) ) ;
rеtu·r ч theC ar'l3To Sort ;

38М~aюtе. Стр1!НИца тесtировани~, геf1ерируемв.я С!10М


ОЩЬЮ Def a1'.11tWsdlHclpGeneratar.as:pJ<,.
fte может ВЬ(ЗЫВ;:iТh МВТОДЫ, ИОПО,/JЬЗУЮЩ!'lе в КЗ'lвстве парам
етров м'!iссивыипав •.

Доступ к структурам
Про1"01tОJ1 SOAP позволяет перt:дачу XML-преде'Fав.l1еннJ."j полъзо;вате:iIЬСКИ
Х ти­
па}) данных {таких :как клаССЬ1 и структуры). WеЪ-
сервисы XМL ИС'llОЛЬЭУЮТТЩ1
Xro:1S eri аl iJ;!€r
ДJ'Щ 11реdбразованиитипа в XМL-иод (€M. главу
17, где имеется <$0-
лее подробная ИНфО{!!МсЩИЯ по ЭТОМУПОБоцуJ.
Нanом,н;и:м. что Xm1S eriali ze:r:
• Н'е выnо:лняет серИaJJ.Изацию прuват:щ,IX
данных: для сериапиЭац~m ИСПQJlЬЭУ·
Ю'1'СЯ ТОJ!J>ZФ OTКPЫТbl~ поля И евойства:;

• требyt:'I; чтобы шl.ждый поа:воляюXnnЙ серщзлизацию класс


имел шшстр утстор.
за.дЩЩЫЙ по умолчанию:

• не требуе-т исполъзовЗ.Нин атрибута [Se r i a l i zable ] .


с учетом с«аЗаlnЩГО . наш следующий WеЬ,м
е:год будет ВQ:и~ращать массив
CТPYRryp ЭаlеSlnfоDеt.аi 105. опред~енных
слецующим образом .
1154 Часть V. WеЬ·приложения и WеЬ·сервисы XML

// Пользовательский тип.
publi c struct SaleslnfoDetails
{
public string info;
public DateTime dateExpired;
public string Url;

Дрyrим интересным моментом в отношении XmlSerializer является то, что


этот тип позволяет осуществлять многослойный контроль представления типа . По
умолчанию сериализация CTPYI'ТYPЫ SaleslnfoDetails выполняется путем преоб­
разования каждого полSI данных поля в уникальный ХМL-элемент.

<Salesl nfoDetails>
<iпfо> цены на Colt сниженьr на 50%!</info>
<dateExp ired>2004 -12-0 2 ТОО:ОО:ОО.000ОООО-06:00 </dаtеЕхpired>
<Url>http ://www. CarsRUs . com</Url>
</Sa1eslnfoDetails>

Чтобы изменить поведение. предлагаемое по умолчанию, вы можете в опре­


деления своих типов добавить атрибуты. определенные в пространстве имен
System .Xml.Serialization (снова см. главу 17).
public struct SaleslnfoDetails
(
public string info;
[XmlAttribute]
public OateTime dateExpired;
public string Url;

в результате будет получено следующее ХМL-представление данных.

<Salesl nfoDetails dateExpired="2004-12-02ТОО:ОО:00">


<iпfо>Це ны на Colt снижены на 50%!</info>
<Url>http ://www. CarsRUs.com</Ur1 >
</Salesln f oOetails>

Реализация GetSalesInfoDet.ai ls () возвращает заполненный массив этой


пользовательской CTPyкrypы.

[WebMetho'd (Descripti on ="Get details оЕ current sales")]


publi с Salesl о foDeta i 1s [] Ge-tSаlеs IпfоDеtаils ()
{
Sa1eslnfoOetails[] theloEo = new SаlеsIпЕ оDеtа ils[З];
thelnfo[O] .iofo = "цены н а Co1t снижены на 50%!";
theInfo[O] .date Expired = DateTime.Parse("12/02/04");
thelnfo [О] . Url= ''http://www . CarsRUs. сот";
theInfo [1] . inEo = "Все BMW компдектуются 8-каналы,!Ь!М звуком";
theIn Ео [1] . dateExpired = Оа teT ime. Parse (" 8 /11 / О З ") ;
thеlлfо[l] .Url = ''http ://www .Bmws4U.c om ;
ТO

thelnfo[2].info = "Caravan бесплатно ... с пр осите у дилера!";


theln fo [2] . da teExpired = Оа teTiroe. Ра rse (" 12/01 /0 9 ") ;
thelnfo[2] .Ur1= .. http://www.AIIPinkVans.com··;
return theIof o ;
Глава 25. Web-с~рвисы XML 1155

Доступ к типам DataSet ADO.NET


Чтобы ~еРlШ1Т1i> roащпше. нашего WеЬ~сер:виса. XМL. вот вам еще ОДИН Wep"Me-
ТОД. который ВQзвращает Dat.aSe~ а д<iНEI:ЫМИ таблицы Inventory ба..эы дащIЬ1X Сагв,
создаННой при изучении ADO.NEТB главе. 22.

11 ПО3'lучeюte СШИсха B~ машин 1'1!,9 'J!аб.п,ИЦDI Inventory.


[wеЬМеt!Юd('Dезсriр,tiОr>. =
"В,озвраЩiJЕ'Т слисок машш.J из таС>лt-ЩЫ Inventory базы даН:fJЫХ са.rэ, ") J
pllt,lic DataSe1t Ge'l:Cu.r reXj tlJ'lve.Ilt.Qry ()
I
11 ЭапOJПieиие DataS.e.t даRRIIмИ !l.'а.бm,щJ.1 ~ventory.
SqlС:Dпnесtlan sчlСОlШ = new S:glCdn.nectiol:'i (j ;
5q1roJ1rf, СЩJf1.е.сtiопStrj!~g =
"dаt11зоur с е=10са1hQ S -t. ,; io i tj,al 'c atal'Og'=Cars; uid=5i1'1 p\>id="1
S;qlDataAdapter mYDj\=
&qlDat,a A,;'Ia.pter I"Sel.e ct
nElII '* from IrJ'II'€!ntory"; sqlСспn).
DataSet. ан; = ,new Dataget (') ;
rnYDA. 'F i1 1 (,J;з, ~lП \7 еГLt &ry");
J:e:turn ds:

Исходный код. Файлы ПРl1мера СаrэSаleslnfоWS размещfЖЫ в Гlflдкэта/J'Оге.,· соответс.твующем главе 25,

Клиент Windows Forms


Чтобы проверитъ работу новОго Web-еервиса XML. созда:й1'.е приложение
Window~ Fbп:ils И')'RаЖ1'iте в нем ссылку lIa
Car:S'8alesInfoWS, исttоmюyя димоговое
отщо Add Web Reference 'в Visua! 5tйwо 2005 (рис , 25 ..7).

A-D::Т:Оl"I'обl1Р.ьt1I=о:~ 'Web~ept,'l;J.1c

Th~ fdI10~.in_I).®_'V~~QЩ" af.!J" !tuppprtild.. F.'iir 01 rofТl1l11.denмi'Iif.l,


1 p.1_eaSf) lr~vj=e.w .th~ ~~Kf 1l.~~ ..

18 ~~~rl·r:ntlli.'''II'[J.t,:оry
Ъ.О"tтflЩ~Т ~11C'0K l'IaW\11<f fI}· ~:аfiлliju.hl !nw~!'!.t.i:H""1 fi-Mbl
..о..онны ;( Cors

~ G.clrS:.*.!.Jлf.rфl!t~
1~I-IЭАРI}5нь14' A'Of#I&tf"Go l"~r."ЩI1)" n'P-О,цlliК.а)(

• G.... tsatr-.I~
ПtmV"'t!tlие tteJ!iпе"'t1ы i (J"hJ1v~

, ..!!l!-t t .at'N"Ik \9.i


Gr;Р:n'ipt.Б~11 t1H'Ict(a 1'1.o!!i.ptt'r::

,
111'
I _ , _ ,;; ..:_ _ """ _ ' ""i; =.=-

Рис, 25.7. Добавление ССЫЛКИ lIIa GarSSaleSlnfaV'JS


1156 Часть V. WеЬ·приложения и WеЬ·сервисы XML

Теперь просто используйте генерируемый атент ДЛЯ вызова доступных Web-Me-


тодов. Вот одИН из возможных вариантов реализации формы.

u5ing CarsSales1nfoCLient. lo calhost;

патеарасе Car5SaleslnfoC li ent


{
public partial class Main,Win.dow : !,'orm
(
private CarSales1nfoWS ws = new CarSalesIn foW S();

private void MainWindow Load(object sender, Even tA rgs е)


(
11 ПрИВRзха х таблице.
inventoryDataGridView. DataSo ur c e
= ws.GetCurrentInye nt ory() .Tables( Oj ;

private void btnGetTagLines_Click(object s e nder, EventArgs с)


(
string[] tagL1 nes ~ WS.GetSalesTagLines();
foreach (string tag in tagLines)
listBoxTags. Items. Add (tag) ;

privat e void btnGetAI I Detai 1 5 Cli c k(ob ject sender, EventA rgs е)
(
SalesInfoDetails [ ] tbeSkinny = ws.GetSalesI n f .oDe t ai l s (i ;
foreach (SalesInfoDetails s in theSk in ny)

string d =
string. Format (" 1пЕо: (О) \nURL: (l) \nExpiratiOn Date: (2) " ,
5.1пЕо, s.Url, s.dateExpired);
MessageBox.Show(d, "Deta il s") ;

На рис. 25.8 показан результат тестового запуСI(а приложения.

Colo, P.IN.m. ~
.. ~ - ;~'<, •...,. " ••._. ~ , --....::
-;-:"

~ j зеfll!!lный Chuoky '"

6мый S"ake
; ~ОРИЧf1евый Zippy
, розовый ~ 8uddh. ~

_ ___. - ____ - -
_
-
,
_
,
О
~
Цоны на Coll сниж.ны на 507.1
B~ BMW KPМnIleJ<T~IOТC" 8,канаnьным '.~'OM
Подро6ноcrи
IC.~r~van бесплатно .. Cl1pocwтe ~ AlAnepal
I- ... . - :, ~-:-- =~ - --

Рис. 25.8. Клиент СаrsSаlеslпfо


ГlJflза 25. Web-серsисы XML 1157

Лредста'вление типов на стороне кnиеНТ$


КQща КЛiИ~Т устаJIавлиnает ссылку на Web-сервис, преДОСТW':lJUЦOrщЩ доступ
п полыюваТелъсц:им т:иnaм. файл :класса. игенТа получает определения ДJ.IЯ кащдо­
го отрытого дользовате:дьско:tо 'Типа. Так. если:вы посмотрите на представление
IUшенrа для S1;leslntoD€tail:s (В rе.нерируемом фai':Ше RefereHce.c;s), вы удиците.
что ЩlЩЦое поле щmапсулирOВi1НO .s строто тИnИ30ВaRй'ое свойство (тащке обратите
внимание на ТQ, что '1тот ТIШ теперь определен, как ШJaCС,а liЮ нaif c'ГpYJ<тypa).

j9уэ.tеrn. Serinl iz.ablelitlt.ribute () ]


r sуs·tе.щ . Xml , Se r ial Ь:а t i
оп. Xml TypeAt t-r ibute
(:blameврас::е=·'httр:I!I.пtsttеСЬТrаiпing .com/ U
)]

public parti.al сlаэ;!! 5&le.InfoDeWl~ .{


private st r.iлg 11lIoFil?Id;
private strl:r.g urlField,
privat.e Sys~m.OateT:r.me 'date.ExpiredF.iel;j;
pиы.c з·tri·пg iIJfo
{
g 'e t { ге.tuпi this. il1foFi-еld;
sE;'t tr-,is.infЬFiеld = value; 1

putolic scring [11'1


{
g~t I ret urn thi s .urlField;
·эеi;: ,( t[Ji~.\1.rlF·iel .d = value.; I

jВу: stl9щ.У.ml . Seri1'11 lzat i'ОП . XmlAttributej'l;t:tr ibute () 1


publlc Syst em,DateTi:roe dateE.x pired

get :trJ.is.dateEJ...-рirеtiF'iеld;
rE·tlJrl1
S€t thi$ .dat.eE xp.:i r e dF i~ld = val\l:e ; J
}

Лри <УЛОМ. Rопе'ЧНQ, следует IJонимa::rь.. что IJО.цобнс случаюудаленного взэ.имо­


дёЙСТБИЯ .NEJ: тlЩbl, сери.ализ~ция НО1"Орых ·ВЬП10JШлется IlO сети 'в формате XМL,
не СDXpaн.нI<Yl'логи;ку реаJ"II~ЗЗ,ЦIЩ.. Поэтому. если стру-ктура Sale.s!nfoDetails под­
держивала ряд О'Т'1-tрщrык методов, тене:ратор агеН-Г.аучесть это не .сможет (прежде
Бсеrо. потому. что эти :методР1 це отражены в Д;OКyмeнт~ WSDU). 6дшщо если вы
УСТaROI:IИТе :компоновочный БЛОI( КГ~IеП'Iа, lютоpый будет содержать проrра:м:мный
.коД реаJШЗацJЩ: т]Ша .КIIИ~та. вы сможете ИСПQЛЪЗО.Ватъ npограммную ЛЩ'JШY :rип8.

Но.при этоМ тре/5у~ся:, чторы СQОТQетству:ющая :мanшна обеспечиваяа ПОДДе]!))lЩ)I


.NEJ:

иехОДНItl.Й код·, Проеt<Т СагSаlеэlпfиСliеl1t размещен в подкаталоге•. СDответствующем главе 25,


1158 Часть V. Web-приложения и Web-сервисы XML

Стандарт поиска и взаимодействия


(протокол UDDI)
Тот фЗRт, что первый ТИПИЧНЫЙ шаг клиента при обращении к удаленному Web-
серверу оказьmается темой заключительного раздела этой главы. может казаться
немного странным. Причиной этой несообразности является то, что процесс про­
верки существования (или отсутствия) данного Web-сервиса с ПОМОIЦЬю UDDI явля­
ется не только необязательным. но во многих случаях вообще невужным.
Пока WеЬ-сервисы XМL не стали де-факто стандартом распределеШIЫХ вычис­
лений. большинство Web-сервисов используется компаниями в жесткой связи с
конкретным поставщиком. При зтом и компания. и поставщик уже хорошо знают
один другого. позтому У них не возникает никакой необходимости запрашивать
UDDI-сервер для выяснения вопроса о существовании Web-сервиса. Однако если
создатель Web-сервиса XМL пожел:ает открыть свободный досryп R Фуннциональ­
ным возможностям своего WеЬ-сервиса. этот Web-сервис можно внести в UDDI-
каталог.

UDDI (Uпivегsаl Description, Discovery and Iпtеgгаtiоп - универсальное описа­


IШе. ПОИСR и взаимодействие. стандарт UDDI) является инициативой. позволяю­
щей разработчикам WеЬ-сервисов "выставить" коммерчеСRИЙ Web-сервис в обще­
известном хранилище. Независимо от того. что вы могли сейчас подумать. UDDI не
является технологией. поддерживаемой ИСIUIючительно фирмой Мicrosoft. На са­
мом деле IВM и Sun Мiсrosуstешs имеют не меньшую заинтересованность в успехе
инициативы UDDI. Н:ак и следует ожидать, UDDJ-каталоги обслуживаются многими
поставщиками. Например. посвященный UDDI официальный Web-сайт Мiсrosоft
досryпен по адресу http://uddi.microsoft. сот. Официальньтй Web-сайт UDD1
(http://www.uddi . org) предлагает множество "белых книг" и SDR, которые по­
зволяют строить внутренние UDDI-<:ерверbl.

Взаимодействие с UDDI в Visual Studio 2005


Вспомните о том. что диалоговое оюю Add Web Reference позволяет не только
получить СПИСОR всех Web-сервисов XМL, размещенньrx на вашей локальной ма­
шине (а также по известному URL). но и предъявить запрос к UDDI-серверу. Как
правило. вы имеете на выбор следующие варианты.

• Вьшолнить обзор UDDI-сервера во внутренней сети вашей RОМШlliИИ.

• Вьшолнить обзор спонсируемого фирмой Мiсrоsоft производствеШIОГО UDDT-


сервера.

• Выполнить обзор спонсируемого фирмой Мiсrоsоft тестового UDDI -сервера.


Предположим, что вы строите приложение. которое должно получать текущий
ПрОl'ноз погоды на основе почтового инден:са. Вашим первым шагом должен быть
запрос R UDDI-Rаталогу с вопросом следующего содержания.

• Есть ли У вас Web-сервисы. имеющие отношение в: погодным данным'?

В том случае. Rогда UDDI-сервер имеет СПИСОR Web-сервисов. связанных с про­


гнозом погоды. вы получите список всех зарегистрированньrx ИRL. предлагающих
возможности. соответствующие вашему запросу. С помощью этого списка ВЫ СМО-
Глава 25. WеЬ·Сё.рви(;ы XML 1159
тете выбрать подх:одлщиИ вам WеЬ.се:рвис и. в конечном итоге. получить WSDL-
ДОЕумент с описанием фy:mщ:ионалЬ1IblX :8UЭМОЖ:НОC'I'еЙ., связанных с' обработкой
IJOrOAHЫX дaНliЫX.

Для примерасьздаiпе НОВblЙ проекr 1tонСОЛЫЮГО приложеl.ffiН .И акпmизируй­


те дй.аil0,О:Вое 6ИНО Add Web Heference. Затем выберите CCbIJl1<y Те::>! Мiсrоsоft UDDI
Dlrectoгy, которал приведет' вас к тестовому UDD1-серЕеру Microsoft. ПОCJ"1е этого
укажите слово weathel' (погод;'!) n i{аЧi:i:ст..ве критерин поиска. После вЫПОлнении
запроса lJDDI-каталОfОМ 8Ь! ПО;-JyЧите спИСOltВСех соответствующИх WеЬ~серюrсов
XМL. Обнаружив Web-сервис XМL, IФТОРЫЙ I10дойдe-r ваЬ! для использованИя в раз­
работке. Добавьте ССЫЛКУ на этот сервис в свой проею. как вы и :долж.ны ожидать.
"СЫРОЙ:~' WSDL-хоА будет проаННJtизиро:ван соответСТВУЮЩИМ инструмен-rом разра-
60ТЮI. и вы получите СОQтветcrйуЮЩИЙ агент на Я3ЫRе С#.

Замечание. СледУет панимать, что цеНтр тестИ!"ОВВIliИЯ является' не более чем центром тестиро­
В8I'1Иfl. Не удивляиreСЬ,если БЫ обмаРУ*~1Те там множеС'fIЮ ~lещейств~тельныx ссылок. ПР14
запросе ПРQИЗ80,цСТВёhl~rых UDDI-серверов И")<i URL оказываюн:я горазд-о более ыа,цежными,
поеКОЛЫtу КОМПВliИЯМ оБЫЧI~О приходит-ся платиТЬ за 10, 'll'оБы Oltlji1 nриоyrСТ!lовали е соответ­
ствующем спи оке.

Резюме
в ЭТОЙ главе Оbl.7Ш paccм:oTpeНhJ базОвые компоненты WеtyсервиСО.в и ОСFю.виые
этШIы ихпостроени,я в пределах штатформы .NEТ. lЛа:ва нач:инае't~я (' рассмотре­
н:и;л пространств имен (и базовых тИIlОВ в этих ЛРОСТjJаЕСТВах имен), используемых
при СО3Дании wеhссервисО:в. Вы УЗВ8J1И. что До'1Я разработки WеЬ..()е~8иса в .NEТ
требуется .iIИШЬ яемнoлw большее. ~eM примене:ние атрибута rWe:bMetk10el] R: нвж­
,дому члену. н:оторый вы хотите сделать доступным в рамках тшrа. преДСТавляю­
щего дан:н:ы'й WеЪ.сервис XМL. Вы TaJtJRe можете (rЮ 'это необлззтелько) сделатъ
соот:ветствующиеТИIIl:iI' IJ}>ОИЗВDДНI:iIМИ от SуВtеrп.wеЬ.Sеrv ice:s _WelJ$,erv.1ce. ~IТO­
бы (среди IIpочerоJ получить доступ RСВОЙСТВам Appllcation и Sessiol1. В СВJ1ЗИ
с о.сношrGЙ темО'Й обсуждения этой ГЛ8:вы БЪDШ таюке раССМfПРены три В3ЮJМ<iiс­
IiН38HHыe ЮIЮчевые технологии - это механизм поиска (UDDJ)"язъш описания
(WSDL) ИnPОТfJКОЛЫ связи (GEI.. }'ОБI или 86АР).
После CD3Дa.Jl1m любого числа членов с атрибутами [WebMet.hod] вы :можете на­
чать ВЗайМодейСl'Вие с. We1J-сервиtfJМ посреДствmvr промежуточноtо агента. Ттюй
агент может быть .сгеверирQВaН с :ПОМGЩЪЮ утилиты \VБ·dl.ехе. а сам аген1' может
испоЛЬзоваться клиентом подоб:во mобому друто:м:у типу С# , Аль'I"f'рнативG:Й ИШ.'ТРУ­
менту командной стро:ки WSdl,8lte является испо.IIЬ30вание ViSLla] Studio 2005. :rдe
аналоi'ичные ВО3МОЖ1:ЮСТШ .предлагafjТ ДИaшirовое окно Add Web Referer)ce,
Предметный указатель

А J
ADO,929 JIТ-компилятор,57
ADO.NEТ, 929 Jitter.57
API,44
м
ARGB,820
MBR,708
ASP, 1022
МВR-объект,708
ATL,46
MBV.708
в МВV-объект,708
BCI,,43 МDI.753
МDI-Rонтейнер, 799
с MFC, 44
САО-объект, 711
CIL, 53; 599 о
СIL-инетрукция newobj, 251 ОТВ-тестер, 109
CLI,75
Р
CLR,48;65
РШ,538
CLS,48: 64
eode DOM, 642 5
СОМ, 45 50I,753
CТS,48 БОК 79
СШ,124 SOAP.1143
D т
DCOM,741 ТСР-канал, 706
DI5СО, 1120 TLS,539
DNA,46
DОМ,1017
u
UODI. 1120; 1158
Е URL активизации. 718
Еета International, 75
v
F VE5,75
FCL.43
w
G Web-пркпожение, 1010
GAC,72;471 Web-еервер, 1011
GDI+, 803 Web-сервие XМL, 1 117
GШ,124 Web-элемент управления, 1052
GUfD,471 WеЬЩ, 124
Windows Foгms, 751
н
WКО-объект, 711
HSB,820
WSOL, 1120; 1137
НТМL,1013
WSI.1122
НТМL-форма, 1014
НТГР. 1009 А
НТГР-запрое, 1043 Абсолютное имя, 71: 196: 444
НТГР-канал. 706 Абстрактный
НТГР-ответ, 1046 клаее.234
поставщик данных. 940
член. 212; 234
IDL,53
Автоприращение, 975
IIS, 746; 1011
Агент,1118
ILIDE#,611
прозрачный, 704
IРС-канал, 706
реальный, 705
Пред.м~тныЙ Уf(эзатель 1161
Адanхер ;/lщtных. 938; 969 синхронный. 572
ЛЮ11IlНыес~рnерные страниuы [А8Р). 1022 ВЫХОДНDЙ naрзметр, 144
Аионцмный метоД, 371
API-у'МС!iТ1Ф-МаЁдНОЙ СТРФRи. 117
r
I'aрНИТур'а Шрифта. 824
др-китен.тура 1~NЛ. 4~
Генерация объектов. 255
Асинх:рщшое у7.tалеЙНI)(;; БЗаЬ!\{{JдеЙстви.е. 748
Thнерирава1iИе и,с;:нлючениа. 2'7 7
.'\GЮIXpо.юiliIЙ
DJOбa11ЪiIblЙ
ВВОД-l;IЬШОД. 675
:КЭШ1Юм'поновочных. блОЯОВ
(GACJ. 72; 471
Вl.>IЭ.Q1'!" Q 70
yникaJ1bliЫЙ Щ1mтиф.инатор
(GU1DJ, 472
д9С1'.}'Р Н-ДЩIНЫМ. 968
Ipaфичосюш mпeрфейСll~ (GlЛ), 124
,'L'T'Qt-1арн!щ ошtрашLЯ. 567
IpyшIОВ'ое npеобраэование методов. 373
Атрцбут. 516
Ip)"rJповой
[Nlщ&tiЭJizеdJ,680
блОК-. .874
(ser1allZa.bleJ.6'7'8
ВlJlЭОВ. 354
!sync;h.mntzatloilj.593
[WebMelhod]. J 133 А
(WebSf'rV1cej, 1 LЭО Дaнm.l!"
(WebscrvlceBindingj. J 132 фоk!е. 1104
CIL.60l сщ~k!е. временные, 1! 05
ковте}>С'Тш,Щ. 556 cookJe. aepM!UJeIlIИble. 1105
ПОЛЬ30IЩ'J'e,I).ъскЩi.521 сооЮе. сеаноовые. ] 1'05
УРЩП-IЯ J{()МnОНОВ{Jчноrо б.WRа. 524 состояшrл npиложеНИЯ'. 1092
уровня модуля. 524 Сl'атичес~ие. 13'8
уровня ·~К3емnлsфа. 137
Б
ДвоичньiЙ код операдни CIL. 1302
БазОВЫЙ _КJlaCC. 211
Делегэ.'1', 62; 344; 568
о(,5общеШl'lll~ 434-
QооащеиныИ. 437
Бпб.!lИО'Тl;!rrа
Де.ilещроваиие.22'8
AТL,46
Демон. БВ5
MfC, 44
ДИЫlогово[> OIOiQ. 752: 9] 9
бilЭОIJЫХ K1Iar;-СоВ. 48
_модальное. 921
lЩвсе:ав.444
I10льзов;!\Те..i1ЬСХое. 919
npoг~",lМНoгo iЮда. 444
,ДинаМlftt~('RЩI
БiIOR
З'!-ГРУЗIЩ. 509
fiааllу.29З
памя:г.Ь. 121
iry/catrb.218
Дштамичееюш lWМnО1iОВQЧНЫЙ бmж.632
ЬДОfЩРQВНIl, 589
ДинамичеCi\0е C13SlэываННе. 513
БУМВ8.1l.ЫlOе lЮспрtm:,;веДeJ:-ше (':ТРФК. L85
В Д;lреит.uва
'Версии сеРI1!aJПИЗа1{ИИ, 697 ftde,fJi1e-. 41 1
Визуализация изображений, -840 #1f.410
!3ИР'1уальная СИ('7ема вьmОЛI-Ie1-Uffl (VES). 75 #reglon. 409
ClL. ВО)
~иР'''УaJIЫIЫЙ препроцесс-ора. 409
l{aTaJlClГ. 1011 ~IСЩ~'r",ер, 707
стек IlЫПолнеЕI~Л.. 603 )].иcnлеЙJ-JQе ИМJ{.
:511
Ч1Le.R. 172: 212 Домен ПРИJIОЖения. 549; 70]
в.тюжение ТIiШU13. 229 l;оад<iЩц;iИ ПО yNQ!JчаниЮ. 549
'вложенное пространство Кi,~(!.Н.lg,9 Десryn
ВнешнИЙ.код LЮДДер:;щ,,"" , 1 026 аСИZ~РО~IНЬdi.96в
ВliyrренниЙ тип дiurnых' 176 ROНky.pенfuыЙ. 586
Внух:peш-mя структура (;АС . 485 к арryмеmщ,! Еомюumой CТp01rn. 118
Временнысданнъrесооldе.11D5 Достулиостъ
ВспльТБаIOЩая DОДСRаЗlill. 885 ТИПОВ. ].30
ТkГJDоенНhIЙ "I'ИИ ДС!fЩЫХ CTS, ~2 члеНа!!,128
IlTO-РНЧllое И-СШПО'3eRИt". 291 ДочерниН
ВЫ90В ЩJасе.211
аcrшА,,]){}нБый570'' Мементyupав,ления, 797
1162 Предметный указатель

Дочерняя форма, 801 строго ТИПИЗИРОВaнIiое. 285


уровнл приложения. 285
3 Испытательный l\онтеЙНер. 911
Заголовок
Источник данных, 942
CLR. 446
Итератор. 318
Win32.446
Запрос, 957 К
параметризщщнныЙ.964 Канал,705
Значение Каталог
констанТЫ, 133 виртуальный , 1О 11
локальноЙпеременноЙ.131 приложения . 464
пре.цусмотренное по умолчанию. 131 Квантование вреМe1IИ. 5З9
Зондирование. 465 Кэш
загрузки. 450: 489
и
приложения . 1095
Идентификатор
Класс, 120;203;249
версии. 444
Applicatlon.755
процесса [РID/. 538 ArrayLis1. 333
Иерархия
Control. 760
интерфейсов. 312
Fbгm.759
интерфейсов System.Collections. 330
Queue.334
lщассов.178
Stack.335
п олызовательских классов, 203
System.AppDomain. 549
Изолированньrй класс.226
System .Array. 191
Именованное свойство. 522
System.Console. 124
Имя
System.Delegate. 347
абсолютное, 196:444
System .Diagnostics.Process.541
дисплейно е . 51 1
Syst ern. Drawlng. ВгuзЬ. 835
компоновочного блока понятное, 465
Syst.em.Drawing.Graphics. 808
lЮМПОНОВОЧНОГО блока строгое. 471
Sys tem. Drawing. rmage. 840
Индексатор, 377
System.Enum. 166
Инкапсуляция . 210
System. Environmen t, 119
Интерфейс . 61: 299 System.Exception, 274
_Exception. 275
System.GC, 256
IСlопеаЫе. 320
Systеm.Ю.Strеam 663
ICollection. 330
Systещ.МultiсаstDеlegаtе. 347
IComparable.324
System.ObJect . 168
rComparer. 327
System.Threading.Thread. 578
lDictionary, 330
System.'JYpe.501
lDictionaryEnumcrator. 331
System.Web.Service.WebSernce. 1129
lDisposable.263 System.Web.UI.Control. 1055
IEпшпегаblе.315
System.Web. U1. Page, 1041
IEnumerator. 315
WebControl, 1059
lList, 331
абстраюныЙ.234
графических устройств (GDI). 804
базовый. 211
обобщенный, 435
изолированный. 226
обратноro вызова, 339
производный . 211
п олиморфный. 212
родитe.rrьскиЙ. 2 11
программирования приложеlШЙ. 44 статический , 141
с множеством ба.'IОВЫХ интерфейсов 31 3
Классичеrnое наследование. 211; 222
Интерфейсная ссылка. 304 .
Кпиент. 702
Информационный сервер Интернет (ПS). 746:
КлО.IШрование.319
1011
Ключевое слово
Исключение. 272
abstract. 142; 234
вторичное, 291
аЗ.242
оставшееся без обработки. 295
base.225
пользовательское,285
checked.399
системного уровнл. 284
const. lЗЗ
Пр.едметныЙ УkЭ,звrель Н6З

(kfa:u!t.428 мRoгoмoдyJlEныiii544
delegate. 344 общед;оступный, 471
dekle.l23 О.бщи:й:.714
iМ'лf.36.З· ощюмоДУш.ныЙ. 54: 44'9
eJ.1pliClt. 392 прИЩi'mыЙ', 464
n'''"'e.d , 407 conyrcтвуюЩИЙ •.44~
for.147 drатическИЙ., 632
'(OJ"e3clJ, 147 СТРОГО~Lченов~, 445
!Jnpl1c1t. 392 R,6нецлиmш,8ЗЗ
·lnterfare. 299 КOНRypf!НТI'IЫЙ ДQC'JYЛ. 586
iпtem.a] , 129 КанCOJ1bНЫЙ интерфейс ШllI&Эова1'еЛЯ {СЩ), 124
15.242 КОНШaFIта. 1.33
loCk.589 KOJl.C!l'JJYkТOp. 204
пamlЩрасе. 195 3а..ЦaнRый ШI УМОJIЧШIИlO, 12 {; 204
пёW. 120; 250 шmьзЬвaтf'.дьcRИЙ, 1,21: 204
oper<ltdr. 383; 393 ста'!ПЧесRИil·. 14 J
6fJ.t. НЗ Кoвтe.RC'I: 555
ФVеmdе. 2~)2 блt'.iКИРОшш. 589
рщЩщi , 14:~ GоадаваeмblЙlIЮ УМOJN<ЦnПO. &55
part:!al. 243 KOf.lTeh:ct.bo-независим:ьm ,ип. 555
pr:lvat e. ] 29 КОЕ,~RСТfЮ -CВJiзаН.ltllilЙ ТИП. 556
protecteo. 129; 225
protёcted illt.e mal129 .RJПOчеlЮе слово, 217
pulilic. 129 Мf:ню.780
reado111y. 13э M'Н"r.CRC'rН'Ыi1 атриб)l'li', 556'
rcl. 143 Контрнль ющца. Н)?4
.sl:aJecl. 227 Коор,цинмы
S'Шоf. 408 мировые. 8'l Б
эtасlmllос •. 406 при:борные. 81'5
statiG. 137 C"J'раничные, 815
tЫS.207 !Wnия [lOвеРЫJОСТНШI. ,З 19
tlщ:)w.277 Кореньцрилож~.253
l.1Ill.:heded ..400
цшщfе,402:
n
)Ща.ив:г no}'МОлчав.ию. 735
U5U1g, 70: 196;265
ЛОШU1Ънан uамяТБ ЩIТОна ('fL$), 539
'VirttIii] , 16912з 2
whеrе.4З2 М
w.hlle.148 Манифест. 53
)'ield.318 IЮМПЩiOВiiJ~!Ногоб]шit"d.. 449
fiOlnев:еuюе.217 УРО1ЩЯ ',\\,ЮДУЛ&. 450: 4'62
ИВОlD:\З..871 Маркер блокировки. 5'89
ВlJбираемая по умолчанию. 8&2 Марша.il}Пд 708
с· зависимой фиксацией. 873 ПО ЭlЩiЧени1О (МБVJ. 708
с н'езаБ'ИСИМОЙ ф'ИКС~)ДIiей, 873 по Gti'lJЛхе (MBR). 708
КоваРИa!fl'FlОСТЬ, 361 МаС:КИРЩ!Э.Юiое тенстоное !ЖНО. 869
Код операции CIL. 602; 6~ 1 М;;!ДЩЩ J,.в7
Itолле-ъ."цил обобщенная, 429 Н~ЫРОВ1fеИВhIЙ. 190
~i>IбинироваННDе'Оюш, 8/30 UРЖ>iоу['одьИЬ'IЙ, ! 89
Кdl\>mИЛJJТОР Me$ъIltJыool1e Ire'р~€crпgе ваеледозание. 460
CI1. 6О4 Меню,776
cS'C;. ехе , 80 tCQHTelOCТНoe.180
J1авЮ . ехе. 604 Meтai\aн1J:Ыe.53
опера.:rивЯЫЙ.448 /(ОМIlQ1ЮВОЧ!10г0 блt>:ка. 449
l{.dМПИЛJЩШl у~ная. 4J (.) тшщ.496
~oМnOHeнT. 752 Метка K(JДa. БО']
КОМll Оl'lовоч r:IЫЙ БЛОN. 50: 443 Метод.2Р3
диваМК'lее/шЙ. 632. g~t. 215
1164 Предметный указатель

МainО·116 Общий
set,215 компоновочный БЛОI(, 714
анонимный. 371 11ромежyroчный язык (СЩ, 53; 599
обобщенный, 424 ОбъеRТ, 120
статический, 137 номанды, 937; 959
Мировые координаты , 815 одиночного вызова , 712
Мнемоника CIL, 602 предусматривающий освобождение
Мнемоническая клавиша, 865 ресурсов, 263
Многодокументный интерфейс (МШ), 753 предусматривающий финализaциIO . 260
Мноroмодульный }(Омпоновочный блок. 54 соединения, 936
Модальное диалоговое окно, 921 сообщения. 704
Модель транзакции, 937
ЛDО.929 чтения данных, 939; 957
code ПОМ, 642 Объектная модель документов (DОМ), 1018
СОМ, 45 Объектныйграф, 253;679
локализации/делегирования, 228 Объекты поставщика данных ADO.NEТ, 931
ОДНО модульной страницы, 1026 Ограничение
страницы с внешним кодомподдержки. 1032 атрибута,523
Модификатор обобщения, 430
partial.243 Однодокументный интерфейс (8Ш), 753
доступности, 128 Одно модульный компоновочный блок, 54;
параметра, 143 449
Модуль, 54; 449 Окно
первичный , 54; 449 диалоговое, 919
Мониторинт файлов. 673 комбинированное, 880
модальное, 921
Н
отмечаемого списка. 877
Надпись . 865 с nисJtа,879
Наследование. 211 Оперативный компилятор , 448
конфиrypаuии,1115
Оператор
перекрестное,460
if/else, 149
форм , 921 swltch,150
Невыровненный массив. 190 Операuии со строками, 183
Необработанное исключение. 295 Операция
Несвязный уровен:ь ADO.NEТ. 969 &.404
Неуправляемые ресурсы , 260
".404
Не.явноеприведение типов. 241
+=.354
о =.355
Область клиента формы, 811 >,406
Обобщение. 413 ? ? 194
Обобщенная атомарная, 567
коллеКЦИЛ,429 восстановления иа объектного образа, 161
структура, 426 разыменования указателя, 406
Обобщенный создания объектного образа, 160
базовый класс. 434 Ответный файл, 86
делегат. 437 Открытый
метод. 424 интерфейс класса, 209
свободный тип. 431 ключ.,472
Обработка Отладчик cordbg.exe. 87
исключения. 278 Отложенная подци:сь, 476
множества исключений. 288 Отношение
Общая система типов (CТS), 48 локализации, 222
Общедоступный компоновочный блок , 471 подчиненности, 222
Обще.языковая Отобрcuнение, 501
инфраструктура (CLI), 75 Очередь финализации, 263
среда выполнения (CLR). 48 Ошибка
Общеязыковые спецификации (CLS), 48; 64 пользовательская. 271
программная.271
Пре.дМЕ\ЛIЫЙ укsдатель 1165
n Предста Rлешre. 989
Jla:p.3.Мe'rP J1pеооравовзиис:
I'J:Ы:ШJ,/Щ oiii. 144 неЯRНiое.300

~=~.1.4G '''IШ:ЛОЮ>IX ;!'ЮJmI. 391


~"113 .wшое,З91
Парaм:t::fjЖarrnaсмныki 3illJlPOC. 9tt4· Приfu:rpныli' Шlо.рдишш'Ы. 815
Л еp:в.ичньrй Прив;rrRЫ:И JillмдJ}ыв(}''!нъI.й Ш1Lт:.. 464
ЫDд;yJll•. 54~ 44В ПРJlmедение ТЮЮ:В. 240
{ю:rj)~ 938 вemшGt", 2'41
D е~р.У·ШЕ'ElflliIЁ -<'J'.!jeu ТШШ.· 206 ЯБнщ:.. 241
Л epe:r~ . .:ю6: ЗВ2 ПРИ~~ ~Qбыruий,З40
I1J'1.Ш!.Р аых 'JПеpaциii . З8~ ПрИНЦl'Ш pш!l'раничения О§Н3a:FfВооm', 124
IДШраций '11]ЭШ'!е,fJ iru: на 1'G:ЖДOCl'Веннoe"J'f;, 385 J1p:иuJl!fl'f'JIПЫЙ D D'J'Qlj;. :585 .
a:nep!iЦИЙ СР<i.!Шmnш. 386 ПРQГРaМJllШрование ~GГ{), ящ;кв;а~, 2]4
J"ББIрНЫ1liОUf! [!ёЩИЁ ЗВЧ ПрШ'ра:ммнан
П4' [}~jЦ'WiЩ загрpзшi сш 563
lШpRМf:чюв по ЭШl."leВИШ.143 ПIm,JЙКil. '27]
ссыл.сffiНЬJX, тиrnw.. Ш7' Прl:lЗJYo'JЧIIЫij a:re.EPr, 7(}4
Л е Ре&f.l6[· ТН\11! нac.ТleДI;JВa.вI1e.. 4€Ю Пр!ШЗВодный шmcс . 2.11
Пt'peМ е.lrfI:J.1Л ,\Шаза:п~дя. 4О I ПУЮСТРВВСТВО ИМi:f!
lJе[iJсопре,а~'1'!:ШIИ ме'lЮда.1:"lшШze(), 26 1 Syblcrn. 176'
Пе [il'Ш.CIJ.lEetнщ ,, 397 System.GodeDQM. б42
Л ере 9.'I~Hh. б 1 ~ 1;64 SYsi'em.СсЙJ~1Н!ШS 329
D ер.мar.:Н!IП'WШ: ДМlliЬШ ("'oo.kit'., 1 ] 05 System. Сo1W.cl.Юns. GeneШ. 4.Ж)
ПовеР!ili0,~ТВ31I.I\:И'lПW~ 3 1:9 S)!S1em..eo]J ~ Spt'..иialize8.. ~36
llQДЮIaCe. 21 ) S}ЪtcШL.СronрtmerJtМOOе}" 9 14
llQ,:шись SуstCilILСonfiguг.'l.tЮn. 491
IJ"I'Л\J1Ш'ШRЮI. 4,76 9ystemJЛrta. 935
цифр(),ш.ш:.. 472 sysr.em .D14gпcr.st1~ , ма
Л оле _ 1З2 Sу!<tеш.Dп{wJлg, 80'4
Д<WJiЫX.. 2(JЗ $.уsteшJJraw.Iпg. Dтawing2D. 830
!ШC'l"УrJFШt;: ТШJ~IШ дл.я ",',СНЯЯ. 135 ~temJО.б47
Гlо,шмС!рфизм.212 sуsrem.RetlесtJ (щ. 50 I
П QirШморфвЫй :юtJерфеи:G. 212 S:ys1em. Rеrk:сt.lщ~. Еn.зjt. 632
Лщшnmа цуБJnill!.aцJ!Ш. 487 Sу~tеl11.R~so,Ш'{;es. 851
Л{).чНCiСТhюоrrpeделeшrn~ :ю;т . 71 Sf:;tеш. Ruritl{iJte.5Ш"iаlizatii)n. 691
П OJIЬЗGиа'П"JIbl'lta.!] .оши{'J!tа.. 2 71 S-yы.em:.rъreading.
'5'17
ПОJI&30мon:-.JThC1аШ SjЖem.WеЬ :SеmЬе's. 1122
атр.и6у.r: 521 System.Wlntlo\Vs.FOrms. 75 J
lt01kJi!yIf!''Op. 12'1'; 204 ШlОжеинОt!.. ИЮ
Х<ос!l' CLR, 563 ЛТЙ;1ТОИОП
ЗЛf!I\!t:ит yщ:i<'lВ.DI'"J;ШIJ. ~0iJ. [)€'pi:да'Ч1i'l f'mreРТfШС'1'<:I(НIГГF'). 1009
ПО:1IЪ:;!ова.т\iЩЬ~1юе t.l1l!Sи. 'Web-f!tlj:iItИ1tЭ X:МL.. .1] 42
дItWI:arGBoe Of>11o.. '9 Н'! 'ТранеflортныЙi. 112'1
!ofСВЛЮЧШiflе.285 Прооесс. 5:~T

tlpt!iо(i.разФ!$-Ше CJ;ИI!ОВ.S91 ПРНМDУ'ГQЛЬF!юЙ :\I.Ш.сtив. ] 89


JJоw.rrлQ е ИМЯ: . 465 ЛС'.евЦt>:I!И?ii. 198
Поt'1:;CllЩL:IOi О1юrнъut.. '93'1 Пул I1rп~о:в:!д\. 595
абс:трwh1iЪШ, 94'0 р
Поетр~.~
РаClочш1' nОСГ JiШ. '539
IIO ~ ц" 996 Разг-раничe.tmе: 'ФБJlз:а:шю~. r24; 755·
~'I'р(~.к _ф(~efmIЙ. 954
~'1CII"''Ula~ 1':ЙD'оп. , 242
Потере 3'I1<i"IИl\oЮcrи. :за?
раcnреДt"т:Нnая :МСщеillЪ KOМ'cloeeH'I'HblX
Лоrr(ж, flЗБ
объe:J<ТС'!EI (DCQ~Чт 741
перl!~ftПiы[l. 538 Р,~crrщрени(" обnбЩ€! fIJ;lЩЮ I\Лас~~. 434
ПРИdрuте~.585
reWИЗ~ИJr ЩПfOрфffiса . .300
раБQ"!Юi.539
.яВFfа.n, 3 1Q
фШ;lФВblЙ. 585
1166 Предметный указатель

Реальный агент. 705 Статический


Реryлярное выражение, 1076 класс, 14]
Реl\ОНСТРyrщия объекта. 677 компоновочный блок. 632
Родительская форма, 800 кон структор, 141
Родит~ский класс. 211 метод. 137
Статическое
с
свойство. 221
СБОРIЩ мусора, 253
связывание , 525
Свободный обобщеюIый тип. 431 Стек,151
Свойство . 215
вьшолнения . 603
доступное только для записи. 220
Стенд тестирования объеь."I'Oв (агв-тестер). 109
доступное только для чтения, 220
Страница тестирования, 1125
именованное, 522
пользовательская, 1125
статическое, 221
Страничные координаты. 815
Связный ypoBeHbADO.NEт. 951
Строгое имя, 471
Связывание
Строго типизированное исюпоченИе. 285
динамическое, 513
Строка
статическое , 525
соединения . 952
Сеанс . 1101
состояния, 785
Сеансовые данные, 1101
Структура, 60
сооЮе. 1105
обобщенная, 426
Секретный ЮIЮч, 472
Стыковка, 925
Семейство шрифтов. 822
Стыковочное поведение элемента
Сервер , 702
управле ния. 925
ПS.I011
Схема лизингового управления. 735
UDDI.1120
Сценарий
разработки ASP. NEТ 2 .0. 1012
клиента. 1017
Серверный элемент управления, 1052
сервера. 1020
Сервис Windows, 742
Сериализацlffi,677 т
Символ Таймер обратн.ого вызова. 593
DEBUG.410 Текстовое окно.866
форматирования, 247 маскированнос, 869
форматирования строк, 127 ТИп. 59; 115
СшП'лет, 712 Llst<Г>.421
Синхронизация, 572 System.String. 182
Синхронный вызов. 572 агента. 1118
Событие. 364 вложенный. 229
Patn1.810 делегата. 62
Сопутствующий компоновочный блок 449 интерфейса. 61
Состояние класса. 59
пр~дставления, 1084 .kohteItctho-независдмыЙ. 555
приложения, 1092 }(OHTeRctho - связанныЙ. 556
сеанса. 1091 парциадьНЫЙ. 243
qл ементауправления. 1087 перечня . 61
Спе цификации WSI, 1122 ссьmо<ШыЙ . 151
Спонсор. 740 структуры. 60
Спонсорство лизинга, 740 с разрещением прrornмaть зН?чение nuI1. 192
Сравнимые объс!('Гы , 324 указателя. 401
Среда CLR. 65 хар сштеризуемый значением, 151
С сылка Точность типа. 683
на интерфейс. 304 Транспортный протокол , 1121
на константу , 134
У
Ссылочный
УдалеНIIоевзаимодеЙствие.701
параметр , 145
асинхронное. 748
тип. 151
Указатель на следующий объект, 252
Стандарт UDDI, 1158
Управление состоянием. 1083
Статические данные. 138
Предметный указате:пь1167'

'УправlIВемал дивамичеCWlR llilМЛТЬ . .121 ресурсов .NEТ,850


УnpавJI.IIf."МЫЙ npаrpaммнъrй [('ОД. 5р с~И&1щзации. 68 I
Yn.РaIlШ\ЮЩaR ШJCЛfЩоватеJ,lЪ:ВОcn:., ] 8$ ФQрматирЩ\ани{'. ctp!ж. ] 26
"Yt::J'aнoБI{'a Фцрщттер, 693: 707
.NEТ Frаш.еwork2.0 SDK, 79 Фу1-пtцин.20З
StJar.pDeYelop. 94 о.бра.тно.го. ВЫЗова. 343
Tex:tPad. 89
\'lsual С# 2005 .Eh:press. ~9 Х
\11sual stшl10 2005, 100 ХешйроВ!UЩL"<lЙ :МОД..472
У'J"I"~каПaм.Бn\ •. 1,28 Хостинг сш. 5630
УI'ИЛИ'l'а Хранима:я пр{Ще~, 965
dUDlpb.in.exe.401-6 Ц
ga{':\!11l..ше.475 Цвет.овая IЮпс-raнТ'а ARGB, 820
iblasm.eк<1, 72 цикл
peve.r:lfy.ext"'. 6'] 1 do/wbl1e. 14В:
гe-sge.n.ше. 85~~ [ОГ, 147
SD.exe., 473 J()ceach. 147
WebDev:WebServer.exe. 1 Q12 wl1ile. 148
wsd1,eIШ. 114-5 Цифровая .rfOДIШСЪ. 472
адмивис..jJИрОВ8НИН узла, 1114
'IШ,l,lфигурации .NEТFramework 2.0; 469 Ч
Чfi.rПю'ШШr Т.ex:Rолorия vазработки. 604
Ф Член
Файл абстрактНюй.212; , 234
*.ге80uп::е's.851 nиртуаJiьЮdЙ. 172; 212'
•..I'еSХ. '851
дерerpУЖCDНbIй:. 122; 206
conki.e. J 104 пmа.62
.corclbg.eKe, 87 Члев-neремеmraл.203
~sc. ex.e. во
сsс> . rSp. 87 Ш
GlobaJ..asax. ] 088 Шаблон
Web.canfj.g, ] 10В Jv,I~j<И, 869
КQIнjхИ1)"РaI(l'1И маШИИЬL 4:92 стр<I.EщЦЪ!, 106]
JiQНфиrjтpю:rии npиложенив.. 466 Э
J~~НфЙГ}'Раци:и удал€RНого ЭлеМб!Т
1'!зацмодеЙСТВИfl. 725 <СDnnесtJрщ5t,riЩ~:». 947
OТ'tIетныЙ. 86 3лем.ент упрaв.n:ения .7~2; 859
ФиваЛЙl1aци.я , 260 Web-фоРМbl.10.52
Фдar
дочернЩi . 197
J clJecket'l. 400 rюльзщ!~Сlrn:й. 904
/u.nSafe. 402 оерверный. 1'052
форматированннСТРOR.127 l' цpoкpyт.кo~ . 892
ФО1_уt BBOД~.
768
ФОfJа2ыИ ПОТОК. 585 R.
Фарм.ь Явная реалмзация интерфейса, 310
ДО'lepRЯа.801 Явноеприведениет.ипов,241
РQДИтe.rrъ.crш,я.. 800 Язык

Формализованный шаблон освобождения WSDL, 1120; 1137


pe(.·ypCI'JB. '268 Г1шеРТeJ{С'!'ОВОЙ раз.меТlU1 (НГМЦ, 1о JЗ
Формат опредмен.и.в :ивrepфt"'й('в (1ПL), :53
(i.dМПОlюooчnога '6лО1.Щ, 446 Ярлыкv.а1uе, 2]7
язык ПРОГРАММИРОВАНИЯ С # 2005
3-,._н., ИПЛАТФОРМА . NЕТ 2.0
Унажа<'мый "Illтатсш,!
Первое издание этоii KНJlГfI было J'РСДl-"ТаОЛeJЮ , Щ КOIJфеРСIЩIШ

.., _ С Е. __ в Лl:Jlа IlТС, шг. Джорджин, в 2001 ''ОД}" В то I3рсмя Jlлатфuрм'\


.NET была сше па у рОlше бета'Вt:РСИИ, пroтому и К ЮII'" была,

""- ......-"'"
"" I6ТU"bIfJtlIl
IЮСУТИ, еще рукоrшсью. При ;)ТОМ I IСЛЬ..'IJI скаЗ<I:rъ. 'по ' lepftblC
Ш!а излаtmя КПИНi был.и uсуда'flIЫМИ - в катеГОРИJI КIIИ-Г
по програ.мМIJроваmпu книга I3bl,дВJlгалась на 11000УЧСIJИ('
fXI4f".NПD I ~ Щ>СМlшJoIt Л\\'ard в 2002 I'ООу, а в 2003 ''ОД}' ей БЫJlа IlРИСJЮСIli\
ЪeafleJlErU,. .tad прсмия Rct('l"('ncC\\'arc Ехссll"лсс A\\-·ard. Но. участвуя [:осле
Afla' dм~ ЭТОЮ в pa3p.tOOткe общ"языкOIЮЙ С[1Сды 1II,ПТОJ[D('I:ШЯ (CLR),
Я CMOI' npLliiTJ1. к 6олсt:' l"Лу60кому IЮIПlМi\lШ.ю ВU3МОЖlJостей
DIICOIII"".913.0
'" "~""'" ПJш'fформ:ы .NET 1I тонкостеА языка п рограмми роваЮIS\. С#,
и ТВlcpb мо.гу 3аЮlитЬ, 'ттотреТЬС ИЗДВIIне ;JToii КIШГН Ile менее
полно. чем МОII ,",наш'я в .}тоЙ oful acтlt.
Если 8Ы Чl1ТilJII1 r: (Х"дыдущие 1Iз,даН1I,н I<IJИrn, пам буд<'Т "РИЯТII.о
УЗ llаТl" 'Jтo u TPCTI,C издание доб.ШЛСfЮ IlCCKOJlbKO нопых ГJlап.
К,юме ТОГО, ТСК('Т глав, J1ОСlJЯЩCJШЫХ 1"'P<"IMIoI<lТlIIte 06щero
Pd3 . . ._ ... · • промежутоЧJЮro я:н:.гка (С I L), о(юбщеuШI.М .NET и сервисам
. . . . . . ~IIO •• _ _
сср"аJШЗJЩIIИ объектон был ПОЛlfOС'fЬЮ нерсрабornl1 с У'I"t"Гом
-.=:$ &$1." ......
IIODЫ); IJОЗМОЖIIОt:теЙ. п редлагаемых платформо.Й ,NET 2.0 (YrO,
2 qм.......
••.., "* 5 СО: "QiFI
на п ри мер. 'ЛШ Ы, допускаЮIШlе зtlачеllИС " uH, коварИ',шпю..;ть
nCJICraтoD, шаблон ы страJlИЦ ЛSР .NET 2.0 11 »оные злем~пты
управления Wiooo\\'!'j Fопш. IJCOO1H,,'JYCMblt:' ДЛЯ созданиS\.
• • iJ '1 r.lК Ra;jhlВИемых 'СЧХ)I( СОСТОЯНИЯ· ).
n., г

*Wo,.. ::аь • ',.


· ••.,. r' ,. Если жс вы З"ЗКОМНl'C{'ЬС материалом зroii КlfffПI
10. ,ItOJ!ЖU Ы (L\leтb ВВИДУ, что Оllа ПРСДlIззнаЧСIICl
нп<,рl:lые,

---
о; р . ,..

'''_.3 I
.... . . .
yr
.. np ... , . а
:
l$ 3I

::;11',,,,, 1$!1.1
для СПСЩJалuсто,П 8 областн разработки програ ммноro
обеСIlСЧСIIIНI и студеитоо старших KypcOtl, IIзучающих
и..нформати ку. Поэтому 111" СJlеДУСТОЖJщать, '11'0 в не.!
"ССКОДЫ(О ГЛdВ будет ПО"DЯЩCJЮ IЩ"К.lШ.М н КOHCТP)'l(ЦIJSl.M
УСЛО8ВО/'О Щ'р('};ода. Нitшсй цел ью Я1lJlН{"f{'Я "оздание н адежной
базы для п рограММНPUВaJ-JИЯ на языкс С# 11 ПОlШМ<J,II.ИС
ОСНОНIfЫ:Х ЗJ1емеllТОВ 11 ВО3МОЖllOстсilШJilтформъь . NЕТ
(сборок, удалеШlOfО roаимодейсТ8НЯ, windows Forms, Web
Foгrns. АОО .NEТ, wсь..служБ ХМL IIT.II,). Освш:.н материал.
[)реДСТa.вJJсШ:tl,ш в 25 ГJlil вахзroй КIШГИ, вы СМОЖt'ТС
щmменить НОЛУЧСIIН Ы(' :шаllИЯ Б той конкретной области
WWW\' .SI 3 !1PA=~ u роrраммироnания, в котороН работаете 11101.)1 самостоятельно
rl fЮдOJlЖ ИТЬ да 'D>IIСЙШсе исследова.н ~IС IICWt:ЧСРl1асмы х

Apress· во.1можno('Тсil . NЕт.


Жслаю вам )'Д3'IИ, Эндрю ТрОСЛССfI.
WW"\' J т cnm

c.2005.

''''''1
.".-~
~

.. --
NП"l-t.

. " .ot'p .,
_ _ __
• '".... , _ ..
_ " . , ..... ~f\>Co!IO_"Vin;:o..r
_ . faroa. ",,,,·r M . '""
Ученье - свет, анеученье - тьма
ШlpoдшIlI МУДРОСТЬ.

Да будет Свет! - сказал Господь


БОJkeCТ8eНШlII МУДРОСТЬ

NataHaus - Знание без границ:


С"IЮМJЮС ОО[[ЛQЩСIIНС I lатюДlЮИ и ООЖСCТ1JeIll ЮЙ МУдlЮС'ПI.:-)

библиотека
форум
каталог

Вам также может понравиться