Академический Документы
Профессиональный Документы
Культура Документы
2-е издание
Управляй
данными Познай секреты
с Ы № абстрагирования
и наследования /
Изучай С#
на забавных ^
примерах
Узнай,
как методы Научись
расширения эффективно
облегчают жизнь использовать
программиста обобщенные
коллекции
Изучаем С#
2-е издание
Э. Стиллмен
Дж. Грин
С ^ П П Т Е Р '
М о скв а ■С а н кт-П е т е р б у р г ■Н и ж н и й Н о вгород ■В о р о н еж
Р о с то в -н а -Д о н у • Е ка тер и н б ур г ■С а м а р а - Н о в о си б и р ск
К и ев • Х арь ков ■М и н с к
2012
ББК 32.973.2-018.1
УДК 004.43
С80
ISB N 978-5-459-00422-9
ББК 32.973.2-018.1
УДК 004.43
Права на издание получены по соглашению с O'Reilly. Все права защищены. Никакая часть данной книги не может быть
воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные.
Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать
абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные
с использованием книги.
ISBN 978-1-4493-8034-2 англ. © Authorized Russian translation of the English edition Head First C# © O'Reilly
Media, Inc. This translation is published and sold by permission of O'Reilly Me
dia, Inc., the owner of all rights to publish and sell the same
ISBN 978-5-459-00422-9 © Перевод на русский язык ООО Издательство «Питер», 2012
© Издание на русском языке, оформление
ООО Издательство «Питер», 2012
авторы
Эндрю
А в т о р эт о й ф о т о гр а ф и и (как
и с н и м ка канала Гованус) ~
Ниша Сондхе
О одерж ание (с Б о д к а )
Введение 23
1 Эффективность с с#. Визуальные приложения за 10 минут 35
( ^ о д е |^ Ж а н и е ( н а с л іо й г Щ е е )
Введение
Ваш мозг и С # . Вы учитесь — готовитесь к экзамену. Или
пытаетесь освоить сложную техническую тему. Ваш мозг пытается
оказать вам услугу. Он старается сделать так, чтобы на эту оче
видно несущественную информацию не тратились драгоценные
ресурсы. Их лучш е потратить на что-нибудь важное. Так как же за
ставить его изучить С # ?
Для кого написана эта книга 24
Мы знаем, о чем вы думаете 25
Метапознание: наука о мышлении 27
Заставьте свой мозг повиноваться 29
Что вам потребуется 30
Информация 31
Технические рецензенты 32
Благодарности 33
7
оглавление
э ^ '^ е К т и Б н о с ш ь С
D
«П(« Как превратить ВАШЕ приложение в приложение для ВСЕХ 69
Передаем приложение пользователям 70
Oala access
Работа НЕ окончена: проверим процесс установки 71
Управляющее данными приложение готово 72
оглавление
э щ о Б сеГ о Л и Ш ь К оД
Под покровом
Вы — программист, а не просто пользователь
ИСР. И С Р может сделать за вас многое, но не всё. При на
писании приложений часто приходится решать повторяющиеся
задачи. Пусть эту работу выполняет ИСР. Вы же будете в это
время думать над более глобальными вещами. Научившись
писать код, вы получите возможность решить любую задачу.
А л я т х д с й програм м ы вк
Р™ , « Ч Е Т
(Сля£;с имен
ф р а гм е н т бйиде«
(омен»
АМХА&неки&л^ограммы ^
могитCOC^v\oi:^A\t:>
и з й е г о о д н о го к л а с с а ;.
К л а с с в ю \к / ч а е у л о Зми и л и н с с к о л о к о
A^£Уv'.o^oв. M£^■vxoдo^всегё^а
какому-либо
'Л из о м р а ь у ^ о р о в ,
оглавление
Приемы программирования
Каждая прогроАлма решает какую-либо проблему. Перед на
писанием программы нужно четко сформулировать, какую задачу она будет
решать. Именно поэтому так полезны объекты. Ведь они позволяют струк
турировать код наиболее удобным образом. Вы же можете сосредоточить
ся на обдумывании путей решения, так как вам не нужно тратить время на
написание кода. Правильное использование объектов позволяет получить
интуитивно понятный код, который при необходимости можно легко отре
дактировать.
гоипь! и ссылки
L
10:00 утра. Куда подевались наши данны е?
Без данных програллмы бесполезны. Взяв информацию от поль
зователей, вы производите новую информацию, чтобы вернуть ее им же.
Практически все в программировании связано с обработкой данных тем
или иным способом, в этой главе вы познакомитесь с используемыми в С #
типами данных, узнаете методы работы с ними и даже ужасный секрет объ
ектов (только т-с-с-с... объекты — это тоже данные).
Dog fldo; Две ссылки это ДВА способа редактировать данные объекта 176
Dog lucky new Do g (); Особый случай; массивы 177
Массив 177
Массив может состоять из ссылочных переменных 178
Добро пожаловать на распродажу сэндвичей от Джо! 179
fido = new D o g O ;
Ссылки позволяют объектам обращаться друг к другу 181
Сюда объекты еще не отправлялись 182
Играем в печатную машинку 187
lucky = null;
poof!-~
/ \ \
11
оглавление
инкапсуляция
Ш еШ ю і». FuKy
(•їрсгріпм '' №oratloHi
WMbervf
peo^e.
ыт
AluikU«t
СтНїівв:
fSAieowitoH
total cut)
Pamv
raney
deeoratioH^
> І*і;рсггіпм
•VOdutrathig
ful
i=oo4»2$|i<r
(НГМЯ) NomuU
АМЫ, Picontlnu
(*20|ИГ №ЇОр(Г
liemid
s s s s a is i
12
оглавление
наследование
13
оглавление
интер^^ейсы и а^ аи р ак тн ы е классы
Наследование
Вернемся к нашим пчелам 284
Классы для различных типов пчел 285
И нтерфейсы 286
ДбсторакЦия
Классы, реализующие интерфейсы, должны включать ВСЕ
методы интерфейсов 289
^|нкаисуляЦия Ф Учимся работать с интерфейсами 290
Ссылки на интерфейс 292
Ссылка на интерфейс аналогична ссылке на объект 293
IJoiuMop'fuaM Интерфейсы и наследование 295
КоЬоВее 4000 функционирует без меда 296
Locsl&on Кофеварка относится к Приборам 298
Name
Exfts Восходящее приведение 299
Нисходящее приведение 300
Oescrip&mO
Нисходящее и восходящее приведение интерфейсов 301
Модификаторы доступа 305
R oom O utsid e
Decoration Hot Изменение видимости при помощи модификаторов доступа 306
Классы, для которых недопустимо создание экземпляров 309
Абстрактный класс. Перепутье между классом и интерфейсом 310
Как уже было сказано, недопустимо создавать экземпляры
некоторых классов 312
14
оглавление
п е р е ч и с л е н и я U К о л л е к ц и и
15
оглавление
16
оглавление
17
оглавление
собьдпия и ДеЛеГашь!
у г л о м и на
т а к о е расст оя н и е
дольш е, чем 28 32 .).
Р 1 Ь с Ь в г .С а Ь с Ь В а 1 1 (7 0 , 90)
18
оглавление
O&beV^
19
оглавление
Наводим красоту
20
оглавление
21
оглавление
LINQ
Управляем данными
Этот мир управляется данными... вам лучше знать, как в нем жить.
Времена, когда можно бы ло программировать днями и даже неделями, не
касаясь множества данных, давно позади. В наши дни с данными связано
все. Часто приходится работать с данными из разных источников и даже раз
ных форматов. Базы данных, XM L, коллекции из других программ... все это
давно стало частью работы программиста на С#. В этом ему помогает LINQ.
Э та функция не только упрощ ает запросы, но и умеет разбивать данные на
группы и, наоборот, соединять данные из различных источников.
22
к а к р а б о зза а ш ь с эш ^ й к н и Г о й
Введение
[З а м е т к а о т о т д ела продаж-
в о о б щ е -т о эт а кныга для лю бого
У кого е с т ь деньги.]
24 введение
введение
^^Ж но. ^
U мы знаем, о чем думает Ваш мозг /
Мозг жаждет новых впечатлений. Он постоянно ищет,
анализирует, ожидает чего-то необычного. Он так устроен, и это
помогает нам выжить.
В наши дни вы вряд ли попадете на обед к тигру. Но наш мозг
постоянно остается настороже. Просто мы об этом не знаем.
Как же наш мозг поступает со всеми обычными, повседневными
вещами? Он всеми силами пытается оградиться от них, чтобы
они не мешали его настоящей работе —сохранению того, что
действительно важно. Мозг не считает нужным сохранять
скучную информацию. Она не проходит фильтр, отсекающий
«очевидно несущественное».
Но как же мозг узнает, что важно? Представьте, что вы выехали
на прогулку, и вдруг прямо перед вами появляется тигр. Что
происходит в вашей голове и в теле?
Активизируются нейроны. Вспыхивают эмоции. Происходят
химические реакции.
И тогда ваш мозг понимает...
д ал ьш е > 25
как работать с этой книгой
V a к н и г а ДЛЯ г о е - . Н™ £ 5
к а к « ы ч т о -т о "^ к о
Затолкать в голову поболь Ф g ^огии и психологии обучения, для
О с н о в н ы е п р и н ц и п ы с е р и и « H e a d F ir s t » ;
наглядность. Графика запоминается гораздо лучше, чем о б ьн ны и
V o I
—
столом или лекция?
Ппка вы не начнете напрягать извилины,
А кти вное участие чит ‘ читатель должен быть заинтере-
в вашей голове ничего не п р о ф ормулировать выводы
сован в результате; он долж р g д^^^лы упраж нения и
= е Г п " ; „ : = ^ ^
А
•Х -= Е
Г = Г о , . т е ^ и ^
\ ;г :;г г г ::;« ч » .с е ,„ ,й к .5 о а
26 введение
введение
К а к ж е У Б Е Д И Т Ь м о зг, ч т о п р о г р а м м и р о в а н и е н а С #
та к ж е в аж но, как и ти гр ?
Есть способ медленный и скучный, а есть быстры й и
эфф ективны й. П ервы й основан на тупом повторении.
Всем известно, что даже самую скучную инф ормацию можно
запомнить, если повторять ее снова и снова. П ри достаточном
количестве повторений ваш мозг прикидывает: «Вроде бы несущественно,
но раз одно и то же повторяется столько раз... Ладно, уговорил».
Быстрый способ основан на повы ш ении активности мозга, и особенно
на сочетании разных ее видов. Доказано, что все факторы, перечисленные
на предыдущей странице, помогают вашему мозгу работать на вас.
Например, исседования показали, что размещение слов вмуте/ж рисунков
(а не в подписях, в основном тексте и т. д.) заставляет мозг анализировать
связи между текстом и графикой, а это приводит к активизации большего
количества нейронов. Больше нейронов = выше вероятность того, что
информация будет сочтена важной и достойной запоминания.
Разговорный стиль тоже важен: обычно люди проявляют больше внимания,
когда они участвуют в разговоре, так как им приходится следить за ходом
беседы и высказывать свое мнение. Причем мозг совершенно не интересует,
что вы «разговариваете» с книгой! С другой стороны, если текст сух
и формален, то мозг чувствует то же, что чувствуете вы на скучной лекции
в роли пассивного участника. Его клонит в сон.
Но рисунки и разговорный стиль —это только начало.
д ал ьш е > 27
как работать с этой книгой
все для повышения вероятности того, что материал будет закодирован в не
скольких областях вашего мозга.
Мы используем концепции и рисунки несколько неож иданным образом, пото
му что мозг лучше воспринимает новую информацию. Кроме того, рисунки и
идеи обычно имеют эшщитшльное содержант, потому что мозг обращает внима
ние на биохимию эмоций. То, что заставляет нас чувствовать, лучше запоминает
ся —будь то гиутка, удивление или интерес.
Мы используем/»озгово/шый стиль, потому что мозг лучше воспринимает инфор
мацию, когда вы участвуете в разговоре, а не пассивно слушаете лекцию. Это
происходит и при чтении.
В книгу включены многочисленные упражнения, потому что мозг лучше запо
минает, когда вы работаете самостоятельно. Мы постарались сделать их непро
стыми, но интересными —то, что предпочитает большинство читателей.
Мы совместили несколько стилей обучения, потому что одни читатели любят по
шаговые описания, другие стремятся сначала представить «общую картину»,
а третьим хватает фрагмента кода. Независимо от ваших личных предпочтений КЛЮЧЕВЫЕ
полезно видеть несколько вариантов представления одного материала. МОМЕНТЫ
Мы постарались задействовать оба полуишрия вашего мозга; это повышает вероят
ность усвоения материала. Пока одна сторона мозга работает, другая часто име
ет возможность отдохнуть; это повышает эффективность обучения в течение
продолжительного времени.
А еще в книгу включены истории и упражнения, отражающие другие точки зре
ния. Мозг качественнее усваивает информацию, когда ему приходится оцени
Беседа У камина
вать и выносить суждения.
в книге часто встречаются вопросы, на которые не всегда можно дать простой
ответ, потому что мозг быстрее учится и запоминает, когда ему приходится что-
то делать. Невозможно накачать мышцы, наблюдая за тем, как занимаются дру
гие. Однако мы позаботились о том, чтобы усилия читателей были приложены
в верном направлении. Вам не придется ломать голову над невразумительными
примерами или разбираться в сложном, перенасыщенном техническим жарго
ном или слишком лаконичном тексте.
В историях, примерах, на картинках использованы антропоморфные образы.
Ведь вы человек, и ваш мозг уделяет больше внимания людям, а не вещам.
28 введение
введение
дал ьш е > 29
как работать с этой книгой
Ч то Вам потребуется
Эта книга написана при помощи Visual C# 2010 Express Edition, работающей с C# 4.0 и .NET Frame
work 4.0. Именно в этом приложении были сняты все скриншоты, поэтому мы рекомендуем устано
вить себе именно его. Если у вас установлена Visual Studio 2010 Professional, Premium, Ultimate или Test
Professional-версия, картинки будут отличаться. Где необходимо, это будет оговариваться. Бесплатно ска
чать версию Express можно с сайта Microsoft.
Скачать и установить версию Visual C# 2010 Express довольно легко. Дистрибутив программы находит
ся здесь:
h t t p : / /w w w .m ic r o s o f t . c o m /e x p r e s s /d o w n lo a d s /
Для выполнения упражнений из этой книги вам не потребуются никакие дополнительные параметры
установки, но вы можете воспользоваться ими, если хотите.
IfistaMatkm Options
O Q * ^ IO »-2010
Sefect iht Gp^on^ produst(s) you wouki ice to îTBîsi.' Если y вас уст ановле
^HiCK»softS<^S«ver2IM »Ex|»«ssSenncePadkl(x«4)(0(W fa(H idStze; 104 на долее старая версия
SQL 5 e w Express integrates nith Visu^ Studio to {ïsvkie b e æ ami
Visual Studio, или .NET
a r v e r - d a tie s c capetàtes. Framework, некоторые
примеры из книги м огут
не сработать.
Скачайте и установите Visual C# 2010 Express Edition. Убедитесь, что установка завершена. На вашем
компьютере окажутся интегрированная среда разработки (которую вам предстоит изучить),
NET Framework 4.0 и другие инструменты.
После установки в меню Start появится новая строка: Microsoft Visual C# 2010 Express Edition. Это
команда для вызова ИСР. Вы полностью готовы к работе.
30 введение
введение
д ал ьш е ► 31
обзор команды
Технические рецензенты
!<р,'UC
Лиза Кельнер ^»рроус
Дэвид действи
тельно нам помог,
показав некоторые,
приемы работы
с ИСР.
При работе над этой книгой мы столкнулись с массой неточностей, вопросов, проблем, опечаток
и даже ужасных математических ошибок. Впрочем... все было не так ужасно, как можно подумать. Но
мы все равно очень благодарны за ту работу, которую выполнили наши технические рецензенты. Кни
га вышла бы в свет с ошибками (парочка из которых довольно серьезны), если бы у нас не было самой
лучшей на свете команды...
Прежде всего хотелось бы сказать большое спасибо Крису Барроусу и Дэвиду Стерлингу за техни
ческое сопровождение. Также хотелось бы поблагодарить Лизу Кельнер —это уже шестой обзор, ко
торый она для нас делает, и именно она сделала конечный продукт настолько читабельным. Спасибо,
Лиза! И особая благодарность Нику Паладине.
Крис Барроус —разработчик компании Microsoft из команды компиляторов С#. Его специализация —
дизайн и реализация различных свойств языка C# 4.0.
Девид Стерлинг работает в команде компиляторов Visual C# последние три года.
Николас Паладине был одним из наиболее ценных специалистов Microsoft, занимавшихся .NET/С # . Он
имеет 13-летний опыт в программировании, особенно в позиционировании технологий Microsoft.
32 введение
введение
Благодарности
Редакторы
Мы хотим поблагодарить наших редакторов Б р е т т а
Маклафлина и Кортни Нэш за работу над этой книгой.
Бретт был автором большинства рассказов, а идея главы 14
принадлежит ему полностью. Спасибо!
Брет т Маклафлин
Кортни Нэш
Визуальные приложения
за 10 минут
Зачем В ш изучать C #
lACPj или Интегрированная
C# и ИСР Visual Studio облегчают и ускоряют процесс Среда Разработки, --
написания кода. э т о программа для
редактирования кода,
управления файлами
_ , ^ и публикации проектов.
Задачи, которые за бас решает UCP
Чтобы поместить на форму кнопку, вам потре
v o i d I n i t i a H .e C o m p o n e n tT ,
буются большие куски повторяющегося кода.
th is .SuspSi, |y“ “ , f ■W ndow B . P o rm a. B u tto n () ;
/ / b u tto n l
56) ;
in ,.s i.e „ s , .3 , ,
‘ s t a t i c claB B p ro g ra m
) f
Встроенные в С*
j^g-f FrAmeworK
C =3I ИжЛ
How good"’ ©
)Let% get Parted!]
i/ir p V iS M ^ !
УЛе
Studio с т р и ^ ' 0 - , - .................—
избавляют » f .
0^ рутинное paoom
Aft ritfht! ЬйИКПШ ■■••■■■•*■:
доступ К данным
36 глава 1
эффективность с C#
дальше > 37
помогите начальнику
Найдите способ
Имя: Laverne Smith быстро перекинуть
эти данные на
Фирма: XYZ Industries ноутбук директора.
Телефон; ( z i z ) s s s - s i z ‘i
Email: LavemeSmith^XyZindustriescoM
Клиент; Yes Поел, звонок: 05/я& /07
..,- 1 -W « * ,
■Muirtiiiiln'ir чш —
38 глава 1
эффективность с С#
дальше ► 39
ваша цель
Ч то мы собираемся сделать
Нам потребуется приложение с графическим интерфейсом
пользователя, объекты, взаимодействующие с базой дан
ных, собственно база данных и программа установки. Вся эта
огромная с виду работа будет проделана к концу этой главы.
Вот структура нашей будущей программы:
Ве>|
элем еи ^б
упрлбдения.
Команда DELETE ^
ОЭьектВ«'*''
Э т и обьекты являются
элементами управления
адресной книги. 'Ш ^ обгр
\гШ у -
„ „ „ Z I
40 глава 1
эффективность с С#
Готовая программа
поме-щаетсй внутрь
инсталлятора \лЛпйо^5.
.е х е
Ол
б а з а данны х
I Файл
программы
Отде -
лу продаж
'ч останется
4 л >^олько за ~
1л уст ит ь
эт от ин
сталлятор.
дальше > 41
приступим
....
iSrai^hbsSailedTempiatti
Im t^ T e m p t^ e s
№tdov«sFt»msAf)p<»catK3n Visuet С *
УвиЫС»
А pro^ect fof <feetjng an äpplicebon with »
Windev« Forms user tnteiface
Q a isLibn ry Visual С *
; WPFApplicrtksn \ftsuel С *
at
Forml.cs Forml.Deslgner.cs
ßce эт и файлы Visual Studio
создает автоматически.
42 глава 1
эффективность с C#
Возьми в руку карандаш
« .В О З І
Ниже показан возможный вид экрана. Вы должны понимать назначение большинства окон
ч и файлов. Убедитесь в наличии панелей Toolbox и Error List, вызываемых одноименными
командами меню View » Other Windows. В пустые строки впишите назначение каждой части
ИСР, как показано в примере.
дальше > 43
изучи И СР
ЗЭесь собраны
визуальные
элементы
управления, ^
которые \ ®
можно
перетащить
на форму.
здесь
\^оказаны
свойства
выбранного
эдеменилд
управления
суормы-
44 глава 1
эффективность с C#
_ Часзцо
ЧадаБаеМые
Б оЦ Р о Сь !
Если код создается автоматически, я загрузил и установил бесплат Окно ИСР отличается от показан
не сводится ли изучение C# к изучению ную версию Visual Studio Express. ного на картинке в книге. Что делать?
функциональности ИСР? Достаточно ли этого для выполнения
упражнений из данной книги или мне ; Для перехода к настройкам по умол
I Нет. ИСР поможет вам в выборе
потребуется купить другую версию чанию выберите команду Reset Window
начальных точек или измении свойств
приложения? Layout в меню Window, затем восполь
элементов управления форм, но понять, зуйтесь командами меню View » Other
какую работу должна выполнять про ! Все упражнения из этой книги вы Windows, чтобы открыть недостающие
грамма и как достичь поставленной цели, полняются в бесплатной версии Visual окна.
можете только вы. Studio (которую можно получить на сайте
Microsoft). Различия между версиями
Express, Professional и Team Foundation
Я создал новый проект, но не никак не повлияют на процесс написания
нашел его в папке Projects, вложенной
в папку Му Documents. Почему?
профамм на C# и создания полнофункцио
нальных приложений.
Visual Studio
Q ; Новые проекты Visual Studio 2010 генерирует код,
Express помещает в папку L o c a l
Mojy ли я переименовывать фай
S e ttin g s \A p p lic a tio n
D a t a \ T e m p o r a r y P r o j e c t s . При
лы, созданный ИСР?
который можно
первом сохранении вам предлагается Q ; Конечно. Новому проекту ИСР
задать новое имя и поместить в папку по умолчанию присваивает имя F o r m l использовать
Му D o c u m e n t s \ V i s u a l S t u d i o (F o r m l. c s , F o r m l. D e s i g n e r . c s
2 0 l 0 \ P r o j e c t s . При открытии нового
проекта или закрытии временного, вам
и F o r m l . r e s x ) . Ho его можно поменять
в окне Solution Explorer. По умолчанию как основу для
предлагается сохранить или удалить имена файлов совпадают с именем
последний. (ПРИМЕЧАНИЕ: Отличные формы. Как можно видеть в окне Properties,
их изменение не влияет на имя формы.
программы.
от Express версии Visual Studio не ис
пользуют папку временных проектов, Последнее меняется изменением поля
а помещают файлы непосредственно (Name) в окне Properties. Имя файла при
в папку Projects!) этом остается тем же.
Но только вы
Для файлов, форм (и других частей про
Что делать с ненужным кодом,
автоматически созданным ИСР?
граммы) можно выбирать произвольные
имена. Выбор значимых имен облегчает
отвечаете за
работу с программой, но об этом пока
; Его можно отредактировать. Если можно не думать. корректную работу
по умолчанию создан не тот код, который
вам требуется, достаточно внести в него
необходимые изменения — вручную или
это11 программы.
средствами пользовательского интерфей
са ИСР.
дальше > 45
лучше один раз увидеть
^ PictureBox —
l‘' W ¥ \ \ .
0 RaAfcbt\toi
RkhTextBox
Щ. TextBox
0 файле Forml.Designer.es.
вы не им еете представления
разработке пользовательских ин
терф ей сов, ничего страшного.
46 глава 1
эффективность с C#
здесь
дальше > 47
экономим ресурсы
48 глава 1
эффективность с C#
Редактирование кода
Иногда в автоматически созданный код требуется внести из
менения. Рассмотрим эту процедуру на примере добавления
к логотипу всплывающего текста.
При редактировании формы двойной щелчок на любом эле
менте управления автоматически добавляет в проект код.
Дважды щелкните на фрейме PictureBox и вы увидите код, ко
торый выполняется при каждом щелчке на данном элементе
управления. Этот код должен выглядеть примерно так:
}
Введите эт от код Пи введя код, щелкните
V
После двойного щелчка на кнопке Save панели
инструментов ИСР
на элементе РкЫгеВок выберите команду Save
курсор окажется
здесь. При вводе в меню File.
кода игнорируйте
всплывающие окна.
Ч асзц о
^аД аБаеМ ы е
дальше k 49
запуск приложения (наконец-то!)
About
Щелчок на логотипе
вызывает только
что созданное окно
C o n tact List 1,0. About.
W ritten b f. Your N am e
OK
_ Часвдо
--------- ^ а Д а В а е М ы е ------------
Где )ke Mou файлы? B onj=*oc:bi
П ри запуске программы Visual в моей ИСР кнопка с зеленой стрелкой
Studio копирует файлы в папку С* превращает называется Debug (Начать отладку). Она
Му D o c u m e n ts \V is u a l S t u d io вашу программу запустит программу?
20 1 0 \P ro je c ts \C o n ta c ts \ в исполняемый
айл, находящийся
C o n t a c t s \ b i n \ d e b u g . Дважды щелк-
ните на созданном ИСР файле .ехе, Г папке debug. • Конечно. Щелчок на ней приведет к вы
полнению программы внутри ИСР, что в дан
/
и программа начнет работать. ный момент вам, собственно, и требуется.
О процедуре отладки мы поговорим позднее.
с»
Я не вижу на панели инструментов
сй 1=orm1 Ш] кнопку Stop Debugging. Что делать?
Designer.cs шти
Form1.cs Forml.resx Properties
Q l Эта кнопка появляется только при запу
Это не ошибка. Существуют два щенной на выполнение программе. Запустите
уровня папок, файлы с кодом С # приложение, и вы увидите, как она появится.
находятся во внутренних папках.
50 глава 1
эффективность с C#
Визуальные
объекты .NET
Объекты Хранилище иис1л|»умеиты
в)<ез{^ения
П оэтому снячйЛй
Мы не можем нцжно написать
соединить форму базу Wзаполнить
с базой данных, так ее данными.
как база данных пока
от сут ст вует .
Э т о мы уже
сделали... которое УД
с 9анн1?|Мм м3 олз
52 глава 1
эффективность с C#
Язык SQ L
SQL расшифровывается как Structured Query
Language —язык структурированных запросов.
Этот файл содержит базу
Он предназначен для доступа к данным в базах SQL. Именно 3dect> будут
и имеет собственный синтаксис и структуру. Код сохранены наши таблицы
SQL представлен в виде операторов (statements) SQ L и данные.
и запросов (queries), а также хранимых проце
дур (stored procedures). ИСР генерирует за вас
операторы SQL и хранимые процедуры, позво ContactDB.sdf
ляя создаваемому вами приложению осущест
влять доступ к данным базы.
дальше ► 53
хранить данные легко
T
А о б а в ^ е столбец ContactID с т ипом данных int. В списке Allow Nulls
М е р и т е вариант No, а в списках Unique и Primary Key вариант Yes.
54 глава 1
эффективность с C#
в о кк. з о г « " «
U
с ■3 E^i^tTriЯe-^*eop^e
ш
Generei
iJ Rdresh
Istame
ColumnName
М
е}р
Peopie
Datatype length AllowNutts Uniqu« PrtmaryKey
Первичный ключ
является ун и
кальным иден
тификатором
записи, поэтому
4 Mo Yes ¥e он обязательно
должен быть за
дан.
1
Ы«-
MicfosoftSQLServerCompact
ContactD6.sdf
Det^Vah«'............
is WmisiffisdsniKOEHtiss Ddaultvafoefor^iiscoSumrv.
добавлении
»овои записи поле
OK Cftncet ^о>л^лсИО будет
обновляться
‘^°<^0Алатически.
8 раскрывающемся списке справа от поля
Identity нужно указат ь значение True,
чтобы сделать столдец Contact!Р
идентификатором записей таблицы.
дальше ► 55
оформим в таблицу
р е о р '®
Имя: Laverne Smith p.«.
Фирма: XYZ Industries
Телефон: ( z i z ) s s s ~ s x z ‘i
Email; Laverne.Smith@XyZindustries.com
Клиент: Yes Поел, звонок: o s /z & /o i
JLJI
о каждом человеке мы
ШТУРМ
Какие проблемы могут возникнуть, если одному
человеку сопоставить несколько записей?
56 глава 1
эффективность с C#
КТ9 И Н Т<ЛеЛ#^Т?
Таблица People готова и для нее уже задан первичный ключ, осталось добавить столбцы для всех
полей с данными. Укажите, к каком типу должны принадлежать данные, и выберите для них вер
ное описание.
дальше * 57
это именно мой тип
КТО
Проверьте, правильно ли вы сопоставили типы данных и описания таблицы People.
58 глава 1
эффективность с C#
, Если
, парамет р
i Allow Nulls
имеет
' значение
NOj столбец
не может
оставаться
•пустым.
Поля типа
Bit при -
нимают
значения True ^^ф орм ация
или False и о клиенте
м огут быть может, быть
предст ав , неполной,
лены в виде .^ о э т о м у
переключа - o f io u p a c M
теля. значение Yes
Щ елкните на кнопке ОК для сохранения таблицы. Она будет добавлена к ва
шей базе данных.
ЕМ.?»« •
Щелчок на кнопке^
ОК добавит
таблиц!^ People реор'в
к базе Эанных.
дальше ► 59
добавляем данные
60 глава 1
эффективность с C#
Частно
Л аД аБаеМ ы е
БоЛ роС ь!
Б: Куда попадают введеные мной данные? Становятся ли введенные записи частью моей про
граммы?
грам
О ; ИСР автоматически сохраняет данные в таблице People
вашей базы данных. При этом таблица, ее столбцы, типы 0: Да, данные — такая же часть программы, как код или
данных и сами данные хранятся в файле ContactDB.sdf, кото созданные формы. Файл ContactDB.sdf копируется и хранит
рый является частью вашего проекта, поэтому ИСР автомати ся вместе с исполняемым файлом. Для доступа к данным
чески обновляет его при внесении любых изменений. приложение читает файл ContactDB.sdf и вносит в него новые
записи.
3
ContaetDB.sdf
дальше k 61
все данные в одном месте
People Query(OW...cts\ContactDB.sdf)^X j
ContactID Name Company Telephone Email Client LasCail
1 Lloyd Jones Black Box Inc. (Л8)555-5638 Uones@xbtack.„ True 5/26/201012:00..
2 Lucinda Ericson Ericson Events C212)555-9523 lucy@ericsonev... False 5 Л 7 /Ж 0 12:00..
3 Liz Nelson JTP (419)555-2578 liznelson@JTP.... True 3/4/200S12;00:...
4 Matt Franks XYZ Industries (Л2}555-8125 M8tt.Franks@x... True 5/26/201012:00..
5 Sarah Kalter Kalter, Riddle a„ (614)555-5641 sarah@krs.,org False 12,^0/200812:0..
§ Laverne Smith X¥Z Industries (22)555-8129 Laverne.Smith... True 4/ll/201012iOO..
NULL NULL NULL NULL NULL NULL
Создаваемый исмочник
данных будет отвечать
за все взаимодействия
формы с базой.
3
62 глава 1
эффективность с C#
к л
Form!
- взаимодействия с базой ContactPB-
OK
Файл с базой
C ontactD B D ataS et. данных.
1L Это ваша
D esigner.cs
дальше > 63
соединим все вместе
I
Database Exptorer Можно щелкнуть
на корешке
вкладки Data
Sources, распо
ложенном внизу
окна Database
Explore.
С И N am e
Jgii C o m p a n y
Здесь должны Q T e le p h o n e
64 глава 1
эффективность с C#
- cS
Щ ^ в щ
здесь
Этот объект
соединяет форму
с і^адлицеи People.
^лнель
дальше ► 65
сделаем красиво
•i* Forml
И << О
Cratad ID:
П ш леретаскибйнмы N«ne:
эм м ент ов управления
бцдцт появляться Согаржиу:
{cbedcBoxl 4 m _
TextAlign Middieleft
TexUmageRetation Overiay
ThreeState False
UseCompetibteTect( Fat»
UseMnemonk True
UseVtsualStyleBeckC Tnie
UseWaitCursoF FaEse
___ iOsible--___ ____ Тше_ ............
Tert
The text associated with the cofrtiel
66 глава 1
эффективность с C#
^Ь 1 здесь
@ П р и д ан и е процзессионального вида.
Щелкните в произвольном месте формы в окне Properties При раскрытии формы
и найдите свойство Text. Введите в это поле название на полный экран положение
Objectville Paper Company Contact List. элементов управления
не меняется, форма
Присвоив свойствам MaximizeBox и MinimizeBox знау выглядит некрасиво.
чение False, можно убрать возможность разворачивать Поэтому мы просто
и сворачивать форму. уберем эт у возможность.
Propertws
F orm l Sy5tem.Windows.Fortns.Form
........:
^owbTaskbar True
должно „одд p- Size 408, 261 Свойство Text
SizeGripS'^te Auto
StartPositior» WindowsDefauttlocati.
задает название
Tag
формы.
Тестирование
Осталось запустить программу и убедиться, что она работа
ет правильно! Вы уже знаете, как это сделать, поэтому про
сто нажмите F5 или щелкните на кнопке с зеленой стрелкой
на панели инструментов (также можно выбрать команду
Run из меню Debug).
Запускать можно даже не полностью готовые программы.
Если в коде ИСР есть ошибки, программа не запустится.
Objectville Paper Company Contact List
И < n У a
Э т и элем енты
у прав л е н и я
Contad 10: 0
Переходить от одной Name: Lloyd Jones
записи базы к Эругом.
Смтрапу: 0ad< Bax inc.
Tel^hone: (7181555-Ж8
Компиляция БпаЙ; LJones@xbiad<bo3dnc.com
профаммы (Sent:
UrtM: Wednesday. May 26,2Й10
переписывает
данные в базе.
Этот аспект
будет подробно
рассмотрен
в следующей главе
РР у д ,ьш
ь ш ее
о С Ш о |э о Ж Н ь 1 !
UCP сначала компилирует, При каж дой ком пиляции програм
мы в папку bin пом ещ ается свежая
потом запускает на Выполнение копия базы данны х. Все д анны е, н а
При запуске программы ИСР выполняет две операции. Сна копленны е с м ом ента последней от
чала она строит программу, затем вы п олн яет ее. Эта работа л адки, при этом уничтож аю тся.
состоит из нескольких этапов. Код компилируется, то есть В процессе отладки, если код был
превращается в исполняемый файл. Этот файл вместе с дру изменен, ИСР перестраивает про
гими ресурсами помещается в подпапку папки bin. грамму — это означает, что база
данных может оказаться перепи
Свой исполняемый файл вместе с базой SQL вы найдете санной. Этой проблемы можно избе
в папке b in /d e b u g . Его запуск позволяет сохранить все вне жать, запуская программу из папки
сенные в процессе пользования приложением изменения. b i n / d e b u g или b i n / r e l e a s e или
В то время как при запуске из ИСР база заменяется копией предварительно воспользовавшись
с данными из окна database Explorer. установщиком.
68 глава 1
эффективность с C#
дальше > 69
поделитесь любовью
цНСтДЛАЯЦи^.
руды пе
оС Щ о|Э оЖ Н Ь 1
Д л я ин стал л яц ии вам
м огут пона д об итьс я права
а д м и н и стратора. э т о т файл говорит С помощью этого
файла осуществляется
Если у вас еще нет базы инсталляция.
SQL Server Compact, ин в инстйлдмру^ллу
сталлятор автоматически прогряллму-
скачает и установит ее.
На некоторых компьюте
рах это возможно только
при наличии у вас прав
администратора. Щелкни
те правой кнопкой мыши Секретарь сказала, что у нас уже
на файле setup и выберите работает новая база данных с контактами.
команду Run as administrator. Вы хорошо поработали и заслужили
Если у вас нет такой воз небольшой отдых...
можности, ничего страш
ного. Это не помешает
вам выполнять следующие
задания.
А
1Саже,тся, начальник доволен.
V Но перед т е м кяк собирать
^ i o / a H b b -Р о т е с т и р у ш
резулЕ?тдтЬ 1 своего тру
70 глава 1
эффективность с C#
процесс установки It
Перед тем как открыть бутылку шампанского и отпразд
новать победу, нужно протестировать внедрение и уста В ; Щ
новку
Закройте ИСР Visual Studio. Запустите файл setup и ука
жите, куда следует установить ваше приложение. Убе
дитесь, что все работает так, как вы ожидали, — можно Б:ы здесь
добавлять и редактировать записи, и все изменения со
храняются в базе данных.
от одной записи
к другой, ч ^ Name:
Tdephone: (718)555-5638
Oert: a <-
Попробуйте,
вн&сти изменения LaS Ы1: edi sda , fv:3>‘ 26. 201С Q »"
б данные. Все контакты
на месте. Ведь
они являются
частью
базы данных
ContactDB.sdf,
которая была
установлена
вместе
ТЕСТИРУЙТЕ ВСЁ! с приложением.
дальше ► 71
быстрее не бывает
приложение готово
Visual Studio позволяет с легкостью разработать
Windows-приложение, написать базу данных и свя
зать эти два объекта вместе. Вы даже смогли по
строить программу-инсталлятор всего нескольки
ми дополнительным щелчками мыши.
Q m б у м а ж н о й к а ]= ^ о Ч к и
«Ц: Objectville Paper Company Contact list
Hi 4 \1 crf6 : ► И ! Я
Имя: LloydJones О — Cortad ID: 1 ............ }
Фирма: Black Box Inc. Name; Ltoyd Jones
Телефон: (718)555-5638 До эЛ еК- Csmpanjr: Hack Box Inc.
ЗРроННой Tdeptone; (718)555^638
Email: Uones@Xblackboxinc.com
finaS: U o n e s # > d 3 l a c k b o * i n c .c o n i
Клиент; Yes Поел, звонок: 05/26/07
a « : В
LaSCd: Wednesday, May Ж 2010
наМноГо бьхсгорее,
Чем к а з а л о с ь .
72 глава 1
2 ЭЗЦО Б с е Г о Л и Ш ь К о д
Под покровом
Properties X
о Задание свойств qjopMU Form! Sy5tem.Window$.Ferms.Form
Окно Properties ^
TransparencvKey
UieWaitCursor
□Fafs« .'3
‘простой способ самый / WindowState Normet
Text
><ода формы F orm i Th« text essocmed wrtb the control.
74 глава 2
это всего лишь код
p a r t i a l c l a s s Forml
F o rm l .Designer.cs
„u обновляет эт у cvnpomy.
дальше > 75
средства коммуникации
76 глава 2
это всего лишь код
дальше * 77
ваш маленький помощник
Solution Explorer » П X
Ä ij S t . ; .
)
*33 Solution 'Contacts' (1 projectj
^ I^ ^ C o n ta ctsi В окне Solution Explorer
Можно посмот реть,
^ ^ Properties о каких папках на -
l> References ходятся те или иные
appxonfig файлы.
ContactDB,sdf
Ej . ContactDBDataSet.xsd
i> Э Forml .cs
Progrann.cs
Здесь находится
Вкладки позволяю т л е гко перейти о т одного ф айл а к д ругом у файл с ресур
Так как типичная программа состоит из множества файлов, приходится сами формы,
к которому вы
работать с несколькими файлами одновременно. Каждый из них имеет добавляли лого
т ип компании.
открыты две
вторая с кодом,
пользуйтесь
78 глава 2
это всего лишь код
MessageBox. Show! J
В окне Error
List бк>! найдете
список всех
О кно E rro r L ist показы вает ош ибки ко м п и л яц и и ошибок,
мешаьош,1^Х
Вы даже не представляете, как легко сделать опечатку при наборе кода на С#! компиляции
К счастью, существует инструмент, позволяющий бороться с этой проблемой. iA.pozpaMMt><-
При построении решения всё, мешающее компиляции, будет перечислено
в окне Error List, расположенном в нижней части экрана:
О 2 Errors 1 О W a rn in g s M i ) О M e s sa g e ^
О т сут ст вие tin e C o lu m n P roject
D escription Fite
точки с запятой
после оператора
является одной
из самых частых
ошибок.
дальше ► 79
подробности
П р осм о тр кода і
Откройте файл F o r m l.D e s ig n e r .c s , но не в конструкторе форм. Для этого Ж
щелкните правой кнопкой мыши на имени файла в окне Solution Explorer и вы
берите команду View Code. Посмотрите на объявление класса Forml:
p a r t i a l c l a s s Form l
м. .olTopZZZ.
О Д обавление к срорме эл ем ента PictureB ox
Привыкайте работать с множеством вкладок. Вернитесь в окно Solution Explorer
и откройте конструктор форм двойным щелчком на имени файла F o rm l. cs. П е
ретащите на форму элемент PictureBox.
//
// p ictu reB o x l
//
Ваши значения
th is .p ic tu r e B o x l.L o c a tio n = new S y s t e m . D r a w i n g . P o i n t ( 2 7 6 , 2 8); параметров
Location
th is.p ic tu r e B o x l.N a m e = " p ic tu r eB o x l" ; и Size будут
отличать>ся
t h is .p ic t u r e B o x l. S iz e = new S y s t e m .D r a w in g . S i z e (1 0 0 , 50); от указанных
th is.p ic tu r e B o x l.T a b ln d e x = 0;
в книге.
th is .p ic tu r e B o x l.T a b S to p = fa lse ;
80 глава 2
это всего лишь код
Структура программы
имен, отделяя ^о/^® ^^Я-янстбо
■n e t F r a m e Z r L
Код любой программы на C# структурируется
одинаково. Все программы используют про
странства имен, классы и методы.
Класс содержит^
ф рагм ент вашей
программы (очень
маленькие програм
мы м огут состоять
из всего одного класса)
using System;
using System.Collections.Generic;
Такие строчки
using System.ComponentModel; находятся в верхней
части любого кода.
using System.Data; 1^ажаая из них
показывает, какое
using System.Drawing;
using System.Linq; из .NET Framework
использует т от
using System.Text; или инои файл .CS.
using System.Windows.Forms;
В принципе без оператора u s in g можно обойтись, если пользоваться полными именами.
Если строчка u s in g System .W indow s. Forms отсутствует, окно сообщений можно создать,
написав S y s te m . W indows. F o rm s. M essageB ox. Show(), и компилятор все равно поймет,
какое пространство имен вы имеете в виду.
82 глава 2
это всего лишь код
0 ТТрогроААМЫ н а C # и с п о л ь з у ю т кл а с с ы
Программы на C# используют классы. Каждый класс выполняет свою задачу. Когда вы соз
давали свою первую программу, ИСР добавила класс Form l, отображающий формы.
дальше > 83
еще более подробно
, част ь им ени
1<ласса и л и м е т о В а н а
Сь~лча^, на стадии начального
з ы в а е т с я о б ъ яв л е н и ем . ^ знакомства с кодом, вам требуется
только понять, на что следует
обращать внимание.
84 глава 2
это всего лишь код
Постепенно вами
программы 5удцт
содержать все дольше
пространств имен.
О Встроенные функции C # и .NET.
Подобные строчки находятся в верхней части почти
всех файлов классов С#. S ystem .W indow s.F orm s Без строчки using вам
это пространство имен. Строка u s in g S y stem . придется в явном виде вводить
Windows . Forms дает программе доступ ко всем объ Sustem.Windows. Forms при
ектам этого пространства. В данном случае к визу обращении к объекту из этого
альным элементам —кнопкам и формам. пространства имен.
Технически програм м а
Наш код содержит один метод, состоя ^ может имет ь несколько
щий из нескольких операторов.
Внутри любого метода может находиться произ \ только у к а за т ь , кякои из
вольное количество операторов. В нашей про -V них будет точкой входа.
грамме именно операторы вызывают форму
Contacts. Любая программа на C#
должна иметь единственный
Точка входа. метод Main. Он является
Каждая программа на C# долж на иметь один ме
тод с названием Main. Именно он выполняется точкой входа дая вашего
первым. C# проверяет классы на его наличие,
пока не находит строчку s t a t i c v o id M ain (). кода.
После этого выполняется первый и все следую
щие за ним операторы.
При запуске кода метод
M ain() выполняется
ПЕРВЫМ.
дальше > 85
элегантные решения
О Добавьте этот метод к классу А п оЬ Ь егС Х а в з, написав его внутри фигурных скобок:
Класс MessatjeBox
принадлежит c l a s s A n o th e r C la s s
пространству имен {
System.Windows. p u b lic s t a t i c v oid MainO
Forms, поэтому
на шаге # 3 вы {
и добавили оператор M e ssa g e B o x . Show ( "Fow l" ) 7
using. Метод Show О }
является частью
класса MessageBox.
86 глава 2
это всего лишь код
Ч то )ке произошло?
Теперь вместо приложения Contacts программа вызыва
ет вот такое окно диалога. Переопределив метод M ain (),
вы указали новую точ 1су входа. Поэтому программа пер
вым делом выполняет оператор M essageB ox.Show O .
Данный метод больше ничего не содержит, поэтому
после щелчка на кнопке ОК программа завершит свою
работу.
Подсказка: Д о с т а т о ч н о
о Верните программу в исходное состояние. измеш т ь^пару строк
в двух файлах.
{
c l a s s M yC lass
p u b l i c s t a t i c v o id D o S o m e th ln g () {
дальше > 87
время получить ответы
Часто
^аД аБ аеМ ы е
B o lIp o C b i
Какую роль играют фигурные 3 * Объясните, пожалуйста, еще раз, Почему при запуске программы
скобки? что такое точка входа. в окне Error List появляется сообщение
об ошибках? Я думал, что подобное
О ? Скобки группируют операторы С I Программа состоит из множества возможно только при выполнении
в блоки и используются только попарно. операторов, но они не могут выполняться команды Build Solution.
Открывающей скобке всегда должна соот одновременно. Операторы принадлежат
ветствовать закрывающая. разным классам. Как же при запуске С I Первое, что происходит при запуске
программы определить, какой оператор программы на выполнение, это сохране
Для выделения пары скобок достаточно выполнять первым? ние всех файлов в виде решения и по
щелкнуть на любой из них. пытка их компиляции. А при компиляции
Компилятор просто не будет работать при кода — неважно, запускаете вы при этом
отсутствии метода M a i n (), который и на программу или строите ее решение — все
зывается точкой входа. Первый оператор имеющиеся ошибки отображаются в окне
этого метода и будет выполняться самым Error List. f
первым.
В тексте кода ошибки
выделяются красным
цветом.
Возьми в руку карандаш
Вот правильные варианты ответа на вопрос о строках.
Этот оператор
вТет о к н о с т екст овы м
сообщением.
88 глава 2
это всего лишь код
number_of_pit_stopsLabel.Name
= "nuraber_of_pit_stopsLabel";
number_of_pit_stopsLabel.Size
Подсказки системы, объясняющие
= new System.Drawing.Size(135, 17) назначение различных блоков
number_of_pit_stopsLabel.Text кода.
= "Number of pit st o p s :";
дальше ► 89
решение упражнения
КТ9 И H W A E A a I t ?
Вот правильные ответы на вопрос о назначении различных бло
ков кода. Сравните со своими вариантами!
this.BackColor = Color.DarkViolet;
Задает свойства объекта label.
number_of_pit_stopsLabel.Name
= "number_of_pit_stopsLabel";
number_of_pit_stopsLabel.Size
■Подсказки системы, объясняюш:ие
= new System.Drawing.Size (135, 17)
number_of_pit_stopsLabel.Text назначение различных блоков
= "Number of pit st o p s :"; кода.
I I I <summary>
III При щелчке на кнопке появляется Меняет фоновый цвет окна Form l.
III графический фрагмент Rover
III </summary>
90 глава 2
это всего лишь код
class Cat {
public void MeowO {
M oreC lasses.cs
дальше ¥ 91
параметры могут варьироват ься
Ч то такое переменные
Переменные меняются
Для работы
В процессе работы программы любой переменной может быть с числами, тек
присвоено произвольное значение. То есть значения перемен
ных меняются. Это ключевая идея любой программы. К приме стом, булевы
ру, если переменной m yH eight было присвоено значение 63:
in t m yH eigh t = 63;
ми значениями
как только имя m y H e i g h t появится в коде, C# заменит его зна и любым другим
чением 63. Представим, что позднее ему было присвоено зна
чение 12: видом данных
m yH eigh t = 12;
используйте
Теперь C# будет заменять параметр m yH eight на число 12, не
смотря на то что имя переменной не изменилось. переменные.
92 глава 2
это всего лишь код
Присвоение значений
Поместите в программу эти операторы:
int Z ;
Отсутствие у пере
M e s s a g e B o x . S h o w ("О твет " + z ) ;
менных значения
При попытке запустить программу, ИСР откажет
ся компилировать код. Компилятор проверил ваши
переменные и обнаружил, что им не присвоено ни
является препят
какого значения. Чтобы избежать подобных ошибок,
- <
имеет смысл комбинировать оператор объявления
„
Присвоенные ствием для компи
переменной с оператором присвоения значения: значения. ляции. Этой ошиб
ки легко избежать,
xWeight объединив в один
message оператор объяв
bxChecked ление переменной
и присвоение ей
ß объявлении переменной, как
и раньше, указывается ее тип.
значения.
пе-ре-мен-ный, пррш.
умеющий меняться или приспосабливаться.
Переменная скорость дрели позволяет Бобу свер
лить то быстро, то медленно, в зависимости от
текущих задач.
дальше > 93
операторы наготове
Д л я пр о гр ам м и ста сл о во string
Знакомые математические симболы почти всегда о значает «строка
текста», а со кр ащ ен и е int указы
Числа, хранящиеся в переменных можно складывать, вы в ает на целое число.
читать, умножать и делить. В этом вам помогут о п е р а т о р ы
(o p e r a to r s). Часть из них вам уже знакома. Рассмотрим код,
решающий несложную математическую задачку:
Третий оператор присваивает
переменной num ber значение
Мі?і об-ьябилы пере 3 6 * 1 S = S40. Следующий
М бнкую num ber int number = оператор присваивает новое
целочисленного значение 12. - (42. / 7) = 6.
U присбои nvunber = number
ли ей знйченме
з а т е м .puö^6«A« number = 36
m 6 резул і^тате
О п ератор « :S ^ Im b e r
чего ока іА,олучилй number = 12 - к значению перем
зняченме Я-?-
number += 10; г » « “«
вы получите %■&>■
Оператор *= number *= 3;
означает, что
вы должны ум н о number = 71 / 3;
жить текущее л Если71 разделить на 3 получится 2 3 .6 6 6 6 6 6 ...
значение перем ен \ __ _ МО результ ат деления целых чисел также должен
ной на 3^ в итоге быть целым, поэт ому 23.666... округляется до 23.
получаем 48. int count =
count ++; Целочисленные переменные используются в счетчике
в сочетании с операторами +-+ и Инкремент ++-
Класс MessageBox count --; увеличивает значения на
вызывает окно а декремент — ум еньш ает на 1.
с текстом «hello
again kello».
String result = "hello"; Оператор + в данном
случае соединяет вместе
result += " again " + result; две строки. При этом
числа авт омат иче
MessageBox.Show(result); ски преобразовываются
Пустые кавычки к т ипу string.
обозначают строку. result = "the value is: + count;
О
не содержащун:>
символов. result =
Переменные типа
bool принимают
значения true или
false. Оператор ! bool yesNo = false;
обозначает от рица
ние и меняет зна- bool anotherBool = true; Н е в о л н у й т е с ь , е с л и в ы н е см о гл и
чение с true на false зап ом н и ть в сё.
и обратно. y e ^ o = !anotherBool;
Эта информация будет часто повто
ряться в книге.
94 глава 2
это всего лишь код
b ool yesH o = f a ls e ;
b o o l a n o t h e r B o o l =» t r u e ;
y e sfto = ta n o th e r B o o l;
}
lioo%~
О О тл ад ка программы
Щ елкните на кнопке Start Debugging (или нажмите клавишу F5) (Можно использовать команду
Start Debugging, меню Debug.) Программа начнет работу и откроет форму.
П е р е х о д к точке останова
Как только программа дойдет до точки останова, ИСР автоматически вызовет редактор кода
и выделит нужную строчку желтым цветом.
О in t пияЬег « I5j
num ber = numiaer + 1 0 j
num ber = 3 6 * I S ; В процессе отладки
; num ber = 12 - (4 2 / 7 ) достаточно навести на
: num ber + = l e j переменную указатель
num ber * = 3 ;
мыши,, чтобы появилось
всплывающее окно
: num ber = 7 1 / 3 j
со значением этой
переменной.
Циклы
СОБЕТ^
в большинстве сложных программ одни и те же действия повто Наличие непарных скобок, явля
ряются больше одного раза. В такой ситуации на помощь прихо ется препятствием к построению
дят циклы (loops) —они заставляют программу выполнять набор программы. К счастью, достаточно
операторов, до тех пор пока указанное вами условие не примет навести указатель мыши на скобку,
значениёС ^^е (или false])). и ИСР тут же выделит ее «вторую
половину»:
bo o l te st^
w h ile == t r u e )
Вот почему іл'як бйжны
переменные логического {
типа. // C on ten ts o f t h e lo o p
fo r (in t § = if
{
Достаточно изменить имя
переменной здесь, и оно
автоматически изменится
во всем фрагменте кода.
дальше ► 97
приготовились, настроились, кодируем!
еС К о Л ь К о С оБ ехооБ
н
Перейдем к практике
★ Не забывайте, что в конце оператора
Работа программы определяется операторами. Но должна стоять точка с запятой:
операторы существуют не в вакууме. Впрочем, про name = " J o e " ;
ще всего это понять на примере. Создайте проект
W indows Form s A pplication. ★ За двумя косыми чертами идут
комментарии:
// этот тек ст игнорируется
{
м елое им ело, остальная О Л е к т PI относится
часть оператора ^ // э т о ком ментарий
Присваивает п е р е м е н н о й strin g name = " Q u e n t i n " ; Math из пространства
знам ени е 3 . I ^м ен System , поэтами
in t X ^ 3;
^х~о 1^оследовательность,
OK добавляющая в окно сообщения символ
переноса строки.
98 глава 2
это всего лишь иод
о п е р а т о р -Т
с проверки условия.
Л
if (someValue == 24)
{
О „ер а« о р в ф м а Р " » "
MessageBox.S h o w ("Значение 24 ."); . сковах выполняется
со1ли>д‘ ш и
> д л я сравнения аввх » « S e ®
условия.
и сш А И и етея о « ер « ~ о р «
з н » « р «венет6«^
дальше > 99
то, что вы можете сделать
ПроберЬ условий
Рассмотрим работу условного оператора i f / e l s e .
Использование
Проверка при помои^и операторов условного опера
Вы уже познакомились с оператором ==, проверяющим, равны ли
друг другу две переменные. Существуют и другие операторы срав-
тора для сравне
нения:
ния двух чисел
Оператор != в отличие от оператора == принимает значе
ние true, если переменные н е р а в н ы . называется про
★ Операторы > и < выявляют, какая переменная больше.
★
веркой условия.
Операторы ==, ! = , > и < называются о п е р а т о р а м и с р а в н е
н и я . С их помощью осуществляется п р о в е р к а у с л о в и й .
100 глава 2
это всего лишь код
{
in t som eV alu e = 4;
str in g name = "Bobbo J r ." ;
if ((so m eV a lu e == 3 ) && (na m e == " Joe" ))
{
M e s sa g e B o x .S h o w (" x i s 3 a n d t h e name i s Joe" );
}
M e s sa g e B o x .S h o w ( " t h i s lin e ru n s no m a tte r w h at" );
Добавление цикла
Ниже показан код для последней кнопки. Он содержит два цикла. Цикл
w h ile , который реализует операторы в скобках, пока выпоняется заданное
условие. И цикл f o r . Посмотрим, как это работает.
Здесь переменной
используемой Оператор, при
для подсчета каждом проходе
проходов цикла цикла увеличиваю-
присваивается щый значение I на х.
’ . . . - л значение.
н а ч а л ь н о е M e s sa g e B o x .sh o w (" T h e a n s w e r is " + cou n t)
}
дальше ► 101
выше и выше и выше и...
w h ile (х > 3) {
r e s u l t = r e s u l t + х ; / / прибавим х
X = X - 1; / / вычтем
}
for (in t z = l ; z < 3 ; 2 = z + l ) {
/ / 191КЛ р а б о т а е т пока
r e s u lt = r e s u lt + z; / / ....................
}
/ / Следующий о п ер атор вызывает окно д и а л о г а с т ек ст о м
//
M e ssa g e B o x .S h o w (" Р езу л ь т а т р а в ен " + r e s u l t ) ;
Э т и о п е р а т о р ы и с п о л ь з у ю т с я ч а щ е всего.
102 глава 2
это всего лишь код
цикдізі-
Ц и кл # 1 Ц и кл # 3 Ц и кл # 5
in t count = 5; in t 3 = 2 ; in t р = 2;
w h ile (count >0) { fo r (in t і = 1; і < 100; fo r ( in t q = 2; q < 32;
count = count * 3; І = І * 2) q = q * 2)
count = count * -1; { {
‘И = j - І; w h ile (p < q )
} Сколько раз w h ile (j < 25)
будет выполнен {
эт от опера { P = P * 2;
тор? j = j + 5;
Ц и кл # 2
in t i = 0; Сколько раз kq = p - q;
}
in t count = 2; будет выполнен ■
э т о т оператор?
w h ile (i == 0 ) {
count = count * 3; Ц и кл # 4
При повторении
count = count * -1 w h ile (tru e) { in t і = 1;} \ 1^тератора ^ = а * 2
помните, что начальное
} значение ^ равно 2..
\
8 цикле for сначала
выполняется проверка
услобия, а потом
итератор.
^ Ш ТУРМ
Подумайте, в какой ситуации может понадобиться
бесконечный цикл? (Один из вариантов ответа вы
найдете в главе 13...)
дальше ► 103
если только, но только если
i n t r e s u l t = 0; / / п ер ем ен н ая , в которую б у д е т за п и с а н р е зу л ь т а т
w h i le
r e s u lt = r e s u lt + z; / / п е р е м е н н а я Z п р и б а в л я е т с я к п е р е м е н н о й result
}
/ / Следующий о п ер атор вызывает окно ди а л о га с тек ст о м
Г в руку карандаш
Сравните свои ответы на вопрос, сколько раз будет выполнен
'ешение тот или иной цикл с правильными ответами.
Ц и кл # 1 Ц и кл # 3 Ц и кл # 5
Будет выполнен один раз. Будет выполнен семь раз.
Будет выполнен
восемь раз.
Ц и кл # 2 Ц и кл # 4
Бесконечный цикл Еще один бесконечный цикл.
Попробуйте реш ить задачу номер пять. Это отличная возможность поработать с о т
ладчиком! Сделайте оператор с) = р ~ точкой останова и проверьте, как изменяются
значения переменных р и q с помошрю окна Watches■
104 глава 2
это всего лишь код
часвдо
Задаваем ы е
БоЦ|эос;ь1
Всегда ли оператор принадлежит к какому-нибудь клас Насколько внимательно нужно относится к автоматиче
су? ски создаваемому коду?
^ а Г н и ш ы С К одам и
Мы поместили блоки с фрагментами кода на С# на магниты для холодильника. Со
ставьте из них работающую программу, результатом которой является окно с сообще
нием. Некоторые магнитики упали на пол, и они слишком малы, чтобы их поднимать,
поэтому просто впишите недостающий код от руки! (Подсказка: вам понадобится все
го пара фрагментов!)
Этот магнит if (X == 2) {
s t r in g R esu lt = упал на пол..
R esu lt = R esu lt + " b e
}
--------------
if (X > 2) {
R e s u l t = R e s u l t + "a";
>
i n t X = 3; 1
Результат:
О ш Б е ш ы н а с. I 3 -
106 глава 2
это всего лишь код
Вам придется выполнить много В процессе чтения книги вам предстоит соз
подобных упражнений. О т ве дать много разных приложений, и все их нужно
ты можно найти на следуюш,ей оудет кйк~т .0 млдснобйте?, Мь>/ совстуем присво-
странице. Если вы не знаете, как umt? эт ому приложению имя «Z Fun with if-else
реш ит ь задачу, не стесняйтесь statem ents» ( z Забава с операторами if-else ), п о
подсмотреть. лученное в результ ат е комбинации номера главы
и текста 8 строке заголовка формы.
дальше * 107
симпатично!
Построим что-то яркое! Начните с создания нового проекта Windows Forms Application.
п'ражнение
Вот ф орм а, которую
нуж но получить
\ „ в» MOW-“ зага««»» “
Метод РоЕуепЬв () принудительно на о д g варианты п т
обновляет форму на каждом витке свои собственные р
цикла, но это «обходной» пут ь,
который не следует использовать
в серьезных программах.
о З ам ед л и те процесс
Чтобы цвета менялись медленнее, после стро
ки A p p l i c a t i o n . D oE vents () напишите:
108 глава 2
это всего лишь код
О О становите ее!
Сделаем так, чтобы цикл, добавленный на предыдущем
шаге, останавливался при закрытии формы. Замените пер
вую строчку на:
w h ile (V isib le )
дальше у 109
решение упражнения
ажнение Вот как должен выглядеть текст программы, циклично изменяющей фоновый цвет формы.
еш ш е
u s in g System ;
u sin g S y s te m .C o lle c tio n s.G e n e r ic ,■
u sin g S ystem .C om p on en tM od el,•
u sin g S y stem .D a ta ;
u sin g S y stem .D r a w in g ;
u s in g S y stem .L in q ; Mb/ присвоили решению имя
Fun with if Else, поэтому
u sin g S y stem .T ex t;
ИСР назвала пространство
u sin g S y stem .W in d o w s.F o r m s; имен т аким ооразом.
110 глава 2
это всего лишь код
/ '" '^ w h i l e (V is ib le ) {
A p p l i c a t i o n . D o E v e n ts ( ) ; ^ „г
V Ч Чиклй меняют
S y ste m .T h re a d in g .T h re a d .S le e p O );
^ в разные стороны.
}
fo r (in t с = 254; С >= О && V i s i b l e ; с--) {
t h i s . B a c k C o l o r = C o l o r . F ro m A rg b ( с , 255 - с , с );
Б бассейне
i f ( X == 1 ) {
if ( ) {
Такие задачки иногда будут встречаться
в книге. Любители логических загадок
должны их оценить. Но даже если вы
не относитесь к их числу, попробуйте
найти решение.
К а ж д ы й ф р а гм е н т
ко д а м о ж н о и с п о л ь
зо в а т ь то л ь ко од и н
раз!
X>0
X< 1 X=X
X> 1 X=X
X>3 X= X Poem = Poem + “noys
Poem = Poem +“ x<4 X= X Poem = Poem + “oise
Poem = Poem + “a Poem = Poem + “ oyster'
Poem = Poem + “n“; Poem = Poem + “annoys”;
MessageBox.Show(Poem);
Poem = Poem + “an“; ^ Poem = Poem + “noise”;
аГниш ь! с КодаМи
е Ш е н и е ]= » е ^ с :а Б б а с с е й н е
in t X = 0;
S t r i n g P o em =
w h ile ( X < 4 ) {
if ( X > 1 ) {
3 noise зппо^fs ап oyster
Poem = Poem + ” oyster";
X = X + 2;
ОК 1
}
if ( X == 1 ) {
if ( X < 1 ) {
В и д и те д р у го е р е ш е н и е ?
Poem = Poem + "olse П р о в ер ь те его в И С Р
} и п о с м о тр и те , как о но
р аб о та ет!
X = X + 1;
}
MessageBox.Show(Poem);
Подсказка: Существует еще
одно решение.
114 глава 2
3 обьекш ы , 310 порядку ст|^ойс:я
%
8 результ ат е появ
ляется совсем другой О
Ничего, я успею
марш рут .
вовремя, если сверну
на 28-е шоссе!
116 глава 3
объекты, по порядну стройся!
Навигационная
система генерирует
V
"Take 31st Street Bridge to Liberty Avenue to Bloomfield"
м арш рут , исходя из
пункта назначения.
Навигационная система
узнает, каких улиц
следует избегать. ^
\1/
ModifyRouteToAvoid("Liberty Ave");
string route;
route = GetRoute();
string route =
GetRoute {);
вм вровде« 3 „ „ e"L ,
КЛЮЧЕВЫЕ
МОМЕНТЫ
Классы состоят из методов, которые, в свою очередь, состоят из операторов. Осмысленный выбор методов позво
ляет получить удобный для работы класс.
Некоторые методы могут возвращать значение. Тип этого значения нужно объявлять. Например, метод, объявлен
ный как public int, возвращает целое число. Пример такого оператора: return 37 ;
Метод, возвращающий значение, обязан включать в себя оператор return. Если в объявлении метода указано
public string, значит, оператор return возвращает значение типа string.
Метод, при объявлении которого было указано public void, не возвращает значения. Но оператор return
может использоваться для прерывания такого метода: if (finishedEarly) { return; }.
^ Создайте новый проект Windows Forms Application. В окне Solution Explorer щелкни
те правой кнопкой мыщи на имени проекта и вы берите в появившемся меню команду
A dd»C lass... Н азовите файл Talker.cs, при этом класс автоматически получит имя
Talker. В ИСР появится новая вкладка с именем Talker.cs.
Сверху вставьте строчку using System.Windows .Forms, a затем введите код самого класса:
class Talker {
p u b lic s ta tic i n t B la h B la h B la h (s tr in g th in g T o S a y , i n t n u m b e r O fT im e s )
{
О перат ор s tr in g f in a lS tr in g =
о5т?я6ляет
кхеременнуи? fo r ( i n t c o u n t = 1; c o u n t <= numberOf T im e s; co u n t+ + )
finalString { X
и пры соаийа fin a lS tr in g = fin a lS tr in g + t h i n g T o S a y + " \n " \
t m ей нулевое ^
К переменной FinalString
c m y ^ C w p o K y )- M e s s a g e B o x .S h o w (fin a lS tr in g ) ;
добавляется значение
r e tu r n fin a lS t r in g .L e n g t h ; , переменной thingToSay и знак
1
} Метод BlahBlahBlahQ возвращает
^ переноса строки (\п).
значения типа int. Свойство .Length ^ Свойством Length
можно добавить к любой строке обладают все строки.
и узнать ее длину. Знак переноса (\п)
считается за один
символ.
IJej^eBepHuiue с тр ан и ц у и и родолж ш !
дальше ► 119
знакомство с объектами
*
Ч то )ке мы только что построили?
Новый класс содержит метод с именем B l a h B l a h B l a h {) и двумя параметрами. Первый пара
метр — строка с произносимым текстом, а второй — количество повторений. Этот метод вы
зывает окно, в котором фраза повторяется указанное количество раз. Метод возвращает дли
ну строки. Ему следует передать строку для параметра t h i n g T o S a y и число для параметра
n u m b e r O f T i m e s . Указанные значения вводятся в поля формы, созданные на основе элементов
управления T e x t B o x и N u m e r ic U p D o w n .
Добавим в проект форму, работающую с новым классом!
О т редакт ируйте свойство
Text таким образом, ч т о
бы в текстовом поле по
умолчанию появлялся т екст Saythis: Hello!
«Hello!» # oftimes: 3
...... ......
дальше ► 121
про экземпляры
N a v ig a to r
3 « « . « SetCurrentLocationO
W3 C^WCKOM SetDestinationO
ModifyRouteToAvoidO
дде1ляоЭоб.' ModifyRouteTolncludeO
GetRouteO
GetTimeToDestinationO
TotalDistanceO
122 глава 3
объекты, по порядку стройся!
Дом
ДатьКровО
ВыраститьЛужайкуО
ДоставитьПочтуО
ПочиститьКанализациюО
ЗаплатитьНалогО
СделатьРемонтО
Экземпляры
Все элементы окна Toolbox являются классами: класс B u t t o n ,
класс T e x t B o x , класс L a b e l и т. п. При перетаскивании
на форму кнопки из окна Toolbox автоматически создает
ся экземпляр класса B u t t o n , которому присваивается имя
b u t t o n l . Перетаскивание второй кнопки приводит к появле
нию второго экземпляра с именем b u t t o n 2 . Каждый экзем
пляр имеет собственные свойства и методы. Но при этом все
кнопки работают одинаково, так как они были созданы из од
ного класса.
' ^выполняется
/ ^<^ератор new.
Ho u s e m a p l e D r i v e l l S = n e w H o u s e ();
^"^У п р> аж н ен и е
Убедитесь сами!
Откройте любой проект, в котором присутству
ет кнопка b u t t o n l , и найдите в его коде текст
b u t t o n l = new. Этот код ИСР добавила в кон
Экземпляр,
структор форм при создании экземпляра класса образец, вещь, подобная
B u tton . другим, функция поиска и заме
ны находит все экземпляры слова
и заменяет их.
124 глава 3
объекты, по порядку стройся!
Майк добавил к СТЛ текстовое поле — ЪехЬВох!, содержащее информацию о пункте назна
чения. Затем создал поле te x tB o x 2 , для ввода названия улицы, которую нельзя включать
в маршрут; и поле ЬехЬВохЗ, содержащее информацию о еще одной улице, которую жела
тельно объехать.
Это экземпляр
© О н со зд а л о б ъ е к т N a v i g a t o r и указал пункт н а зн а ч ен и я . класса Navigator.
N a v ig a to r
SetCurrentLocationO
SetDestinationO
IVIodifyRouteToAvoidO
str in g d e stin a tio n = te x tB o x l.T ex t;
!\/1odifyRouteTolnclude()
GetRouteO N a v ig a to r n a v i g a t o r l = n e w N a v i g a t o r () ;
GetTimeToDestinationO
TotalDistanceO n a v ig a to r l. S e t D e s t i n a t i o n ( d e s t i n a t i o n ) ;
ro u te = n a v i g a t o r l . G e t R o u t e () ;
vigotorl'
5,6 км Создание нового
объекта на основе
класса называется
Теперь Майк может вызвать общий для всех объектов метод
T o t a l D i s t a n c e O (Общее расстояние) и определить самый
созданием экзем
короткий маршрут. И для этого ему понадобился один фраг пляра класса.
мент кода, а не три!
дальше У 125
немножко маленьких секретов
Теория U практика
Наша книга построена по следуюгцему принципу. Вводится некое по
нятие, для демонстрации которого используются картинки и неболь
шие фрагменты кода. На данном этапе вы должны всего лишь попять
новую информацию. Процедура написания рабочих программ будет
рассматриваться позднее.
Ho u s e m a p l e D r i v e l l S = n e w Ho u s e О ;
Г в руку карандаш
Попробуйте вслед за Майком написать код для объектов
N a v i g a t o r И вызова ИХ
методов.
Navigator navigator2 =
navigator2.
navigator2.
int distance2 =
^2.CoздaйтeoбъeктnavigatorЗ,yкaжитeпyнктнaзнaчeния,вызoвитeмeтoд^ЯodifyRouteToincludeO,~*
a затем воспользуйтесь методом TotalDistanceO ДЛЯ вычисления целой переменной distances.
L _
Встроенный в .NET Framework метод Math.MmQ
сравнивает два числа и возвращает меньшее. Именно
с его помощью Майк нашел самый короткий путь.
N a v ig a to r n a v ig a to r l = n e w N a v i g a t o r () ; ^ ^ СОзЭаеМ о б ш к т
n a v ig a to r l. S e tD e stin a tio n (d e stin a tio n ) ; ^ a .v ig a t o r , у к а з ы в а е м п у н к т
in t d ista n ce l = n a v ig a to r l .T o ta lD ista n c e 0 ; ^ н а з н а ч е н и я и определяем
расст ояни е.
navigator2 . SetDestination(destination);
navigator2 . ModifyRouteToAvoid(routeZStreetToAvoid);
navigator3.SetDestination(destination);
navigator5.ModifyRouteTolnc!ude(route3StreetTolnclude);
128 глава 3
объекты, по порядку стройся!
Чаап°
'^ а Д а Б а е М ы е
Б о гц р о с ь і
Слово «статический» ассоциируется у меня с веща Почему не сделать статическими все методы?
ми, которые не меняются. Означает ли это, что нестати
ческие методы могут меняться, а статические нет? 0 1 При наличии объектов, отслеживающих данные, напри
мер, экземпляров класса N a v i g a t o r , каждый из которых
0 ; Нет. Единственным отличием статического метода от отслеживал свой маршрут, для работы с этими данными мож
нестатического является невозможность создавать его эк но использовать собственные методы экземпляра. Скажем,
земпляры. Слово «статический» в данном случае не следует при вызове метода M o d i f y R o u t e T o A v o i d () для экзем
воспринимать слишком буквально. пляра n a v i g a t o r s менялся только маршрут номер два.
На маршруты экземпляров n a v i g a t o r l и n a v i g a t o r s
То есть я не смогу пользоваться классом, не создав эта процедура никак не влияла. Именно поэтому программа
экземпляр объекта? Майка могла работать с тремя маршрутами одновременно.
0 ; Создание экземпляров является обязательным услови • А как именно экземпляры отслеживают данные?
О
ем для работы с нестатическими классами. Для статических
классов это не требуется. ; Переверните страницу и узнаете!
дальше * 129
как дела у объектов
Поля
Редактирование расположенного текста на кнопке осуществляет
ся при помощи свойства T e x t . При внесении подобных измене Технически вы
ний в конструктор добавляется следующий код: задаете свойстЯп
Свойс\тва очень
b u tto n l.T e x t = "Текст для кнопки";
покожи на поля^
Как вы уже знаете, b u t t o n l —это экземпляр класса B u t t o n . ' Код но об эт ом мы
же редактирует поле этого экземпляра. На диаграмме классов спи поговорим чуть
позже.
сок полей находится сверху, а список методов —снизу.
Сверху на диаграмме
классов находится список
полей. Экземпляры
класса используют их Эта линия
для отслеживания своего отделяет поля
состояния. от методов.
Метод — это то, что объект делает. Поле — это то, что объект знает^.
в результате создания Майком трех экземпляров класса N a v i g a t o r его программа создала три
объекта, каждый из которых отслеживает свой маршрут. При вызове метода S e t D e s t i n a t i o n ()
для экземпляра n a v i g a t o r 2 пункт назначения указывается только для этого экземпляра. Он ни
как не влияет на экземпляры n a v i g a t o r l и n a v i g a t o r 3 .
N a v ig a to r Каждый из экземпляров
Navigator знает свой пункт
Destination назначения и свой маршрут.
Route
Создаем эЬемкдяры!
M em odL
Для добавления полей достаточно объявить I
п е р е м е н н ы е в н е м е т о д о в . Так, в с е эк зем п л я - c l a s s C lo w n {
ры будут и м ет ь с в о и к о п и и э т и х п е р е м е н н ы х . str in g Nam e;
p u b lic in t H eig h t;
p u b lic v o id T a l k A b o u t Y o u r s e l f () {
M e s s a g e B o x . S h o w ( "My n a m e is "
+ Nam e + " and I'm "
+ H eig h t + " in c h e s ta ll." );
o n e C lo w n .H e ig h t = 14;
C lo w n a n o th er C lo w n = new C lo w n ();
a n o th e r C lo w n .N a m e = " B iff" ;
a n o th e r C lo w n .H e ig h t = 16;
C lo w n c lo w n s = new C lo w n {);
c lo w n 3 .N a m e = a n o th e r C lo w n .N a m e ;
c lo w n 3 .H e ig h t = o n e C lo w n .H e ig h t - 3^
a n o th e r C lo w n .H e ig h t *= 2;
Спасибо за память
Создаваемые объекты находятся в так называемой куче
(heap) — области динамической памяти, выделяемой на
стадии исполнения программы. Применение оператора
new автоматически резервирует место в памяти под хра
нение данных. /
Вот так выглядит куча до
начала работы программы.
Д д, она действительно
пуста.
C lo w n a n o th er C lo w n = (n ew C lo w n ( );
a n o th e r C lo w n .N a m e = " B iff" T
a n o th e r C lo w n .H e ig h t = 16;
--- - .
C lo w n c lo w n 3 = m ew C lo w n () ;
c lo w n B .N a m e = an o B H S F C T o ra .N a m e;
c lo w n 3 .H e ig h t = o n e C lo w n .H e ig h t - 3;
a n o th e r C lo w n .H e ig h t *= 2;
C lo w n m y l n s t a n c e = new C lo w n ( );
C lo w n o n eC lo w n = new C lo w n о
o n e C lo w n .N a m e = " B offo" ;
^Ьект
o n e C lo w n . H e ig h t = 14; vvOA^-
o n e C lo w n .T a lk A b o u tY o u r s e lf();
C lo w n a n o th er C lo w n = new C l o w n ()
a n o th e r C lo w n .N a m e = " B iff" ;
Операторы
создают второй
a n o th e r C lo w n .H e ig h t = 16; объект и п р и
' • a n o t h e r C l o w n . T a l k A b o u t Y o u r s e l f {)
сваивают ему
данные.
о C lo w n c lo w n 3
c lo w n 3 .N a m e
= new C lo w n 0 ;
= a n o th e r C lo w n .N a m e ;
c lo w n 3 .H e ig h t = o n e C lo w n .H e ig h t - 3
-c lo w n s.T a lk A b o u tY o u rself();
a n o th erC lo w n .H eig h t *= 2;
■ ' a n o t h e r C l o w n . T a l k A b o u t Y o u r s e l f () ;
Значимые имена
При написании кода методов выбирается структура программы. Вы ищете
ответы на вопросы: воспользоваться ли одним методом или, может быть,
разбить его на несколько? А может быть, метод тут вообще не нужен? В ре
зультате можно получить как интуитивно понятный код, так и нечто за
путанное и нечитаемое.
О Перед вами пример компактного кода. Это программа, управляющая автоматом по производ-
ству шоколадных батончиков.
© Операторы не дают даже намеков на предназначение кода. Зато нужный результат полу
чен при помощи всего одного метода. Но всегда ли максимальная компактность является
конечной целью? Давайте разобьем код на несколько методов и сделаем его более про
стым. Вы на практике убедитесь в необходимости классов и осмысленных имен. Для на
чала нужно понять, какую задачу решает данный код.
• В ы п устите воду
134 глава 3
объекты, по порядку стройся!
О Инструкция не только помогла определить назначение кода, но и показала, как сделать его
более понятным. Мы узнали, что проверка условия определяет, не превышает ли параметр
Ъ значение 160, ведь в инструкции написано, что при 160 °С нуга становится слишком го
рячей. А буква т оказалась классом, который контролирует поведение автомата. В класс
входит статический метод проверки температуры нуги и работы воздушной системы. Вы
делим проверку температуры в отдельный метод и дадим классу и методу смысловые имена.
' p u b l T c ^ ' b o o l e a ^ I s N o u g a t T o o H o t {) {
Тип значений, in t tem p = M aker. C h eck N ou gatT em p eratu re{);
возвращ аем ы х (tem p > 160) { ^ Назовем к л а с с M a k e r
^ r e t u „ t.ue,. L
j , , , ^ 3 ^ наг«).
, ^ В о зв р а щ а е м ы е зн ач ен и я
J т и п а B o o le a n , э т о tr u e
и л и fa lse .
p u b l i c (y o i d J P o C I C S V e n t P r o c e d u r e () {
/к о д и ф и к а т о р v o id T u r b in e tu r b in e C o n tr o lle r = new T u r b i n e () ;
озн ач ает , чт о м ет о д t u r b i n e C o n t r o l l e r . C lo se T r ip V a lv e (2) ;
Т н а ч е н т '^ ^ ^ '^ ни как ого I s o l a t i o n C o o l i n g S y s t e m . F i l l {) ;
Is o la tio n C o o lin g S y ste m .V ent О ;
M a k e r .C h e c k A ir S y ste m O ;
}
Теперь, даже если вы не читали инструкцию и не знаете, что процедура CICS запускается
при слишком высокой температуре нуги, вы все равно можете понять, что делает код:
if { I S N o u g a t T o o H o t () == tru e) {
D oC IC S V en tP roced u re( ) ;
Структура классоб
Почему же методы следует делать интуитивно понятными? Потому что каждая
программа решает проблему или имеет конкретную цель. Целью далеко не
всегда являются серьезные задачи, иногда программы могут писаться просто для
забавы! Но каково бы не было предназначение программы, чем больше код напо
минает о том, какая именно проблема с его помош;ью решается, тем проще такую
программу писать, читать и редактировать.
Д и а гр а м м а к л а с с а
C la s s N a m e
Э т о н а гл я д н о е п р е д с т а в л е н и е MethodO
к л а с с а на л и с т е дуллаги. MethodO
MethodO
В в е т ц п и ш е т с я амя к л а с с а ,
а п о д н и м в се в х о д я щ и е
в класс м ет оды !
c la ss C a n d y C o n tro ller {
p u b lic v o id D o M a i n t e n a n c e T e s t s () {
if ( I S N o u g a t T o o H o t () == tru e) {
D oC IC S V en tP roced u re(); C a n d y C o n tro lle r
} DoMaintenanceTestsO
DoCICSVentProcedureO
} IsNougatTooHotO
p u b lic v o id D o C I C S V e n t P r o c e d u r e ()
p u b lic b o o le a n I S N o u g a t T o o H o t {) ..
136 глава 3
объекты, по порядку стройся!
“»І»
Ж "
ддетоЭ.
Программа содержит
м °m Z “
дальше ¥ 137
несколько советов
_________________
138 глава 3
объекты, по порядку стройся!
Р азносчи кП и ц цы
Эти классы являются частью системы учета доставки пиццы.
ДобавитьПиццуО
ПиццаДоставленаО
ПодсчетСуммыО
ВремяВозвращенияО
Р а зн о с ч и ц а П и ц ц ы
ДобавитьПиццуО
ПиццаДоставленаО
ПодсчетСуммыО
ВремяВозвращенияО
дальше * 139
создадим класс
V
Этот класс является частью знакомой вам системы
производства шоколадных батончиков. Д елаем Б атончики
Из имени класса С1а5523.ао() не понятно его назначение. ВесБатончикаО
ПечатьОберткиО
Кроме того, мы выбрали более осмысленное имя для ПечатьОтчетаО
СоздатьБатончикО
последнего метода.
140 глава 3
объекты, по порядку стройся!
c la ss {
p u b lic i n t ________ = 0;
Дополнительны й
p u b lic s t r i n g ________
в о пр о с!
retu rn " h e llo o o o ..
Как решить задачу, чтобы
} вместо 10 в последней
строке оказалось 24? Для
} этого достаточно заме
нить всего один оператор.
К аж д ы й ф р а г
м ент можно
и с п о л ь зо в а ть <4
IS 'теч*' X
н е с ко л ь ко раз! Echo
х х<5
у X > 0 Tester
е2 X > 1 Echo() е2 = е1;
count count(; Echo е2;
ЄІ = ЄІ + 1;
НеІІо() Echo е2 ■ el; x==3
ЄІ = count + 1;
Echo е2 ■ new Echo(); x == 4
el .count = count + 1;
el count = e1 count + 1;
ж®;
ОіюБЄї О на с.
дальше *
работающий класс guys
Помогите парням
Джо и Боб все время одалживают друг другу деньги. Напишем программу отслежи
G uy
вания истории займов. Для начала нужно понять структуру создаваемого класса.
Name
Cash
Создадим класс би у и добавим в цю рм у два е го экземпляра
Первое поле формы будет называться j ое (для слежения за первым
объектом), а второе bob (для слежения за вторым объектом). GiveCashO
ReceiveCashO
Оператор neWj
создающий экземпляры Os
запускается в м омент
загрузки формы. На
рисунке представлен Итак, наш класс на
вид кучи после этой зывается Quy (Парень).
операции. Метод GiveCaskQ
’Ьект отвечает за передачу
денег в долг, а м е
тод ReceiveCashQ —
за их получение. Поля
Name и Cash содержат
информацию об имени
о Сопоставим каж дом у объекту б и у поля N am e и Cash
Два объекта соответствуют двум парням, у каждого из кото
и сумме соот вет
ственно.
рых есть свое имя и некоторое количество денег в кошельке.
Ч5іект&'^ Параметром
метода ReceiveCash()
является количество
получаемых денег.
Поэтому запись
О П а р н и д а ю т деньги и п о л учаю т и х назад
Метод R e c e iv e C a sh () увеличивает количество денег у объ
joe.ReceiveCash(ZS)
означает, что Джо
получит Я5" долларов.
екта, а метод G iveC ash () уменьшает этот параметр.
142 глава 3
объекты, по порядку стройся!
Проект «Парни»
Создайте проект Windows Forms Application (ведь нам понадо
У п і^ а ж н е н и е
бится форма). В окне Solution Explorer создайте класс с именем
G u y . Добавьте в верхнюю часть файла этого класса строчку u s i n g
S y s t e m . W i n d o w s . F o r m s ; , затем введите следующий код:
return 0;
Если переменная am ount больше
} нуля, мет од ReceiveCash ()
} добавляет ее к переменной
Cash. В противном случае
Следите за т ем , чтобы появляется окно с сообщением
количество открывающихся и возвращается значение О (ноль).
скобок совпадало с количеством
закрывающихся. ИСР поможет вам
в этой задаче.
Для отслеживания финансового состояния наших героев потребуются два поля. Назовите
их j ое и bob. Затем добавьте поле с именем b an k для расчета, сколько форма должна взять
у объектов, а сколько отдать им. Дважды щелкните на третьей метке и добавьте в появив
шийся код строки:
n am esp ace Y o u r_P roject_N am e {
p u b lic p a r tia l c la ss Form l : F orm {
Guy joe?
Поля Joe ------^
и Bob объяв- Guy bob ; Значение поля ban k
лены в классе т о возраст ает , т о
in t bank ■ 100; И м еньилает ся в з а
Guy. ви си м ост и о т т о го
p u b lic F o rm l0 { ^1<олько д е н е г ф о р м а
In itia liz e C o m p o n e n t(); о т д а л а о б ъ е к т а м Quu
и ск о л ьк о взяла о т них
}
144 глава 3
объекты, по порядку стройся!
М етки U p dateForm О {
ляю т сй при jo esC a sh L a b el .T ex t = jo e .N a m e + им еет $" + jo e .C a s h ; Этот метод
и м е е т $" + b o b . C a s h ;
обновляет м е т
поМОЫА,и п о л е й г b o b s C a s h L a b e l . T e x t = b o b . Name +
N a m e и C a sh J - i ки, изменяя их
м е т о д а G u y. ( b a n k C a s h L a b e l . T e x t = "B б а н к е с е й ч а с $" + b a n k ; свойство Text.
} e ls e {
M e s s a g e B o x . S h o w ( "B б а н к е н е т д е н е г . " ) ;
I 6 Ът ьш .
Ц 5o6a метод GiveCashQ
возвращает О.
„ з а д а й т е н а ,а л ь н ь .е з н а ^ е н и ^ и Т Г е Г л /^ Г
}
О Введите запятую, и сразу после нажатия пробела появится еще одно поле,
joe = new G u y O { Cash = 50,
ф N am e string Guy.Name
- з д а н и е u H in J J u n iu B H o ц о н я щ н ь іх кЛ ассоБ
G
ROAD CLOSED
CHEMIN FERMÉ І
★ Присваивайте классам и методам значимые имена.
Другие пользователи должны понимать назначение классов и методов по виду их
имен.
148 глава 3
объекты, по порядку стройся!
Вот как стала выглядеть программа после того, как к ней добавили кнопки передачи денег
аж нш е от Боба к Джо и обратно.
у г ш ш г
p u b lic F o rm l0 {
In itia liz e C o m p o n e n tO ;
b o b « n ew G u yO { C a sh = 1 0 0 , Name * "Bob" } ;
j o e a n ew Q u yO { C a sh = 5 0 , Name = " J o e" } ;
U p dateF orm O ;
}
p r i v a t e v o i d b o b G i v e s T o J o e _ C X ic k ( o b j e c t s e n d e r , E v e n tA r g s e ) {
V j o e .R e c e iv e C a s h ( b o b .G iv e C a s h ( 5 ) ) г
U p d a te F o r m O ;
}
объекты, по порядку стройся!
еШ ение ® бас:с:е^1Не
e 2 . c o u n t = e 2 . c o u n t + 1;
}
if ) {
е 2 .count = е 2 .count + e l.c o u n t;
x = X + 1;
}
MessageBox.Show(result + "Count; " + e2.count)
c la ss Echo {
p u b lic in t count = 0;
p u b lic str in g HelloQ {
retu rn " h e llo o o o ..." ;
}
дальше * 151
4 щ ипь! и ссылки
10:00 утра. ^
Куда подевались наши данные?
Без д а н н ы х п р о г р а л ш ы б е с п о л е з н ы .
Взяв информацию от пользователей, вы производите новую и н ф о р м а
цию, чтобы вернуть ее им же. Практически все в программировании свя
зано с о б р а б о т кой д а н н ы х тем или иным способом. В этой главе вы
познакомитесь с используемыми в С # т ипам и данны х, узнаете методы
работы с ними и даже ужасный секрет объ ект ов (только т-с-с-с... объ
екты — это тоже данные).
не мои тип
154 глава 4
типы и ссылки
дальше у 155
взять деньги на мороженое
Tun int
хранения це з ^47.
не больиле 2. 3-^
Tun short хранит целые
числа до 32.767.
156 глава 4
типы и ссылки
$ к о гЬ . С о д е р ж и м о е с т а к а н а m t п р и э т о м н е
и м е е т значен и я.
b y te days = 365;
дальше ► 157
приведения
Приведение типов
О 1 C annot tmpKcttly convert type 'd e d m a f to ’in f. An ecpttcrt conversion ecists (are you nfiissing a cast?) ИСР предполагает,
что вы забыли осуще
ст вит ь приведение
о Устраните ошибку, преобразовав тип d e c im a l в i n t . Внесите во вто
рую строку следующие изменения:
типов (casting).
i n t m y ln tV a lu e = (in t) m y D ecim a lV a lu e;
Этот оператор делает
приведение типа decimal
Ч то )ке произошло? к т ипу int. Вернитесь в начало
'предыдущей главы
Компилятор не позволяет присваивать значения несовпадающих ти « посмот рите, как
пов, даже если переменная попадает в правильный диапазон. Приведе ^ приведение типов
ние типа —это объяснение компилятору, что вы знаете о несовпаденйи использовалось для
і^ередачи значения
типов, но в данном конкретном случае осознанно осуществляете опера
^J^'^encUpDown ф ор
цию присваивания. м е Talker Tester
158 глава 4
типы и ссылки
d o u b l e m y D ou b le = (d o u b le)m y B y te;
m y S trin g =
(b o o l)m y S trin g ;
(str in g )m y in t;
дальше ► 159
настоящее преобразование
П арам ет р т ипа
lo n g 1 = 139401930;
sh o rt вы чит ает ся
из п а р а м е т р а т и п а
sh ort s = 516;
lo n g , а о п е р а т о р =
п р ео б р а зует
d o u b le d = 1 ;
р езул ьт ат к т ипу нение--------- ----------
d = d / 1 2 3 .4 5 6 ;
d o u b le .
)'^ешенке
M e s s a g e B o x . S h o w ( "О твет " d) ; И так, вы у б е д и л и с ь , что п р и в е д е н и е в о з
м о ж н о д а л е к о не д л я всех ти п о в . Зачер
кнутые строки не позволяют осуществить
В л а го д а р я о п е р а т о р у ^ построение программы.
п р о и сх о д и т п реоб разован и е
т и п а d e c im a l к т и п у s tr in g . in t m y in t = 10;
b y t e m yB yte = (b y te )m y in t;
Во-вторых, автоматическое преобразование происхо
d o u b l e m yD ou ble = (d o u b le)m y B y te;
дит при объединении строк оператором Все числа
при этом преобразуются к типу string. Рассмотрим при bqrri—и у В в е 1 ■—— —
мер, в котором первые две строки кода написаны пра
вильно, третья не может быть скомпилирована. str in g m yS trin g = " f a l s e " ;
M e s s a g e B o x .S h o w (" О т в ет " + х ) ;
mygtrrreg--~~4-&fe*4:ag-)-my»m‘fe-;—^
m yS trin g = m y in t.T o S tr in g ();
M e s s a g e B o x .S h o w ( х ) ;
- « ¥ E ucl1—^=— (-b e o l ) m y B y t «-,-
Компилятор выдает сообщение об ошибке в связи с не
правильным аргументом (аргументом в C# называется —(fa^'tTc:') myOoa lf ----
значение, передаваемое методу в качестве параметра). s h o r t m yShort = (sh o rt)m y in t;
Параметр метода M e s s a g e B o x . S h o w O должен при
надлежать типу s t r i n g , код же передает переменную c h a r myChar = ' x ' ;
типа l o n g . Впрочем, вы легко можете осуществить jny S t r ir a g —^— (-at r i n g ) m y e h a g ;
это преобразование при помощи метода T o S t r i n g ( ) .
Этим методом обладают все объекты. (И все создан l o n g m yLong = (lo n g )m y in t;
ные вами классы имеют метод T o S t r i n g ( ) , возвраща
d e c im a l m yD ecim al = (d e c im a l)m y L o n g ;
ющий имя класса.) Именно с его помощью можно пре
образовать X в параметр метода M e s s a g e B o x . S h o w (): m y S t r i n g = m y S t r i n g + m y in t + m yB yte
+ m y D o u b l e + m y C h a r;
M essageB ox. S h ow (x. T o S tr in g ( ) ) ;
160 глава 4
типы и ссылки
Все работает, пока вы передаете методу то, что он ожидает получить (логиче
скую переменную), вызов MyMethod ( tr u e ) или MyMethod ( f a l s e ) позволяет
легко скомпилировать код. ^ ^
Переменной, парам е-
Но что получится, если передать методу число или строку? Вы получите <тру или полю Можно
уже знакомое сообщение об ошибке. Попытайтесь передать параметр типа Р\р01АЗ —
вольное значение при
B o o lean , но в качестве возвращаемого значения укажите строку или передай
помощи типа object.
те результат методу M essageB ox. Show (). Код перестанет работать, ведь ме
тод возвращает значение типа i n t , а не lo n g или s t r i n g , которое требуется
методу M essageB ox. Show().
условия.
na m e s p a c e
£ог
class
p u bl ic
else
while
using
if
пеуг
О ш Б е г п ы н а с. 9
162 глава 4
типы и ссылки
Создадим калькулятор расходов для деловой поездки. Вы будете вводить данные с одометра
п^ражнение в начале и конце путешествия, а счетчик вычислять, какое расстояние было пройдено и какую
сумму денег вам вернут в бухгалтерии, при условии, что за каждую пройденную милю полага
ется $.39.
З ап уск прогроАлмы.
Убедитесь в вводе правильных значений. Укажите начальный пробег больше конеч
ного и убедитесь в появлении окна с сообщением.
d o u b le r e im b u r se R a te = .3 9 ;
d o u b le am oun tO w ed ;
p u b lic F o rm l0 {
} П ом ни те, « ,ц ц я
, э л е м е н т а управления
p r iv a te v o id b u tto n l_ C lic k (o b je c t sender, E ven tA rgs e) { ,,iflA ericU p'P ovJn о а м
н у ж н о п о т н я т ^ > у^ ^ ^
S ta r tin g M ile a g e = (in t) n u m e r ic U p D o w n l.V a lu e ;
d L l m a l на m t.
e n d in g M ile a g e = (in t)n u m e r ic U p D o w n 2 .V a lu e ; ^
164 глава 4
типы и ссылки
Ещ е одна кнопка
Попытаемся понять, почему не работает форма, добавив еще одну кноп
ку, которая будет показывать значение поля m ile s T r a v e le d . (Это можно
сделать при помощи отладчика!)
З ап уск програ/имы
Введите какие-нибудь значения и посмотрите, что
получится. После ввода начального и конечного про
бега щелкните на кнопке Calculate. После этого щел
чок на кнопке Display Miles покажет значение поля
m ile sT r a v e le d .
Комбинация с оператором =
Обратите внимание на оператор, который использовался для вычитания на
чального пробега из конечного (-=). Проблема в том, что он не только вычита
ет, но и присваивает значение переменной. То же самое происходит в строке,
где мы умножаем пройденные мили на тариф возмещения. Поэтому нужно
заменить операторы -= и *= на - и *:
am oun tO w ed = m ile sT r a v e le d (^ ^ )r e im b u r se R a te ;
} e ls e {
166 глава 4
типы и ссылки
-г
___/ Получается, что нет разницы \
^ с чем работать, с объектом или с числом. )
В любом случае я работаю с переменной. )
О
дальше ► 167
получи ссылку
Это куча до
запуска кода. Там
ничего нет.
p u b lic p a r t i a l c la s s F o rm l Form
{
Guy joe;
p u b lic F o rm l0
Это переменная { Создание ссылки подобно
joe, ссылающаяся I n i t i a l i z e C o m p o n e n t () наклеивании) стикера, вы
на объект йму< помечаете объект, чтобы
joe = new Guy(); получит ь возможность
ссылаться на него в даль
нейшем.
Это ссылочная ^ объект,
переменная... на который
она ссылается.
168 глава 4
типы и ссылки
Переменная ^т о ссылочные
«папа» является ’л еременные,
ссылкой на экзем указывающие на
пляр класса Quy. ОДИН И ТО Т ЖЕ
ооьект. Различные методы м о
гут использовать эк-
Обращение к объекту никогда не проиходит напрямую. К примеру, не
ссь/лоч-
возможно записать G u y . G i v e C a s h {), если G u y принадлежит типу object. НЫМ п е р е м е н н ы м р а
Компилятор не понимает, куда именно вы обращаетесь, так как в куче мо зум н о п р и сво и т ь разн ы е
жет храниться несколько экемпляров G u y. Вам нужна ссылочная перемен в зависимости от
ная, например, j o e , которой будет присвоен конкретный экземпляр G u y контекста.
j ое - new G u y ().
дальше ► 169
вот водитель мусоровоза
■■■поэтому
Объект
удаляется!
170 глава 4
типы и ссылки
О бъ ектов:
lu c k y .B r e e d = "D achshund";
Бах!
fid o = rover;
/ Л \
Lucky — это т рет ий
объект. A Fido теперь
ссылается на Object *%.
На object не
остается ссылок. Он
удаляется.
дальше ► 171
так много меток
їозьми в руку карандаш Теперь ваша очередь. Вот длинный код, разбитый на блоки.
Укажите, сколько объектов и сколько ссылок на них имеется на
каждой стадии. Справа нарисуйте объекты в куче и их метки.
О бъ ектов :.
Ссы лок:
© Dog sp ot
s p o t .B reed
= new
=
D o g {);
"D achshund";
sp ot = rover;
О бъ ектов :______
С сы лок:_____
О Dog lu c k y
lu c k y .B r e e d
= new
=
D o g {);
" B e a g le" ;
Dog C h a rlie = fid o ;
fid o = rover;
О бъ ектов :______
С сы лок:_____
^ r in T in T in = lu c k y ;
Dog la v e r n e = new DogO ;
la v e r n e .B reed = "pug";
О бъ ектов :______
Ссы лок:_____
О C h a r lie
lu ck y =
= la v e r n e ;
r in T in T in ;
О бъ ектов :______
С сы лок:_____
172 глава 4
типы и ссылки
S S e .t e ,- " -
с E le p h a n t
Name
т акое окно.
EarSize
WhoAmIO
Класс Elephant
Согласно диаграмме классов E le p h a n t вам потребуется поле типа i n t с названием E a r S iz e
и поле типа S t r i n g с названием Name. (Убедитесь в наличии модификатора public.) Добавьте
метод WhoAmI (), вызывающий окно с информацией о размере уха и имени слона.
“^ешение
Dog rover = new D og();
r o v e r .B reed = "G reyhound";
Dog r in T in T in = new D og();
Dog f id o = new D og(); % dof
Dog q u e n tin = fid o ;
(^здан новый объект
^од со ссылкой Spot. В
результ ат е операции
Spot = Rover объект
исчезает.
y/
© Dog sp ot
s p o t .B reed
= new
=
D og();
"D achshund";
^
spot = rover;
О б ъ екто в :. 4
Ссылка Rin Tin Tin
Ссы лок: ^ т е п ^ ь указывает
на объект Lucky,
поэт ому предыдущий
C h a rlie = la v e r n e ; объект был удален.
lu c k y = r in T in T in ;
О бъ ектов: 4
\
Ссы лок: 8 Происходит перенаправление
ссылок, но новые объекты не соз
даются. Последний оператор не
делает ничего, так как обе ссылки
174 глава 4 и т ак указывали на один и т от
же объект.
типы и ссылки
E lep h a n t lu c in d a ;
E lep h a n t llo y d ;
p u b lic F o rm l0
In itia liz e C o m p o n e n tO ;
Это код класса l u c i n d a = n e w E l e p h a n t ()
F orm l из файла { Name = " L u c i n d a " , E a r S i z e = 3 3 } ;
Formi.cs. l l o y d = new E le p h a n t 0
{ Name = " L l o y d " , E a r S i z e = 4 0 } ;
}
p r iv a te v o id b u tto n l_ C lic k (o b je c t sen d er, E ven tA rgs e) {
С с ы л к а H o ld e r llo y d .W h o A m I( ) ;
н у ж н а для т о го }
ц т о б ы о б ш к т L io y d p r iv a te v o id b u tto n 2 _ C lick (o b je ct sen d er, E ven tA rgs e) {
не и сч ез, п о сл е т о го lu c in d a .W h o A m I( ) ;
как ссы лка б уд ет
п е р е н а п р а в л е н а на
}
объект Lucinda. p riv a te v o id b u tto n 3 _ C lick (o b je ct sen d er, E ven tA rgs e) {
- E lep h a n t h o ld e r ; О п е р а т о р n e w не и с п о л ь з у е т с я ,
S S r= ^ u c? n d ;; ^ ^ т а к к а к н а м не н у ж е н е щ е оЗын
lu c in d a = h o ld e r ; э к з е м п л я р E le p h a n t .
M e s s a g e B o x . S h o w ( "Obj e c t s s w a p p e d " ) ;
}
Щ елкните на кнопке, чтобы проверить свои догадки. Смотрите, это окно ^1ер)г\0!Г^
Lucinda. При том что вы вызывали метод WhoAmI () для Ллойда.
Lucinda says...
Но это значение
Окно Аизсинды... <^е.ременной
EarSize было
определено для
Ллойда. Что
'происходит'?
Данные при эт ом
не п ереп и сы вали сь,
изменились толч^ко
ссылки.
176 глава 4
типы и ссылки
Тип элементов
ИМЯ
Мйссибя heights;
heights = new int [7];
[0] = 68
[1] = 70
Вы ссыла [2] 63
етесь на j
элем енты < [3] = 60
массива по
индексу [4] 58
[5] = 72 ^ М а с с и в — это единый объ
^QCOA^ ект , хотя и состоящий из
[б] = 74 семи обычных переменных.
дальше ► 177
выбираем объект из очереди
о д у -
178 глава 4
типы и ссылки
той
в о зв р а щ а ет число э л е м е н -
M e a ts. Т а к ч т о r a n d o m i z e r
N e x t ( M e a t s .L e n g t h ) да ем , с л ц ч а й н о е ч и гл п
Me a t s [ й ш 5 о т Г г е г .Ne xt (M e a t s .Length) ]
Meats - э т о массив строк, состоящий
из п я т и э л е м е н т о в . tA e a ts ^ ^
Веер’ (Ростбиф), л Meats[3j н
(Ветчина).
© Построение срормы
Добавьте к форме шесть меток и задайте свойство T ex t. Для этого воспользуй
тесь объектом МепиМакег. Объекту потребуется присвоить начальные значения,
исп ол ьзуя н о в ы й э к зем п л я р класса Random .
^ П р и сво и т е п о л ю R a n d o m iz e r
p u b l i c F o rm l О { О б ъ е к т а М е п и М а к е г новы й
I n i tializecom ponent О ; Г х^^^сса R a n d o m .
180 глава 4
типы и ссылки
В ы у ж е в и д е л и , как ф о р м ы о б р а щ а ю т с я к о б ъ е к т а м п р и п о м о щ и с с ы л о ч н ы х
п е р е м е н н ы х , к о т о р ы е вы зы ваю т м етоды и п р о в ер я ю т зн ач ен и я п ол ей . Д ля
объ ек тов оп р ед ел ен ы те ж е оп ер ац и и , ч то и для ф о р м , потом у ч то форма —
это объект. П р и о б р а щ е н и и объ ек тов друг к другу и сп ол ьзуется к л ю ч евое
с л о в о t h i s , с е г о п о м о щ ь ю д е л а е т с я ссы л к а н а т е к у щ и й э к з е м п л я р класса.
}
М ож н о добавить м етод b u t t o n 4 _ C l i c k ( ) , н о сделать эт о нуж но до оператора, переза
гружающего ссы лки ( 1 1 o y d = l u c i n d a ;)!
lloyd.TellMe("Hi", lucinda);
М ы в ы зв а л и м е т о д T e l l M e () (С к а ж и м н е ) д л я Л л о й д а и п е р е д а л и е м у д в а п а р а м е т р а : с т р о
ку H i и с с ы л к у н а о б ъ е к т L u c i n d a . П а р а м е т р w h o S a i d I t ( К т о э т о с к а за л ) и с п о л ь з у е т с я д л я
д о с т у п а к п а р а м е т р у N a m e л ю б о г о с л о н а , и м я к о т о р о г о б у д е т п е р е д а н о м е т о д у T e l l M e ()
вторы м параметром.
Э т о т м е т о д к л а с с а E le p h a n t
к-як- ч т о п я б о т я р т в(р13Ь1в а е т м с т о д T a lk T o Q
П о с м о т р и м , как э т о р а б о т а е т . (П о го в о р и с ), п о з в о л я ю щ и й о д н о м у
lloyd.SpeakToducinda, "Hello") ; слон у р а зго в а р и в а т ь с д р уги м .
П р и в ы з о в е м е т о д а S p e a k T o () д л я Л л о й д а в ы и с п о л ь з у е т е п а р а м е т р t a l k T o ( я в л я ю щ и й
с я с с ы л к о й н а Л ю с и н д у ) , ч т о б ы в ы з в а т ь м е т о д T e l l M e () д л я Л ю с и н д ы .
WhoToTalkTo.TellMe(message, this);
ключевое слово this
Ллойд использует ^заменяется ссылкой
WhoToTalkTo (со ссылкой на .на объект Ллойд
Люсинду) для вызова Те11Ме()
/ \Л o b i« ''''
Часш'»
• Форма — это только объект? ^адаБаеМые
Что на самом деле происходит
Б оИ роС Ь !
О
с объектами после попадания в мусор?
^ ! Да! Именно поэтому код класса начи ! Вот для такой проверки условия:
нается с объявления этого класса. Открой Q ; Помните, в начале первой главы мы
те код формы и убедитесь сами. Затем if (llo y d == n u l l ) { говорили об Общеязыковой исполняю
откройте файл P r o g r a m . c s для любой щей среде (CLR)? Это виртуальная ма
из написанных программ и посмотрите на Оно имеет значение t r u e , если ссылка шина, управляющая всеми программами
метод M a i n O , вы найдете там строку l l o y d указывает на n u l l . •NET. Виртуальной машиной называется
new F o r m l {). способ изоляции работающих программ
Кроме того, ключевое слово n u l l от остальной оперативной системы. Вир
Зачем мне может потребоваться позволяет быстро избавиться от туальная машина управляет используемой
ключевое слово null? ставшего ненужным объекта. Если памятью. Она следит за всеми объектами,
ссылки на объект остаются, а вам он уже и как только последняя ссылка на какой-
не нужен, достаточно воспользоваться нибудь из объектов исчезает, она освобож
ключевым словом n u l l , и объекг будет дает оперативную память, которая была
уничтожен. выделена под этот объекг.
182 глава 4
типы и ссылки
Часзцо
Задаваем ы е
Б оИ роС Ь !
J > Я до сих пор не очень понимаю, П : я не понимаю, почему переменные Напомните мне еще раз, какую
как работают ссыпки. ^зн ы х типов имеют разный размер. ( )ункцию выполняет ключевое слово
Зачем это нужно? th is?
Q ; Ссылки — это способ доступа к мето
дам и полям. Создав ссылку на объекг Dog, ^ ! Переменные определяют размер Q ; t h i s — это специальная перемен
вы получаете возможность пользоваться присваиваемого значения. Если вы объя ная, используемая только внутри объекта.
любыми методами, определенными для это вите переменную типа l o n g и присвоите Она ссылается на поля и методы выбран
го объекта. Для нестатических методов D o g . ей значение (например, 5), CLR все равно ного экземпляра. Это полезно при работе
B a r k () и D o g . B e g () можно определить выделит памяти на большое значение. с классом, методы которого обращаются
ссылку s p o t . После чего для доступа к В конце концов, на то они и переменные, к другим классам. Объект может передать
ним достаточно написать s p o t . B a r k () чтобы все время меняться. ссылку на себя другому объекту. Если
и s p o t . B e g (). Ссылки позволяют S p o t вызывает один из методов R o v e r
редакгировать поля объекта. Например, для CLR предполагает, что вы выбираете при помощи параметра t h i s , объект
внесения изменений в поле B r e e d доста тип осознанно и не будете объявлять R o v e r получает ссылку на объект S p o t .
точно написать s p o t . B r e e d . переменную ненужного типа. Сейчас
Невозможно присвоить значение большего типа пе Ссылки подобны наклейкам: вы можете иметь мно
ременной, принадлежащей к меньшему типу. жество ссылок на один и тот же объект.
При работе с константами используйте суффикс F Если на объект нет ни одной ссылки, он отправляется
для обозначения типа float (15.6F), а суффикс М — в мусор, и место, занимаемое им в памяти, освобож
для обозначения типа decimal (36.12М). дается.
дальше ► 183
возьми в руку карандаш
Перед вами массив объектов E l e p h a n t и цикл для поиска слона
с самыми большими ушами. Определите значение b i g g e s t E a r s .
E a r s п о с л е каждой итерации цикла f o r .
{
' 8w создаете массив
E l e p h a n t [] e le p h a n ts = n e w E l e p h a n t [7 ] из 7 ссылок ElephantQ.
e l e p h a n t s [0 ] = n e w E l e p h a n t () { Name = "L loyd " , E a rS ize =40 };
Первый индекс
e l e p h a n t s [1 ] = n e w E l e p h a n t () { Name = " L u cin d a" , E a rS ize =33 }; любого массива
e l e p h a n t s [2] = n e w E l e p h a n t () { Name = "Larry", E a rS ize =42 };
равен О,
поэтому первым
e l e p h a n t s [3 ] = n e w E l e p h a n t () { Name = " L u c ille " , E a rS ize =32 }; элементом
I Зудет
e l e p h a n t s [4] = n e w E l e p h a n t () { Name = "L ars", E a rS ize =44 }; Elephants[0].
e l e p h a n t s [5 ] = n e w E l e p h a n t () { Name = " L in d a " , E a rS ize =37 };
Итерация #1 biggestEars.EarSize =
E lep h a n t b ig g e s t E a r s = e le p h a n t s [0];
^ Итерация #2 biggestEars.EarSize =.
if (e le p h a n ts[i].E a r S iz e > b ig g e stE a r s.E a r S iz e )
{
b ig g e stE a r s = e le p h a n ts[i] ;
i^licpc
Итерация #3 biggestEars.EarSize =,
} Точкд останова в этой ст ро-
ке поможет отследить значения
clepkantsli] всех элементов массива.
M essageB ox. S h o w (b ig g e stE a r s. E a r S iz e . T o S tr in g ( ) ) ;
Итерация #4 biggestEars.EarSize =
184 глава 4
QuiBeni на с. 92*
типы и ссылки
^ а Г н и т ь ! с КоДоМ
Магниты с фрагментами кода упали с холодильника.
Верните их на место, чтобы получить код, приводящий
к появлению показанной ниже формы. refNum = index[y]; |
int refNum;
while (y < 4 ) {
MessageBox.Show(result);
□
index =
new int f4J ;
у = Y + i
istartd = Fiji
island = C o zu m el p r iv a te v o id b u tto n l C lick frihTIZZ ^ — ---------- ------ -------------
island = B erm uda I _ -Lick ( o b j e c t s e n d e r , E ven tA rgs e)
island = Azores
□
OK
-------------- ► QuiBero Ha c. 93 -
магниты с кодом и ребус в бассейне
c la ss T r ia n g le
е ^ с Б бассейне
{
Возьмите фрагменты кода из d o u b le area;
бассейна и поместите их на
пустые строчки. Каждый
in t
in t
h e ig h t;
len g th ;
I K “e
фрагмент можно исполь p u b lic sta tic v o i d M a i n ( s t r i n g [] args
зовать несколько раз. {
В бассейне есть и лишние str in g r esu lts =
фрагменты. Нужно получить
окно, в котором для каждого
треугольника (triangle) будет по
w h ile (
казана его площадь (area).
{
Результат: .h e ig h t = ( x + 1) * 2;
•le n g th = X + 4;
tria n g le 0, a re a 4
tria n g le 1, a re a = 1 0 resu lts += " t r i a n g l e " + x + ", area",
tria n g le 2, a re a = 1 8 resu lts += " = " + _______. a r e a + "\n " ;
tria n g le 3, a re a = _
y=__________ }
X = 27;
OK T r ia n g le t5 = ta [2 ];
t a [ 2 ] .area = 343;
r esu lts += "y = " + y ;
M e s sa g e B o x .S h o w (r e s u lts +
Дополнительный вопрос! ", t5 area = " + tB .a r e a );
Подсказка: М е
Вспользуйтесь фрагментами из бас тод SetAreaQ
сейна, чтобы составить код, заполняю v o id setA reaO HE является
щий даже пустые поля в нижней части статическим.
окна диалога. = (h e ig h t * len g th )
К аж д ы й ф р а гм е н т кода
м о ж н о и с п о л ь зо в а ть н е
с ко л ь ко раз!
4, t5 area = 18
area
4, t5 area = 343
ta.area int х;
27, t5 area = 18
ta.x.area int у; X = X + 1; ta j^ ^
X ta.x.area 27, t5 area = 343
y laixj.area
ta[x].area ^ int X == 0; X = X + 2; ta(x)
int X == 1; ' X = X -1;
Triangle [ ] ta = new Trlangle(4); setArea(); ta[x]
int у == x;
Triangle ta = new [ ] Triangle[4]; ta.x = setArea(); ta = new TriangleO;
28
Triangle [ ] ta = new Triangle[4]; ta[x].setArea(); ta[x] = newTriangleO
30.0
^ ta.x = new TriangleO;
О хпБ еш на С.
186 глава 4
типы и ссылки
H it th e к в у іїЗ
и К К N С
C otrect, 18 M issed-3 Total: Д A ccuracy; 85%
^ іо р м а
Вот как она должна выглядеть в конструкторе форм.
listBoxI
Correct О Total: О Accuracy: 0 %
0 timerl statusStripl
J
дальше * 187
создадим что-нибудь забавное
D ro p D o w n B u tto n
Ш SplitButton
/ / Д обави м с л у ч а й н у ю клавиш у к э л е м е н т у L i s t B o x
l i s t B o x l . I t e m s .A d d ( ( K e y s ) r a n d o m . N e x t ( 6 5 , 9 0 ) ) ;
Скоро вы добавите
i f ( l i s t B o x l . I t e m s . C o u n t > 7)
поле гап(£от. Мо
жете ли вы сейчас
{ назвать его тип?
lis tB o x l.Ite m s.C le a r 0 ;
l i s t B o x l . I t e m s . A d d ( "Игра окончена!");
t im e r l. S to p ();
}
188 глава 4
типы и ссылки
c la ss S ta ts
{
p u b lic in t T o t a l = 0;
p u b lic in t M is s e d = 0;
p u b lic in t C o r r e c t = 0;
p u b lic in t A c c u r a c y = 0;
if ( ! correctK ey)
{
M isse d + + ; При каждом вызове метода
} UpdateQ вычисляется про
e ls e цент правильных попаданий,
{ а результ ат эт их вычислений
C o r r e c t ++; помеицается в поле Accuracy.
}
A c c u r a c y = 100 * C orrect / (M isse d + C orrect) ;
}
}
Поля для отслеживания объектов S tats и Random
Вам потребуется экземпляр класса Stats для хранения информации, поэтому добавьте поле
stats. Как вы уже видели, поле random, содержащее объект Random, пока отсутствует.
дальше ► 189
ключ к прекрасной игре
Запуск игры.
В окно ListBox должно помещаться ровно 7 букв, поэтому может потребоваться поменять
размер шрифта. Для изменения уровня сложности отредактируйте значения вычитаемые из
параметра t i m e r l . I n t e r v a l в методе F o r m l _ K e y D o w n ( ) .
190 глава 4 ★ -
типы и ссылки
while Цикл while выполняется до тех пор, пока условие имеет значение
true.
{ П ом ните, 41^0
E l e p h a n t [] e le p h a n ts = new E l e p h a n t [ 7 ]; цикл НйИиНйбИЛСЯ
со второго эле
e l e p h a n t s [0] = n e w E l e p h a n t () { Name = " L l o y d " , E a rS ize =40 }; мента т с с м а .
К ак 6(?| Эуллаете,
e l e p h a n t s [1 ] = n e w E l e p h a n t () { Name = " L u c i n d a " , E a rS ize =33 }; почему?
e l e p h a n t s [2] = n e w E l e p h a n t () { Name = " L a r r y " , E a rS ize =42 };
Итерация #1 biggestEars.EarSize = 4 0
E lep h a n t b ig g e s t E a r s = e le p h a n t s [0];
{
Итерация #2 biggestEars.EarSize = 4 2
if ( e l e p h a n t s [ i ] . E a rS ize > b ig g e stE a r s.E a r S iz e )
Ссылка
{
b ig g e stE a r s = e le p h a n ts [i] ;
t biggestEars
отслеживает
самый боль - Итерация #3 biggestEars.EarSize =
42,
иОЛОШ^, .. - .......
/ Итерация #5 biggestEars.EarSize =
192 глава 4
типы и ссылки
f ^ c b задаются
^лчальные
значения
массива fndexli
Здесь задаются
начальные значения
массива 1$1апи51].
Итоговая строка
составляется
in t refN u m ; | при помощи
оператора +=■
w h i l e ^ (y < 4) { P
while
^^^Менилы
retN um = in d ex [y ];
^^ссива indexn
r esu lt += " \n isla n d = 1
r esu lt += i s l a n d s [ r e f N u m ] ; J
island = Fiji
island = C o zu m e l
island = B erm u da
island = Azores
еШ ен и е ]= * е^ са Б б а с с е й н е
triangle 1, area = 10 }
triangte 2, area = 18 i n t у ~ x;
V = 4^ ts a r e a = 3 4 5 T r ia n g le t5 = ta[2];
t a [2 ] . a r e a = 343;
resu lts += "y = " + y ;
M e s sa g e B o x .S h o w (r e su lts +
OK ", t5 area = " + tS .a r e a );
}
v o id setA reaO
{
area = (h eig h t * len g th ) / 2;
Метод setAreaQ использу
ет поля height и length для
вычисления значения поля }
area. Так как мет од не
является стат ическим, он
может вызываться только
для экземпляра Triangle.
194 глава 4
5 инкапсуляция
5юЭжет.
196 глава 5
инкапсуляция
В некоторых случаях
меняется не только
стоимость на одного
Для наглядности представим эти правила в виде гостя, но и общая цена
диаграммы: мероприятия.
$5
на человека-«^ $15 на че
5% скидки ловека
с общей +ВЗНОС
суммы $50
Кол ичество О собо е
гостей «Здоровы й» чштшшлштт
ч оф о р м л е
Еда ($2Ь вариант? ние?
на человека)
Нет ч
$20 с ч ел о $7.50 на ^
века
человека 1
+взнос$30 1
г
Выбор этого варианта
не только влияет на ст о
имость за одного человека,
но и включает единовре
менный взнос.
{
C o stO fD eco ra tio n s = (N u m b erO fP eo p le * 1 5 .00M ) + SOM;
} e ls e {
C o stO fD eco ra tio n s = (N u m b erO fP eop le * 7.50M ) + 30M;
}
}
p u b lic d e c im a l C a lc u la te C o s t(b o o l h ea lth y O p tio n ) {
d e c im a l to ta lC o st = C o stO fD eco ra tio n s +
( (C o stO fB e v er a g e sP er P e r so n + C ostO fF oodP erP erson )
N u m b erO fP eo p le)
^ М ы и сп о л ьзо ва л и скобки , ч т обы г а -
ран т и роват ь прави льн ост ь м а т е
if (h ea lth y O p tio n ) {
м а т и ч е с к и х вы чи слен и й .
retu rn to ta lC o st .95М ;
} e ls e {
retu rn to ta lC o st;
} S % -я ск и дк а п р и у с л о в и и ,
ч т о обед п о д а ет ся без а л -
к о го л я .
200 глава 5
инкапсуляция
ш P arty P la n n e r
Изменив парамет р
Number rf People Number of People с l o
12 на IZ j в итоге п о лу
Fancy DecoraJtons
чаем $б&5. Сумма
кажется ей слишком
H He^hyC^ton маленькой...
Coat !№65:00
202 глава 5
инкапсуляция
МшЛег«# Peofrfe
Снятие флажка
Рапсу Decorations Ратсу OecOTthjns
s r s a r
^пакого не может!
Healthy Option
Coat | ii«6b.00
Itete-rfPes^jte
12
g ] Fancy DecotaHws
не Эол>кно!
H HeafchyOptiOTi
ШТУРМ
Как вы думаете, почему каждое изменение условий давало ошибку в результате?
дальше У 203
неожиданно оказалось, что...
«в Party Planner
5 человек
Nwnbe-of Peof^e
$20 с человека за напитки С тоим ость напитков = $100
$25 с человека за ед у -— С тоим ость еды = $125 Farwy O ecw afior»
10 человек
Щ Party Planner
$20 с человека за напитки С тоим ость напитков = $200
Рес^
$25 с человека за ед у —- - V С тоим ость еды = $250
$15 с человека за ------------- ^ С тоим ость оф ор м л ения = $200 Н Fmc^ Оеояйюпз
оф орм л ение и взнос $50 В НеЛуйэеоп
Сол
$200 + $250+ 200 = $650
П р о б л е м а Ц оД у В е Л и Ч ш п е Л ь Н ь їМ с т е И І о М
} e ls e {
...иногда этими
К сожалению, пользователи не всегда исполь «пользова теля -
зуют классы так, как предполагал разработчик. м и » являетесь
К счастью, в C# есть функция, позволяющая га вы сами!
рантировать корректную работу программы, даже
когда пользователь делает вещи, о которых вы и
предположить не могли. Она называется инкапсу
л яцией (encapsulation).
дальше ► 205
защити свои объекты
NumberOfPeople = Ю;
CalculateCostOfDecorations(true)
NumberOfPeople - 10;
C a l c u la te C o s t O возвращ;ает$575
Метод CalculateCostO
\ возвращает значение,
но К эт лин не может
знать, что результ ат
расчетов некорректен.
206 глава 5
инкапсуляция
Статические методы им ею т
доступ к закрытым полям всех
к .« .» ,
полю достаточ-
Х" хлк>чевым словом private
ключедым
class DinnerParty { X при его объявлении. В итоге доступ к полю
^ экземпляра DinnerPartu
p r i v a t e int numberOfPeople; ^ ><^лсса.
А ругие объекты не смогут его «увидеть ».-
numberOfPeople = people;
CalculateCostOfDecorations(fancy); лребу-
'i го-
}
public int GetNumberOfPeople() { ^
return numberOfPeople;
дальше ► 207
шпионское противостояние
ContactComradesO
OverthrowCapitalistsO
e Кажется, что это вполне надежная защита, не так ли? Если
объект, вызывающий метод, не «знает» правильного пароля,
он не «узнает» настоящ ее имя агента Джонса.
"Д а ш М а р т и н "
208 глава 5
инкапсуляция
Модификатор
public означает, piiblic st ri ng RealName;
что переменная
доступна для
редактирования
извне класса.
А поле-то осталось
в общем доступе...
Так зачем мне тогда
пароль? Я могу узнать
имя и без него! name = ciaA g e n t . RealName;
s tr in g
ябляется поле, о к р
хранится пароль.
f
Их может видеть
только другой "Херб Джонс
CiaAgent.
_ Чаапо
^аД аБ аеМ ы е
БоГ^осы
из нескольких дюжин чисел, которые гарантируют,
Что произойдет, еспи класс с закрытыми по что метод Next () всегда даст на выходе случай Е^цинствен-
лями не даст мне доступа к данным, в то время ное число. Создав новый экземпляр Random, вы
как мне это нужно? не сможете увидеть этот массив. Да это и не нужно. ным способом
Имея доступ, вы могли бы добавлять значения, что
^ ! В этом случае вы не сможете получить доступ привело бы к генерации неслучайных чисел. Поэто получения ин
извне. При конструировании классов нужно гаранти му начальные числа должны быть полностью инкап
ровать доступ для других объектов. Закрытые поля сулированы. формации из
являются важной частью инкапсуляции, но нужно
оставлять удобный способ доступа к данным на слу
чай, если вдруг возникнет такая необходимость.
^ 5 А почему все обработчики событий объяв закрытых по
ляются со словом private?
лей являются
А зачем запрещать доступ к полям объектам
: Формы в C# сконструированы таким образом,
из другого класса?
что запуск обработчиков событий может осущест методы общего
вляться только при помощи элементов формы. Мо
I Иногда классу приходится отслеживать инфордификатор p r i v a t e означает, что метод может ис
мацию, необходимую для выполнения каких-то опе пользоваться только внутри класса. По умолчанию
доступа, кото
раций, но при этом другим объектам эта информа обработчики событий не могут управляться посто
ция не нужна. Скажем, при генерации случайных ронними формами или объектами. Но нет правила,
рые возвраща
чисел применяются так называемые начальные это предписывающего. Вы можете щелкнуть два
числа (seeds). О том, как именно происходит ге раза на кнопке и указать в объявлении обработчика ют данные.
нерация, мы говорить не будем, достаточно знать, событий модификатор p u b l i c . И код все равно бу
что каждый экземпляр R andom содержит массив дет компилироваться и запускаться.
210 глава 5
инкапсуляция
class SuperChef
{
public string cookieRecipe;
private string secretlngredient;
private const int loyalCustomerOrderAmount = 60;
public int Temperature;
private string ingredientSupplier;
else
return cookieRecipe;
4. mySuperChef.secretlngredient = "кардамон";
7. Какое значение будет иметь переменная recipe после запуска всех этих строк кода?
retu rn c o o k ie R e c ip e + + se c r e tln g r e d ie n t;
e lse
retu rn c o o k ieR ec ip e;
Единственным способом получения
секретного компонента ябляется
заказ всех ингредиентов. Виешнии
код не им еет непосредственного
доступа к эт ому польо.
Попытка присвоить
■‘^ ^Р^менную типа
C V ^ rin g ovenTemp = m ySu perC hef . T e m p e r a tu r e ;
переменной
типа string
^ ^ ^ T itr in g s u p p l i e r = m ySuperC hef . i n ^ e d i e n t S u ^ T T e r 7
Строки 2. м 4 не будут ко м
3. i n t lo y a lC u sto m e r O r d e rA m o u n t = 54;
V пилироваться из-за закры-
/. Какое значение будет иметь переменная r e c i p e после запуска всех этих строк кода^Ту
212 глава 5
инкапсуляция
ШTVP»Л
Как плохо инкапсулированные классы могут помешать
редактированию программы в будущем?
StartPoint
EndPoint
Length
GetRouteLengtliO
GetStartPointO
GetEndPointQ
SetStartPointO
SetEndPointO
ChangeStartPointO
ChangeEndPointQ
Есть много потенциально правильных способов решения этой задачи!
Запиш ите лучшие.
214 глава 5
инкапсуляция
ЧУ
МйЫК цспешно ислоле?зобйЛ
Правильно н Х г ^ т о р и т еп ер . он хочет
«лЙидо^7Н0 исполмоба!!!^
инкапсулировав объект Route.
классы сегодня, V
завтра.
Майк хочет воспринимать
объект Route как «черный
ящмк». Просто указывать
координаты, а о от вет
s t a r t P o in t
получать длину маршрута.
И сходная точка Его не волнует, как именно
объект Route вычисляет
эт у длину.
L ength
Длина марш рута
End P o i n t
Конечная точка
дальше * 215
как лучше провести инкапсуляцию
Получается, что
инкапсулированный класс делает ровно
то же, что и неинкапсулированный.
216 глава 5
инкапсуляция
дальше ► 217
читаем, записываем, получаем результат
Пример инкапсуляции
Класс F arm er (Фермер) использует поле для хранения информации о количество коров
(numberOfCows), которое затем умножается на количество мешков с кормом, необходимое
для одной коровы:
218 глава 5
инкапсуляция
in t K u ^ e .0 .c o „ s
r e t u r n numberOfCows;
} записи
n u m b e r O f C o w s = value;
B a g s O f F e e d = n u m b e r O f C o w s * Fe edMultiplier;
Методы чтения и записи используют так же, как поля. Вот код В этой строчке м е
для кнопки, которая задает количество коров, а в ответ получает тод записи задает
количество мешков с кормом: значение закрытого
p r iv a te v o id b u tto n l_ C lic k (o b je c t sen d er, E ven tA rgs e) { поля numberOfCows
и т ем самым обнов
F arm er m yFarm er = new F a r m e r ( ) ; ляет открытое поле
m yF arm er.N u m b erO fC ow s = 1 0 ;
BagsOfFeed.
p r i v a t e i n t nu m berO fC ow s;
p i i b l i c i n t N um berO fC ow s {
(добавьте методы чтения и записи с предыдущей страницы)
О Создайте форму:
% [ м е т о д Console.WriteLineO
^ от правляет строчки
WriteLine () замещает
значением первого парамет ра, а {1} -
с текст ом в окно Output. значением второго параметра.
220 глава 5
инкапсуляция
Автоматические сВойстВа
Кажется, наш счетчик коров работает корректно. Запустите программу и щелкните на кнопке для про
верки. Сделайте количество коров равным 30 и снова щелкните на кнопке. Повторите эту операцию
для 5 коров, а потом для 20 коров. Вот что должно появиться в окне Output:
Запустите программу. Все работает до нажатия новой кнопки. Попробуйте после этого нажать кнопку
Calculate. Окажется, что 5 мешков корма требуется для любого количества коров! После редактирова
ния параметра N u m e r i c U p D o w n кнопка Calculate снова начнет работать корректно.
о Снова нажмите tab, чтобы выделить поле M y P r o p e r t y . Введите имя B agsO fF eed :
дальше ► 221
настройки
J
Так как вместо константы общего доступа у наст кры т ое поле
типа intj его имя теперь начинается со строчной буквы г.
0 Запуск кода после внесения в него изменений покажет абсурдный результат. Свойство
B agsO fFeed всегда возвращает Омешков.
Дело в том, что переменная F e e d M u lt ip l ie r не была инициализирована. Поэтому она по
умолчанию имеет значение ноль. Добавим инициализатор объекта:
p u b lic F o rm lО {
I n itia liz e C o m p o n e n t();
f a r m e r = n e w F a r m e r {) { N u m b e r O fC o w s = 1 5 , fe e d M u ltip lie r =30 };
Error List ’ OX
0 1 Error -li 0 Warnings {! i) 0 Messages
222 глава 5
инкапсуляция
Конструктор
Итак, вы уже убедились, что с закрытыми полями инциализатор объектов не Чтобы снабдить
работает. К счастью, существует особый метод, называемый конструктором класс конструкто
(constructor). Это самый первый метод, который выполняется при созда ром, добавьте метод,
нии класса оператором new. Передавая конструктору параметры, вы указыва
ете значения, которые требуется инициализировать. Но этот метод не имеет
имеющий имя класса
возвращаемого значения, так как напрямую не вызывается. Параметр пере и не имеющий воз
дается оператору new. А как вы уже знаете, этот оператор возвращает объект, вращаемого значения.
поэтому конструктору возвращать уже ничего не нужно.
KoHCHlJ>ljKlIioJ>bi
зіоД м и к р о с к о п о м Внимательно рассмотрим конструктор F a r m e r , чтобы составить
представление о том, как он работает.
Ч асзцо
ЧаДаБаеМые
Б о Іїр о С ь і
Метод I n i t i a l i z e C o m p o n e n t ( ) вызывается внутри
• Бывают ли конструкторы без параметров? конструктора формы, поэтому инициализация всех элементов
управления происходит в момент создания формы. (Помните, что
Q ; Да. Классы часто снабжены конструктором, не имеющим все формы — не более чем объекты, использующие методы .NET
параметров. И вы уже видели пример — конструктор вашей Frameworl< из пространства имен S y s t e m . W i n d o w s . F o r m s
формы. Посмотрите на его объявление: для отображения окон, кнопок и другох элементов управления.)
p u b lic F o rm lО {
In itia liz e C o m p o n e n t О ;
}
Как разл ич ать од нои м енн ы е
Как видите, конструктор формы действительно не имеет па парам етры и поля?
раметров. Откройте файл F o r m l . D e s i g n e r . c s и найдите qae
метод I n i t i a l i z e C o m p o n e n t (), щелкнув на значке + о О р_ о рJ о Ж Н ь ї ! Вы заметили, что параметр
рядом со строчкой «Windows Form Designer generated code».
конструктора f e e d M u l t i p l i e r называется
так же, как вспомогательное поле свойства
Этот метод задает не только начальные значения всех элемен
F e e d M u l t i p l i e r ? Чтобы использовать в кон
тов управления формы, но и их свойства. Перетащите на фор
структоре последнее, не забудьте про ключевое
му новый элемент управления, отредактируйте его свойства слово t h i s ; имя f e e d M u l t i p l i e r указывает на
в окне Properties и обратите внимание на изменения, которые параметр, а запись t h i s . f e e d M u l t i p l i e r на до
произойдут с методом I n i t i a l i z e C o m p o n e n t ( ) . ступ к закрытому полю.
224 глава 5
инкапсуляция
_ Часзцо
ЧаДаБаеМые
BoTij>oc:i>i
Ql
Зачем нужна процедура создания
методов чтения и записи? Почему
нельзя просто создать поле?
p u b lic
в которое другие шпионы
могут только записывать информацию, но
не читать ее:
c la ss С аЫ еВ Ш {
p r iv a te in t r e n ta lF e e ;
p u b lic C a b le B ill( in t r e n ta lF e e ) {
t h i s . r e n ta lF e e ^ r e n ta lF e e ;
d isco u n t = f a ls e ;
}
p r i v a t e i n t p a y P e rV ie w D isc o u n t;
p r iv a te b ool d isco u n t;
p u b lic b o o l D isco u n t {
set {
d isc o u n t = v a lu e ;
i f (d isc o u n t)
p a y P e r V i e w D i s c o u n t = 2;
e ls e
p a y P e r V i e wD i s c o u n t = 0;
}
}
p u b lic in t C a lc u la t e A m o u n t ( in t p a y P erV iew M o v iesO r d ered ) {
retu rn ( r e n t a l F e e - p a y P e r V ie w D is c o u n t) * p ay P erV ie\» M o v iesO rd ered ;
}
Значение
переменной
amountOwed?
1. C a b l e B i l l j a n u a r y = new C a b l e B i l l ( 4 ) ;
M e s sa g e B o x .S h o w (ja n u a r y .C a lc u la te A m o u n t( 7 ) . T o S t r i n g O );
2. C a b l e B i l l f e b r u a r y = new C a b l e B i l l ( 7 ) ; Значение
f e b r u a r y .p a y P e r V ie w D is c o u n t = 1; переменной
M e ssa g e B o x .S h o w (fe b r u a r y .C a lc u la te A m o u n t( 3 ) . T o S t r i n g O ) ; amountOwed?
3. C a b l e B i l l m arch = new C a b l e B i l l ( 9 ) ;
m a r c h .D isc o u n t = tr u e ;
M e ssa g e B o x .S h o w (m a r c h .C a lc u la te A m o u n t( 6 ) . T o S t r i n g O ); Значение
переменной
amountOwed?
226 глава 5
инкапсуляция
Часзцо
'^ а д а В а е М ы е в C# регистр имеет значение. Внутри 4. В некоторых методах, особенно это ка
Б оП роС Ь ! одного метода можно иметь две пере сается конструкторов, имена параметров
менные с именами P a r t y и p a r t y . совпадают с именами полей. В итоге пара
Почему имена одних полей начи Это не помешает компиляции кода. Вот метр маскирует поле, то есть операторы
наются с прописной буквы, а других — несколько советов по выбору имен для методов, которые используют это имя,
со строчной? Это что-то означает? переменных, которые упростят чтение ссылаются на параметр, а не на поле. Эта
программы в будущем. проблема решается при помощи ключе
0 ; Да, означает. Для вас. Но не для ком вого слова th is, достаточно добавить
пилятора. С# все равно, какие имена вы вы 1. Имена полей закрытого доступа должны его к имени, и компилятор поймет, что вы
бираете для переменных. Выбор странных начинаться со строчной буквы. имеет в виду поле, а не параметр.
имен затруднит чтение кода в будущем. Вы 2. Имена свойств и полей общего доступа
можете запутаться в одноименных перемен должны начинаться с прописной буквы.
ных, все отличие имен которых заключается 3. Имена параметров методов должны
в регистре первой буквы. начинаться со строчной буквы.
c la ss G u m b allM ach in e {
p r iv a te in t g u m b a lls;
p r iv a te in t p r ice ;
p u b lic in t P rice
■{
get
{
retu rn p r ic e ;
g u m b a lls = t h i s . g u m b a lls;
^ ^ о з ь м и В руку карандаш
Вот какие значения должна иметь переменная a m o u n t O w e d
'ешение после выполнения кода:
Значение
переменной
1 . C a b l e B i l l j a n u a r y = new C a b l e B i l l ( 4 ) ;
amountOwed?
M e s s a g e B o x . S h o w {j a n u a r y . C a l c u l a t e A m o u n t ( 7 ) . T o S t r i n g () )
Z8
2. C a b l e B i l l f e b r u a r y = new C a b l e B i l l { 7 ) ;
f e b r u a r y . p a y P e r V i e w D i s c o u n t = li- Значение
M e s s a g e B o x .S h o w C fe b r u a r y - C a lc u la t e A m o u n t (3) . T o S t r i n g O переменной
amountOwed?
3. C a b l e B i l l m a r c h = n e w C a b l e B i l l (9);
m a r c h .D isc o u n t = tr u e ; не компилируется
M e ssa g e B o x .S h o w (m a r c h .C a lc u la te A m o u n t( 6 ) . T o S t r i n g O );
Значение
переменной
amountOwed?
42.
228 глава 5
инкапсуляция
NumberOfPeople 10;
.CalculateCostOfDecorations()
★ Вот как выглядит конструктор для формы. Все остальное остается без изменений:
p u b l i c F o rm lО {
In itia liz e C o m p o n e n tO ;
d i n n e r P a r t y = new D i n n e r P a r t y ( ( i n t ) n u m e r i c U p D o w n l . V a l u e ,
h e a lth y B o x .C h e c k e d , fa n c y B o x .C h e ck ed );
D isp la y D in n e r P a r ty C o st0 ;
}
Итак, вот как нужно было написать программу для расчета стоимости
мероприятий:
i
set
n u m b ero fP eo p le = v a lu e ;
C a lc u la te C o s tO fD e c o r a t i o n s (fa n c y D e c o r a t i o n s )
} При помои^и свойства вы га -
Р ^ ‘^ирует е, что стоимость
p riv a te bool fa n c y D e co r a tio n s; оформления пересчитывается
при каждом изменении коли
p u b lic d e c im a l C o stO fB e v er a g e sP er P e r so n ; чества гостей.
p u b lic d e c im a l C o s tO fD e c o r a tio n s = 0;
p u b l i c D i n n e r P a r t y {i n t n u m b e r O fP e o p le , b o o l h e a l t h y O p t i o n , b o o l f a n c y D e c o r a t i o n s )
N u m b erO fP eo p le = n u m b e r O fP e o p le ;
t h i s . fa n c y D e co r a tio n s = f ancyD ecorat io n s; ^ будьте внимательны c КЛЮ-
S etH ea lth y O p tio n (h ea lth y O p tio n ); О чевым " словом this. И м ен
C a lc u la teC o stO fD ec o r a tio n s(fa n c y D e co r a tio n s); но оно поможет различить
} парамет р и закрытое поле
numberOrPeople.
p u b lic v o id S e tH ea lth y O p tio n (b o o l h ea lth y O p tio n )
i f (h ea lth y O p tio n ) {
C o s t O f B e v e r a g e s P e r P e r s o n = 5 . 0 0 M;
{
Перед fancyDecorations
\
} e lse { нужно помест ит ь кл ю
C o s tO fB e v e r a g e s P e r P e r s o n = 20. 00M;
чевое слово tkis.j чтобы
} отличить название з а
} крытого поля от назва
p u b lic v o id C a lc u la teC o stO fD ec o r a tio n s(b o o l fancy) {
ния параметра.
fa n c y D e co r a tio n s = fa n cy ; _____________
_________ Информация об осо
i f (fan cy) { ^
бом оформлении
C o s t O f D e c o r a t i o n s = (N u m b erO fP eo p le 1 5 . 0 0 М ) + 50М;
должна храниться
} e lse {
C o s t O f D e c o r a t i o n s = (N u m b erO fP eo p le 7 . 5 0 М) + ЗОМ;
в поле, чтобы ею
смог воспользовать
} ся метод записи
}
NumberOfPeople.
p u b lic d e c im a l C a lc u la t e C o s t ( b o o l h e a lth y O p tio n ) {
d e c im a l t o t a lC o s t = C o s tO fD e c o r a tio n s
+ ( (C o stO fB e v er a g e sP er P e r so n + C ostO fF ood P erP erson ) * N u m b e rO fP eo p le );
if (h ea lth y O p tio n ) {
r e t u r n t o t a l C o s t ■ . 9 5 M;
} e lse {
retu rn to ta lC o st;
}
230 глава 5
6 н а с Л е Д ‘^ ® а н и е
Генеалогическое древо
объектов
Входя в крутой поворот, я вдруг
понял, что унаследовал свой
велосипед от Двухколесного,
но забыл добавить метод Тормоза()...
В итоге двадцать шесть и
и лишение прогулок на целый
месяц.
------
Ваша программа сможет
посчитать стоимость дня
рождения?
Эт и пункты
остались без
изменений.
232 глава 6
наследование
_ Чаап°
<аДаБаеМые —
Б о їїр о с ь і
>Почему нельзя просто создать экземпляр DinnerParty, что же мне поместить в этот новый класс?
как это делал Майк, когда ему потребовалось сравнить три
маршрута?
О Перед тем как приступить к созданию класса, нужно понять,
какую проблему он будет решать. В данном случае вы должны
^ ! Потому что новый экземпляр класса D i n n e r P a r t y годится поговорить с Кэтлин, ведь это она будет пользоваться програм
только для расчета стоимости званых обедов. Два экземпляра одно мой. Впрочем, у вас есть ее заметки! Определить поля, методы
го класса полезны, когда требуется работать с данными одного типа. и свойства класса можно, продумав его поведение (что он дол
Но для хранения других данных, вам потребуется другой класс. жен делать) и его состояние (что он должен знать).
th is.n u m b e r O fP e o p le = n u m b e rO fP eo p le ;
Для подсчета стоимости
надписи на торт е конст рук
t h i s . fa n c y D e co r a tio n s = fa n c y D e co r a tio n s; тор вызывает метод записи.
C a lc u la teC a k e S ize (); Чтобы понять, не является ли
t h i s . C a k eW r itin g = c a k e W r itin g ; эт от парамет р слиилком
большим, сначала нужно у з
C a lc u la teC o stO fD ec o r a tio n s(fa n c y D e co r a tio n s);
нать размер торта.
234 глава 6
★ Для хранения информации о надписи на торте потребуется свой наследование
ство C a k e W r i t i n g типа s t r i n g . Этот метод записи проверяет пара
метр C a k e S i z e , так как максимальное количество букв зависит от
размера торта. Затем при помощи метода v a l u e . L e n g t h проверяет
ся длина строки. Если строка оказывается слишком длинной, метод
записи вызывает окно с текстом, «Слитком много букв для 16-дюй- Эт о более сложное свой
ство. Оно проверяет,
мового торта» (или 8-дюймового).
помест ится ли на т о р
★ Кроме того, вам потребуется метод C a l c u l a t e C a k e S i z e (): те строка. Информация
о максимально воз
можной длине хранится
p riv a te v o id C a l c u l a t e C a k e S i z e () { в переменной maxLengtk.
if ((Numbc^^w.^,.
N u m b e r O f P e o p l e <= 4 ) Если строка слишком
r’o’ Memos O? длинная, появляется со -
C abk-caC
e S-ii zryea
e =— 8Q;. W
задает »^оле записи обш,ение об ошибке, а п о
e l s ee т ом вспомогательное
C a k eS iz e = 16; поле обрезается до нуж
Ca\cu\ckteCo$tQj- ного размера.
}
После
дальше ► 235
кэтлин это понравится
p u b lic d e c im a l C a lc u la te C o s tO {
d e c im a l T o ta lC o s t = C o stO fD eco ra tio n s + (C ostO fF oodP erP erson * N u m b e rO fP eo p le );
d e c im a l C ak eC ost;
if (C a k eS iz e == 8)
C akeC ost = 40M + C a k e W r i t i n g . L e n g t h * .2 5 M ; 8 данном случае метод
e lse CalculateCostQ вместо
стоимости напитков
C akeC ost = 75M + C a k e W r i t i n g . L e n g t h . 2 5 М; прибавляет стоимость
r etu rn T o ta lC o st + C ak eC ost; торта.
}
p r iv a te in t n u m b e rO fP eo p le ;
p u b lic i n t N u m b erO fP eo p le { Метод Саке^гШ пд должен не
только обрезать строку, но
get { r e t u r n n u m b e rO fP eo p le ; } ^ и запускать метод записи п^и
set { изменении количества гостей.
\
n u m b e rO fP eo p le = v a lu e;
C a lc u la teC o stO fD e c o r a tio n s(fa n c y D e c o r a tio n s);
C a lc u la teC a k e S ize ();
При изменении коли
th is-C a k e W ritin g - c a k e W r itin g ; чества гостей класс
^ Подобный метод вы уже сначала пересчитывает
} использовали в классе размер т орт а и, ис
DinnerParty. ^ пользуя метод запи-
си, заставляет метод
p u b lic v o id C a lcu la teC o stO fD ec o r a tio n s(b o o l fancy) { CakeWriting обрезать
fa n c y D e co r a tio n s = fancy; текст.
if (fan cy)
C o stO fD eco ra tio n s = (N u m b e rO fP e o p le * 1 5 . 00M) + SOM;
e lse
C o stO fD eco ra tio n s = ( N u m b e r O f P e o p l e * 7 . SOM) + 30M;
}
%
236 глава 6
наследование
Перейдите на вкладку
■i* Party Planner 2.0 Birthday Party и до
anner Party Birthday Party. бавьте новые элем ен
ты управления.
ЭИ.
Number cf People
iv Добавьте элемент управ
ления TextBox с именем
4»|2! Fm cy Decorations cai<eWrittng для ввода надпи
си на торте. Свойству Text
Cake W i№ g присвойте значение Нарри
Happy Birthday Birthdau. Эта надпись бу
дет выводиться по ум олча
$
J нию.
дальше ► 237
закончим создание формы
b i r t h d a y P a r t y = new B i r t h d a y P a r t y ( ( i n t ) n u m b e r B i r t h d a y .V a l u e ,
fa n c y B irth d a y .C h e ck ed , c a k e W r itin g .T e x t);
D isp la y B ir th d a y P a r ty C o st();
}
238 глава 6
наследование
] тельно.
О ТТрогроААма готова!
Убедитесь, что программа работает корректно. Если надпись на тор
те слишком длинная, должно появляться окно с сообщением. Про
верьте правильность расчета конечной суммы. Если все функциони
рует, значит, работа сделана!
240 глава 6
наследование
дальше ► 241
не тратьте золото, если нужен только блеск
Наследование
Классы D i n n e r P a r t y и B i r t h d a y P a r t y не случайно имеют одинаковый код. При написании программ
C# часто создаются классы, соответствующие процессам из реального мира, и эти процессы, как прави
ло, связаны друг с другом. Ваши классы имеют одинаковый код, так как процессы, взятые за их основу
(дни рождения и званые обеды), имеют одинаковые признаки.
Количество
гостей и ст ой-
DinnerParty BirthdayParty Mocmi? оф орм
NumberOfPeople ^ ►МитЬеЮГРеорІе ления для дней
CostOTOecorations ......... ►СозЮГОесогаІіопз рождения обраба
HealthyOption CakeSize
CostOffieveragesPerPerson
тываются п о ч -
К э т л и н в обоих CakeWriting •VI« т ак же, как
случаях нужно Ч CalcuiateCostOTOecorationsO ^ ►CalculateCostOTOecorationsO званые обеды.
рассчитать ( _CalculateCost() ^ ►CalculateCostO
стоимость SetHealthyOptionO
мероприятия
Оба производных
С класса наслеЭу-
Стрелка в диа ю т процедуру
грамме указы DinnerParty расчета ст ои BirthdayParty
вает, что класс NumberOfPeople мости оф орм NumberOfPeople
DinnerParty HealthyOption ления от ба CakeSize
унаследован CostOffieveragesPerPerson зового класса, CakeWriting
от класса Party. поэт
,, ому
0О них /?она
не вклю
CalculateCostQ CalculateCostQ 6z-----------------
SetHealthyOptionO чена.
242 глава 6
наследование
О бщ ее О бщ ее
8 модели классов Сыр Лю5ая
А м о ж е т наследовать птица —■
животное,
от Ежедневного
продукта, который, но не любое ^
в свою очередь, животное — ( Ж и в отно е
наследует от класса Еда. птица.
Симулятор зоопарка
Львы, тигры и медведи... о, боже! А еще бегемоты, волки и случай
но затесавшаяся кошка. Вам нужно написать симулятор зоопарка.
(Не бойтесь, сам код писать не потребуется, на данном этапе доста
точно создать диаграмму классов, представляющую всех животных).
Мы получили небольшой список животных, которые должны по
пасть в программу. Каждому животному будет соответствовать объ
ект со специфическими свойствами.
Программа должна легко читаться и редактироваться другими про
граммистами, если позднее потребуется добавить другие классы или
других животных.
с чего же начать? Перед обсуждением отдельны х животных нужно
понять, что они имеют общ его —выделить характеристики, подхо
дящие всем видам. Именно на их основе будет построен класс, от ко
торого будут наследовать другие классы.
244 глава 6
наследование
Г
Производный класс на ( Сено — это вкусно!
следуем от базового
все его поведения, но вы ^ Я бы съел стог-
можете их отредак V. другой прямо сейчас.
тировать. Сено?! Нет, мне
хочется мяса!
О
A n im al
Picture
Food
Hunger
Boundaries
Location W TVPM
Mal^eNoiseO
Для некоторых животных требуется перекрыть методы
Eat()
MakeNoise () и Eat (). А для кого нужно перекрыть метод
SleepO
Sleep О или R o a m O ? И нужно ли это делать вообще? Подумаі^-
Roam()
те также, для каких животных будут перекрываться свойства.
246 глава 6
наследование
О м класса
Anim al наследу
ются все чет ы
ре метода, но
перекрываются
только м ет о
ды MakeNoiseO
и EatQ.
Вот почему Нй
диаграмме классов
показаны только
эти два метода.
дальше * 247
расширяем объекты
иерархия классов
Конструкция, в которой под базовым классом располагаются про Animal
изводные классы, которые, в свою очередь, являются базовыми Picture
для других классов, называется иерархией (class hierarchy). По Food
добный подход не только позволяет избежать многократного ду H unger
блирования кода, но и делает код намного более читабельным. На Boundaries
пример, при просмотре кода симулятора зоопарка, наткнувшись Location
на метод или свойство из класса Feline, вы сразу поймете, что они
имеют отношение к кошкам. Иерархия становится картой, позво IVIakeNoiseO
ляющей отследить происходящее в программе. Eat()
SleepO
Roam ()
Завершение построения иерархии
Вам осталось добавить классы Feline и Canine,
и иерархия будет готова.
s c e KOIMKU двигаются
Wolf MakeNoiseO
поэт а!
мет од RoamO дляны^
общий. Но они п Т MakeNoiseO Волки и соба
Eat() ки п и т а ю т
разному пит аю т ся < MakeNoiseO MakeNoiseO
издают разные s S y i ^ Eat() ся одинаково,
^оэт ом у методы ^ ^ поэт ому ик "
и MakeNoiseO общий метод
Унаследованные ' EatQ помещен
от класса Anim ai в класс Canine
^^Рекрываются.
248 глава 6
наследование
Canine
вызываем м ем од класса Canine s p o t.S le e p ();
дальше ^ 249
как низко вы можете паст ь?
Синтаксис наследобания
При наследовании имена производного и базового классов разделя
ются двоеточием (:). Производный класс получает все поля, свойства
При наследо
и м етоды базового класса.
вании все поля,
Vertebrate
NumberOlegs
c la ss V erteb rate свойства и ме
{
p u b lic in t N u m berO fL egs; тоды базового
Eat()
p u b lic v o id E atO
// код, заставляющий есть
{
класса автома
}
} тически добавля
Класс Bird (Птица) наследует от ются в производ
класса v e rte b ra te (Позвоношое).
Bird c la s s B ir d : '^ T e r t e b r ^ t ^
ный класс.
Wingspan
{
p u b lic d o u b l e W in g sp a n ;
p u b lic v o id F ly O { расш иряет е класс,
добавив в конец его
FlyO // код, заставляющий летать обьявления двоеточие
} и им я базового класса.
Часто }
t w e e t y .E a t {); У зем пляры Bird имеиут поля
и мет оды , определенные
в классе Vertebrate.
^ а Д а В а е М ы е ______________________________
Бо11р»ос;ь1
Поведение базового класса не только никак не меняется, он даже
Почему стрелка указывает от производного класса к ба не знает о появлении производных классов. Методы класса, поля
зовому? Не логичней было бы рисовать ее наоборот? и свойства никак не затрагиваются. А вот производный класс свое
поведение меняет. Все его вновь создаваемые экземпляры полу
0 ; Это было бы не совсем точно. Заставляя один класс насле чают свойства, поля и методы базового класса. И все это происхо
довать от другого, вы встраиваете это отношение в производ дит благодаря единственному двоеточию! Стрелка на диаграмме
ный класс, а базовый остается без изменений. Это имеет смысл, является частью производного класса и поэтому она нацелена
если подумать о происходящем с точки зрения базового класса. на базовый класс.
250 глава 6
наследование
в руку карандаш
Посмотрите на эти модели и объявления классов и обведите
некорректные операторы.
Aircraft c la s s A ir cr a ft {
AirSpeed p u b l i c d o u b le A irS p eed ;
Altitude p u b lic d o u b le A lt it u d e ;
p u b lic v o id T akeO ffO {
p u b lic v o id LandO { . . .
Tal<eOff()
Land()
c la s s F ir eP la n e : A ir c r a ft {
p u b lic d o u b le B u ck etC a p a city ;
p u b lic v o id F illB u c k e tO { ... };
}
FirePlane p u b l i c v o i d F i r e F i g h t i n g M i s s i o n () {
Bucl(etCapacity
F i r e P l a n e m y F i r e P l a n e = n e w F i r e P l a n e ()
new F i r e P l a n e .B u c k e t C a p a c i t y = 5 0 0 ;
A i r c r a f t . A l t i t u d e = 0;
m y F ir eP la n e . T a k e O ff();
FillBucl<et()
m y F ir e P la n e .A ir S p e e d = 1 9 2 .5 ;
m y F ir eP la n e . F il l B u c k e t ();
A ir c r a f t .Land();
}
Sandwich c l a s s S a n d w ic h {
Toasted p u b lic b o o le a n T oasted ;
SlicesOfBread p u b lic i n t S lice sO fB r ea d ;
p u b lic in t C o u n tC a lo r ie sO { • }
}
CountCaloriesO
c l a s s BLT ; S a n d w i c h {
p u b lic i n t S licesO fB a co n ;
p u b l i c i n t A m ou ntO fL ettu ce;
p u b lic i n t A d d S id e O fF r ie sO { • }
}
BLT p u b l i c BLT O r d e r M y B L T O {
SlicesOfflacon
BLT m y S a n d w i c h = n e w B L T ( ) ;
AmountOfLettuce
B L T .T o a sted = t r u e ;
S a n d w i c h . S l i c e s O f B r e a d = 3;
m y S a n d w ic h .A d d S id e O fF r ie s 0 ;
AddSideOfFriesO
m y S a n d w i c h . S l i c e s O f B a c o n += 5 ;
M e s s a g e B o x . S h o w ( "B м о е м с э н д в и ч е "
+ m y S a n d w ic h .C o u n tC a lo r ie s + " к а л о р и й " .);
r e t u r n m y S a n d w ich ;
}
дальше ► 251
я знаю, как заставить летать пингвина...
Aircraft c la s s A ir c r a ft {
AirSpeed p u b lic d o u b le A irS p eed ;
Altitude p u b lic d o u b le A lt it u d e ;
p u b lic v o id T ak eO ffO { ... };
p u b lic v o id LandO { };
Tal<eOff() }
LandO
c l a s s F ir e P la n e : A ir c r a f t {
p u b lic d o u b le B u ck etC a p a city ;
p u b lic v o id F illB u c k e tO { ... };
}
FirePlane p u b lic v o id F ir e F ig h tin g M issio n 0 {
BucketCapacity
F i r e P l a n s my F i re.P l a n s = n e w F i r e P l a n e 0 ;
F ir e P la n e .B u c k e tC a p a c ity = isbO;
CAirc?afTTS]EItii3 e =
FillBuci(et()
m y F ir e P la n e T r a k e O ff( ) T Э т и оператору
m y F ir e P la n e .A ir S p e e d = 1 9 2 .5 ;
m y F ir eP la n e . F il l B u c k e t (); r r & r
C g\i!ccral:t. Land o 7 D __
имени экзеМ1^лярй
^уР1>еР1ли.е.
Sandwich c l a s s S a n d w ic h {
Toasted p u b lic b o o le a n T oasted ;
SlicesOfBread p u b lic in t S lice sO fB r ea d ;
p iib lic in t C o u n tC a lo r ie sO
} Э т и свойства п р и
СоипЮа1опе8() надлежат экзем пляру,
c l a s s BLT : S a n d w i c h { в то врем я как о п е
p u b lic i n t S licesO fB a co n ;
рат оры пы т аю т ся
вызыоать их по имени
p u b lic i n t A m ou ntO fL ettu ce;
классов.
p u b lic i n t A d d S id eO fF riesO
}
BLT
p u b l i c BLT O r d e r M y B L T O {
SlicesOffiacon
^ L T , j m^San d w i c h = n e w BLT () ;
AmountOfLettuce
B L T .T o a sted = t r u e ;
^ S a n d w ic h .S lic e sO fB r e a d = 3
r r y S a n d w i c h . A d d S i d e O ' f b'r l e s ' () скобки (f
AddSideOfFriesO
m y S a n d w i c h . S l i c e s O f B a c o n += 5 ; \
M ess" a g e B o x .S h o w (" B м о ем с э н д в и ч е
+ m y S a n d w ic h .C o u n tC a lo rie s + " к а л о р и й " .)
andw ich,---------- ----------------------------------- — J '
252 глава 6
наследование
Что делать, если базовый класс имеет метод, кото p u b lic v o id B ird S im u la to r 0 {
рый в производном классе требуется отредактиро
вать} P ig e o n H a r r ie t = new P i g e o n ( ) ;
У ЗКЗеМУ1АЯр
обьекта Penguin P en g u in I z z y = new P e n g u i n 0 ;
(пингвин). Он
H a r r ie t. F ly O ;
унаследовал м е -
FlyO, и т е H a r r ie t.C o o 0
Как класс Pigeon, так
перь ничто не и класс Penguin на
Удерживает его I z z y .F ly (); следуют у класса
от полета! Bird, так что оба
они получают м е -
Pigeon Penguin тоды Fly(), LayEggsQ
Соо() Swlm() и PreenFeathersQ.
Но f l лет ат ь!
нйслсЭует о т класса Bird,
У дедняг просто нет выбора'
Гола«“
ШТУРМ
Если бы этот код писали вы, как бы вы избавили пинг
em. вина от необходимости летать?
дальше ^ 253
перекрытие вручную
Перекрытие Алето0 о 6
Иногда нужно, чтобы производный класс унаследовал не все поведения базо
вого, а только часть их. Чтобы изменить унаследованное ненужное поведе
ние, достаточно п ерекры ть (override) метод.
}
}
о Д обавление одно им енного м етода в производны й класс
Переопределенный метод должен иметь такую же сигнатуру, то есть то же самое возвращае
мое значение и параметры. В его объявлении используется ключевое слово o v e r r i d e .
}
Используйте ключевое слово
При перекры т ии си гн а т у
ра нового м ет ода должна
override для добавления в произво
совпадать с сигнат урой
исходного м ет ода из базового дный класс методов, замещающих
класса. В случае с м ет одом
Fly эт о означает о т с у т
ст вие возвраш,аемого значе
методы унаследованные. Перекры
ния и парам ет ров.
вать можно методы, помеченные
в базовом классе словом virtual.
254 глава 6
наследование
Sandwich
Вместо базового класса мо)кно в зя ть один из производных Toasted
SlicesOfBread
BLT
Допустим, у нас имеется метод, анализирующий объекты S a n d w ic h : SlicesOfBacon
AmountOfLettuce
p u b lic v o id S a n d w ic h A n a ly z e r (S a n d w ic h sp ec im en ) {
in t c a lo r ie s = s p e c im e n .C o u n tC a lo r ie s0 ;
AddSideOfFriesO
U p d a te D ie tP la n (c a lo r ie s);
P e r fo r m B r e a d C a lc u la tio n s(sp e c im e n .S lic e sO fB r e a d , sp e c im e n .T o a s te d );
дальше ► 255
немного практики
а = б 56
съ лесь „ Ниже показана короткая программа, у которой
Ь = 5 11
отсутствует фрагмент кода! Вам нужно сопо
а = 5 65
т нт сО О ёщ гииа ставить фрагменты кода (слева) с возможными
результатами. Не все строчки, приведенные
под заголовком «Результат», должны использо
И нструкции: ваться, хотя некоторые могут использоваться
больше одного раза.
1. З апо л ни те четы ре п роб ел а в коде.
2. С о вм естите ф р агм енты и результаты .
c la ss А { c la ss С : В {
p u b li c i n t iv a r = 7; p x j b l i c ______ s t r i n g m 3() {
p u b l i c ______________ s t r i n g m l ( ) { r e t u r n " C ' s m3, " + ( i v a r + 6 ) ;
r e t u r n " A 's m l,
} ^ о ч к а входа в п р о грам м у, иона
} ^ не п о к й з ы б а е т формы, а т олько
p u b l i c s t r i n g m 2 () { вызывает окно с сообщением.
r e t u r n " A ' s m2, c l a s s M ixedS { ^
} p u b l i c s t a t i c v o i d M a i n ( s t r i n g [] a r g s ) {
p u b l i c ______________ s t r i n g m 3 () { A a = new A O ;
в Ь = new В О Подсказка: К ак
r e t u r n " A ' s m3,
с с = new С ()
следует подум айт е,
} чт о именно эт о
} А а2 = new С О ,- 4 / означает.
str in g q =
c la ss В ; A { Вставьте сюда
p u b l i c __ ___________ s t r in g m l() { t фрагмент (три
r e t u r n " B 's m l, строчки)
} Sy stem .W in d o w s. Form s. M essa g eB o x . S h o w (q );
}
}
Фрагменты q += b . m l O ; Результат:
кода:
q += c . m 2 0 ;
q += a . m3 ( ) ; } A 's m l. A ' s m2, C ' s m3. 6
B 's m l.
B ' s m2.
A ' s m2.
A ' s m3.
C 's m 3, 13
q += a . m l O ;
B 's m l. C 's m2. A ' s m3.
q += b . m 2 0 ;
q += c . m S 0 ;
} B 's m l. A ' s m2. C 's m 3, 6
256 глава 6
наследование
class TestBoats {
c l a s s R ow boat ........................................ {
public ..................... 0 {
return " ................ ";
}
public move() { )
return
}
А съ лесь „
а = 6
Ь = 5
56
11
сО О егг^еН ий а = 5 65
ажнеше c la ss А { c la ss В : А {
)^ешение p u b lic virtual s t r i n g ml О { p u b lic override s t r i n g ml 0 {
p u b lic virtual s t r i n g m 3 () { c la ss С : В {
} p u b lic override s t r i n g m 3() {
В ребусе в бассейне точка входа Можно ли наследовать от класса, 1 ); Почему я могу двигаться по диа
указывала наружу, означает ли это, содержащего точку входа? грамме классов только вверх?
что в программе отсутствует форма
Forml? ^ ! Да. Точка входа должна быть 0 2 Классы, расположенные в диаграмме
статическим методом, но этот метод сверху, являются более общими. Именно
^ ; Для нового проекта Windows может и не принадлежать к статиче от них наследуют более детализиро
Application, ИСР создает все необходимые скому классу. (Ключевое слово s t a t i c ванные классы (скажем Рубашка или
файлы, включая файл Program.cs (содер означает невозможность создания экзем Автомобиль могут наследовать от
жащий статический класс с точкой входа), пляров класса, но его методы доступны с классов Одежда или Транспорт). Если
и файл Forml. CS (содержащий пустую момента запуска программы. В ребусе в вам нужен транспорт, вам подойдет как
форму Forml). бассейне метод T e s t B o a t s . M a i n {) автомобиль, так и мотоцикл или даже
можно вызвать из любого другого метода поезд. Но если вам требуется именно
Попробуйте при создании нового проек без объявления ссылочной переменной автомобиль, вы не сможете выбирать все
та выбрать вариант Empty Project вместо или создания экземпляров при помощи транспортные средства.
Windows Application. Добавьте файл оператора new.)
с классами через окно Solution Explorer Именно так работает наследование. При
и введите код из решения ребуса в у» я не понимаю, почему эти методы наличии метода с параметром Транс
бассейне. Так как в программе должно по называют «виртуальными», они впол порт и при условии, что класс мото
являться окно диалога, необходимо доба не реальные! цикл наследует от класса Транспорт,
вить ссылку на форму. Щелкните правой вы можете передать методу экземпляр
кнопкой мыши на строчке References в Мотоцикл. Если же параметром метода
Q ; Ключевое слово virtual относится
окне Solution Explorer, выберите коман является Мотоцикл, вы не сможете
к способам обработки методов в .NET.
ду Add Reference, в открывшемся окне передать объекг Транспорт, так как это
Используется так называемая таблица
перейдите на вкладку .NET и выберите может оказаться поезд, и С# не будет
виртуальных методов, которая отслежи
строчку System.Windows.Forms. Затем вы знать, что делать, при попытках метода
вает какие методы были унаследованы,
берите команду Properties в меню Project получить доступ к свойству Руль.
а какие перекрыты.
и укажите в списке output type вариант
Windows Application.
дальше ► 259
они вам и в самом деле нужны
А/
О Создайте консольное п ри л ож ени е, выбрав вариант Console application.
Добавьте пять классов через окно Solution Explorer: J e w e ls (Драгоценности), S a fe (Сейф),
Owner (Владелец), L o c k sm ith (Слесарь) и J e w e lT h ie f (Вор).
Метод
sta tic
„
v o i d M a i n ( s t r i n g []
^
args) { возьми 6 руку карандаш
ReadKeyQ не Owner () ;
допускает Safe sa fe = new S a f e O ;
завершения
программы J e w e lT h ie f je w elT h ie f = new J e w e l T h i e f О
Внимательно посмотрите код программы
пока поль je w e lT h ie f.O p e n S a fe (s a fe , ovm er);
и запишите сообщение, которое появится
зователь
не нажмет C o n so le.R e a d K ey O ; в консоли. (Подсказка: Определите, какие
клавиилу. } свойства класс J e w e l T h i e f наследует от
} класса L o c k s m i t h ! )
% дальше k 261
скры т ь и обнаруж ит ь
Но кажется, наш вор JewelThief повторяет действия слесаря Locksmith! Как это могло произойти?
Description
Сразу же после этого сообщение об ошибке исчезнет. Хотя программа все равно не станет работать так,
как нужно! По-прежнемувызывается метод R e t u r n C o n t e n t s ( ) , определенный для объекта L o c k s m i t h .
Почему так происходит? Дело в том, что вызов этого метода осуществляется через метод, определенный
в классе Locksmith, а именно через L o c k s m i t h . O p e n S a f e (), несмотря на то, что его начальные значения
были заданы в классе J e w e l T h i e f . Если бы J e w e l T h i e f просто скрывал метод R e t u r n C o n t e n t s () из
базового класса, его собственный метод R e t u r n C o n t e n t s () никогда бы не был вызван.
П о д у м а й т е , к а к з а с т а в и т ь о б ъ е к т J e w e lT h ie f п е р е к р ы т ь , а н е с к р ы т ь м е т о д
R e tu r n C o n te n ts O ? С м о ж е т е д о г а д а т ь с я д о т о го , к а к п е р е в е р н е т е с т р а н и ц у ?
class JewelThief {
Error List П X
O 1 Error I j \ 0 Warntngs j ij) 0 Messages
, Description
.... . . , .. ..............
O 1 'ieweLThief.JewelThief.ReturnCor?tetitsPewel_Thief.JwelSi Jewel_Thief,Owner}'; cannot override irtherited member
'Jewel_Thief.Locksmith,ReturnContentsOewetThief Jewete, iewel_Thief.Owner)’ because it is not marked virtual, abstract, or override
class Locksmith {
Этого мы и добивались!
264 глава 6
наследование
© Хамелеоны ловят пищу языком. Поэтому класс C ham eleon наследует от класса V e r t e b r a t e ,
переписывая при этом метод E a t ().
c la ss C h am eleon : V erteb rate {
p u b lic o v e r r id e v o id E at(F ood m o rsel) {
C a tch W ith T o n g u e(m o rsel);
S w a llo w (m o r se l)
D ig e s t ();
}
}
266 глава 6
наследование
}
, Эт от парам ет р нужен
' базовому конструктору.
_ _ Эта ошибка
© „ ч-
Добавьте производиыи класс, но не вызывайте конструктор означает, что
Добавьте к форме кнопку, которая делает то же самое для производного класса: производный
ґ ^ с іа з з M y S u b cla ss : M y B a seC la ss{
Выберите К О - p u b l i c M y S u b c l a s s ( s t r i n g b a s e C l a s s N e e d s T h i s , i n t a n o t h e r V a l u e ) { ^ о б в
манду Build » M essa g eB o x . show (" T h is i s t h e s u b c l a s s : " + b a s e C la s s N e e d s T h is ^^/^^acca
Build Solution, + « and " + a n o th erV a lu e) ; ч
u Ш получи- ________________________ ____________________________ )
Me сообіцение a 1 No overload fo r m e th o d 'MyBaseClass' takes 'O' a rg u m e n ts ^ -------------------- -—
об ошибке. }
Party
NumberOfPeople
CostOfDecorations
CalculateCostOroecorationsO
CalculateCostO
268 глава б
о Б а з « .я й клосс P a rty
Создайте класс P a r t y и укажите уровень доступа public. Рассмотрим диаграм
му классов и решим, какие свойства и методы следует перенести из классов
D in n e r P a r ty и B i r th d a y P a r ty в класс P a r ty .
★ Переместите свойства N um berO fPeople и C o s tO fD e c o ra tio n s , так как
они определены как для D in n e r P a r ty , так и для B i r th d a y P a r ty .
★ Проделайте то же самое с методами C a lc u la te C o s tO f D e c o r a tio n s O
и C a lc u la te C o s t (). Не забудьте перенести и закрытые поля, необходи-
Вскоре вы п о- методов. (Помните, что производные классы видят только
знаком ит есь------ 3 ^ „ г. ^
с ключевым ПОЛЯ общего доступа. После переноса закрытого поля в класс P a r ty , клас-
словом protected. D in n e rP a r ty и B ir th d a y P a r ty потеряют к нему доступ.)
по ле°^я^прои^ ^ потребуется конструктор. Внимательно посмотрите на кон-
водного класса, структоры B i r t h d a y P a r t y и D in n e rP a r ty : не имеют ли они что-нибудь
осмавляя его общее.
закрытым для
всего ост аль- Теперь добавим платеж $100 к мероприятиям с количеством гостей бо-
ного. лее 12. Это касается как званых обедов, так и дней рождения, поэтому
поместим этот платеж в класс Party.
•ЗЖНбНИб Вот как следовало отредактировать классы DinnerParty и BirthdayParty, чтобы они
унаследовали свойства базового класса Party. Именно это позволило вам доба-
cLlcnrlc вить новый платеж ($100), не редактируя при этом форму!
p r iv a te in t n u m b e rO fP eo p le ;
p u b l i c v i r t u a l i n t N u m b erO fP eo p le { Перед парамет ром
g e t { r e t u r n n u m b e rO fP eo p le ; } «_ « > := :_________
NumberOfPeople т ребует
set {
ся вставить ключевое сло
во virtual, так как класс
n u m b e rO fP eo p le = v a l u e ; BirthdayParty должен его пере
C a lcu la teC o stO fD eco ra tio n s(fa n c y D eco r a tio n s) крыть (в эт ом случае измене
ние количества гостей меняет
} диаметр торта).
Стоимость оформления
p iib lic v o id C a lc u la teC o stO fD eco ra tio n s(b o o l fancy) {
для мероприятий обоих
fa n c y D e co r a tio n s = fan cy;
\ типов рассчитывается
i f (fan cy)
по одной и той же схеме,
C o stO fD eco ra tio n s = (N u m b erO fP eo p le * 1 5.00М ) + БОМ; у х .о э т о му ее логично п о
e ls e м ест ит ь в класс Party.
C o stO fD eco ra tio n s = (N u m b erO fP eo p le * 7.50М ) + ЗОМ;
}
p u b lic v i r t u a l d e c im a l C a lc u la te C o s tO {
d e c im a l T o ta lC o s t = C o s tO fD e c o r a tio n s + (C ostO fF ood P erP erson * N u m b e rO fP eo p le )
i f (N u m b erO fP eo p le > 12)
{
T o ta lC o st += lOOM;
^ М етоЭ вычисления стоимости
}
r etu rn T o ta lC o st; пом ет ит ь
словом vlrtuaL так как его п о -
270 глава 6
наследование
p u b l i c B i r t h d a y P a r t y ( i n t n u m b e rO fP eo p le , b o o l f a n c y D e c o r a t i o n s , s t r i n g c a k e W r itin g )
: b ase(n u m b erO fP eo p le, fa n c y D e c o r a tio n s )
C a lc u la teC a k e S ize (); К онст рукт ор ост авляет
th is .C a k e W r itin g = c a k e W r itin g ; основной объем работы За-
C a lc u la te C o stO fD e c o r a tio n s(fa n c y D e c o r a tio n s) зовами классу. И вызывает
} мет од CalculateCakeSizeQ, как
делал и старый конструктор
p r iv a te v o id C a lcu la teC a k eS ize! { и з класса B ir th d a y P a r ty
i f ( N u m b e r O f P e o p l e <= 4 )
C a k e S iz e = 8;
М етод Са1сиЫеСаке$1леО помещ ен
e lse
C a k eS iz e = 16;
в класс B irth d a yP a rty, т ак диам ет р
т о р т д учит ы вает ся т олько при
} расчет ее ст оим ост и дней рождения.
расчет
p r i v a t e s t r i n g c a k e W r i t i n g = ""
p u b l i c s t r i n g C a k e W r itin g { По эт ой же причине свой
g e t { r e tu r n t h i s . c a k e W r itin g ; } ст во CakeW riting ост ает ся
set { в классе B irthdayP arty.
i n t m axL ength;
i f ( C a k e S i z e == 3)
m a xL ength = 16;
e ls e
m axL ength = 40;
i f ( v a l u e .L e n g t h > m axL ength) {
M e s s a g e B o x .S h o w ( " T o o many l e t t e r s f o r a " + C a k e S i z e + " i n c h c a k e " )
i f (m axL ength > t h i s . c a k e W r i t i n g . L e n g th )
m axL ength = t h i s . c a k e W r i t i n g . L e n g t h ;
t h i s .c a k e W r i t in g = c a k e W r i t in g .S u b s t r in g (0, m axL ength);
} e ls e
t h i s . c a k e W r itin g = v a lu e ; М етод CalculateCostQ и з
дается в п е р е к р ы т и и ,, т ак
^кст чала^вш исляет е
}
cmouMocmt>
p u b l i c o v e r r i d e d e c i m a l C a l c u l a t e C o s t O0 { . т о м ко т и т е добавить ее
d e c im a l C ak eC ost; к сум м е,
i f ( C a k e S i z e == 8) т а л м ет од CalculateCostQ
C a k e C o s t = 40M + C a k e W r i t i n g . L e n g t h * .25M ; из класса Party.
e lse
C a k e C o s t = 75M + C a k e W r i t i n g . L e n g t h * .25 M ;
r e t u r n b a s e . C a l c u l a t e C o s t () + C a k e C o s t ;
} 3dect> свойство NumberOfPeople
должно перекры ват ь одноименное
p u b l i c o v e r r i d e i n t N u m b erO fP eo p le {
свойство из класса P arty, т ак как
g e t { r e t u r n b a se.N u m b e rO fP e o p le ; }
м ет оду записи нужно пересчит ат ь
set {
^ разм ер т орт а. При эт о м вызы-
b a se.N u m b e rO fP e o p le = v a l u e ; \ вает ся base.NumberOfPeople, чт о
C a lc u la teC a k e S ize ();
провоцирует выполнение мет ода
t h i s . C a k e W r itin g = c a k e W r itin g ;
записи из класса Party.
дальше ► 271
отличная работа!
— Э т о п оследний к л а с с ,
в программе Кэтлин.
'Код формы остался без
х/у изменений.) Это поле общего доступа
ажненке V
c l a s s D in n erP a rty : P a rty
используется только
I / расчете стоимости званых
реш ение { (V. обедов и поэтому остается
p u b l i c d e c i m a l C o s t O f B e v e r a g e s P e r P e r s o n ; в эт ом классе.
Метод
C ostO fB everagesP erP erson = 20 .0 0 M ;
}
c o s t l получит значение 261.25, в то время как c o s t 2 - значение 250. Проблема... Иногда в базовом
классе существует код, к которому вы не хотите обращаться напрямую. Или вы не собирались созда
вать экземпляры класса P a r t y , но такая возможность сохранилась. А что будет, если человек, редакти
рующий наш код, создаст экземпляр класса P a r t y ? Явно не то, что мы планировали.
О том, как решить эту проблему, вы узнаете в следующей главе!
272 глава 6
наследование
Построение осноб
Этот проект делится на две части. Начнем с обзора системы управления
ульем. Вам потребуются два класса - Queen (Матка) и W orker (Рабочий), -
которые следует инкапсулировать. Не обойдемся мы и без связанной с эти
ми классами формы.
Иногда в диаграмме классов м огут т
появиться закрытые поля и типы.
- ,-^ б ъ е к т Queen указывает, какую работу нужно сделать.
Q ueen
Массив объектов W o r k e r отслеживает текущую занятость
private workers: WorkerQ j
каждой рабочей пчелы. Эта информация хранится в закры
private shiftNumber: int
тых полях W o r k e r [ ] .
Форма вызывает метод A ssignW ork {) (Назначить задание),
передавая строку с названием работы и переменную типа int с
AssignWorkO
количеством смен. При наличии свободной пчелы, которая в
WorkTheNextShiftO
состоянии выполнять эту работу, возвращается значение true.
Кнопка Work the next sh if t вызывает метод W o r k T h e N e x t
S h i f t O , заставляющий каждый объект Worker отработать
одну смену и затем проверяющий его состояние, чтобы сфор
Свойства C u m n tJ o b мировать отчет.
(ТекущееЗадание) и ShiftsLeft
(ОставилиесяСмены)
предназначены только для чтения.
W o rk e r Посмотрим на функции массива Worker.
CurrentJob: string Свойство C u r r e n tJ o b дает понять объекту Queen, какую ра
ShiftsLeft: int боту выполняет каждый рабочий. Если рабочий на момент
проверки ничем не занят, возвращается пустая строка.
private jobslCanDo: string[] ^ Раздача заданий рабочим происходит при помощи метода
private shiftsToWork: int D o T h isJo b {). Если рабочий не занят и в состоянии выпол
private shiftsWorked: int нять указанную работу, метод возвращает значение true.
DoThisJobO Метод W o rk O n eS h iftO заставляет рабочего отработать
WorkOneShiftO одну смену, а также отслеживает оставшееся количество
смен. Если работа закончена, возвращается пустая строка.
Это означает, что он готов к выполнению следующего зада
ния.
^ момент времени х т -
U p o ЗЦ О , как
уСоБерШеНСЩБоБатпь
систему угд^аБления уЛьеМ
Tij^u поМоЩи наследования,
Чшпагедге на с.
наследование
О П о стр о ен и е qx>pMbi
Форма будет очень простой, всю работу предстоит выполнить классам Q u e e n и W o r k e r .
Добавьте закрытое поле Q ueen и две кнопки, вызывающие методы A s s i g n W o r k () и
W o r k T h e N e x t S h i f t О . Еще потребуется элемент C o m b o B o x , управляющий работой
пчел (пункты этого раскрывающегося списка были перечислены на предыдущей стра
нице), элемент N u m e r i c U p D o w n , две кнопки и текстовое поле для отчета о сменах.
Вид формы показан ниже.
Кнопка nextShift вы
Элемент ComboBox нд- ^ Beehw« Menagement System зывает метод
зывается workerBeeJob. WorkTheNextShiftQ из
Используйте свойство . Wsika-BeeAssignma^s класса Queen, возвраща
Items для ф орм ирования^ Wotka-beejsrfj Ms ющий строку с от че
списка. DropPownStyle W oittme т ом о сменах.
присвойте значение collector neadshe
PropPownListj чтобы Assign toabee
пользователь мог вы Рассмотрим отчет,
бирать значения только Reportforahft#12 созданный объектом
из раскрывающего Woiker#1 !Sdoing 'NectarcoSector'for1moredifts Queen. Он начинается
ся списка. Поле Shifts WorkerЙ2wi bedonewth 'Eggcare'sftertHsЙЛ с номера смены, за
Woike-#3isdoing'aingрйго!’for1moreshfts
является элементом Woiker#4firtshedthejob т ем следует спи-
NumericUpPown с им е coK дел работников.
i
Woiker#4isnotwraking
нем shifts. Используйте esc-
Д айт е элементу
TextBox имя report,
а его свойству MultiLine последоёательности
значение true.
" \ г \п ’] чтобы доба ^
Конструктор каждого объекта
p u b lic F o rm lо { вить знак переноса
чает массив строк с информацией о т ом ,
In itia liz e C o m p o n e n tO ; в строку. пчела.
какие работы может выполнять
W o r k e r [] w orkers = n e w W o r k e r [4]
w o r k e r s [0 ] = n e w W o r k e r ( n e w s t r i n g [] " N ectar c o ll e c t o r " , "Honey m a n u f a c t u r in g " });
w o r k e r s [1 ] = n e w W o r k e r ( n e w s t r i n g [] "Egg c a r e " , "Baby b e e tu to rin g " });
w o r k e r s [2 ] = n e w W o r k e r ( n e w s t r i n g [] "H ive m a in t e n a n c e " , " S tin g p a tr o l" }) ;
w o r k e r s [3 ] = n e w W o r k e r ( n e w s t r i n g [] " N ectar c o l l e c t o r " , "Honey m a n u f a c t u r in g " .
"Egg c a r e " , "Baby b e e tu to r in g " , "H ive m a in t e n a n c e " , " S tin g p a tr o l" });
276 глава 6
наследование
c l a s s Q ueen {
p u b l i c Q u e e n ( W o r k e r [] w o r k e r s ) {
th is .w o r k e r s = w orkers;
}
p r iv a te W o r k e r [] w o r k e r s ;
к о м /т к Т з н « ч е £ < е поля заЭ ает конструктор.
p r iv a te i n t S h iftN u m b er = 0;
retu rn fa lse r
\ \ умеет делать работы икЛаи-
Сколько же м ед а потребляет ул е й
Матке только что позвонила пчела-бухгалтер и сказала, что в улье недостаточно меда. Бух
галтеру нужна информация о количестве потребляемого меда, чтобы понять, не нужно ли
часть рабочих, занимающихся заботой о потомстве, перевести на добычу меда.
★ Все пчелы потребляют мед, поэтому его должно быть много.
★ Занятые рабочие потребляют больше меда. Больше всего его требуется в момент на
чала работы, потом потребление падает. В последнюю смену пчела съедает 10 единиц
меда; в предпоследнюю - 11; перед этим - 12 и т. д. То есть если пчела работает (ее
переменная S h i f t s L e f t больше нуля), количество потребляемого меда можно вы
числить, прибавив 9 к S h i f t s L e f t .
★ Неработающая пчела ( S h i f t s L e f t равно нулю) съедает 7.5 единиц меда за всю смену
★ Это данные для обычных пчел. Если вес пчелы превышает 150 мг, она ест на 35%
меда больше. (К матке эта информация не относится.)
★ Чем больше занятых рабочих, тем больше меда потребляет матка, так как больше
сил тратится на управление ульем. Количество рабочих смен матки равно количе
ству смен пчелы, которой осталось работать дольше всего.
★ Матка потребляет в смену на 20 единиц меда больше, если работает не больше
2 пчел или меньше, или на 30 единиц больше, если работают 3 пчелы и более. Так
как вес матки составляет 275 мг, к ней не относится правило 35%.
★ Данные о количестве потребляемого меда нужно добавить в конец отчета об отра
ботанных сменах.
278 глава 6
наследование
дальше ► 279
решение упражнения
p u b lic v ir tu a l d o u b l e G e t H o n e y C o n s u m p t i o n () {
ние, учитьшающее
d o u b le c o n su m p tio n ;
i f ( S h i f t s L e f t == 0)
П чела, к о т о р о й
____о с т а л а с ь всего 3-
потребление меда.
сменй^ п отреб л я
e lse
c o n su m p tio n = 7 .5 ;
е т 3-0 единии, меоа,
если смен Я, т о 13-
Только представь
con su m p tio n = 9 + S h i f t s L e f t ;
if (w e ig h t > 150)
c o n s u m p t i o n *= 1 . 3 5 ;
и т. д. Свободная
от работы пчела те, что вместо
(ShiftsLeft = О ) по -
r e t u r n co n su m p tio n ; щаребляет 7.5. этого вам при
} Пчелы с весом более
больше меда.
шлось бы вводить
повторяющийся
В форме меняется только код вручщвд!
p u b lic Forml О конструктор.
In itia liz e C o m p o n e n t(і
W o r k e r [] w orkers = new W o r k e r [ 4 ] ;
w o r k e r s [0 ] = new W orker(new s tr in g [] { " N e c t a r c o l l e c t o r " , "Honey m a n u f a c t u r i n g
w o r k e r s [1 ] = new W orker(new s tr in g [] { "Egg c a r e " , "Baby b e e t u t o r i n g " } , ( l l 4 )
w o r k e r s [2] = new W orker(new s tr in g [] {" H iv e m a in t e n a n c e " , " S t in g p a t r o l " ) T ^ 4 9
w o r k e r s [3] = new W orker(new s tr in g [] {" N ectar c o lle c t o r " "Honey m a n u f a c t u r in g "
"Egg c a r e " , "Baby b e e t u t o r i n g " , "H ive m a i n t e n a n c e " , " S t in g p a t r o l " },
q u e e n = new Q u e e n ( w o r k e r s ) ;
8 конструкторы класса
Worker добавляется информа
ция о весе рабочей пчелы.
280 глава 6
наследование
p u b l i c W o r k e r ( s t r i n g [] jo b slC a n D o , in t w eig h t)
: b a se(w eig h t ) {
t h i s . jo b s lC a n D o = jo b slC a n D o ;
}
p u b l i c ..o v e r r id e in t S h iftsL eft {
// ... th e rest of th e c la ss is th e sam e ..
. Сначала мы делаем класс Queen
производным от класса Вее.
c l a s s Q u een : B e e { М ат ка весит Z 7 S м г, именно э т о т парам ет р
p u b l i c Q u e e n ( W o r k e r [] w o r k e r s ) передает конст рукт ор данного класса к о н ст р ук
: b a s e (275) { ^
т ору класса Вее.
th is.w o r k e r s = w orkers;
}
d o u b le to ta lC o n B u m p tlo i. - 0; Эанто“ “
fo r (in t і = 0; і < w o r k e r s . L ength; i+ + ) оощее потребление.
to ta lC o n su m p tio n += w o r k e r s [ і ] . G e t H o n e y C o n s u m p t i o n ( ) ;
to ta lC o n su m p tio n += G e t H o n e y C o n s u m p t i o n ( ) ;
С
‘
м ет о д а W o r k ^ N e x tS h iftQ
о с т а л а с ь без и зм ен ен и й , в ы т о л ь к о д
8 эт ом классе перекрывает ся
м ет од CietHoneyConsumptionQ
в о т ч е т да н н ы е о п о т р е о л е н и и м е о а .
класса Вее. Чтобы учест ь п о
p u b l i c o v e r r i d e d o u b l e G e t H o n e y C o n s u m p t i o n () { т ребление меда м а т ко й , вы н а
d o u b le c o n s u m p t io n = 0; ходит е рабочего с сам ы м высо
d o u b le la r g e s t W o r k e r C o n s u m p t io n = 0; ким пот реблением и добавляете
in t w ork ersD o in g J o b s = 0; 2 0 или 3 0 (в зависимост и от
^ for (in t i = 0; i < w o r k e r s . L ength; i+ + ) количест ва занят ы х пчел).
Ґ --- {
Э т от цикл if ( w o r k e r s [ i ] . G etH o n ey C o n su m p tio n 0 > la rg estW o r k e rC o n su m p tio n )
определяет la rg estW o rk erC o n su m p tio n w o r k e r s [ і ] .G etH o n ey C o n su m p tio n ();
самое самое if (w orkers [і] . S h i f t s L e f t > 0)
высокое w o rk e rsD o in g J o b s+ + ;
пот ребление
меда среди }
рабочих пчел. c o n s u m p t i o n += l a r g e s t W o r k e r C o n s u m p t i o n ;
if (w o rk ersD o in g J o b s >= 3)
con su m p tio n += 3 0 ;
Если т рудят ся 3 пчелы и более,
e ls e
retu rn
c o n s u m p t i o n += 2 0 ;
co n su m p tio n ;
Ч м а т ке т ребует ся 3 0 дополни
т ельны х единиц меда-, в п р о т и в
ном случае ей нужны всего ЯО до -
полни т ельны х единиц.
шв
/
%
и а б с т р а т с ш н ы е К Л ассь!
Пусть классы
держат обещания
Симулятор улья
Для лучшего представления жизни в улье укажем спеии-
альные возможности рабочих пчел:
• Все пчелы потребляют мед и обладают весом.
. Матка раздает задания, следит за отчетами и отправ
ляет рабочих на следующую смену.
• Рабочие трудятся посменно.
• Охранники «точат» жало, ишут врагов и жалят их.
. Сборшики меда ишут цветы, собирают нектар и воз-
врашаются в улей.
сохранять
от ви! в зависимости
от выполняемых ими функций
284 глава 7
интерфейсы и абстрактные классы
о оесе пчел
SharpenStingerO FindFlowersO
LookForEnemiesO GatherNectarO
StingO RetumToHiveO
1 классы
>^анят ин~
Формации)
ШТУРМ
А как быть с пчелой,
совмещающей функции охранника
><отдельным и сборщика меда?
заданиям.
интерфейсы
Наследовать класс может только от одного класса. Поэтому создание
двух производных классов S t i n g P a t r o l и N e c t a r C o l l e c t o r не помо
Класс, реализу
жет нам описать пчелу, которая в состоянии выполнять задания разны х ющий интерфейс,
типов.
Метод D e f e n d T h e H i v e () (Защита улья) из класса Q u e e n может заста
должен вклю
вить объекты S t i n g P a t r o l защищать улей. Но если матка захочет, что
бы за эту работу принялись другие пчелы, она не сможет дать им команду:
чать в себя все
class Queen {
методы и свой
}
private void DefendTheHive(StingPatrol patroller) { }
ства, указанные
в определении
'G интерфейса.
286 глава 7
интерфейсы и абстрактные классы
Так как же помочь матке? Теперь она может создать метод, берущий Л(^0 ЭЛСМ6НТЫ ОТКОЫ“
в качестве параметра любой объект, который знает, как защитить улей: *
p r iv a te v o id D e fe n d T h e H iv e (IS tin g P a tr o l p a tr o lle r ) ТОГО интерфейса по
J умолчанию являются
открытыми. То есть
Этот метод может использовать объект S t i n g P a t r o l , N e c t a r S t i n g e r
или любую другую пчелу, знающую, как защитить улей. При реализации с помопц>ю интер-
I S t i n g P a t r o l м ето д D e f e n d T h e H iv e о гарантирует нал и ч и е у объек- ( g jjJ О П О е Д е Л Я С ”
т а с в о й с т в и м е т о д о в , н е о б х о д и м ы х д л я з а щ и т ы ул ья . i г
те открытые методы
Теперь, когда я знаю, и свойства любого
как защитить улей, мы
в безопасности! реализующего его
^ент О ^ класса.
дальше > 287
_ Чаоцо
немного от сборщика нектара и немного от охранника улья
------------^ а Д а Б а е М ы е ------------
БоЛ роС Ь!
Экземпляр NectarStinger Зачем симулятору улья интерфей
сы? Ведь мы добавляем еще один класс
Используйте двоеточие, чтобы реализовать интерфейс. По NectarStinger, и все равно получаем
сле двоеточия сначала указывается класс, от которого про дублирующийся код?
исходит наследование, затем список интерфейсов. Если на
следования не происходит, то интерфейсы перечисляются
в произвольном порядке. О ; Интерфейсы и не предназначены для борьбы
с'дублирующимся кодом. Они просто позволя
ют использовать один и тот же класс в разных
.. ^ _ Э т о т класс нлслеЭчеил ом ситуациях. Вам требовалось создать класс рабочих
Как и в случае наследования, класса Worker и реализуем пчел, которые могут выполнять два задания.
для реализаиии инмерфейса иимерфейсы INectarCollector
Интерфейсы дают возможность получить класс,
используется Эвоемочие. IStingPatrol. ^
выполняющий произвольное количество заданий.
class NectarStinger^^^WorkerQlNectarCollector, Скажем, у вас есть метод P a t r o l T h e H i v e ( ) ,
IStingPatrol { ■\Анмердрейсы работающий с объектом S t i n g P a t r o l ,
public int AlertLevel { п еречи сляю т ся и метод C o l l e c t N e c t a r () для объекта
y' ^ get { return alertLevel; J через з а п я т у ю . N e c t a r C o l l e c t o r . При этом хочется, чтобы
.ласс
’e c t a r S t i n g ii^.r
er ^ класс S t i n g P a t r o l мог наследовать от
адаем вспо- класса N e c t a r C o l l e c t o r ИЛИ наоборот, ведь
ргдЬИс int StingerLength {
хогательное в каждом классе есть открытые методы и свойства,
get { return StingerLength;
,оле для сбой- отсутствующие у другого. А теперь подумайте, как
тва A l e r t L e v e l set {
можно создать класс, экземпляры которого могут
StingerLength = value;
I методе быть переданы обоим методам. Есть какие-нибудь
^ o o k F o r E n e m ie s Q - } идеи?
КаждоЩ Проблему решают интерфейсы. Создав ссылку
методу 6 1ЛН- public bool LookForSnemies() {•■•} IStingPatrol, вы можете указать на любой объ
щ лерфейсе со- public int SharpenStinger(int length) екг, реализующий I S t i n g P a t r o l , какому
отб етстба “
е т т глоЪ в бы классу этот объекг ни принадлежал. Можно
к лассе. Иначе указать, как на объекг S t i n g P a t r o l , так и на
pxiblic void FindFlowersO {...}
у ^ рограм м а не объекг N e c t a r S t i n g e r ИЛИеще на что-нибудь.
public void GatherNectarO {...}
5 у д е т KOfAnu При этом вы можете использовать все методы и
лироват м я. pviblic void R e t u m T o H i v e O {.•.}
свойства, которые являются частью интерфейса
I S t i n g P a t r o l , независимо от типа объекта.
Вели врагов нем Разумеется, вам придется создать новый класс,
С оздан н ы й в а м и о б ь е к т пчела прячем жало, который и будет реализовывать интерфейс. Так что
N e c ta r S tin g e r с м о ж е т б ы - оспомогамельное этот инструмент не позволит избежать создания
п о л н я т » р а б о т у пчел как 1^оле StingerLenqtk дополнительных классов или сократить количество
и з к л а с с а N e c ta r C o lle c to r , т а к м еняем свое
дублирующегося кода. Он всего лишь дает возмож
и из к л а с с а S t i n g P a t r o l .
значение.
ность получить класс для выполнения нескольких
работ без привлечения наследования. Ведь наследу
ются все методы, свойства и поля другого класса.
Класс, реализующий интерфейс так же, как и обычный, соз
дает экземпляры при помощи оператора new и использует ме Как при работе с интерфейсами избежать ду
тоды: блирующегося кода? Можно создать отдельный
класс с именем s t i n g e r и кодом, относящимся
N e c t a r S t i n g e r b ob T h eB ee = new N e c t a r S t i n g e r ( ) ;
к укусам или сбору нектара. После чего объекгы
N e c ta r S tin g e r H N e c ta r C o lle c to r
b o b T h eB ee. L o o k F o rE n em ies{);
смогут создать закрытый экземпляр s t i n g e r ,
bobT heB ee. F in d F lo w e r s(); и для сбора нектара будут использовать его методы
и задавать его свойства.
288 глава 7
интерфейсы и абстрактные классы
Error List
!O 4 Errors 0 Warnings 0 Messages
Description
Oi 'IStingPatroLExperimervt.Bee' does not implement interface member ’IStingPatroLExperiment.!StingPatrol.ShsrpenStingef(int)'
©2 IStingPatroL&tpsriment.Bee' does not implement interface member 'IStingPatrQl_Experiment,IStitT§Patrol.LooicFQrEnem(e50‘
©3 'IStingPatroLExperiment.Bee' does not implement interface member '!StingPatrol_E](perrment.lStingPatro!.Stin§erLength‘
0 4 'IStingPatroLExperiment.Bee' does not implement interface member ’IStingPatrol_Experiment.lStingPatrol.AtertLever
дальше * 289
поваляем дурака
Это класс T a l l G u y (Высокий парень) и код для кнопки, нажатие которой создает объект
и вызывает его метод T a l k A b o u t Y o u r s e l f () (Рассказ о себе). Пока ничего нового:
c la s s T a llG u y {
p u b lic str in g Nam e;
p u b lic in t H eig h t;
p u b lic v o id T a lk A b o u tY o u r se lf0 {
M e s s a g e B o x . S h o w ( "М еня зовут " + Nam e + " я ростом "
+ H eig h t + " с м ." );
}
}
p r iv a te v o id b u tto n l_ C lic k (o b je c t sender, E ven tA rgs e) {
T a llG u y ta llG u y = new T a l l G u y () { H eig h t = 190, Nam e = "Джимми" };
ta llG u y .T a lk A b o u tY o u r se lf();
1
© Создадим и н т е р ф е й с I C lo w n .
Вы уже знаете, что все элементы интерфейса должны быть открытыми. Проверим это ут
верждение на практике. Создайте новый проект и объявите интерфейс:
i n t e r f a c e I C lo w n
М одиф икат ор public
Теперь попробуйте объявить внутри него закрытый метод: оку т ри и н т ер ф ей
са писат ь не нужно,
p r i v a t e v o i d Honk О ; дост уп ко всем его
м ет одам и свойст вам
Выбрав команду B u ild»B uild Solution, вы увидите сообщение: им еет ся по ум олчанию
290 глава 7
интерфейсы и абстрактные классы
Убедитесь, что остальной код класса остался без изменений. Выберите команду
Build Solution в меню Build, чтобы построить решение. Появятся два сообш,ения об
ошибке. Вот одно из них: , „ „
Объявив, что класс
I TallGuy реализует ин-
'T a llG u y ' d o e s n o t im p lem en t i n t e r f a c e терф&ис IClown, вы
m e m b er ' I C l o w n . H o n k ( ) ' пообещали добавить
в эт от интерфейс все
методы и свойства, но
не сделали этого!
Это не работает...
IS ti ng Pat ro l dennis = n e w I S t i n g P a t r o l ();
Q I Cannot create an instance of the abstract class or interface |
292 глава 7
интерфейсы и абстрактные классы
т
Пусть класс StingPatrol реализует интерфейс
IStingPatrolj а класс NectarCollector — интерфейс
INectarCollector.
Оператор is
_ Часто
Иногда требуется понять, реализуется ли интерфейс определенным - ^ а Д а Б а е М ы е ------
классом. Предположим, что все рабочие пчелы представлены в виде Б оц|эос;ь1
массива Bees. В массиве можно хранить переменные типа W orker, так
как все рабочие пчелы принадлежат классу W orker или производным Свойства, добавляемые в
от него классам. интерфейс, выглядят как авто
матически реализуемые. Неуже
Но какая из рабочих пчел может собирать нектар? Для ответа на этот во
ли при реализации интерфейса
прос нужно узнать, реализует ли класс интерфейс I N e c ta r C o ll e c to r .
я могу использовать только
Это можно сделать при помощи оператора i s . такие свойства?
Ш ТУРМ
Класс, не производный от класса worker, но реализующий ин
терфейс INectarCollector, может ВЫПОЛНЯТЬ работу по сборке
нектара! Но так как класс Worker не является для него базовым, вы
не можете поместить его в массив с другими пчелами. Подумайте,
каким образом можно получить массив из пчел обоих видов?
294 глава 7
интерфейсы и абстрактные классы
На д и а гр а м м е кл ас со в
интерфейсы U наследование насл едование интер
Когда один класс наследует от другого, он получает все его ме ф ей со в обозначается
тоды и свойства. Н аследование и н терф ей сов происходит еще пунктирной линией.
проще. Так как в интерфейсах отсутствуют тела методов, вам
уже не придется заботиться о вызове конструкторов и методов
базового класса. Наследующие интерфейсы просто накапливают
в себе методы и свойства своих родителей.
i
(interface)
O m созданного ^ IW orker
i n t e r f a c e IW o r k e r НЛММ интерфей
ca M orker м о Job
{ г у т наследовать ShiftsLeft
все остальные
s t r in g Job { g e t; } интерфейсы.
DoThisJobO
in t S h ift s L e ft { ge t; } Worl<OneShift()
v o id D o T h is J o b (s t r in g jo b , in t s h ift s )
v o id W o r k O n e S h iftO
(interface) (interface)
Isting P atrol IN ectarC ollector
Класс реализует ^ м е т о д ы u cßoücmßa StingerLength Nectar
EnemyAlert
ICnacc, реализующий интерфейс, должен включить в себя все
свойства и методы этого интерфейса. В ситуации, когда один ин
терфейс наследует от другого, все их свойства и методы также SharpenStingerO FindFlowersO
должны быть реализованы. Looi<ForEnemies() GatherNectarO
StingO RetumToHiveO
DoThisJobO
class Robot Г Г Г г Х Г к о М .
{
public void ConsumeGasO Класс RoboBee наследиет от
/г’ейлизует
} а н те ^ ф е а с (Worker. В итоге
B e e s [0 ] = new N e c t a r S t i n g e r 0 ;
B e e s[l] = new R o b o B e e O ; ................................................................................................
B e e s [2] = n e w W o r k e r 0 ; 2 . (Bees[i] is istingPatrol)
B ees[3 ] = B ees[0] as IW orker;
B e e s [4] = i s t i n g P a t r o l ;
B e e s [5 ] = n u l l ;
3. (B e e s[i] is IW orker)
B e e s [6 ] = B e e s [0 ] ;
B e e s [7] = n e w I N e c t a r C o l l e c t o r {) ;
298 глава 7
интерфейсы и абстрактные классы
Восходящее приведение
Когда вы используете производный класс вместо базового, например, ссылаясь на кофеварку вместо
прибора, - это называется в о с х о д я щ и м п р и в е д е н и е м ( u p c a s t in g ) . Это очень мощный инструмент,
который вы получаете, построив иерархию классов. К сожалению, он работает только с методами и
свойствами базового класса. Другими словами, рассматривая кофеварку как прибор, вы не можете
заставить ее сварить кофе MakeCoffee() или налить воду FillWithWater(). Зато вы жожеше определить,
включена ли она в розетку, так как это состояние относится ко всем приборам (и именно поэтому свой
ство P l u g g e d i n помещено в класс A p p l i a n c e ) .
Q Создадим объекты
Классы C o f f e e M a k e r и O ven создаются обычным способом:
C offeeM ak er m i s t e r C o f f e e = new C o f f e e M a k e r О ; ? Д л я начала получиМ
Т экземпляры оо-ьектоо
O ven o l d T o a s t y = n e w O v e n {) ; __ J O v e n и C offeeM aker.
Нисходящее приведение
Теперь вы знаете, что рассматривая кофеварку и духовку как приборы, вы лишае
тесь доступа к их собственным методам и свойствам. К счастью, суш;ествует проце
дура н и с х о д я щ е г о п р и в е д е н и я ( d o w n c a s t in g ) . Узнать, относится ли рассматрива
емый объект A p p lia n c e к классу C offeeM aker, можно при помощи оператора i s . В о т ссылка
Теперь ничто не мешает вам вернуться от класса A p p lia n c e к классу C offeeM aker АррИапсе,
при помощи оператора a s . указывающая
на обьект
CoffeeMaker
О Н ачнем с объекта C o ffe e M a k e r
Вот код, который мы использовали для восходящего приведения:
A p p l i a n c e pow erC on sum er = new C o f f e e M a k e r ( ) ;
p o w er C o n su m e r .C o n su m eP o w e r 0 ;
Nul№teferenceExceptkm wm ifftwreiled
I
300 глава 7
интерфейсы и абстрактные классы
= m iste rT o a sty ;
ступ к различным
pow erC onsum er. методам и свой
Ссылка powerConsumer [icolor Appllance.Color ствам, в зависи
принадлежит клас ConsumePower
су АррИаме. Она дает
доступ к открытым
Equals мости от своего
GetHashCode
полям, методам и свой
ствам этого класса. Но GetType типа.
при желании вы можете ^ Pluggedin
с ее помощью сослаться "^ T o S trin g
на обьект CoffeeMaker. дальше > 301
неглупые вопросы
_ Часто
^аД аБ аеМ ы е
Почему восходящее приведение Q ; Нет, компилятор не позволит вам это Можно ли реализовать интерфейс,
можно осуществлять всегда, а нисхо сделать. Интерфейс не должен содержать не вводя много кода?
дящее нет? операторов. Оператор в виде двоеточия, ре
ализующий интерфейс, не имеет отношения
! Конечно! Средства ИСР позволяют
Q « Компилятор может предупредить к оператору, использующемуся при насле
реализовать интерфейс автоматически.
вас о том, что восходящее приведение довании классов. Реализация интерфейса
Начните вводить код класса:
происходит неправильно. Более того, эта ничего не меняет в кпассе. Она всего лишь
операция не работает только когда вы гарантирует присутствие в кпассе методов,
c la ss
пытаетесь сопоставить объект классу, от перечисленных в интерфейсе.
M ic ro w a v e IC ooksF ood
которого не происходит наследования,
{ }
или интерфейсу, который этим объекгом Интерфейс выглядит как наложе
не реализуется. Компилятор распознает ние ограничений, ничего не меняющих Щелкните на ICooksFood — под буквой I
невозможность подобной операции и вы в самом классе. Зачем мне его исполь появится маленькая полоска. Если задер
водит сообщение об ошибке. зовать? жать на ней курсор, появится значок:
302 глава 7
интерфейсы и абстрактные классы
дальше * 303
уберите страшных клоунов!
ажнение Вот как выглядит расширение интерфейса IClown с реализующими его классами.
]^ешение
in te rfa c e IC lo w n {
s t r i n g F u n n yT h in glH ave { get; }
v o id H onk();
}
in te rfa c e IS ca ry C lo w n : IC lo w n {
str in g S c a r y T h in g lH a v e { get; } Метод HonkQ
v o id S c a r e L ittle C h ild r e n 0 ; использует
} эт от метод
записи для
c la ss FunnyFunny : IC lo w n { отображений
p u b lic F u n n y F u n n y (str in g fu n n y T h in g lH a v e ) { сообщения, что
Можно еще раз
th is.fu n n y T h in g lH a v e = fu n n y T h in g lH a v e ; избавляет вас
} от дублирую- реализовать
эт от мет од и
p r iv a te str in g fu n n y T h in g lH a v e ; щегося кода. свойство инт ер
p u b lic str in g F u n n yT h in glH ave {
Г фейса IClown,
get { retu rn "Б и-би! У меня е с т ь + fu n n y T h in g lH a v e ; но почему бы не
} унаследовать их
}
от FunnyFunny?
p u b lic v o i d HonkO {
M e s s a g e B o x . S h o w {t h i s . F u n n y T h i n g l H a v e ) ;
p r iv a te in t n u m b ero fS ca ry T h in g s;
p u b lic str in g S caryT h in g lH a v e {
get { retu rn "У м е н я " + n u m b e r O f S c a r y T h i n g s + " пауков"; }
304 глава 7
интерфейсы и абстрактные классы
Модификаторы доступа
В ы , * е з н а е т е , н а с к о л ь к о в а ж н ы м я .^ я е т с я к л ю ч е в о е с л о в о p r l v a t e , Л
как его нужно использовать и чем оно отличается от ключевого слова ^ членами (tnembers)
p u b l ic . В C# подобные ключевые слова называются модификаторами Людой член можем быть
доступа (access modifiers). Меняя модификатор свойства, поля, метода помечен модиф икам о-
или даже всего класса, вы меняете способ доступа других классов к ука- Р°М доступа public или
занным элементам. В этом разделе мы вспомним про уже известные вам priva е.
модификаторы и познакомимся с новыми: сущ ествует
доступ к объявленному классу.)
Q public означает свободный д оступ ^
Пометив класс или его члены модификатором p u b l i c , вы объявляете открытый
доступ для всех экземпляров всех классов. Это наименее ограничивающий из мо
дификаторов. И вы уже видели, причиной каких проблем он может стать,
зуйте его только тогда, когда это действительно нужно. модификато -
ра доступа при
Q private означает доступ только для д р у ги х членов этого ж е класса объявлении члена
Пометив члены класса модификатором p r i v a t e , вы оставляете доступ к ним класса о зт ч м т ,
только для других членов этого же класса или экземпляров этого же класса, _
Сам класс можно пометить словом p r i v a t e , только если он находится внутри Private.
другого класса. После этого доступ к нему сохранится только у экземпляров э т ^
го внешнего класса.
}
void HonkO;
I
Ключевое слово tkis позволяет
о ж л и ч ы т ь вспомогательное поле
interface IScaryClown : IClown { ^^Р<^метра
string ScaryThinglHave { get; Имя f'^'^nyrkm ^H ave от носит ся
void ScareLittleChildren0; запись tkis.FunnyThnqlH ave
}
О Класс FunnyFunny реализует интерфейс IClown. Поле fu n n y T h in g lH a v e
помечено модификатором p r o t e c t e d , значит, доступ к нему имеют все эк
земпляры производного класса.
class FunnyFunny : IClown {
piiblic FunnyFunny (string funnyThinglHave) {
_ t h ^ .funnyThinglHave = funnyThinglHave;
} П осмот рит е, как м оди
ф икат ор p ro tected п о
protected string funnyThinglHave; влиял на м ет од ScaruScaru
public string FunnyThinglHave { ScareLittleChildrenQ .
get { return "Би-би! У меня есо?ь " + funnyThinglHave;
Употребив рядом со свой
ством ключевое слово this, вы
запускаете метод чтения
piiblic void HonkO { или метод записи.
MessageBox.Show(this.FunnyThinglHave);
■Кл н?чебое слово tkis указывает,
что б данном случае имеется
б оиду вспомогательное поле
л не одноименный парам ет р.'
306 глава 7
интерфейсы и абстрактные классы
или ScaryScary.
дальше ► 307
ox уж этот дублирующийся код!
_ Часто
Зачем нужен интерфейс, если все ^аД аБ аеМ ы е
Получается, интерфейсные ссыл
необходимые методы можно написать B oiiJJoC bi
ки ограничивают мои возможности.
непосредственно в теле класса? Зачем же мне их тогда использовать?
Зачем нужны свойства, если есть
; По мере усложнения программ
I ОЛЯ? S Интерфейсные ссылки позволяют
классов становится слишком много. работать с различными объектами,
Интерфейсы позволяют сгруппировать
эти классы в соответствии с выполняе Q ; Интерфейс определяет способ, которым
выполняющими одну функцию, их помос
щью вы можете создать массив, обме
мыми задачами. Это гарантирует, что для класс выполняет определенную работу. При
нивающийся информацией с методами ин
выполнения определенной работы классы этом он не является объекгом, поэтому вы
терфейса I C a r r y P a s 3 e n g e r , неважно,
будут использовать одни и те же методы. не можете создавать экземпляры и хранить
работаете вы с грузовиком, лошадью или
Благодаря интерфейсу вам не придется в них информацию. Добавив поле путем объ
автомобилем. Способы, которыми эти
беспокоиться о том, как именно выполня явления переменной, вы ставите перед про-
объекты выполняют работу, различаются,
ется эта работа. фаммой задачу сохранить данные. Свойство
но благодаря интерфейсным ссылкам вы
же, с точки зрения остальных объектов, вы-
знаете, что все они имеют одни и те же
Представим, что у вас есть классы глядиг как поле, но по сути является методом
методы, одинаковые параметры и воз
грузовиков и парусников, реализую и не нуждается в хранении данных.
вращают значения одинаковых типов. Это
щие интерфейс перевозки пассажиров дает вам возможность единообразно вы
1 С а г г у Р а з з е п д е г . Он требует Чем обычная ссылка отличается зывать их и передавать им информацию.
у реализующих его классов наличия от интерфейсной?
метода С о п з и т е Е п е г д у ( ) . Про
грамма может использовать оба класса Зачем мне может понадобиться
Q ; Как работают обычные ссыл ключевое слово protected?
для перевозки пассажиров, хотя метод ки, вы уже знаете. Еспи создать эк
С о п з и т е Е п е г д у () для парусников земпляр S k a t e b o a r d С именем
использует силу ветра, а для грузовиков — ! Оно позволяет инкапсулировать
V e r t B o a r d , и ссылку на него с именем
дизельное топливо. класс. Иногда требуется доступ из про
H a l f p i p e B o a r d , ОНИ будут указывать на
изводного класса к внутренней части
один объект. Но еспи S k a t e b o a r d реали
Без интерфейса Х С а г г у Р а з з е п д е г базового. Но при построении классов поля
зует интерфейс i s t r e e t T r i c k s , а вы
программе сложно объяснить, какие оставляют открытыми только если без
создаете ссылку на s k a t e b o a r d с именем
транспортные средства могут перевозить этого совсем нельзя обойтись. Модифика
s t r e e t B o a r d , она будет знать только
пассажиров, а какие — нет. Вам потре тор доступа protected позволяет открыть
методы класса s k a t e b o a r d , являющиеся
бовалось бы просматривать все классы, поля, доступ к которым нужен производ
частью интерфейса i s t r e e t T r i c k 3 .
чтобы определить в них наличие методов, ному классу, оставив их закрытыми для
пригодных для перевозки пассажиров, остальных объектов.
Все три ссылки указывают на один объ
а потом вызывать эти методы вручную. ект. Но если ссылки H a l f P i p e B o a r d
Без стандартного интерфейса они, скорее и V e r t B o a r d дают доступ ко всем
всего, назывались бы слишком разнород свойствам и методам объекта, ссылка
но, кроме того, могли оказаться скрытыми
в теле других методов. Запутаться в этом
S t r e e t B o a r d видит только те методы Интерфейсные
и свойства, которые указаны в интерфейсе.
очень легко.
ссылки «знают»
только о тех свой
ствах и методах,
которые упомянуты
в интерфейсе.
308 глава 7
интерфейсы и абстрактные классы
}
p u b lic v ir tu a l v o id B u y F a v o r ite S tu ff 0 { BuyFavoriteStuffO BuyFavoriteStuffO
}
Г /
} Классы A rtS tu d e n t
Класс A r t S t u d e n t производный от класса S h o p p e r : и EngineeringStudent
перекрывают м ем од
c la ss ArtStudent : S h o p p e r { BuyFavoriteStuffQ.
p u b lic o v e r r id e v o id B u y F a v o r ite S tu ff () { Они покупают р а з
ные книги.
B u y A rtS u p p lie s 0 ;
B u y B la c k T u rtlen ec k s();
B u y D ep re ssin g M u sic ();
}
}
И класс E n g in e e r in g S tu d e n t наследует от класса Shopper:
c la ss EngineeringStudent : S h o p p e r {
p u b lic o v e r r id e v o id B u y F a v o r ite S tu ff О {
B u y P e n c ils {);
B u y G r a p h in g C a lc u la to r {);
}
}
Ч то п р о и з о й д е т с п о я в л е н и е м э к з е м п л я р а к л а с с а S h o p p e r? И м е е т л и с м ы с л е го с о з д а в а т ь ?
310 глава 7
интерфейсы и абстрактные классы
И ногда исто чни ком части кода явл яю тся п рои зв од ны е классы .
Бывает так, что создание ненужных объектов имеет плохие последствия.
Поля самого верхнего класса иерархии обычно задаются в производных
классах. В классе Animal могут находится вычисления, зависящие от зна
чения логической переменной HasTail или Vertebrate, но в нем невозмож
но задать эту переменную.
К ласс PlanetMission клуб
аст роф изиков и спользу Вот еще один пример... Один полет совершаемся на
ет для от правки ракет — Венеру, д р у го й -н а Марс.
к различны м планет ам .
c la ss Plan 0 tMission {
\
Присваивать этим
полям значения в c la ss
\
Venus : P l a n e t M i s s i o n {
p u b lic l o n g R o c k e t F u e l P e r M i l e ; базовом классе бес p u b lic V enus О {
смысленно, потому M ile sT o P la n et = 40000000;
p u b lic l o n g R ocketSp eedM P H ;
что мы не знаем,
p u b lic in t M ile sT o P la n e t; J какая ракета куда R o c k e tF u elP er M ile = 100000;
полетит. R ocketSpeedM PH = 2 5 0 0 0 ;
p u b lic l o n g U n i t s O f F u e l N e e d e d () {
r e tu r n M ile sT o P la n et * R o c k e tF u elP er M ile; }
} }
c la s s Mars : P l a n e t M i s s i o n {
p u b lic in t T im eN eed ed O {
p u b lic M ars 0 {
r e tu r n M ile sT o P la n et / (in t) R ocketSp eedM P H ;
M ile sT o P la n et = 75000000;
} R o c k e tF u e lP e r M ile = 100000;
p u b lic str in g F u e lN e e d e d 0 ( R ocketSpeedM PH = 2 5 0 0 0 ;
retu rn " Y o u 'll need "
}
+ M ile sT o P la n et * R o c k e tF u e lP e r M ile
} Конструкторы производных классов^Мап
+ " u n its of fu el to get th ere. I t'll tak e
и Venus задают значения трех полей, уна
+ T i m e N e e d e d () + " h o u r s ." ; следованных от класса Planet. Но поля не
} могут получить значения от экземпляра
Planet. Что произойдет, если их ‘допыта
} ется использовать метод FuelNeeaed{).
p riv a te v o id b u tto n l_ C lic k (o b je c t s. E ven tA rgs e) {
M ars m ars = new M a r s ( ) ;
M essa geB ox. Sh ow (m ars. F u e lN e e d e d ())
}
p riv a te v o id b u tto n 2 _ C lick (o b je ct s, E ven tA rgs e) {
V enus v e n u s = new V e n u s ( ) ;
M e s s a g e B o x . Show ( v e n u s . F u e lN e e d e d ( ) ) ;
}
p riv a te v o id b u tto n 3 _ C lick (o b je ct s, E ven tA rgs e) {
P la n e tM issio n p la n e t = new P l a n e t M i s s i o n ( ) ; П ер ед тем как п ер е в е р н уть с тр а
M e s s a g e B o x . Show ( p l a n e t . F u e lN e e d e d ( ) ) ; — — ницу, п о д у м ай те, что п р о и зо й д е т
} п о с л е щ е л ч к а н а т р е т ь е й к н о п к е ...
дальше ► 311
абстрактные классы избегают порядка
..... ...r.J
Search for more Help Onlftie..,
Actions
View DetaiL.,
Copy ocep tion detaiHo the cffpboarci
Ш ТУРМ
Вернитесь к программе расчета стоимости мероприятий на с. 270-272
и подумайте над проблемой инкапсуляции. Придумайте ее решение при
помощи абстрактных классов.
312 глава 7
интерфейсы и абстрактные классы
pi^lic(^abstrac^void SetMissionlnfo (
int milesToPlanet, int rocketFuelPerMile,
long rocketSpeedMPH);
1лрогрйММй нб ь-а
c la ss V enus : P la n e tM issio n {
Наследуя от
абстрактно
p u b lic V enus О { го класса, нужно
перекрыть осе
S e t M is s in ln f o (40000000, 100000, 25000) его абстрактные
} spr методы.
p u b lic o v e r r id e S e tM is sio n ln fo (in t m ile sT o P la n e t, lo n g r o c k e tF u e lP e r M ile ,
in t rocketSpeedM P H ) {
th is.M ile sT o P la n e t = m ile sT o P la n e t;
th is-R o c k e tF u e lP e r M ile = r o c k e tF u e lP e r M ile ;
th is.R o ck etS p eed M P H = rock etS peed M P H ;
}
дальше > 313
стоит тысячи слов
Дано: Диаграмма
( in t e r f a c e )
1) in terfa ce Foo { } 1) F oo
c la ss Bar : Foo { }
2) in terfa ce V in n { } 2)
ab stract c la ss Vout : V in n { }
in te rfa c e W h u ffie { }
4) 4)
c la ss Zoop { }
c la ss Boop : Zoop { )
c la ss G oop : Boop { }
5) 5)
c la ss Gamma : D e lta , E p silo n { }
in te rfa c e E p silo n { }
in te rfa c e B eta { }
c la ss A lp h a : G a m m a ,B e ta { }
c la ss D e lta { }
314 глава 7
интерфейсы и абстрактные классы
А в этом задании слева приведены диаграммы классов, вам же нужно превратить их в объявления,
как показано в первом примере.
Дано: Объявление
О бозначения
наследует
; реализует
I
C la c k ^ i класс
Clack i интерф ейс
C la c k абстрактный
класс
\
~7К
Л- (in terfa ce '!
Boop
Vout M u ffie B eta
/s /Ч
Goop
Fluffie A lp h a
Готовые диаграммы
316 глава 7
интерфейсы и абстрактные классы
дальше * 317
проблемы множественного наследования
MoviePlayer ✓
int ScreenWidth
ShowAMovieO
И Television (Телеви -
дение) и MovieTkeater
(К и н о т е а т р ) на
следуют от класса
MoviePlayer (По- — ^
каз кино), и ода эти Television MovieTheater M o v ie T h e jx te r ^
класса перекрывают
метод ShowAMovieO каждого ^ произойдет,
(Показ дрильма). ShowAMovieO ShowAMovieO значение. Ч ^ gYi^eater (
и л и класс ^o m e i
К-роме того, они на
следуют свойство рамный значения
ScreenW idth (Ширина
экрана).
фил&мм?
■егр
избегайте неопределенности!
в языках, допускающих смертельный ромб, возможны крайне неприятные си
туации, так как для работы с подобными неопределенностями требуются специ
альные правила... А это означает дополнительные усилия при написании про
граммы! C# позволяет этого избежать. Сделайте T e l e v i s i o n и M o v ie T h e a te r
интерфейсами, и вам хватит одного метода ShowAMovie (). Главное, чтобы этот
метод присутствовал в указанном вами месте.
318 глава 7
интерфейсы и абстрактные классы
ебус Б б ассей н е
N ose { c la ss : .............................{
p u b lic A cts О : b a se(" A cts" ) { }
str in g Face { get; } p u b lic o v e rr id e {
} retu rn 5;
c la ss : {
p u b lic C low n s 0 : b a s e ( " C lo w n s" ) { }
}
К аж д ы й ф р а г
м е н т ко д а м о ж но
и с п о л ь зо в а ть
н е с ко л ь ко раз.
QniBem на с. 3 3 7 -
в форме... ведра с орлами
данные
Думаю, что теперь ^ код в классы на мо~
я хорошо умею появления
оыла революционной,
управлять объектами! но теперь это вполне
обычная практика про
граммирования.
В ы о б ъ е к т н о -о р и е н т и р о в а н н ы й п р о г р а м м и с т .
То, чем вы занимаетесь, называется о б ъ е к т н о - о р и е н т и -
р о в а н н ы м п р о г р а м м и р о в а н и е м (ООР). До появления
таких языков, как С#, объекты и методы при написании
кода не использовались. Применялись функции (кото
рые в тех языках назывались методами), сосредоточен
ные в одном месте. Можно сказать, что каждая программа
имела один статический класс, наполненный статически
ми методами. Писать программы было намного сложнее.
К счастью, вам не придется писать программы без ООР,
так как это ключевая часть языка С#.
Инкапсуляция
Это слово буквально
означает «множе
ство форм». Мо
ж е т е ли вы пред
Абстракция ставить ситуацию,
Создание модели, начинающейся когда объект прини
с более общих - абст ракт м ает разные ф ор
ні,,^ __ классов, от которых
наследуют более подродн ы е^^^^/
Полиморфизм мы?
классы.
320 глава 7
интерфейсы и абстрактные классы
if (w orker is H iv e M a in ta in e r ) { w orker.
H iv e M a in ta in e r m a in ta in e r = w orker as H iv e M a in ta in e r ;
дальше * 321
приступим
Длинные
упражнения
Давайте построим дом! В модели дома классы будут представлять комнаты
и прочие помещения, а интерфейс пусть соответствует двери.
Класс Location
Н ачнем с м о д ел и классов ''•абстрактный.
Каждая комната и помещение должны быть представлены ' Именно поэт о
отдельным объектом. Внутренние комнаты наследуют от м у на диаграм-
класса R o o m (Комната), а внешние от класса O u t s i d e (Сна iMe он серый.
ружи). Для этих классов в свою очередь имеется базовый
класс L o c a t i o n (Помещение) с двумя полями: N a m e для
названия помещения (Кухня) и E x i t s (Выходы) массив
объектов, связанный с отдельными помещениями. В итоге
запись d i n i n g R o o m . N a m e будет иметь значение D i n i n g Room Outside
R o o m (Столовая), а запись d i n i n g R o o m . E x i t s будет соот
Decoration Hot
ветствовать массиву { L i v i n g R o o m , K i t c h e n }.
Переместиться
с лужайки на зад
ний двор можно
через сад.
Это дверь, ведущая из
гостиной на лужайку. Все комнаты им ею т
Существует также двери, но только не
дверь из кухни на зад которые двери ведут
ний двор. наружу.
322 глава 7
интерфейсы и абстрактные классы
Класс Location
Вот код для абстрактного класса L o c a tio n :
поля nam e, которое является
a b stra ct c la ss L o c a tio n {
'предназначенным только для
p u b lic L o c a tio n (str in g nam e ) { чтения полем свойства Name.
М ет од t h i s , name = nam e;
Description вир- }
т уальны й,его p u b lic L o c a t i o n [] E x its;
Открытое поле Exits являет
нужно пере- ся массивом ссылок Location,
крыть. p r iv a te str in g name; который отслеживает, какие
\ p u b l i c s t r i n g Name {
}
g e t { r e tu r n nam e;
помеш,ения связаны с т ем , в
котором находитесь вы.
/
d a ss O u tsid eW ith D oo r : O u tsid e, IH a sE x te r io r D o o r
{ Это будет очень
/ / Тут б у д е т с в о й с т в о D o o r L o c a t i o n (П олож ение д в е р и ) большое упражне
/ / А з д е с ь с в о й с т в о D o o r D e s c r i p t i o n (О писание дв ер и )
ние... но мы обеща
ем, что вы получите
c la ss R oom W ithD oor : Room, IH a sE x te r io r D o o r удовольствие! и обя
зательно запомните
// Т у т б у д е т с в о й с т в о D o o r L o c a t i o n (П о л о ж е н и е д в е р и ) новый материал.
// А з д е с ь с в о й с т в о D o o r D e s c r i p t i o n (О писани е д в е р и )
дальше У 323
понаблюдаем за поведением объектов
Длинные
упражнения
Пришло время создать объекты, представляющие различные части дома, и добавить
форму для работы с ними.
К ак работаю т объекты
Рассмотрим архитектуру объектов f r o n t Y a r d и d i n i n g R o o m . Из-за наличия дверей они долж
ны быть экземплярами класса, реализующего I H a s E x t e r i o r D o o r . Свойство D o o r L o c a t i o n
хранит ссылку на помещение, расположенное по другую стороны двери.
F r o n tV a r d - эт о
объект класса LivinßRoom это экзем
f OutsideWithDoor, пляр класса RoomVJithDoor,
производного от производного от клас
класса Outside са Room и реализующего
и реализую щ его IHasExteriorDoor.
IHasExteriorDoor.
DoorLocation
DoorLocation
ExitsO Ви начали создание интерфейса \ Exitsfl !
IHasExteriorDoor и добавили два реали J
зующих его класса. Один из них наследу- £)àts — массив ссылок на
ет от класса Room, другой—от класса прилежащие помещения.
Outside. О бьект LivingRoom имеет
О Закончим создание классов и создадим и х экземпляры р Т б н Т і,
Практически все готово для построения объектов. Вам осталось:
★ Убедиться, что конструктор класса O u t s i d e задает предназначенное только для чтения
свойство H o t и перекрывает свойство D e s c r i p t i o n , добавляя текст «Тут очень жарко»,
когда переменная H o t имеет значение t r u e . Ж арко должно быть на заднем дворе, но не на
лужайке и не в саду.
★ Конструктор класса R o o m должен задавать свойство D é c o r a t i o n и перекрывать свойство
D e s c r i p t i o n , добавляя «Здесь вы видите (интерьер)». В гостиной находится старинный ко
вер, в столовой —хрустальная люстра, а на кухне —плита из нержавеющей стали и сетчатая
дверь, ведущая на задний двор.
★ Форма должна создавать объекты и хранить на них ссылки. Добавьте метод C r e a t e -
O b j e c t s О , который будет вызываться конструктором формы. к д
^ Создайте по экземпляру для шести помещений дома. Вот пример для гостиной: мещение будет
Exits (массив R oo m W ith D oo r l i v i n g R o o m = n e w R o o m W ith D o o r ( " Г о с т и н а я " , имет ь соб
ссылок Location), ственное поле 6
"старинны й ковер" , " дубов ая д в ер ь с л а т у н н о й р у ч к о й " );
создает массив классе формы.
из двух строк. Метод C r e a t e O b j e c t s {) должен добавлять поле E x i t s [] к каждому объекту:
о П о стр о ен и е qx>pMu
Создадим простую форму для экскурсии по дому. Потребуется большое текстовое поле
d e s c r i p t i o n , в котором будут появляться описания помещения. Элемент C o m b o B o x
с именем e x i t s содержит список выходов из комнаты. Кнопка д о Н е г е перемещает вас
в помещение, выбранное в C o m b o B o x , а кнопка g o T h r o u g h T h e D o o r появляется при на
личии выхода наружу.
W Вфз» the Hause Г Г Й Ь в о .
задается зЭес&-
дает
Индекс элемента, выбран-
списке, совпа-
t
класса Location. Хотя она указывает на объект
реализующий интерфейс IHasExteriorDoor на
писать «currentLocation.PoorLocation» нельзя
Оает с индексом соответствиюшЛп так как PoorLocation не является полем класса
•помещения в массиве ExitsU
Location. Вам потребуется нисходящее приве-
"ргшение
длинных
ПраЖНеНИЙ Вот код для модели дома. Комнаты и другие помещения представлены с помо
щью классов, в то время как интерфейс соответствует дверям.
in te rfa c e IH a sE x te r io r D o o r {
strin g D o o r D e sc rip tio n { get; } Это интерфе.йс
iHasExteriorPoor.
L o c a tio n D o o r L o c a tio n { get; set; }
}
326 глава 7
интерфейсы и абстрактные классы
c la ss O u tsid e ; L o c a tio n
p r iv a te b o o l h ot;
p u b lic b o o l Hot { get { retu rn h o t; } } Класс Outside во многом
подобен классу Room. Он
p u b lic O u tsid e (str in g nam e, bool h ot) тоже является производ
: base(n am e) ным от класса Location
и добавляет вспомога
{ тельное поле для свой
th is.h o t = h ot; ства Hot. Это свойство
используется в методе
DescriptionQ.
p u b lic o v e r r id e strin g D e sc rip tio n
get {
str in g N e w D e sc r ip tio n = b a s e .D e sc rip tio n ;
if (h ot)
N e w D e sc r ip tio n += " Очень ж а р к о ." ;
r e tu r n N e w D e sc r ip tio n ;
дальше ► 327
решение упражнения
решение
длинных
ПраЖНеНИЙ Это код формы, расположенный в файле Form1.cs, внутри объявления Forml
328 глава 7
интерфейсы и абстрактные классы
}
Оператор as осуществляет нис-
)содящее приведение currentLocation
к IHasExteriorDoor, что дает нам
доступ к полю DoorLocation.
О Д ополнительны е колонаты
Обновите метод C re a te O b j e c t s (), чтобы создать больше комнат:
★ Добавьте лестницу с деревянными перилами, соединяющую гостиную с к о р и д о р о м вто
р о г о э т а ж а , где висит картина с собакой и стоит шкаф.
★ Верхний коридор ведет в три комнаты: г л а в н у ю с п а л ь н ю с большой кроватью, в т о р у ю
спальню с маленькой кроватью и в а н н у ю с раковиной и туалетом. Прятаться можно как
под кроватями, так и в душе.
★ Передний и задний дворы соединены п р оезд ом с гаражом, пригодным для укрытия.
Прятаться можно и в сарае.
330 глава 7
интерфейсы и абстрактные классы
О О бновляем qx>pмy
Создадим несколько новых кнопок. Они будут появляться и исчезать, в зависимости от
того, на какой стадии находится игра.
Гюедтя кнопка называется
В е р х н и е две кноп ^ А
ки и
с п и с о к бцдут видимы
только 0 игре- ^
°^о5раж ается
^^олько кнопка Hide (Прячься!).
После щелчка на ней в т ек-
стовом поле идет отсчет
г
м десять сваивается
раз вызывается метод Movef) г г ^ о р ^ м ГлеЭуе^ " 1 ? Г х о Э и -
места. Например, появится
S u l ? e
З аставим кнопки работать триМ под кроватью).
Появились две новые кнопки.
В главе 2 t
уже вст ре Центральная кнопка становится видимой только когда вы находитесь в комнате
чали м ет о с местом для укрытия. Она ищет соперника при помощи метода C h e c k { ) . Если
ды DoEventsO вы его находите, игра перезапускается.
и SleepO, ко
торые будут Нижняя кнопка запускает игру. В текстовом поле с задержкой 200 миллисекунд
использоваться появляются цифры от 1 до 10. После каждой из них соперник перемещается
в данном случае. при помощи метода M o v e (). Затем на полсекунды появляется надпись «Я иду ис
кать!», и игра начинается.
дальше ► 331
решение упражнения
c la ss R o o m W ith H id in g P la c e : Room , IH id in g P la ce {
p u b lic R o o m W ith H id in g P la c e (s tr in g nam e, strin g d e c o ra tio n , str in g h id in g P la ce N a m e )
: b ase(n am e, d e co ra tio n )
{
th is.h id in g P la c e N a m e = h id in g P la ce N a m e ;
К л а с с RoomW ithHidingPlace
} наследует от класса Room
p riv a te str in g h id in g P la ce N a m e ; и реа ли зует инт ерф ейс ^
p u b lic strin g H id in g P la ceN a m e {
IHidingPlace, добавляя свойство
HidingPlaceName. Д анное вспо
get { r e t u r n h id in g P la ce N a m e ; } м огат ельное поле задается
} конст рукт ором .
p u b lic o v e r r id e str in g D e sc rip tio n {
get {
retu rn b a se .D e sc r ip tio n + " Спрятаться
}
) }
c la ss R oom W ithD oor : R o o m W ith H id in g P la c e , IH a sE x te r io r D o o r {
p u b lic R o o m W ith D o o r (s tr in g nam e, str in g d e c o ra tio n ,
s t r i n g h id in g P la ce N a m e , s t r i n g d o o r D escrip tio n )
: b ase(n am e, d e c o r a t i o n , h id in g P la ce N a m e )
{ укры мия было решено
t h is .d o o rD escrip tio n = d o o rD e scrip tio n ; в Комнаты с внеш-
} ними дверями, мы сделали нКласс
RoomWithDoor производным от
p riv a te str in g d o o r D esc rip tio n ; RoomWithHidingPlace. Теперь
p u b lic str in g D o o r D e sc rip tio n {
конструкт ор этого производ-
get { retu rn d o o rD e scrip tio n ; } Тк7ом ТпТ название
укромного мест а конст рикт о
} ру R-oomWithHidingPlace
p r iv a te L o c a tio n d o o r L o c a tio n ;
p u b lic L o c a tio n D o o rL o ca tio n {
get { r e tu r n d o o r L o c a tio n ; }
set { d o o r L o c a tio n = v a lu e ; }
}
332 глава 7
интерфейсы и абстрактные классы
p r iv a te str in g h id in g P la c e N a m e ;
p u b lic str in g H id in g P la ceN a m e {
Класс OutsideWithHidingPlace наследует
get { r e t u r n h id in g P la ce N a m e ; } от класса Outside и реализует
} метод IHidingPlace аналогично классу
RoomWithHidingPlace.
p u b lic o v e rr id e str in g D escrip tio n {
get {
retu rn b a se .D e sc r ip tio n + Можно с п р я т а т ь с я " + h id in g P la ce N a m e +
дальше ► 333
решение упражнения
p u b lic F o rm lО {
O u tsid eW ith D oo r fr o n tY a r d ;
In itia liz e C o m p o n e n tO ;
O u tsid eW ith D o o r backY ard;
C reateO b jects О ;
O u tsid e W ith H id in g P la c e g ard en ;
o p p o n e n t = new O p p o n e n t ( f r o n t Y a r d ) ;
O u tsid e W ith H id in g P la c e d riv ew a y ;
R ese tG a m e (fa lse );
}
p r i v a t e v o i d M oveT o A N ew L o ca tio n (L o ca tio n n e w L o c a tio n )
M oves++;
c u r r e n tL o c a tio n = n ew L o ca tio n ;
Redraw Form 0 ; _
I Hemod MoveToANevJLocationQ
задает новое положение и пере
рисовывает форму-
p r iv a te v o id R edraw Form О { ^
e x i t s . I t e m s . C l e a r () ;
fo r (in t і = 0; і < c u r r e n tL o c a tio n .E x its.L e n g th ; i+ + )
e x its .Ite m s.A d d (c u r r e n tL o c a tio n .E x its[i].N a m e );
e x i t s . S e l e c t e d l n d e x = 0;
334 глава 7
интерфейсы и абстрактные классы
M oves = 0;
h id e .V isib le = tru e;
g o H e r e .V isib le = fa lse ;
«Hide!», невидимыми кнопки
c h e c k .V isib le = fa lse ;
g o T h r o u g h T h e D o o r .V is ib le = fa lse ;
Нам нужно отобразить имя
e x its .V is ib le = fa lse ;
}
p r iv a te v o id c h e ck _ C lic k (o b je c t sender,
M oves++;
if (o p p o n en t. C h e c k (c u r r e n tL o c a tio n ))
R esetG am e(tru e);
e lse
Кнопка check проверяет, не
Redraw Form ( ) ; прячется ли соперник в комна
} те, где вы находитесь. При его
обнаружении перезагружает
p r iv a te v o id h id e _ C lic k (o b je c t sen d er, E ven tA rgs e) {
игру. Не обнаружив его, перери
h id e .V isib le = fa lse ;
совывает форму (чтобы обно
вить число ходов).
fo r (in t i = 1; i <= 1 0 ; 1++) {
o p p o n e n t.M ove();
Помните обработчик событий
d e sc r ip tio n .T e x t = i + " ... ^oEventsO главы а.? Именно
A p p l i c a t i o n . D o E v e n t s () ; — ------- --------- он отвечает за обновление ин-
S y s t e m . T h r e a d i n g . T h r e a d . S l e e p (2 0 0 ) ; <рормации в текстовом поле.
}
Игра начинается с кнопки «Н/^е?» Сначала
d e sc r ip tio n .T e x t = "Я и д у и с к а т ь ! " ; кнопка становится невидимой. Зат ем о к л ю -
A p p lic a tio n .D o E v e n tsО ; чается счет до Ю и сопернику говорится,
S y s te m .T h r e a d in g .T h r e a d .S le e p (5 0 0 ); что нужно спрятаться. Напоследок, видимы
ми становятся первая кнопка и раскрываю-
ш,ийся список, и игрок помеш,ается в гост и
g o H e r e .V isib le = tru e;
ную. М етод МоуеТоАЫе\миосаИоп() вызывает
e x its .V is ib le = tru e; метод Кес1га\л/Рогт().
M o v eT o A N ew L o ca tio n (liv in g R o o m );
интерфейсы и абстрактные классы
І^еіИение Б бассейне со с. ^ 9
Вам требовалось взять фрагменты кода из бассейна
и поместить их на пустые строки таким образом,
чтобы получить показанный ниже результат.
r они
OHM сосредото- Л
i [0] = new A c t s ( ) ;
p u b lic v ir tu a l str in g Face { с бе рК Ц , HO о i [1 ] = new C low n s 0 ;
___ _ ! ___ ______ _________
get { ....retHrP.-f!“?.®
. 1 ^
даиноМ случае M t>i
АЛІ
i [ 2 ] = new Of 76 0 ;
} поместили сбои- f o r ( i n t X = 0; X < 3; x++) {
str in g face; ство face в ниж
нюю часть класса r e su lt += ( ..iM-.Eqr.O........+ " "
} + i[xi.Face ) "\п"
Picasso.
}
c la ss Clowns : Picasso { M e s sa g e B o x .S h o w (r e s u lt);
p u b l i c C lo w n s О : b a s e ( " C lo w n s" ) { }
} Face -- это метод записи,
возвраш,аюш,ий значения свой
ства face. И метод, и свой
ство определены в классе
Picasso и наследуются произ
водными классами.
дальше ► 337
8 перечисление и Коллекции
340 глава 8
перечисления и коллекции
Перечисления
Э т о имя перечисления.
у^араттры типа
Вы не можетие взять и создать новое значение для Worker. Jobs.
перечисления! Программа не будет компилироваться.
p r iv a te v o id b u tto n l_ C lic k (o b je c t s e n d e r E v e n tA r g s e)
{
W orker b u z z = new W o r k e r (J o b s . A t t o r n e y G e n e r a l ) ;
Сообщение об
342 глава 8
перечисления и коллекции
C ard c a r d = new C a r d ( S u i t s . S p a d e s , V a l u e s . A c e ) ;
str in g cardN am e = c a r d .N a m e ; 4mO$fc>i э т о заработало.
в класс C ard нужно п о
П е р е м е н н а я c a r d N a m e д о л ж н а и м е т ь з н а ч е н и е “А с е o f S p a d e s ”. м е т и т ь конст рукт ор
с Эбумя п а р а м е т р а м и .
К н о п ка , отображаю щ оя название случайной карты
Будем выбирать карту, случайным образом присваивая число от О до 3 переменной
S u i t s , а числа от 1 до 13 - переменной V a l u e s . Воспользуемся встроенным классом
R a n d o m , позволяющим вызвать метод N e x t () тремя способами:
дальше ► 343
массивы... ному они нуж ны?
D iam on d s, \
к единще, т рет ий к двойке и т. д.
H earts
}
c la ss Card
p u b li lu it { get; set; }
p u b lic V alu e { get; set; }
344 глава 8
перечисления и коллекции
public void P r i n t C a r d s O {
for (int 1 = 0; i < cards .Length; i-i--i-;
C o n s o l e . W r i t e L i n e ( c a r d s [ i ] . N a m e ()]
}
ШТУРМ
Как бы вы д о б ав и л и м етод s h u f f l e О , м од елирую щ ий процесс т а
сован и я к о л о д ы ? Как см о д е л и р овать п р оце сс сдачи к а р т ? Каким о б
разом д о б а в и ть карты в к о л о д у ?
Каждый массив имеет длину, которую вы должны знать. Для получения пустых
элементов можно воспользоваться ссылкой на неопределенное значение null:
П а р а м е т р L en g th э т о
г о м а с с и в а р а в е н 7 , но
в нем хранит ся и н ф о р
мация т олько о т рех
карт ах.
346 глава 8
перечисления и коллекции
Коллекции
в .NET Framework существует множество классов коллекций (collection), позволяющих легко решить
вопросы с добавлением и удалением элементов массива. Чаще всего используется коллекция L ist< T > .
L ist< T > позволяет легко добавить, удалить, выбрать элемент и даже поменять порядок следования
элементов.
^ент
дальше > 347
какой прогресс!
Коллекции List
lOiacc L i s t встроен в .NET Framework и позволяет делать вещи,
о которых вы не могли даже мечтать, имея в арсенале только ста
рые добрые массивы. Перечислим новые возможности:
L ist 6 ^ g объект е
О М ож но создать ко л л е кц и ю
L ist< E g g > m yC arton = new L ist< E g g > ();
m y C a rto n . A dd(у );
©
увеличивается
принят ь ^
0<^орой обьект Е дд.
/
Q Удалить оттуда элем ент
m y C a r to n .R em o v e( у ) ;
^ 1 )а J —
р м е р « о « '“ ““ “
348 глава 8
перечисления и коллекции
Возьми в руку карандаш Заполните таблицу, сверяясь с расположенным слева кодом. Вам нужно
показать, как выглядел бы код, если бы вместо коллекции использовался
массив. Мы не ожидаем от вас 1 0 0 % правильных результатов, просто по
пытайтесь догадаться.
Предположим, ч т о эти
операторы выполняются Мы начали выполнять
по порядку. упражнение...
\ Коллекция Обычный массив
N/
L is t < S t r in g > m y L ist = S t r in g и m y L is t = n e w S tn n g [ Z ] ;
new L i s t < S t r i n g > ( ) ;
in t th e S iz e = m y L ist.C o u n t;
G uy о = m y L i s t [ 1 ] ;
Подсказка: П от ре
бует ся более одной
ст роки кода.
щЫьт в руку карандаш Итак, вот как выглядит правильный вариант кода с использова
нием вместо коллекций обычных массивов.
Решение
Коллекция Обычный массив
L is t < S t r in g > m yL ist = String [] myList = new String [2];
new L i s t < S t r i n g > { ) ;
in t th e S iz e = m y L is t. C ou n t;
in t th e S ize - m y L iSt. L e n g t h ;
G uy o = m y L i s t [ 1 ] ;
Guy 0 - m y L ist[l];
Коллекции используют методы, как и уже зна Массивы довольно сильно вас ограничивают.
комые вам классы. Чтобы увидеть список до В момент создания массива требуется указать
ступных методов, введите . после имени L i s t . его размер, а код для нужных процедур при
Параметры методам передаются так же, как ходится писать вручную.
это делалось для созданных вами классов.
/К
350 глава 8
перечисления и коллекции
Обобщенные коллекции
Вы уже видели, что коллекция может состоять как из
строк, так и из ботинок. Можно поместить туда целые
числа или произвольные созданные вами объекты.
Именно поэтому класс L i s t является о б о б щ е н н о й
коллекцией. В момент его создания нужно указать
КЛЮЧЕВЫЕ
тип значений, которые могут храниться внутри.
МОМЕНТЫ
Эта запись не означает, что вы добавляете букву Т. L i s t — ЭТО класс .NET Framework.
Она всего лииль показывает, что класс или инт ер
фейс может работать со значениями любого типа, Класс L i s t динамически меняет свой
(достаточно указат ь эт от т ип в скобках). З а размер.
пись List<Shoe> означает, что коллекция содержит
только элементы класса Shoe. _ Для добавления элементов в класс L i s t
используйте метод A d d ( ) . Удалить
List<T> name = new List<T>() элементы можно с помощью метода
R e m o ve ().
Класс List очень
KUM (^озволяю и^м лю дш m Метод R em oveA t () позволяет удалять
объекты, начиная с заданного индекса.
^ Т м о Т у т ^ а с с и в ы (плюс eu,e кое-что).
Тип значений, которые могут храниться в
коллекции, указывается в угловых скобках.
В .NET Framework существуют обобщенные интер)- Запись L i s t < F r o g > означает, что в
фейсы, позволяющие созданным вами коллекциям коллекции L i s t могут храниться только
работать с любыми типами. Класс L i s t реализует объекгы класса F r o g .
эти интерфейсы, именно поэтому работа с коллек
цией целых чисел практически не отличается от ра Метод In d e x O f () определяет индекс
боты с коллекцией объектов класса Shoe. для заданного объекта.
352 глава 8
перечисления и коллекции
|^ а Г н и т ь 1 С К оД »М
В о с п о л ь з у й т е с ь ф р а гм е н та м и кода
д л я р еко нстр укц и и ф о р м ы с кнопкой,
н аж ати е которой б уд ет в ы в о д и ть п о
к а за н н о е ниж е окно с с о о б щ е н и е м .
a . R e m o v e A t (2 );
a .A d d (fir s t);
a .A d d (se c o n d );
a.Add(third)_;^______
..- ...........ге зи 1 Г Т ~ ^ Г Г Г ~ |
if (a.Contains ("three") ){ Iswu^l
-1 _T / w ^ ____ \ -
a.Add("four");
}
in a)
{
re s u lt
MessageBox.Show(result:
(a.indexOf("four") ГГ"
^ a.Add(fourth);
li^ 5 5
[p ri^ tL T ^
string zilch = "zero";
string first = "one";
zero
one string second = "two";
three
four string third = "three";
42 string fourth = "4.2";
string twopointtwo = "2.2";
I OK 1
дальше ► 353
решение упражнения
П ом ните, в главе 3 го ворилось об и н туи
ти в н о п онятны х им енах? С ей час мы их с о
l^e^eHue 5аДаЧи зн ател ь н о изб егаем , та к как это слиш ком
у п р о щ а е т реш ени е ребуса. Но в ж и зн и такие
с МаГнищаМи им ена, как рпп1Ь(), и спользовать не стоит!
{
List<string> а = new List<string>
string zilch = "zero";
string first = "one";
zero string second = "two";
one
three
string third = "three";
four string fourth = " 4 . 2 " ;
4.2
string twopointtwo = "2.2";
Sb/ понимаете,
OK
a .
if
W W 'fi'r" '" "
Add(second) ;
- ------
(a.Contains("three")){
I почему « z . z »
никогда не п о
падет в кол
лекцию. несмо
a . A d d ("four"); т ря на то что
эт от элемент
} оыл объявлен?
354 глава 8
перечисления и коллекции
Ч аст»
ЧаДаБаеМые
Б о и р о с :^ ,!
Разве перечисления и класс L i s t Возможна ли коллекция, не имею
выполняют
ВЫ1 не одну и ту же задачу? щая типа?
ща5
Вы можете преобразовать коллек
инициализаторы коллекций
C # п о з в о л я е т у м е н ь ш и т ь к о л и ч е с т в о в в о д и м о го т е к с т а п р и с о з д а н и и к о л л е к ц и и . В ы м о ж е те в о с п о л ь з о
в а ть ся и н и ц и а л и з а т о р о м к о л л е к ц и й ( c o l l e c t i o n in i t i a li z e r ) , к о т о р ы й д о б а в л я е т э л е м е н ты в к о л л е к ц и ю
н е п о с р е д с т в е н н о в м о м е н т ее со зд а н и я . ^ „
Этот код вы видели несколько
страниц назад. Он создает
объект List<Shoe> и заполняет
к/ его объектами Shoe.
Llst<Shoe> shoeCloset = new List<Shoe>();
shoeCloset.Add(new ShoeO { Style = Style.Sneakers, Color = "Черный" });
shoeCloset.Add(new ShoeO { Style = Style.Clogs, Color = "Коричневый" });
shoeCloset.Add(new ShoeO { Style = Style.Wingtips, Color = "Черный" });
shoeCloset.Add(new ShoeO { Style = Style.Loafers. Color = "Белый" });
shoeCloset.Add(new ShoeO { Style = Style.Loafers, Color = "Красный" });
shoeCloset.Add(new ShoeO { Style = Style.Sneakers, Color = "Зеленый" });
Инициализатор коллекции
Обратите внимание: может быть создан добав
каждому объекту Shoe лением каждого полученного
присваивается началь при помощи метода AddQ
ное значение при помощи элемента к оператору,
инициализатора. формирующ ему коллекцию.
К аж дая у т к а и м е е т р а з м е р ,
эт а — 1 7 дю йм ов.
У т ки бы ваю т
д и к и е ... ( m a l l a r d s ) .
c la s s D uck {
p u b lic in t S iz e ;
p u b lic K in d O fD u ck K in d ;
И даж е д е р е в я н н ы е
м ан к и (d e c o y s) }
М ускусны е ут к и К л а с с и м е е т два о т
(M u sc o vy ).
I кры т ы х поля и м е т о
ды , к о т о р ы е з д е с ь не
показы ваю т ся.
't m i'
enum K in d O fD u ck {
M a lla r d ,
инициализатор коллекции уток M uscovy,
у нас ш есть уток, п о это м у мы со зд а д и м к ол л ек ц и ю L is t < D u c k > D ecoy,
с состоящ им из ш ести операторов инициализатором . К аж
}
д ы й и з э т и х о п е р а т о р о в с о з д а е т н о в у ю утку, у к а з ы в а я з н а ч е н и я
п о л я s i z e и K i n d . Д о б а в ь т е э т о т к о д в м е т о д M a i n () в ф а й л е
П еречи слен и е
P r o g r a m .c s :
K in d O fD u c k х р а н и т и н
ф о р м а ц и ю о видах у т о к
L ist< D u c k > ducks = new L ist< D u c k > О { Ö коллекции.
new D uckO { K in d = K in d O fD u c k .M u sc o v y , S iz e = 11 },
new D uckO { K in d = K in d O f D u c k . M al l a r d . S iz e = 14 },
M a in Q к о д , в ы в о д я щ и й р е з у л ь -
new D uckO { K in d = K in d O f D u c k . D e c o y , S iz e = 13 }, °ст ав-
^ л я ет р е з у л ь т а т видим ы м
^ — п о к а вы не н а ж м е т е к л а ви ш у.
с о р т и р о в к а п о в и д у у т к и .^ _
...UAU по Виду...
Сортировка объектов
осуществляется с помощью
интерфейса 1Сотрагег<Т>,
о котором мы поговорим
чут ь позже...
Встроенные методы copmupoBku
К а ж д а я к о л л е к ц и я с н а б ж е н а м е то д о м S o r t ( ) , м е н я ю щ и м п о р я д о к эл е м е н
т о в . С у щ е с тв у ю т за д а н н ы е с п о с о б ы с о р т и р о в к и б о л ь ш и н с т в а в с т р о е н н ы х
т и п о в и кл а ссов, к р о м е т о г о , м о ж н о н а п и с а т ь с п о с о б с о р т и р о в к и в а ш е го
с о б с т в е н н о го класса.
SortO
Сортировка всего \
лиш ь меняет по -
рядок расположе ^ е /г г Ы '
ния элементов. 17
I дюймов
358 глава 8
перечисления и коллекции
*
и н т е р ф е й с IC o m p a r a b le < D ( ie k >
М е т о д L i s t . S o r t О ум е е т с о р т и р о в а т ь о б ъ е к т ы л ю б о го т и п а и кл а ссы ,
к о т о р ы е р е а л и з у ю т и н т е р ф е й с 1 С о т р а г а Ы е < Т > . В со ста в е э т о г о и н т е р
Любой класс
ф е йса н а х о д и т с я т о л ь к о м е то д С о т р а г е Т о { ) . S o r t ( ) , к о т о р ы й и с п о л ь з у
е т м е то д С о т р а г е Т о () о б ъ е к та для с р а в н е н и я с д р у г и м и о б ъ е к та м и . В о з
может рабо
вращ аем ое з н а ч е н и е (т и п а in t ) п о ка зы в а е т, к т о д о л ж е н б ы т ь п е р в ы м .
тать со встро
Д ля к о л л е к ц и й о б ъ е кто в , к о т о р ы е н е р е а л и з у ю т и н т е р ф е й с IC o m p a -
г а Ы е < Т > , в .N E T им еется д р у го й сп о со б . М е т о д S o r t () м о ж н о передать э к енным методом
зем пляру класса, ре а л и зую щ е го IC o m p a r e r < T > . Э т о т и н т е р ф е й с т о ж е им еет
всего о д и н м етод и п о зв о л я е т задавать с п е ц и а л ьн ы е с п о с о б ы с о р т и р о в к и . Sort ( ) объекта
Метод СотрагеТоО сраВнибает два объекта List с помощью
М о ж н о о т р е д а к т и р о в а т ь класс D u c k т а к и м о б р а зо м , ч т о б ы о н н а ча л реа IComparable<T>
л и з о в ы в а т ь и н т е р ф е й с IC o m p a r a b le < D u c k > . Д л я э т о г о д о б а в и м м е то д
С о т р а г е Т о ( ) , и с п о л ь з у ю щ и й с с ы л к у н а о б ъ е к т D u c k в к а ч е с тв е пар а м е и СотрагеТо().
тр а . Е сл и утка , взя та я для с р а в н е н и я , д о л ж н а о ка за ть ся п о с л е а н а л и з и р у е
м о й у т к и , м е то д С о т р а г е Т о {) в о з в р а щ а е т п о л о ж и т е л ь н о е ч и с л о .
Реализуя 1СомрагаЫе<Т> J
c la s s D uck : IC o m p a r a b le< D u ck > { вы указываете т ип
p u b lic in t S iz e ; парамет ра, по которому
осуществляется сравнение.
p u b lic K in d O fD u c k K in d ;
d u c k s.S o r t ();
Способы copmupoßku
Д л я к о л л е к ц и й сущ е ств уе т с п е ц и а л ь н ы й в с т р о е н н ы й в .N E T
F ra m e w o rk и н т е р ф е й с , п о з в о л я ю щ и й со зда ть о т д е л ь н ы й класс для
Способ сортировки
с о р т и р о в к и с о с т а в л я ю щ и х о б ъ е к та L i s t < T > . Р е а л и з у я и н т е р ф е й с
IC o m p a r e r < T > , вы объясняете ко л л е кц ии, каким способом н уж н о
зависит от способа
у п о р я д о ч и т ь ее с о д е р ж и м о е . З адача в ы п о л н я е т с я с р е д с тв а м и м е то
да C o m p a re ( ) , к о т о р ы й б е р е т п а р а м е т р ы д в у х о б ъ е к т о в х и у и воз
реализации
в р а щ а е т ц ел ое ч и с л о . Е с л и х м е н ь ш е , чем у, в о зв р а щ а е тс я о т р и ц а
те л ь н о е ч и с л о . В случае и х р а в е н с тв а в о з в р а щ а е тс я н о л ь . Н у а е сли
интерфейса
X б ол ьш е , ч е м у , будет в о з в р а щ е н о п о л о ж и т е л ь н о е ч и с л о .
IComparer<T>.
В о т п р и м е р о б ъ я в л е н и я класса, с р а в н и в а ю щ е го о б ъ е к т ы D u c k п о
размеру. Д об авьте э т о т класс к сво е м у п р о е к ту , з
f r “ “
сортируемых объектов- Duck
\ Тип сравниваемых
class DuckComparerBySize : IComparer<Duck> значений всегда бу
дет совпадать.
{ public int Compare (Виск'^хГвис^^^Т^
{ м ет од
целое имело
^^сдоват ь зд долж ен
p означает совпадение
р«м еро6 У-
Ф
360 глава 8
перечисления и коллекции
м чи слени и
чем>>^и«метш1 «больш е, J
значение. Мы и с п о л ь з о в а ^ ^ ІТ
операт оры < и > Ъло . Т ^°^'^‘^ ^ские
} перечисления при cop T u p S Z Z ' k ^^ ^'
Дополнительный код
для метода MainQ.
дальше ► 361
выберите карту, любую карту
c o m p a r e r .S o r tB y = S o r t c r i t e r i a . S izeT h en K in d ;
d u ck s. Sort(com p arer) ;
много раз!
P rin tD u ck s(d u ck s);
362 глава 8
перечисления и коллекции
Конечны й результат
В о т к а к в ы гл я д и т о к н о вы вода:
дальше ► 363
ищите!
)‘^ешение
c la ss C ard C o m p a rer_ b y V a lu e : IC om parer<C ard> { ^ ^ ‘^ Р ^ е н н ы и
p u b lic in t C om p are(C ard x , Card y) {
(x .V a lu e < y .V a lu e ) {
Если X имеетл retu rn -1;
оольшее значение,
меилод возвра - (x .V a lu e > y .V a lu e ) {
щает 1 . Если retu rn 1;
значение меньше,
оозЬращается
-!■ Но оба опе (x .S u it < y .S u it) { Э ти операторы выполняются ^
ратора, возвра retu rn -1; только при совпадении значении
щающие значе х и ч , ведь первые два оператора,
ние, завершают возвращающие значение, в этом
(x .S u it > y .S u it) {
радоту метода. случае не выполняются.
retu rn 1;
}
retu rn 0;
Это обобщенная
sta tic v o id M a i n ( s t r i n g [] args) коллекция объектов
{ Card. Ее легко сор
R andom r a n d o m = n e w R a n d o m ( ) ; тироват ь с пом о
щью интерфейса
C o n s o l e . W r i t e L i n e ( "Пять с л у ч а й н ы х к а р т : " ) ;
IComparer.
L ist< C a rd > cards = n e w L i s t < C a r d > () ;
fo r (in t i = 0; i < 5; i+ + )
{
c a r d s.A d d (n e w C a r d ((S u its)r a n d o m .N e x t(4 ),
(V a lu e s )r a n d o m .N e x t(1 , 14)));
C o n so le .W r ite L in e (c a r d s[i].N a m e );
}
C o n so le . W r ite L in e ();
C o n s o l e . W r i t e L i n e ( "Те ж е к а р т ы , отсор ти рован н ы е:");
c a r d s . S o rt(n ew C a r d C o m p a r er _ b y V a lu e ( ) ) ;
foreach (C ard c a r d in cards)
{ Метод Console.ReadKeyO нужен, чтобы про
C o n so le .W r ite L in e (c a r d .N a m e ) грамма не закрывалась после завершения. Для
} реальных приложений он не подходит. При за
C o n so le.R e a d K ey 0 ; < .......... пуске программы по Ctrl-F5 она начинает рабо
тать без отладки. После завершения появляется
строка Press any key to continue... и приложение
ждет нажатия клавиши. Но отладки не происхо
дит, и точки останова с контрольными значени
ями не работают.
364 глава 8
перечисления и коллекции
В п р о гр а м м е с о р т и р о в к и у т о к п о м е с т и т е т о ч к у о с т а н о в а в м е то д M a in () п о с л е и н и ц и а л и з а ц и и ко л л е к- х
ц и и и з а п у с ти те отл а д ку п р о гр а м м ы . З атем н а в е д и т е у к а з а т е л ь м ы ш и н а л ю б у ю п е р е м е н н у ю d u c k s ,
ч т о б ы з н а ть ее з н а ч е н и е . У в и д е т ь п р и отл а дке с о д е р ж и м о е к о л л е к ц и и м о ж н о , щ е л к н у в н а к н о п к е со
зн а ко м слева о т и м е н и п е р е м е н н о й :
Вместо передачи зна
□ Ф d u cks
Count = 6 чения методам Console.
И Ф ducfes'C ount = 6 )
ш ^ т {MyProject.Dudî,} WriteLineO, String.
ш V [11 flHvProject.Dudt} FormatQ и т. п., мож
[2] {MyProject.Duck} но передать им обьект.
Метод ToStringO вызывается при от о Его мет од ToStringO
ш # И ^ y P ro jectD u ck }
бражении обьекта в окне Watch. Но будет вызван авт ом а
ш ФЫ flHyProject.Duck>
метод ToStringOJ унаследованный клас тически. (Это работает
ш é т {MyProject.Dudc}
сом Риск от класса Object, возвращает и для значимых типов,
только имя класса. Мы можем сделать ш # Raw View таких как int и enums!)
эт от метод более информативным.
И т а к , в ы в и д и т е , ч т о к о л л е к ц и я с о д е р ж и т ш е с т ь о б ъ е к т о в D u c k (M y P ro je c t — э т о п р о с т р а н с т в о и м е н ,
в к о т о р о м м ы н а х о д и м с я ). Щ е л ч о к н а к н о п к е (сл ева о т н о м е р а у г к и ) п о к а з ы в а е т з н а ч е н и я п а р а м е тр о в
K i n d и S i z e . Н о н е льзя л и сделать т а к , ч т о б ы вся и н ф о р м а ц и я п о к а з ы в а л а с ь о д н о в р е м е н н о ?
T o S t r i n g О - э т о в и р т у а л ь н ы й м е то д класса O b j e c t , к о т о р ы й я в л я е тс я б а зо в ы м п о о т н о ш е н и ю к л ю
бом у объекту. Т а к ч т о вам о с та е тс я т о л ь к о п е р е к р ы т ь м е т о д T o S t r i n g O , и в ы у в и д и т е р е зул ь та ты
в о к н е w a tc h ! О т к р о й т е класс D u c k и до б а вьте н о в ы й м е то д с к л ю ч е в ы м с л о в о м o v e r r i d e . П о с л е на
ж а т и я П р о б е л а п о я в и т с я с п и с о к д о с т у п н ы х для п е р е к р ы т и я м е то д о в :
override
= ♦ E q u a l s ( o b j e c t o b j)
G e tH a s h C o d e O
В ы б е р и т е в а р и а н т T o S t r i n g () и за м е н и те с о д е р ж и м о е м е то д а в о т э ти м :
p u b lic o v e r r id e str in g T o S tr in g O
{
retu rn "A " + S i z e + " in ch " + K in d .T o S tr in g 0 ;
З а п у с ти т е п р о гр а м м у и с н о в а п о с м о т р и т е н а к о л л е к ц и ю . Т е п е р ь в ы в и д и т е все с в е д е н и я о D u c k !
В # ducks C ount = 6
Чтобы показать обьект,
Ш t [0] {A 17m di Maliard} отладчик вызывает метод
Ш {A 18 iid i №jscovy> ToStringO-
Ш 9 m {A H in d i Decoy)
ffl Ф т {A 11 inch Misrnvy}
Ш Ф [4] {A 14indiMa8ard}
ffl #151 {A 13 indi Decoy} дальше > 365
Ш Ч) Raw ¥iew
цикл foreach
f o r e a c h (C a rd c a r d i n c a r d s )
^ У мм инание о методе
C o n s o le . W r i t e L i n e ( c a r d . N am e) ; -ToStringO в данном с л у -
} чае Можно опуст ит ь,
, оператор + вызовет его
М е т о д P r i n t D u c k s () в ы п о л н я л а н а л о г и ч н у ю ф у н к ц и ю для о б ъ е к то в D u c k : ‘^отоматически-
fo r e a c h (D uck d u c k i n ducks) /
^ C o n s o le . W r it e L in e ( d u c k . S iz e . T o S t r in g O + " - in c h " + d u c k .K in d .T o S tr in g O ) ;
Т е п е р ь, ко гд а о б ъ е к т D u c k п о л у ч и л с в о й м е то д T o S t r i n g ( ) , м е то д P r i n t D u c k s () д о л ж е н и с п о л ь з о
ва ть э т о п р е и м у щ е с тв о :
366 глава 8
перечисления и коллекции
Д и к Л ГоГеаЛ П о д
интерфейс IEnumerable<T> уБеЛиЧитпеЛьНыМ свдеКЛоМ
С о зд а й те н о в ы й м а сси в о б ъ е к т о в D u c k :
Э т о о б ъ е к т E n u m e r a t o r , о б е с п е ч и в а ю щ и й м е х а н и з м п е р е б о р а к о л л е к ц и и . Р а с с м о тр и м ц и к л f o r e a c h ,
просм атриваю щ ий коллекцию L i s t < D u c k > с перем енной d u c k :
Поэкспетментиюийте, заставив метод ToStringO объекта Риск увеличивать свойство Size на единтУ-
Запустите отладку и наведите указатель мыши на имя Риск. Проделайте это несколько раз. Помни-
mCj что кажЭь>!й рдз будет вi:?(3 t>iвamtfcя метод ToSt\rmg().
Как вы думаете, что будет происходить при выполнении цикла foreach, если метод ToStringO
меняет одно из полей объекта?
В о т п е р в ы е с т р о к и м е то д а M a i n { ) , и н и ц и а л и з и р у ю щ и е к о л л е к ц и ю и о с у щ е с т в л я ю щ и е е е в о с х о д я
щ ее п р и веден и е. ^ ------- ^
, . , ^ , , , , ^ с к о п и р у й т е и н и ц и а л и за т о р для к о ллекц и и ц т о к
L ist< D u c k > d u c k s = new L is t< D u c k > () { / * и н и ц и а л и зи р у й т е о б ъ е к т , как обычно * / }
IE n u m e r a b le < B ir d > u p c a s tD u c k s = d u c k s ;
П о с м о т р и т е н а п о с л е д н ю ю с тр о ч к у . С с ы л к у н а к о л л е к ц и ю L i s t < D u c k > в ы п р и с в а и в а е т е и н т е р ф е й с
н о й п е р е м е н н о й l E n u m e r a b l e < B i r d > . З а п у с ти т е о тл а дку и у б е д и те сь, ч т о о б е с с ы л к и у к а з ы в а ю т на
о д и н и т о т ж е об ъ е кт.
p u b lic sta tic b o o l D oesC ard M atch(C ard card T oC heck, S u its su it) {
if (c a r d T o C h e c k .S u it == s u i t ) {
retu rn tru e; Перегруженные методы не обязаны быть
} e lse { статическими, но мы решили, что
retu rn fa lse ;
Ьам будет полезно попрактиковаться
о написании статических методов.
}
]
p u b lic sta tic b o o l D oesC ard M atch(C ard card T oC heck, V a lu e s v a lu e ) {
if (c a r d T o C h e c k .V a lu e == v a l u e ) {
retu rn tru e; Кяк работает процедура перегрузки,
e lse {
(и в п р о г р а м і d f рсксчета ст оим о
видели
сти вечеринок^из главы &, т ам
retu rn fa lse ; перегруженный мет од CalculateCostO о класс
PinnerParty.
}
C a rd .D o e sC a rd M a tch (
, lo f 2 ▼ b o o l C a rd .D o e s C a r d H a tc h (C a r d c a rd T o C tie c k ^ S u i t s s u it) |
П о э к с п е р и м е н т и р у й т е с э т и м и м е то д а м и , ч т о б ы п р и в ы к н у т ь к р а б о те .
дальше * 369
колоду на стол
П о п р а к ти к уе м с я в п р и м е н е н и и о б ъ е к то в L i s t , с о зд а в к л а сс д л я хр а н е н и я
іа ж н ен и е
кол од ы карты и ф орм у, к о то р ая б уд е т его и с п о л ь зо в ать .
Затем вам п о т р е б у е т с я ф о р м а , п о к а з ы в а ю щ а я с о д е р ж и м о е д в ух о б ъ е к то в D e c k . П р и п е р в о м
запуске п р о гр а м м ы в к о л о д е #1 м о ж е т б ы т ь д о 10 с л у ч а й н ы х ка р т, а в к о л о д е #2 — п о л н ы й н а
б о р (52 к а р т ы ). О б е к о л о д ы о т с о р т и р о в а н ы п о м а с ти и п о с т а р ш и н с т в у В э т о п о л о ж е н и е к о л о
ду м о ж н о в е р н у т ь в л ю б о й м о м е н т щ е л ч к о м н а к н о п к е Reset. Т а к ж е ф о р м а с н а б ж е н а к н о п к а м и
« и » , к о т о р ы е п е р е м е щ а ю т к а р т ы и з о д н о й к о л о д ы в д р у гу ю .
Кнопки моуеТоРескЯ (верхняя) и тоуеТоРеск! (нижняя)
першеьцаким. карты из одной колоды в другую.
Д л я показа обеих колод ис
П ом ните, что с пом ощ ь ю пользуются два элемента
св ойств а N am e эл ем енту ustBox. При щелчке на кнопке
уп равл ени я м ож но при св ои ть ^ o v e r o P e c k l выбранная карта
имя, ул учш и в тем сам ы м ч и та ^перемещается из колоды J z
б ел ь ность кода. П ри д в ойно м о колоду
щ елчке на кнопке об раб о тчи ку
соб ы тий б уд ет д а н о со о тв ет
ствую щ ее имя.
Имена эт их кнопок shuffle! и
Кнопки r e se ti и resetz shuffieZ. Они вызывают п о
сначала вызывают ходящий метод P e c k .S K M rn 6 ()j
метод ResetDeckQ, чтобы перетасовать колоду,
а потом метод а зат ем перерисовывают ее.
R-edrawPeckQ.
дальше * 371
решение упражнения
372 глава 8
перечисления и коллекции
p u b lic F o rm lО {
In itia liz e C o m p o n e n t О ; К онст рукт ор формы сна
R esetD eck (1); чала возвращает колоды
R esetD eck (2); в исходное состояние п—о т о м
R edraw D eck(1 ); перерисовывает их.
R edraw D eck(2 );
}
p r iv a te v o i d R e s e t D e c k ( i n t deckNum ber) {
if (deckN u m ber = = 1 ) {
i n t nu m berO fC ards = ra n d o m .N e x t (1 , 1 1 ) ;
d e c k l = new D e ck (n e w C a r d [] { } ) ;
f o r ( i n t i = 0; i < nu m berO fC ards; i+ + )
d e c k l.A d d (n e w C a r d ((S u its )r a n d o m .N e x t(4 ) ,
(V a lu e s)r a n d o m .N e x t(1, 14)));
d e c k l. S o r t ();
} e ls e
deck2 = n e w D e c k () пользуется мет од random N extn и п . / ’
V
создается пистая после чего
}
For туда добавляются г ‘^омощи цикла
l4emod RedrawPeckQ следок остается провест Тгп^',^ карты. Напо-
вам уже встречался. становить колоди ^с>ртировку. Вос
создать экземпляр D ecl^ '^Р°'Че. Достаточно
У ер е^ ер н и тп е с т р а н и ц у и 1 ^ о Д о Л ж и м !
374 глава 8
перечисления и коллекции
Слобари
К о л л е к ц и и п о д о б н ы д л и н н ы м с т р а н и ц а м , з а п о л н е н н ы м и м е н а м и . А т е п е р ь п р е д с та в ь те , ч т о в ы х о т и т е
с о п о с т а в и т ь ка ж д о м у и м е н и адрес. И л и с н а б д и т ь к а ж д ы й а в то м о б и л ь в в а ш е й к о л л е к ц и и о п и с а н и е м .
Д ля э т о г о вам п о т р е б у е т с я с л о в а р ь ( d ic t io n a r y ) . Э та с т р у к т у р а п о з в о л я е т в з я ть н е к и й к л ю ч (k e y ) и свя
зать е го со з н а ч е н и е м (v a lu e ). К а ж д ы й к л ю ч п р и э то м п о я в л я е т с я в с л о в а р е т о л ь к о о д и н р а з .
А в о т п р и м е р р а б о т ы со словарем :
p r i v a t e v o i d b u t t o n l _ C l i c k (o b j e c t sen d eri> E v e n tA r g s e )
{
D i c t i o n a r y o t r i n g , s t r in g > w o r d D e fin it io n = ‘Принадлежат 'T m u n ^T -^^-
new D i c t i o n a r y o t r i n g , s t r i n g > ( ) ; Моделируем обычный t
слова.
дальше ► 375
сопоставь что угодно чему угодно
Функциональность слобарей
С л о в а р и во м н о го м н а п о м и н а ю т к о л л е к ц и и . О н и г и б к и , п о з в о л я ю т вам р а б о та ть с д а н н ы м и
п р о и з в о л ь н о г о т и п а и и м е ю т м н о ж е с т в о в с т р о е н н ы х ф у н к ц и й . Р а с с м о тр и м о с н о в н ы е м е то
д ы класса D i c t i o n a r y :
^Добавление элементов.
Добавление осуществляется путем передачи ключа и значения методу Add о .
s tr in g lo o k u p V a lu e = m y D i c t i o n a r y [" к а к о й -т о к л ю ч " ];
^Удаление элементов.
Как и при работе с объектами L i s t , для удаления из словаря используется метод
Remove о. Достаточно передать ему ключ, как сам ключ и значение будут удалены.
fo r e a c h ( s t r i n g k e y i n m y D ic t io n a r y .K e y s ) { . . . } ;
—^ К л ю ч и о т н о с я т с я к свойствам словаря, ß рассматри-
★ П о д сче т пар. ^ ваемом случае они принадлежат типу string, поэтому
_ э т о набор строк.
Свойство C ount возвращает число пар «ключ-значение», имеющихся в словаре:
int howMany = myDictionary.Covmt;
c l a s s J e rsey N u itib er {
p u b lic s t r i n g P la y e r { g e t; p r iv a t e s e t ; } Йоги £>ерра играл под * 2 за
p u b lic in t Y ea rR etired { g e t ; p r iv a t e s e t ; } одну команду, а Карл Рипкин-
младший... под т е м ж е ном е
p u b lic J e rsey N u m b e r(strin g p la y e r . in t n u m b erR etired ) { ром за другую. Но в словаре
P la y e r = p la y e r ; одному значению может со
Y e a r R e tir e d = n u m b erR etired ; ответ ст воват ь только один
ключ, поэт ому в программе на
}
данный м омент перечислены
} номера игроков одной команды.
В о т ф орм а; Попытайтесь придумать спо
соб сохранять информацию об
! Retired Jersey Numbers игроках нескольких команд.
p u b l i c p a r t i a l c l a s s F o r m l : F orm {
D i e t io n a r y < in t , JerseyN um ber> r etired N u m b ers n e w D i c t i o n a r y < i n t , J e r s e y N u m b e r > () {
{ 3 , new J e r s e y N u m b e r ( "Babe R u th " , 1 9 4 8 ) } ,
{ 4 , n e w J e r s e y N u m b e r { "Lou G e h r i g " , 1 9 3 9 ) } ,
{ 5 , new J e r s e y N u m b e r ( " Joe D iM a g g io " , 1 9 5 2 ) } , Объекты JerseyNumber
{ 7 , new J e r s e y N u m b e r ( " M ick ey M a n t le " , 1 9 6 9 ) } , передаются в словарь при
{ 8 , new J e r s e y N u m b e r ( "Y ogi B e r r a " , 1 9 7 2 ) } ,
помои^и инициализатора
{lO , new J e r s e y N u m b e r ( " P h il R i z z u t o " , 1 9 8 5 ) } ,
коллекции.
{ 2 3 , n e w J e r s e y N u m b e r { "Don M a t t i n g l y " , 1 9 9 7 ) } ,
{ 4 2 , new J e r s e y N u m b e r { " J a c k ie R o b in so n " , 1 9 9 3 ) } ,
{ 4 4 , new J e r s e y N u m b e r ( " R e g g ie J a c k s o n " , 1 9 9 3 ) } ,
};
p u b l i c F o r m l {) { К л ю ч ы из словаря
In itia liz e C o m p o n e n tO ; ^ добавляются в
коллекцию элементов
f o r e a c h ( i n t k e y i n r e t ir e d N u m b e r s . K eys) {
ComboBox.
num ber. I te m s.A d d (k e y );
}
}
p r i v a t e v o id n u m b e r _ S e le c te d I n d e x C h a n g e d (o b je c t s e n d e r , E ven tA rgs e) {
JerseyN um ber jerseyN u m b er = r e tir e d N u m b e r s [ (i n t ) n u m b e r .S e le c te d lte m ] a s JerseyN um ber;
nam eLabel .T ex t = je rse y N u m b e r . P la y e r ;
Свойство Selectedltem эле
y e a r L a b e l.T e x t = je r se y N u m b e r .Y e a r R e tir e d .T o S tr in g O ;
мент а ComboBox является
объектом. Так как ключ
\ SelectedlndexCkanged элемента ComboBox принадлежит т ипу int, вам
(K одновляет две м ет ки на форме, выводя значение потребуется операция при -
объекта JerseyNumber, взятое из словаря. ведения к эт ому типу.
сыграем в карты!
Длинные
упражнения
Создадим симулятор карточной игры.
С пециф икация
Р а б о та над л ю б ы м п р о ф е с с и о н а л ь н ы м п р о гр а м м н ы м о б е с п е ч е н и е м н а Если задачу не
ч и н а е т с я со с п е ц и ф и к а ц и и . Вам п р е д с т о и т п о с т р о и т ь с и м у л я т о р клас
с и ч е с к о й к а р т о ч н о й и г р ы Go Fish! С у щ е с тв у ю т р а з л и ч н ы е в а р и а н т ы сформулиро
п р а в и л , в о т те , к о т о р ы е будете и с п о л ь з о в а т ь вы :
★ И с п о л ь з у е тс я ко л о д а и з 52 ка р т. И г р о к а м р аздается п о п я т ь
вать заранее,
ка р т. К а р т ы , о с та в ш и е с я п о с л е р а зд а ч и , н а з ы в а ю тс я з а п а с о м .
И г р о к и п о о ч е р е д и с п р а ш и в а ю т п р о н а л и ч и е к а р т (« Е с ть л и
как узнать,
у к о г о се м е р ки ? » ). И г р о к , и м е ю ш ;и й н у ж н у ю карту, о тд а е т ее.
Е сл и т а к о й к а р т ы н и у к о г о нет, б е р е тс я о д н а к а р та и з запаса.
что работа
★ Ц е л ь ю и г р ы я в л я е тс я с б о р в з я то к . В з я т к о й с ч и т а е т с я н а б о р и з закончена?
ч е т ы р е х о д и н а к о в ы х ка р т. Д л я в ы и г р ы ш а н у ж н о с о б р а ть м а к с и
м ал ьн о е к о л и ч е с т в о в з я то к . С о б р а н н ы й н а б о р и з ч е т ы р е х к а р т Именно по
в ы к л а д ы в а е тс я н а с то л .
★ П р о ц е д у р ы сд а ч и к а р т и в з я т о к а в т о м а т и з и р о в а н ы . К а к т о л ь к о
спецификации,
к т о -т о п о б е ж д а е т, и г р а з а к а н ч и в а е тс я , и в ы в о д и т с я и м я п о б е д и
те л я (и л и п о б е д и т е л е й в случае н и ч ь е й ). Б о л ь ш е н и к а к и х д е й
которая сооб
с т в и й в ы п о л н и т ь нельзя. И г р о к у о с та е т с я т о л ь к о п е р е з а гр у з и ть щает вам, что
п р о гр а м м у, ч т о б ы н а ч а т ь н о в у ю п а р т и ю .
должно полу
читься в итоге.
378 глава 8
перечисления и коллекции
о П о стр о ен и е формы
Ф ор м е нуж ен эл ем ен т управления L i s t B o x для от о б р а ж ен и я карт игрока, два эл ем ен
та управления T e x tB o x для от обр аж ен и я п р оц есса игры и кнопка, с п ом ощ ью к отор ой
и г р о к б у д е т с п р а ш и в а т ь о н а л и ч и и карт.
П р и с в о й т е с в о й с т в у Name э т о
го э л е м е н т а T e x tB o x -зн ачени е
Свойства Namp
и ^ Ы а т е . Ш э т о м сниМ ке эк р а н а
о н о в к л ю ч е н о , но п ш з а п у с к е п р о
г р а м м ы долж но о т о б р а ж а т ь ся . ^;^^о игра уже началась
Books
a has a book rf fives
Joe has a ЬоЫс of Aces
a h a sa to rfto f Fot*B
BobhasabD(^!^№es
Asd<foracw<tl
jJej^eBeJ^’H u m e с г о р а н и п у и и р 'о Д о Л Ж и м !
дальше У 379
код формы
Длинные
упражнения
О Э то код дюрмы
В в е д и т е е го в И С Р .
p u b l i c p a r t i a l c l a s s Form l : F o rm {
p u b l i c F o r m l () {
In itia liz e C o m p o n e n t();
} Это единственный класс, с которым
взаимодействует фо^>ма. Именно он
p riv a te Game gam e; управляет всей игрои.
380 глава 8
перечисления и коллекции
p u b lic b o o l ContainsValue ( V a l u e s v a l u e ) {
Метод ContainsValueQ ищет
foreach (C ard c a r d i n cards) 6 колоде карты определенного
if (c a r d .V a lu e == v a l u e ) старшинства и, находя их,
retu rn tru e; возвращает значение true. Вы
retu rn fa lse ; догадываетесь, как он будет
}
использоваться в нашей игре?
p u b l i c D e c k PullOutValues ( V a l u e s v a l u e ) {
D e c k d e c k T o R e t u r n = n e w D e c k ( n e w C a r d [] { });
f o r ( i n t i = c a r d s . C o u n t - 1 ; i >= 0 ;
if (c a r d s [i] .V a lu e == v a l u e )
d e ck T o R etu rn .A d d (D ea l(i ) ) ;
retu rn deckT oR eturn;
и з в л е к а е т ад из
}
варіант t Z f d T e T
p u b l i c b o o l HasBook ( V a l u e s v a l u e )
включена и в з я т к ^ Р ^ ' ^
i n t N um berO fC ards = 0;
foreach (C ard c a r d i n cards) Г Метод HasBookQ, п о
if (c a r d .V a lu e == v a l u e )
лучив в качестве п а
N u m berofC ard s++; рамет ра карт у, на
if ( N u m b e r O f C a r d s == 4) чинает искать взятки.
retu rn tru e; Обнаружив четыре
e ls e одинаковые карты, он
retu rn fa lse ;
возвращает значение
true.
}
Длинные
упражнешя
Э то С Л О Ж Н А Я часть: создание класса Player
Э к зем п л я р ы класса P l a y e r сущ ествую т для в сех и гроков. О н и с о з
даю тся обработчиком собы тий кнопки b u t t o n S t a r t .
c la ss P la y e r
{
Внимательно читайте
p r i v a t e s t r i n g name; комментарии, т ам сказано,
p u b l i c s t r i n g Name { g e t { r e t u r n n a m e ; } } н Х действия долж
ны выполнять методы для
p r i v a t e R andom random;
которых вы пишете код
p r i v a t e D e c k cards;
p r i v a t e T e x t B o x textBoxOnForm;
p u b lic Player ( S t r i n g n a m e , R andom r a n d o m , T e x tB o x tex tB oxO n F orm ) {
// Конструктор класса P l a y e r инициализирует четыре закрытых поля, а затем
// добавляет элементу управления T e x t B o x строку " J o e h a s j u s t
// j o i n e d t h e g a m e " , используя имя закрытого поля. Не забудьте поставить
// знак переноса в конец каждой строки, добавляемой в T e x t B o x .
}
p u b lic I E n u m e r a b l e < V a l u e s > PullOutBooks () { } // код на следующей странице
p u b lic V a l u e s GetRandomValue () {
// Этот метод получает случайное значение, но из числа карт колоды!
p u b lic D e c k DoYouHaveAny ( V a l u e s v a l u e ) {
// Соперник спрашивает о наличии у меня карты нужного достоинства
// Используйте метод D e c k . P u l l O u t V a l u e s о для взятия карт. Добавьте в T e x t B o x
// строку " J o e h a s 3 s i x e s " , используйте новый статический метод C a r d . P l u r a l ()
382 глава 8
перечисления и коллекции
Длинные
упражнешя
Создание классо б а т е
Ф о р м а с о х р а н я е т э к з е м п л я р Game, у п р а в л я ю щ и й и г р о й . П о с м о т р и т е , ка к
о н и сп о л ьз у е тся .
8 классах Player и Game имеются ссылки на
c la ss Game {
элем ент формы TextBoxJ в котором появля
p riv a te L i s t < P l a y e r > players;
ются сообщения о ходе игры. Убедитесь, что
p r iv a te D i c t i o n a r y < V a l u e s , P l a y e r > books; в верхней части файлов есть строка «using
p r iv a te D e c k stock; System.VJindows.Forms;».
p r iv a te T e x t B o x textBoxOnForm;
p u b l i c Game ( s t r i n g p l a y e r N a m e , I E n u m e r a b l e < s t r i n g > o p p o n e n t N a m e s , T e x tB o x textB oxO n F orm )
R an do m r a n d o m = n e w R a n d o m { ) ;
t h i s . textB oxO n F orm = textB oxO n F orm ;
p l a y e r s = new L i s t < P l a y e r > ( ) ;
Интерфейс IEnumerable<T>
делает классы более гибки
p la y e r s .A d d ( n e w P la y e r (p la y e r N a m e , random , textB oxO n F orm ))
f o r e a c h ( s t r i n g p l a y e r i n opp onentN am es)
ми. Об этом следует помнить
p l a y e r s .A d d ( n e w P l a y e r ( p l a y e r , random, textB oxO n F orm ))
при дальнейшем редакти
b o o k s = new D i c t i o n a r y < V a l u e s , P l a y e r > ( ) ;
ровании кода. В данный мо
s t o c k = new D eck О ; _ -у.
мент для получения экзем
D eal ();
Это полезно и для инкапсуляции. пляра класса Game можно
Если использовать 1ЕпитегаЫе<Т> написать stringQ.List<string>.
p l a y e r s [0] . S o r t H a n d O
вместо List<T>j вы не сможете
^ случайно отредактировать код.
p r iv a t e v o id D ea lO {
/ / И м ен н о з д е с ь н а ч и н а е т с я и г р а .
// Тасуется колода, р а з д а е т с я по пять кар т каждому и г р о к у , з а т е м с помощью
// цикла f o r e a c h вы зы вается м е т о д P u l lO u t B o o k s О для каж дого и гр ок а.
384 глава 8
перечисления и коллекции
385
решение упражнения
решение
длинных
П р З Ж Н е Н И Й В о т ка к п о л н о с т ь ю в ы г л я д я т м е т о д ы д л я к л а с с а Game.
}
РУ^<>тся!^ч^тТб1Т ипог>яд^‘^'^‘ copm u-
retu rn fa lse ;
Зат ем n p o 6 e S e m !^ ^ отображаемый
cnucoK.
лась ли игра b L u J Z закончи-
}
I E n u m e r a b l e < V a l u e s > b o o k s P u l l e d = p l a y e r . P u l l O u t B o o k s ()
f o r e a c h (V a lu es v a lu e i n b o o k sP u lle d )
b o o k s.A d d (v a lu e , p la y e r )
i f ( p l a y e r . C a r d C o u n t == 0) Метод PullOutBooksQ проверяет карты
retu rn t r u e ; игрока на наличие б з я т о к . Обнаруженная
retu rn f a ls e ; взятка добавляется в словарь. Если карт
н е осталось, возвращается значение true.
386 глава 8
перечисления и коллекции
p u b l i c s t r i n g GetWinnerName() {
D i c t i o n a r y < s t r i n g , i n t > w i n n e r s = n e w D i c t i o n a r y < s t r i n g , i n t > ()
fo r e a c h (V a lu es v a lu e i n b o o k s.K e y s) {
s t r i n g name = b o o k s [ v a l u e ] . N a m e ;
i f (w in n e r s.C o n ta in sK e y (n a m e )) После взятия последней карты
w i n n e r s [ n a m e ] ++; требуется определить победителя.
e ls e Именно эт им занимается метод
w in n er s.A d d (n a m e , 1 );
GetWinnerNameQ на основе инфор
мации из словаря winners. К л ю ч о м
} является имя игрока, а значением -
i n t m ostB ook s = 0;
f o r e a c h ( s t r i n g nam e i n w i n n e r s . K e y s )
количество взяток.
i f (w in n ers[n a m e] > m ostB ook s)
m o s t B o o k s = w i n n e r s [na m e] ; <3
bool t ie = fa lse ; \ Зат ем определяется
s t r in g w in n er L ist = ;
максимальное количество
f o r e a c h ( s t r i n g nam e i n w i n n e r s . K e y s )
взяток. Оно помеи^ается
в переменную mostBooks.
i f ( w i n n e r s [na m e] == m o s t B o o k s )
{
if ( 'S tr in g .I s N u llO r E m p ty (w in n e r L is t)
{
w i n n e r L i s t += " a n d ";
t i e = tru e;
Теперь, когда мы знаем
}
w in n er L ist += n a m e ; игрока с максимальным
количеством взяток,
} можно вывести строку
w i n n e r L i s t += " w i t h " + m o s t B o o k s + " b o o k s " ;
с именем победителя
i f (tie)
(или победителей).
r e t u r n "A t i e b e t w e e n " + w i n n e r L i s t ;
e ls e
r etu rn w in n er L ist;
Ц ереБернш пе сш ранипу U п р о д о д ж ш !
дальше ► 387
решение _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
длинных
ПрЗЖ НеНИЙ Так в ы гл я д я т п о л н о с ть ю н а п и с а н н ы е м етод ы к л а сса P l a y e r .
p u b l i c P l a y e r ( S t r i n g n a m e . Random r a n d o m , TTeexxttBB oo x t e x t B o x O n F o r m ) {
t h i s . n a m e = name;
t h i s . r a n d o m = random ; Э т о к о н с т р у к т о р к л а с с а P la y e r . О н
t h i s . textB oxO n F orm = textB oxO n F orm ; ^ —з а д а е т з нa а ч е...................................
н и я п р и в а т н ы х п о л е й и до -
t h i s . c a r d s = new D e ck ( new C ard [] {} ) ; 5 в т екст овое поле ст року с ин-
t e x t B o x O n F o r m . T e x t += n a m e + ф о р м а ц и е й о п р и со ед и н и вш ем ся и гр о к е.
" has ju st jo in e d th e g am e " + E n v ir o n m e n t.N ew L in e ;
}
p u b l i c V a l u e s G e t R a n d o m V a l u e () {
C ard random C ard = c a r d s . P e e k (r a n d o m . N e x t (c a r d s . C o u n t)
r e t u r n random C ard. V a lu e ; М е т о д а ^ Я а п с 1 о т \ / а 1 и е ( ) с п о м о ш ,ьн >
} м е т о д а Р еекО вы б и р а е т с л уч а й н ую
к а р т у с р е д и и м е ю ш ,и х с я у и г р о к а .
p u b l i c D eck D oY ouH aveA ny(V alues v a l u e ) {
D eck c a r d sIH a v e = c a r d s . P u l l O u t V a l u e s ( v a l u e ) ;
t e x t B o x O n F o r m . T e x t += Name + " h a s " + c a r d s I H a v e . C o u n t +
+ C a r d .P lu r a l (v a lu e) + E n v ir o n m e n t.N e w L in e ; М е т о д P o Y o u H a v e A n u Q и с п о л ь з ц е т
retu rn cardsIH ave; М е т о д P u llO u tV a lu e s i) д л я и з в л е ч е -
‘ ^ ------' Н и я к а р т , к о т о р ы е с о о т в е т с т в у
ю т заданны м п а р а м е т р а м
p u b l i c v o i d A s k F o r A C a r d ( L i s t < P l a y e r > p l a y e r s , i n t m y ln d e x . D e ck stock)^ ( ^
V a l u e s r a n d o m V a lu e = G etR a n d o m V a lu e ( ) ;
A s k F o r A C a r d ( p l a y e r s , m y ln d e x , s t o c k , r a n d o m V a lu e) В програм м е два п е
регруж енных мет ода
As.kPorACard{). Э т от
Дополнительное мини-упражнение: Улучшите инкапсуляцию использует ся со пер
класса Player, заменив в этих двух методах List<Player> на ни ка м и — выбирает
IEnumerable<Player>, не повлияв на работу программы. случайную карт у из
имеюш,ихся и вызы
вает вт орой м ет од
AskForACardQ.
p u b l i c v o i d A s k F o r A C a r d (L ist< P la y er > p l a y e r s , i n t m y ln d ex .
D eck s t o c k . V a lu e s v a lu e ) {
t e x t B o x O n F o r m . T e x t += Name + " a s k s i f a n y o n e h a s a "
+ v a lu e + E n v ir o n m e n t.N ew L in e ;
i n t t o t a l C a r d s G i v e n = 0;
f o r ( i n t i = 0; i < p l a y e r s .C o u n t ; i+ + ) {
i f ( i != m y l n d e x ) { М е т о д A sk F o rA C a rd Q
P la y e r p la y e r = p l a y e r s [ i ] ; п р о в е р я е т всех и гр о к о в
D eck C a r d sG iv e n = p l a y e r . D o Y o u H a v e A n y (v a lu e ); (за и ск л ю ч ен и ем с п р а ш и
t o t a l C a r d s G i v e n += C a r d s G i v e n . C o u n t ; ^ в а ю щ е го ), вы зы в а ет их
w h i l e ( C a r d s G i v e n . C o u n t > 0) м ет о д P oY ouH aveA nyQ
c a r d s.A d d (C a r d sG iv e n .D e a l 0 ) ; и д о б а вл я ет найденны е
} подходящ ие карт ы .
}
if ( t o t a l C a r d s G i v e n == 0 ) {
t e x t B o x O n F o r m . T e x t += Name +
" m ust draw from t h e s t o c k . " + E n v ir o n m e n t. N ew L in e;
c a r d s . A d d ( s t o c k . D e a l () ) ;
П ри о т с у т с т в и и у с о п е р н и к о в п од ходя щ и х
к а р т и гр о к б ер е т к а р т у из зап аса п ри
п о м о щ и м е т о д а P e a lQ .
перечисления и коллекции
В
Объекты РеекО returned; first in tine ( I )
ждут своей
очереди. The firs t DequeueO returned; firs t in lin e ( i)
The second DequeueO returned: second in tin e @
C o u n t before ClearO was 2 ®
C o u n t after ClearO is n ow 0 0
390 глава 8
перечисления и коллекции
Поме- С о з д а н и е с т е к а ничем не
о т с^о з д а н и я л ю д о и
м а е /л ы й отличается от
б стек д р уго й о6обиА,еннои коллекции.
элем ент
6 c e ^ ^ n p o * ^ w e S ta c k < str in g > m y S t a c k = new S t a c k < s t r i n g > () ;
э л е М ^ е н т Е ^ ^ ;;^ ^ ^ ^ " f ir s t in lin e ");
П оследни й о б ъ е к т ,
пом ещ ен ны й в с т е к ,
PeekO returned fast: in ltne@ ст ан овит ся п ервы м
The first PopO returned: last in line @ объект ом , кот оры й
б уд ет о т т у д а взят .
The secorrd PopO returned; third in fine Q
Count before ClearO was 2 ©
Count after ClearO is now 0 ©
OK
дальше > 391
лепешки и лесорубы
Заполним ст ек четырьмя
ст роками. ^
BM « « я р е
Sam cKonupo«» m y Q u e u e h a s 4 ite m s ...Д Л Я д о с т у п а ко в с е м
б новые колдекцим. m y L ist h a s 4 ite m s членам очеред и или стека
a n o th e r S ta c k h a s 4 ite m s
д остаточно воспол ьзоваться
циклом ^ ге а с И !
392 глава 8
перечисления и коллекции
© Ф о р м а п о з в о л я е т в в е с ти и м я л е с о р у б а в т е к с т о в о е п о л е , ч т о б ы п о м е с т и т ь е го в о че р е д ь.
В ы д а в п е р в о м у в о ч е р е д и л е с о р у б у л е п е ш к и , в ы о тп р а в л я е т е е го е сть , щ е л к н у в н а к н о п
ке N e x t lu m b e rja c k . М ы н а п и с а л и о б р а б о т ч и к с о б ы т и й к н о п к и A d d fla p ja c k s . О т с л е ж и
в а й те с о с т о я н и е л е с о р у б о в п р и п о м о щ и о ч е р е д и b r e a k f a s t L i n e .
p r iv a t e v o id R ed r a w L ist0
i n t num ber = 1;
l i n e . I te m s . C lea r
М ет од f o r e a c h (L um b erjack lu m b e r ja c k in b re a k fa stL in e ) {
Reйf^'йu/L/st(') п р и l i n e . I t e m s . A d d ( n u m b e r + ". " + lu m b e r ja ck .N a m e );
<°2мощи цикла num ber++;
гогеаск убира Этот оператор обновляет
ет лесорубов из Е ( b r e a k f a s t L i n e . Count = = 0 ) {
мет ку 6 соответствии
°^ереди и поллеи^а - g r o u p B o x l.E n a b le d = f a l s e ;
с информацией о следующем
ет сведения о ник n e x tln L in e .T e x t =
лесорубе в очереди.
о список. e lse {
g r o u p B o x l.E n a b le d = tr u e ;
L um b erjack c u r r e n tL u m b e r ja c k = b r e a k f a s t L i n e . P e e k ( ) ;
n e x t l n L i n e . T e x t = c u r r e n t L u m b e r j a c k . Name + " h a s "
^ + cu rren tL u m b erja ck .F la p ja ck C o u n t + " fla p ja c k s "
c l a s s L um b erjack {
p r i v a t e s t r i n g nam e;
p u b l i c s t r i n g Name { g e t { r e t u r n n a m e ; } }
p r i v a t e S t a c k < F la p ja c k > m ea l;
p u b l i c L u m b e r j a c k ( s t r i n g nam e) {
t h i s . n a m e = nam e;
m ea l = new S t a c k < F l a p j a c k > ( ) ;
Метод
TakeFlapjacks
обновляет со p u b lic in t F la p ja c k C o u n t { get { retu rn m e a l.C o u n t; } }
держимое с т е
ка Меа! (Прием p u b lic v o id T a k e F la p ja c k s(F la p ja c k fo o d , in t how M any) {
пищи). f o r ( i n t i = 0 ; i < how M a n y; i + + ) {
m e a l . Push ( f o o d ) ; Именно здесь перечисление
} } Flapjack впервые пишется про
писными буквами. Постарайтесь
P iib lic v o id E a tF la p ja c k s О { понять, как это происходит.
^кЦ е ^ C o n s o l e . W r i t e L i n e (n a m e + " ' s e a t i n g f l a p j a c k s "
Ф орм аии ^ ( m e a l . C o u n t > 0) {
^ ^ Ж д о го ^ ‘^ P '^ n e s e C o n s o l e . W r i t e L i n e (nam e + ate a _к Г
'^^‘^ o p yS a . + m e a l . P o p () . T o S t r i n g () . T o L o w e r () + " f l a p j a c k " ) ;
^ ~ —
© Ф о р м а п о з в о л я е т в в е с ти и м я л е с о р у б а в т е к с т о в о е п о л е , ч т о б ы п о м е с т и т ь е го в о че р е д ь.
В ы д а в п е р в о м у в о ч е р е д и л е с о р у б у л е п е ш к и , в ы о тп р а в л я е т е е го е с ть , щ е л к н у в н а к н о п
ке N e x t lu m b e rja c k . М ы н а п и с а л и о б р а б о т ч и к с о б ы т и й к н о п к и A d d fla p ja c k s . О т с л е ж и
в а й те с о с т о я н и е л е с о р у б о в п р и п о м о щ и о ч е р е д и b r e a k f a s t L i n e .
4 -
stream.Write(...);
Переменная output
содержит данные,
занисьшаемые
в объект s tream .
•процесса не изменится.
396 глава 9
чтение и запись файлов
Объект FileStream
В о т ч т о п р о и с х о д и т п р и з а п и с и в ф а йл н е В верхней части любой
с к о л ь к и х с т р о к те к с та : программы, ра6отаюш,ей
с потоками, должна п р и
сутствовать строчка
u s in g S y s te m .10;
С озда е тся о б ъ е к т F i l e S t r e a m , п о л у ч а ю щ и й
к о м а н д у з а п и с ы в а ть в ф айл.
П рисоедини т ь
объект
•ект FileStream
Л10ЖН0 только
><одному объекту
Зй раз. ^
О б ъ е к т F i l e S t r e a m п р и с о е д и н я е т с я к файлу.
С т р о к и , к о т о р ы е .т р е б у е т с я з а п и с а ть , следует
п р е о б р а з о в а ть в м а сси в т и п а b y t e .
117 114 1 0 1 107 97 33
Эта процедура называется
перекодированием, мы
E ureka! П1П101П1П1П1П
поговорим о ней чут ь позже.
В ы з ы в а е тс я м е то д W r i t e ( ) , к о т о р о м у пе р е д а
е тся м а сси в т и п а b y t e .
П о т о к з а к р ы в а е тс я , ч т о б ы д р у ги е п р о гр а м м ы м о гл и п о л у ч и т ь
д о с т у п к файлу.
Забы в _
398 глава 9
чтение и запись файлов
■Чнлк & п е р е д и т н е / л ф а й л а
/а т н а е т
кяк т е к с т о в а я с т р о к а д ез
е5С-последователшостеи.
w r ite r .C lo s e о ;
\
находится в
File Edit F orm at tietp
п ростран ств е How I ’l l defeat cap tain Amazing ......... . ~ .......... ■
“ -------------------- -
и м е н S y s te m .lO , Another genius secr et plan by The swindler
п оэтом у в в ер х cloTO « ® a t tt c k r ? ^ e ° L l1 ° " “ uf’teash them upon the c itiz e n s o f o b je c t v n ie .
Clone #1 attacks downtown
ней части п р о Clone #2 attacks the mall
clon e #3 attacks downtown
гр а м м ы д о л ж н а clone #4 attacks th e mall
бы ть стр о ка Clone #5 attacks downtown
Clone #6 attacks the mall
u s in g S y s te m .lO ;
400 глава 9
чтение и запись файлов
Zap);
sw.WriteLine(Zap) I
^[аГ ниш ы ДЛЯ о б ъ е к т а ^еаш ^Г 1|;еГ Zap = ange";
orange"; t
return true;
у вас есть код для кнопки b u t t o n l _ C l i c k (). Расположи
те магниты таким образом, чтобы создать класс Р1оЬЬо.
При этом обработчик событий должен привести к резуль
тату, показанному внизу страницы. Удачи!
sw.WriteLine(Zap);
a
private void buttonl_Click(object sender, EventArgs e) {
sw.Close();
Flobbo f = new Flobbo("blue yellow");
return false;
StreamWriter sw = f.SnobboO;
f .Blobbo(f .Blobbo(f .Blobbo(sw), sw), sw);
} public bool Blobbo '
(bool Already, StreamWriter sw) {
sw.WriteLine(Zap);
Zap = "green purple";
return false;
Результат:
с m acaw .txt - Notepaa
Fte Е Л Format View Н ф
blue yellow
green purple
red orange
class Flobbo {
private string Zap;
L iJ
return new
StreamWriter("macaw.txt");
f ю т задачу слишком легкой! Но
пожалуйста, не нужно брать
с нас прим ер, когда вы пишете
программы.
r
bool Blobbo (StreamWriter sw) {~j^
sw.WriteLine(Zap);
Zap = "green purple";
return false; Метод BlobboQ nepe-
имеет два
UJ объявления с двумя
различными парам е-
Іграм и . ^
public bool Blobbo
(bool Already, StreamWriter sw) {
if (Already) {
sw.WriteLine(Zap);
s(Zap); I
7
sw.Close О
После завершения
return false; Т будьт е закрыть файлы.
} else {
I Результат:
sw.WriteLine(Zap);
Zap = "red orange"; macaw - Notepad
return true;
File Edit Format View Help
M o e y e flo w
g re e n p u rp le
r e d o ra n g e
< у ,J. ■
402 глава 9
чтение и запись файлов
Чтение Uзапись при noMoui^u двух объектов В данном случае мы слишком вольно
используем слово «поток». Класс
StreamReader (наследующий ОТ
TextReader) читает символы из по
С е кретны й план Ж ул и ка м ы прочитаем п р и пом ощ и п о тока
тока, но это не поток. Поток создается,
S t r e a m R e a d e r . И м е н н о е го к о н с т р у к т о р у п е р е д а е тс я и м я
когда вы передаете имя файла в его
ф айла, к о т о р ы й тр е б у е тс я п р о ч и т а т ь . М е т о д R e a d L i n e () воз конструктор, и закрывается с помо
в р а щ а е т с т р о к у с т е к с т о м и з ф айла. Д ля п р о ч т е н и я в с е х с т р о к щью метода Close О . Он имеет также
и с п о л ь з у й т е ц и к л , к о т о р ы й р а б о та е т, п о к а п о л е E n d O f S t r e a m перегруженный конструктор, которо
не п о л у ч и т з н а ч е н и е tru e , т о е с ть п о к а н е з а к о н ч а т с я с т р о к и : му можно передать объект stream.
Теперь вы поняли, как это работает?
S tre a m R e a d e r re ad e r =
new S t r e a m R e a d e r ( @ " с : \ s e c r e t _ p l a n . t x t " ) Передайте файл, который т р е
буется прочит ат ь конструктору
S t r e a m W r it e r w r i t e r = класса StreamReader.
new S t r e a m W r it e r ( @ " c : \ e m a ilT o C a p t a in A m a z in g . t x t ' ) !
С помощью класса StreamReader программа читает план r f
Жулика, а средства класса StreamWriter позволяют на- )
писать файл, который будет отправлен по электронной ^
почте супергерою Капитану Великолепному.
w r it e r . W r it e L in e ("Т о : C a p t a in A m a z in g @ o b je c t v ille .n e t " ) ;
s t r in g lin e F r o m T h e P la n = r e a d e r .R e a d L in e 0 ;
Ъелить>^
w r it e r . W r it e L in e ("T h e p la n -> " + lin e F r o m T h e P la n );
w r it e r .C lo s e О ;
Hrfp
r e a d e r .C lo s e О ; To : c a p t a 1 r w u n a z in g @ o b je c tv i li e .n e t
F ro m ; coB®»1 s s 1 o n e r @ o b 3 e c t 1 v i n e .n e t
s u b j e c t : c a n y o u s a v e t h e d a y . . . a g a in ?
w e ’v e d i s c o v e r e d t h e s w i n d l e r ’ s p l a n :
Вы должны закрыть все о т The p l a n - > HOW I ' l l d e f e a t c a p t a i n /M nazing ,
крытые потоки, даже если T h e D la n - > AF>oTher g e n i u s s e c r e t p l a n b y T h e s w i n d l e r _ ^ ^
T h e p l a n - > I ’ l l c r e a t e a n arm y o f c l o n e s a n d u n l e a s h th e m u p o n t h e c i t i z e n s o f o b j e c t v i l l e .
всего лишь читаете файл. T h e p l a n - > c l o n e # 0 a t t a c k s t h e m a ll
T h e p l a n - > C lo n e #1 a t t a c k s dow ntow n
T h e p l a n - > c l o n e 42 a t t a c k s t h e » a l l
/|\ T he p l a n - > c l o n e # 3 a t t a c k s d<»^ntown
T h e p l a n - > c l o n e # 4 a t t a c k s t h e m a ll
T h e p l a n - > c l o n e # 5 a t t a c k s d o w n to w
T h e p l a n - > c l o n e # 6 a t t a c k s t h e m a ll
О б ъ е к т ы S tr e a m R e a d e r
и S t r e a m W r i t e r после созда c a n you h e lp u s?
Б о л ь ш и м п р е и м у щ е с тв о м р а б о т ы с п о т о к а м и в .N E T я в л я е тс я в о з м о ж н о с т ь
п усти ть данны е через н есколько п о то ко в . О д н им из м н о го ч и с л е н н ы х т и
п о в д а н н ы х в .N E T я в л я е тс я класс C r y p t o S t r e a m . О н п о з в о л я е т за ш и ф
р о в а т ь д а н н ы е , п е р е д те м к а к п р о д е л а ть с н и м и все о с та л ь н ы е о п е р а ц и и :
^П О Н О В и
Класс C ryptoStream
наследует от аб
страктного класса
O iv e K m C r a p t o S t « « » Stream , как и все про
соединяется с oov чие потоковые классы.
ект ом FileStream, '^'^Фрованньїй
и создает поток за- >^екст в файл.
мифрованного тек
ста.
р Л е ї’'
Б б а ссей н е
Fite Edft Format View Help
нужно взять фрагменты кода Infest
из бассейна и поместить их на
пустые строчки. Любой фраг
мент можно использовать не
East
so u th
North
That's all folks'
a
сколько раз. В бассейне есть
и лишние фрагменты. В ре
зультате нужно получить окно
c la ss P iz z a {
с текстом, показанное справа.
p r i v a t e ____
c la ss P in e a p p le { p u b lic P i z z a (_______________ _ J {
c o n s t _________ d = " d e l i v e r y . t x t " ; _________. w r i t e r = w riter;
p u b l i c _______ _________ }
{ N orth , S ou th , E ast, W est, F la m in g o } p u b lic v o id ..F a r g o f) {
P iz z a i = new P i z z a c la s s P arty
i . Id a h o ( (F argo)w ); reader) {
P a r t y p = new P a r ty (n e w
p . ________________ ( o ) ;
^ 0 in p H u e J * e ^ c a Б б а ссей н е
.■ s s ;- S * = = -
c la ss P in ea p p le {
const String d = " d e liv e r y .tx t" ;
p u b lic enum Fargo { N orth , S ou th , E ast, W est, F la m in g o }
p u b lic sta tic v o i d M a in O {
StreamWriter o = n e w StreamWriter ( " o r d e r , t x t " ) ;
P i z z a p z = n e w P i z z a (n e w StreamWriter ( d , t r u e ) ) ;
Это точка б х о -
да программы. 3<?еси p z . Idaho ( F a r g o . F l a m i n g o ) ;
создается обьект for (int w = 3; w >= 0 ; w --) {
З Ш а т Ш и г, кот о- P i z z a i = new P i z z a (new StreamWriter ( d , fa lse ));
т й передается в класс i . Id a h o ( (F argo)w );
РаНц. Зат ем члены P a r t y p = new P a r t y (new StreamReader ( d ) ) ;
перечисления Рагдо p.HowMuch( o) ;
в цикле п е р е д а й с я \}
методу Р 1» й Л ^ а К о О o . WriteLine ( "That' s a l l fo lks !") ;
для вывода в форму- o . Close () ;
}
}
c la ss P iz z a { /^ е т о д nepe-
e файл f
p riv a te StreamWriter writer;
численмя F 4nStringQ>
p u b lic P i z z a (StreamWriter vwiter) {
this . w r i t e r = w r i t e r ;
}
p u b l i c v o i d Idaho (Pineapple. F a r g o f )
w r i t e r . WriteLine (f ) ;
w r i t e r . Close 0 ;
}
}
c la ss P arty {
в классе Party p r iv a te StreamReader r e a d e r ;
имеется поле p u b lic P a r t y (StreamReader r e a d e r ) {
Stream Reader, и this . r e a d e r = reader;
метод HowMuckQ
читает о т т у }
да сгшуоки, за p u b l i c v o i d HowMuch (StreamWriter q)
писывая их в поле q . WriteLine ( r e a d e r . ReadLine ( ) ) ;
Stream W riter. r e a d e r . Close () ;
406 глава 9
чтение и запись файлов
« Dsslogs
Pointer
IS CoiofDiatog
^ FoEderBrovwerOiatog
23 FontDiaiog
Это окно
F o !d e r8 r o w se P i« io 3 '
предназначенное
Зля работы с пап
KflMW-
Мы р ассм от рим эт и
Метод Shov/DialogO б укваль
но через м ин ут у.
Д ля в ы зо в а о к н а д и а л о га вам н у ж н о :
С о зд а ть э к з е м п л я р о к н а д и а л о га . Э т о м о ж н о сделать п р и п о м о щ и
о п е р а т о р а n e w и л и п уте м п е р е т а с к и в а н и я и з T o o lb o x .
© З адайте с в о й с т в а о к н а . Н а п р и м е р , T i t l e (т е к с т в с т р о к е з а го л о в
к а ), I n i t i a l D i r e c t o r y (ад рес о т к р ы в а е м о й п о у м о л ч а н и ю п а п к и )
и F i l e N a m e (д ля о к о н д и а л о га O p e n и Save).
@ В ы з о в и т е м е то д S h o w D i a l o g ( ) . О н в ы з ы в а е т о к н о д и а л о га и н е воз
в р а щ а е т з н а ч е н и е , п о к а п о л ь з о в а те л ь н е щ е л к н е т н а к н о п к е О К и л и
C a n c e l и л и д р у ги м с п о с о б о м н е з а к р о е т о к н о .
О М е т о д S h o w D i a l o g О в о з в р а щ а е т п е р е ч и с л е н и е D i a l o g R e s u l t . Е го
ч л е н ы п р и н и м а ю т з н а ч е н и я ОК (о зн а ч а е т, ч т о п о л ь з о в а те л ь щ е л кн ул
н а к н о п к е О К ) , C a n c e l , Y e s и No (д ля о к о н д и а л о га Y e s /N o ) .
дальше * 407
окна диалога — это тоже объекты
л D ialo g s
№
% P o in te r
fnrmi
Щ] C o lo rD ia to g
12 F o n tD ia lo g ^
^ O o en F ile D ialo g
D ia lo g R e su lt r e su lt = o p e n F ile D ia lo g l. S h o w D ia lo g ();
if (r e su lt == D i a l o g R e s u l t . O K ) {
408 глава 9
чтение и запись файлов
<9SeveAs
, * UbrBries ► Documents ► 1 11 Sesfxh
» 4^ p
Свойство Title
меняет т екст 1=.; Р едакт и рован и е
раскрывающегося
в строке за Favorites Afrsiigeby; FoWet с п и с к а Save as type
головка. ■ Desktop Irscfudes: 2 iocatfons
осущ ест вляет ся при
Downloads Name Date morirfted Type
^ Recent Places
пом ощ и свойст ва
Sterdock ЗЛг/ЖЗ 7tS?AM Fitefoide Filter.
^ Visual Studio 2010 зттш ^11 PM' Fifefoidei
дальше ► 409
справочная система
О Удалить пап ку
Э та н е с л о ж н а я п р о ц е д у р а в ы п о л н я е т с я м е то д о м D e l e t e ()
410 глава 9
4acm°
чтение и запись файлов
за д а в а ем ы е
B o Ilj^ o C b i
И все-таки зачем нужны символы {0} и {1} в объявлении ^ ! Наверное, вы уже слышали, что файлы на диске представ
объекта StreamWriter? лены в виде битов и байтов. Другими словами, при записи фай
ла на жесткий диск операционная система воспринимает его как
набор байтов. Объекты S t r e a m R e a d e r и S t r e a m W r i t e r
5 При вводе строк в файл иногда возникает необходимость
преобразуют эти байты в понятные вам символы, то есть вы
ввести туда и содержимое многочисленных переменных, к при
полняют кодирование и раскодирование. Помните, в главе 4
меру, можно написать:
упоминалось, что переменная типа b y t e хранит значения от
+ name +
О до 255? Все файлы на жестком диске представляют собой
w r i t e r . W r i t e L i n e ("Меня з о в у т
длинные последовательности чисел из этого диапазона. При от
"мне " + а д е + " лет.");
крытии файла, скажем, в приложении Блокнот, каждый байт пре
образуется в символ: например, Е соответствует 69, а а — 97
Но комбинировать строки таким образом долго, кроме того, воз
(впрочем, все зависит от кодировки... но мы поговорим об этом
растает вероятность опечаток. Намного проще писать код:
чуть позже). Соответственно, при сохранении введенного вами
в Блокнот текста символы преобразуются обратно в байты. Это
w r ite r .W r ite L in e (
преобразование нужно и для записи переменной типа s t r i n g
"Меня з о в у т { о } м н е {1} лет",
в поток.
nam e, a g e);
Такой вариант легче читается, особенно, когда в одной строке Разве для записи в файл недостаточно объекта
оказывается много переменных. treamWriter? Зачем создавать объект FileStream?
I Зачем нужен знак (§ перед именем файла? ! Для записи строк в текстовый файл и их дальнейшего
чтения действительно достаточно объектов S t r e a m R e a d e r
и S t r e a m W r i t e r . Но для более сложных операций требуется
^ ; При добавлении в программу строковых констант компиля
задействовать другие потоки. Записывать в файл числа, масси
тор преобразует езс-последовательности ( \ п или \ г ) в специ
вы, коллекции и объекты S t r e a m W r i t e r не умеет. Впрочем,
альные символы. Но косая черта присутствует и в маршрутах
эта тема будет подробно рассмотрена чуть позже.
доступа к файлам. Поместив в начало строки знак вы говорите
С#, что в строке отсутствуют езс-последовательности. Кроме
того, в нее начинают включаться знаки переноса (то есть нажа ; Как мне создать собственные диалоговые окна?
тия клавиши Enter фиксируются автоматически): Б
str in g tw oL in e = @ "это с т р о к а ,
5 Вы можете добавить в проект форму и придать ей нужный
за н и м а ю щ а я д в е с т р о ч к и ." ;
вид. Затем при помощи оператора n e w создается ее экземпляр
(именно так вы поступали с объектом O p e n F i l e D i a l o g ) . По
сле чего остается вызвать ее метод S h o w D i a l o g (), и новое
Напомните еще раз, что означают символы \ п и \ t .
окно диалога готово. Подробно этот процесс будет рассмотрен
в главе 13.
I I; Это так называемые езс-последовательности. \ п — пере
нос строки, \ t — табуляция, \ г — символ возврата. В тексто
вых файлах Windows строки должны заканчиваться символами ; Зачем закрывать потоки после окончания работы?
Б
\ г \ п (об этом мы говорили в главе 8 при знакомстве с констан
той E n v i r o n m e n t . N e w L i n e ). Чтобы использовать в строке ! Сообщал ли вам когда-нибудь текстовый редактор, что он
обратную косую черту, которую компилятор не воспринимает как не может открыть файл, потому что «файл используется другим
езс-последовательность, делайте ее двойной: \ \ . приложением»? Windows блокирует открытые файлы и не позво
ляет открывать в других приложениях. Именно это происходит
А что там говорилось про преобразование строки в мас с вашей программой при открытии файла. Файл остается забло
сив байтов? Как это работает? кированным, пока вы не воспользуетесь методом C l o s e ( ) .
дальше ► 411
напиш ит е эт о с а м о с т о я т е л ь н о
F i l e . E n c r y p t (@"с: \S Y P \B o n k \w e ir d o . t x t " ) ;
к Это вы еще не проходили...
с м о ж е т е ли вы угадать
назначение этого метода?
F i l e . С о р у (@"с: \S Y P \B o n k \w e ir d o . t x t " ,
© " c :\S Y P \c o p y .tx t" );
D a t e T i m e m y T im e =
D ir e c t o r y .G e tC r e a tio n T im e (@ " c :\S Y P \B o n k " );
F i l e .D e le te (@ " c : \S Y P \B o n k \w e ir d o .t x t " );
412 глава 9
чтение и запись файлов
★ У к а ж и т е , ч т о о б ъ е к т O p e n F i l e D i a l o g д о л ж е н п о к а з ы в а ть
то л ько те ксто вы е ф айлы , с пом о щ ью свойства F i l t e r .
дальше ^ 413
бросайте мусор в нужную корзину
Решение
Код Функция
if (! D i r e c t o r y . E x i s t s {@"с : \ S Y P " )) { проверяет существование папки C:\SYP
D i r e c t o r y . C r e a t e D i r e c t o r y ( @ " с :\ S Y P " ) ; и создает ее, если она от сут ст вует .
}
if (D ir e c to r y .E x ists(@ " c :\S Y P \B o n k " )) { Проверяет существование папки С:\
D i r e c t o r y . D e l e t e (@"C : \ S Y P \ B o n k " ); SYP\Bonk. Если папка сущ ест вует , она
} удаляется.
D i r e c t o r y . C r e a t e D i r e c t o r y ( @ " c : \ S Y P \ B o n k " );
Создает папку C:\SYP\Bonk.
D i r e c t o r y . S e t C r e a t i o n T i m e ( @ " c :\ S Y P \ B o n k " ,
Задает время создания папки C:\SYP\
new D a t e T im e (1 9 7 6 , 09, 2 5 )); Bonk, Z S сентября 1 9 7 6 .
F i l e . W r i t e A l l T e x t (@"с : \ S Y P \ B o n k \ w e i r d o . t x t " ,
Создает файл w eirdo.txt (если он
@"Это п е р в а я с т р о ч к а еще не создан) в папке C:\SYP\Bonk
a эт о вторая строчка и записывает в него три строки
а это последняя с т р о ч к а " ); текста.
F i l e . E n c r y p t (@"с : \ S Y P \ B o n k \ w e i r d o . t x t " ); Зашифровывает файл weirdo.txt
' Э то альтернативный способ исполь -■ при помощи встроенной системы
зования обьекта CryptoStream. шифрования Windows.
F i l e . C o p y (@ "с: \ S Y P \ B o n k \ w e i r d o . t x t " ,
К опирует содержимое файла C:\SYP\
© " C :\S Y P \c o p y .tx t" ); B onk\w eirdo.txt в файл C:\SYP\Copy.txt.
D a t e T i m e m y T im e =
Объявляет переменную m yTim e и п р и
D ir e c t o r y .G e t C r e a t io n T im e ( @ " c : \S Y P \B o n k " ) ; сваивает ей время создания п а п к и С:\
SYP\Bonk.
F ile .S e t L a s t W r it e T im e (© " c :\S Y P \c o p y .tx t" , m yT im e); Меняет последнее время записи файла
copy.txt в папке C:\SYP\, присваивая
ему значение переменной myTime.
414 глава 9
чтение и запись файлов
интерфейс IDisposable
М н о ж е с т в о кл а ссо в .N E T р е а л и зуе т к р а й н е п о л е з н ы й и н т е р
ф ейс I D i s p o s a b l e . О н с о д е р ж и т в с е г о о д и н м е то д Dispose О .
При объявлении
Э т о т и н т е р ф е й с о б ъ я с н я е т п р о гр а м м е , ч т о е сть в а ж н ы е в е щ и ,
к о т о р ы е тр е б у е тс я сделать для з а в е р ш е н и я р а б о т ы . В едь р а с
о^ектов в разделе
п р е д е л е н н ы е р е с у р с ы н е о с в о б о ж д а ю т с я с а м о с то я те л ь н о .
Д ля э т о г о и м тр е б у е тс я м е то д D i s p o s e ( ) .
using вызов метода
В о сп о л ь зуе м ся ф у н к ц и е й И С Р G o То D e fin itio n для п р о с м о т р а
Dispose () таких объ
о п р е д е л е н и я и н т е р ф е й с а I D i s p o s a b l e . В в е д и те « ID isp o sa b le »
в п р о и з в о л ь н о м м есте в н у т р и класса, щ е л к н и т е н а э т о й с т р о ч
ектов будет осущест
ке п р а в о й к н о п к о й м ы ш и и в ы б р и т е в м е н ю к о м а н д у G o То
D e fin itio n . О т к р о е т с я вклад ка с ко д о м . В о т ч т о в ы у в и д и те :
вляться автоматически.
Многие классы распределяют между со
бой такие важные ресурсы как память,
namespace System файлы и другие объекты. Они соединя- ^
ются с эт ими ресурсами и не разрыва
{ ю т связь, пока не получат сигнал, что
работа закончена.
// Краткое описание:
II Дает метод освобождения распределенных р е с у р с о в .
{
// Краткое описание:
// Выполняет определяемые приложением задачи,
/ / неуправляемых ресурсов
void Dispose О;
/\юбой класс, реализуюш,ий интерфейс рас-пре-де-лять, гл.
IDisposable, немедленно освобождает любые
задействованные ресурсы после вызова его м е
раздавать ресурсы или
тода DisposeQ. Это последнее, что делается обязанности для опре
перед заверилением работы с объектом.
деленных целей. Управ
ляющий р а с п р е д е л и л все
конференц-залы под беспо
лезный семинар по менедж
менту.
u sin g
(Stream R ead er r e a d e r
(S trea m W riter w r i t e r
= new S tr e a m R e a d e r { " s e c r e t _ p l a n . t x t " ))
Проблемы на работе
П е р е д в а м и Б р а й а н . О н р а з р а б о т ч и к п р о гр а м м н а С # и л ю б и т с в о ю работу, н о обожает у с т р а и в а т ь н е
з а п л а н и р о в а н н ы е в ы х о д н ы е . Е го н а ч а л ь н и к н е н а в и д и т та к и е с и т у а ц и и , п о э т о м у Б р а й а н у п р и х о д и т с я
придум ы вать веские п р и ч и н ы . ^ —
Сынок, с марта ты
О
отпрашиваешься к
ветеринару уже девятый раз.
Не поискать ли тебе новую
работу?
Excuse Manager
Брайан хочет
Иногда Брайану лень
выдумывать. Поэто
хранить пере- Bccuse My dog has a headachae
м у добавим кнопку
чень причин ухода
с работы в одном FtemAs Didnt woik. Boss knows I dwit have a dog Random, которая
будет случайным
м ест е, поэтому Last Wednesday, Mardi 04. Ж Ш Q -»
выделим для него образом выбирать
3 ^ 1 /2 )1 0 12;®:51 PM причину прогула.
Ы кпку- Wedaie
f^ n k m
дальше * 417
уваж ительные причины д ля брайана
Д о б а в и м в ф о р м у п о л е C u r r e n t E x c u s e для о т о б р а ж е н и я в ы б р а н н о г о о п р а в д а н и я . Вам п о
т р е б у ю т с я т р и п е р е г р у ж е н н ы х к о н с т р у к т о р а : для з а гр у з к и ф о р м ы , для о т к р ы т и я ф айла и
для с л у ч а й н о го о п р а в д а н и я . Д о б а вьте м е то д ы O p e n F i l e () и S a v e () для о т к р ы т и я и с о х р а
н е н и я о п р а в д а н и я . А затем ещ е и м е то д U p d a t e F o r m ( ) , о б н о в л я ю щ и й э л е м е н ты у п р а в л е
н и я (а в о т п о л е з н ы е п о д с к а з к и ) : Этот парам ет р определяет,
вносились ли изменения в ф ор
p r i v a t e v o id U p dateF orm (bool changed)
му. Вам потребуется поле
i f (! ( ! changed) { '' ^
Помните, что для его хранения.
t h is .d e s c r ip t io n .T e x t = c u r re n tE x c u se.D e scr ip tio n ;
символ ! озна t h i s . r e s u l t s . T ext = c u r r e n tE x c u se .R e su lts,•
чает Н Е Т — то t h is j la s t u s e d .V a lu e = c u r re n tE x c u se .L astU sed ;
ест ь здесь п р о i f ^ i g t r i n g . I s N u l l O r E m p t y ( c u r r e n t E x c u s e . E x c u s e P a t h ) )
веряется, не яв _ y T i r e D a t e . T e x t = F i l e . G e t L a s t W r i t e T i m e ( c u r r e n t E x c u s e . E x c u s e P a t h ) . T o S t r i n g O ;
ляется ли марил
у- , t h i s . T e x t = " E x c u s e M a n a g e r " ; <5 ..^ А ва ж д ы щ е л к н и т е на э л е м е н т а х в в о д а для
г о п іу а в - с о зд а н и я о б р а б о т ч и к а с о б ы т и й C h a n g e d . Т р и
обработ чи ка собы т ий сначала б уд ут м ен я т ь
(^дниеМ п у с t h i s . T e x t = " E x c u s e M a n a g e r * " ; ( ^ э к з е м п л я р E x c u se , а з а т е м в ы з о в у т м е т о д
или со зн ач е ^ h i s . form C han ged = c h a n g e d ; U p d a t e F o r m f tr u e ) , дальи ле с п о с о б и з м е н е н и я п о -
^ u ll- } ф о р м ы в ы б и р а е т е т о л ь к о вы.
П р и с в о й т е н а ч а л ь н о е з н а ч е н и е п а р а м е тр у L a s t U s e d в к о н с т р у к т о р е ф о р м ы ;
p u b lic F o rm lО {
In itia liz e C o m p o n e n t();
c u r r e n tE x c u s e . L astU sed = la stU s e d .V a lu e ;
418 глава 9
чтение и запись файлов
парамет р MessageBoxlcon.
★ В о к н е д и а л о га O p e n п о у м о л ч а н и ю д о л ж н а б ы т ь о т к р ы т а п а п к а , в ы б р а н н а я п о л ь з о в а
те л е м п р и п о м о щ и к н о п к и F o ld e r.
★ Д об а вьте м е то д O p e n () в класс E x c u s e .
★ М е т о д C o n v e r t . T o D a t e T i m e О з а гр у ж а е т с о х р а н е н н ы е д а н н ы е в э л е м е н т у п р а в л е
н и я D a t e T i m e P i c k e r , в ы б и р а ю щ и й врем я.
★ П о п ы т к а о т к р ы т ь н о в о е о п р а в д а н и е , н е с о х р а н и в п р е д ы д ущ е е , д о л ж н а п р и в о д и т ь
к п о я в л е н и ю в о т т а к о г о о к н а д и а л о га :
Д ля вызова подобных окон диалога
используйте перегруженный метод
MessageBox.ShowO, позволяющий за -
аавате> парам ет р MessageBoxButtons.
YesA/c?. При щелчке пользователя на
1^рпке No мет од ShowO возвращает
PialogResult.No. ^
★ О б ъ е к т R a n d o m н у ж н о будет с о х р а н и т ь в п о л е и п е р е д а ть о д н о м у и з п е р е гр у ж е н н ы х
к о н с т р у к т о р о в о б ъ е к та E x c u s e .
★ Е сл и о т к р ы т о е в д а н н ы й м о м е н т о п р а в д а н и е н е с о х р а н е н о , д о л ж н о п о я в л я т ь с я п о
ка з а н н о е в ы ш е о к н о д и а л о га с п р е д у п р е ж д е н и е м .
дальше ► 419
решение упражнения
Вот как выглядит код для поиска оправданий отсутствия на работе, который мы написали
ненке для Брайана.
ешение форма использует поля для сохранения
p r i v a t e E x c u s e c u r r e n t E x c u s e = n e w E x c u s e () текущего объекта Excuse о о ы д р а н н у ю
p r iv a te s tr in g se lec ted F o ld e r = i / папку, a также, запоминает, оыл ли из
p r i v a t e b o o l form C han ged = f a l s e ; менен эт от объект. К-роме того, она
R andom r a n d o m = n e w R a n d o m ( ) ; ( сохраняет объект Random для кнопки
Random Excuse.
p r i v a t e v o i d folder_Click( o b j e c t s e n d e r , E v e n t A r g s e ) {
fo ld e r B r o w s e r D ia lo g l.S e le c te d P a th = s e le c te d F o ld e r ; выбора ПОАЬЗО-
D i a l o g R e s u l t r e s u l t = f o l d e r B r o w s e r D i a l o g l . S h o w D i a l o g ( ) ; "Л ПоСЛе OWOop«
i f ( r e s u l t == D i a l o g R e s u l t .OK) { / бйИлелеМ ^
s e le c te d F o ld e r = fo ld e r B r o w s e r D ia lo g l.S e le c te d P a th ; сохр^няе
s a v e . E n ab led = t r u e ; ( %кл\ОЧает v
___ ) m e КН01А.КИ.
o p e n .E n a b le d = tr u e ;
ran d om E xcu se. E n ab led = t r u e ; Это символ оператора ИЛИ, выражение и м е
} ет значение true (если не указано оправдание)
ИЛИ результ ат .
p r iv a te v o id sa v e _ C lic k (o b je c t sen d er, E v e n t A r g s e j /{
i f ( S t r i n g .I s N u l lO r E m p t y ( d e s c r i p t io n .T e x t ) m i t r i n g . IsN u llO rE m p ty ( r e s u l t s . T e x t ) ) {
M es sa g e B o x .S h o w (" P le a s e s p e c i f y an e x c u g ^ a n d a r e s u l t " ,
" U n a b l e t o s a v e " , M e s s a g e B o x B u t t o n s . OK, M e s s a g e B o x I c o n . E x c l a m a t i o n ) ;
retu rn ;
} Здесь задаются ф и л ь -
s a v e F ile D ia lo g l. I n itia lD ir e c to r y = se lec ted F o ld e r ; ^
<тры для окна S a v e A s .
s a v e F i l e D i a l o g l . F i l t e r = "Text f i l e s ( * . t x t ) | * . t x t | A l l f i l e s [ *. *) I* .* " ;
s a v e F i l e D i a l o g l . F ile N a m e = d e s c r i p t i o n . T e x t + " . t x t " ;
D i a l o g R e s u l t r e s u l t = s a v e F i l e D i a l o g l . S h o w D i a l o g ()
i f ( r e s u l t == D i a l o g R e s u l t . O K ) {
c u r r e n tE x c u s e . S a v e ( s a v e F il e D ia l o g l . F ile N a m e);
U p d a te F o r m (fa lse ); ч а с т и nurSJ 2 ^Уре- в нижнеи
M e s sa g eB o x .S h o w (" E x c u s e w r i t t e n " ) ; ст рочки две
1 ^ ^ <^екстовык
}
420 глава 9
чтение и запись файлов
p r i v a t e b o o l CheckChanged() {
i f (form C han ged ) {
D i a l o g R e s u l t r e s u l t = M e s sa g e B o x .S h o w (
"The c u r r e n t e x c u s e h a s n o t b e e n s a v e d . C o n t i n u e ? " ,
" W arn in g" , M e s s a g e B o x B u t t o n s .Y e s N o , M e s s a g e B o x I c o n .W a r n in g )
i f ( r e s u l t == Ц1 a 1 o g R e s u l t ^ I ^ ,
retu rn f a l s e , M essa g eB o x S h o w C ) в о зв р а щ а е т
} перечисление DialogResult, которое мы
retu rn tru e;
можем проверить. Это т ри обработчика
}
событшА СЫпдес1 рля
p r i v a t e v o i d description_TextChanged(object s e n d e r , EventArgs е) { ) лей ввода ^°рМЫ. Ш Ме-
c u r r e n tE x c u se .D e sc r ip tio n = d e s c r ip tio n .T e x t; / нение состояния
U p d ateF orm (tru e);
‘ из них говорит о >^оМ,
ч т о оправдание было от
} редактировано, поэтому
Сначала следует обно-
p r i v a t e v o i d results_TextChanged( o b j e c t s e n d e r , E v e n t A r g s e ) { \ вить экземпляр Excuse
c u r re n tE x c u se.R e su lts = r e s u lt s .T ex t;
^ т ем e^e^ZZb
> ГUpdateFormO,
U p d ateF orm (tru e); добавить
} звездочку в строку за
головка и
p r i v a t e v o i d la s t U s e d _ V a lu e C h a n g e d (o b je c t s e n d e r , E ven tA rgs e) { свойству Changed значе
c u r re n tE x cu s e . L astU sed = la s tU s e d .V a lu e ; ние true.
J
значения true методу UpdateFormO
U p d a t e F o r m ( t r u e T ^ — ./Те/зеЭдча
заставляет его пом ет ит ь форму как из-
J
, мененную, но не обновлять состояние полей
c l a s s E xcuse { ввода.
p u b lic s tr in g D e sc rip tio n { g e t; se t; }
p u b lic s tr in g R e su lts { g e t; s e t ; }
p u b l i c D ateT im e L a s tU s e d get; s e t ;
p u b lic s t r i n g E xcuseP ath get; se t;
Кнопка Random Exru<p , ^
^%ectoru.QetFiles(\ dAt u^ ^°^^^<^^'‘^ метод
p u b lic E xcuse 0 { лов Є выбранной n a n Z е Т . Т " ' <^^кстовык ф ай-
E xcu seP ath = чего выбиЪается с Т Х й н Т ^ ^ ^ ^ ^осТе
} крыть один из ф айлов чтобы от~
p u b lic E x c u s e (s tr in g excu seP ath ) {
O p e n F ile (ex c u seP a th );
} гарантировали
Mfc>i
p u b l i c E xcuse(R an dom random, s t r i n g f o l d e r ) использование опера-
s t r i n g [] f i l e N a m e s = D i r e c t o r y . G e t F i l e s ( f o l d e r . * .tx t" ) тора using при каждом
O p e n F ile ( f ile N a m e s [ r a n d o m .N e x t ( f ile N a m e s . L e n g t h ) ]) открытии потока.
} В эт ом случае наши
p r iv a t e v o id O p e n F ile (s tr in g excu seP ath ) { файлы всегда будут
th is.E x c u se P a th = excu seP ath ; закрыты.
u s i n g (S tr ea m R ea d er r e a d e r = new S t r e a m R e a d e r ( e x c u s e P a t h ) ) {
D e s c r ip tio n = r e a d e r .R e a d L in e 0 ;
R e s u lt s = r e a d e r .R e a d L in e 0 ;
L a stU se d = C o n v e r t. T o D a te T im e (r e a d e r .R e a d L in e 0 ) ;
}
}
p u b l i c v o i d S a v e ( s t r i n g file N a m e ) {
u s i n g ( S t r e a m W r i t e r w r i t e r = n e w S t r e a m W r i t e r ( f i l e N a m e ) )'.
{
w r ite r .W r ite L in e (D e sc r ip tio n );
w r ite r .W r ite L in e (R e su lts);
w r ite r .W r ite L in e (L astU sed ) ;
Вы вызывали мет од LastUsed.ToStringO? Помните, что мет од \л!гiteUneO вызывает
его автоматически!
дальше > 421
блок вы б о р а
enum BodyPart {
WTVPM
Какие проблемы могут возникнуть при написании кода
с многочисленными операторами if/else? Подумайте об
опечатках, несовпадающих скобках и т. п.
422 глава 9
чтение и запись файлов
default-., кот о-
switch начинает
работу с указания значения
-------- с которым будет осущТст:
Suits suit; ,/ Дйннмы
^ оператор switch вт ь7а
swxtch (suitstring) ( метода, поэтами
переменная suit сохранена
case "Spades' о строке. ^
suit = Suits.Spades;
break;
case "Clubs": . Каждая из строчек case срав
нивает некоторое значение
suit = Suits .Clxibs; с переменной, указанной в
строчке switch. В случае со
break; впадения вьтолнян?тся все
операторы, расположенные до
case "Hearts" ; — ключевого слова break.
suit = Suits.Hearts;
break; Строчка default стоит
в самом конце. Если значе
case "Diamonds"; ние переменной не совпало
ни с одним из предложенных
suit = Suits.Diamonds; вариантов, будут выполнены
операторы, стоящие после
break; ключевого слова default.
default:
MessageBox.Show(suitstring + " не подходит!");
424 глава 9
чтение и запись файлов
В о т к р а й н е п о л е з н ы й м е то д S p l i t О . О н р а з б и в а е т к а ж д у ю с т р о
ку н а м а сси в б олее м е л к и х с т р о к , пе р е да ва я е й м а с с и в c h a r [ ]
р а з д е л и те л ь н ы х зн а ко в . Здесь строку nextCard
разбивают при помощи пробелов.
В результ ат е строковая
p u b lic D e c k (s tr in g file n a m e ) { переменная Six of Diamonds
c a r d s = new L i s t < C a r d > ( ) ; превращается в массив {"Six”,
S tr e a m R e a d e r r e a d e r = new S t r e a m R e a d e r ( f i l e n a m e ) ; "o f’\ "Diamonds”}.
w h i l e ( ! r e a d e r . E ndO fStream ) {
b o o l in v a lid C a r d = f a l s e ; J
s t r i n g n e x t C a r d = r e a d e r . R e a d L i n e () ; U :
s t r in g [ ] ca rd P a rts = n e x tC a r d .S p lit(n e w ch a r[] { ' });
V a lu es v a lu e = V a lu e s.A c e;
s w it c h ( c a r d P a r t s [0]) {
c a s e "A ce": v a l u e = V a l u e s . A c e ; b r e a k ;
c a s e "Two": v a l u e = V a l u e s . T w o ; b r e a k ;
Onejoamop switch
c a s e "Three" v a lu e = V a lu e s.T h r e e ; break;
сравнивает зна
c a se "Four"; v a lu e = V a lu e s.F o u r ; break; чение с первым
c a se " F iv e" : v a lu e = V a lu e s.F iv e ; break; словом строки.
case " S ix " : v a l u e = V a l u e s . S i x ; b r e a k ; В случае совпаде
case "Seven": v a lu e = V a lu e s . S even; b rea k ; ния значение при -
case " E ig h t" : v a lu e = V a l u e s . E i g h t ; b rea k ; сваивается пере
case " N in e" : v a l u e = V a l u e s . N i n e ; b r e a k ; менной value.
case "Ten": v a l u e = V a l u e s . T e n ; b r e a k ;
case Jack": v a lu e = V a lu e s.J a c k ; break;
case Q ueen": v a l u e = V a l u e s . Q ueen; b r e a k ;
c a s e " K in g" : v a l u e = V a l u e s . K i n g ; b r e a k ;
d e f a u lt : in v a lid C a r d = tr u e ; break;
}
S u its s u it = S u its .C lu b s ;
s w it c h ( c a r d P a r t s [2]) {
c a s e "Spades": s u i t = S u it s .S p a d e s ; b rea k ; с 6
c a s e " C lu b s" : s u i t = S u i t s . C l u b s ; b r e a k ;
c a s e "H earts" : s u i t = S u i t s .H e a r t s ; b reak ;
c a s e " D iam on d s" : s u i t = S u i t s . D i a m o n d s ; b r e a k ;
d e f a u lt : in v a lid C a r d = tr u e ; break;
}
if ( ! in v a lid C a r d ) {
c a r d s.A d d (n e w C a r d (su it, v a lu e ));
}
426 глава 9
чтение и запись файлов
Э кз е м п л я р о б ъ е к та и м е е т н е к о е П ри с е р и а л и з а ц и и полностью
состояние. О б ъ е к т п р и э то м сохраняется состояние объек
«знает» т о л ь к о , ч т о д елает экзем та, п о э т о м у к а ж д ы й э к з е м п л я р
п л я р о д н о го класса о т л и ч н ы м м о ж е т б ы т ь в о з в р а щ е н в кучу.
о т д р у г о го э к з е м п л я р а э т о г о ж е
класса.
00100101 ^ ^с о х р а н е н ы в ^
_ I вм ест е с аополнителЙ ой
01000110
да л ьн ей ш его в о с с т а
новлени я о б ъ ек т а ( н а п ш -
file.d at
И за те м ...
З атем , м о ж е т б ы т ь , че р е з м н о го д н е й и
в д р у г о й п р о гр а м м е , в ы м о ж е т е п р о в е
с т и десериализацию о б ъ е кта . И с х о д
н ы й класс будет в о с с та н о в л е н и з ф айла
ровно в то ж е состояние, в к о т о р о м о н
б ы л , со все м и е го п о л я м и и з н а ч е н и я м и .
Ч то именно мы сохраняем
В ы уж е зн аете, ч т о объект хранит и н ф о р м а ц и ю в полях. С о о т в е т
с т в е н н о п р и с е р и а л и з а ц и и н у ж н о с о х р а н и т ь ка ж д о е и з п о л е й .
П о д у м а й т е о б это м . К а ка я ч а с т ь о б ъ е к та я в л я е тс я п о т е н ц и а л ь н о у н и
ка л ь н о й ? П р е д с та в ь те , ч т о и м е н н о н у ж н о в о с с т а н о в и т ь , ч т о б ы п о л у
ч и т ь о б ъ е кт, к о т о р ы й б ы л с о х р а н е н . Т а к и л и и н а ч е , н о все с о д е р ж и м о е
к у ч и н у ж н о з а п и с ы в а ть в ф айл.
ИЛПРЯГИ
м о з г и
Каким образом следует сохранить объект Car, чтобы потом его
можно было восстановить в исходное состояние? Предположим,
что в автомобиле едут три пассажира, он имеет трехлитровый
двигатель и всесезонные шины... разве вся эта информация —
не часть состояния объекта Car? И что с ней делать?
Объект Engine
является закры
тым. Нужно ли
(U Juna) его сохранять?
p a s s e n g e r С 6 ^ , g масілл«
Э1ЛЛ.0 a Ц іл^о
«о н»«“
К аж ды й из о б ъ е к
т о в перечи слени я
^ P assen ger и м е ет
ссы лки на д р у ги е
объект ы . С ледует
ли их сохран ят ь?
■ ^ L is\*
428 глава 9
чтение и запись файлов
Пр» *“
объект ы .
Одним из полей
объекта Кеппе!
является коллекция
Ust<V 0 3 >, в кот о
рой содержатся Ш
объекта Vog (Со- ^
бака). Ш также q
требуется сериа-
лизовать. к
оба объекта Dog и м е
ю т ссылки на объект
DoggylD (И дент иф и
катор собаки) и объ
ект Collar (Ошейник).
Они тоже будут
подвергнуты сериали ^ ъ е к т О О '^ II
зации.
using System.Runtime.Serialization.Formatters.Binary;
• • •
BinaryFormatter formatter = new BinaryFormatter();
430 глава 9
чтение и запись файлов
Атрибут [Serializable]
А т р и б у т о м н а зы в а е т с я сп е ц и а л ь н ы й тег, д о б а в л я е м ы й в вер х н ю ю ч асть классов. С пом ощ ью атри бутов
в C# с о х р а н я ю т с я м е т а д а н н ы е , т о есть и н ф о р м а ц и я о том , как нуж но и с п о л ь зо в ат ь код. Д о б а в и в а т р и
бут [ S e r i a l i z a b l e ] н а д о б ъ я в л е н и е м к л а с с а , вы п о к азы в а ет е, ч т о э т о т класс м ож н о сер и ал и зо вать.
П о д о б н о е допусти м о, скаж ем , д ля классов с п о л я м и зн а ч и м ы х т и п о в (н ап р и м ер , i n t , d e c i m a l и л и
епитп). О тсу тстви е это го атр и бу та, как и н а л и ч и е в классе п о л ей , с е р и а л и за ц и я к о т о р ы х н ево зм о ж н а,
п р и в о д я т к со о б щ ен и ю об ощ иб ке. Убедитесь в этом сами...
{ î h e b a r * h a s $100
u s in g (Stream in p u t = F il e .O p e n R e a d ( " G u y _ F i l e . d a t " ))
B in aryF orm atter fo rm atter = new B i n a r y F o r m a t t e r ( ) ; (3»в$№ Receive
jo e = (G u y )fo r m a tte r .D e s e r ia liz e (in p u t); to Jae
пр о гр ам м ы . Т еп ер ь Д ж о м о ж ет с о х р а н и т ь с во и к а п и т а л ы в ф а й л
и в о с с та н о в и т ь и х в л ю б о й м ом ент.
дальше ► 431
сериализация колоды карт
© Д обавление атрибута
Д о б ав ьте атр и б у т [ S e r i a l i z a b l e ] к о б о и м классам , скоп и ро-
ван ы м в п р о ек т. , э т о г о сериализация
^ ___ У невозможна.
432 глава 9
чтение и запись файлов
Предварительная подготовка з а ко н ч е н а ... сер иал и зуем колоду
Д об авьте к н о п к и , у п р а в л я ю щ и е з а п и с ь ю и ч т е н и е м к о л о д ы . С в е р я й те с ь
с р е зул ьтата м и н а к о н с о л и . К о л о д а , к о т о р у ю в ы з а п и с ы в а е те в ф айл ,
д о л ж н а с о в п а д а ть с к о л о д о й , к о т о р у ю в ы ч и та е те . 0 5 ш к т B in a r ijfo r m a U e r
бер&т объекты, п о
private void buttonl_Click(object se n d e r , EventArgs a) { м еченны е а т р и б у т о м
Deck deckToWrite = R a n d o m D e c k (5);
using (Stream output = F i l e .C r e a t e (" D e c k l .
“ “ к “ «:“ Г сы в » е» «
B i n a ryF ormatte r bf = n e w B i n a r y F o r m a t t e r ();
bf.Serialize(output, deckToWrite); в i o K с « о » » »
т о д а S e r i a l iz e Q .
}
DealCards(deckToWrite, "Что было з а п и с а н о в ф а й л " );
Метод peserializeQ объ
} екта BinaryFormatter
private void button2_Click(object sender, EventArgs e) ■
возвращает объект
обобщенного типа, п о
using (Stream input = F i l e . O p e n R e a d ( " D e c k l . d a t " )) {
эт ому требуется осу
Bina ryForma tter bf = n e w B i n a r y F o r m a t t e r (); ществить приведение
Deck deckFromFile = (D e c k ) b f . D e s e r i a l i z e ( i n p u t ) ; к объекту Реек.
DealCards (deckFromFileTT^TO бьіло п р о ч и т а н о и з ф а й л а " ) ;
}
}
С ериализация набора колод
П о с л е о т к р ы т и я п о т о к а в н е го м о ж н о з а п и с ы в а ть п р о и з в о л ь н о е к о л и ч е
с т в о и н ф о р м а ц и и . П о э т о м у д о б а в и м две к н о п к и , п о з в о л я ю щ и е с о х р а
н я т ь в ф айл п р о и з в о л ь н о е к о л и ч е с т в о ко л о д .
Обратите внимание,
как в строчке, чит а
private void button3_Click(object sen d e r , EventArgs e) {
ющей информацию
using (Stream output = F i l e .C r e a t e (" D e c k 2 .d a t " )) { из файла, осущ ест
B i n a ryF ormatte r bf = n e w B i n a r y F o r m a t t e r (); вляется приведе
for (int i = 1; i <= 5; i++) { ние результ ат ов
3 один поток Deck deckToWrite = R a n d o m D e c k ( r a n d o m . N e x t (1,10)); работы метода
можно сериали PeserializeQ к объек
bf.Serialize(output, deckToWrite);
зовать несколь т у Реек.
DealCards(deckToWrite, " К о л о д а #" + i + " записана");
ко объектоо.
}
Число сериализуемых
} объектов может быть
private void button4_Click(object sender, EventArgs e) { произвольным, главное —
using (Stream input = F i l e . O p e n R e a d (" D e c k 2 . d a t " )) { не забывать приводить
Binary Formatt er bf = n e w B i n a r y F o r m a t t e r ();
читаемые из потока объ-
екты к нужному типу.
for (int і = 1; і <= 5; І+-І-) {
Deck deckToRead = (Deck)bf.Deserialize(input);
DealCards(deckToRead, " К о л о д а #" + і + " прочитана")
}
}
}
О П р осм о тр результата
О т к р о й т е ф айл D e c k l . d a t в п р и л о ж е н и и Б л о к н о т . В ы н а й д е те та м всю
и н ф о р м а ц и ю , н е о б х о д и м у ю для в о с с т а н о в л е н и я о б ъ е к та D e c k .
сД еной
Для представления символов или строк в виде байтов в .NET используется Ю ни
код. К счастью, в W indows имеется инструмент, позволяющий понять принцип работы Юникода. Откройте
приложение Character Мар (выберите в меню Start команду Run. введите «charmap.exe» и щелкните на
кнопке ОК).
П р и взгляде н а с и м в о л ы и з р а з н ы х я з ы к о в с т а н о в и т с я п о н я т н о , с к о л ь к о и н ф о р м а ц и и тр е б у е тся
за п и с а т ь в ф айл для с о х р а н е н и я т е к с т а . П о э т о м у .N E T п р е о б р а з у е т все с т р о к и и с и м в о л ы в ф о р
м ат Ю н и к о д . Б е р у тс я д а н н ы е (н а п р и м е р , буква Н ) и п р е о б р а з у ю т с я в б а й т ы (ч и с л о 72). В едь б укв ы ,
ц и ф р ы , п е р е ч и с л е н и я и д р у ги е д а н н ы е х р а н я т с я в п а м я т и и м е н н о в в ид е б а й то в . У з н а ть ж е соответ^
с т в и е м еж д у ч и с л а м и и с и м в о л а м и м о ж н о в Т а б л и ц е с и м в о л о в (C h a ra c te r М а р ).
в Chaiacttr Мар
Выдерите в с п и
ске шрифтов A nal и Pont! О feW
ЙФ
прокрут ит е вниз до
еврейского алфавита. (h 4 j J b fb H j Hj G G Ъ Ъ G e Ц Q q w
Щ елчком выберите д A. Юникод — это
6 w
Л A
W V Ч-- V Л, V 0
символ Shin. 0 A Л A Л
•w' д 0 индустриальный
у V' V У w W 0 0
■ Л, 1 0
стандарт. Вы
После выбора символа в t 9 H 3. я T можете посе
строке состояния поя IN Ü t Э ■? □ n
1 Э Y i Л о M n т ит ь сайт h ttp : //
вится его код. Д ля буквы « ' )i_ i . Л.
ё w Л unicode.org/
>
Shin ~ это число OSE 4 ip f S’ 1 i
j ] iS i г Cl ih <- с
о шестнадцатеричной п
J j iß i t t - Ji 4 J и
системе счисления.
J 0 У Л V 0 Q 0 0 Д Ö Г: 6
V 0 ö * > т r £ a 1 у A % z , ♦
J
Д ля преобразования р- ____ ^
полученного значения
в десятичную систему
воспользуйтесь калькуля Hehiewletar
тором Windows в режиме
Scientific.
434 глава 9
чтение и запись файлов
/ ^ ^ Ф
^ З ап и ш ем в ф айл обы чную строку и прочитаем е е .
В о с п о л ь з у й те с ь м е то д о м W r i t e A l l T e x t { ) , ч т о б ы з а с та в и ть п е р в у ю к н о п к у з а п и с ы в а ть
с т р о к у « E u re ka !» в ф айл « e u re k a .tx t» . З атем со зд а й те м а сси в б а й т о в e u r e k a B y t e s , п р о ч и
т а й т е в н е го и з ф айл а и в ы в е д и т е п о л у ч е н н ы й р е зультат:
дальше ► 435
изменение порядка байтов
byte[] greeting;
Ые\\о\и
greeting = File.ReadAllBytes(filename
/ ІПІПІПІПІПІПІПІ
0 1 2. } ^ 5 4
JZ 101 108 108 111 33 33
Это статический
метод для объекта
Arraцs, меняюищи
В ои /
порядок байтов н й
обратный. Здесь он
используется, чтобы
показать, как внесенные
в массив байтоб
изменения б л м я ю т Нй \
вид читаемой из <раила
информации.
Array.Reverse(greeting);
File.WriteAllBytes (filen^e, greeting)
массива
После за п иси
байтов в файл текст
также оказывается 7 переменных типа byte
^ перевернутым.
ОІПІПІПОІПІП
0 1 г г ^ ч к
33 33 111 108 108 101
О С о зд а й те к о н с о л ь н о е п р и л о ж е н и е и у к а ж и т е д а н н ы е для з а п и с и в ф айл.
in t i n t V a l u e = 4 8 7 6 9 4 1 4 ;
String stri n g V a l u e = "Hello!"; д а ^ ^ ^ с о з а ^ е т новый файл,
даже при наличии уже суш,ест6ующего.
byte[] byteArray = { 47, 129, 0, 116 };
float floatValue = 491.695F;
открывает
имеющийся файл и записывает новию
char charValue = 'E '; информацию поверх старой.
© П р и п о м о щ и м етода F i l e . C r e a t e () о тк р о е м н о в ы й п о т о к : ^
О П р и ка ж д о м в ы з о в е м е то д а W r i t e О в к о н е ц ф а йл а , в к о т о р ы й о с у щ е с тв л я е тс я
за п и с ь д а н н ы х , будет д о б а в л я ть с я байт.
дальше * 437
смесь байтов
Значения типов float и int занимают
по 4 бита, в то время как типы long
и double т риебую т по 8 байт. ^озьми в руку карандаш
V Решение
^ ^ q z о& ± 8 ^ вс вс Z 1 Z f 8 1 0 0 7 4 f e d s f s 4 3 4 -s - ZO b y te s
-J I____ ______ _ — »I--------- jn-Z
in t V a lu e ~ Ш ш д \/ а (и е b yte A rray V
c k a r V a iu e
О Н а ч н е м с за д а н и я о б ъ е к т о в F i l e S t r e a m и B in a r y R e a d e r :
© У к а ж и т е т и п д а н н ы х , к о т о р ы й будет в о з в р а щ а ть к а ж д ы й и з м е то д о в о б ъ е к та
B in a r y R e a d e r .
in t in tR ea d = r e a d e r . Readlnt32();
str in g str in g R e a d = reader.ReadStringO; ) ЙЗ которых возвращает данные
byte[] byteA rrayR ead = r e a d e r . R e a d B y t e s (4) ;
flo a t flo a tR ea d = r e a d e r . ReadSingle () ; ^ ^ a d B y te s Q ^ M ^ t) /^ '^ ’’
char c h a r R e a d = r e a d e r . ReadChar(); Щет парам ет р, сообщающий
В о т к а к о н будет в ы гл я д е ть :
438 глава 9
чтение и запись файлов
Г
С ериализуем два объекта C ard в различны е ф айлы
И с п о л ь з у й т е н а п и с а н н ы й р а н е е к о д , ч т о б ы с е р и а л и з о в а ть тройку крестей в ф айл t h r e e - с .
d a t , а шестерку червей — в ф айл s i x - h . d a t . У б е д и те с ь , ч т о о б а ф айла н а х о д я тс я в о д н о й
п а п к е , и м е ю т о д и н а к о в ы й р а зм е р и о т к р о й т е и х в Б л о к н о т е :
ß файле
встречаю тся
1 three-C - N otepad
З Н Й К О М Ь іЄ СЛОбЙ,
но по больше-и File Edit F o rm at V iew H e lp
т ст и он не- W yy P S e r i a liz e a deck of cards, v e r s i o n = l . 0 . 0 . 0 .
~ ^
ч іхтй еМ - ^
Cu1ture=neutral, PubTicKeyToken=nu11 serialize_a_deck _of_card s.C ardi
i< S u it > k _ B a c k in g F ie ld r < v a lu e > k _ B a c k in g F ie 1 d -5 e r ia l1 z e _ a _ d e c k _ o f_ c a r d s . S u it S T
s e r ia T iz e _ a _ d e c k _ o f_ c a r d s . v a lu e s , | y y y y t 5 e r ia l- iz e _ a _ d e c k _ o f _ c a r d s . s u i t s
. v a l u e __ s- ‘u y ^ iy s e r i a l l z e _ a _ d e c k _ o f _ c a r d s . v a l u e s .v a lu e — a, >• /
IJ e J ^ e ^ ej^ H u ix ie с ш р а н и Щ ) U И ^ ^ о д о л ж и м !
439
от празднуем наши различия
B yte #322 : 1 v e r s u s 3
B yte #382: 3 versus 6
В е р н и т е с ь к п е р е ч и с л е н и ю S u i t s в п р е д ы д у щ е й главе, и в ы у в и д и т е , ч т о м а с ти C lu b с о о т
ве тс тв у е т з н а ч е н и е 1, а м а с ти H e a rt - з н а ч е н и е 3. Э т о п е р в о е р а з л и ч и е . В т о р о е р а з л и ч и е ,
к а к л е г к о д о га д а ть ся, — с т а р ш и н с т в о к а р т. Р а з л и ч н о м у к о л и ч е с т в у б а й т т а к ж е и м е е тс я о б ъ
я с н е н и е : о б ъ е к т ы м о гл и н а х о д и т ь с я в р а з н ы х п р о с т р а н с т в а х и м е н , ч т о и з м е н и л о д л и н у
ф айла.
Помните, каким образом ин Получается, что если байт в сериа
формация о пространстве имен лизованном файле соот вет ст вует м а
ёкАючается в с е р и ^ и зо в а н н ь Т ст и, мы сможем поменять маст ь карты,
пространства имен прочитав файл, изменив один байт и сно
различаются, размер байтов ва записав информацию в файл. (И м ей
о сохраненном файле также те в виду, что в ваияем сериализованном
оуоет отличаться. файле информация о маст и может быть
сохранена в другом м ест е)
440 глава 9
чтение и запись файлов
3 king-s - Notepad
дальше ¥ 441
69 73 6е 27 74 20 74 68 69 73 20 66 75 6е 3f0a
В о т к а к о н будет в ы гл я д е ть в дам пе д а н н ы х :
К а ж д а я с т р о ч к а в дам пе д а н н ы х п р е д с т а в л я е т ш е с т н а д ц а ть в ы в о д и м ы х с и м в о л о в , и з к о т о р ы х о н а со б
с т в е н н о и б ы л а создана. П е р в ы е ч е т ы р е ц и ф р ы в н а ш е м случае — э т о с м е щ е н и е в н у т р и ф айла: п е р в а я
с т р о ч к а н а ч и н а е т с я с О, след ую щ ая с 16 (в ш е с т н а д ц а т е р и ч н о й с и сте м е э т о 10), след ую щ ая с 32 и т. д.
(Д р у ги е ш е с т н а д ц а т е р и ч н ы е д а м п ы м о гу т в ы гл я д е ть п о-д ругом у.)
in t j = 0x20;
M e s s a g e B o x .S h o w ( " T h e v a l u e is " + j );
442 глава 9
чтение и запись файлов
}
str in g buf ferC o n ten ts = new string (buffer) ; ^ ^ ^
w r i t e r W r i t e L i n e (" " + b u f f e r C o n t e n t s . Substring(0, charactersRead));
дальше у 443
построение дампа данных
С о зд а й те к о н с о л ь н о е п р и л о ж е н и е с и м е н е м hexdumper. К о д для н е г о н а х о д и т с я н а с л е д ую щ е й с т р а н и
ц е, а в о т к а к в ы гл я д и т р е з у л ь та т е го р а б о т ы :
I C;\Windo№s\system32Sc!nd.f
|‘
,::\teiiip>hexdurapei* 1
111 sage - >iexdrkpe;I- ,fi l s - -to ~ d l np
1
г :\t erap^Jiexdumpe i* dot JS~!11./t~exi St-dat
1lie doe:s n o t ISXi:St: doe not“exist -dat' ^ — "
А а м п данных
usage - hexdpipejr file-' to "'.durip суи^ествуюи^его
файла выводит
r ‘:\temp>hexduripei' kina- s.dat ся на консоль.
‘«100: 00 01 00 00 00 ff ff ff 01 08 00 .00 00 00 00 ..
00 0c 02 00 80 00,/50 53
И020: 20 61 20 64 65 63 6b 20
— 65 72 69 61 6c 69 7a 65 . . , - ..PSes'ialxae
6f 66 2,0 63 61 72 ■64 73 cl d.i?ck of cai'ds
ЙЙ30: 2c 20 S6 6S 72 69 6f 6e 3d 31 2e 30 2e 30 2e .. Uersion =1.0,0.
H040: 30 2c 20 43 75 бЗ 74 75 — 72 65 3d 6e 65 75 74 72 .0, Gultui'e -neiiti'
И0\.0:- 61 6c 2c 20 50 7S 62 6c — 69 63 4b 65 79 54 6f 6b
И060: 65 6e 3d 6e 7S 6|fc 6c 05 — 01 00 00 '00 le 53 65 72
al, PublicKeA^Tok
e n 11 11..... 8er
i Обычно вывод на
И07В: 69 61 6c 69 7a 6Б Sf 61 — Sf 64 6S 63 6b Sf 6f 66 ia iis e_;a__deck_o f консоль осущ ест
ИИ80: 5f 63 61 72 64 ,?3 2e 43 — 61 72 64 02 00 80 00 15
ИЯ90: 3c S3 75 69 74 3e 6b Sf — Sf 42 61 63 6b 69 6e 67
„cai'-ds,Card..... вляется методом
<Suit >k_.Backing
46 69 65 6c 6(4 16 3c 56 — 61 6c 75 65 3e 6b 5f 5f Fie id.<Ualue>k__ Console.WriteLineO.
H0b0: 42 61 63 6b 6:9 бе 67 46 69 65 6c 64 04 04 If &3 BackingField...S Но мы восполь
И0С0: 65 72 69 61 6c 69 7a 65 — ' Sf 61 Sf 64 65 63 6 b e y ia 1ia'e_a deck
ti!Jd0: 6f 66 Sf 63 61 72 64 73 2e 53 75 69 74 73 02 00 of __cai'ds .Suits .. зовались м ет о
ИиеВ: 00 0Й 20 S3 65 72 69 6,1 — 6c 69 7a 65 5f 61 5f 64 - - St^rialise_a_.d дом Console.Error.
it0f0: 65 63 6b Sf 6f 66 5f 6;-! 61 72 64 73 2e 56 .61 6c e c k„o f ,_cards .U a 1
Й10Й: ?5 6S 73 02 j00 00 00 00 00 00 05 fd ff ff ff wes ............. WriteLineQ для
И110: If 53 65 72 69 61 « 6jV 7a 6S 5f 61 Sf 64 65 63 вывода сообщений
(1120: 6b 5f 6f 66 Sf 63 i i .Sei*iali2 e_a dec
64 73 2e 53 7S 69 74 73 ^—0f_cai'ds .Suits
W130: 01 00 00 00 07 76 t 1 75 65 Sf Sf 00 08 02 m «... .value об ошибке.
HI 40: 00 00 00 00; 00 08 05 ff ff ff 20 53 65 72 69 Seri
i!lS0: 61 6c 69 7a ,65 Sf 61 64 65 63 6b Sf 6f 66 5f a 1;ijje ,_a„deck.__of„
«160: 63 61 72 64, 73 2e Su ,1 ■
--- 6c 75 65 73 01 00 00 00 carcts.Ualues«...
(S178: 07 76 61 .6cj 75 65 Sf k t ■
— 00 08 02 00 00 00 0d m -value_-..... .
И180; 00 08 0b “ .
1
;C:\tenp> I
f { ' F i l e . E x i s t s ( a r g s [0 ] ) ) .^ Е с л и п ер ед а н н о го ф а й л а
не с у щ е с т в у е т , в ы в о -
C o n s o ll ee ..EE rrrroorr . W r i t e L i m
ne (" F ile does not e x ist: {O }", a rg s[0 ]); д и т с я д р у г о е сообщ ение
S y s te m .E n v ir o n m e n t.E x it(2 ); и во зв р а щ а ет ся д р уго й
-у код ош ибки.
u s in g (S tream in p u t = F i l e . O p e n R e a d ( a r g s [0] ) )
т а к как байт ы ч и т а - М е т о д S tr e a m .
in t p o sitio n = 0 ; ю т ся непосредст вен но R -e a d () ч и т а е т
b y t e [ ] b u f f e r = new b y t e [ 1 6 ] ; из п от ока. — ^айт ы в буф ер.
w h ile (p o sitio n < i n p u t . L en gth ) i ^ данном случае
{ оиф ера и гр а ет
in t c h a r a c t e r s R e a d = i n p u t . R ead (b u f f e r , 0, b u f f e r . L ength) ; fe d b M b ^ ^ ^ a c M ^ ’
if (ch a ra ctersR ea d > 0) байт ы из т ек ст а -
{ во го ф ай ла.
C o n so le .W r ite (" {0 }: ", S t r i n g . F o r m a t ( " { о : х 4 } ", p o s itio n ));
p o sitio n += c h a r a c t e r s R e a d ;
и же м асси в a« “ з“ «
дальше * 445
часто задаваемы е вопросы
_ Чаацо
г
буются.
1гека!» записываются как единичные обратном порядке!
байты, в то время как для записи букв
Если в классе FileStream еврейского алфавита требуется по два
имеются методы чтения и записи, за байта? И что это за символы FF FE
чем нужны классы StreamReader
и StreamWriter?
в начале? Символы с неболь
! Вы увидели разницу между двумя шими значениями
^ ! Класс F i l e S t r e a m используется
для чтения и записи байтов в двоичный
файл. Его методы работают с байтами
и массивами байтов. Но есть и про
О 13K0 связанными кодировками Юникод.
Латинским буквам, цифрам, знакам пре
пинания, а также ряду стандартных симво
лов (фигурным скобкам, амперсандам и
Юникод записы
ваются но одному
граммы, работающие только с тексто другим элементам вашей клавиатуры)
выми данными, например, наша первая
версия программы Excuse Generator.
соответствуют небольшие значения Юни
код — от О до 127. (Аналогичная ситуация
в байт, в то время
Там мы использовали методы классов
S t r e a m R e a d e r ИS t r e a m W r i t e r ,
с символами кодировки ASCII.) Если файл
содержит только такие символы, они вы
как для записи сим
созданные специально для работы со
строками текста. Без них вам приходи
водятся в виде одиночных байтов.
волов Юникод вы
лось бы сначала читать массив байтов
и писать цикл, ищущий в этом массиве
Все усложняется, когда на сцену выходят
символы с большими значениями. Ведь
деляются два байта.
знаки переноса строки, так что иногда эти байт может иметь значение от О до 255,
классы сильно облегчают жизнь. и не больше. А вот в двух байтах уже
можно хранить числа от О до 65,536 — Это используем м в -NET по
Когда используется класс File, у м о л ч л н м ю кодировка называется
в шестнадцатеричной системе счисления
а когда класс Filelnfo? U T F -8 . Д л я смены кодировки
это FFFF. Чтобы сообщить программе,
нужно передать методу File.
WriteAllTextO другое значение.
446 глава 9
чтение и запись файлов
О тредактируйте срорму
М ы б о л ь ш е н е р а б о та е м с т е к с т о в ы м и ф а й л а м и , п о э т о м у р а с ш и р е н и е . t x t н е п о д
х о д и т . И з м е н и т е о к н а д и а л о га , за д а н н ы е п о у м о л ч а н и ю и м е н а ф а й л о в и к о д п о и с к а
п а п к и , п о д с т а в и в туда р а з р е ш е н и е * . e x c u s e .
D e s c r ip tio n = te m p E x c u se .D e s c r ip tio n ;
R e s u lt s = te m p E x c u s e .R e s u lts ;
L a stU sed = tem p E x cu se. L astU sed ;
}
p u b lic v o id S a v e ( s t r i n g file N a m e ) {
B in a r y F o r m a t t e r f o r m a t t e r = new B i n a r y F o r m a t t e r ( ) ;
u s i n g (S tr e a m o u t p u t = F ile.O p er^ J jsifee ( f i le N a m e ) ) {
fo r m a tte r .S e r i a li z e (ou tp u t, (th is)); у и a
} ^ ------ С -X Ключевое слово this т у т ф игури-
j рует , так как требуется сериалы -
зооать именно эт от класс.
448 глава 9
1 0 о б |> а б о 1 ][1 х а и с:1С Я 1«ч ;ен и й
Программу для
ге нерац и й оправданий
установил на
^ ґ ^ о у т б у к , и она
Г осегда под рукой.
Старит
оп я т ь и щ е т побод
сбежать с работы
Но программа не работает!
П о с л е щ е л ч ка н а к н о п к е R a n d o m E xcuse (С л у ч а й н о е о п р а в д а н и е ) п о
я в л я е тс я с о о б щ е н и е о б о ш и б к е . О п р а в д а н и я н е н а й д е н ы ? К а к ж е так?
ІЖШЄ Manager
Иеобработанное 0Ш
и с к л ю ч е н и е ... эту
проблему МЫ не
учли.
450 глава 10
обработка исключений
d o u b le to ta lH o n ey = 3 6 .5 + 1 2 .5 ;
s t r i n g beesW eC anFeed =
for (in t 1 = 1 ; i < (in t) to ta lH o n ey ; i+ + ) {
b e e s W e C a n F e e d += i . T o S t r i n g ( ) ;
; \ OvertlowException was unhandled (J
Vdue was ather too large or too sma# for a Single.
}
flo a t f =
f l o a t . P arse(b eesW eC an F eed );
i^ NullReferenceException was unhandled
in t drones = 4;
I Object refererrce not set to m hstance of an object
in t queens = 0;
in t dronesP erQ ueen = d ro n es / queens;
н у ж н о передать
H oneyB ee a n o th e r B e e = new H o n e y B e e ( 1 2 . 5 , "Buzzy") 6 определенном ф орм а-
'лле. При эт ом мет од не зна-
d o u b le beeNam e = d o u b l e . P a r s e ( a n o th e r B e e .M y N a m e ); , ет , как преобразовать строки
Виггу в число. Это и становится
о С Г л сообщ ениГ
}
flo a t f = f l o a t . P arse(b eesW eC an F eed );
Разумеется, все эти исключения
появляются по очереди, программа
выводит первое из них и останавливается.
OverflowException was uniiandled Второе исключение вы можете увидеть,
Vakje was either too large or too smd for a Single. только после того как исправите ошибку,
приведшую к первому исключению.
452 глава 10
обработка исключений
in t drones = 4;
Увидеть такое сообщение
in t queens = 0; об ошибке очень легко.
in t dronesP erQ ueen = d ro n es / queens;
Достаточно поделить
любую переменную на ноль.
дальше ► 453
арахисовая карамель
Объект Exception
И т а к , в ы у зн а л и , к а к и м о б р а зо м .N E T с о о б щ а е т вам,
ч т о с п р о гр а м м о й ч т о -т о н е та к , — э т о и с к л ю ч е
н и я ( e x c e p tio n ). П р и и х п о я в л е н и и создается о б ъ
ект, к о т о р ы й н а зы в а е тся , к а к н е с л о ж н о до га д а ть ся. ис-клю-че-ние, сущ.
E x c e p tio n . человек или вещь, выда
П р е д с та в ь те м а сси в и з ч е т ы р е х эл е м е н то в . А т е п е р ь ющиеся из общего ряда,
п о п ы т а й т е с ь п о л у ч и т ь д о с т у п к э л е м е н ту н о м е р ш е с т
не подчиняющиеся пра
н а д ц а ть (с и н д е к с о м 15, т а к к а к о т с ч е т ве де тся с н у л я ):
Очевидно,
вилам. Джим ненавидит
i n t [ ] апАггау = {З, 4 , 1 , что это арахисовое масло, но дела
11}; ошибочный
код. ет исключение для конфет
i n t aV alue = апА ггау[ 1 5 ] ; от Кена.
Д л^ просмотра подроб
ной информации щелкните
При возникновении на строчке View Detail 6 окне
1ясключения создается ^ с сообщением о необработан-
объект, содержаш,ий ■КТ Ехс«'^ н о м исключении. ф
сведения об ошибке.
Detail
Exceptionsnapshot
е строке Message л System.Ind€3cOutDffengeException {"Ind©( wasoutsidethe bounds of the array."} |
обьясняетсй смысл [System,b\dexOtJtOfRengeException] {"Indexwasoutsidethe boundsof thearray,”} 1
^ Data {System.Col!ectior\s.ListDictiDnaf>^nternal}
ошибки, а также HefpLink
с о Э е р ж и т с я список веек nutt
Inr>erExceptior> nuff
обращений к памяти, ытзт
к событию, ■ H Indexwas outsidethe bounds of thearray.
Source BrokenCodeExceptions
ставшему причинои StackTrace at Broken_Code_Excepttons.Program.BeeProcessor1
ошибки. _____ i> TargetSite {Void BeeProcessorO}
...J
454 глава 10
обработка исключений
Чаапо
за д а в а ем ы е
B o r ij= » o c:b i
Б• Так что же такое исключение? Правда ли, что появление исключения вовсе не
означает, что я сделал что-то не так?
Q ; Это объект, который .NET создает в случае возникно
вения проблем. Впрочем, вы и сами можете создавать такие ^ ; Правда. Иногда данные ведут себя не так, как вы
объекты (об этом мы поговорим чуть позже). ожидаете, например, можно написать метод, который будет
работать с массивом иной длины, чем изначально пред
Что? Это объекты? полагалось. Следует помнить, что пользователи действу
ют непредсказуемым образом. Благодаря исключениям
программы не останавливаются в нетипичных ситуациях,
^ ! Да, исключение — это объект. Свойства объекта сооб а продолжают работу.
щают вам информацию об исключении. Например, свойство
M e s s a g e — ЭТО строка вида «Указанное присвоение неосу
ществимо» или «Слишком большое или слишком маленькое После сообщений об ошибках стало ясно, что код
значение для переменных данного типа», которая появляет на предыдущей странице работать не будет. Всегда ли
ся во всплывающем окне. Вам дается максимум возможной исключения позволяют понять, что происходит?
информации о том, что именно происходит при выполнении
оператора, который стал причиной исключения. ^ ! к сожалению, иногда локализовать проблему, просто
посмотрев на код, невозможно. Поэтому в ИСР существует
К сожалению я все равно не понял, зачем нужно отладчик. Он выполняет программу строчка за строкой,
такое количество исключений? оператор за оператором, показывает мгновенное значение
каждой переменной и каждого поля. Это позволяет обнару
жить, где именно код работает не так, как вы предполагали.
Q ; Потому что способов некорректной работы кода вели
кое множество. Существуют ситуации, в которых код просто
перестает работать. Не зная, что стало причиной, устранить
проблему крайне сложно. Создавая разные исключения,
.NET дает вам информацию, позволяющую отследить ошибку
и исправить ее.
Исключения помогают
обнаружить и исправить
код, который работает не
так, как вы думали.
О П о с л е п е р е н е с е н и я п р о гр а м м ы E xcuse M a n a g e r н а н о у тб у к , о н а стала
с сы л а ть с я н а п у с ту ю папку. В и т о г е щ е л ч о к н а к н о п к е R a n d o m п р и в е л
к по я в л е н и ю о кн а с сообщ ением об необ работанном и скл ю ч е н и и :
Excuse M anager
M ato
□ Ccrtmje ал
О О к н о со о б щ а е т, ч т о и н д е к с в ы ш е л за г р а н и ц ы м ассива. П о с м о т р и м н а
ко д для о б р а б о т ч и к а с о б ы т и й к н о п к и R a n d o m Excuse:
М а с с и в о в т у т нет. З а то п р и п о м о щ и о д н о г о и з п е р е гр у ж е н н ы х к о н
с т р у к т о р о в создается о б ъ е к т E x c u s e . М о ж е т б ы т ь , м а сси в с о д е р ж и т с я
в ко де э т о г о к о н с т р у к т о р а ?
456 глава 10
обработка исключений
О ка зы в а е тс я , м е то д D i r e c t o r y . G e t F i l e s { ) , п о с л е т о г о к а к в ы
указали н а п у с т у ю папку, ста л в о з в р а щ а ть п у с т о й м ассив. И с п р а в и т ь
ситуац ию м о ж н о , добавив п р о в е р к у с о д е р ж и м о го п а п к и перед о т
к р ы т и е м ф айла. И в м е с то о к н а с и н ф о р м а ц и е й о н е о б р а б о т а н н о м и с
к л ю ч е н и и будет п о я в л я т ь с я о к н о с с о о б щ е н и е м .
Г
if (C h eck C h an ged 0 == t r u e ) {
C u rren tE xcu se = new E x c u s e ( r a n d o m , F o ld er )
папке ^ создания
о6ъект 1^ Е хси 5е, м ы
U p d a te F o r m (fa lse );
предотвращаем
появление сообщения
об исключении —
и вызываем окно
с вспомогательной
информацией.
дальше ► 457
генеалогия исключений
С в о й с т в о M e s s a g e э т о г о класса с о д е р ж и т с о о б щ е н и е об о ш и б к е . А с в о й
с т в о S t a c k T r a c e указы вает, к а к о й к о д в ы п о л н я л с я в м о м е н т п о я в л е н и я
и с к л ю ч е н и я и ч т о и м е н н о п р и в е л о к е го п о я в л е н и ю .
Exception
Метод ToStringO Message
сум м ирует всю StackTrace
информацию GetBaseExceptionO
ToStringO
из полей объекта
и возвращает ее
в виде строки.
458 глава 10
контрольное значение
460 глава 10
обработка исключений
Работа с отладчиком
П е р е д те м к а к д о б а в л я ть о б р а б о т к у и с к л ю ч е н и я , н у ж н о п о н я т ь , к а к и е
и м е н н о о п е р а т о р ы с та л и п р и ч и н о й е го п о я в л е н и я . В э т о м вам п о м о ж е т
в с т р о е н н ы й в И С Р о т л а д ч и к . Вам уж е п р и х о д и л о с ь и м п о л ь з о в а ть с я , н о Панель инст румент ов
д ава й те т е п е р ь р а с с м о т р и м е го п о д р о б н о . П о с л е е го за пуска п о я в л я е тс я Debug появляется только
п а н е л ь и н с т р у м е н т о в с к н о п к а м и . П о о ч е р е д и н а в е д и те к у р с о р н а ка ж д ую в режиме отладки п р о
граммы.
и з н и х и п о с м о т р и т е н а результат:
Перезапустить: останавливает
Прервать все: прекращает работу программы и начинает ее
работу программы, как при сначала
достижении точки останова
Шестнадцатеричный Вывод
Н а ж м и т е H e x для в к л ю ч е н и я р е ж и м а ш е с т н а д ц а т е р и ч н о го в ы в о д а и н а в е д и те к у р с о р н а л ю б о е п о л е
и л и п е р е м е н н у ю . С н о в а щ е л к н и т е н а к н о п к е для в о з в р а щ е н и я в о б ы ч н ы й р е ж и м . П р е о б р а з о в а н и е зна
ч е н и я в ш е с т н а д ц а т е р и ч н у ю с и с те м у с ч и с л е н и я будет о с у щ е с тв л е н о а в т о м а т и ч е с к и . В п р о ш л о й главе
в ы у зн а л и , зачем э т о н у ж н о .
Это одно число, слева
¥ va(uel 0 k 3 sfb 8 3 d9
в шестнадцат еричной, ------ ^
дальше * 459
обработка исключений
О Работа с окном W a tc h
Щ елкните правой кнопкой м ы ш и на перем енной file N a m e s и вы берите ко м а н д у
E xpression: ‘fileN am es’ » Add Watch. З атем щ елкните на пустой строчке под перем ен
ной fileN am es и введите r a n d o m . N e x t ( f i l e N a m e s . L e n g t h ) , ч т о б ы о т л а д ч и к д о б а в и л
к о н т р о л ь н о е з н а ч е н и е . В о т к а к будет в ы гл я д е ть о к н о W a tc h для п а п к и с т р е м я ф а йл а м и
о п р а в д а н и й (к о гд а п е р е м е н н а я f i l e N a m e s и м е е т д л и н у 3).
Д важ д ы щ е л к н и те н а с т р о ч к е f i l e N a m e s , ч т о б ы в ы д е л и ть т е к с т { s t r i n g [3 ] } . З а м ените
е го н а n e w s t r i n g [ 0 ] . Т ут ж е и сч е з н е т к в а д р а ти к со зн а ко м «плю с» ря д о м с п е р е м е н н о й
f i l e N a m e s , т а к ка к т е п е р ь э т о т м ассив пуст. А ря д о м со с т р о ч к о й r a n d o m . N e x t {) п о я в и тс я
значок {Щ. Щ е л к н и т е н а нем , ч т о б ы ещ е раз в ы п о л н и т ь м етод. О н д о л ж е н в е р н у ть 0.
’Г □ X
W atch
Э т о т значок Value Type
Name
в окне Watch
сообш,ает об *: fiieName$ {sfringfOl} stringD
обнаружении
"Л
# random .N extPeN am es.Lengti) 0 Ф tnt
исключения. ^"^lieNames[randQm.Next{f»eNames.Lengtti)] Out ofbounds array index sfring
В ы в с егд а м о ж е те в о с п р о и зв е с ти с п о м о щ ь ю о тл а д ч и ка с и ту а ц и ю , с т а в ш у ю п р и ч и н о й
появления исклю чения.
дальше > 461
сделаем перерыв
Часщо
задаваем ы е
B o IlJ > o C b i
Почему сообщение о необрабо Как выбрать место для точки у» То есть я могу запустить в окне
танном исключении, которое получил останова? I /а!сЬ что-то, меняющее способ рабо
Брайан, отличается от моего? ты программы?
Q ; Это хороший вопрос, который, к со
Q ; Запуская программу в ИСР, вы жалению, не имеет однозначного ответа. 0 ; Да! Пусть не всегда, но здесь вы
работаете из отладчика, который при При появлении исключений, имеет смысл можете влиять на конечный результат
обнаружении исключения прерывает начинать с операторов, которые стали их работы программы. Более того, даже на
выполнение (как при нажатии кнопки причиной. Но обычно проблема гнездится ведение указателя мыши на поля вну
Break АН или при вставке точки останова). в предьщущих строках кода, а исключение три отладчика может поменять поведение
При этом появляется окно с сообще является не более чем последствием, программы, так как при этом выполня
нием. Это позволяет исследовать объект к примеру, оператор, ставший причиной ется метод чтения свойства. И если
E x c e p t i o n , а также поля и переменные появления исключения «Деление на этот метод задает какое-то значение, оно
вашей программы, и понять, в чем именно ноль», может использовать значения, вы перейдет в программу. Такое поведение
состоит проблема. численные десятком строк ранее. Поэтому делает результаты отладки непредсказу
ответ на вопрос о месте точки останова емыми. Программисты в шутку называют
Программа, с которой работал Брайан, в каждом конкретном случае будет такие результаты гейзенбергскими (эта
была установлена на его компьютер. отличаться. Но если вы представляете шутка понятна только физикам и котам
Помните, вы делали это в главе 1 для принцип работы своего кода, проблемы с в ящике).
приложения со списком контактов? Для выбором места не будет.
запуска программы вне ИСР достаточно
построить решение, то есть получить
Любой метод можно запускать
исполняемый файл, который находится в
в окне Watch?
папке b i n / , вложенной в папку проекта.
После этого вы будете видеть такие же
окна с сообщениями об исключениях, как И ; Да, любые корректные операторы
Зацущенная в ИСР
и Брайан. с д у т работать в окне Watch. Даже те,
которые не имеет смысла запускать. Запу профамма при
стите программу, прервите ее выполнение
То есть если программа работает
вне ИСР, с появлением сообщения об
и добавьте в окно Watch строку: S y s t e m . появлении необ
T h r e a d i n g . T h r e a d . S l e e p ( 2 0 0 0 ).
исключении ее выполнение просто
останавливается, и пользователь ниче
(Как вы помните, этот метод вызывает
двухсекундную задержку работы про
работанных исклю
го не может сделать?
граммы.) Использовать его нет смысла, но
чений прерывается,
Ql Да, программа останавливается,
столкнувшись с необработанным исклю
интересно посмотреть, что произойдет: на
две секунды, которые занимает выполне
как при достиже
ние этого метода, появится изображение
чением. Но ведь нигде не сказано, что все
исключения относятся к необработанным!
песочных часов. Так как метод s l e e p ()
не возвращает значения, в окне Watch нии точки останова.
О способах обработки исключений и о том, появится сообщение. E x p r e s s i o n h a s
каким образом создать программу без со b een e v a lu a te d and h as no v a lu e,
общений о необработанных исключениях, информирующее об отсутствии возвраща
мы поговорим чуть позднее. емых значений.
462 глава 10
обработка исключений
П р и л о ж е н и е Б л о к н о т п о з в о л я е т в о ссо зд а ть ф а й л ы с о п р а в д а н и я м и . П е р
вая с т р о к а — о п р а в д а н и е , в т о р а я — р е зульта т е го п р и м е н е н и я , т р е т ь я —
дата е го п о с л е д н е го и с п о л ь з о в а н и я ( « 1 0 / 4 / 2 0 0 7 12:08:13 Р М » ).
© В ы з о в и т е E xcuse M a n a g e r и о т к р о й т е о п р а в д а н и е . П р и п о я в л е н и и и с к л ю ч е н и я ш ;елкните
на к н о п к е D e ta ils . О б р а т и т е в н и м а н и е н а стек вы зова - и м е н н о т а к н а з ы в а е тс я м е то д , в ы
з ы в а ю щ и й д р у г о й м е то д , к о т о р ы й , в с в о ю о ч е р е д ь , т о ж е в ы з ы в а е т м е то д и т. д.
atSystem.Runtime.Serialization.Formatters.Binary._BinaryParser.Run()
atSystem.Runtime.Serialization.Fon7iatters.Binary.ObjectReader.Deserialize(HeaderHandler
проблема с классом handler, _BinaryParserserParser, Boolean fCheck, Boolean isCrossAppDomain,
BinaryFormatter. IMethodCallMessage methodCallMessage)
Это предположе
ние имеет смысл, at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream
так как приложение serializationStream, HeaderHandler handler, Boolean fCheck, Boolean IsCrossAppDomain,
пыталось десериа IMethodCallMessage methodCallMessage)
лизовать текстовый
файл. atSystem.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream
serializationStream)
Многое можно узнат ь из
г.тека вызова, ведь т ам 'a t Chapter10.Excuse.OpenFile(String ExcusePath) in C:\Documents and Settings\Administrator\
показывается список ^My DocumentsWisua! Studio 2005\Projects\Chapter10\Chapter10\Excuse.cs:line 40
запущенных А/івтодов.
Вы видите, что м е at Chapter10.Excuse..ctor(Random random , String Folder) in C:\Documents and Settings\
тод OpenFileQ класса ^dm inistrator\My DocumentsWisual Studio 2005\Projects\Chapter10\Chapter10\Excuse.cs:line 30
Excuse вызывается его
конструктором (.ctor), ^C hapter10.Form 1.ran dom E xcuse_C lick(O bject sender, EventArgs e) in C:\Documents and
который, в свою очередь, Settings\AdministratortMy DocumentsWisual Studio 2005\Projects\Chapter10\Chapter10\Form1.
вызывается обработ
csiline 146
чиком событии кнопки
Random Excuse.
© Щ е л ч о к н а к н о п к е D e ta ils п о з в о л я е т м н о го е у з н а ть о п р и ч и н а х в о з н и к ш е й п р о б л е м ы . Есть
идеи, что со всем этим делать?
дальше ► 463
непредсказуемость пользователей
464 глава 10
обработка исключений
s t r y {
if (CheckChangedО == true) {
currentExcuse = new Excuse(random, selectedFolder);
UpdateForm (false) ;
Ключевое слово catch означаетj что еле-
дующий за ним блок операторов содержит
оораоотчик исключения.
<gjtch ^SerializTtl^nExce'gagig, {
Исключе 4essageBox.Show(
ние вызы
вает не "Your excuse file was invalid.",
медленный
переход к
операт о "Unable to open a random excuse")
рам блока
catch. J It
"I Это прост ейилий способ
обработки исключения,
остановить программу, ШТУРМ
написать сообщение
и зат ем продолжить Если исключение немедленно передает управление
выполнение программы. операторам блока c a t c h , что происходит с объектами
и данными, с которыми вы работали до этого?
( D П редполож им , п о л ь
зо в ател ь в в одит не те
данны е.
Вводимые Написанный
Пользователь данные вами класс
Часто
за д а в а ем ы е
с каждым следующим шагом. Это по
Б оЦ Р о СЬ!
^ J Так когда используются ключевые зволяет отслеживать, что именно с ними
слова t r y и c a t c h ? происходит после выполнения каждого
Аварийное завершение работы про оператора.
Q ; в случаях, когда вы пишете сомни граммы при вводе некорректных данных
бесполезно. Нет таюке никакой пользы В окно Watch можно ввести любой
тельный код, который может привести
от попыток читать все вводимые данные. оператор и увидеть его значение. Если
к исключениям. Сложнее всего понять,
А вот появление окна с сообщением о не оператор влияет на значения полей
что код является сомнительным.
возможности прочитать файл позволяет и переменных, значит, он будет выпол
принять верное решение. нять еще и эту функцию. Это позволяет
Вы уже видели, что сомнительную
менять параметры, не прерывая работу
работу кода может инициировать ввод
То есть отладчик нужен для поиска программы, что дает вам еще один
неверных данных. Пользователи могут
инструмент для отслеживания дефектов
выбирать не те файлы, вводить буквы причины исключений?
и исключений.
вместо цифр и имена вместо дат, а еще
они нажимают все кнопки, до которых г ; Нет. Вы уже не раз видели, что от
могут дотянуться. Хорошая программа ладчик работает с любым кодом. Иногда
должна работать предсказуемо вне
Редактирование данных в окне
имеет смысл пошагово просмотреть VJatck влияет на их состоя
зависимости от вводимых данных. Поль значения определенных полей и перемен ние в памяти на время рабо
зователь может не получить нужного ных — именно так можно гарантировать ты программы. Д л я возвра
результата, но по крайней мере он узна корректную работу сложных методов. щения переменным исходных
ет о наличии проблемы и ознакомится значений достаточно переза
с предложенным решением. Но основным назначением отладчика грузит ь программу.
является поиск и устранение дефектов
А как можно предложить решение программы. Исключения также относятся
проблемы, о существовании которой к дефектам. Но отладчик решает пробле
заранее неизвестно? мы и другого рода, например, поиск кода,
В е сь п р е д о с т а в л е н н ы й в э т о й главе к о д н у ж н о в с т р о и т ь в о б р а б о т ч и к с о б ы т и й к н о п к и
R a n d o m E xcuse. Т о ч к у о с т а н о в а следует п о м е с т и т ь в п е р в у ю с тр о ч к у . И з а п у с ти ть п р о гр а м
му. Щ е л к н и т е н а к н о п к е F o ld e r и в ы б е р и т е п а п к у с е д и н с т в е н н ы м ф а й л о м в н е й . У б е д и те сь ,
ч т о э т о некорректны й файл с оправданием (н е с м о т р я н а р а с ш и р е н и е .e xcuse). Щ е л к
н и т е н а к н о п к е R a n d o m E xcuse, а затем ш е с т ь раз щ е л к н и т е н а к н о п к е Step O v e r (и л и н а
ж м и т е F 10) для п е р е х о д а к о п е р а то р у , в ы з ы в а ю щ е м у к о н с т р у к т о р E x c u s e . В о т к а к д о л ж е н
в ы гл я д е ть ва ш э к р а н н а э т о м этапе:
p riv a te v o id ra n d c n B E x c u s e _ C lic k (o b je c t s e n d e r^ E v e n tA rg s e )
точка
Э т о {
останова, кот о s trin g [j file n a m e s = P ir e c to r y .6 e tF ile 5 (s e le c te d F o Irfe r. " * .e x c o s e " ) j
рую МЫ поме - if (file N a is e s .L e n g th == 0 )
стили в первую
строчку одра - ~~ M e s s a g e B o x .S h o w C ’P l e a s e s p e c ify a f o l d e r w ith ex cu se f i l e s in i t ”,
ботчика событий. "H o e x c u s e file s fo u n d ");
}
try
{
[if (CheckChanged{) =« tr u e )
{
c u rre n tE x c u s e = new E x c u s e (r a n d a ® , s e le c te d F o ld e r)j
Нажимайте Рй-О, U p d a te F o rre (fa ls e );
пока не дойдете } Используйте команду Step
до строки, пред } Over (F io) для обхода м е -
шествующей ^ c a tc h (s e ria iiz a tio n e x c e p tio n ) moda Ch.eckCkanged().
созданию объек {
та Excuse. M e s s a g e B o x . Show(
"Y our e x c u se f i l e w as i n v a l i d . ” ,
" U n a b le t o open a ran d cM e x c u s e " ) ;
}
468 глава 10
обработка исключений
О П о с л е в ы п о л н е н и я о п е р а т о р а D e s e r i a l i z e () п о я в и т с я и с к л ю ч е н и е , и п р о г р а м м а , п р о
и гн ори р ов ав вы зов м етод а U p d a te F o r m ( ) , перей дет непосредствен но к блоку c a tc h .
Н а ж м и т е клавиигу F 5 д л я за п у с к а п р о г р а м м ы . О н а н а ч н е т в ы п о л н я т ь с я с о с т р о ч к и , п о м е
ч ен н о й ж елты м , в наш ем случае с блока c a t c h .
Совет на буду
щее: Интервью
с соискателями
на должность
программиста А кку о атн е е с и скл ю чени я м и в конструкторе!
часто включа Рр у |Дыпе
ды пе
ю т в себя вопрос
о т ом , как сле
осш^роЖНьїІ Думаю, выуже заметили, чтоу конструктора от
дует пост упат ь сутствуетвозвращаемое значение. Дело втом, что его назначением
с исключениями является инициализация объекта, и именно поэтомутак сложно об
в конструкторе. рабатывать исключения, возникшие внутриконструктора. Наличие
исключения означает, что оператор, создающийэкземпляр класса, не
смог его получить. Блок t r y / c a t c h вэтомслучае имеетсмысл по
местить в обработчик событий кнопки, чтобыкод не ожидал найти
вклассе C u r r e n tE x c u s e корректный объектExcuse.
p r i v a t e v o id ra n d o m E x c u s e _ C lic k (o b je c t s e n d e r , E v e n tA rg s e) {
s t r i n g [ ] file N a m e s = D i r e c t o r y . G e t F i l e s ( s e l e c t e d F o l d e r , " * .e x c u se ");
i f ( file N a m e s .L e n g th = = 0 ) {
M e ssa g e B o x .S h o w ("P le ase s p e c i f y a f o l d e r w ith e x c u s e f i l e s i n i t " ,
"No e x c u s e f i l e s f o u n d " ) ;
} e ls e {
tr y {
if (C heckC hanged0 == t r u e ) {
c u r r e n tE x c u s e = new E x c u se (ra n d o m , s e l e c t e d F o l d e r ) ;
Р я д о м с к л ю ч ев ы м с л о в о м c a tc h у к а за н о , ч т о о т с л е ж и в а е т с я и с к л ю ч е н и е S e r i a l i z a t i o n
E x c e p t i o n . Это принятый в C# код c a t c h ( E x c e p t i o n ) , хотя тип исключения можно и опустить, оста
вив только слово c a t c h . В этом случае б у д у т о т с л е ж и в а т ь с я в с е в о з м о ж н ы е и с к л ю ч е н и я . Хотя это
«е очень хорошо. Лучше указывать, какое исключение вы хотите отследить и указывать, как можно более
подробно.
470 глава 10
обработка исключений
ДА т е ^п е р
отладим !
. %
Обновите обработчик событий кнопки Random Excuse, вставив туда код с предыдущей
страницы. Поместите точку останова на первую строку метода и отладьте программу.
О Теперь укажите папку, содержащую всего один дефектный файл, и снова щелкните на
кнопке Random Excuse. Из блока t r y при обнаружении исключения управление перей
дет к блоку c a tc h . После того как будут просмотрены все его операторы, начнет выпол
няться блок f i n a l l y .
дальше ► 471
исключения как причина нестабильности
Часвдо
Задаваем ы е
При отсутствии блока catch моя Б оцрось! Но если блок c a t c h может об
программа, столкнувшись с исключе рабатывать исключения любых типов,
нием, будет просто остановлена. Что Зачем указывать в блоке catch тип зачем мне указывать конкретный тип?
же в этом хорошего? обрабатываемого исключения? Разве
не безопаснее использовать универ
^ S К сожалению, не существует
^ I Основным преимуществом исключе сальный код?
универсального способа обработки всех
ний явлется то, что они не дают пройти исключений. Для устранения проблемы
мимо проблемы, в больших приложениях ^ ! Безопасней всего вообще избегать с делением на ноль в блоке catch нужно
сложно следить за всеми объектами, ситуаций, в которых возникают объ изменить значения некоторых пере
с которыми ведется работа. Исключения екты E x c e p tio n . Лучше провести про менных и сохранить некоторые данные.
привлекают внимание к проблемам и по филактику, чем употреблять лекарство. А чтобы убрать ссылку на значение null
могают понять причины их возникновения. Это правило работает и с исключениями. может потребоваться создание нового
Попытка обработать все исключения экземпляра.
Появление исключения в вашей про
сразу указывает на плохое программиро
грамме предупреждает — что что-то идет
вание. Например, лучше воспользоваться
не так, как было запланировано. Может Обработка ошибок происходит
методом File .Exists () для проверки
быть, ссылка указывает не на тот объект, только в последовательности t r y /
наличия файла, а не обрабатывать ис
на который нужно, или пользователь ввел c a tc h /fin a lly ?
ключение FileNotFoundException.
совсем не то значение, которое требова
Хотя бывают исключения, которых просто
лось, или даже файл, с которым ведется ! Нет. Можно воспользоваться набором
й
не избежать, но большинство из них не
работа, вдруг стал недоступен. Подобные жов c a t c h , если вы хотите учесть
имеет приоритета.
вещи меняют результат работы вашей различные виды ошибок. Этого блока может
программы. и не быть вовсе. В этом случае исключения
Иногда имеет смысл оставлять исключе
А теперь представьте, что вы даже не ния необработанными. Логика реальных обрабатывать не будут, просто код блока
знаете обо всех этих ошибках. Пользо программ зачастую крайне сложна и кор fina 11у начнет выполняться даже при
ватель же вводит некорректные данные ректно обойти ошибку не всегда удается, остановке из-за появления исключения
и начинает жаловаться, что приложение особенно если проблема возникает где-то в блоке try.
нестабильно. Нарушающие работу вашей там в нижней части кода. Обрабатывая
программы исключения позволяют узнать конкретные исключения, избегая слишком
о проблеме на том этапе, когда ее реше общих подходов и попыток решить все
ние еще можно провести относительно проблемы сразу, позволяя исключени
легко и безболезненно. ям всплывать, чтобы обработать их на
croc = з;
} else {
using System.lO;
public static void M a i n O { .O p e n R e a d (" w o b b i e g o n g " );
in t k o a l a = joey.Wombat( }
j o e y .W o m b a t {j o e y .W o m b a t (1))), catch (lOException) {
try { croc = -3 ;
C o n s o l e . W r i t e L i n e ((15 / koala)
}
+ " e g g s p e r p o u n d " ); catch {
} croc = 4 ;
catch (________________________) {
}
C o n s o l e .W r i t e L i n e (" G ' D a y M a t e !" ! finally {
. } if (_______ > 2) {
c r o c ___ dingo;
} J
К аж д ы й ф р а гм е н т
м о ж е т б ы ть и с п о л ь
зо в ан н е ско л ь ко
раз!
Ребусы становятся все более слож ны м и, а им ена перем енны х все менее очевидны м и. Н ад реш ением при
ходится много д ум ать! Но ребусы реш ать не обязател ьно, вы м ож ете просто перевернуть страницу и про
д ол ж и ть ч тен и е... это д л я л ю бител ей головоломок!
Объект FiieStiream } }
имеет метод
OpenReadQ и с т а - Этот catch, работает
новится причиной
class Kangaroo { только с исключениями,
появления исключе FileStream fs; возникшими в результ ат е
ния lOException. int croc;
деления на ноль.
int dingo = 0 ;
} }
return CrOC;
474 глава 10
обработка исключений
Что за ерунда
происходит?
message = ex.Message;
476 глава 10
обработка исключений
- Разумеется, дывает и
такое, что один метод
Один класс создает исключение, другой его обрабатывает может формировать ис
ключение, которое ликви
в момент создания класса неизвестно, как с ним будут работать. Иногда дируется другим методом
действия пользователей становятся источником проблемы. Именно в этих этого же класса.
сиутациях возникают исключения.
Вам нужно заранее понять, что может пойти не так, и предусмотреть план
перехвата. Вы обычно не видите, какой метод создает исключение, а какой
устраняет его. Это, как правило, различные методы, принадлежащие раз
личным объектам.
К онст рукт ор обьекта BeeProfile
ожидает имя содержащего
профиль пчелы, чтобы открыть его
Вместо того, чтобы... методом File.OpenQ. Если файл не
Без обработки исключений программа пере открывается, программа перест ает
стает работать. Вот что происходит в програм работать.
ме управления профилями пчел. stream = File.Open(profile);
О
создать обьект 8ееРгоР((е,
передав неверное имя ф ай
ла, бцдет сделана запись об
ошибке и появится моби^ение
об исключении. Улей сможет
try { обработать исключение, на
prof = new BeeProfile("prof.dat") пример, пут ем
. создания файла с профилем
} catch (FileNotFoundException) {
пчелы.
Hive.RecreateBeeProfile("prof.dat
}
дальше > 477
ваше собственное исключение
Методы показывают таков
исключение при некорректных
Exception
исключение OutOfHoney для пчел значениях параметров.
Message
Ваши классы могут формировать собственные исключения. Например, при получе StackTrace
GetBaseExceptionO
нии внутри метода параметра null вместо ожидаемого значения имеет смысл исполь
ToStringO
зовать исключение, которое вызывает в такой ситуации .NET:
throw new ArgumentException(); ^
p i i b l i c O u t O f H o n e y E x c e p t io n ( s t r i n g m e s s a g e ) : b a se(m essa g e) { }
p r i v a t e v o i d c o n s u m e H o n e y _ C lic k ( o b j e c t s e n d e r , E v e n tA r g s e ) {
H o n e y D e liv e r y S y s t e m d e l i v e r y = n ew H o n e y D e li v e r y S y s t e m ( ) ;
tr y {
d e l i v e r y . F e ed H o n e y T o E g g s () илл я пользоват & льского <^сключетя ука зы ^^ ^
У к» «»полня-
} iC - ются все операции для его обработки.
c a tc h (O u tO fH o n e y E x c e p tio n e x ) {
M e s s a g e B o x .S h o w ( e x .M e s s a g e , " W a rn in g : R e s e t t i n g H i v e " ) ;
Wamir^j Resetting Hive
H iv e .R e s e t 0 ;
Если в улье от сут ст вует мед, деят ель-
} ность пчел останавлиоается, и симулят ор T he h'fve is o u t of h o n ey
I Ц сЮ П оЧ иш еЛ ьН ы е М а Г н и т ь !
public static void MainO { Расположите магниты с кодом таким об
Console.Write("when it разом, чтобы на консоль был выведен
ExTestDrive.Zero{"yes") следующий результат.
Console.Write(" it ");
ExTestDrive.Zero("no"); Результат:
Console.WriteLine("."); ► when it thaws it throws.
P u b lj ^ ^ ^ a t ic v o id Z ero ( s t r i n g t est) {
s t a t i c v o id D o R isk y (S tr in g t) {
C o n s o le .W r it e ( " h " ) ;
дальше ► 479
небольшой обзор
Задачи с МаГнищаМи
public static void MainO { Расположите магниты с кодом таким образом,
Console.Write{"when it "); чтобы на консоль был выведен следующий ре
ExTestDrive.Zero("yes"); зультат.
Console.Write(" it ");
ExTestDrive.Zero("no"); Результат:
Console.WriteLine("."); > — ► when it thaws it throws.
}
class MyException : Exception { }
c l a s s E x T e s t D r iv e {
p u b lic s t a t i c v o id Z e r o (s t r in g t e s t ) {
Эта строчка опреде
ляет пользовательское
исключение МцЕнсерНоп,
которое обрабатывается 8 зависимости от. mozoj
C o n s o le .W r it e ("t ");
в блоке catch. какой парам ет р test
D o R is k y (te s t);
был передан методу
ZeroQ (строка yes или
чт о-т о другое), выво
C o n so le .W rite ( " о " ) ; дится строка thaws или
throws.
} c a tc h (M y E x c e p tio n ) {
} fin a lly {
1
C o n s o le .W r it e (" W " ) ;
[
1 I
/ 5лок finally гарант ирцет ,
что мет од всегда выоо-
дит символ W . А так как
ф f символ S выводится вне
1 обработчика исключений,
\ он так же всегда попадает
C o n s o le .W r it e ("s "); в список вывода.
□
G J
s t a t i c v o id D o R is k y (S tr in g t ) {
Эта строчка выполня C o n s o le .W r it e ( "h") ;
ется только в случае,
]
когда метод doRiskyQ
не становится причи
ной появления исклю
if (t = " y es" ) {
th r o w n ew M y E x c e p t io n ( ) ;
I
чения. I
} Метод doRiskyO вы-
to n s o le .W r it e ( " r " ) ;
передаче ему строки'^
480 глава 10
обработка исключений
КЛЮЧЕВЫЕ
МОМЕНТЫ
Причиной исключения может стать любой оператор. Каждому оператору try может соответствовать
несколько операторов catch:
Для обработки исключений пользуйтесь блоком
try/catch. Необработанные исключения приво try { ... }
дят к прекращению работы программы. catch (NullReferenceException ex) {
// эти операторы срабатывают при
Обнаружение исключения в блоке try приводит // NullReferenceException
к немедленной передаче управления первому опе }
ратору блока catch. catch (OverflowException ex) { ... }
catch (FileNotFoundException) { ... }
Объект Exception содержит информацию об catch (ArgumentException) { ... }
исключении. Объявив переменную Exception
в операторе catch, вы получаете доступ к инфор Для сообщения об анамальных ситуациях использу
мации об исключении, появившемся в блоке try: ется оператор throw:
IDisposable
Потоки снабжены кодом для их закрытия после удаления объекта. Но что делать с пользовательским
объектом, после удаления которого требуется произвести некие действия? Имеет смысл написать свой
код для случая, когда объект используется внутри оператора u s in g .
В C# это можно сделать при помощи интерфей
са I D is p o s a b le . Реализуйте его и вставьте ос
вобождающий ресурсы код в метод D is p o s e (), ® операторе using
— можно только при условии реализации
как показано ниже: им интерфейса IDisposable^ Ъ противном
SyT eZ компилироваться не
c la s s N e c ta r : I D is p o s a b le { Объект, который предполагается использовать вместе
p r iv a te d o u b le a m o u n t;
^D isposablT^^ должен реализовывать интерфейс
p r iv a te B e e H iv e h iv e ;
p r iv a te S tr e a m h iv e L o g ;
p u b lic N e c ta r (d o u b le a m o u n t, B e e H iv e h iv e . S tr e a m h iv e L o g ) {
th is .a m o u n t = a m o u n t;
th is .h iv e = h iv e ;
th is .h iv e L o g = h iv e L o g ; Sudem выполнено в конце оператора using.
} sL- Метод
p iib lic v o id D is p o s e О { DisposeQ
if (a m o x m t > 0 ) {
уже написан,
т ак что он
h iv e .A d d ( a m o u n t ) ; может быть
h iv e .W r it e L o g ( h iv e L o g , am ount + " mg n e c t a r added to th e h iv e " ); вызван произ-
am ount = 0 ; / вольное коли-
^ чество раз.
}
У В руководстве по реализации и нтерф ейса
i D i s p o s e сказано, что метод D i s p o s e О
м ож но вы зы вать много раз. Вы поним аете
важность этого обстоятельства?
Теперь можно несколько раз воспользоваться оператором Вложенные операторы using исполь
u s in g . Возьмем встроенный объект S tream , реализующий зуют ся при необходимости объявить
дбе ссылки на интерфейс IDisposable
I D is p o s a b le , как и наш обновленный объект N e c ta r: о одном блоке кода.
u s in g (S tre a m lo g = new F i l e . W r i t e ( " l o g . t x t " ))
u s in g (N e c ta r n e c t a r = new N e c t a r ( 1 6 .3 , h i v e , lo g ) ) {
B e e .F ly T o ( f lo w e r ) ;
Объект Nectar использует поток
B e e . H a r v e s t ( n e c ta r ) ; j автоматически закрываюи^иися
B ee. FlyT o (h iv e ) ; у S внеилнего оператора using.
к о н ц е
482 глава 10
обработка исключений
часацо
^аД аБ аеМ ы е
Боцрось!
try {
я правильно понимаю, что Может ли метод быть вызван DoSomethingRiskyО ;
объекты, реализующие интерфейс D i s p o s e О вне оператора u s i n g ? SomethingElseRisky();
I D i s p o s a b l e , используются только }
внутри оператора u s i n g ? Q ; Конечно. На самом деле в этом finally {
случае оператор using вообще не нужен. AlwaysExecuteThis{);
^ ! Да. Интерфейс ID is p o s a b le Вызывайте метод Dispose (), завер }
предназначен для работы с оператором шив работу с объектом. Он высвободит
При обнаружении исключения в методе
u s in g , и добавление этого оператора указанные ресурсы, как и при вызове
DoSomethingRisky О немедленно
эквивалентно созданию экземпляра клас вручную метода Close {) для потока.
будет запущен блок final 1у.
са, только тут всегда вызывается метод Оператор using всего лишь облетает
D i s p o s e {). чтение и понимание кода и предотвращает
проблемы, которые могуг возникнуть, если Правда ли, что метод D i s p o s e О
Любой ли оператор может быть не удалить объект. работает только с файлами и потока
Iомещен в блок u s in g ? ми?
)• Вы упомянули блок t r y / f i n a l l y ,
^ ! Разумеется. Оператор u s in g всего
лишь гарантирует уничтожение любого
созданного вами объекга. Но использо
вать эти объекгы вы можете на свое усмо
!значает ли это, что операторы t r y
и f i n a l l y могут фигурировать без
оператора c a t c h ?
О ; Нет, многие классы реализуют
интерфейс IDisposable, и при
работе с ними всегда нужно использовать
оператор using. (С некоторыми из этих
трение. Можно даже создать объект при ! Да! Вы можете скомбинировать классов вы познакомитесь в следующей
помощи оператора u s in g и не упоминать
его внутри блока. Впрочем, это не имеет
практического смысла.
О ж try непосредственно с блоком
finally, как показано здесь:
главе.) Если вы пишете класс, который
нужно утилизировать определенным
способом, таюке реализуйте интерфейс
IDisposable.
дальше * 483
упущенные возможности
c l a s s C a lc u la to r {
t h i s .q u o t i e n t = d iv id e n d / d iv i s o r ;
Почему н есм от ря на наличие
} c a tc h { — "■ олока catch мы получаем с о -
оощение об ошибке?
I I П рим ечание Джима: нуж но п о н я т ь , как п р е д о т в р а т и т ь в в о д
// п о л ь зо в а т е л я о ш н у л е в о г о з н а ч е н и я в д е л и т е л ь .
484 глава 10
обработка исключений
c l a s s C a lc u la to r {
p x ib l lc v o i d D i v i d e ( i n t d i v i d e n d , in t d iv is o r ) {
tr y {
t h i s .q u o t i e n t = d iv id e n d / d iv is o r ;
} c a tc h (E x c e p tio n e x ) {
u s in g ( S t r e a m W r it e r sw = n ew S t r e a m W r i t e r ( @ " C : \ L o g s \ e r r o r s . t x t " ) ;
s w .W r it e L in e ( e x .g e t M e s s a g e ( ) ) ;
дальше у 485
несколько предложений
486 глава 10
обработка исключений
О Загрузите только что созданый файл, и вы получите то же самое исключение. Для получе
ния другого исключения нужно попытаться открыть файл, который не содержит оправда
ния. Добавьте блок обработки исключения, вложенный внутрь добавленного на шаге 2 , чтобы
загрузить файл, не содержащий оправдания:
1. Объявите булеву переменную clearForm перед блоком try/catch. Она должна иметь
значение true при наличии исключения и использоваться при проверке необходимо
сти очистки формы.
2. Добавьте еще один блок try/catch внутрь блока кнопки Open.
3. Добавьте блок finally к внешней конструкции try/catch, чтобы вернуть форму
в исходное, пустое состояние. Если переменная clearForm имеет значение true,
присвойте LastUsed.Value свойство DateTime.N ow (оно возвращает текущую дату).
дальше ► 487
решение упражнения
Вот каким образом ваши знания о конструкции try / c a tc h / fin a lly помогли улуч-
_ шить программу Брайана.
аж нш е
реш ение
488 глава 10
обработка исключений
дальше ► 489
11 с о б ь Ш 1и я и д е Л е Г а ш ы
Подающий может
мяч под
'^^ к и м углом и на
^ а к о е расстояние
больше, чем 82 )
P i t c h e r . C a t c h B a l l (7 0 9 0)
События
После удара по мячу вам потребуется с о б ы т и е ( e v e n t ) . Этим тер
мином называется что-то происходящее в вашей программе. На
событие могут прореагировать объекты, например. Pitcher. С О -б Ы “Т И -е , с у щ .
Разумеется, это могут быть и объекты Catcher, ThirdBaseman, то, что случается.
Umpire и даже Fan. При этом реакция для каждого объекта будет Солнечное затмение -
своя.
это событие, которое
То есть объект Ball должен в ы з ы в а т ь с о б ы т и е . Остальные же
объекты будут п о д п и с ы в а т ь с я н а с о б ы т и е э т о г о т и п а ... и реагиро нельзя пропустить.
вать на его возникновение. На это .
- ° событие, может
пО >^ри эт ом объек -
Тпй подписчи-
ииной ^ ков неизвестен.
13д|[|1л.р!йУ-
Вызвано событие BalllnPlay
Ва\'
В ИСР события
помечаются знач Фанаты подписыва
ком 6 виде м о л ются на случай, если
нии. Вы уже могли мяч попадет на три -
его видеть р я НапаЭдкзьцмй Судья проверяет, по пра- буны.
дом с событиями м други е и гр о бмлйм ли обрабатывается
в окнах IntelliSense ки ст а р а ю т ся каждый мяч, и от сле
и Properties. noAy4wt^f мям. живает происходящее
на поле.
Обработчик событий
Логичным результатом оповещения объектов о событии должен
быть запуск некоего кода. Этот код называют о б р а б о т ч и к о м с о б ы
3(71
т и й ( e v e n t h a n d le r ) . одного р uwon^A
илелиок н« к
Все это происходит во время работы программы без вашего вмеша с1ллаиоби ^om ofo^
тельства. Вы пишете код, вызывающий событие, затем код для его образом
обработки и запускаете приложение. П ри возникновении события,
обработчик начинает свою деятельность... вы при этом не делаете ни
чего. И лучше всего то, что объекты при этом заботятся только о себе,
а не о других объектах.
Событие запускается
По мячу наносится удар. Именно в этот момент объект B a l l вы
зывает новое событие.
6 и гр у-
Событие BalllnPlau
вызывается обьек-
>^ом Ball.
494 глава 11
события и делегаты
Обработка события
После возникновения события все подписанные на него объекты
получают уведомления и могут совершать различные действия.
Событие
BalllnPlay
p u b l i c e v e n t E v e n t H a n d le r B a l l l n P l a y ;
496 глава 11
события и делегаты
IJepeBepHuine с т р а н и ц у и л|=оДоЛжиМ
дальше ► 497
Объект B a ll оповещает подписчиков, что он в игре, с помощью события
Теперь, когда все настроено, объект B a l l может в ы з в а т ь с в о е с о б ы т и е в ответ на
определенные действия симулятора. Вызвать событие легко.
По мячу наносится
удар, tA обьект
Bal начинает
действовать... Событие, не имеющее
обработчика, становит-
,Ы передавая 5 и д ы п е I ся причиной
его возиикме- « с щ о р о ^ к н ы ! исключения.
длу c o o b i m u h o . : J
Если другие объекты не
Г Событие акт ив добавят событию свои
но. К т о на него обработчики, оно полу
подписан?
чает значение null, и
появляется исключение
N u llR e f e r e n c e E x c e p tio n .
О бьект Pitcher связы
Именно поэтому нужно
вает свой обработчик
с событием BalllnPlay. копировать событие в
переменную перед про
веркой на равенство null.
Поэтому метод
обьекта Pitcher В крайне редких случаях
вызывается с п р а событие успевает приоб
вильными данными рести это значение уже
и может рабо после проверки.
Prtc т ат ь с событием.
498 глава 11
события и делегаты
Ч асто
Задаваем ы е
В опросы
Зачем в объявлении события ^ I Да! Ваши события вместо объ И поэтому для добавления об
писать слово E v e n tH a n d le r ? Я екта и ссылки E v e n t A r g s могуг работчика события я использовал опе
думал, что обработчиками называют отправлять что угодно или вообще ничего! ратор +=? Как будто добавляя новый
способ других объектов подписаться Посмотрите на последнюю строчку, пред обработчик к уже существующим.
на событие. лагаемую функцией IntelliSense. Обратили
внимание, как метод o n D r a g O r o p ^ : Именно так! Благодаря оператору +=
принимает ссылку D r a g E v e n t A r g s ваш обработчик событий не замещает
; Это верно, для подписки на со-
вместо E v e n t A r g s ? D r a g E v e n t A r g s предыдущий. Он становится еще одним
jbiTMe пишется метод, называемый
наследует от E v e n t A r g s точно так в цепочке обработчиков одного и того же
обработчиком событий. Но вы заметили,
же, как и B a l l E v e n t A r g s . Со события.
каким именно образом E v e n t H a n d l e r
бытие формы D r a g D r o p не ИС-
использовался в объявлении события
пользует E v e n t H a n d l e r . Он берет
(на шаге #2) и в строчке подписки (на Почему при вызове события
D r a g E v e n t A r g s , И ДЛЯ его обработки
шаге #4)? E v e n t H a n d l e r определяет о использовалось
B a llln P la y
ваш метод должен брать объекг и ссылку
сигнатуру события, он сообщает объ слово this?
DragEventArgs.
ектам, подписывающимся на событие,
каким именно образом они должны
Параметры события определяются I 1; Это первый параметр стандартного
определить методы-обработчики. А для о6|эаботчика событий. Вы заметили, что
подписки метода на события он должен
делегатами. Примеры делегатов —
E v entHandler и DragEventArgs. любой обработчик события C l i c k имеет
иметь два параметра ( obj e c t и ссылку параметр object sender? Это ссылка
О том, ЧТО это такое, мы поговорим чуть
E v e n t A r g s ) И не возвращать значений. на вызывающий событие объект. То
позже,
есть когда вы обрабатываете щелчок
Что произойдет при попытке на кнопке, объект s e n d e r указывает
) • Можно ли сделать так, чтобы обра-
:
воспользоваться методом, не совпа на эту кнопку. При обработке события
отчик событий возвращал значение? B a l l l n P l a y параметр s e n d e r будет
дающим с определенным при помощи
E v e n tH a n d le r ? указывать на объект Ball, а мяч при
П ; Можно, но делать этого не следует. вызове события обозначит этот параметр
Ведь именно отсутствие возвращаемых ключевым словом this.
Q ; Программа не будет компилировать
значений позволяет соединять обработчи
ся. Компилятор следит за тем, чтобы вы
ки событий в цепочки, присоединяя к од
случайно не подписались на метод-об-
ному событию несколько обработчиков.
работчик, несовместимый с событием.
Стандартный обработчик событий
E v e n t H a n d l e r показывает, как именно Б ! Зачем нужны цепочки? ОДНО событие
должны выглядеть ваши методы.
; Они позволяют подписать на одно со- всегда вызывается
!
)'• Что значит «стандартный» обра-
отчик событий? Разве есть и другие? 0 гие несколько обработчиков. Эта про
цедура будет рассмотрена чуть позднее. ОДНИМ объектом.
Но отвечать
на него M O iy r
МНОГИЕ объекты.
дальше ► 499
это сэкономит вам время
500 глава 11
события и делегаты
Нажмите клавишу tab, чтобы добавить этот обработчик событий в класс Pitcher. Вы
бор имени будет проведен по схеме objectName_HandlerName ():
void ball_BallInPlay{object sen d e r , EventArgs e) {
t h r o w n e w N o t l m p l e m e n t e d E x c e p t i o n ();
„звоЗииО »“ З е г и е м к нисхо-
EventArgs, ^pu помощи
П р и ш л о вр е м я п р и м е н и ть п о л у ч е н н ы е зн а н и я на практике. В а м нуж но
пражнение за к о н ч и ть к л а сс ы Ball и Pitcher, д о б а в и т ь к л а сс Fan и уб е д и ть ся, что
они р а б о т а ю т п р ави л ьн о .
class Pitcher {
public Pitcher(Ball ball) {
ball.BalllnPlay += new EventHandler(ball_BallInPlay);
Fan
502 глава 11
события и делегаты
Список вывода.
Вот какой результат должен выдать симулятор после трех мячей:
Output - П X
ешение
c la s s B a ll
{
public event EventHandler BalllnPlay;
public void OnBalllnPlay(BallEventArgs e) {
EventHandler balllnPlay = BalllnPlay;
if (balllnPlay 1= null)
balllnPlay(this, e) Метод ОпВаШпР1ау() вызы
вает событие ВаШпР(аи. Не
} забудьте проверить, не равно
ли его значение пиЦ иначе он
ст анет причиной исключения
В качестве ар гу- c l a s s B a ll E v e n t A r g s : E v e n tA r g s
ментов события (
прекрасно подхо public int Trajectory { get; private set; }
дят авт ом ат и
чески добавляемые public int Distance { get; private set; }
предназначенные public BallEventArgs(int trajectory, int distance)
только для чт е
ния свойства. this.Trajectory = trajectory;
Ведь обработчики
событий только this.Distance = distance;
чит аю т переда К онструктор объекта
ваемый им данные. } Fan привязывает свой
обработчик события
c l a s s F an { к событию BalllnPlay.
public Fan(Ball ball!
504 глава 11
события и делегаты
c la s s P itc h e r {
public Pitcher(Ball ball) {
ball.BalllnPlay += n e w E v e n t H a n d l e r ( b a l l _ B a l l I n P l a y ) ;
В классе pitcher уже им е-
} —s е т с я обработчик события
void ball_BallInPlay(object sender, EventArgs e) dC BalllnPlay. Он проверяет
низко летящ,ие мячи.
if (e is B a l l E v e n t A r g s ) {
BallEventArgs ballEventArgs = e as B a l l E v e n t A r g s ;
if ((ballEventArgs.Distance < 95) S=& ( b a l l E v e n t A r g s . T r a j e c t o r y < 60))
C a t c h B a l l ();
else
C o v e r F i r s t B a s e ();
}
}
private void C a tc h B a ll0 {
Console.WriteLine("Pitcher: I caught the ball");
}
p r i v a t e v o i d C o v e r F i r s t B a s e () {
Console.WriteLine("Pitcher: I covered first base");
}
}
дальше *■ 505
представление страницы событий
Обобщенный EventHandler
Посмотрим на объявление события в классе Ball:
public e v e n t E v e n t H a n d le r B a l l l n P l a y ;
public event E v e n t H a n d le r C l i c k ;
Обобщенный
Они называются по-разному, но объявляются одним и тем же способом. А так аргумент
как все это прекрасно работает, постронние могут и не знать, что обработчик EventHandler
должен быть
BallEventHandler передает BallEventArgs при возникновении события. К сча производным
стью, .NET имеется инструмент, позволяющий легко сообщить эту информацию, — от EventArgs.
обобщенный EventHandler. Измените обработчик события BalllnPlay вот таким
образом:
Дело в том, что после изменений в объявлении события нужно обновить классы Pitcher и Fan, заста
вив их передавать обработчику обобщенный аргумент:
b a l l .B a l l l n P l a y += n e w E v e n t H a n d l e r < B a ll E v e n t A r g s > ( b a l l _ B a l l I n P l a y ) ;
НеяВное преобразование
Автоматически созданный обработчик событий будет обязательно содержать ключевое слово new, за
которым следует его тип. Если же убрать это ключевое слово и тип обработчика, C# осуществит н е я в
н о е п р е о б р а з о в а н и е и определит тип за вас:
b a l l . B a l l l n P l a y += b a l 1 _ В а 1 1 I n P l a y ;
Замените код в конструкторах классов Pitcher и Fan указанным выше способом. Вы увидите, что на
работе программы это не отразится.
506 глава 11
события и делегаты
просмотра событий,
язаннш с элементом
управления, выделите Properties П X
эт от элемент и щ елк F o n n l'''5 y ^ m .W in d o w s .F o r m s .F o r m
ните на кнопке со значком Найдите строчку Click
молнии. и дважды щелкните на
_________ І ней. ^удет добавлен но
C h a n q eU IC u es вый обработчик события,
Чтобы создать со
бытие, возникающее FormI.Ctick который акт ивизиру
при щелчке на фор ClientSizeChanged ется п р и любом щ елч
м е, нужно выбрать СontextMenuStripC ке на форме. В файле
в раскрывающемся formX.Designer.cs по
списке в строчке Ctick явится новая строка,
Click вариант связывающая обработчик
Occurs when the component is dkkerf.
Forml^Click. и событие.
О После двойного щелчка на строчке Click к форме будет автоматически добавлен обработ
чик события Forml_Click. Введите для него код:
private void Forml_Click(object sen d e r , EventArgs e) {
M e ss a g e B o x .S h o w (" Y o u j u s t c l i c k e d o n t h e f o r m " ) ;
}
© Visual Studio не только пишет за вас объявление метода, но и связывает обработчик с со
бытием Click формы. Откройте файл Forml .Designer .cs и воспользуйтесь функцией
Quick Find (Edit » Find and Replace » Quick Find) для поиска текста Forml_Click. Вы
найдете строчку:
this.Click += n e w S y s t e m . E v e n t H a n d l e r ( t h i s . F o r m l _ C l i c k ) ;
страниїзу u продолжим
508 глава 11
события и делегаты
Обработчик события
^ ___ Click формы вызвал
■
— окно с сообщени
ем «Вы только что
щелкнули на форме».
★ Теперь щелкните на кнопке buttonl, а затем снова на форме. Появятся два окна
с текстом: «Youjust clicked on the form» и «Something». 4:— '
Каждый щ ел
чок на кнопке
приводит к
появлении:) еще
одного окна
диалога.
★ Дважды щелкните на кнопке button2, а затем снова на форме. Появятся четыре
окна: «Youjust clicked on the form», «Something», «Something else» и «Something else».
до/
“*Г н .й
4 У
Собы тие BalllnPlay
Св prtcV'®’
О б ьект Ball не должен и м ет ь
связи с объект ом P itcher Ему
все равно, объект ы какого т ипа
работ аю т с событием: Fan,
Pitcher, U m pire и т. п.
510 глава 11
события и делегаты
\
public delegate void EventHandler(object sender, EventArgs e)
Эт от элемент сигнатуры делегата Имя делегата
показывает, что EventHandler может EventHandler.
ссылаться только на методы, не
возвращающие значения. Vу п ] ^ а ж^ н е н и е
Добавляем к проекту ноВый т и п данных
Добавляя к проекту делегаты, вы добавляете данные нового типа, Используя их для создания поля или
переменной, вы создаете э к з е м п л я р этого типа. О т к р о й т е н о в ы й п р о е к т C o n s o le A p p l i c a t i o n и добавьте
к нему новый файл классов ConvertsIntToString.cs. Введите одну строчку:
delegate string ConvertsIntToString(int i); Еще одним добавленным в про
ект делегатом является
Добавьте в класс Program метод HiThere (): ConvertsIntToString. Его мож
но использовать для объявления
private static string HiThere(int i) Сигнатура этого переменных точно так же, как
{ 'F - метода совпадает вы делали бы это для класса или
return "Hi t h e r e ! #" + (i * 100)
с ReturnsAString. интерфейса.
)
someM etkod — это переменная типа
Заполните метод M ain (): ConvertsIntToString. О т обычной ссылочной
s t a t i c v o i d M a i n ( s t r i n g [] args)
переменной она отличается т ем , что цка-
^ зывает не на объект в куче, а на м ет о1
{
ConvertsIntToString someMethod = new ConvertsIntToString(HiThere);
string message = s o m e M e t h o d (5);
console.writeLine(message) ; д П е р е м е н н о й
}
Переменная someMethod указывает на метод HiThere (). Записью someMethod (5) вызывается метод
HiThere (), которому передается аргумент 5. В итоге возвращается строкаН1 there! #500. Просмотрите
программу в режиме отладки, чтобы понять, что именно происходит.
дальше ► 511
делегируйте свои полномочия
Делегаты В geiicmBuu , ,
Работать с делегатами легко, они не требуют большого объема кода. По- \Г '
пробуем с их помош;ью помочь владельцу ресторана рассортировать се- J T lj^& yK H 0H U 0,
кретные ингредиенты с кухни шеф-повара. ^
(Полностью удалите объявление класса.) Этот делегат станет основой переменной, указы-
ваюш;ей на метод, который, взяв параметр типа i n t , возвраш;ает строку.
512 глава 11
события и делегаты
Secret Ingredients
Добавьте к проекту делегаты
Постройте форму. — Gei the ingredient
%
дальше > 513
чересчур открытые события
Б бассейне
public FormlО {
InitializeComponentO ;
t h i s . ________ += n e w E v e n t H a n d l e r {___________
}
void Towtruck(object sender, EventArgs e) {
b u t t o n l .________ += n e w E v e n t H a n d l e r (Dumptruck) ;
b u t t o n l . ________ += n e w E v e n t H a n d l e r (________________);
}
v o i d ________________( object se n d e r , EventArgs e) {
514 глава 11
события и делегаты
в& ^
Событие HitTheBall
Вал
вит
О й ' Э т о ж е были резер
м ячи, добавленные на вс ЯКий
'1С
случаи.
Обратный Вызов
События в системе работают корректно, если объекты Ball и Bat имеются в единственном числе. В си
туации, когда мячей больше одного, все они оказываются подписаны на событие HitTheBall и при его
возникновении вбрасываются в игру, что не имеет никакого смысла. Другими словами, нам нужно свя
зать с битой всего один мяч, исключив возможность привязки других мячей.
в этом нам поможет о б р а т н ы й в ы з о в ( c a llb a c k ) . Так называется техника работы с делегатами, при
которой вместо события, доступного для подписки любым объектам, используется метод (часто кон
структор), с хранящимся в закрытом поле делегатом в качестве аргумента. Обратный вызов позволит
нам гарантировать, что объект Bat оповещает всего один объект Ball:
О Конструктор объекта B at
Когда мяч оказывается в игре, конструктор создает экземпляр биты и передает указатель
на него методу OnBalllnPlay ( ). Это м е т о д о б р а т н о г о в ы з о в а , так как объект Bat ис
пользует его для вызовы объекта, который его создал.
Объект Ball передает
сш лк у на делегат свое-
_ м у собственномц методи
ОпВаІІІпРІауО в конст рук-
1 \^ ^ торе объекта Bat. По
следний сохраняет данный
делегат в закрытом поле
hitriaeBallCallback.
О Когдо Bat ударяет по мячу, он использует метод обратного вызова
Но пока Bat скрывает делегат, можно быть полностью уверенным в том, что ни
один другой мяч в игру не попадет. Вот решение проблемі.]!
-Г hitBalICalIback
Теперь объект
t Bat может вы
звать делегат
hitBallCallhackj ко
Д ругие мячи не м огут торый, в свою оче
подсоединиться к эт о редь, вызовет м е
м у делегату, так как тод OnBallInPiayQ
данное поле объекта объекта Ball.
Bat закрыто.
516 глава 11
событ ия и делегаты
К о н с т р у к то р public Forml0 { і ^ і п е н и е p * e ^ c :a
последователь I n i t i a l i z e C o m p o n e n t ();
но добавляет t h i s . Load += n e w E v e n t H a n d l e r ( M i n i v a n ) ; Б бассейне у
два обработчи
ка к событи!^М
this. Load += n e w E v e n t H a n d l e r (Motorcycle) ;
Load. В итоге }
они вызываются void Towtruck(object s ender, EventArgs e) {
сразу после за C o n s o l e . W r i t e ("is c o m i n g ");
грузки формы.
}
void Motorcycle(object se n d e r , EventArgs e) {
}
void Dumptruck (obj e c t sen d e r , EventArgs e) {
C o n s o l e . W r i t e (" F i n g e r s ");
}
Мы воспользовались оператором =, так как в данном случае нужно, чтобы объект bat
получал сообщения только от одного объекта ball, соответственно, данный делегат
настраивается всего один раз. Но ничто не запрещает написать обратный вызов с
оператором +=, который будет адресован нескольким методам. Основной смысл
обратного вызова — в отслеживании вызывающим объектом адресатов. В случае
события, объекты требуют оповещения, добавляя обработчики, в то время как при
обратном вызове объекты перебирают делегатов и просят об оповещении.
Свяжем биту с мячом
© ...........
Каким гг__________
же образом конструктор объекта Bat получает ссылку на метод OnBalllnPlay () определен
ного мяча? Он вызывает метод GetNewBat {) (Получить новую биту) этого объекта, который мы
сейчас добавим:
Метод QetNewBatÇ) создает
p u b l i c Bat G e t N e w B a t О объект Bat и использует де
легат BatCallBack для передачи
{ ссылки на эт от новый объ
return new Bat(new BatCallback(OnBalllnPlay)) ект собственному методу
) / OnBallInPiayQ. Именно эт от
мет од обратного вызова будет
применен битой в м омент уда
ра по мячу.
его 8 m L oS „ л » м З “ /„ Г “
518 глава 11
события и делегаты
Инкапсулируем класс B a l l
Методы, вызывающие события, названия которых начинаются с On... не бывают открытыми.
Попробуйте в форме вызвать событие OnClick () кнопки playBall, вы не сможете это сделать,
так как оно защищено (в итоге производный класс может перекрыть его). Сделаем такой уровень
доступа и для метода OnBalllnPlay ():
protected void OnBalllnPlay(BallEventArgs e) {
EventHandler<BallEventArgs> balllnPlay = BalllnPlay; й
H (b a llln P la y „ u lll ^
b a llI n P la y (th is , e ) ,■ N E T
С одним событием можно связать несколько обработ Обратные вызовы и события используют делегатов
чиков. Именно поэтому для присвоения обработчика для ссылки и вызова методов других объектов.
событию используется оператор +=. Чтобы понять, как работают делегаты, используйте
Всегда проверяйте события и делегаты на равенство null. отладчик.
_ Часто
даД аБ аеМ ы е
Bollj=»oCbi
Чем обратный вызов отличается от события? Получается, что обратные вызовы — это всего лишь
закрытые события?
! События и делегаты — это часть .NET. Это способ, которым
одни объекты оповещают других о произведенных действиях. На Q ; Не совсем. Проще всего представлять их таким образом,
событие может подписаться произвольное количество объектов, но закрытые события имеют свою специфику. Помните, что на
при этом издатель лишен возможность узнать о них. При запуске самом деле означает модификатор доступа p r i v a t e ? Доступ
события все подписанные на него объекты запускают обработ к помеченному им члену класса имеют только экземпляры этого
чики. класса. Поэтому, пометив событие как p r i v a t e , вы запре
щаете подписываться на него из других классов. В то время как
Обратные вызовы не принадлежат .NET — это всего лишь на обратные вызовы допускают анонимную подписку.
звание способа использования делегатов (или событий, ничто
не мешает вам создать обратный вызов из закрытого события).
Но обратный вызов выглядит как событие, не снабжен
Этим термином всего лишь называются отношения между двумя
ное ключевым словом event.
классами, при которых объекг запрашивает оповещение. В слу
чае же событий объекты требуют оповещений.
Q l Обратный вызов похож на событие, потому что они оба ис
пользуют делегатов. Это имеет смысл, так как обратный вызов
То есть обратные вызовы не принадлежат .NET?
является инструментом, позволяющим одному объекту передать
другому ссылку на свой метод.
Q ; Нет. Обратный вызов — это шаблон, способ использования
существующих типов, ключевых слов и инструментов. Рас Но событие — это способ, которым класс оповещает мир о неких
смотрите внимательно код обратного вызова, написанный для действиях, в случае же обратных вызовов оповещения отсут
биты и мяча. Присутствуют ли там неизвестные вам ключевые ствуют. Они являются закрытыми, и метод, осуществляющий
слова? Нет! Но при этом там не используются делегаты, которые вызов, отслеживает, кто именно вызывается.
являются типом .NET
520 глава 11
событ ия и делегаты
Вот почему Генри потерпел поражение. Добавив обработчик событий конструктору ТгеазигеН ипЪ ег,
он нечаянно сделал это для всех охотников] Обработчики событий всех охотников оказались связанными
с одним и тем же событием RunForC over. и сообщение о перемещении краба в новое укрытие пришло
всем. Можно было бы обернуть дела в свою пользу, если бы Генри получил сообщение первым. Но Генри
не мог узнать, в каком порядке проводится оповещение. В итоге все подписавшиеся до него получили
новость о местоположении краба раньше.
pu b l i c p a r t i a l class Forml : F o r m {
M o l e mole;
R a n d o m r a n d o m = n e w R a n d o m {);
public F o r m l 0 {
I n i t i a l i z e C o m p o n e n t ();
m o l e = n e w M o l e ( r a n d o m , n e w M o l e . ________ J) ;
t i m e r l .I n t e r v a l = r a n d o m . N e x t (500, 1000)
timerl.Start 0 ; форма передает делегата
} указыван>и1,ий на метод
private void timerl_Tick(object sen d e r , EventArgs e) обратного вызова в кон
timerl.Stop 0 ; ст рукт оре моли.
T o g g l e M o l e () ;
522 глава 11
события и делегаты
using System.Windows.Forms;
class Mole {
Впиш им е делегаш, и поле, в к о
public void PopUp(int hole, bool show) т о р о м он будет хранит ься,
,ода п а р а м е т р а должны нахо
p r i v a t e _____________ p o p U p C a l l b a c k ; дит ься в верхней част и класса
p r i v a t e bool hidden;
Mole.
p u b l i c bool H i d d e n { get { re t u r n hidden; } }
З десь м ы п роверя ем н е
p r i v a t e in t t i m e s H i t = 0;
равен ст во обрат ного
p r i v a t e in t t i m e s S h o w n = 0;
p r i v a t e i n t h o l e = 0;
вызова null, в п р о т и в
R a n d o m random; ном случае объ ект Mole
/в ы з о в е т исключение
p u b l i c M o l e ( R a n d o m random, Po p U p p o p UpCallback) { у / A rgu m en tE xception .
if ( p o p U p C a l l b a c k == null)
t h r o w n e w A r g u m e n t E x c e p t i o n (" p o p U p C a l l b a c k c a n ' t b e null")
t h i s . r a n d o m = random;
При создании нового о б ъ
t h i s .___________
ект а Mole ф о р м а п е р е
h i d d e n = true; дает ссылки на его м ет од
} обрат ного вызова. П осмо
т р и т е , как именно вы
public void S h o w O { зы вает ся к о н ст рукт ор
timesShown++; ф орм ы , и заполнит е эт о
h i d d e n = false;
поле.
h o l e = r a n d o m .N e x t (5)
(hole, true)
т ек ст «H IT ME!» ^ ^<^ображает
public void Hi d e A g a i n O
h i d d e n = true;
М етоды Н1с(еАда1пО и 5таскес1()
(hole. false); также п ользую т ся дел егат ом
C h e c k F o r G a m e O v e r () обрат ного вы зова, чтобы вы зват ь
м ет о д ф орм ы .
public void Smacked(int holeSmacked) {
if ( h o l e S m a c k e d == hole) { В игре использует ся т а й м ер со
timesHit++; случайной задерж кой о т 0 . 5 до Л..5
h i d d e n = true; секунд. После т ого как врем я выилло,
C h e c k F o r G a m e O v e r ();
он вы зывает м оль. О брат ны й вызов
объект а Mole заст авля ет ф о р м у
(hole, false) п оказат ь или скры т ь м оль в одном
из пят и полей, ф орм а и сп ользует
т а й м ер , чтобы через п ром еж у
т ок врем ени (о т 0 .5 до i-.S секунд)
private void CheckForGameOver( { уб р а т ь моль.
if ( t i m e s S h o w n > = 1 0 ) {
p o p U p C a l l b a c k (-1, fal s e ) ;
MessageBox.Show("You scored + timesHit, " G a m e over"
Application.ExitO ;
^ И гра заканчивает ся после т ого, как м оль
'У появит ся ХО раз. Баши очки п оказы ваю т ,
сколько р а з вам удалось ее прихлопнут ь.
дальше ¥ 523
решение упражнения
popUpCallback
t h i s .___________
popUpCallback
h i d d e n = true;
} Создав новый экземпляр объекта Mole,
public void S h o w O {
\ форма передает конструкт ору в ка-
timesShown++;
\ честве парамет ра ссылку на метод
h i d d e n = f a lse;
MoleCallBackQ. Эта строка в кон
hole = r a n d o m .N e x t (5);
ст рукт оре копирует ссылку в поле
popUpCal back. Методы конст рук
popUpCallback тора при помощи этого поля м о
(hole, true) гут вызывать мет од MoleCallBackQ
} в форме.
public void Hid e A g a i n O {
h i d d e n = true;
524 глава 11
12 обзор и преДБа]=пхіхіеЛьНьіЄ реЗуЛьніазпьі
CalculateCostOfDecorationsO
CalculateCostO
SetLocationO
б а з а д анны х SetDestinationO DinnerParty BirthdayParty
M odifyRouteToAvoidO NumlwrOfPeopte NumljerOfPeople
Вы научились работать M odifyRouteTolncludeO CostODecorations CostODecorations
eimii
7 переменных т ипа in t
в«
Вы стали пчелоВодом
Помните, как в главе 6 вы моделировали работу улья? Разные виды пчел выполняли
разную работу...
Sysfem.V'fi'
интерфе.йс.
О бьект World
представляет
общую кар
тину.
'разумеется, мы
Для каждого не обойдемся без
цветка пот ре класса Вее.
буется обьект
Flower.
528 глава 12
обзор и предварит ельны е результ ат ы
О С о з д а т ь к л а с с F lo w e r, в к о т о р о м ц в е т ы р а с п у с к а ю т с я ,
д а ю т н ектар, в я н у т и ум ир аю т.
О С о з д а т ь к л а с с H iv e , с о д е р ж а щ и й в х о д , в ы х о д , и н к у б а
то р пчел и ф а б р и ку д л я п ер ер аб о тки со б р ан н о го н екта
ра в мед.
О С о з д а т ь к л а с с W o rld , у п р а в л я ю щ и й у л ь е м , ц в е т а м и
и пчел ам и в каж д ы й м о м ент врем ени.
дальш е ► 529
поню хаем цвет ы
'^пражнение П е р е й д е м к н а п и с а н и ю к о д а . Н а ч н е м с к л а с с а F lo w e r . О н с о
реш ение д е р ж и т и нф о р м ац и ю о пол ож ен ии ц ветка, во зр асте и про дол ж и
т е л ь н о с т и ж и з н и . Н у ж н о с д е л а т ь т а к , ч т о б ы ц в е ты с о в р е м е н е м а .w vca
с т а р е л и и у м и р а л и . Э т и м вы с е й ч а с и з а й м е т е с ь . ^ с к е л е т о м класса
oSv^eACH f
Основа класса Flower ^о ка не н.ап\лсана реа
Н а о с н о в е д и агр ам м ы кл асса F l o w e r н ап и ш и те его скелет. L o c a t i o n (М есто п о л о ж ен и е),
А де (В о зр аст), A l i v e (Ж и в о й ), N e c t a r (Н ек т ар ) и N e c t a r H a r v e s t e d (С о б р ан н ы й н е к та р ) -
автоматические свойства. П о с л ед н ее сво й с тв о п е р е за п и с ы в ае м о , в то в р ем я как п е р в ы е ч е
т ы р е п р е д н а зн а ч е н ы только для чтения.
О Необходим ы е константы
Конст ант ы обычно
не показываются на
Д о б а в и м в класс F lo w e r ш е сть к о н с та н т: диаграмме классоО.
♦ Lif eSpanMin - м и н и м а л ь н а я п р о д о л ж и т е л ь н о с т ь ж и з н и ц в е т к а .
♦ InitialNectar — с к о л ь к о н е к т а р а и з н а ч а л ь н о с о д е р ж и т ц в е т о к .
♦ MaxNectar - м а к с и м а л ь н о е к о л и ч е с т в о н е к т а р а , к о т о р о е м о ж н о с о б р а т ь с ц в е т к а .
♦ NectarAddedPerTurn - к о л и ч е с т в о н е к т а р а , д о б а в л я е м о е п о м е р е р о с т а ц в е т к а .
♦ NectarGatheredPerTurn - к а к м н о г о н е к т а р а у д а е тс я с о б р а т ь за о д и н ц и к л .
о с н о в у з н а ч е н и е к а ж д о й к о н с т а н т ы , в ы б р а ть д л я н е е т и п . Ц в е т ы ж и в у т о т
15 ООО д о 3 0 ООО ц и к л о в , и в н а ч а л ь н ы й м о м е н т и м е ю т 1 .5 е д и н и ц ы н е к т а р а . К о л и ч е с т в о
н е к т а р а м о ж е т д о х о д и т ь д о 5 е д и н и ц . З а о д и н ц и к л п р и б а в л я е т с я 0 .0 1 е д и н и ц ы н е к т а р а
и 0 .3 е д и н и ц ы м о ж е т б ы ть с о б р а н о . 4 ^ ’
530 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
О П о стр о ен и е конструктора
К о н с т р у к т о р д о л ж е н п е р е д а в а т ь с т р у к т у р у Point, з а д а ю щ у ю п о л о ж е н и е ц в е т
к а , и э к з е м п л я р к л а с с а Random. Н у ж н о у к а з а т ь , ч т о в о з р а с т ц в е т к а р а в е н О, ч т о
о н ж и в о й , а т а к ж е п р и с в о и ть ему начал ь н о е ко л и ч е ств о н е кта р а . Н а к о н е ц , тр е
б у е т с я р а с с ч и т а т ь п р о д о л ж и т е л ь н о с т ь ж и з н и ц в е т к а . В о т с т р о ч к а , к о т о р а я в ам
пом ож ет:
lifeSpan = random.Next (LifeSpanMin, LifeSpanMax -н 1);
Х Г и Г » Г е р е Э .6 » .« «
конст рукт ору Flower.
М ето д HarvestNectar О
П р и к а ж д о м в ы зо в е э т о г о м е т о д а п р о в е р я е т с я , д о с т и гл о л и к о л и ч е с т в о с о б р а н
н о г о н е к т а р а м а к с и м у м а , к о т о р ы й м о г у т д а ть ц в е т ы . В э т о м с л у ч а е м е т о д в о з
в р а щ а е т 0. В п р о т и в н о м случае в о зв р ащ ается к о л и ч е с тв о с о б р а н н о го н е к т а р а ,
ко то р о е добавляется к п е р е м е н н о й N e c ta r H a r v e s te d , х р а н я щ е й и н ф о р м а
ц и ю об общ ем сборе с каж д о го ц ветка. ^
О М ето д Go {)
К а ж д ы й в ы зо в э т о г о м е т о д а с о о т в е т с т в у е т п р о ж и в а н и ю ц в е т к о м о д н о г о ц и к л а ,
зн а ч и т, д о л ж е н о б н о в л ять ся и е го в озраст. Н у ж н о п р о в е р я ть , н е д о сти гл а л и
э та п е р е м е н н а я за д а н н о го для ц в е тк а в р е м е н и ж и з н и . В случае п о л о ж и те л ь н о
го резул ь тата ц в е т о к ум и р ает.
К ж и в ы м ц в етам н у ж н о д обав и ть ко л и ч е с тв о п р о и з в е д е н н о го за ц и к л н е кта р а .
П р о в е р я й те , н е п р ев ы ш ает л и о н о м акси м ал ь но в о зм о ж н о е зн ач е н и е .
дальш е * 531
куда пропали цвет ы ?
1
не н ке В о т к а к в ы гл я д и т к л а с с Flower д л я с и м у л я т о р а у л ь я . F lo w e r
Location: Point
)^еш енке class Flower { Age: int
private const int LifeSpanMin = 15000; Alive: bool
private const int LifeSpanMax = 30000; Nectar: double
private const double InitialNectar = 1 .5 ; NectarHarvested: double
private const double MaxNectar = 5.0;
lifespan: int
private const double NectarAddedPerTurn = 0.01;
private const double NectarGatheredPerTurn = 0.3; HarvestNectar(): double
свой ст ва public Point Location { get; private set; } Go()
L o c a t io n .,
{
public int Age { get; private set; }
Aü v e U Nectar
к\реЭназнйчене>| public bool Alive { get; private set; }
Д ост уп к полю
т о л ь к о Эля public-----------------------
double Nectar { get; private Oset; ^}
чтени я.
^ ^ -t. V d u e C U ;
N ectarH arvested
public
ГМ 1 T -i ^ double
/-ЧЧ ЛV-4 1 Л NectarHarvested
^ 4- _ ^ T T _ _________ ______ □ {f get;
_ set; } <2^ ^ должны имет ь
private int lifeSpan; другие классы.
532 г л а в а 12
обзор и пред вар и т ельны е результ ат ы
I
У Каждого цветка. метода записи.
аде - 30291 1
nectar = ,83
Г ; Вы могли написать код по-другому,
но если он функционирует так же,
s s r
alive = false как и наш, все в порядке. Особенность
і paem - < инкапсуляции в том, что внутренняя
структура классов не важна для других
/ классов, нужно только, чтобы класс вы
O Q y S (S полнял свою функцию.
ШТУРМ
М етод Со () увеличивает возраст цветка на 1 при продолжительности ж изни от
15 ООО д о 30 ООО циклов. То есть м етод Со {) д ля одного цветка будет вызван
по м еньш ей м е р е 15 ООО раз. Как обработать та ко е количество вызовов?
А в случае 10 цветов? 100? 1000?
дальш е ► 533
т руд я га -пч ел а
Класс Вее
Д л я цветов, г о т о в ы х дать нектар, п о н а д о б и т с я класс Вее, в к о т о р о м с о д е р ж а т
ся свед е н и я о возрасте п ч е л ы , ее м е с т о п о л о ж е н и и и в о з м о ж н о м колич е с т в е со
б и р а е м о г о нектара. В класс б ы л т а к ж е д о б а в л е н метод, п е р е м е щ а ю щ и й пчелу
в н у ж н у ю точку пространства.
class Bee {
private const double HoneyConsumed = 0.5;
private const int M o v e R a t e = 3;
P l o w e r ^ ^
private const double MinimumFlowerNectar = l
•только с эт им к л а ссо м ""'''''''’'''
private c onst int CareerSpan = 1000;
534 г л а в а 12
обзор и предварит ельны е результ ат ы
M oveR ue paccm o-
ямии от пункт а
ЧРЛЛ
Элемент функция
Idle Пчела ничем не занята
FlyingToFlow er Пчела лет ит на цветок
G atkerin gN ectar Пчела собирает нект ар
Retu rningToH ive Пчела возвращается в улей
M akingHoney Пчела производит мед
R etired Пчела прекращ ает работ у
епш п B e e s t a t e {
I d le ,
F ly in g T o F lo w e r ,
Это перечисление
G a th e r in g N e c ta r , возможных состояний
R e tu r n in g T o H iv e , пчелы.
M a k in g H o n e y ,
R e tir e d
}
class Bee {
// объявление констант
// объявление переменных
536 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
дальш е k 537
внеш ний вид улья
У л е й — н е п р о с т о м е с т о , куд а в о з в р а щ а ю т с я п ч е л ы . В н у т р и о н р а з д е л е н н а з о н ы
р а з л и ч н о го н а з н а ч е н и я . С ущ ес тв ую т вход и вы ход, п и т о м н и к для в ы р а щ и в а н и я
м о л о д ы х п ч е л и ф а б р и к а п о п е р е р а б о т к е н е к т а р а в м ед.
Зоны б н у т р м ул1?я
отделены друг от
друга, и пчела м о
жет переходить
из зоны в зону т ак
же, как она лет ит
из улья к цветку.
Нобы е іячел&і
їл о я б л я ю т с я из
lA.UWOMHWKfl-
Пчелы залет аю т в цлей
через вход, а покидают
е го через выход. Все
хорошо организовано.
538 г л а в а 12
о б з о р и предварительные р е з у л ь т а т ы
^ п р а ж нн е н и е В а ш а з а д а ч а — с о з д а т ь к л а с с H iv e .
Н а п и ш и те скел ет класса H iv e
К ак и в случае класса F lo w e r , нуж но н ач а ть со ске Honey; double
лета. Д и агр ам м а класса п о к а за н а сп рава. С д елай те locations: Dictionary<string, Point>
с в о й с тв о H o n e y п р е д н а зн а ч е н н ы м т о л ь к о д ля ч т е н и я , beeCount; int
п о л е l o c a t i o n s д о л ж н о б ы ть за к р ы т ы м , к ак и п о л е
b e e C o u n t. InitializeLocationsQ
AddHoney(Nectar; double); bool
ConsumeHoney(amount: double); bool
AddBee(random; Random)
О О пред ел и те константы
У к а ж и т е н а ч а л ь н о е к о л и ч е с т в о п ч е л ( 6 ) , м е д а (3 .2 ) ,
Go(random; Random)
GetLocation(location; string); Point
м а кс и м ал ь н о ко л и ч е с тв о м еда (1 5 ), к о э ф ф и ц и е н т п е р е
р а б о т к и н е к т а р а в м е д ( .2 5 ) , м а к с и м а л ь н о е к о л и ч е с т в о
п ч е л (8 ) и м и н и м а л ь н о е к о л и ч е с т в о м е д а , п р и к о т о р о м
п о я в л я ю т с я н о в ы е п ч е л ы ( 4 ). Присвойте констлн-
■т ам и м е н а
В хо д (6 0 0 ,1 0 0 )
Все эт о привязано
П и т о м н и к (9 5 , 1 7 4 ) к т очкам в дву
м ерном п ро ст ра н
Ф а б р и к а (1 5 7 , 9 8 ) стве улья.
В ы х о д (1 9 4 , 2 1 3 ) впользуеімся
к « - « « Г Г^ Г РсинТат“ ам
“ “ и.
-
р о в а и н ь їм и ^ ^ 0 -
Конструктор H i v e hpu м ож но
К о н с т р у к т о р д о л ж е н за д а в а т ь н а ч а л ь н о е к о л и ч е с т в о
м е д а в уль е и п о л о ж е н и е в н у т р е н н и х э л е м е н т о в , а т а к ж е л а н и и зд _
ж е с о зд а в а т ь э к з е м п л я р R andom . З а т е м д л я к а ж д о й за
р о ж д а ю щ е й с я п ч е л ы н у ж н о в ы зв а ть м е т о д A d dB ee {),
п е р е д а в а я е м у т о л ь к о ч т о с о з д а н н ы й э к з е м п л я р Random .
540 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
Странны й способ
н а п и са н и я кода. П ч е л ы п о ка не знаю т
о ц ветах, а у л е й п о л о н п усты х объ явлений
м етод ов. И н и ч е го не работает, не так л и?
В б о л ь ш и н с т в е с л у ч а е в к о д п и ш е т с я ф р а г м е н т за ф р а г м е н т о м .
М ы п р а к т и ч е с к и н а п и с а л и класс F lo w e r , а в о т для кл асса В е е
э т о с д е л а ть н е п о л у ч и л о с ь . В а м н у ж н о д о п и с а т ь , ч т о с л е д у е т
делать в к а ж д о м и з с о с т о я н и й .
Н о м ы ещ е д аж е н е п р и с ту п и л и
к с о е д и н е н и ю классов д р уг с другом !
М ы разобрались с ар хи те ктур о й
и п р и с ту п и л и к п о с тр о ен и ю .
Р а б о т а т ь н а д п р о е к т о м н а м н о г о п р о щ е , е с л и вы з а р а н е е п р е д с т а в
л я е т е к о н е ч н ы й р е зу л ь та т. Э т о в п о л н е о ч е в и д н о . И и м е н н о э т о п о
зволяет в н ести в ко н е ч н ы й п р о д укт зн ачи тел ь н ы е и зм е н ен и я .
д а л ь ш е *■ 541
добавьт е в улей м ет од Goß
class Hive {
// объявление констант
// объявление переменных
// I n i t i a l i z e L o c a t i o n s ()
// G e t L o c a t i o n ()
// H i v e constructor
542 г л а в а 12
о б з о р и п р е д в а р и т е л ь н ы е результаты
AddBee(random);
T o m ж е экзем пляр R an dom ,
■ к о т о р ы й передавалсям ет о д у PoQ,
п ередает ся м ет о д у AddBee[)-
К с о ж а л е н и ю , э т а м о д ел ь д а л е к а о т р е а л ь н о с т и . Ч а с т о п ч е л и н а я
м а тк а улья о казы в ается н а с то л ь ко з а н я т а , ч т о н е и м е е т в р е м е н и
н а со зд ан и е нов ы х п ч ел . У нас н е т класса QueenBee, н о п р е д п о л о
ж и м , ч т о п р и н а л и ч и и н е о б х о д и м о го ко л и ч е ств а м еда новы е п ч е
л ы п о я в л я ю т с я в 1 0 % с л у ч ае в . Э т о м о ж н о н а п и с а т ь т а к :
8 эт о м случае вы получает е
Ш м ож н осм ь сохранит ь
случайным образом ген ери руем ое
Часщо п от о м ст во и в будуи^ем
п овт орно за п у ст и т ь к а к и ю -т о
г ,аДаБаеМые кон крет н ую с и м у л я ц и ю .е с л и
Б о Іїр о С Ь і оам эт о за ч е м -т о нужно!
О
; Разумеется. После этого метод
; й а в е е будет использовать это свойство
о! Мы как раз собираемся рассмотреть
этот вопрос. Для начала нам потребуется
класс W o r l d , отслеживающий происхо
пчел, которые могут одновременно суще вместо передавамого ему параметра, Но дящее в улье, состояние всех пчел и даже
ствовать в улье. вряд ли имеет смысл так поступать. всех цветов.
u лиед-
классов
System.VW' ^ока не написан,
но все сост ав-
м Т с:“
Г “ «
Объект IVorld упрабдяеш миром
О с н о в н о й з а д а ч е й о б ъ е к т а W o r l d я в л я е т с я в ы зо в м е т о
д а G o ( ) в к а ж д о м к а д р е д л я к а ж д о г о э к з е м п л я р а Flow e r ,
В е е и Hive. Д р у г и м и с л о в а м и , о б ъ е к т W o r l d о б е с п е ч и
в а е т с у ш ;е с тв о в а н и е п ч е л и н о г о м и р а .
М е т о а ^ о ( ) й з кл ас
са W orld вызывает,
одноименный м ет од
ОЛЯ веек объектов f o r e a c h (F lo w e r f lo w e r i n F lo w e r s )
пчелиного мира. f l o w e r . G o(ra n d o m );
f o r e a c h (В ее b e e i n B e e s)
b e e . Go (rctndom) ;
О сновная
ф орм а ^epe4env>'
(# ^ V , . . , ___
[h i v e . G o(randonO
1
544 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
Каждый вызов ФО
в классе W orld приводит
я Л Ф этого ллетода для
всех объектов симулятора.
М е т о Э Go() должен
для каждой пчелы и каждого
цветка, иначе симулят ор
перест анет paSomamt^.
^ з ь м и в руку карандаш
П ри по стр о ени и си м у л я то р а б уд ет использован один из основны х
п ринципов о б ъ е ктн о -о р и е н ти р о в а н н о го п р о гр ам м и р о в а н и я — и н кап
сул яц ия. П р и в е д и те по д ва п р и м е р а и нкапсул яц и и д л я каж д ого из
созд анны х нам и классов.
Н і^ Вее Flower
1. 1. 1.
2. 2. 2.
дальш е * 545
ваш е м ест о в мире
Проблемы с инкапсуляцией!
Код для класса l/1/orld Обратите внимание на открытые
поля Hive, Bees и Flowers. Другой
К л асс W o r ld — о д и н и з сам ы х п р о с ты х в н а ш е м сим ул я то класс может случайно присвоить им
р е. В о т о тп р а в н а я т о ч к а для н а п и с а н и я кода. Е сл и п р и с м о значение null, что станет причиной
т р е т ь с я , вы у в и д и т е о т с у т с т в и е о т д е л ь н ы х ф р а г м е н т о в , к о серьезных проблем! Подумайте, ка
то р ы е мы совсем с ко р о добавим . кими свойствами и методами можно
воспользоваться для инкапсуляции.
using System.Drawing;
class World {
private const double NectarHarvestedPerNewFlower = 50.0;
private const int FieldMinX =15;
private const int FieldMinY = m ■. i KoopduHamfi,
определяющие границы
private const int FieldMaxX = 69 0
цветочного поля.
private const int FieldMaxY = 2 90
public World 0 {
Bees = new List<Bee>(); ф о р т р у я новый м ир, мы
Flowers = n e w L i s t < F l o w e r > ()
Random random = n e w R a n d o m ()
. создаем улем м добавляем
for (int i = 0 ; i < 1 0 ; i++) %0 цветков.
AddFlower(random);
}
double totalNectarHarvested = 0;
Зля „.ж Э ого
for (int i = F l o w e r s .C o u n t - 1; i >= 0 ; І-) {
Flower flower = Flowers[i]; Нужно отслеживать
flower.Go 0 ; количество нектара,
totalNectarHarvested -t-= f l o w e r . N e c t a r H a r v e s t e d ; -собранное на каждом
if (!f l o w e r . A l i v e )
этапе. Поэт ому мы
сум м ируем нект ар,
F l o w e r s .R e m o v e (flower) ; собранный с каждого
} цветка.
ц вет ы такжр
следует удалить.
546 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
^ з ь м и в руку карандаш
Решение П ри нап и сан и и с и м у л я то р а и спользовался один из основны х прин
В от т е . кот оры е пришли ципов о б ъ е ктн о -о р и е н ти р о в а н н о го п р о гр ам м и р о в ан и я, инкапсуляция.
4 м 8 голову. У вас ест ь В от прим еры и нкапсул яц и и д л я каж д о го из созд анны х н ам и классов.
други е вариант ы ?
Hive Вее Flowe_r
_ Ч а с з» °
------- ^ а Д а Б а е М ы е ------
B o rtp o ^ jjl
вы получите список из четырех пунктов,
Почему бы не воспользоваться А почему циклы f o r считать новый цветок, который будет записан
циклом foreach для удаления увяд от конца списка к началу? под индексом #3, в следующий раз будет
ших цветов и отслуживших пчел? пропускаться, так как цикл перескочит на
^ ; Цикл не должен нарушать ну индекс #4.
Q ; Потому что удалить элемент коллек мерацию. Предположим, мы начали
Начав же просмотр с конца, вы никогда
ции изнутри просматривающего ее цикла просматривать с начала список из пяти
не пропустите цветок, помещенного на
foreach невозможно. Попытка сделать цветов, и оказалось, что один из них увял. освободившееся место.
это приведет к появлению исключения. Удалив цветок с индексом, например #3,
дальш е ► 547
соединим все вм ест е
Обновите класс в е е .
Пчелы должны узнать о существовании улья и внешнего мира. Поместите в кон
структор класса Вее в качестве параметров ссылки на улей и мир и сохраните их
для дальнейшего использования.
О Обновите класс H iv e .
Пчелы должны знать, что улей существует, улей же должен знать о существо
вании внешнего мира. Поместите в конструктор класса H i v e ссылку на объект
W o r l d и сохраните ее. Обновите код создания новых пчел, передав в него
ссылки на улей и на мир.
О Обновите класс W o r ld .
Обновите класс w o r l d таким образом, чтобы при создании нового
улья ему передавалась ссылка на мир.
548 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
ча<ап°
<аДаБаеМые
Б о 1 1 |=»ос;ь1
Зачем нужно исключение в методе G e tL o c a tio n () Q ; Вне зависимости от того, отмечаем мы местоположение
класса H ive? пчелы на экране или нет, оно является ее характеристикой. Объ
екг Вее отслеживает, где именно находится каждая из пчел. При
^ ! Для обработки некорректных данных, передаваемых в ка каждом вызове метода Go () пчела перемещается на неболь
честве параметра. Внутри улья есть несколько мест, но в метод шое расстояние в направлении пункта назначения.
GetLocations () МОЖНО передать произвольную строку. Что Нам нужно отслеживать положение пчел внутри улья и на поле,
произойдет, если переданное значение отсутствует в словаре? так как иначе невозможно узнать, достигли та или иная пчела
Какой результат должен вернуть метод? пункта назначения.
В случае некорректных параметров проще всего вызвать ис Но почему для хранения информации о местополо
ключение ArgumentException. Вот как это делает метод жении используется именно структура P o in t? Разве она
GetLocation(): предназначена не для рисунков?
Обновление класса в е е
П челы д олж ны у зн а ть о с у щ е с тв о в а н и и улья и в н е ш н е го м и р а . П о
м е с т и т е в к о н с тр у кто р к л а с с а В е е в к а ч е с т в е п а р а м е тр о в ссы лки на
ул ей и м ир и с о хр а н и те их д л я д а л ь н е й ш е го и спо ль зов ания.
class Вее {
// объявление констант
// объявление переменных
p r i v a t e W o r ld w o r l d ;
p r i v a t e H iv e h i v e ;
class Hive {
p r i v a t e W o r ld w o r l d ;
Ссылка на м ир да-
первой, т ак
public H i v e (W o r ld w o r ld ) как она использи-
t h i s .w o r ld e w o r ld ; в остальной
11 к о д части Конст рик
тора.
}
public v o i d A d d B e e (R a n d o m random) {
// код создания новых пчел
Bee newBee = new Bee(beeCount, StartPoint, w o r ld , th is )
}
550 г л а в а 12
Если у вас не получается написать
)аботаюпщй код, скачайте готовую версию с сайта:
1Нр://\¥те.ЬеаёПг811аЬ8.сот/Ьоок8/ЬГс8Ьагр/
е О граничение на создание пчел
В кл а с с е Hive е с ть к о н с та н та MaximumBees, у ка зы в а ю щ а я , скол ь
ко п ч е л в с о с т о я н и и п о д д е р ж и в а т ь у л е й (у ч и т ы в а е т с я к о л и ч е с т в о
н а с е к о м ы х в н у тр и и с н а р у ж и ). Т е п е р ь , ко гд а о б ъ е к т H i v e и м е е т
д о с т у п к о б ъ е к т у Wor l d , о г р а н и ч е н и е м о ж н о у с и л и т ь . О бъект W orld позво
public v o i d G o (Random random) {
if ( w o r l d .B e e s .Cotuat < M a x i m i m B e e s пчел и
СрйОНЦИлЬ его г ААґЧґп,
&& H o n e y > M i n i m u m H o n e y F o r C r e a t i n g B e e s м а л ш о в о з м о ж т ш для
ScSc r a n d o m .N e x t {10) == 1) { ^ ^лнного улья.
AddBee(random); |
) ‘р о в н е н ш
)
М ир должен знать о создании новых пчел
Кл асс World использует п е р еч и с л е н и е List д л я сл е ж е н и я за и м е
ю щ и м и с я п ч е л а м и . У б е д и те с ь , что новы е пчелы п о п а д а ю т в сп исо к,
с о с т о я н и е к о т о р о г о о т с л е ж и в а е т с я о б ъ е к т о м W orl d .
О Обновление класса w o r ld .
О б н о в и т е к л а с с w o r l d т а к и м о б р а з о м , чтоб ы при с о з
д а н и и н о в о го у л ь я е м у п е р е д а в а л а с ь с с ы л к а н а м и р .
public World 0 {
Bees = new List<Bee>(); Здесь передается ссылка
Flowers = new L i s t < F l o w e r > () на объект H iv e.
H iv e a n ew H i v e ( t h i s ) ;
Random random = n e w R a n d o m ();
for (int i = 0 ; i < 1 0 ; i++)
AddFlower(random);
Победение пчел
у нас отсутствует м е т о д G o () объе к т а Вее. М ы начали
писать код для н е к о т о р ы х состояний, н о м н о г о е оста
лос ь н е з а к о н ч е н ы м (Idle, F l y i n g T o F l o w e r и ч а с т и ч н о
MakingHoney).
552 г л а в а 12
обзор и п редварит ельны е результ ат ы
\
П оэт ом у в словаре location
i
case B e e s t a t e .R e t u r n i n g T o H i v e :
if (!I n s i d e H i v e ) {
if ( M o v e T o w a r d s L o c a t io n ( h iv e .G e t L o c a t io n ( " E n t r a n c e " ) ) ) {
I n s id e H iv e = tr u e ; в случае проникновения
l o c a t i o n = h iv e .G e t L o c a tio n ( " E x it" ) в улей обновите п о л о
} жение и ст а т у с п е р е
менной insideHtSl'e.
}
else
if ( M o v e T o w a r d s L o c a t i o n ( h i v e .G e t L o c a t i o n ( " H o n e y F a c t o r y " ) ) )
C u r r e n tsta te = B e e S t a t e .M a k i n g H o n e y ;
break; £сли вы находитесь R
case BeeState.MakingHoney: ^ ‘^ '^ Р а в л я й т е с ь н Г ^
if {NectarCollected < 0 . 5 ) {
NectarCollected = 0;
Currentstate = Beestate.Idle;
}
else
if (h iv e .A d d H o n e y ( 0 . 5 ) ) ва т ь эт о т нектир'
N e c t a r C o lle c t e d -= 0 . 5 ; изводст ва меда...
e ls e
...заберите его у пчелы,
N e c ta r C o lle c te d = 0;
break;
case Beestate.Retired: вели У'^^‘^ ,^ Т ^ ;1 ;е Т Т н 1 е н и е fa
A d dH oneyO верн ^ н<
// D o nothing! We're retired!
break;
h
У
Отработавшая пчела
должна ждать, пока о б ь
ект Hive не удалит ее из ШТУРМ
перечисления. После этого Каким образом следует отредактировать симулятор,
она больна делать, что
хочет! чтобы пчела трати л а два кад ра на д остиж ение цветка,
а потом е щ е д ва кадра на в озвращ ение в улей? Какие
методы каких классов следует изменить, чтобы
получить та ко е поведение?
дальш е ► 553
продолж аем создават ь наш м ир
Оснобная форма
К а к вы з н а е т е п р и к а ж д о м в ы зо в е м е т о д а О о ( ) в се о б ъ е к т ы
н а ш е г о м и р а с л е г к а м е н я ю т с я . Н о к а к в ы зв а ть э т о т м е то д ?
Р а з у м е е т с я , п р и п о м о ш ;и ф о р м ы ! П р и ш л о в р е м я з а н я т ь с я е е
п о стро ени ем .
М е т к и 6 п р а во м ст олб це будит
Д об авьте ф орм у к п р о е кту и п р и д а й те ей п о ка за н н ы й н и ж е от ображ ат ь с т а т и с т и к у .
вид. О н а с о д е р ж и т н ов ы е эл ем е н ты у п р а в л е н и я , н а з н а ч е н и е П рисвойт е и м им ена B e e s ,'
Flowers, H oney InHiVe и т . п.
к о то р ы х будет р а с с м о тр е н о н а сл ед ую щ и х с тр а н и ц а х .
Ч/
Каждая из э т и х м е т о к
Э л е м е н т T o o lS trip п о Forml.cs [Design] ПX за н и м а е т одну ячей -
м е щ а е т в ве р хн ю ю ку э л е м е н т а у п р а вл ен и я
ч а с т ь ф о р м ы п а н ель T ableLayoutP anel. Вы з а
инст рум ент ов. В ос ^ ieehwe Simulator f “s п о лн я е т е ее, как обычную
пользуйт есь раскры т а б ли ц у в M icrosoft W ord.
Start Simulation Reset
ва ю щ и м ся с п и ско м Щ е л к н и т е на м ален ькой
. # Bees —-j gggj —
элем ент а в конст рук
:^A.itavW9l&.....Howws . черной с т р е л к е , чт обы
т о р е и добавьт е две Tsstal honey n the twe | Hon^hHve " добавит ь, у д а л и т ь или
кнопки. П р исвойт е па Tdirf'riedarjiJhgS^_| NeaBrtiftewera_^~ п о м е н я т ь р а зм е р ст олбца
р а м е т р у P isp la yS tyle Frames rm ....... *__ "I FranesRun
fiame rate ........ f Frametoe и ли ст роки.
кнопок значение T e x t
Simuletion paused
Э лем ент S ta tu sS trip
добавляет в н м ж - ^
нюю част ь ф орм ы Добавьте элемент
ст р о к у состояния. EooiStripl taistatusStripl © timerl у п р а в л ен и я T im er. Он
В оспользуйт есь р а с
не я вля ет с я ви зуа льн ы м
крывающ имся с п и поэт ом у п о я вля ет ся
ском эт о го эл е м е н снизу от формы.
т а , чтобы добавит ь
также StatusL abel. Э ле м е н т у п р а вл ен и я T o o lS trip добавляет п а н ель
и н с т р у м е н т о в в вер х н ю ю ч а ст ь ф о р м ы , а э л е
м е н т S ta tu s S tr ip — с т р о к у сост ояния в ниж нюю.
Но кр о м е э т о го , они п о я вля ю т ся в виде значков
под ф о р м о й , чт обы д а т ь ва м д о с т у п к р е д а к т и
р о ва ни ю своих свойст в.
[ f o r e a c h (F lo w er f lo w e r i n F lo w e rs)
flo w e r.G o (ra n d o m );
1%
'®pewe«v>
h i v e . Go (random ) ;
System.Wv
554 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
Получение с та ти сти ки
О т р е д а к т и р у е м д о б а в л е н н ы е э л е м е н т ы у п р а в л е н и я . М ы н е будем
п о о ч е р е д и в ы д ел я ть к а ж д ы й и з н и х , а в о с п о л ь зу е м с я м е т о д о м ,
о б н о в л я ю щ и м с та т и с ти к у сим улятора:
Мы поговорим об
эт ом подробнее
С о тв орени е М ира в разделе,
посвященном
Здесь вы видите т от Вы п р ав ы , нам нуж но со зд ать о б ъ е к т W o rld . Д обавьте создании}
ж е самый мет од в ко н стр у к то р ф о р м ы следующую строчку: объекта
S tring .Form a tQ J к о Tim eSpan.
т орый использовали p u b l i c F o r m l () {
в ш ест надцат ирич- In itia liz e C o m p o n e n tO ;
н о м дампе. Но вмест о , „
вывода информации W o r ld ( ) ;
в xZ, вы указываете f3 >
и отображаете число „ , ,
с т ремя десятичными Д о бавьте к ф о р м е за к р ы т о е п о л е w o r ld .
знакам и . касается кода, с в я за н н о го со врем ен ем ... нам
ж е т р е б о в а л с я сп о со б м н о го к р ат н о го в ы зо в а м етод а
Go О в классе W o rld ... зн ач и т, нам т р е б о в а л с я тай м ер .
дальш е * 555
Создайте новый проект , чтобы
повт орное воспроизведение
посм от рет ь, как именно р а
бот аю т таймеры. З я т е м мы
вернемся к сим улят ору и п р и
Таймеры меним полученные знания на
практике.
Т а к о й п о л езн ы й о б ъ е кт к а к т а й м е р п о зв о л я е т запускать од но
и т о ж е с о б ы т и е с н о в а и с н о в а , т ы с я ч у р а з в секунд у. ^^Упр^нение!
Создайте новый проект
О т к р о й т е V is u a l S tu d io и с о з д а й т е п р о е к т с ф о р м о й . П е р е т а щ и т е н а
н е е та й м е р и т р и к н о п к и . Щ е л к н и т е н а т а й м е р е и п р и с в о й те свойству
In t e r v a l з н а ч е н и е 1 0 0 0 . И з м е р е н и я в е д у тс я в м и л л и с е к у н д а х , с о о тв ет^
с т в е н н о , с о б ы т и е б у д е т з а п у с к а т ь с я о д и н р а з в секунд у.
в“ « » ! " ::
О Код для события T i c k и кнопок
В о т код, ко то р ы й п о зв о л и т п о н я ть п р и н ц и п раб оты тай м ер а:
на консоль
556 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
К а к и м о б р а з о м т а й м е р у з н а е т , ч т о д е л а т ь в к а ж д о е м гн о в е н и е ?
П о ч е м у м ето д t i m e r l _ T i c k () запускается п р и к а ж д о м о тс ч е те
тайм ера? Т у т мы возвращ аем ся к с о б ы т и я м и д е л е га та м , с к о а сД еной
т о р ы м и вы п о з н а к о м и л и с ь в п р е д ы д у щ е й гл ав е. В о с п о л ь з у й т е с ь
ф у н к ц и е й G o Т о D e f in it io n , ч т о б ы в с п о м н и т ь п р и н ц и п р а б о т ы В предыдущем прим ере
использотлся ст а н
д е л е га т а E v e n t H a n d l e r :
дартный обработчик
событий.
T r
Один из делегатов класса
К
напысаннш
Э „о T ic k э л е м е н т « System : базовый обработчик Это t i^ w e r 3 - Т .с к О -
событии. Эт о делегат ° именно
а указат ель на один или Д е л е гя » ^
-------------------
несколько -
методов. на него.
1 Л -9 П
558 г л а в а 12
обзор и п редварит ельны е результ ат ы
■шаtcfOlStripl lisstatusStripi
S tm y i^o n paused
560 г л а в а 12
обзор и п редварит ельны е результ ат ы
Тестирование
# Bees 6
# Rowers 10
Total h o n ^ n the hive 3.200 J
ра5о-
Должны
кнопки Total nectar in the fleld 15.СМЮ
запуска си м ул я Frames run 0
т о р а , пост ановки N/A
на п а узу и п е р е
Frarrre rate
загрузки-
■
Simulation paused я »
i \
дальш е ► 561
реш ение упраж нения
решение
Эт о было добавлено в класс Вее.
Мы воспользовались
f вызовом для
class Вее {
// весь уже существующий код
p u b lic B eeM essage M e ssa g e S e n d er ;
if ( o l d S t a t e != C u r r e n t s t a t e
&& M e s s a g e S e n d e r != n u l l )
M e ssa g e S e n d e r (ID , C u r r e n t S t a t e .T o S t r in g ( ) ) ;
При изменении ст ат уса пчелы
} вызывается м ет од BeeMessaqe
на который указывает делегат.
Изменения,,
внесенные в об ъ ек т у Hive также
Класс Hive. т ребует ся делегат, ^
чтобы передать каждой
class Hive { пчеле метод, который
С // весь уже существующей код будет вызываться после
- создания этой пчелы
p u b lic B eeM essage M e ssa g e S e n d er ; мет одом Ас1<1Вее().
562 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
p u b lic d e l e g a t e v o id B e e M e s s a g e ( in t ID , s t r i n g M e ssa g e );
class World {
// весь существующий код
и , наконец, нужно
обновить ф орм у.
sT Новый делегат создается из
public partial class Forml Form { класса Вее (убедитесь, что
// объявление переменных мет од BeeMessage является
от кры т ы м ) и указывает на
public FormlО { мет од SendMessageQ.
InitializeComponentO ;
w o r l d = n ew W o r ld ( n e w B e e M e s s a g e ( S e n d M e s s a g e ) ) ; 3
// остальная часть конструктора формы
p r iv a t e v o id S e n d M e s s a g e (in t ID , s t r i n g M essa g e) {
s t a t u s S t r i p l . I t e m s [ 0 ] . T e x t = " B e e #" + ID + " + M essa g e;
} f Э т о м ет о д . K o m o p f M b i e ^ M
} -------дали... не забудьте вписать его.
дальш е ► 563
сгруппируем пчел
Ф орм а обновляет с т а
т и с т и ч е с к и е данны е и »У Beehive Simulator
о т о б р а ж а е т со о б щ ен и я,
кот оры е пчелы п о сы ла Pause simulation Reset
ю т как о т ч ет о п р о д е #Bees 6 Добавим элемент ListBox для
ланной работ е. # Rowers 11 отображения дополнительной
Total hofwy in the hive 1.Ш информации о пчелах.
С Told nectar in the Add 34.330
frames Rjn
Frame rate 16(K.5m s)
kte: 1 bee
FlyingToRower: 2tjees
GatheringNectar: 1 bee
ReturningToHive: 2 bees
U одна ни ч е го не д е л а е т .
ШТУРМ
Вы зн аете достаточно д ля вставки в ф орм у эл ем ен та ListBox. П одум айте, как
им енно он ф ункционирует. Э то слож нее, чем каж ется на первый взгляд. Что
нужно сделать, чтобы определить количество пчел в каж дом из состояний Вее
s ta te ?
564 г л а в а 12
обзор и п р ед варит ельны е результ ат ы
®Pe4eHV»
П о л я о б ъ е к та В ее с о д ер ж ат м н о ж еств о д ан н ы х. К о л л екц и ю о б ъ е к то в
можно п р е д с та в и ть в виде с т р о к б азы д ан н ы х. К аж д ы й о б ъ е к т со д ер
ж и т д ан н ы е в п о л ях , т а к ж е как каж дая с т р о к а б азы со д е р ж и т д ан н ы е
в столбцах.
База данных
^ а б л ц ц а с названием
есть Большинство коллекций,
Вее со ст о лб ц а м и ID
« c u rre n tsta te. особенно содержащих
объекты, можно предста
вить как своего рода базу
данных.
дальш е * 565
вст роенны й язы к запросов
З а п р о с ы L IN Q оам накобо
З о т а ю т с коллекциям и
и с базами данных-
var beeGroups =
from bee in world.Bees
group bee by bee.Currentstate
into beeGroup 1 currentstate = MakingHoney L
ID = 12 1 currentstate = FIvinaToFlower B F
orderby beeGroup.Key 1 1 ID = 1982 j currentstate = GatheringNectar [,
select beeGroup; База данных
Е сл и бы данные о пчелах находились
в базе (или в файле X M L)? UN Q
работал бы с ними т ем же способом.
дальш е ► 567
сохраняем мир
568 глава 12
реш ение упраж нения
|^ешение using S y s t e m . R u n t i m e .S e r i a l i z a t i o n .F o r m a t t e r s .B i n a r y ;
570 г л а в а 12
о б з о р и п р е д в а р и т е л ь н ы е результаты
2. Кнопка Open десериализует мир из файла. Таймер ведет себя так же, как при щелчке на кнопке Save. Долж
но открываться окно диалога Open и происходить десериализация указанного пользователем файла. После чего
можно снова связать делегат M e s s a g e S e n d e r с формой и при необходимости перезапустить таймер.
3. Не забудьте про обработку исключений! Убедитесь, что проблемы с чтением и записью в файл не влияют
на состояние мира. Создайте всплывающие окна с сообщениями об ошибке.
\сод
p r i v a t e v o i d o p e n T o o l S t r i p B u t t o n _ C l i c k ( o b j e c t s e n d e r , E v e n tA r g s e ) {
W o r ld c u r r e n t W o r l d = w o r l d ; Перед т ем как о т кры т ь и п роч и т ат ь
i n t c u rre n tF ra m e s R u n = fram e sR u n ; ф айл, сделаем копию т екущ его состояния
м и ра и п а р а м ет р а fram esRun. В случае
проблем м и р будет восст ановлен из эт ой
b o o l e n a b le d = t i m e r l . E n a b le d ;
копии.
i f (e n a b le d )
t i m e r l . S to p ();
O p e n F i l e D i a l o g o p e n D i a l o g = new O p e n F i l e D i a l o g ( ) ;
o p e n D ia lo g . F i l t e r = " S im u la to r F i l e ( * . b e e s ) |* .b e e s " ;
o p e n D ia lo g . C h e c k P a th E x is ts = t r u e ; Hacm pouKu
o p e n D ia lo g .C h e c k F ile E x is ts = t r u e ; u вызов окна
o p e n D i a l o g .T i t l e = "C h o o se a f i l e w ith a s i m u l a t i o n t o диалога
O pen File.
i f ( o p e n D i a l o g . S h o w D i a l o g () == D i a l o g R e s u l t . OK) {
try{
B i n a r y F o r m a t t e r b f = new B i n a r y F o r m a t t e r ( ) ;
O n c p a m o p ___ m u s i n g ( S t r e a m i n p u t = F i l e . O p e n R e a d ( o p e n D i a l o g . F ile N a m e ) )
2A f —
panm u p ye-m w o r l d = (W o rld ) b f . D e s e r i a l i z e ( i n p u t ) ; Здесь происходит
fra m e sR u n = ( i n t ) b f . D e s e r i a l i z e ( in p u t) ; десериализация м ира
mOKa. J и количест ва кадров.
c a tc h (E x c e p tio n ex) {
M e s s a g e B o x .S h o w ( " U n a b le t o r e a d t h e s i m u l a t o r f i l e \ r \ n " + e x .M e s s a g e ,
"B e e S i m u l a t o r E r r o r " , M e s s a g e B o x B u t t o n s . OK, M e s s a g e B o x I c o n . E r r o r ) ;
w o r l d = c u r r e n t W o r l d ; «as---------
При появлении исключения мы
f r a m e s R u n = c u r r e n t F r a m e s R u n ;<
восст анавливаем последню ю
} сохраненную версию м ира
и п а р а м ет р а fram esRun.
w o r l d . H i v e . M e s s a g e S e n d e r = new B e e M e s s a g e ( S e n d M e s s a g e ) ;
f o r e a c h (B ee b e e i n w o r l d . B e e s )
b e e . M e s s a g e S e n d e r = new B e e M e s s a g e ( S e n d M e s s a g e ) ;
if (e n a b le d )
После загрузки мы
tim e rl.S ta rt 0 ; ^ ~ подсоединяем Э е л е г о т u
перезап ускаем т аймер-
дальш е у 571
13 э л е м е н т ы у п р а в л ен и я и Г р а'^и Ч есК и е ‘^’р а Ш е н ш ы
Ф ^1^
Ф Наводим красоту ^
И н о г д а у п р а в л е н и е г р а ф и к о й п р и х о д и т с я брать в с в о и р у к и .
До этого момента визуальный аспект наших приложений был отдан на от
куп элементам управления. Но иногда этого недостаточно, например, если
вы хотите анимировать изображение. Вам предстоит научиться создавать
свои элементы управления для .NET, применять двойную буф еризацию
и даже рисовать непосредственно на формах.
везде о бъ ект ы
ся в окне toolboK
574 г л а в а 13
элем ент ы управление и гр аф ические ф рагм ент ы
Трем
ф о р м е ^ roo>^6evv\"
t o n t r o is
1ЛЛЙ Lahel-
^ B eeteveS m uiebr
HetumrigToHive: 2bees
Beehive Simuiator
576 г л а в а 13
элем ент ы управление и граф ические ф рагм ент ы
О О кно Hive
П ч е л ы л е т а ю т п о в се м у п р о с т р а н с т в у с и м у л я т о р а . К о г д а о н и
залетаю т в улей, п р о и схо д я щ ее о то б р а ж а ется в это м о кн е.
The Hive
\
Эт о вход в улей. /До
Сборнектара происходит в окне Field стигнув этой точки,
О сн о в н ы м за н я ти е м пчел является сбор н е кта р а , ко то р ы й пчелы исчезают с поля
б у д е т п е р е р а б о т а н в м ед. П о е д а н и е э т о г о м е д а д а е т п ч е л а м и появляются у выхода
э н е р ги ю для сб о р а н о в о го н е кт а р а . в окне hive.
дальш е у 577
зам ечат ельное предст авление
Эти обш кт ы у ж е п о ст р о ен ы .
Визуализатор
Н а м тр е б у ется класс, к о т о р ы й н а о сн о в е и н ф о р м а О бъект W orld отслеживает все. ч т о ^
ц и и и з н а ш е г о м и р а б у д е т р и с о в а т ь в д в ух н о в ы х состояние
ф о р м а х улей п ч е л и ц веты . С э то й зад ач ей о тл и ч н о улья, каждой пчелы и каждого цветка
с п р а в и т с я кл а с с Я е п д е г е г . Б л а го д а р я и н к а п с у л я
ц и и у ж е и м е ю щ и х с я классов нам н е п о тр еб уе тся
сил ьн о м е н я ть код.
Эт от объект задает
главное окно. Вы его
уже построили.
Каждая пчела зн а-
ет свое положение
о прост ранст ве,
м ы же можем
его использовать,
'^'^обы нарисовать
об ъ ект ы Hive пчелу в форме.
и Field явля
ют ся ф орм а
м и, связанны^
ми с основной
Благодаря инкап
формой. Визуализатор бе-
рет информацию
суляции классов
от объекта W orld
и обновляет две до
Вее, Hive, Flower
черние формы.
и World добавле
ние класса, визу
ализирующего эти
в и -з у -а -л и -з и -р о -в а т ь , г л а г .
представлять или изображать. объекты, не тре
Учитель попросил визуалижровать бует значительного
линии модели на листе бумаги. редактирования
кода.
578 г л а в а 13
элем ент ы управ ление и гр аф ические ф рагм ент ы
•состояние
объектов
о б ъ е к т W orld инкапсулирован, поэт ом у
объект R en d erer использует для получе
ния информации свойства этого объекта
и всех, объектов, которые с ним связаны.
дальш е ► 579
управляем граф ическим и ф рагм ент ам и
Controls.Add(new BeeControl()
О бъе^
О П р и п е р е м е щ е н и и п ч е л ы и з улья н а п о л е э л е м е н т у д а л я е тс я и з
к о л л е к ц и и C o n t r o l s улья и д о б ав л яется в о д н о и м е н н у ю ко л л е к
цию ф ормы F ie ld .
Controls.Remove(theBee
580 г л а в а 13
Q
элем ент ы управление и граф ические ф рагм ент ы
^ з ь м и в руку карандаш_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
О пределите назначение каждого из ф рагм ентов кода. В се эти
ф рагм енты принадлеж ат форме.
}
C o n t r o l s .Remove (controlToRemove) ; Что получит ся, если т ак сделат ь, вы мож ете
c o n tr o lT o Remove.Dispose О ; п о см о т р ет ь сами. П опыт айт есь понят ь, поч е
м у получает ся именно т акой р е зу л ь т а т !
дальш е ► 581
\шг \же \щг
/ш\ Уш\ / П Уш\щшш
582 г л а в а 13
элем ент ы управ ление и гр аф ические ф рагм ент ы
P o in te r i
i
1 B u tto n
ki C h ec k B o x
H C h ec k ed L is tB o x w
я в л ,е т „ B u C o n tro l
дальш е ► 583
пользоват ельские элем ент ы управления
Bee animation l.png Bee animation 2.png Bee animation 3.png Вее animation 4.png
И м п о р т и р у й т е эт и изобра
жения и д о Ш ь т е их к р е с у р
584 г л а в а 13 с ам своего проекта.
элем ент ы управление и граф ические ф рагм ент ы
pictureBoxl.Image =
Properties.Resources.Bee_animation_l;
I
Здесь указывается изображение,
связанное с элемент ом PictureBox
(в данном случае наиле начальное
изображение).
Нужно добавить с т т ч -
^ y / j s i n g System. ^
Эти изображения M n dow s-Form s», т ак
хранят ся в виде как б проект е при -
с Ы с т в а обще ^ ^ с т в у ю т элементы
го дост упа класса PictureBox и Tim er.
P ro p e rtie s . Resources.
Добавим элемент BeeC ontrol!
Здесь т аймеру
class BeeControl : PictureBox { присваивают -
ся начальные
private Timer a n im a tio n T im e r = new Timer() значения п у
public BeeControl() { т ем создания
убедит есь, animationTimer.Tick += new EventHandler(animationTimer_Tick) его экземпляра,
что в верхней задания свой -
animationTimer.Interval = 150; ства Interval
части клас
animationTimer.Start 0; и добавления
са находит
ся ст рочка BackColor = System.Drawing.Color.Transparent; обработчика
«using System. BackgroundlmageLayout = ImageLayout.Stretch; событий.
Windows. J
fo rm s» .
private int cell = 0; После возвращения
void animationTimer_Tiok (object sender, EventArgs е) { к кадру # 1 , значение
парам ет ра cell с т а -
cell++; ^ новится равным О.
Каждый от счет switch (cell) {
т аймера вы Backgroundlmage Properties.Resources.Bee_animation_l; break;
зывает событие, Properties.Resources.Bee_aniraation_2; break;
Backgroundlmage
оно увеличивает
значение п е р е Backgroundlmage Properties.Resources.Bee_animation_3; break;
менной cell на Backgroundlmage Properties.Resources.Bee_animation_4; break;
1 , и при п о м о Backgroundlmage Properties.Resources.Bee_aniraation_3; break;
щи операт ора default: Backgroundlmage = Properties.Resources.Bee_animation_2;
sw itch назначает^
cell = 0; break;
карт инку свой введя код элемент а управления, перест ройт е програм м у
ст ву Image. }
для отображения изменений в конст рукт оре.
П острой те программу. Э л е м е н т у п р а в л е н и я B e e C o n t r o l д о л ж е н п о я в и т ь с я в о к н е
to o lb o x . П е р е т а щ и т е е г о н а ф о р м у , и вы п о л у ч и т е анимированную п ч е л у !
удален ие элем ент ов управления
586 глава 13
элем ент ы управ ление и гр аф ические ф рагм ент ы
И С Р в в е д е т с т р о ч к у b a s e . D i s p o s e ( ) , к о т о р о й б у д е т в ы зы в а ть с я д а н н ы й м е то д :
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
}
Любой соз
Код удаления тайм ер а данный вами
Д о б ав ьте в к о н е ц н о в о го м е то д а D is p o s e () ко д , в ы зы в аю щ ий м е
то д a n im a tio n T im e r .D is p o s e О , ко гд а а р гу м е н т d i s p o s i n g и м е элемент управ
е т з н а ч е н и е tr u e .
protected override void Dispose(bool disposing) {
ления должен
base .Dispose (disposing) ;
if
^
( d is p o s in g ) {
.
Мы перекрываем n o-
меченный ключевым
удалять все
словом, protected метод
a n im a t io n T im e r .D is p o s e О ;
DisposeQ, вызываемый
реализаиией метода
порожденные
IDisposable.DisposeQ эле
}
мента управления. им элементы
Т еп ер ь эл ем ен т B e e C o n tro l у б и р а е т за собой!
и другие допу-
П р о в е р ь т е с а м и . Добавьте точку останова в в с т а в л е н н у ю с т р о ч к у
и з а п у с т и т е п р о гр а м м у . П р и к а ж д о м у д а л е н и и э л е м е н т а B e e C o n t r o l скаюпще уда
и з к о л л е к ц и и C o n t r o l s б у д е т в ы зы в а ть с я м е т о д D i s p o s e ( ) .
ление объекты.
Если вы собираетесь создавать свои элементы управления, вам будет полезно
прочитать материалы с сайта ЬЦр://т5с1п.т1сг080Д.С0т/ги-ги/11Ьгагу/8У81ет.1а1вР05аЫе.а15Р0$е.а50х
Класс UserControl
Есть и более простой способ создания элементов управления для окна
toolbox. Вместо того чтобы пользоваться классом, наследующим от су
ществующего элем ента управления, вам нужно добавить в свой проект ^
кл асс U s e r C o n t r o l. Вы работаете с ним, как с ф орм ой, перетаскивая н а 'чк'тт тт
него элементы из окна toolbox. И точ н о так же, как в случае с ф орм ой вы ] 0 U 0,
пользуетесь собы тиями. П оэтому заново создадим элем ент B e e C o n t r o l , V/ m
но уже с помощью класса U s e r C o n t r o l . ^
О О ткройте новы й проект W indows Forms A pplication и добавьте к его ресурсам ч еты ре и зо
браж ения. П еретащ ите на форму кнопку и введите известны й вам код, добавляю щ ий и уда
ляю щ ий элемент B e e C o n t r o l .
© Щ елкните правой кнопкой мыши на имени проекта в окне Solution E xplorer и вы берите
команду A dd » U ser C ontrol... Д об авьте эл е м ен т B e e C o n tr o l. О н будет откры т в кон
структоре форм.
Используйте метод animationTimerJTickQ и поле cel! field
^— из старой версии элемента управления.
А
^
^
П еретащ ите на ваш элем ент управления элемент Timer. Н азовите его a n im a tio n T im e r ,
св о й ств у I n t e r v a l п р и с в о й т е зн а ч е н и е 150, св о й ств у E n a b le d - зн а ч е н и е tru e . Дваж
ды щ елкните на тайм ере, чтобы добавить обработчик собы тия Tick. И спользуйте для него
то т же код, что и раньше.
О З ап у сти те програм м у, кнопка долж на работать так же, как и раньш е, просто теп ерь ею
управляет класс U s e r C o n t r o l . И м енно он отвечает за появление и удаление элемента
BeeControl.
-------^
I С кол
ко л ько я н и пользовалась
Z' эл ем е н та м и уп р а вл е ни я, м н е
элементам
ни разу не п р и х о д и л о с ь и х удалять!
О
Зачем это вообщ е делать?
-- —
Ч
Вам не тр еб о в ал о сь уд ал ять элем енты упр ав л ени я, та к как
эту раб оту вы пол няла ф орм а.
Впрочем, не верьте нам н а слово. Воспользуйтесь функцией по
иска и найдите в коде слово Dispose, вы обнаружите, что ИСР
добавила метод в ф айл F o r m l .D e s i g n e r .c s для перекры ти я
метода D i s p o s e {) и вы зова своего собственного метода b a s e .
D i s p o s e О . П ри удалении ф орм ы а в т о м а т и ч е с к и у д а л я е т с я
в с я с в я з а н н а я с н е й к о л л е к ц и я C o n t r o l s . Н о если вы вручную
удаляете элементы управления с ф орм ы или создаете новы е эк
зем пляры , не принадлеж ащ ие коллекции C o n t r o l s , высвобож
дать ресурсы предстоит уже вам.
^аД аБаеМ ы е
Ч асто Б о Т І р о С ь і
и O p e n F i l e D i a l o g . Их значки появля
ются под формой. Редактируя свойства элемента
Почему код формы элементов
OpenFileDialog, Я заметил сообще
BeeControl, один из которых получен
Проверьте сами. Создайте пустой класс, ние об ошибке: «Вы должны перестро
из элемента PictureBox, а другой на
наследующий от класса P i c t u r e B o x . ить проект, чтобы изменения отобрази
базе класса UserControl, работает
Перестройте проект и дважды щелкните лись в любом открытом конструкторе».
совершенно одинаково?
на его названии в окне Solution Explorer Почему оно появилось?
После двойного щелчка на строчке Перетащите из окна toolbox элемент Помните, как двигались крылья пчелы на
OldBeeControl в окне Solution O p e n F i l e D i a l o g . Он появится в элементе B e e C o n t r o l , даже в про
Explorer появилось сообщение о до виде значка. Вы можете выделите его цессе перетаскивания его из окна Toolbox?
бавлении в мой класс компонентов. и изменить его свойства. Проделайте Программа еще не была запущена, но напи
Что это значит? эту операцию, а затем вернитесь к коду санный вами код уже выполнялся. Таймер
класса. Посмотрите на конструктор, там запускал событие T i c k , а его обработчик
вы найдете код создания экземпляра менял картинку. Подобное поведение
Q ; Когда вы создаете элемент управле
O p e n F i l e D i a l o g И задания его возможно только после компиляции кода,
ния, добавляя к проекту класс, наследу
свойств. когда программа работает в памяти. Именно
ющий от элемента P i c t u r e B o x , ИСР
поэтому вам напомнили о необходимости
позволяет вам работать с компонентами.
регулярно обновлять код, чтобы элементы
К ним относятся невизуальные эле
управления отображались корректно.
менты управления, например, T i m e r
590 глава 13
элементы управление и графические фрагменты
воздействует как на
обе формы, т ак и на все объекты
построенные вами в предыдущей '
глабе для симулятора.
О бъект^
М ет од BringToFrontO га р а н т и р уе т .
J.H , ^гррда бидцт появляться
Помните, что в качестве ключа словари м огут исполь . «А»
зовать что угодно? В данном случае ключом выступает
объект Вее. Визуализатору нужно знать, какой элемент ф е р т Him э т о т “ 7
BeeControl на форме соот вет ст вует той или иной пче бает появление пчел поверх фона.
ле. Поэтому он находит пчелу в словаре, определяет ее
элемент управления и таким образом получает возмож
ность перемещать ее по форме.
Поменяйте
размеры
форм, как
показано
на скрин
шотах.
присвойте значение
Мы скоро запустим форму. В этот момент вам нужно будет щелкнугь на изображ ении
выхода, и обработчик собы тий покаж ет точную координату этого места.
Добавьте аналогичны й обработчик к ф орм е Field. Щ елчком получите коорди-
наты вьгхода, питом ника и ф абрики. Эти координаты позволят обновлять метод
I n i t i a l i z e L o c a t i o n s {), написанны й в предьщущей главе для класса Hive:
З а п уст и в
Симу л я - Fp r i v a t e v o i d I n i t i a l i z e L o c a t i o n s О
т о р , вы ^
смож ете l o c a t i ,o n s = n e w D i c t i o n a r y^ < ws I-#
t ir i-Lxx^
n g ,f IPt ^oXi Xn Lt>> ( ) ;
-- и ____________
locations.Add("Entrance'', n e w Point (626, j a o ) ) •
І у н ч и в , уберите обра
заполнит ь ботчик щелчка м ьт ки
locations. A d d ("Nursery", n e w P o i n t d T T T l e z E ^ '-g
коллекцию l ocations.Add("HoneyFactory", n e w P o i n t T l 57 , 78))• .он m oZo -
координат locations. A d d ("Exit", n e w P o i n t U V S , 180));} нат коорди -
внут ри ваших объектов.
улья.
Э т о наш вари ант координат , для ваших
ф о р м координат ы м о г у т от личат ься.
элементы управление и графические фрагменты
p u b lic v o id Reset 0 {
fo re a c h (P ic tu re B o x flo w e r in flo w e rL o o k u p .V a lu e s ) {
fie ld F o rm .C o n tro ls .R e m o v e (flo w e r);
flo w e r.D is p o s e ();
}
fo re a c h (B e e C o n tro l bee in b e e L o o k u p .V a lu e s ) { При перезагрузке симулятора для
h iv e F o rm .C o n tro ls .R e m o v e (b e e ); каждой формы вызывается метод
fie ld F o rm .C o n tro ls .R e m o v e (b e e ); Controk.RemoveQ, удаляющий все
b e e .D is p o s e 0 ;
элементы управления. Он находит
каждый элемент в словаре и вызы
} вает для него.метод PisposeQ. По
flo w e rL o o k u p . C le a r (); сле чего очищает и словари также.
b e e L o o k u p .C le a r 0 ;
594 глава 13
элементы управление и графические фрагменты
}
e ls e
b e e C o n tro l = b e e L o o k u p [b e e ]; М ет од MoveBeeFromHiveToFieldQ
б ерет указанный эл ем ен т BeeControl
re tu rn b e e C o n tro l;
из коллекции Controls улья и добавля
} ет его в коллекцию Controls поля.
Г '
p riv a te v o id MoveBeeFromHiveToField ( B e e C o n t r o l b e e C o n t r o l )
h iv e F o rm .C o n tro ls .R e m o v e (b e e C o n tro l);
b e e C o n t r o l. S iz e = new S iz e (2 0 , 20
Пчелм на поле им ею т меньший размер,
fie ld F o rm .C o n tro ls .A d d (b e e C o n tro l); чем пчелы в улье, соответственно,
b e e C o n tro l.B r in g T o F ro n t( ) ; метод должен менять свойство Size
элемента BeeControl.
p riv a te v o id MoveBeeFromFieldToHive( B e e C o n t r o l b e e C o n t r o l ) {
fie ld F o rm .C o n tro ls .R e m o v e (b e e C o n tro l); Метод MoveBeeFromFieldToHiveO
b e e C o n tro l.S iz e = new S iz e ( 4 0 , 4 0 ); возвращает элемент управления
h iv e F o rm .C o n tro ls .A d d (b e e C o n tro l); BeeControl обратно в улей. Соот
b e e C o n tro l.B r in g T o F ro n t( );
ветственно, он должен увеличить
его размер.
}
К ак только методы VrawBees()
и PrawFlowersO обнаруживают,
p riv a te v o id RemoveRetiredBeesAndDeadFlowers() что цветка или пчелы больше
нет, они добавляют эти объекты
fo re a c h (B e e b e e in re tire d B e e s )
в списки deadFlowers и retiredBees,
b e e L o o k u p .R e m o v e (b e e ); чтобы в конце кадра произошло их
re tire d B e e s .C le a r (); удаление.
f o r e a c h (F lo w e r f l o w e r in d e a d F lo w e rs )
flo w e rL o o k u p .R e m o v e (flo w e r);
d e a d F lo w e rs . C le a r ( ) ; Напоследок визуализатор вызывает м е
тод для удаления из словарей всех увядших
] цветов и прекративших работу пчел.
p riv a te v o id re s e t_ C lic k ( o b je c t s e n d e r, E v e n tA rg s e)
r e n d e r e r .R e s e t 0 ;
Эля
R e s e tS im u la to r ( );
i f ( ! t i m e r l . E n a b le d )
t o o lS t r ip l. Ite m s [ 0 ] .T e x t = "S ta rt s im u la tio n " ;
}
p riv a te v o id o p e n T o o lS trip B u tto n _ C lic k (o b je c t s e n d e r, E v e n tA rg s e) {
/ / О статок кода для кнопки остается без изм енений.
Тестирование... М М . . .
Скомпилируйте код, исправьте лю бы е обнаруж енные ош ибки и запустите симулятор.
пчелы должны
Теп е р ь
радост но т т т ь
крыль>яллы- ,
4?
598 глава 13
элементы управление и графические фрагменты
П р облем ы с производительностью
Вы заметили, как замедляется симулятор, когда все пчелы залетаю т в улей? О собенно это
заметно, если добавить пчел, увеличив константу в классе Hive. О братите внимание, как
при этом меняется частота кадров.
Проблемы с производительностью
Загруж енны е изображ ения пчел имею т больш ой размер. Д ействительно большой. О ткройте одно из
них в программе просм отра и убедитесь. Это означает, что элемент P i c t u r e B o x ужимает изображ ение
при каждом изменении. А ведь изм енение масш таба изображ ения занимает время. И м енно поэтому
движ ение пчел замедляется, когда они залетаю т в улей. П розрачн ы й ф он элемента B e e C o n t r o l удваи
вает работу: сначала требуется уменьшить изображ ение пчелы , а затем участок ф она, на котором будут
выводиться п розрачны е пикселы.
J
Hive (Inside).png
600 глава 13
д л я у в е л и ч е н и я и р и и а ь и д и гсльиис 1И Д^иао^гио плпо^си/и'ю«! \
метод, меняющий размер изображения в момент его загрузки, что- ^
б ы для о т о б р а ж е н и я п ч е л и ф о н о в о г о р и с у н к а улья пользоваться у ж е ^ у 1Х р а Ж Н 0Н и 0
п т м а с ш т а б и п о в а н н ы м и веосиями. ^
B a c k g ro u n d lm a g e = P ro p e rtie s .R e s o u rc e s .B e e _ a n im a tio n _ l;
Зам ените Properties .Resources .Bee_animation_l на cells [0 ] .И зам ените и остальны е строки
case, введя на вторую позицию c e l l s [1 ], на третью —c e l l s [2 ], на четвертую — c e l l s [3 ], на
пятую - c e l l s [2 ], на позицию default - c e l l s [1 ], чтобы отображ ались только отмасштабиро-
ванны е рисунки.
j ^
Запусти те си м улятор, он раб отает нам ного бы стрее!
подробно о графике
{
Bitmap resizedPicture = new Bitmap(width, height);
вы зо в эт о го экзем пляра
f
Вы ничего не р и с уе т е
S y ste m .^ " йгарЬ1с5 влияет на ф о р м у , на объект е С(гарЫс5.
с о з д а в ш у ю о б ьек т б1гарП1С5. Он использует ся для
рисования на других
объект ах.
Вы вы зы вает е м ет оды
объект а С га р Ь Ъ , .n v a w L i n e S ( )
а изображение
появляет ся на
породивш ем его К п р и м ер у, м ет о д
объект е. DrawLinesQ р и с у е т
набор линий на
объ ект е, создавш ем
экзем п л я р Q m pkics.
604 глава 13
элементы управление и графические фрагменты
М етод D r a w s t r i n g () вводит текст. Н о сначала вам нужно создать объект Font, определяю щ ий
ш рифт. Этот объект реализует и нтерф ей с I D i s p o s a b l e :
u s in g (F o n t a r ia l2 4 B o ld = new F o n t(" A r ia l" , 24, F o n tS ty le .B o ld )) {
g .D ra w s trin g (" H i th e re !", a ria l2 4 B o ld , B ru s h e s .R e d , 50, 7 5 );
дальше ► 605
рисуем картинку
Рисуем на форме
Создадим новое прилож ение Ш1п(1о\У5, к о т о р о е рису
ет картинку п о с л е щ ел ч к а н а ф о р м е . J a p u c y u n ie э щ о
606 глава 13
Это мы добавим внутрь
оператора using, создающего
объект Реп.
Д обавим указы ваю щ ую на цветок стрелку
Н екоторы е методы объекта G r a p h i c s берут массив объектов P o i n t и соединяю т их ли
ниями или кривыми. Методом D r a w L i n e s {) нарисуем наконечник стрелы, а методом
D r a w C u r v e О —ее основу. Есть и другие методы, работающ ие с массивами точек (например,
метод D r a w P o l y g o n () рисует замкнутые формы, а метод F i l l P o l y g o n O заполняет их).
д . D r a w L in e s (th ic k B la c k P e n , new P o i n t [] {
new P o i n t (13 0, 11 0 ), new P o i n t (12 0, 1 6 0 ), new P o i n t (15 5, 1 6 3 )});
g .D ra w C u rv e (th ic k B la c k P e n , n e w P o i n t [] {
new P o i n t (12 0, 1 6 0 ), new P o i n t (17 5, 120) new P o in t(2 1 5 , 70) });
О Д обавим шрифт
П ервое, что требуется для написания текста, — создать объект Font. С нова напиш ем опе
ратор usin g , так как он реализует и нтерф ей с I D i s p o s a b l e . Создать ш риф т просто. Суще
ствует несколько перегруж енны х конструкторов, самый простой использует имя ш рифта,
его разм ер и п еречи слен ие F o n t S t y l e .
u s in g (F o n t fo n t = new F o n t( " A r i a l " , 16, F o n tS ty le . I t a l i c ) ) {
Д обавим текст « N e c ta r h e re »
О пределим место для строки, выяснив, какой разм ер она будет иметь на ф орм е п ри по
мощи метода M e a s u r e S t r i n g () , возвращ аю щ его парам етр SizeF. ( S i z e F — это версия
f l o a t парам етра Size). Так как мы знаем, где находится конец стрелки, поместим над ним
центральную точку надписи.
S iz e F s iz e = g .M e a s u re S trin g ( "N e c ta r h e re ", fo n t);
g .D ra w s trin g (" N e c ta r h e re ", fo n t. B ru s h e s .R e d , new P o i n t (
215 - (in t)s iz e .W id th / 2, 70 - ( i n t ) s iz e .H e ig h t) ) ;
} _______________
} ^ ^ f 'I® forml ^
Ч f-je забудьте закрыть оба блока using.
в руку карандаш
u s in g (G ra p h ic s g = t h i s . C re a te G ra p h ic s О )
u s in g (F o n t f = new F o n t ( " A r i a l " , 6, F o n tS ty le .R e g u la r)) {
fo r (in t X = 0; X < th is .W id th ; x += 20) {
} .........................................................................
fo r (in t у = 0; у < th is .H e ig h t; у += 20 ) {
2Q
2. Ч то п р о и зо йд е т при за п уске п р и ве д е н н о го ниж е ed
кода? Н а рисуй те результат на ф о р м е, используя ЇІ
д л я р а зм е щ е ни я отд ельны х то ч е к то л ько что
в и зуа л и зи р о в а н н ую сетку. ■ tie
u s in g (Р еп р е п = : lab
МЙ
new Р е п (B ru s h e s .B la c k , 3 .0 F )) {
ЖГ
g .D ra w C u rv e (p e n , new P o i n t [] {
(ёб
new P o i n t (8 0 , 6 0 ), »3
new P o in t (2 0 0 ,4 0 ),
new P o i n t (18 0, 6 0 ),
new P o in t (3 0 0 ,4 0 ),
' Ш
})
g .D ra w C u rv e (p e n , n e w P o i n t [] { @
new P o i n t (3 0 0 ,1 8 0 ), new P o i n t (18 0, 200) Wl
new P o i n t ( 2 0 0 ,1 8 0 ), new P o i n t (8 0 , 20 0 ), №
});
g .D ra w L in e (p e n , 300, 40, 300, 1 8 0 );
g .D ra w L in e (p e n , 80, 60, 80, 2 0 0 );
g .D ra w E llip s e (p e n , 40, 40, 20, 2 0 );
g .D ra w R e c ta n g le (p e n , 40, 60, 20, 3 0 0 );
g .D ra w L in e (p e n , 60, 60, 80, 6 0 );
g .D ra w L in e (p e n , 60, 200, 80, 2 0 0 );
608 глава 13
элементы управление и графические фрагменты
P o lt ы ойектоб
Ko/nt и рисует вершины, которые
3. Э то т код р а б отае т с н е п р а ви л ь н ы м и ф о р м а м и . зат ем соединяются линиями.
О п р е д е л и те , ка ко й р и су н о к со зд а е т это т код,
и в о с п р о и зв е д и те его на сетке.
g .F illP o ly g o n (B ru s h e s .B la c k , new P o i n t [] {
new P o in t(6 0 ,4 0 ), new P o i n t (1 4 0 ,8 0 ), new P o i n t (2 0 0 ,4 0 ),
new P o in t (3 0 0 ,8 0 ), new P o i n t (3 8 0 ,6 0 ), new P o i n t (3 4 0 ,1 4 0 ),
new P o i n t (3 2 0 ,1 8 0 ) new P o in t (3 8 0 ,2 4 0 ), new P o i n t (3 2 0 ,3 0 0 )
new P o i n t (3 4 0 ,3 4 0 ) new P o in t (2 4 0 ,3 2 0 ), new P o i n t (1 8 0 ,3 4 0 )
new P o i n t (2 0 ,3 2 0 ), new P o i n t (6 0 , 2 8 0 ), new P o i n t (1 0 0 , 240)
new P o i n t (4 0 , 2 2 0 ), new P o in t (8 0 ,1 6 0 ),
});
Щ Forml
sr Г“ иГ w rr ISP ST ST w s - !&" pr ssr w
90
16
й
IS
fSB
m-
Ш
ssr
a
Ub
ЯЗ
8^
!5S
BB
Я
Ш
дальше ► 609
выглядит прекрасно, но...
u s in g (G ra p h ic s g = t h i s .C re a te G ra p h ic s ())
u s in g (F o n t f = new F o n t( " A r i a l " , 6, F o n tS ty le .R e g u la r)) {
fo r (in t X = 0; X < th is .W id th ; x += 20) {
90 ;
Forml о S
ю 2«0 I2S0 а -тщ'- к -
166
120
1«
160
т
ээо
220
Ё40
яэ
ж
кй
т
здз
»0
610 глава 13
Нарисованные наш им визу-
ализатором перекрываю
щиеся пчелы имели ст ран
ный вид.
Решение проблемы с прозрачностью
Давайте устраним артефакты изображения в местах, где ма
ленькие картинки перекрываются! Решить проблему поможет
метод D raw lm age () . Мы вернемся к нашему приложению »ажнение!
Windows и отредактируем его таким образом, чтобы перекры
вающиеся изображения не удаляли фрагменты друг друга.
дальше ► 611
вернемся к событиям
І- ^ е ш е н и е
Построим форму, которая загружает
изображение из файла, а потом меняет его
о л ? ““
масштаб.
ширину « » « « « 6
public partial class Zoomer : UserControl {
614 глава 13
элементы управление и графические фрагменты
М ет о^^‘^'^^ p ro te c te d o v e rrid e v o id O n P a in t ( P a in tE v e n tA r g s e) {
O nPaint для -— — ^Console. WriteLine ("OnPaint {O} {l}", DateTime .Now, e.ClipRectangle) ;
ЛН?(5ой ф о р м ы b a s e .O n P a in t (e) ;
и добавьте }
э т у ст року.
Подвигайте форму за границы экрана, сверните ее, спрячьте под другим окном и посмотрите на
окно вывода. Вы увидите, что метод O n P a i n t вызывает событие P a i n t каждый раз, когда ф ор
ма становится н ед ей ств и тел ьн о й и нуждается в перерисовке. П араметр C l i p R e c t a n g l e - это
прямоугольник, описывающ ий ставшую недействительной часть формы. О н передается свой
ству P a i n t E v e n t A r g s собы тия P a i n t и увеличивает производительность, так как перерисовке
подвергается только та часть ф ормы , которой это действительно нужно.
О И спользуйте м етод I n v a l i d a t e о
Если скрыть, а потом снова сделать видимой часть ф орм ы , .NET вы
зы вает собы тие Paint, которое вы зы вает метод I n v a l i d a t e () , и Метод InvalidateQ
передает ему парам етр R e c t a n g l e , указываюш;ий, какая часть фор- по ф орм ы
мы долж на быть перерисована. П осле чего .NET вы зы вает метод ^^дейст вит ель-
O n P a i n t , инициируюш,ий собы тие P a i n t ф орм ы , и недействитель-
пая область перерисовы вается. J V.
дальше ► 615
что это за мерцание?
Часто»
^аД аБ аеМ ы е
БоЦ|=>ос;ь1
616 глава 13
элементы управление и графические фрагменты
1
UTVPM
Как избавиться от этого м ерцания? Что делать, если
вы д олж ны нарисовать на ф орм е м нож ество граф и
че ски х ф р агм ентов? В едь им енно это является при
чиной.
Отмасш табированная графика будет выглядеть лучше, если перед вы зовом метода
D r a w l m a g e () объекта G raphics свойству InterpolationlWlode этого объекта присвоить
значение i n t e r p o l a t i o n M o d e . H i g h Q u a l i t y B i c u b i c . (В верхню ю часть кода при этом
нужно добавить строку u s i n g S y s t e m .D r a w i n g .2 D ;)
двойная буферизация
В ернитесь к масштабируемому изображ ению и подвигайте ползунки. О братили внимание на ноявляю-
ш,ееся мерцание? Д ело в том, что п ри каждом перемеш;ении ползунка обработчик собы тия P a i n t ри
сует белый прямоугольник, а уже на нем - изображ ение. Эта процедура происходит несколько раз в
секунду и им енно она и нтерпретируется человеческим глазом как мерцание. И збеж ать м ерцания мож
но с помощью д во й н о й б у ф ер и зац и и (d o u b le b u fferin g ). Каждый кадр или ячей ка анимации сначала
рисую тся в буфер, и новы й кадр отображ ается только после его полной прорисовки. Рассмотрим п рин
цип работы этой техники на прим ере объекта Bitm a p .
О Вот типичная программа, рисующая на ф орм е при помощ и объекта Graphi сs.
Объект
System.V'A*'
618 глава 13
элементы управление и графические фрагменты
О Реконструкция визуализатора
Вам больше не понадобятся словари, так как элементов P i c t u r e B o x и B e e C o n t r o l уже нет.
Вместо них фигурирует метод D r a w H i v e ( g ) , рисующий форму H i v e на объекте Graphics,
и метод D r a w F i e l d ( g ) , рисующий форму Field.
дальше ► 619
перестраиваем визуализатор
p u b l i c v o id R u n F ra m e (o b je c t s e n d e r, E v e n tA rg s e) {
fra m e s R u n + + ; Уберем, вызов renc^erer.Ren^^er()^
w o r l d . Go ( ra n d o m ) ; так как эт от метод нам
end = D a te T im e .N o w ; больше не нужен.
T im e S p a n fra m e D u ra tio n = end - s ta rt;
s ta rt = end;
U p d a te S ta ts (fra m e D u ra tio n ) Sb( регулярно обновляете м ир, а обе
h iv e F o r m .In v a lid a t e 0 ;
формы им ею т ссылку на визуализатор,
значит нужно всего лишь анимировать
f ie ld F o r m .In v a lid a t e 0 ; их, оызвав соответствуюи^ие методы
InvalidateQ. Об остальном позаботятся
их обработчики события Paint.
620 глава 13
элементы управление и графические фрагменты
дальше ► 621
решение упражнения
p u b l i c R enderer( W o r ld T h e W o r ld , H iv e F o rm h iv e F o rm , F ie ld F o rm fie ld F o rm ) {
t h i s . w o r l d = T h e W o rld ;
t h is . h iv e F o r m = h iv e F o rm ; * He забудьте внести изменения в файл
t h i s . fie ld F o rm = fie ld F o rm ; Renderer.cs, объявив класс Renderer, как
fie ld F o rm .R e n d e re r = t h is ; public. Проделайте аналогичную опе-
h iv e F o rm .R e n d e re r = t h i s ;
рацию для классов World, Hive, Flower
In itia liz e lm a g e s {);
и Bee, в противном случае вы получите
ошибку построения. у'^игле
p u b l i c s t a t i c B i t m a p R e s i z e lm a g e ( I m a g e I m a g e T o R e s i z e , in t W id th , in t H e ig h t) {
B itm a p b itm a p = new B itm a p ( W id th , H e ig h t ) ;
u s in g (G ra p h ic s g ra p h ic s = G r a p h ic s . F ro m lm a g e (b itm a p )) {
^ g ra p h ic s .D ra w lm a g e (Im a g e T o R e s iz e , 0, 0, W id th , H e ig h t)
re tu rn b itm a p ;
} “ сохийняет их в полях
Bitm ap обьекта Renderer. В результ ат е
B itm a p H iv e ln s id e ; метоЬы PaintHiveQ и PaintFormQ
B itm a p H iv e O u ts id e ; 1 ^ ^ ^ ° ^ ^ о с т ь рисовать
B itm a p F lo w e r; немасштабированные изображения при
B i t m a p [] B e e A n im a tio n S m a ll; омощи методов PrawlmaaeUnscaledf)
обьекта Graphics формы.
B i t m a p [] B e e A n im a t io n L a r g e ;
p r i v a t e v o i d I n itia liz e lm a g e s 0 {
H i v e O u t s i d e = R e s i z e l m a g e ( P r o p e r t i e s . R e s o u r c e s . H i v e ___o u t s i d e _ , 8 5 , 1 0 0 ) ;
Flower = R e s i z e l m a g e ( P r o p e r t i e s .R e s o u r c e s .Flower, 75 , 75); ~
H iv e ln s id e = R e s iz e lm a g e (P ro p e rtie s .R e s o u rc e s .H iv e in s id e
h iv e F o r m . C lie n t R e c t a n g le .W id th , h iv e F o r m . C lie n t R e c t a n g le .H e ig h t ) ;
B e e A n im a tio n L a rg e = new B it m a p [ 4 ] ;
B e e A n i m a t i o n L a r g e [ 0] = R e s i z e l m a g e ( P r o p e r t i e s . R e s o u r c e s . B e e _ a n i m a t i o n 1, 40, 40)
B e e A n i m a t i o n L a r g e [ 1] = R e s i z e l m a g e ( P r o p e r t i e s . R e s o u r c e s . B e e _ a n i m a t i o n ~ 2! 40, 40)
B e e A n im a t io n L a r g e [2 ] = R e s iz e lm a g e ( P r o p e r t ie s . R e s o u r c e s . B e e _ a n im a t io n ~ 3’ 40, 40)
B e e A n im a t io n L a r g e [3 ] = R e s iz e lm a g e ( P r o p e r t i e s . R e s o u rc e s . B e e _ a n im a tio n ~ 4 ,' 4 0 , 40)
B e e A n im a tio n S m a ll = new B it m a p [ 4 ] ; “
B e e A n im a tio n S m a ll[0 ] = R e s iz e lm a g e ( P r o p e r t ie s . R e s o u rc e s . B e e _ a n im a tio n _ _ l, 20, 20)
B e e A n im a tio n S m a l l [ 1] = R e s i z e l m a g e ( P r o p e r t i e s . R e s o u r c e s . B e e _ a n i m a t i o n _ 2 ,' 20, 20)
B e e A n im a tio n S m a l l [2 ] = R e s iz e lm a g e (P r o p e r t i e s . R e s o u rc e s . B e e _ a n im a tio n _ 3, 20, 20)
B e e A n im a tio n S m a ll[3 ]
= Resizelmage(Properties.Resources.Bee_animation 4' 2 0 , 2 0 )
622 глава 13
элементы управление и графические фрагменты
p u b l i c v o i d PaintHive ( G r a p h i c s g ) {
g .F illR e c ta n g le (B ru s h e s .S k y B lu e , h iv e F o rm .C lie n tR e c ta n g le );
g .D ra w Im a g e U n s c a le d (H iv e ln s id e , 0, 0 ) ;
f o r e a c h (B e e b e e i n w o r ld . B e e s ) {
if (b e e . In s id e H iv e )
g .D ra w Im a g e U n s c a le d (B e e A n im a tio n L a rg e [c e ll],
b e e .L o c a tio n .X , b e e .L o c a tio n .Y ) ;
f o r e a c h (B e e b e e i n w o r l d . B e e s ) {
if ( !b e e . In s id e H iv e )
g .D ra w Im a g e U n s c a le d (B e e A n im a tio n S m a ll[c e ll],
b e e . TL o c a t i o^ nVI . X"V , b i—
e^ Лe . TL Лo c a ^t i o n . Y )
}
У К Метод ^ координатам р и с у -
в классе WoИd ^ не5о и земная
е т поле. Сначала Р “ ^ д м е л е эт о го
[ поверкност ь, зат ем
p riv a te in t c e ll = 0;
p riv a te in t fra m e = 0;
^ улей. ^ .. .„ в а н и я о ёье к т о в и м е ет
1^елы. нарисовать пчел
p u b lic v o id A n im a te B e e s 0 {
fra m e + + ; г;«“ » . " ™
if ( f r a m e >= 6)
fra m e = 0;
s w itc h (fra m e ) {
case 0 : c e ll 0; b r e a k ;
c a s e 1: c e l l 1; b r e a k ;
case 2 : c e ll 2; b r e a k ;
case 3 : c e ll 3; b r e a k ;
case 4: c e ll 2; b r e a k ;
case 5: c e ll 1; b r e a k ;
d e fa u lt : c e ll = 0; b r e a k ;
3. и заилем ^»ова Z заилем z . затолл
h iv e F o rm .In v a lid a te О ;
f i e l d F o r m . I n v a l i d a t e ()
Выбод на печать
М етоды объекта G r a p h i c s , которы м и вы пользовались для рисования
на ф орм е, п о д х о д ят и д л я в ы в о д а н а п еч ать. И нструменты .NET для % %
этой процедуры находятся в пространстве имен S y s t e m . D r a w i n g .
P r i n t i n g . Вам нужно только со зд ать о б ъ е к т P rin tD o c u m e n t. С ним а п е Ч а щ а й т е
связано собы тие P r i n t P a g e , которое прим еняется так же, как и со
бы тие T i c k таймера. Затем требуется вы звать метод P r i n t () этого э т о
объекта и распечатать документ. Вот как это выглядит.
Г '
С о зд ай те п р и л о ж е н и е W indow s и добавьте к ф орм е кнопку В верхню ю часть кода
ф орм ы добавьте строку u s in g S y s te m .D ra w in g .P rin tin g , Дважды щ елкните на
кнопке и введите код. Вот что появится, когда вы введете +=:
private v o i d buttonl_C l i c k ( o b j e c t sender, EventArgs e) {
PrintDocument document = ne w P r i n t D o c u m e n t {);
do c u m e nt.PrintPage +=
j~7i ^ P r l n t P a g e E v e r r t H a n d l e r ( d o c u m e n t . P r l n t P B g e ) r ^ Т р г ё з Г т А В t o in s e T t)
о Наж мите Tab, и нужный код появится автоматически. И м енно так вы добавляли
обработчики собы тий в главе 11:
private v o i d buttonl_C l i c k ( o b j e c t sender, EventArgs e) {
PrintDocument document = n e w P r i n t D o c u m e n t ();
d o c u m e n t .PrintPage += n e w PrintPageEventHandler(document_I»rlntPage);
P f e s s TAB t o g e n e r a t e h a n d l e r 'd o c u m e n tjF rin tp ia B e * i n t h l s ~ e i a ^
624 глава 13
элементы управление и графические фрагменты
b o o l firs tP a g e = tru e ;
v o id d o c u m e n t_ P rin tP a g e (o b je c t s e n d e r, P rin tP a g e E v e n tA r g s e) {
D ra w B e e (e .G ra p h ic s , new R e c ta n g le (0 , 0, 300, 300));
© u s in g (F o n t fo n t = new F o n t( " A r i a l " , 36, F o n tS ty le .B o ld )) {
if (fir s tP a g e ) {
e . G r a p h ic s .D r a w s t r in g ("П е р в а я стра ниц а". F o n t, B ru s h e s .B la c k , 0, 0 );
e .H a sM o re P a g e s = tr u e ; присвоить свойству e.HasMorePages значение true.
firs tP a g e = fa ls e ; D ocum ent снова вызовет обработчик событий
} e ls e { и от правит на печать следующую страницу.
е .G r a p h ic s .D r a w s tr in g ( "В тор ая стра ниц а". F o n t, B ru s h e s .B la c k , О, 0 );
Напишите код для кнопки Print, которая вызывает окно предварительного просмотра со
f i n t t a b le X = e .M a rg in B o u n d s .X + ( i n t ) s t r i n g S i z e . W i d t h + 5 0 ;
Процедура ) t a b le W id t h = e .M a rg in B o u n d s .X + e .M a rg in B o u n d s .W id th - t a b le X - 20-
построения Л f ir s tc o lu m n x = ta b le X + 2 ;
таблицы. in t s e c o n d C o lu m n X = t a b l e X + ( t a b l e W i d t h / 2) + 5 ;
_ in t t a b le Y = e .M a rg in B o u n d s .Y ;
/ / Вам н уж н о н а п и с а т ь остальную часть метода, ра спеча ты ваю щ его окно
О Используйте м етод P r in tT a Ы e R o w ()
Э тот метод пригодится для создания таблицы со статистикой в верхней части окна.
p riv a te in t P rin tT a b le R o w (G ra p h ic s p r in t G r a p h ic s , i n t ta b le X ,
i n t t a b l e W i d t h , i n t f i r s t C o l u m n X , i n t s e c o n d C o lu m n X ,
i n t t a b l e Y , s t r i n g f i r s t C o l u m n , s t r i n g s e c o n d C o lu m n )
{
Font a r i a l l 2 = new F o n t ( " A r i a l " , 1 2 ) ;
S iz e s trin g S iz e = S iz e . C e ilin g (p rin tG ra p h ic s .M e a s u re S trin g (firs tC o lu m n , a r ia ll2 ) )
t H D lc Y += 2;
p r in tG r a p h ic s .D r a w S tr in g ( fir s tC o lu m n , a r i a l l 2, B ru s h e s .B la c k ,
firs tc o lu m n x , ta b le Y ) ;
p rin tG ra p h ic s .D ra w S trin g (s e c o n d C o lu m n , a r i a l l 2 , B ru s h e s .B la c k ,
s e c o n d C o lu m n X , t a b l e Y ) ;
ta b le Y += ( i n t ) s t r i n g S i z e . H e i g h t + 2 ;
p rin tG r a p h ic s .D ra w L in e ( P e n s .B la c k , ta b le X , ta b le Y , ta b le X + ta b le W id th , ta b le Y ) -
a r i a l l 2 .D is p o s e 0 ; ^ / //
r e tu r n ta b le Y ; V При каждом вызове мет од PrintTableRowQ добавляет
} высоту распечапланной строки в переменную tableY
и возвращает новое значение,
626 глава 13
элементы управление и графические фрагменты
В н и м ател ьно п р очитайте при м ечани я, которы м и мы снаб дили этот скринш от!
# Р - | И Ш В Ш В и Close: Page і 1
Bees 6
Flowers 10
Honey in Hive 0.2Ш
N ectar in Flowers 2S.300
I
, Информация о левом поле Frames Run 286
хранится в переменной Frame Rate 16 (62.5ms)
e.MarginBounds. Эллипс начинает
ся в точке e.MarginBounds.X + 2.
.------ -а
решение
o " Z 2 T e Z lto p L T ^ -
u s in g S y s te m .D ra w in g .P rin tin g ; /
p r i v a t e v o id d o c u m e n t_ P rin tP a g e (o b je c t s e n d e r, P rin tP a g e E v e n tA rg s e) {
G ra p h ic s g = e .G r a p h ic s ;
S iz e s t r in g S iz e ;
u s in g (F o n t a r ia l2 4 b o ld = new F o n t( " A r ia l , 24, F o n tS ty le .B o ld )) {
s trin g S iz e = S iz e .C e ilin g ( Э ту часть
g . M e a s u r e S tr in g ( "B ee S im u la to r " a ria l2 4 b o ld )); кода М 1?1 б я М
g .F illE llip s e (B ru s h e s .G ra y , ы ж е давали.
new R e c ta n g le (e .M a rg in B o u n d s .X + 2, e .M a rg in B o u n d s .Y + 2, Здесь рисце<м-
s trin g S iz e .W id th + 30, s tr in g S iz e .H e ig h t + 3 0 ) ) ; ся заголовок
g .F illE llip s e ( B r u s h e s .B la c k , б овале м зада
new R e c ta n g le ( e .M a r g in B o u n d s .X , e .M a rg in B o u n d s .Y , ются п а р а т
s tr in g S iz e .W id th + 30, s t r in g S iz e . H e ig h t + 3 0 ) ) ; 11плрь1 т й о л и ц к ’!
g .D ra w S trin g ("B e e S im u la to r " , a r ia l2 4 b o ld . со ст ат ист и-
B r u s h e s . G r a y , e . M a r g i n B o u n d s . X + 1 7 , e . M a r g i n B o u n d s . Y + 17) ; ,кой.
g .D ra w S trin g ("B e e S im u la to r " , a r ia l2 4 b o ld .
B r u s h e s . W h i t e , e . M a r g in B o u n d s . X + 1 5 , e . M a r g i n B o u n d s . Y + 15) ;
}
in t ta b le X = e .M a rg in B o u n d s .X + ( i n t ) s t r i n g S i z e . W i d t h + 5 0 ;
in t t a b le W id t h = e .M a rg in B o u n d s .X + e .M a r g in B o u n d s .W id th - ta b le X - 20-
in t fir s tc o lu m n x = ta b le X + 2 ;
in t s e c o n d C o lu m n X = t a b l e X + ( t a b l e W i d t h / 2 ) + 5;
in t t a b le Y = e .M a rg in B o u n d s .Y ; уже поняли, как
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X ,
s e c o n d C o lu m n X , ta b le Y , "B e e s", B e e s .T e x t); v C r"" »Ужно вызывать ^
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X , - каждой строки
s e c o n d C o lu m n X , ta b le Y , " F lo w e rs " , F lo w e rs .T e x t ) ;
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X ,
s e c o n d C o lu m n X , t a b l e Y , " H o n e y i n H i v e " , H o n e y ln H iv e . T e x t) ;
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X ,
s e c o n d C o lu m n X , t a b l e Y , " N e c t a r i n F l o w e r s " , N e c t a r l n F l o w e r s
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X ,
s e c o n d C o lu m n X , ta b le Y , "F ra m e s R u n ", F ram esR un . T e x t) ;
ta b le Y = P rin tT a b le R o w (g , ta b le X , ta b le W id th , firs tC o lu m n X ,
s e c o n d C o lu m n X , ta b le Y , "F ra m e R a te ", F ra m e R a te .T e x t);
H e забудьтенарисовать
g .D r a w R e c ta n g le ( P e n s .B la c k , t a b le X , e .M a rg in B o u n d s .Y ,
ta b le W id th , ta b le Y - e .M a rg in B o u n d s .Y ); границу таблицы «
разделяющую столбцы.
g . D r a w L in e ( P e n s . B la c k , s e c o n d C o lu m n X , e .M a rg in B o u n d s .Y ,
s e c o n d C o lu m n X , t a b l e Y ) ;
628 глава 13
Для рисования рамки вокруг сним
ков экрана вам потребуемся черная
ручка с толш,иной линии 2- пиксела.
/
fu s in g (P en b la c k P e n = new P e n ( B r u s h e s .B la c k , 2 )) Размер объектов
/^ ^ u s in g (B itm a p h iv e B itm a p = new B itm a p (h iv e F o rm . C lie n t S i z e .W id th , Bitmaps должен
/ / h iv e F o rm .C lie n tS iz e .H e ig h t)) совпадать с од
I \u s in g (B itm a p fie ld B itm a p = new B itm a p ( fie ld F o r m .C lie n tS iz e .W id th ,
^ластью формы,
i ^реЭнйЗначеннои
Так как fie ld F o r m .C lie n tS iz e .H e ig h t]_
Зля рисования, <ло
объек- эт ому вам
ты Pen { u s in g (G ra p h ic s h iv e G ra p h ic s = G r a p h ic s . F ro m lm a g e (h iv e B itm a p )) годится объект
и Bitmap CUentSbe.
после {
r e n d e r e r . P a in tH iv e ( h iv e G r a p h ic s ) ;
прим ене
ния нужно }
будет
удалить, i n t h iv e W id th = e .M a rg in B o u n d s .W id th / 2
мы п о f l o a t r a t io = (flo a t)h iv e B itm a p .H e ig h t / (flo a t)h iv e B itm a p .W id th ;
местили in t h iv e H e ig h t = ( in t) ( h iv e W id th * r a t i o ) ;
их в блок in t h iv e X = e .M a rg in B o u n d s .X + ( e .M a r g in B o u n d s .W id th - h iv e W id th ) / 2;
using.
i n t h iv e Y = e .M a rg in B o u n d s .H e ig h t / 3 ;
g .D ra w lm a g e (h iv e B itm a p , h iv e X , h iv e Y , h iv e W id th , h iv e H e ig h t);
g .D ra w R e c ta n g le (b la c k P e n , h iv e X , h iv e Y , h iv e W id th , h iv e H e ig h t);
{ ^'^^^"S’nBounds.SAJidth
r e n d e r e r . P a i n t F i e l d ( f i e l d G r raphics)
a p h i c s ) ;; ласт и, которая бидет
6 ц д е т Т ь 7 .7 ^ '^ .ширину об-
Именно ^ а к ^ шиТиТи ! . ! '''' печать.
i n t f i e l d w i d t h = e . M a r g i n B o u n d s3.w id th ;
. Width; снимки экрана. <^меть ваши
r a t io = ( flo a t) fie ld B itm a p .H e ig h t / ( flo a t)fie ld B itm a p .W id th ;
in t fie ld H e ig h t = (in t) ( fie ld w id th * ra tio ) ; соотношения высоты и ширины ф ор-
in t f i e l d x = e .M a rg in B o u n d s .X ; м ы в ы ч и с л я е м высоту сниМка экрана.
in t fie ld Y = e .M a rg in B o u n d s .Y + e .M a rg in B o u n d s .H e ig h t - fie ld H e ig h t;
g .D ra w lm a g e (fie ld B itm a p , fie ld X , fie ld Y , fie ld w id th , fie ld H e ig h t);
g .D ra w R e c ta n g le (b la c k P e n , fie ld x , fie ld Y , fie ld w id th , fie ld H e ig h t);
630 «rraea 13
Head First
глава
$2.98
14
O b je c tv ille
Hom e of
преступление повт орилось
^Возьм
Возьмиі 8 руку кйрандаш-
V В о т код, о п и сы в а ю щ и й б и тв у м е ж д у В е л и ко л е п ны м и Ж у л и ко м (а та кж е
с а р м и е й кл о н о в ). Н а р и суй те , ч то п р о и с хо д и т в куче при созд ани и
экзе м п л я р о в кл а сса F i n a l B a t t l e . Можно предположить, что
клоны были созданы при
c la s s F in a lB a ttle { помощи инициализаторй
pu b l i c C lo n e F a c t o r y F a c t o r y = new C lo n e F a c t o r y ( ) ; коллекции. I
p u b l i c L is t < C lo n e > C lo n e s = new L i s t < C lo n e > 0 { .
p u b l i c S w in d le rs E s c a p e P la n e e s c a p e P la n e ;
p u b l i c F i n a l B a t t l e () { Р'^сунок
V i l l a i n s w in d le r = new V i l l a i n ( t h i s ) ;
u s in g (S u p e rh e ro c a p ta in A m a z in g = new S u p e r h e r o O ) {
F a c to r y . P e o p le ln F a c to ry .A d d (c a p ta in A m a z in g );
F a c to r y . P e o p le ln F a c to ry .A d d (s w in d le r)
c a p t a in A m a z in g .T h in k ("Я у н и ч т о ж у с сы л ки на кл о н ы ,
одну за о д н о й ");
c a p ta in A m a z in g .Id e n tify T h e C lo n e s (C lo n e s ) ;
c a p ta in A m a z in g .R e m o v e T h e C lo n e s (C lo n e s ) ;
s w in d le r . T h in k ("Ч е р е з н е с к о л ь к о м инут моя армия с та н е т м усо р о м ");
s w in d le r . T h in k (" (б уд е т собр ана
e s c a p e P la n e = new S w in d le r s E s c a p e P la n e ( s w in d le r ) ; 1
s w in d le r.T ra p C a p ta in A m a z in g (F a c to ry ); Нарисуйте, что
M e s s a g e B o x .S h o w ("Ж ул и к у б е ж а л " ) ; происходит о м о
м е н т создания эк
Как 5ydem выглядеть куча после земпляра ооьекта
] e
[S e ria liz a b le ]
зітуска конструктора FinalBattle
(финальная 5итваУ
SwindlersEscapePlane
(План побега Жулика).
c la s s S u p e rh e ro : ID is p o s a b le {
p r i v a t e L is t < C lo n e > c lo n e s T o R e m o v e = new L i s t < C lo n e > ( ) ;
p u b l ic v o id Id e n t ify T h e C lo n e s ( L is t< C lo n e > c lo n e s ) {
f o r e a c h (C lo n e c lo n e i n c lo n e s )
C lo n e s T o R e m o v e .A d d (c lo n e );
}
p u b l i c v o id R e m o v e T h e C lo n e s (L is t< C lo n e > c lo n e s ) {
f o r e a c h ( C lo n e c lo n e i n c lo n e s T o R e m o v e )
c lo n e s . R em ove(c lo n e );
634 глава t4
смерть объекта
c la s s S w in d le rs E s c a p e P la n e {
p u b lic V i l l a i n P ilo ts S e a t;
p u b lic S w in d le rs E s c a p e P la n e (V illa in escapee)
Класс Clone также
P ilo ts S e a t = escapee; не показывается
} как н е с у щ е с т б е н Не забывайте про
} и ш для ременыя мет ки объектов,
данной задачи- указывающих на
c la s s C lo n e F a c to ry { ссылочные пере
p u b l i c T im e r S e l f D e s t r u c t = new T i m e r ( ) ;
менные.
p u b l i c L i s t < o b j e c t > P e o p l e l n F a g t o r y = n e w L i s t < o b j e c t > ()
}
Мы начали отвечать на первый
вопрос. Линии демонстрируют
архит ект уру. Скажем^ мы прове
ли линию между фабрикой клонов
и объектом Villain, так как ф а
брика ссылается на эт от объект
(через свое поле PeoplelnFactory).
Здесь нмисовано
не осе. Остальные
объекты вставьте
самостоятельно.
Нарисуйте, что
'происходит в куче
о остальных двцх
е точках. ^
В ка ко м м есте кода у м и р а е т ка п и та н В е л и ко л е п ны й ?
О ставьте со о тв е тс тв у ю щ и й ко м м е н та р и й на д и а гр а м м е .
дальше ► 635
что бы могли означать эти циф ры
Решение FinalBattle.
указывает на объект
Superhero, а ссылка
swm dfer ~ на объект Villain
>^яшеи фабрики клонов
o s lS r ““ “
Э то о б ъ ект ,
который оы
Эолжнь»! Ь ы т
Эобабиил» к Эйд
граМЛЛ£-
Теперь ссылка
escapePlane у к а
О зывает на новый
экземпляр объекта
SwindlersEscapePlane
а поле PilotSeat ~ на
Пока сущ ествует ссылка объект Villain.
escapePlane на oovckvsa
sv-j'mditr, э т о т oovcK m не
будет отправлен в мусор-
При появлении события selfDestruct
ьГ - ссылка factory начинает указывать на
значение nullj а зн ачит^ отправляется
в мусор и исчезает с нашей диаграммы.
е escape
Plane
Вслед за ссылкой factory в мусор
отправляется объект CloneFactory,
это приводит к исчезновению
^ J- объекта List... а это было послед
ним, что сохраняло наш объект
SuperHero. Он исчезнет при следу
ющем проходе сборщика мусора.
В от м о м е н т и сч е зн о ве н и я на ш е го суп е р ге р о я :
void SelfPestruct_Tick(ohJect sender^ EventArgs e) {
finalBattle.faciory = nciK;.................................................
fin a iB a ttie .
636 глава 14
это твое последнее слово?
p u b l i c C lo n e ( i n t c l o n e l D , s t r i n g lo c a tio n ){
t h is .C lo n e lD = c lo n e lD ;
t h is .L o c a t io n = lo c a t io n ;
}
p u b lic v o id T e llL o c a t io n (s t r in g lo c a t io n , i n t c lo n e lD ) {
C o n s o l e . W r i t e L i n e ("Мой н о м е р { 0 } и " +
"ты н а й д еш ь м е н я т у т : { 1 } ClonelD, location)
}
Знак ~ указывает, ч т о код в
, эт ом блоке выполняется, когда
p u b lic v o id W reakH avoc( ) { объект отправляется о мусор. Зт о мет од завер
шения объекта. Он
one О { отправляет н е-
T e llL o c a t io n (t h i s . L o c a tio n , t h is .C lo n e lD ) ; Ю Г“ ^°общение с
C o n s o le .W r ite L in e (" { 0 } h a s b e e n d e s tr o y e d " C lo n e lD ) несчастного клона.
Но только после
объект
онает помечен для
с-оорки Мусора.
638 глава 14
637
смерть объекта
П р и п о м о щ и о п е р а т о р а u s i n g создайте э к з е м п л я р C l o n e в о б р а б о т
aone#i
Метод ч и к е с о б ы т и я Click. Э т о п е р в а я часть кода кнопки:
создает ,aone#2
экземпляр
Clone и не-
меделенно private void clonel_Click(object sender, EventArgs e) { GC
убивает u s i n g ( C l o n e c l o n e l = n e w C l o n e (1)) {
его, убирая --- ^ // Н и ч е г о не делайте!
все ссылки.
}
} У
Так как cionei был объяв
лен при помощи оператора
using, запускается его метод
DisposeQ. остается I
cSo/ku
640 глава 14
о П о д к л ю ч и те д в е д р у ги е кнопки
С оздайте ещ е один экземпляр C l o n e в обработчике собы тия Click
второй кнопки и присвойте ему зн ачение null:
642 глава 14
смерть объекта
[S e r ia liz a b le ]
class Clone : IDisposable
о О тредактируем м етод D i s p o s e ()
Воспользуемся классом B i n a r y F o r m a t t e r для записи объекта C l o n e в файл
внутри метода D i s p o s e О : _
^ ^ ^ ___________ Аля доступа
using S y s t e m . 10; " " ■— к мтользуел№1М^
using System.Runtime.Serialization.Formatters.Binary; ^
H&CKOAbKO dupCK-
// существующий код using.
Беседа у камина
М етод Disposée) и м е т о д завершения
объекта сп о р ят о т о м , к т о из них
ценнее.
Disposée) М е т о д завер ш ен и я о б ъ е к т а
Ч естно говоря, приглаш ение сюда меня удивило.
Я думал, программисты уже приш ли к соглаше
нию. О том, что я более ценны й. Ты выглядишь
жалко. Ты не в состоянии сериализовать себя
или отредактировать клю чевые данные. Ты же
нестабилен, разве не так?
644 глава 14
Часзг>°
задаваем ы е
БоПр>ос;ь1
дальше ► 645
смерть объекта
О Р а з л и ч и я н а ч и н а ю т с я п р и п р и с в о е н и и з н а ч е н и й . Д ля з н а ч и м ы х т и п о в э та о п е р а ц и я осу
щ е с тв л я е тс я м е то д о м к о п и р о в а н и я : Значение переменной
г1ПеепМоге копирцет ся й
lnt flfteenMore = howMany; ре м е н н у ю
Изменение зна- fifteenMore -1-= 15; к нему прибавляет ^
ченыя перемен ^ ■
ной Р1Н:еепМоге Console.WriteLine("howMany has {0}, fifteenMore has {!]",
никак не влияет howMany, fifteenMore);
НЙ переменную^,
howM any. Результат д е м о н с т р и р у е т, ч т о п е р е м е н н ы е f i f t e e n M o r e и h o w M a n y ни-
______ ксцсне св я за н ы :
howMany has 25, fifteenMore has 40
О В случае о б ъ е к т о в п р и с в а и в а ю т с я с с ы л к и , а н е з н а ч е н и я :
t e m p e r a t u r e s . A d d (56.5 D ) ;
Q t e m p e r a t u r e s . A d d (27.4 D ) ;
r^List<float> differentList = temperatures;
d i f f e r e n t L i s t . A d d ( 6 2 . 9D) ;
H a O b e ссылки
ы к я з ы б я ю т НД
один объект .
И з м е н е н и е о б ъ е к та L i s t м е н я е т з н а ч е н и я о б е и х
с сы л о к :
Р езультат д е м о н с т р и р у е т, ч т о обе с с ы л к и н а ц е л е н ы М ет од
н а один и тот же о б ъ е к т: нГ который ц>^а-
temperatures h a s J., differentList has 3
648 глава 14
смерть объекта
p u b l ic D o g ( s t r in g name, s trin g b re e d ) {
th is .N a m e = nam e;
th is .B re e d = b re e d ;
}
p u b lic v o id Speak 0 {
C o n s o le . W r it e L in e ("М еня зовут {0 } и я {1 }.", Name, B re e d );
}
}
дальше ^ 649
1 ^ з ь м и в руку карандаш На консоли будет написано:
Решение Му name is Spike and j'm a beagle.
My name is Jake and I'm a poodle.
В от, что произошло...
С с ы л к и b o b и s p o t у к а з ы в а л и н а о д и н и тот ж е объект, Создан о б ьек т
соответственно, м е н я л и о д н и и те ж е ПОЛЯ и об е и м е
Canine, на кот орый
т еп ер ь указы вает Spot
л и доступ к м е т о д у S p e a k (). Н о стру к т у р ы р а б о т а ю т ссылка spot.
н е так. Соз д а в структуру bett y , в ы с к о п и р о в а л и в нее
д а н н ы е из струк т у р ы j аке. П р и э т о м друг о т друга эти
струк т у р ы не зависят.
\
Операция присваивания /Добавив
вот и отличие.
перем ен -
Jake
! poodle
А \
j
Jake
poodle
ную ЬеН у, вы полу-,
в случае структур при чили новое значение
betty jake
водит к появлению неза
висимой копшданньк. нГЙКТзГнение ^
Ведь струк™а — это полей с т р у к т у р ы jake. ® ' ' pitBetty
bull
\
j
Jake
poodle
ЗНАЧИМЫМ ТИП. '•■Ч__ . '
betty jake
смерть объекта
S p e a k T h r e e T i m e s (j a k e ) ;
П е р е д а н н а я э то м у м е то д у с т р у к т у р а упаковывается в с п е ц и а л ь н у ю « о б о
л о ч к у » , п о з в о л я ю щ у ю е й н а х о д и т ь с я в куче. В э т о вр е м я р а б о та ть со
с т р у к т у р о й н е в о з м о ж н о . Ее тр е б у е тс я сна ча л а « р а сп а ко ва ть» . К с ч а с т ь ю ,
э т о п р о и с х о д и т автоматически п р и пе р е д а че м е то д у в м е с то о б ъ е к та зна
ч и м о го типа.
Чтобы определить,
ст рукт ура ли перед
вами или другой зна
чимый т ип, упако В о т к а к с т е к и к у ч а в ы гл я д я т п о с л е со з д а н и я п е р е м е н н о й т и п а
ванный в «оболочку» obj e c t и п р и с в о е н и я е й с т р у к т у р ы Dog.
и помещенный в кучу
используйте клю че Dog s i d = new D o g ("S id ", "h u s k y");
вое слом !5-
O b je c t obj = s id ;
- В Л -
Sid
husky /
После упаков
ки ст рукт уры Dog Sid (упакован)
появляются две
копии данных: —
в стеке и в
куче.
О О б ъ е к т д о с т а т о ч н о п р и в е с т и к п р а в и л ь н о м у ти п у , и о н будет р а с п а к о
ван а в т о м а т и ч е с к и . Со значимыми типами использовать ключевое
слово a s нельзя, п о э т о м у п р и в е д е м е го к Dog.
D o g h a p p y ^&=T(Dog)
После этой
строчки вы
получаете
т рет ью ко
паю даш ы^
Pj,empyKmype
карру, которая
получает свое
собственное. Dog sid (упакован)
м ест о в clwKe.
652 глава 14
смерть объекта
сД енои
ъ
Когда вызывается метод, он ищет свои аргументы в стеке.
С т е к и г р а е т в а ж н у ю р о л ь в сп о с о б е , к о т о р ы м C L R запускает в а ш и п р о гр а м м ы . М ы п р и н и м а е м к а к
д о л ж н о е т о т ф акт, ч т о о д и н м е то д м о ж е т в ы з ы в а ть д р у г о й и далее п о ц е п о ч к е . М е т о д м о ж е т даж е
в ы з ы в а ть сам себя (э т о н а з ы в а е тс я рекурсия). Э ту в о з м о ж н о с т ь м ы и м е е т б л а го д а р я стеку.
В о т п а р а м е то д о в и з с и м у л я то p u b lic v o id F e e d D o g (C a n in e dogToFeed, B o w l d o g B o w l) {
ра с о б а ч ь е го п и т о м н и к а . О н и d o u b le e a te n = E a t(d o g T o F e e d .M e a lS iz e , d o g B o w l);
п р о с т ы : м е тод F e e d D o g O в ы re tu rn e a te n + ,0 5 d ; // Н ем ного всегд а разливается
зы ва ет м е тод E a t () , к о т о р ы й
в с в о ю о ч е р е д ь в ы з ы в а е т м е то д
C h e c k B o w l (). p u b lic v o id E a t(d o u b le m e a lS iz e , B o w l d o g B o w l) {
d o g B o w l.C a p a c ity -= m e a lS iz e ;
C h e c k B o w l(d o g B o w l.C a p a c ity );
Н апом ним
}
дальше ► 653
ссылки по запросу
Модификатор out
С у щ е с тв у ю т р а з л и ч н ы е с п о с о б ы п о л у ч е н и я з н а ч е н и й о т п р о гр а м м ы . О н и р е
а л и зую тся п р и п о м о щ и д о б а в л е н н ы х к о б ъ я в л е н и ю м етод а модификаторов,
в ч а с т н о с т и , м о д и ф и к а т о р а out. В о т к а к о н р а б о та е т. С о зд а й те н о в о е п р и
л о ж е н и е W in d o w s F o rm s и д об авьте к ф о р м е п у с то е о б ъ я в л е н и е метода. О б а
п а р а м е тр а п о м е ть те к л ю ч е в ы м сл о в о м o u t :
Параметр out
p u b lic in t R e t u r n T h r e e V a l u e s (out d o u b l e h a lf, out i n t tw ic e )
re tu rn 1;
дает методу
} возможность
До пе
П о п ы т а в ш и с ь п о с т р о и т ь ко д , в ы п о л у ч и т е два с о о б щ е н и я о б о ш и б к е :
редачи управления из текущего метода параметру, помеченному ключе
вернуть бо
вым словом out, ‘h a lf должно быть присвоено значение (а н а л о ги ч н о для
п а р а м е тр а ‘tw ic e ’ ). Р аботая с к л ю ч е в ы м с л о в о м o u t , в ы всегдад,олжек задавать
лее одного
п а р а м е тр д о в о з в р а щ е н и я м е то д о м з н а ч е н и я . В о т к а к в ы гл я д и т м е то д ц е л и
ко м :
значения.
R andom ra n d o m = n e w R a n d o m ( ) ;
p u b l i c i n t R e t u r n T h r e e V a l u e s ( o u t d o u b le h a lf, out in t tw ic e ) {
i n t v a lu e = r a n d o m .N e x t( 1 0 0 0 ) ;
h a l f = ( ( d o u b le ) v a lu e ) / 2 ;
t w ic e - v a lu e * 2; П
t арам ет рам , eпомеченным ГЧ./V
Ul/V\J ключевым
r V / C - U C « '! /V i
r e t u r n v a lu e ; / 'Д Л К Л Д Л
словом o^ u
f .-i- --Ч
t. нужно заранее ^
присвоит ..........
ь
} значения, иначе код ком пилироват ься
не будет . ^
В о сп о л ь зуе м ся з а д а н н ы м и п а р а м е тр а м и . Д о б а вьте к н о п к у со сл е д ую щ и м о б р а б о т ч и к о м с о б ы т и й :
p r iv a te v o id b u tto n l_ C lic k ( o b je c t s e n d e r, E v e n tA rg s e) {
d o u b le b;
in t c;
a = R e tu r n T h r e e V a lu e s ( b , c)
C o n s o le .W r it e L in e ( " v a lu e = {0}, h a lf = { 1}, d o u b le = {2}", b, c) ;
а = R e t u r n T h r e e V a l u e s (out b , ou t с ) ;
Т е п е р ь п р о гр а м м у м о ж н о п о с т р о и т ь и з а п у с ти ть . М е т о д R e t u r n T h r e e V a lu e s () задает и в о зв р а щ а е т
т р и з н а ч е н и я : а п о л у ч а е т в о звр а щ а е м о е з н а ч е н и е м етод а, Ь - з н а ч е н и е , возвр а щ а е м о е п а р а м е тр о м
h a l f , а с - з н а ч е н и е , возвр а щ а е м о е п а р а м е тр о м t w i c e . ^
654 глава 14
смерть объекта
Модификатор ref
Вам с н о в а и сн о в а п р и д е т с я с та л к и в а ть с я с те м , ч т о п р и п е р е д а ч е м е то д у з н а ч е н и й т и п а
i n t , d o u b l e , s t r u c t и л и д р у г о го з н а ч и м о го т и п а в ы ф а к т и ч е с к и п е р е д а е те к о п и ю э т о
го з н а ч е н и я . П р о ц е д у р а т а к и н а зы в а е тся ; передача по значению.
Н о а р гу м е н т ы м о ж н о п е р е д а в а ть и м е то д о м , к о т о р ы й н а з ы в а е тс я передача по ссылке.
Э т о р е а л и зуе тся п р и п о м о щ и м о д и ф и к а т о р а r e f . К а к и м о д и ф и к а т о р o u t , о н и с п о л ь
зуется п р и о б ъ я в л е н и и и п р и в ы зо в е м етода. З н а ч и м о м у и л и с с ы л о ч н о м у т и п у п р и н а д
л е ж и т п е р е м е н н а я , в д а н н о м случае н е и м е е т з н а ч е н и я , п о с к о л ь к у л ю б а я п е р е м е н н а я
с м о д и ф и к а т о р о м r e f будет р е д а к т и р о в а т ь с я н е п о с р е д с т в е н н о м ето д о м .
В отличие от а р
Д об авьте к п р о гр а м м е м е то д , ч т о б ы п о с м о т р е т ь , к а к э ^ о р а б о та е т; гум ент а, помечен
ного м одиф икат о
public void ModifyAnlntAndButton(ref int value, ref Button button) ром ref, аргумент ,
int i value; помеченный м оди
ф икат ором out, не
i *= 5; т ребует инициа
value = i - 3- Задавая значение
? Задавая значение и
иппарам ет рры
а р а м ет кнопки,
ы кнопки,
мет од меняет переменные а и Ь лизации перед его
button = buttonl; в вызвавшем его методе ЬиПопг^Сиск{). переоачей.
}
Д о б а в и м к н о п к у с о б р а б о т ч и к о м с о б ы т и я для в ы зо в а метода;
П р и в ы зо в е о б р а б о т ч и к о м b u t t o n 2 _ C l i c k {) м е то д а M o d i f y A n l n t A n d B u t t o n () п е р е м е н н ы е q и b
п е р е д а ю т с я п о ссы л ке . М е т о д M o d i f y A n l n t A n d B u t t o n ( ) р а б о та е т с н и м и , к а к с л ю б ы м и д р у ги м и п е
р е м е н н ы м и . Н о б л а го д а р я пе р е д а че п о с сы л ке о н все вр е м я о б н о в л я е т э т и п е р е м е н н ы е , а н е п р о с т о
к о п и р у е т и х . П о с л е з а в е р ш е н и я м етод а q и Ь будут и м е т ь о т р е д а к т и р о в а н н ы е з н а ч е н и я .
З а п у с ти т е р е ж и м о тл а д к и и д об авьте к п е р е м е н н ы м q и Ь к о н т р о л ь н ы е з н а ч е н и я , ч т о б ы п о н я т ь , к а к всё
рабо тает.
Рассмотрим парамет р out. встроенный в значимый тип. Иногда строку вида ’"5S.&7”
нужно преобразовать в значение типа double. Это можно сделать при помош,и м е
тода double.Parse("3S,(p7”). Но запись double.ParseC'xyz“) приведет к исключению
FormatEKception. Иногда требуется именно такой результ ат , а иногда требуется
проверить возможность преобразования строки в значение. Здесь вам пригодится
мет од TryParseQ: запись double.TryParse("xyz'’, out d) вернет значение false и п р и
своит парамет ру i значение Oj в то время как запись doub(e.TryParseC'3S.t>7”, out d)
вернет значение true и присвоит переменной d значение 35.<Ь7.
Помните, как 6 главе <? при помощи оператора switch мы преобразовывали Spades
в Suits.Spades? Так вот . сущ ест вую т статические методы Enum.ParseQ и Епит.
TryparseQj которые делают т о же самое!
Необязательные параметры
Б ы в а е т та к , ч т о м е то д с н о в а и с н о в а в ы з ы в а е тс я с о д н и м и и т е м и ж е а р гу м е н т а м и , н о и н о гд а ем у тр е б у
е тс я д о п о л н и т е л ь н ы й п а р а м е тр . Н а п р и м е р , п р и н а л и ч и и з н а ч е н и й п о у м о л ч а н и ю а р гу м е н т ы указы ва
ю т с я , есл и п р и в ы зо в е м етод а ч т о -т о п о м е н я л о с ь .
в та ко й с и т у а ц и и вам н а п о м о щ ь п р и д у т н е о б я з а те л ь н ы е п а р а м е тр ы . В о б ъ я в л е н и и м етод а за т а к и м и
п а р а м е тр а м и следует з н а к р а в е н с тв а и з н а ч е н и е п о у м о л ч а н и ю . К о л и ч е с т в о н е о б я з а т е л ь н ы х парам е
тр о в м ож ет бы ть произвольны м .
В о т п р и м е р м етод а, в к о т о р о м с п о м о щ ь ю н е о б я з а т е л ь н ы х п а р а м е тр о в п р о в е р я е т с я , н е т л и у ч е л о ве ка
в ы с о к о й тем пературы :
v o id C h e c k T e m p e ra tu re (d o u b le te m p e ra tu re , d o u b le to o H ig h = 99.5, d o u b le to o L o w = 96.5)
Н е о б я з а т е л ь н ы й п а р а м е тр t o o H i g h и м е е т з н а ч е н и е п о у м о л ч а н и ю 99.5 , а н е о б я з а т е л ь н ы й п а р а м е тр
t o o L o w - з н а ч е н и е п о у м о л ч а н и ю 96.5. П р и в ы зо в е м етод а C h e c k T e m p e r a t u r e () с о д н и м а р гу м е н т о м
для п а р а м е тр о в t o o H i g h и t o o L o w и с п о л ь з у ю т с я и м е н н о э т и з н а ч е н и я . Е с л и ж е в в ы зо в е м е то д а ука
зать два а р гум е н та , в т о р о й а р гу м е н т будет п р и с в о е н п е р е м е н н о й t o o H i g h , в т о вр е м я к а к п е р е м е н н а я
t o o L o w о с та н е т с я с за д а н н ы м п о у м о л ч а н и ю з н а ч е н и е м . Е с л и ж е ука за ть т р и а р гу м е н та , и х з н а ч е н и я
будут п е р е д а н ы всем тр е м п а р а м е тр а м .
Д ля п е р е д а ч и з н а ч е н и я о п р е д е л е н н о м у п а р а м е тр у сущ е ств уе т и та к а я ф у н к ц и я , к а к и м е н о в а н н ы е а р г у
м е н т ы . Д о с т а т о ч н о п р и с в о и т ь п а р а м е тр у и м я , указав че р е з д в о е т о ч и е е го з н а ч е н и я .
Д о б а в и м к ф о р м е м е то д C h e c k T e m p e r a t u r e () и к н о п к у с о сл е д ую щ и м о б р а б о т ч и к о м с о б ы т и я . В о с
п о л ь з у й те с ь п р о ц е д у р о й о тл а д к и , ч т о б ы п о н я т ь , к а к все э т о р а б о та е т:
Вам не показалось
Типы, gonyckaiouiue значение null странным, что даже
в столбце Client мы до
пуст или присутствие
H a м и н у т у ве р н е м ся к к а р т о ч к а м с к о н т а к т н о й и н ф о р м а ц и е й , к о т о р ы е в пустых значений? А ведь
главе 1 с та л и о с н о в о й для базы д а н н ы х . П о м н и т е , к а к и м о б р а зо м в ы б и р а человек может или быть
л и с ь п а р а м е тр ы т а б л и ц ы , ч т о б ы п о з в о л и т ь п р и с в о е н и е в к а ж д о м с то л б
клиентом или не быть
им, не так ли? Но нет
це з н а ч е н и я n u ll н а сл уч а й, е с л и и н ф о р м а ц и я н е будет введена и л и будет никакой гарантии, что
введ ена н е к о р р е к т н о . К с о ж а л е н и ю , стр уктур а м и д р у ги м зн а ч и м ы м т и п а м поле Client заполнено на
всех карточках. Соот
н е в о зм о ж н о п р и с в о и т ь зн а ч е н и е n u ll. В о т та к и е о п е р а то р ы . ветственно, значение
null требуется на случай,
bool myBool = null; когда мы vwocmo не зна
DateTime myDate = null; ем, какие данные вводить.
на с т а д и и к о м п и л я ц и и с та н у т п р и ч и н о й с о о б щ е н и я об о ш и б к е !
N ullable<D ateTim e>_
П р е д п о л о ж и м , вам н у ж н ы е д а н н ы е о дате и в р е м е н и . Д ля э т о г о и с п о л ь Value: DateTime
зуется п е р е м е н н а я D a t e T i m e . Н о ч т о делать, е с л и е й н е в о в се х сл уч а ях HasValue: bool
тр е б у е тс я п р и с в а и в а т ь зн а че н и е ? В о с п о л ь з у й те с ь т и п а м и , д о п у с к а ю щ и м и
з н а ч е н и е n u ll. Д о с т а т о ч н о п о с т а в и т ь з н а к (?) п о с л е у к а з а н и я т и п а ;
GetValueOrDefault(): DateTime
bool? myNulableInt = null;
DateTime? myNullableDate = null;
С в о й с т в о V a l u e ука зы в а е т н а т и п п р о в е р я е м ы х з н а ч е н и й . К п р и м е р у, для
D a t e T i m e ? с в о й с т в о V a l u e р а в н о D a t e T i m e , для i n t ? - с о о т в е т с т в е н н о in t
и т. п . С в о й с т в о H a s V a l u e в о з в р а щ а е т з н а ч е н и е tr u e , е с л и п а р а м е тр н е р а в е н n u ll. Стриктура
Ыи[1аЫе<Т> позво
З н а ч и м ы й т и п всегда м о ж н о п р е о б р а з о в а ть к ти п у, д о п у с к а ю щ е м у з н а ч е н и е n u ll: ляет хранить как
значим ы е типы, так
DateTime myDate = DateTime.Now; и значение п^лИ. На
DateTime? myNullableDate = myDate; диаграмме вы оиди
те методы и свой
H o об ратное преобразование сопровож дается о п е р а ц ие й привед ения; ства с т р у к у щ р ы
ЫullableФaterlme>.
myDate = (DateTime) myNullableDate;
Е сл и с в о й с т в о H a s V a l u e и м е е т з н а ч е н и е false, с в о й с т в о V a l u e в ы з ы в а е т и с к л ю
ч е н и е I n v a l i d O p e r a t i o n E x c e p t i o n , к а к и о п е р а ц и я п р и в е д е н и я (ведь о н а
п р о и з в о д и т с я с и с п о л ь з о в а н и е м с в о й с т в а Value).
дальше ► 657
почувствуй вкус надежности
c la s s R o b u s tG u y {
p u b lic D a te T im e ? B i r t h d a y { g e t; p riv a te s e t;
p u b lic in t ? H e ig h t { g e t; p riv a te s e t; }
Ш ую строку.
p u b lic R o b u s tG u y (s trin g b irth d a y , s trin g h e ig h t) |
D a te T im e te m p D a te ; ilfT c k s
Восполь T meOfDay
зуйтесь if (D a te T im e .T ry P a rs e (b irth d a y , out te m p D a te ))
типами B ir t h d a y = te m p D a te ; ToBinary
РліеТіте e ls e Ф ToFileTime
и int 6 B irth d a y = n u ll; ToFHeTimeUtc
методе ToLocalTlme
■• jc -ji in t te m p in t;
~ out te m p in t) _____
Ф ToLongTimeString
e t"» ^
польза- e ls e
-# ToO ADate
Ф ToShortDateString
вателем H e ig h t = n u l l ;
данных Ъ Ф ToShortTimeStnng
значения . , , . j -# ToString
p u b lic o v e r r id e s t r in g T o S trin g O { : # ToUniversalTime
s tr in g d e s c rip tio n ; Year
^ ^ i f ( B ir t h d a y != n u l l )
При вводе d e s c rip tio n = "Я р о д и л с я + B i r t h d a y . V a l u e . T o L o n g D a t e S t r i n g () ;
некоррект - e ls e
н и х дан
ных метод d e s c r ip t io n - "Я не знаю д а т у с в о е го р о ж д е н и я ";
HasValueQ i f ( H e ig h t != n u l l )
Поэкспериментируй -
вернет зна d e s c r ip t io n += во мне " + H e ig h t + " дюймов р о с т а " ; те с другими м е
чение False. e ls e тодами, связанными
d e s c r ip t io n += я не знаю свой рост'
с т ипом DateTime,
re tu rn d e s c rip tio n ;
которые начинаются
с То, чтоды понять,
как они влияют на
конечный результ ат .
А в о т ко д м етод а M a in () .О н и с п о л ь з у е т м е то д C o n s o le . ReadLine () для ввода д а н н ы х :
s ta tic v o id M a in ( s t r i n g [] a rg s) {
C onso le . W r i t e (" У к а ж и т е д а т у р о ж д е н и я :, Запустите программу
s trin g b ir t h d a y = C o n s o le . R e a d L in e ( ) ; и попробуйте вводить различные
C onso le .W r ite ("У ка ж и те р о с т в дюймах: ') ; данные. Многие из них будут
s trin g h e ig h t = C o n s o le . R 6 a d L in e ( ) ;
распознаны методом D ateT im e.
R o b u s tG u y g u y = new R o b u s tG u y ( b ir th d a y , h e ig h t);
C o n s o le .W rite L in e (g u y . T o S tr in g ( ) ) ;
T ryP arse О . Если сделать это
C o n s o le . R e a d K e y ( ) ; не удастся, свойство B ir th d a y
класса RobustGuy не будет иметь
^ Метод Console.ReadLineQ позволяет пользователю
вводить информацию в консольное окно. После нажа значения.
тия клавиили Enter метод возвращаем строку.
смерть объекта
Б бассейне
p u b lic T a b le {
Возьмите фрагменты кода из бассей p u b lic s trin g s ta irs ;
на и заполните пробелы. Один и p u b lic H in g e f l o o r ;
тот же фрагмент может исполь p u b lic v o id S e t(H in g e b) {
зоваться несколько раз. В бас flo o r = b;
сейне есть и лишние фрагмен }
ты. Получите код, выводящий p u b lic v o id L a m p (o b je c t o il) {
на консоль запись «вернусь i f ( o i l ______ in t)
через 20 минут» при со зд а н и и
___________ . b u l b = (in t) o il;
э кз е м п л я р а кл а с с а Faucet:
e ls e i f ( o i l ______ s t r i n g )
s ta irs = ( s tr in g ) o il;
p u b lic c la s s Faucet {
p u b l i c F a u c e t () { e ls e i f ( o i l ______ H i n g e ) {
T a b le w in e = new T a b le ( ) ; ___________ v i n e = o i l ______ ,
H in g e b o o k = new H in g e ( ) ; C o n s o l e . W r i t e L i n e ( v i n e . T a b l e ()
w in e . S e t(b o o k ) ;
+ " " + __________. b u l b + " " + s ta irs )
b o o k .S e t(w in e );
w in e .L a m p (1 0 );
b o o k .g a rd e n .Lam p{"b a c k in " );
b o o k .b u lb *= 2;
w in e .L a m p ("m in u te s " );
w in e . L a m p (b o o k); p u b lic H in g e {
p u b lic i n t b u lb ;
p u b l ic T a b le g a rd e n ;
}
p u b l i c v o id S e t (T a b le a) {
g a rd e n = a;
}
При создании объекта Faucet p u b lic s trin g T a b le 0 {
появляется строка: re tu rn ___________ . s t a i r s ;
}
С^ а с к i n 20 m in u te s ^
Дополнительное задание;
Обведите строчки, в которых
получить- происходит упаковка.
Ф р агм ен ты
можно ис
по л ь зо в а ть public
бо л е е о д н о го private if
раза. class or g ard en
new is flo o r s tru c t
Brush a b s tra c t on W in dow
Lam p string
interfac e as D oor int
bulb oop H inge
Table flo a t
stairs single
double
QrnBem Ha c. 668 -
дальше >
надежность структур
Часто
^аД аБ аеМ ы е
B o n j^ o C b i Также вы сталкивались с такой полезной
Какая мне разница, что происходит
в стеке? структурой как S i z e . С ее помощью
fi ! Это поможет вам, к примеру, при
вы определяли размер строки в методе
I нкапсуляции. Посмотрите на уже
MeasureStringО .
S Так как понимание различий между знакомый вам код класса, определяющего
стеком и кучей позволяет корректно местоположение:
пользоваться ссылочными и значимыми Как определить, что мне нужно в
типами. Легко забыть, что структуры и p r iv a te P o in t lo c a tio n ; текущий момент — класс или структура?
объекты функционируют по-разному, ведь p u b lic P o in t L o c a tio n {
операция присваивания для них выглядит get { re tu rn lo c a tio n ; ) ^ ! в большинстве случаев програм
одинаково. Представление о том, какие } мисты работают с классами, потому что
процессы происходят в .NET и CLR по Если бы P o i n t был классом, инкапсу структуры имеют слишком много ограни
зволяет понять, чем именно отличаются ляция не сработала бы. Закрытость поля чений. Они не поддерживают наследова
ссылочные и значимые типы. l o c a t i o n не имела бы значения, ведь ние, абстракции и полиморфизм, а вы уже
вы создали открытое, предназначенное знаете, насколько важны эти вещи при
А зачем нужна информация по по только для чтения свойство, возвращаю создании больших приложений.
воду упаковки? щее ссылку на это поле, то есть дали
Структуры же полезны при повторяю
доступ другим объектам.
щейся работе с ограниченными типами
Q ; Потому что нужно понимать, когда К счастью для нас, P o i n t — это струк данных. Хорошим примером служат
действие переходит в стек, и когда тура. И открытое свойство L o c a t i o n прямоугольники и точки — они применя
данные начинают копироваться туда возвращает копию переменной. Работа ются только в определенных ситуациях,
и обратно. Упаковка требует места в ющий с ней объект может делать, что хо зато с удивительной регулярностью. При
памяти и занимает время. Разумеется, чет — это никак не скажется на состоянии наличии у вас небольших групп разнород
вы не заметите особой разницы, если ных данных, которые требуется сохранить
закрытого поля l o c a t i o n .
эта процедура выполняется редко. Но в поле или передать методу в качестве
представьте программу, выполняющую параметра, скорее всего, имеет смысл
однотипные действия много раз в секунду, Если P o i n t — это структура, то, воспользоваться структурой.
как это делал, к примеру, симулятор улья. может быть, я уже работал и с другими
Работа такой программы будет требовать структурами, сам того не зная?
все больше ресурсов, программа будет
замедляться, поэтому, наверное, имеет ; Да! При изучении графики вы стал
Структура позво
смысл избегать упаковки в часто повто
ряющейся части кода.
кивались со структурой R e c t a n g l e .
Она снабжена полезными методами,
ляет улучшить ин
позволяющими указать границы области,
Я понял, что при операции при и проверит, попадает ли в них выбранная капсуляцию класса,
сваивания одна структурная перемен точка. Достаточно указать местополо
ная копируется в другую. Но как я могу жение и размер структуры, и программа так как возвраща
использовать эти знания? автоматически рассчитает ее остальные
параметры. ющее ее, предна
Возьми Вруку карандаш значенное только
Предполагалось, что этот метод убьет объект
для чтения свой
Clone, но он не работает Почему?
ство, всегда созда
p r i v a t e v o id S e tC lo n e T o N u ll(C lo n e c lo n e ) {
c lo n e = n u l l ;
ет новую копию.
-- О т в ет на с. 6 6 2 .
смерть объекта
Ч то осталось о т Великолепного
П о с л е р а з го в о р а о б у п а к о в к е , в ы д о л ж н ы с о о б р а
зить, почем у ка п ита н В е л и ко л е п н ы й п о те р я л свою л е г
В озм ож ность
ко получать копии
силу. В се дело в т о м , ч т о э т о у ж е н е о н , а у п а к о в а н является боль
ная с т р у к т у р а шим преим ущ е
ством ст рукт ур
и других значимых
типов.
; s tг u c t I сравнение
'х
И н о гд а тр е б уе тс я р а с ш и р и т ь класс, о т к о т о р о г о н е в о з м о ж н о н а сл е д о в а н и е (к п р и м е р у, м н о ги е классы
•N E T п о м е ч е н ы м о д и ф и к а т о р о м se a le d ). Н а э т о т с л уч а й в C # и м е ю т с я м е т о д ы р а с ш и р е н и я (extension
methods). О н и п о з в о л я ю т добавить м е т о д ы в с у щ е с т в у ю щ и е классы. В ам н у ж н о т о л ь к о создать ста
т и ч е с к и й класс и д о б а в и т ь туда с т а т и с т и ч е с к и й м е то д , в к а ч е с тв е п е р в о го п а р а м е тр а п р и н и м а ю щ и й
эк з е м п л я р э т о г о класса.
' “О
•Д обавим м ето д р а с ш и р е н и я S u p e r S o ld i e r S e r u m (С у п е р с о л д а т ); ^ Словом
s ta tic c la s s S u p e rS o ld ie rS e ru m {
p u b lic s j^ tic s trin g B r e a k W a l l s (this OrdinaryHuman h > d o u b l e w a llD e n s ity ) {
re tu rn ("Я сломал стену плотностью " + w a llD e n s ity + ".");
T Z Z lltT a & S S r r
^0 тек nop, пока у него
П р и д о б а в л е н и и класса S u p e r S o ld i e r S e r u m класс O r d in a r y H u m a n доступ к классу
п о л у ч а е т м е то д B r e a k W a ll s , к о т о р ы й м о ж е т и с п о л ь з о в а т ь с я ф о р м о й : ^^^P^'^°^d.ierSerum.
s ta tic v o id M a in ( s t r i n g [] a rg s) ( эт о }делат Ы
O rd in a ry H u m a n s te v e = new O rd in a ry H u m a n (1 8 5 ) ; ><0ИС0ЛЬН0е прилож е-
c o n s o le . W r ite L in e (S te v e .B re a k W a lls (8 9 .2 ) ) ;
ла(7ки и посмот рит е, что про-
, ^ з ь м и в руку карандаш_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ вплщйо.
Рршацир Почему этот метод не уничтожил объект C lo n e ?
p r i v a t e v o i d S e t C lo n e T o N u ll (C lo n e c lo n e ) {
Так как парам ет р Clone c lo n e = n u l l ;
находится в стеке, j
присвоение ему значения
nuil никак не скажется мет ра п^исоаирм nuti, но данный
на состоянии к у Ч (^ ^ парамет р является всего лишь ссылкой на объект Clone.
662 глава 14
смерть объекта
Часто
<аДаБаеМые
В опросы
Почему бы вместо методов расширения не добавить Q ; Если вы можете расширить класс, это нужно сделать —
код нужного метода непосредственно в класс? методы расширения не являются заменой наследования. Но
существуют и классы, для которых это невозможно. Методы
расширения позволяют менять поведение групп объекгов
I ! Именно так и нужно поступать, если речь идет о до и даже добавлять функциональность к базовым классам .NET
бавлении метода в один класс. Методы расширения следует
Framework.
использовать аккуратно и только в случаях, когда по каким-то
При этом, чтобы воспользоваться новым поведением, нужно
причинам вы не можете отредактировать нужный вам класс
работать с новым производным классом.
(например, потому что он является частью .NET Framework).
Методы расширения таюке применяются для редактирования
поведения сущностей, ».который отсутствует доступ, I Методы расширения влияют на все экземпляры
например, типа или объекта из .NET Framework или другой 1асса или только на некоторые?
библиотеки.
Г ; Они влияют на все экземпляры расширенного вами
класса. Более того, созданный метод расширения будет по
А зачем нужны методы расширения, если класс
можно расширить при помощи наследования? казываться ИСР вместе с обычными для рассматриваемого
кпасса методами.
Н мжно пОАЛнить, ч т о
метод расширения не
Я понял! Методы расширения позволяют дает доступа к в н у
т р е н н е м у коду класса.
отредактировать поведение встроенных
классов .N ET Framework.
c la s s X : s trin g { }
П р и к о м п и л я ц и и п о я в и т с я с о о б щ е н и е об о ш и б к е . П о т о м у ч т о н е к о т о р ы е
к л а ссы .N E T п о м е ч е н ы м о д и ф и к а т о р о м s e a le d , з а п р е щ а ю щ и м н а сле д о ва н и е .
М е т о д ы р а с ш и р е н и я д а ю т в о з м о ж н о с т ь п о м е н я т ь п о в е д е н и е т а к о г о класса.
дальше У 663
лучше быстрее сильнее
^ /[а Г н и ш ы J ^ a c m u J ’ e H u ff
using Upside;
namespace Upside { namespace Sideways {
□
public static string ToPrice
дальше * 665
капитан жив!
^ а Г н и ш ы ]= » а с :Ш и р * е н и я
using Upside;
namespace Sideways {
class Program {
I
if (b == true)
Метод Green расширяет
return "be" класс bool — OH возбраила-
else em ст р о к у be, если б ул ев
ская перем енная и м еет
return "gets' / значение true, и g e t —
\ »
У в случае значения false.
666 глава 14
м ы П ЕРЕ С ТРО И Л И КЛАСС
SU PER H ER O . Н О КАК ВЕРНУТЬ
Я проанали зировала\ .
КАПИТАНА НАЗАД?
КОД: ВЕЛИКОЛЕПНЫЙ \
и спол ьзовал св о ю ]
Д ~ .р „ ™ ,ц „ я „ В е л н к о л е „„„„
в прошо?|1™Ль“" » Обмтиль.
« м о казал ась стр а„„а^ Т „ “ а ^ ™
екта C aptain А ш а" in g - Д Н К с б .-
ДВОИЧНОМ формате. ** значения, записанные в
= Г т—
Гв™ елГ“ ^ Великолепный вернулся!
решение ребуса
p u b lic Struct T a b l e {
p u b lic s trin g s ta ir s ;
p u b lic H in g e flo o r;
p u b lic v o id S e t(H in g e b) {
м . » » г и т ) » 0 ^ г « ' - / » п р Г в “« о в Ж flo o r = b;
м п е р е т н н т ^1 м и ^ ■ f ^ кйчестб е п я -
цглочисленнои g ^оде Bulb ссыл- }
“*Т*” p u b lic v o id L a m p (o b je c t o i l ) {
T a b le w in e = new T a b le {); e ls e i f (o il is H i n g e ) {
H in g e book = new H in g e ( ) ;
Hinge v i n e = o il as H inoe;
w in e . S e t(b o o k );
C o n s o l e . W r i t e L i n e ( v i n e . T a b l e ()
b o o k .S e t(w in e );
" "•+ flo o r - b u l b + " " + s ta irs );
V w i n e . Lam p П ;
}
C^^^ o o k . g a r d e n . L a m p ( " b a c k ir? ^ T T ~ -^
Помните, что J
b o o k .b u lb *= 2; ключевое слово as
работает только
(wine . L a m p ( " m in u te s ^ p; ^ с классами? Класс Hinge
и ст рукт ура Table
w in e . L a m p (b o o k); обладают м ет о
p u b l i c class H i n g e { дом SetQ. В классе
) , , . ^ Hinae эт от r e
Bom почему Table - это p u b lic i n t b u lb ; mod задает поле
iJP y x m y p a . Если бы это был кла-^г p u b lic T a b le g a rd e n ; j[ т урьГт М е.^ А у
Ч ^°°^-^лгоіеп " ^
p u b lrc v o rd se t (T a b le a"; ( Т е^ ^ Т зШ ^ ^ Г
g a rd e n = a; F^00r в классе
Hinge.
Обведены строчки, в которых ^ }
происходит упаковка. ^ p u b lic s trin g T a b le 0 {
668 глава 14
^5LINQ
Управляем данными
Простой проект...
О б ъ е к т в и л ь с к а я к о м п а н и я п о п р о и з в о д с т в у б ум а ги р е ш и л а сделать с о в м е с тн у ю
п р о м о -а к ц и ю с к о ф е й н ы м м а га з и н о м . Э т о т м а га з и н и м е е т с п е ц и а л ь н у ю п р о гр а м м у,
о т с л е ж и в а ю щ у ю , к а к о й ко ф е п о к у п а е т к а ж д ы й к л и е н т и к а к ч а с т о о н э т о делает.
Б ум аж ная к о м п а н и я х о те л а б ы у зн а ть, кто из ее клиентов регулярно посещает ко
ф е й н ы й магазин, ч т о б ы о т п р а в и т ь и м б е с п л а тн у ю к р у ж к у и к у п о н н а п о к у п к у и х
л ю б и м о го ко ф е ... Вам н у ж н о с к о м б и н и р о в а т ь д а н н ы е и создать с п и с о к к л и е н т о в
для р а с с ы л к и к р у ж е к и к у п о н о в .
670 глава 15
UNQ
Эт о класс м
»^.еречисленме из
1л.йогрйММК>| ко enum D r in k {
т фе-йного м а га зи B o rin g C o ffe e ,
на Starbuzx- C h o c o R o c k o L a tte ,
T rip le E s p re s s o ,
З а м н у ж н о получить Z e s ty L e m o n C h a i,
данные о т кофеино D o u b le C a p p u c c in o ,
го м агазина и наити H a lfC a fA m e ric a n o ,
т а м клиент ов, к о C h o c o M a c c h ia to ,
т оры е т а к ж е пользу - B a n a n a S p litln A C u p ,
ю т ся услуга м и б у
мажной компании. }
ШТУРМ
База данных Как бы вы ском бини ровали д а н н ы е
ContactDB от д вух организаций д л я получения
единого списка кон тактов?
Все данные о клиент ах ф и рм ы
по і^ о и зво д с т ву бум аги вы
найдет е в базе.
дальше ► 671
UNQ для спасения
var beeGroups =
from bee in world.Bees
group bee by bee.Currentstate
into beeGroup
orderby beeGroup.Key
select beeGroup;
t
Аналогичный запрос позволит нам
получит ь данные о клиентах из
коллекции кофейного магазина.
рдии и те же запросы
UNQ работают как
сбаз1м и Эйнных, т а к
L I N Q р а б о та е т с л ю б ы м и с т о ч н и к о м д а н н ы х в .N E T . Д о с т а т о ч н о (Д с документами X M L .
в с т а в и т ь в в е р х н ю ю ч а с т ь ф айла с к о д о м с т р о ч к у u s i n g S y s te m .
L i n q ; Б ол ее т о г о , И С Р а в т о м а т и ч е с к и п о м е щ а е т в в е р х н ю ю
ч а с т ь создаваем ы х ф а й л о в кл а ссо в с с ы л к у н а L IN Q .
672 глава 15
UNQ
a o to D e U b o n .
namespace S y s t e m . C o l l e c t i o n s .G e n e r i c {
interface IEnumerable<T> : lEnumerable
11 Резюме:
// Осуществляет простой перебор элементов коллекции.
//
11 Возвращает:
11 S y s t e m . C o l l e c t i o n s .G e n e r i c .I E n u m e r a t o r < T > , который
11 и перебирает элементы коллекции.
IEnumerator<T> GetEnumeratorO
Это единственный м ет од и н
К . т ерф ей са. Его р еа л и зуе т каж
дая коллекция. Вы мож ете
создават ь и собст венные o6v ^
ект ы , реализую щ ие инт ерф ейс
IEnumerable<T>... а за т е м Рабо
т а т ь с ними при помощ и UNQ.
М е т о д тр е б у е т указа ть с п о с о б п е р е м е щ е н и я о б ъ е к та
о т о д н о г о э л е м е н та к о л л е к ц и и к другом у. Э т о ус л о в и е
л ю б о г о з а п р о с а L IN Q . Е с л и в ы м о ж е т е п р о с м а т р и в а т ь
к о л л е к ц и ю э л е м е н т за э л е м е н то м , з н а ч и т, м о ж е т е и
реализовы вать ин те р ф е й с IE n u m e r a b l e < T > . С о о т
ве тстве н н о L IN Q в с о с то я н и и п осы лать ко л л е кц ии
запросы .
а сЦ еной
Д ля з а п р о с о в , с о р т и р о в к и и о б н о в л е н и я д а н н ы х L I N Q и с п о л ь з у е т м е т о д ы
р а с ш и р е н и я . У б е д и те с ь в э т о м сам и. С о з д а й те м а сси в т и п а i n t с и м е н е м
l i n q t e s t , п о м е с т и т е в н е го ч и с л а и в в е д и те э ту с т р о ч к у : Теперь вы видит е,
почем у М ет о
IE n u m e ra b le < in t> re s u lt = fro m i in lin q te s t w h e re i < 3 s e le c t i; ды расш ирения, с
кот оры м и вы п о
A т е п е р ь п р е в р а т и т е B к о м м е н т а р и й с т р о ч к у u s i n g S y s t e m . L i n q ; в з а го знаком ились в главе
14^ т ак важны...
л о в к е ф айла. Т е п е р ь р е ш е н и е п о с т р о и т ь н е удастся. В едь и м е н н о те м е то д ы , они позволят .NET
к о т о р ы е в ы в ы зы в а е те , р а б о та я с L I N Q , и с п о л ь з о в а л и с ь для р а с ш и р е н и я мас (а заодно и вам )
сива. м ен ят ь поведение
• сущ ест вую щ их
типов.
674 глава 15
UNQ
САО)кные запросы
Д ж и м м и продал сво ю недавно созданную ф ирм у кр уп н о м у и н в е с то р у и х о ч е т п о т р а т и т ь часть п р и б ы л и
н а п о к у п к у с а м ы х д о р о г и х к о м и к с о в п р о к а п и т а н а В е л и к о л е п н о го , к о т о р ы е т о л ь к о с м о ж е т н а й т и . К а
к и м о б р а зо м L I N Q м о ж е т п о м о ч ь ему в п о и с к е с а м ы х д о р о г и х ко м и к с о в ?
С са й та ф а н а то в В е л и к о л е п н о го Д ж и м м и ска ча л с п и с о к в с е х в ы п у с к о в и п о м е с т и л и х в к о л л е к ц и ю
L i s t < T > о б ъ е к та Comic. Э т о т о б ъ е к т и м е е т два п о л я N a m e (Н а з в а н и е ) и I s s u e (В ы п у с к ).
c l a s s C o m ic {
p u b lic s tr in g Name { g e t; s e t; }
p u b lic in t Is s u e { g e t; s e t; }
}
Э т о и л >Л€,тод 5ыл цказан
как ст ат ический |л я т ого,
Д ля п о с т р о е н и я к а та л о га Д ж и м м и в о с п о л ь з о в а л с я и н и ц и а л а м и :
чтобы его м о ж н о оыао
p riv a te s ta tic IE n u m e ra b le < C o m ic > B u i l d C a t a l o g () ---------- - легко б м з б а т ь из м ет ода
точки входа консольного
{
приложений.
re tu rn new L is t< C o m ic > {
new C o m ic { Name = " J o h n n y A m e ric a v s . th e P in k o ", Is s u e = 6
ne w C o m ic { Name = "R o ck and R o ll ( lim it e d e d itio n ) " , Is s u e =^ 1 9 },
new C o m ic { Name = " W o m a n 's W o r k " , Is s u e = 36 },
new C o m ic ( Name = " H i p p i e M a d n e s s (m is p rin te d )", Is s u e = 57 },
ne w C o m ic { Name = " R e v e n g e o f t h e New W ave F r e a k (d a m a g e d )" , Is s u e = 68 },
new C o m ic { Nam e = " B l a c k M o n d a y " , I s s u e = 7 4 } , ---------
new C o m ic { Nam e = " T r i b a l T a t t o o M a d n e s s " , I s s u e = 8 3 } , 1
new C o m ic { N a m e = " T h e D e a t h o f a n O b j e c t " , I s s u e .= 9 7 } , }
Выпуск Ф74- комиксов
}
про капитана Великолепного
называется «Black Monday».
Д н а щ о М и я s a n j> o c a
П р о а н а л и з и р о в а т ь д а н н ы е , к о т о р ы е со б р а л Д ж и м м и , м о ж н о путе м е д и н с т в е н н о го запроса
L IN Q . П р е д л о ж е н и е w h e r e указы вает, к а к и е эл е м е н ты к о л л е к ц и и н у ж н о в к л ю ч и т ь в к о н е ч
н ы й результат. П р и это м м о ж н о н е о гр а н и ч и в а т ь с я п р о с т о й о п е р а ц и е й с р а в н е н и я , а в к л ю ч и т ь
л ю б ы е в ы р а ж е н и я и з С#. Н а п р и м е р , в о сп о л ь зо в а ть ся сл о в а р н ы м п о ле м v a l u e s для в к л ю ч е
н и я в результат к о м и к с о в , с т о я щ и х д о р о ж е $500. Затем п о л у ч е н н а я п о с л е д о ва те л ь н о с ть будет
упорядочена п р и п о м о щ и предлож ения o r d e r b y .
Запрос LINQ из-
^ влекает объекты
I Comic из пред-
IEnumerable<Comic> comics = BuildCatalog() ; ложенного списка,
^ нд основе данных
co mi c.N a m e , values[comic,Issue]);
m o S ___u..^^
m s у Г чi,AAj?uun
т о и м е н н о вош/о n
в результ ат ,
Т прЖ кило предложение select - запрос вернул
набор объектов Comic.
Результат:
H ip p ie M adness (с опечаткам и) стоит $ 1 3 ,5 2 5 .0 0
Johnny A m e r ic a v s. th e P in k o стоит $ 3 ,6 0 0 .0 0
W o m a n 's W o rk стоит $ 6 5 0 .0 0
676 глава 15
UNQ
вы ничего не
Д й ж е если
знаете об SQL. поводов для
беспокойства н е т — для
работы с LINQ вам не
поплребцюплся никакие со
Я знаю S Q L — именно на него пут ст вую щ ие сведения.
похожи запросы L IN Q , не так л и ?
Е с л и р а с с м о т р е т ь S Q L б олее д е та л ь н о , т о е го з а п р о с ы я в л я ю т с я о п е р а
ц и я м и над м н о ж е с т в а м и . Э т о о зн а ча е т, ч т о о б р а щ е н и е к с то л б ц а м т а б л и
ц ы н е уп о р я д о че н о . К о л л е к ц и и ж е , п р и своей сп о с о б н о с ти со хр а н я ть
что угодно—значения, с т р у к т у р ы , о б ъ е к т ы и т. п . — и м е ю т о п р е д е л е н н ы й
п о р я д о к . L I N Q п о з в о л я е т о с у щ е с тв л я т ь л ю б ы е о п е р а ц и и , к о т о р ы е п о д
д е р ж и в а ю т п р о и с х о д я щ и е в к о л л е к ц и и п р о ц е с с ы , — в ы м о ж е т е даж е в ы
з ы в а ть м е то д ы для с о д е р ж а щ и х с я в н у т р и о б ъ е к то в . П р о с м о т р эле м е н
т о в о с у щ е с тв л я е тс я ц и к л и ч н о , т о е сть в с т р о г о о п р е д е л е н н о м п о р я д к е .
Существуют и дру М о ж е т п о к а з а ть с я , ч т о все э т о н е и м е е т о с о б о го з н а ч е н и я , н о е сл и в ы
гие отличия UNO от
п р и в ы к л и р а б о та ть с S Q L , н а п и с а н н ы е в а н а л о г и ч н о й м а н е р е з а п р о с ы
мы не будем
вдаваться 8 подроб L I N Q дадут вам результат, д а л е к и й о т о ж и д а е м о го .
ности! Аостаточно
чтобы вы поняли, что
ожидать от
L NQ-запросов знако
мого вам поведения.
дальше ► 677
вот почему джимми любит LINQ
Все коллекции реализуют интерфейс
IEnumerable<T> — обратное неверно.
Д ля принадлежности к коллекции
Универсальность LINQ нужно реализовывать ещ е и интерфейс
ICollection<T> , то есть методы Add (),
В ы м о ж е те н е т о л ь к о и з в л е к а ть о тд е л ь н ы е эл е м е н Clear ( ) ,Contains ( ) ,СоруТо () и Remove ( ) ...
т ы ко л л е кц и и , н о и редактировать и х. С ге н е р и р о Разумеется, iCollection<T> расширяет
вав результат, L I N Q п р е д о с т а в л я е т н а б о р м е то д о в 1 Е п ш п е г а Ы е < т > . LINQ ж е работает с
для р а б о т ы с н и м . То е сть в ы п о л у ч а е те и н с т р у м е н последовательностями значений или объектов,
т ы для у п р а в л е н и я в а ш и м и д а н н ы м и . а не с коллекциями, а значит, вам требуется
объект, реализую щ ий 1 Е ш т е г а Ы е < Т > .
Отредактируем результаты запроса
Д о б а в и м в к о н е ц к а ж д о й с т р о к и в э то м м а сси ве д о п о л н и т е л ь н у ю
стр оку. В ы с о з д а д и т е н а б о р м о д и ф и ц и р о в а н н ы х с т р о к .
С п о м о щ ь ю м е то д а Т а к е () м о ж н о
■<т>.
с ф о р м и р о в а т ь п о д м н о ж е с т в о р е зульта то в:
h t t p ://m sd n 2 .m ic r o s o ft .c o m / e n -u s / v c s h a r p /a a 3 3 6 7 4 6 .a sp x
4acm°
ЧаДаБаеМые
B o II p o C jji
Так много новых слов: from ,
v a r u n d e rlO = Именно поэтому LINQ выглядит так стран
w here, o r d e r b y , s e le c t . . . как
fro m num ber in n u m b e rA rra y но с точки зрения С#. Ведь многочислен
будто совершенно другой язык. Почему
w h e r e n u m b e r < 10 ным операциям соответствует совсем
он так отличается от С#?
s e le c t n u m b e r; короткая запись.
КЛЮЧЕВЫЕ I
МОМЕНТЫ
f r o m ука зы в а е т I E n u m e r a b l e < T > , ч т о м ы s e l e c t указы вает, ч т о в х о д и т в к о н е ч н ы й
о сущ е ствл яе м за п р о с . За н и м всегда следует р е зул ь та т ( s e l e c t value).
им я перем енной, потом i n и им я входны х
T a k e позволяет п ол уч ить первы е несколько
д а н н ы х ( f r o m v a l u e i n values).
р е зул ь та то в за п р о с а ( r e s u l t s . Т а к е (10)).
w h e r e в об щ ем случае следует за from. Э т о Д ля к а ж д о й п о с л е д о в а т е л ь н о с ти в L I N Q
предл ож ение использует условны е опера с у щ е с тв у ю т и д р у ги е м е то д ы : M i n (), М а х (),
т о р ы C # д ля ф и л ь т р а ц и и э л е м е н то в ( w h e r e S u m () и A v e r a g e ( ) .
value < 10).
П р е д л о ж е н и е s e l e c t р а б о та е т н е т о л ь к о
o r d e r b y у к а зы в а е т п о р я д о к с о р т и с и м е н е м , с о з д а н н ы м в п р е д л о ж е н и и fr o m .
р о в к и результата. За н и м следует к р и С к а ж е м , е с л и з а п р о с в ы б и р а е т ц е н ы из
т е р и й с о р т и р о в к и и и н о гд а к л ю ч е в о е м а сси ва з н а ч е н и й i n t , к о т о р о м у в п р е д
с л о во d e s c e n d i n g ( o r d e r b y v a l u e л о ж е н и и fr o m п р и с в о и л и им я v a lu e ,
descending). стр о к у с ценам и м о ж н о вернуть так: s e l e c t
S trin g .F o rm a t("{0 :с }", v a lu e .
680 глава 15
UNQ
дальше ► 681
а вы поклонник LINQ?
но ^ ^ ^ ^ o z o T a ^ o c / l t N Q w ^ ^ головоломка,
8 };
J
<^аким: «from^badger in b a d g e T sT
var skunks
Эти операторы UNQ вы
j t l j L , ------------------ бирают из массива числа,
from см pigeon in b a d g e r ^ ^ ^ которые меньше SO и не
равны 3 6 . Зат ем к каж
После этого дому из них прибавляет
оператора п о ся 5, последовательность
следователь сортируется по убыванию
ность skunks со и помещается в новый
держит четыре обьект, на который ука -
числа: 4 6 , 1 3 / select \зывает ссылка skunks.
Ю и 8, pigeon + 5;
var bear
-
skunKs
\ .Take(3); I
i^4>^ks и помешаем
^оват ельност ^ Ь е ^гТ
var weasels
select beeGroup;
Т е п е р ь, ко гд а г р у п п ы г о т о в ы , и м и м о ж н о
у п р а в л я ть . Н а п р и м е р , п р и п о м о щ и п р е д л о
ж е н и я o r d e r b y у п о р я д о ч и т ь и х п о зн а че
н и я м п е р е ч и с л е н и я C u r r e n t s t a t e (Idle,
Теперь н уж но п р и пом ощ и клю че F l y i n g T o F l o w e r и т. п .). С т р о ч к а ord erb y
в о го сл о в а s e l e c t ука за ть, к а к о й beeG roup. Key с о р т и р у е т п о с л е д о в а те л ь н о
р е зульта т в о з в р а щ а е т за п р о с . В дан с т и п о клю чу, в к а ч е с тв е к л ю ч а в д а н н о м случае
н о м случае у ка зы в а е тся и м я г р у п п ы : будет и с п о л ь з о в а т ь с я с в о й с т в о C u r r e n t s t a t e .
s e l e c t beeG roup;
Тйк как пчелы группировались
по сосплоянию, и м е н н о его мы
укажем в качестве ключа.
j currentstate = FlyingToFlower
дальше ► 683
Сгруппируем результаты Д}кимАли
Д ж и м м и п о к у п а е т м н о го д е ш е в ы х к о м и к с о в , ч у т ь м е н ь ш е к о м и к с о в с р е д н е й ц е н о в о й к а т е г о р и и и н е
сколько д о р о ги х , и о н х о ч е т научиться о ц е н ива ть свои ф инансовы е в о зм о ж н о сти перед п о куп ко й .
О н п о м е щ а е т ц е н ы и з к а та л о га Г рега в п е р е ч и с л е н и е D i c t i o n a r y < i n t , i n t > п р и п о м о щ и м е то
да G e t P r i c e s О . В о сп о л ь зуе м ся L I N Q , ч т о б ы р а з б и т ь и х н а т р и гр у п п ы : к о м и к с ы с о с т о и м о с т ь ю д о
$100, со с т о и м о с т ь ю о т $100 д о $1 ,0 0 0 и к о м и к с ы , с т о я щ и е д о р о ж е Ь ,0 0 0 . М ы создад им п е р е ч и с л е н и е
P r i c e R a n g e , к о т о р о е будет и с п о л ь з о в а т ь с я в к а ч е с тв е к л ю ч а , и м е то д E v a l u a t e P r i c e ( ) , о п р е д е л я ю
щ и й цену и возвращ аю щ ий перечисление PriceRange.
fo re a c h (v a r g ro u p in p ric e G ro u p s ) {
C o n s o le .W rite ( " I fo u n d {0 } {1 } c o m ic s : is s u e s ", g ro u p .C o u n tО , g ro u p .K e y );
fo re a c h (v a r p r ic e in g ro u p )
C o n s o le .W rite (p ric e .T o S trin g 0 + " ");
C o n s o le .W rite L in d 0 ; /К
} Результат:
Каждая из групп является последа
ват елш ост ьн), поэт ому мы до Найдены 2 дорогих комикса: выпуски 6 57
бавили внутренний цикл foreach для Найдены 3 комикса по с р е д н е й цене: выпуски 19 36 68
просмотра цен внутри группы.
Найдены 3 д е шевых комикса: выпуски 74 83 97
UNQ
^ e ^ c r Б бассейне
= words.. .(2) ;
class Line {
public string[] Words;
foreach (var group in twoGroups)
public int Value;
{
public Line(string[] Words, int Value) {
int i = 0;
this.Words = Words; this.Value = Value;
К аж д ы й ф р а гм е н т
код а м о ж е т бы ть
и с п о л ь зо в а н from
з! to LineO
+ lines
select
- new
inside int
+= line Value
outside string
group Key
in - =
orderby var
un groups Words
by into
wordGroups words D
Key output
twoGroups this [1]
Value [2]
inner
дальше * 685
последний ребус в бассейне
L in e [] lin e s = {
new L i n e ( new s t r i n g [] " e a tin g " , "c a rro ts ,", "b u t", "e n jo y ", "H o rse s" } , 1 ),
new L in e ( new s t r i n g [] "ze b ra s ? ", "h a y", "C o w s", " b r id g e ." , "b o lte d " } , 2 ),
new L in e ( new s t r i n g [] "fo rk ", "d o g s !", "E n g in e ", "a n d " }, 3 ) ,
new L in e ( new s t r i n g [] " lo v e " , "th e y ", " a p p le s ." , " e a tin g " }, 2 ) ,
new L i n e ( new s t r i n g [] " w h is tle d ." , "B um p" }, 1 )
};
var words =
fro m line i n linp.^
gCQ UP l i n e by l i n e . Value
in to w o rd G ro u p s в соот вет ст вии ^ гр упп ы
o rd e rb y wordSroups. Key
s e le c t wordSroups;
Результат : Horses enjoy eating carrots, but they love eating apples.
686 глава IS
UNQ
ПредАО)кение join
Д ж и м м и со б рал ц е л ую к о л л е к ц и ю к о м и к с о в и х о т е л б ы с р а в н и т ь ц е н ы н а н и х с ц е н а м и в ка та л о ге Гре
га, ч т о б ы п о н я т ь , н е п е р е п л а т и л л и о н . Д ля з а п и с и с в о и х р а с х о д о в о н создал класс P u r c h a s e с двумя
а в т о м а т и ч е с к и м и св о й с тв а м и I s s u e и Price. П е р е ч е н ь к у п л е н н ы х и м к о м и к с о в н а х о д и тс я в к о л л е к ц и и
L i s t < P u r c h a s e > , к о то р а я назы вается p u r c h a s e s . К а к ж е ему те п е р ь о с у щ е с тв и ть с р а в н е н и е с ц е н а м и
и з ка та л о га Грега?
П р е д л о ж е н и е j o i n п о з в о л и т с к о м б и н и р о в а т ь д а н н ы е и з д в у х к о л л е к ц и й в е д и н ы й з а п р о с. Э т о дела
ется п уте м п о и с к а в п е р в о й к о л л е к ц и и с о в п а д а ю щ и х з н а ч е н и й с о в т о р о й . ( L I N Q делает э т о э ф ф е к ти в
н о - с р а в н и в а е т т о л ь к о те п а р ы , к о т о р ы е н у ж н о .) В к а ч е с тв е результа та в ы в о д я тс я с о в п а д а ю щ и е п а р ы .
Аанные Д ж « м м и
fS u d e коллекции объектов Purchase,
которая иазываетсй purchases.
П о с л е п р е д л о ж е н и я f r o m в м е с то к р и т е р и я
о т б о р а р е зульта то в н а п и ш и т е :
class Purchase {
jo in пате in c o lle c tio n
public int Issue
И м я пате н а з н а ч а е тс я чл е н а м , к о т о р ы е и з { get; set; }
в л е к а ю тс я и з о б ъ е д и н е н н о й к о л л е к ц и и н а public decimal Price
{ get; set; )
к а ж д о й и т е р а ц и и ц и к л а . З атем в ы и с п о л ь з у е
)
те е го в п р е д л о ж е н и и where.
Аж имми присоединяет
к комиксам из
коллекции purchases
список кцпленнш им
комиксов.
Д о б а в и м п р е д л о ж е н и е o n , ука
зы ваю щ ее сп особ о б ъ ед и нения
on comic.Issue к о л л е к ц и й . З атем ука ж е м и м я п е р
в о й к о л л е к ц и и , к л ю ч е в о е сл о во
e q u a ls p u r c h a s e .I s s u e
e q u a ls и им я в то р о й ко л л е кц и и .
После предложения
О З атем сл е д ую т п р е д л о ж е н и я
w h e r e и orderby. Т ак ка к в
select new в фигурных
скобках перечислены
данные,, которые сле
ре зул ьта т о б ы ч н о тр е б у е тс я дует вклночить в р е -
в к л ю ч и т ь ч а с т ь д а н н ы х из зильт ат.
о д н о й ко л л е кц и и , а часть из
s e l e c t new { comic.Name,
д р у г о й , в к о н ц е и с п о л ь з у е тс я
comic.Issue, p u r c h a s e .P r i c e }
предлож ение s e le c t new ,
Iqcmo = rI п я те = “Johnnv America" I Price = 3600
создаю щ ее п о л ь з о в а те л ь с к и й
н а б о р ре зул ь та то в п р и п о м о Issue = 1 ^ lame = “Rock and Roll” Price = 375
щ и а н о н и м н о го типа. 1 Issue = 57 name = “Hippie Madnless” Price = 13215 L
дальше ► 687
свой парень Джимми
s t a t i c IE n u m e ra b le < P u rc h a s e > F i n d P u r c h a s e s () {
L is t< P u rc h a s e > p u rc h a s e s = n e w L i s t < P u r c h a s e > () {
new P u rc h a s e 0 { Is s u e = 68, P r ic e = 225M }, 3a выпуск * S 7
new P u rc h a s e 0 { Is s u e = 1 9 , P r ic e = 375M } ,
W ^zxs.
new P u rc h a s e 0 { Is s u e = 6, P r ic e = 3600M },
new P u rc h a s e 0 { Is s u e = 57 , P r ic e = 13215M J
new P u rc h a s e 0 { Is s u e = 3 6 , P r i c e = 660M } ,
};
re tu rn p u rch a s e s ;
}
С делаем з а п р о с к э т о й , у ж е с в я з а н н о й с п р о гр а м
м о й базе д а н н ы х . L I N Q без п р о б л е м с к о м б и н и р у е т
д а н н ы е и з базы с д а н н ы м и , п о л у ч е н н ы м и о т о б ъ
е к то в .
С и н т а к с и с з а п р о с о в н е и з м е н и т с я ... вам п о т р е б у е т
ся в с е го л и ш ь п о л у ч и т ь д о с т у п к базе д а н н ы х .
690 глава 15
Откройте классы L IN Q и SQL в конструкторе O bject Relational
П р о гр а м м о й S q I M e t a l . е х е б ы л и с о з д а н ы к л а с с ы L I N Q t o S Q L . Э т и к л а ссы у м е ю т
п о с ы л а т ь з а п р о с к та б л и ц а м в а ш е й базы д а н н ы х и п р и э т о м р е а л и з у ю т и н т е р ф е й с
I E n u m e r a b l e < T > с ф у н к ц и е й , в о з в р а щ а ю щ е й д а н н ы е в таблицу.
И С Р о с н а щ е н а т а к и м з а м е ч а те л ь н ы м и н с т р у м е н т о м к а к к о н с т р у к т о р O b je c t R e la tio n a l.
З десь в ы м о ж е те у в и д е ть , к а к и е к л а ссы б ы л и с о з д а н ы п р о гр а м м о й S q l M e t a l . e x e .
Д в о й н о й щ е л ч о к н а и м е н и д о б а в л е н н о го к п р о е к т у ф айла C o n t a c t D B .d b m l о т к р ы в а
е т е го в к о н с т р у к т о р е O b je c t R e la tio n a l. В о т ч т о в ы п р и э то м у в и д и те :
ontaetDB.dbml
8 конструкторе Object
Relational вы увидите
класс PeoplCj созданный
—----- —------------------------------ при поАЛош,и программы
Peopfe
SqlMetal.exe. Он присоеди
няет к проект у таблицу
People с дазой данных и
S Properties возвращает данные при
f S * ContactID Create methods by помощи интерфейса
Nante
dragging items from 1ЕпитетЫе<т>, давая
Database Explorer возможность осущ ест
^ Company влять запросы UNQ.
onto this design
Ш Telephone surface
Ш Email
^ Client
^ LastCall
context. C o n ta c tD B c o n t e x t = new C o n ta c tD B ( c o n n e c t io n S tr in g ) ;
И споль - — .
зуйт е его Попрактикуйтесь с применением
свойство v a r p e o p l e D a t a = ключевых слов select new. Именно
People для они из всех данных базы выбира
получения fr o m p e r s o n i n c o n t e x t . P e o p le
ю т только информацию об имени
данных из s e l e c t n e w { p e r s o n . N a m e , p e r s o n . C o m p a n y } ; человека U фирме, в которой OH
таблицы работает. ^
People.
fo re a c h (va r p e rso n in p e o p le D a ta )
C o n s o le .W rite L in e ( " {0 } w o rks at { i: p e rs o n .N a m e , p e r s o n . C o m p a n y);
дальше ► 691
ключевое слово var
КЛЮЧЕВЫЕ
МОМЕНТЫ
Часзпо
Объясните, пожалуйста, что означает v a r .
^аД аБ аеМ ы е
Б о іп р о с ь і
Q ; Ключевое СЛОВО v a r решает сложную проблему, возни
кающую в LINQ. Обычно при вызове метода или выполнении
оператора сразу ясно, с каким типом данных вы работаете. Если вместо последней строчки написать:
К примеру, если метод возвращает значения типа s t r i n g ,
результат его работы нужно сохранить в переменную или поле s e le c t new
именно этого типа. { Nam e = с о т і с . N am e,
Iss u e N u m b e r = "# " + c o m ic .ls s u e };
А вот запрос LINQ может вернугь данные анонимного типа, га-
торый нигде не определен. Вы знаете, что это какая-то после запрос вернет данные анонимного типа с двумя членами — стро
довательность. Но тип содержащихся в ней объектов полностью кой Nam e и строкой Is s u e N u m b e r . Но определение класса
зависит от содержания запроса LINQ. Например, рассмотрим вот для этого типа в нашей программе отсугствует! При том, что
такой запрос:
переменная m o s t E x p e n s i v e должна быть объявлена как
принадлежащая к какому-то типу.
v a r m o s t E x p e n s iv e =
fr o m c o m ic i n c o m ic s Здесь на помощь приходит ключевое слово v a r , которое как
w h e re v a l u e s [ c o m i c . I s s u e ] , > 500 бы объясняет компилятору: «Это корректный тип, просто мы
o rd e rb y v a lu e s [ c o m ic .Is s u e ] пока не знаем, какой именно. Определи это, пожалуйста, само
d e s c e n d in g стоятельно».
s e le c t c o m ic ;
692 глава 15
Часвдо
я так и не понял, как работает при Всегда ли нужно добавлять файл
ЧаДаБаеМые . d bm l, создаваемый программой
ложение join. Б о їїр о с ь і
S q I M e t a l . е х е ? Я так и не понял,
зачем он нужен.
г ; Предложение j o i n работает с И ничто не мешает вам выбрать только
двумя последовательностями. Предпо имена игроков и размеры их футболок:
ложим, у вас есть коллекция футбольных : Если вы собираетесь писать запросы
игроков p l a y e r s . Ее элементами к Вазе данных SQL, без этого файла не
коаз
var results =
обойтись.
являются объекты со свойствами Name, from player in p l a y e r s
P o s i t i o n и N u m b e r . Выбрать where player.Number > 10 Помните, что LINQ требует от объ
игроков, на футболке которых номер join shirt in jerseys ектов реализации интерфейса
больше 10, можно запросом: on player.Number I E n u m e r a b l e < T > . Базы SQL не реа
equals shirt.Number лизуют вообще никаких интерфейсов, так
var results =
s e l e c t new { как не относятся к объектам. Поэтому для
from player in p l a y e r s
player.Name, запросов LINQ источник данных требуется
where player.Number > 10 преобразовать в объект.
s h i r t .S i z e
select player;
};
Вернитесь к только что написанному
У нас есть и коллекция j erseys, ИСР в состоянии самостоятельно разо коду, щелкните правой кнопкой мыши на
элементы которой обладают свойствами браться с результатом, который выдает объекте People и выберите команду Go
N u m b e r и Size. Чтобы определить запрос. При создании цикла, нумерующего to Definition. Это приведет вас к методу
размер футболки каждого игрока за результаты, сразу после ввода перемен доступа C o n t a c t D B .d e s i g n e r .
пишем: ной появится окно IntelliSense со списком. cs, возвращающему объект
Т а Ь 1 е < Р е о р 1 е > . Повторите процеду
var results = foreach (var г in results) ру для объекта Table. Вы увидите, что класс
from player in p l a y e r s г .
T a b l e < T E n t i t y > расширяет ин
where player.Number > 10 терфейс I Q u e r y a b l e < T E n t i t y > .
jo in shirt in jerseys Перейдя к определению этого интерфейса,
on p l a y e r . N u m b e r Equals вы обнаружите, что он реализует интер
e q u a ls shirt.Number I GetHashCode фейс I E n u m e r a b l e < T > .
select shirt; GetType
То есть файл . d b m l (и создаваемый
им файл кпасса .cs) обеспечивает нас
Ш Size
объекгом, реализующим интерфейс
Этот запрос даст мне множество ToString
( )утболок. А как быть, если меня не l E n u m e r a b l e . ИСР точно знает, как
волнуют номера игроков, но хотелось поступать с файлом . dbml: стенерировав
бы узнать размер футболки каждого его, добавив к проекту и отфыв в конструк
В списке присутствуют свойства N a m e торе Object Relational, вы увидите члены
из них?
и Size. Добавленные к предложению класса People, совпадающие с таблицей
s e l e c t дополнительные пункты тоже P e o p l e в базе данных. Этот класс при
^ ; Здесь вам пригодятся анонимные
появятся в этом списке. Это связано с тем, соединяется к SQL, автоматически читает
типы — в них можно положить любые
что запрос создает различные анонимные данные из таблиц и преобразует их в форму,
нужные вам данные. Они позволяют и вы
типы для различных членов. доступную для запросов LINQ.
бирать из объединенных коллекций.
694 глава 15
UNQ
s trin g c o n n e c tio n S tr in g =
"D a ta S o u r c e = | D a t a D i r e c t o r y 1W C o n t a c t D B . s d f '
C o n ta c tD B c o n te x t = new C o n ta c tD B (c o n n e c tio n S trin g ) ;
v a r r e s u lts = ^ S ta rb u zz с данныАМА из т а
SAUUjbt People-
fro m s ta rb u z z C u s to m e r in s ta rb u z z L is t
Предложение
Тирает из w h e re s ta r b u z z C u s to m e r . M o n e y S p e n t > 90 Член Pecwle к л а с
Sa3t>i данные са D a ta d o n text -
oS имени и j o i n p e r s o n i n c o n t e x t . P e o p le эт о коллекция,
ф и р м е, a из обеспечиваюи^ая
коллекции S t a r b u z z C u s t o m e r .N a m e e q u a l s p e r s o n . N a m e вас дост упом к
S ta rb u zz оан- т аблице People
в базе данных.
MoM напит ке s e l e c t n e w { p e r s o n . N a m e , p e r s o n . C o m p a n y ,
M объединяет M
их в р езу л ь -'^ ----------- s t a r b u z z C u s t o m e r . F a v o r i t e D r i n k } ;
т и р ую щ
f \
ую
^ *f-ZJ
П роверьт е результ а -
nocAedoda- 1 . . . . . ...
meAtiHocmb. me?/-
кЛЛ
ySedum ecbj
л
чт о
fo re a c h (v a r ro w in re s u lts ) все работ ает т а к /
"
как вы ' ожидали.
C o n s o le .W r ite L in e ( " { 0 } a t {1 } lik e s {2 }
ro w .N a m e , ro w .C o m p a n y , ro w . F a v o rite D rin k );
C o n s o le . R e a d K e y ( ) ;
Прекрасная работа... благодаря
рекламной акции наш бизнес
пойдет в гору. М и обязательно
позвоним вам снова.
е!«ся UNOP““' “ ^
скачать его здесь-
k t t p y / w w w - I H P “«“' ’ ' * * ' '
Эндрю Стиллмен, Д ж енниф ер Гоин
Изучаем С#
2-е издание
Перевела с английского И. Рузмайкина
ООО «Мир книг», 198206, Санкт-Петербург, Петергофское шоссе, 73, лит. А29.
Налоговая льгота — общероссийский классификатор продукции ОК 005-93, том 2;
95 3005 — литература учебная.
Подписано в печать 29.08.11. Формат 84x100/16. Уел. п. л. 72,24. Тираж 1500. Заказ 26278.
Отпечатано по технологии О Р в ОАО «Первая Образцовая типография»,
обосойленное подразделение «Печатный двор».
197110, Санкт-Петербург, Чкаловский пр., 15.