/
Tags: компьютерные технологии программирование
ISBN: 966—552—121—7
Text
Г, ШИААТ
k учебный
К) КУРС
>> ПРОГРАММИРОВАНИЕ
прекрасное пособие
по новому языку
программирования
G, CISBCIRNE Ев“ ргпппзр®
Г. ШИААТ
дм ргпппзт
Москва - Санкт-Петербург - Нижний Новгород - Воронеж
Ростов-на-Дону - Екатеринбург - Самара
Киев - Харьков - Минск
2003
Содержание
Предисловие ............................................................................................. .. 15
Структура книги ........................................................................................................... .. 15
Опыт программирования не обязателен ..................................................................... .. 16
Необходимое программное обеспечение .................................................................... .. 16
Нс забудьте: код доступен через Internet .................................................................... .. 16
Для дальнейшего изучения .......................................................................................... .. 16
Об авторе ....................................................................................................................... .. 17
Глава 1. Основы языка С# ........................................................................ ..19
Генеалогическое древо языка C11 ................................................................................ .. 20
Создание языка С — начало современной эры Программирования .................. .. 20
Появление ООП и создание языка С++ .............................................................. .. 21
Internet и появление языка Java ............................................................................. .. 22
История создания языка С# .................................................................................. .. 23
Связь С# с .NET Framework ........................................................................................ .. 24
Что такое .NET Framework ..................................................................................... .. 24
Как работает не зависящая от языка среда исполнения ..................................... .. 25
Управляемый и не управляемый коды .................................................................. .. 25
Общая языковая спецификация ............................................................................ .. 26
Объектно-ориентированное Программирование ........................................................ .. 26
Инкапсуляция .......................................................................................................... .. 27
Полиморфизм .......................................................................................................... .. 28
Наследование ........................................................................................................... .. 29
Первая простая программа .......................................................................................... .. 29
Компилирование из командной строки ................................................................ .. 30
Использование Visual C++ [DE ............................................................................. .. 31
Анализ первой программы ..................................................................................... .. 34
Обработка синтаксических ошибок ............................................................................ .. 37
Небольшое изменение программы .............................................................................. .. 37
Вторая простая программа ........................................................................................... .. 38
Другие типы данных ..................................................................................................... .. 40
Проект 1-1. Преобразование значений температуры 42
Два управляющих оператора ....................................................................................... .. 43
Оператор if ............................................................................................................... .. 43
Цикл for .................................................................................................................... -- 45
Содержание 7
Использование блоков кода ........................................................................................ .. 46
Символ точки с запятой и позиционирование .......................................................... .. 48
Использование отступов .............................................................................................. .. 48
Проект 1-2. Усовершенствование программы по преобразованию значения
температуры 49
Ключевые слова в языке C# .................................................................................. .. 50
Идентификаторы .......................................................................................................... .. 50
Библиотека классов С# ................................................................................................ .. 51
Контрольные вопросы .................................................................................................. .. 52
Глава 2. Типы данных и операторы .......................................................... ..53
Строгий контроль типов данных в С# ...................................................................... .. 54
Обычные типы данных ................................................................................................. .. 54
Целочисленные типы .............................................................................................. .. 55
Типы данных с плавающей точкой ....................................................................... .. 57
Тип decimal ............................... ............................................................................ .. 58
Символы ................................................................................................................... .. 59
Булев тип данных .................................................................................................... .. 60
Форматирование вывода ......................................................................................... .. 61
Проект 2-1. Разговор с Марсом 63
Литсралы ........................................................................................................................ .. 65
Шестнадцатеричные литералы ............................................................................... .. 66
Символ ьные езсаре-последовательности ............................................................... .. 66
Строковые литералы ............................................................................................... .. 67
Переменные и их инициализация ............................................................................... .. 69
Инициализация переменной .................................................................................. .. 69
Динамическая инициализация ............................................................................... .. 70
Область видимости и время жизни переменных ....................................................... .. 70
Операторы ..................................................................................................................... .. 74
Арифметические операторы ................................................................................... .. 74
Операторы сравнения и логические операторы ................................................... .. 76
Проект 2«2. Вывод таблицы истинности логических операторов 79
Оператор присваивания ............................................................................................... .. 81
Составные операторы присваивания ..................................................................... .. 82
Преобразование типа в операциях присваивания ................................................ .. 83
ВЫПОЛНСНИС операции ПрИВСДСНИБП ТИПИ MC)KJ1y НССОВМССТИМЫМИ ТИПИМИ ДИННЫХ.
Приоритетность операторов ........................................................................................ .. 86
Выражения .................................................................................................................... .. 86
Преобразование типов в выражениях ................................................................... .. 86
8 Содержание
Использование пробелов и круглых скобок ......................................................... .. 89
Проект 2-3. Вычисление суммы регулярных выплат по кредиту 90
Контрольные вопросы .................................................................................................. .. 93
Глава 3. Управляющие операторы ................................................. ....... ..94
Ввод символов с клавиатуры ....................................................................................... .. 95
Оператор if ..................................................................................................................... .. 96
Вложенные операторы if ......................................................................................... .. 97
Цепочка операторов if-else—if ................................................................................. .. 98
Оператор switch ............................................................................................................ .. 100
Вложенный оператор switch .................................................................................. ..104
Проект 3-1. Построение простой справочной системы С# 105
Цикл for ........................................................................................................................ ..107
Некоторые варианты цикла for ............................................................................. „108
Недостающие части Цикла for ............................................................................... ..109
Циклы, не имеющие тела ............................ ................................................ .. 111
Объявление управляющих переменных Цикла внутри Цикла for ........................... .. 111
Цикл while ................................................................................................................... .. 113
Цикл do—while .............................................................................................................. .. 114
Проект 3-2. Совершенствование справочной системы С# 116
Использование оператора break для выхода из Цикла ............................................ .. 118
Использование оператора continue ........................................................................... .. 121
Оператор goto .............................................................................................................. .. 121
Проект 3-3. Завершение создания справочной системы Cit 123
Вложенные циклы ....................................................................................................... ..12(›
Контрольные вопросы ................................................................................................. „128
Глава 4. Классы, объекты и методы ....................................................... .. 130
Основные понятия класса .......................................................................................... .. 131
Общий синтаксис класса ...................................................................................... .. 131
Определение класеа ............................................................................................... „132
Как создаются объекты ............................................................................................... „136
Переменные ссылочного типа и оператор присваивания ....................................... „137
Методы ......................................................................................................................... „138
Добавление метода K классу Vehicle ..................................... .. . . . . ...... ..139
Возврат управления из метода . . . . ‚ . . . . ‚ ‚ _ , , . _ _ , _ , _ , , , _ , _ __ 141
Возвращение методом значений.. .... .. 142
Использование параметров ........ .. . . . . . . . . . . . . . . . . . . . . . . . . ..145
Дальнейшее усовершенствование класса VehicIe.... .......
Содержание 9
Проект 4-1. Создание справочного класса 148
Конструкторы ............................................................................................................... .. 153
Конструкторы с параметрами ............................................................................... „155
Добавление конструктора к классу Vehicle .......................................................... ..155
Оператор new ............................................................................................................... ..157
Деструкторы и «сборка мусора» ................................................................................. ..157
Деструкторы ................................................................................................... .. 158
Проект 4-2. Демонстрация работы деструкторов 159
Ключевое слово this .................................................................................................... .. 161
Контрольные вопросы ................................................................................................. ..163
Глава 5. Подробнее о типах данных и операторах .................................. .. 164
Массивы ....................................................................................................................... „165
Одномерные массивы ............................................................................................ „165
Проскт 5-1. Сортировка массива 169
Многомерные массивы .............................................................................................. .. 171
Двухмерные массивы ............................................................................................ .. 171
Массивы, имеющие более Двух размерностей ..................................................... „173
Инициализация многомерных массивов ............................................................. „173
Нсвыровненньте массивы ........................................................................................... _.174
Присваивание ссылок на массив ............................................................................... „176
Использование свойства Length ................................................................................. ..177
Проект 5-2. Класс Queue 180
Цикл Гогеасп ................................................................................................................. „184
Строки .......................................................................................................................... „187
Создание объектов класса String .......................................................................... „187
Операции со строками ........................................................................................... „188
Массивы строк ....................................................................................................... ..190
Неизменность строк ............................................................................................... „190
Побитовые операторы ................................................................................................. ..192
Побитовые операторы AND, OR, XOR И NOT ................................................... „192
Операторы сдвига ................................................. .. ..197
Составныс побитовые операторы ......................................................................... .. 198
Проект 5-3. Класс ShowBits 199
Оператор '.’ .................................................................................................................... „201
Контрольные вопросы ................................................................................................. „204
Глава 6. Детальное рассмотрение методов и классов ........................... .. 205
Управление доступом к членам класса ...................................................................... „206
1 О Содержание
Модификаторы в С# .............................................................................................. „206
Проект 6-1. Усовершенствованный класс Queue 212
Передача объектов методу .......................................................................................... ..2l3
Как передаются аргументы .................................................................................... „214
Использование параметров с модификаторами ref и out ......................................... „216
Использование модификатора ref ................................... .. ...217
Использование модификатора out ........................................................ ..219
Использование переменного количества аргументов ............................................... ..221
Возвращение объектов ................................................................................................ „224
Перегрузка метода ....................................................................................................... ..226
Перегрузка конструкторов .......................................................................................... „232
Вызов перегруженного конструктора с использованием ключевого слова this..234
Проект 6-2. Перегрузка конструктора Queue 235
Метод Main() ................................................................................................................ „238
Возвращение значений методом Ма$п() .............................................................. „238
Передача аргументов методу Ма1п() ..................................................................... „239
Рекурсия ....................................................................................................................... „241
Ключевое слово static .................................................................................................. „243
Проект 6-3. Алгоритм Quicksort 246
Контрольные вопросы ................................................................................................. ..249
Глава 7. Перегрузка оператора, индексаторы и свойства ..................... .. 250
Перегрузка операторов ................................................................................................ „251
Синтаксис метода оператора ................................................................................. ..251
Перегрузка бинарных операторов ................. ..
Перегрузка унарных операторов ........................................................................... ..254
Дополнительные возможности класса ThreeD .................................................... ..258
Перегрузка операторов сравнения ........................................................................ ..262
Основные положения и ограничения при перегрузке операторов .................... ..264
индексаторы ................................................................................................................ „265
Многомерные индексаторы ................................. ............................................... ..270
Свойства ....................................................................................................................... „273
Ограничения в использовании свойств ............................................................... „276
Проект 7-1. Создание класса Set 277
Контрольные вопросы ................................................................................................. „286
Глава 8. Наследование ............................................................................ .. 287
Основы наследования .................................................................................................. „288
Доступ к членам класса при использовании наследования ............................... „291
Содержание 1 1
Использование модификатора protected .................................................................... „294
Конструкторы и наследование ................................................................................... „295
Вызов конструкторов наследуемого класса ......................................................... ..297
Скрытие переменных и наследование ...................................................................... „301
Использование ключевого слова base для доступа к скрытой переменной ...... „302
Проект 8.1. Расширение возможностей класса Vehicle 304
Создание многоуровневой иерархии классов ........................................................... „308
Когда вызываются конструкторы .............................................................................. .. 31 1
ССЫЛКИ на объекты наследуемого и наследующего классов ................................... „312
Виртуальные методы и переопределение .................................................................. „317
Для чего нужны переопределенные методы ........................................................ „319
Применение виртуальных методов ....................................................................... „320
Использование абстрактных классов ......................................................................... „324
Использование ключевого слова scaled е целью предотвращения наследования....328
Класс object .................................................................................................................. „329
Упаковка и распаковка ................................................................................................ ..33()
Контрольные вопросы ................................................................................................. „333
Глава 9. Интерфейсы, структуры и перечисления ................................. „334
Интерфейсы ................................................................................................................. „335
Реализация интерфейсов ....................................................................................... „336
Использование интерфейсных ссылок ...................................................................... „340
Проект 9.1. Создание интерфейса Qucuc 342
Свойства интерфейса .................................................................................................. ,.347
Интерфейсные индексаторы ....................................................................................... „348
Наследование интерфейсов ........................................................................................ „350
Явная реализация ........................................................................................................ „352
Структуры ..................................................................................................................... „354
Перечисления ............................................................................................................... ..356
Инициализация перечисления .............................................................................. „358
Указание базового типа перечисления ................................................................. „358
Контрольные вопросы ................................................................................................. ..359
Глава 10. Обработка исключений ........................................................... ..360
Ютасс Systcm.Exception ................................................................................................ „361
Основы обработки исключений ................................................................................. „362
Использование блоков try И catch ........................................................................ „362
Простой пример исключения .......................... .................................................. „363
Второй пример исключения .................................................................................. „364
12 Содержание
Если исключение не перехвачено .............................................................................. „365
Изящная обработка ошибок с помощью исключений ............................................. „367
Использование нескольких операторов catch ........................................................... „368
Перехват всех исключений ......................................................................................... „369
Вложенные блоки try ................................................................................................... „370
Генерирование исключений ....................................................................................... „371
Повторное генерирование исюпючения ............................................................... „372
Использование блока finally ....................................................................................... „373
Более близкое знакомство с исключениями ............................................................. „375
Часто используемые исключения ......................................................................... „376
Наследование классов исключений ........................................................................... ..377
Перехват исключений производного класса ............................................................. „379
Проект |0—1. Добавление исключений в класс Queue 381
Использование ключевых слов checked и unchcckcd ................................................ „383
Контрольные вопросы ................................................................................................. „387
Глава 11. Ввод/вывод ............................................................................. .. 388
Потоки ввода/вывода C# ............................................................................................ .389
Байтовые потоки и потоки символов ................................................................... „389
Предопределенные потоки .................................................................................... „389
Классы потоков ............................................................................................................ „390
Класс Stream ........................................................................................................... „390
Классы байтовых потоков ..................................................................................... „391
Классы потоков символов ..................................................................................... „392
Цвоичные потоки ................................................................................................... „393
Консольный ввод/вьявод ............................................................ .............................. „393
Чтение данных c консоли ...................................................................................... „394
Вывод данных на консоль ..................................................................................... „395
Класс Filestream И байт-ориентированный ввод/вывод в файлы ........................... „396
Открытие и закрытие файла ................................................................................. „396
Чтение байтов с помощью ючаеса FileStrcam ...................................................... „398
Запись в файл ......................................................................................................... „399
Ввод/вывод в символьные файлы .............................................................................. „401
Использование класса Strcamwritcr ..................................................................... „402
Использование класса StreamRcadcr .................................................................... „404
Перенаправление стандартных потоков .................................................................... ..405
Проект 1 1-1. Утилита сравнения файлов 407
Чтение и запись двоичных данных ............................................................................ ..409
Поток Binarywriter ................................................................................................. ..4О9
Содержание 1 3
Поток BinaryRe-uder ............................................................................................ ..410
Демонстрация двоичного ввода/вывода .............................................................. .. 41 1
Произвольный доступ к содержимому фаила ...................................... .................. „413
Преобразование числовых строк в их внутренние представления ......................... ..415
Проект 11.2. Создание справочной системы 419
Контрольные вопросы ................................................................................................. „425
Глава 12. Делегаты, события, пространства имен и дополнительные
элементы языка C# ................................................................................. .. 426
Делегаты ....................................................................................................................... „427
МНОгОаДРССНОСТЬ делегатов ................................................................................... ..431
Преимущества использования делегатов ............................................................. „433
События ............................................................................................................ „434
Широковещательное событие . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . „436
Пространства имен ...................................................................................................... „438
Объявление пространства имен ............................................................................ „439
Директива using ...................................................................................................... ..441
Вторая форма синтаксиса директивы using ......................................................... ..443
Аддитивность Пространств имен ........................................................................... ..444
Вложенные пространства имен ............................................................................. „445
Пространство имен, используемое по умолчанию ............................................. ..447
Проект 12-1. Помещение класса Set B пространство имен 447
Операторы преобразования ........................................................................................ ..450
Препроцессор ............................................................................................................... ..455
Директива препроцессора дбейпе .............................................................................. ..455
Директивы препроцессора fiif и ttendif ...................................................................... ..456
Директивы препроцессора #e1sc и #e1if.. 457
Директива препроцессора #цп(1еГ ............................................................................... ..459
Директива препроцессора #crror ................................................................................ ..460
Директива препроцессора #warning ........................................................................... ..460
Директива препроцессора #1йпе .................................................................................. ..460
Директивы препроцессора #region И #enc1region ....................................................... ..461
Атрибуты ....................................................................................................................... ..46 1
Атрибут Conditibnal ................................................................................................ ..461
Атрибут Obsolete ..................................................................................................... ..463
Небезопасный код ....................................................................................................... ..464
Краткая информация об указателях .......................... .. ...465
Ключевое слово unsafe ........................................................................................... „466
Ключевое слово fixed ............................................................................................. ..46?
1 4 Содержание
Идентификация типа во время работы программы ................................................. „468
Проверка типа с помощью ключевого слова is ................................................... ..469
Ключевое слово аз .................................................................................................. ..470
Ключевое слово typcof ........................................................................................... ..470
Другие ключевые слова ............................................................................................... „471
Модификатор доступа internal .............................................................................. ..47l
Кл ючсвос слово sizcof ............................................................................................ ..47|
Ключевое слово lock .............................................................................................. ..47l
Поле rcadonly .......................................................................................................... ..47I
Ключевое слово stackalloc ...................................................................................... ..472
Оператор using ........................................................................................................ „473
Модификаторы const и volatile .............................................................................. „473
Что делать дальше ........................................................................................................ ..473
Контрольные вопросы ................................................................................................. „474
Приложение. Ответы на контрольные вопросы ..................................... „475
Глава I: Основы язьтка C# .......................................................................................... ..476
Глава 2: Типы данных и операторы ........................................................................... „477
Глава 3: Управляющие операторы .............................................................................. ..479
Глава 4: Классы, объекты и методы ........................................................................... „482
Глава 5: Типы данных и операторы ........................................................................... „483
Глава 6: Детальное рассмотрение методов и классов ............................................... „486
Глава 7: Перегрузка операторов, инвексаторы и свойства ...................................... „490
Глава 8: Наследование ................................................................................................. „492
Глава 9: Интерфейсы, структуры и перечтисления .................................................... ..494
Глава I0: Обработка исключений ............................................................................... „496
Глава Н: Ввод/ вывод ................................................................................................... ..499
Глава 12: Делегаты, события, ктространства имен и доктолньттельпьпс эгясмснты
языка C# ....................................................................................................................... ..502
Алфавитный указатель .............. .......................................................... .. 504
Предисловие
За пару последних лет компьютерный мир несколько сошел с проторенного пути.
перейдя от концепции программирования для отдельных систем к создании) инте-
рактивного сетевого окружения. Многие годы между компьютерными языками,
операционными системами и средствами разработки велась борьба за рынок сбыта,
но для большинства из них «сетыо по-прежнему оставался один компьютер». Теперь
же перед современным языком программирования стоят совершенно новые задачи,
для решения которых и был разработан язык С#‚ являющийся следующей ступенью
в эволюции языков программирован ия. От своих предшественников он взял все еамос
лучшее, объединил новейшие разработки в области проектирования языков програм-
мирования. В первую очередь C# вобрал в себя лучшие качества С++ и Java — двух
наиболее широко используемых языков. Кроме того, в него включены такие инно-
вационные элементы, как делегаты (delegates) И индсксаторы (indexcrs). А поскольку
C# использует средства системы .Nct Framework, его код является в высшей степени
переносимым и может быть использован при разработке программных комплексов в
многоязыковой среде. Компоненты программного обеспечения, созданные с исполь-
зованием C#, совместимы с кодом, который написан на других языках, если он также
предназначен для .Nct Framework.
Основная задача книги — научить читателя основам программирования на Cit. Для
этого применяется пошаговый подход к изучению материала с использованием
большого числа примеров и вопросов для самоконтроля, е разработкой множества
проектов. Читателю необязательно иметь опыт программирования. Книга начинается
с описания таких элементарных операций, как компилирование и запуск С#—про-
грамм. Затем в ней обсуждаются ключевые слова, функции и конструкции, которые
собственно и составляют язык C#. Ознакомившись е изложенным здесь тматсриъълом,
вы получите полное представление об основных принципах программироват-пия на C#.
Важно понимать, что настоящее изданис является всего лишь отправной точкой.
Сад-программирование —— это не только ключевые слова и синтаксис, определяющий
язык. Здесь речь должна идти и об использовании библиотеки .Net Framework Class
Library, которая настолько обширна, что ее описание требует отдельной книги. Хотя
несколько определяемых в библиотеке классов и обсуждается в книге, но по причине
ограниченного ее объема о большинстве элементов библиотеки здесь дажс не
упоминается. Чтобы стать первоклассным программистом на Cit, библиотеку следует
изучить в совершенстве. Освоив данную книгу, вы получите необходимые знания и
mm освоения других аспектов языка Сп.
И последнее: си — это новый язык. Подобно всем новым компьютерным языкам,
он должен пройти период совершенствования и становления. Читателям, надо
полагать, будет интересно следить за появлением и развитием сго новых свойств и
возможностей. Нс удивляйтесь, если он претерпит некоторые изменения. Иеторня
языка Cit только начинается.
Структура книги
Книгу следует рассматривать как учебное пособие, где каждая последующая глава
базируется на предыдущей. Она состоит из I2 глав, в каждой из которых обсуждается
отдельный аспект языка Cit. A несколько специшьных приемов и элементов структуры,
16 Предисловие
призванных прочно закрепить изучаемый материал в памяти читателей, делают книгу
просто уникальной.
О Каждая глава начинается с перечисления рассматриваемых в ней тем.
О Завершается каждая глава разделом «Контрольные вопросы», позволяющим
читателю самостоятельно проконтролировать, насколько полно усвоен материю.
Ответы на задаваемые вопросы приведены в приложении.
О В концс каждого основного раздела главы имеется «Минутный практикум»,
предназначенный для проверки того, как читатель понимает излагаем ые подтоже-
ния. Ответы на задаваемые здесь вопросы приведены в нижней части страницы.
О Под рубрикой «Ответы профессионала» (оформлена в виде вопроса-ответа)
содержится дополнительная информация, а также интересные комментарии к
обсуждаемым темам.
О Каждая глава содержит один или более проектов, призванных продемонстриро-
вать, как на практике применить полученные знания. В основном это примеры
реального кода, которые можно использовать в качестве отправной точки при
разработке собственных программ.
Опыт программирования не обязателен
Книга не требует какого-либо опыта программирования. Даже если вам никогда
прежде не приходилось заниматься таковым, вы без труда освоите излагаемый здесь
материал. Но преобладающая часть читателей наверняка имеет хотя бы небольшой
опыт программирования, в первую очередь на языках С++ и Java. A C#. как вы
сможете убедиться, довольно тесно связан с обоими Этими языками. Таким образом.
если вы уже знаете С++ или Java, то гораздо быстрее изучите Cti.
Необходимое программное обеспечение
Для компилирования и запуска программ, приведенных в этой книге, вам понадо-
бится Visual Studio.NET 7 (или более поздняя версия), а также инсталлированная
система .NET Framework.
He забудьте: код доступен через Internet
Помните, что исходный код всех примеров и проектов, описанных в данной книге,
можно бесплатно загрузить с Web-y3na www.osborne.com.
Для дальнейшего изучения
Данная книга, как уже было отмечено, входит в большую серию книг о программи-
ровании, автором которых является Герберт Шилдт. Ниже приведен перечень изда-
ний, которые могут вас заинтересовать.
Желающим получить более полные сведения о языке C# рекомендуем приобрести
книгу
С д: The Complete Reference
Для тех, кто хочет изучить С++, особенно полезными окажутся следующие издания:
C + +: The Complete Reference
Предисловие 17
С++: А Beginner's guide
Teach Yourself C++
С++ from the Ground Up
S TL Programming from the Ground Up
The C/C++ Programming Annotated Archives
A чтобы изучить ]ауа—программирование, можно обратиться к изданиям:
Java 2: A Beginner's guide
Java 2: The Complete Reference
Java 2: The Programmers Reference
Тем, кто желает Научиться писать программы для Windows, МЫ рекомсндуем следую-
щис книги Герберта Шилдта:
Windows 98 Programming from the Ground Up
Windows 2000 Programming from the Ground Up
MFC Programming/rom the Ground Up
The Windows Programming Annotated Archives
Если же вы хотите изучить язык С, являющийся основой всех современных языков
программирования, обратитесь к книгам:
С: The Complete Reference
Teach Yourself C
Каждый, кому нужны более фундаментальные знания и полные ответы, может
обратиться к Герберту Шилдту, признанному специалисту в области программиро-
вания.
Об авторе
Один из самых популярных авторов книг по программированию, Герберт Шилдт,
считается ведущим специалистом по языкам С, С++, дама И Cit, а также экспертом в
п области создания программ для Windows. Написанные им книги по программированию
были переведены на многие языки и изданы общим тиражом более 3 миллионов
экземпляров. Герберт Шилдт является автором огромного числа изданий, ставших
бестселлерами. Наиболее известные среди них — это С++: The Complete Reference, Java 2:
The Complete Reference, Java 2: A Beginner's Guide, Windows 2000 Programming from the Ground
Up И С: The Complete Reference. ШИЛДТ имеет степень мастера компьютерных наук,
Присвоенную ему университетом штата Иллинойс. Связаться с Гербертом можно через
его консультационный офис по телефону (217) 548-4683.
Основы языка С#
CI
CI
CI
CICICICI
История создания языка С#
Использование среды .МЕТ Framework
Три принципа объектно-ориентирован-
ного программирования
Создание, компилирование и запуск
С#-программ
Использование переменных
Работа с операторами if И for
Использование блоков кода
Ключевые слова С#
20 Глава 1. Основы языка С#
Языки программирования служат самым разнообразным целям — от решения слож-
ных математических задач и проведения 3i<0HoMvIKO-i\1a'rC\Aa’1L1-iccimx расчетов до
создания музыкальной партитуры и машинной графики. Попытки создания совер-
шенного языка программирования предпринимаются столько же лет. сколько суще-
ствует само программирование. В результате этого поиска компанией Microsoft был
разработан язык Cit, соответствующий современным стандартам Программирования
и предназначенный для поддержки развития технологии .NET Framework. Этот язык
предоставляет эффективный метод написания программ для современных компью-
терных еистем предприятий, которые используют операционную систему Windows И
компоненты Internet. ЦСЛЬ нашей книги — помочь вам научиться программировать
на языке C#.
B зтой главе мы расскажем об истории создания языка Cit И опишем наиболее важные
его свойства. Безусловно, изучение этого языка представляет определенные трудно-
сти, поскольку все его элементы тесно взаимосвязаны и не могут рассматриваться
изолированно друг от друга. Чтобы справиться е зтой проблемой, в данной главе мы
кратко рассмотрим общую структуру Сгг-программ, а также основные управляющие
элементы и условные операторы языка, сфокусировав внимание на базисных кон-
цепциях программирования, общих для любой Сайт-программы.
Генеалогическое древо языка С#
Компьютерные языки взаимосвязаны, а не существуют сами по себе. Каждый новый
язык в той или иной форме наследует свойства ранее созданных языков, то есть
оеутцествляется принцип преемственности. В результате возможности одного языка
используются другими (например, новые характеристики интегрируются в уже суще-
ствующий контекст, а старые конструкции языка удаляются). Так происходит эво-
люция компьютерных языков и совершенствуется искусство программирования.
Язык Cit HC является исключением, он унаследовал много полезных возможностей
от других языков программирования и напрямую связан с двумя наиболее широко
применяемыми в мире компьютерными языками —— С и С++, а также с языком Java.
Для понимания С# следует разобраться в природе такой связи, поэтому сначъша мы
расскажем об истории развития этих трсх языков.
Создание языка С —— начало современной эры
программирования
Язык С был разработан Дэннисом Ричи, системным программистом из компании
ВсП Laboratories, город Мторрей-хигтл, штат Нью-Джерси, в 1972 году. Этот язык
настолько хорошо зарекомендовал себя, что в конечном счете на нем было написано
более 90 % кода ядра операционной системы Unix (которую поначалу писшти на языке
низкого уровня — ассемблере). К моменту появления С более ранние языки. самым
известным из которых является Pascal, ИСПОЛЬЗОВЭЛИСЬ достаточно успешно. но
именно язык С определил начало современной эры программирования.
Революционные изменения в технологии программирования, приведшие к появле-
нию структурного программирования I960-x годов, обусловили базовые возможности
для создания языка С. До появления структурного программирования трудно было
писать большие программы, поскольку с увеличением количества строк код программы
Генеалогическое дреео языка С# 21
превращался в запутанную массу переходов, вызовов и возвращаемых результатов, за
которыми сложно было проследить ход самой программы. Структурные языки
решили эту проблему, добавив в инструментарий программиста условные операторы,
процедуры с локальными переменными и другие усовершенствования. Таким обра-
зом появилась возможность писать относительно большие программы.
Именно язык С стал первым структурным Языком, в котором успешно сочетались
мощность, Элегантность и выразительность. Такие его свойства, как краткость и
легкий в использовании синтаксис в сочетании с принципом, согласно которому
ответственность за возможные ошибки возлагается на программиста, а не на язык,
быстро нашли множество сторонников. Сегодня мы считаем эти качества само собой
разумеющимися, но тогда в С впервые были воплощены великолепные новые
возможности, так необходимые программистам. В итоге с 1980-х годов С является
самым используемым языком структурного программирования.
Но по мере развития программирования появляется проблема обработки программ
все большего размера. Как только код проекта достигает определенного объема (его
числовое значение зависит от программы, программиста, используемых инструмен-
тов, но приблизительно речь идет о 5000 строк кода) возникают трудности в
понимании и сопровождении С-программьт.
Появление ООП и создание языка С++
В конце l970~x годов настгш момент, когда многие проекты достигли максимального
размера, доступного для обработки с помощью языка структурного программирова-
ния С. Теперь требовались новые подходы, и для решения этой проблемы было
создано объект}:о-ориентированное программирование (ООП), позволяющее програм-
мисту работать е программами большего объема. А поскольку С, являвшийся в то
время самым популярным языком, не поддерживал ООП, возникла необходимость
создания его объектно-ериентированной версии (названной позднее С++).
Эта версия была разработана в той же компании Bell Laboratories Бьярном Страуст-
рапом в начале I979 года. Первоначально новый язык получил название «С с
ючассами», но в 1983 году был переутмснован в С++. Он полностью включает в себя
язык С (то есть С служит фундаментом для С++) и содержит новые возможности.
предназначенные для поддержки объектно-ориентированного программирования.
Фактически С++ является объектно-ориентированной версией языка С, поэтому
программисту, знающему С, при переходе K программированию на С++ надо изучить
только новые концепции ООП, а не новый язык.
Долгое время язык С++ развивался экетенеивно и оставался в тени. В начале l990—x
годов он начинает применяться массово и приобретает огромную популярность, а к
концу десятилетия становится наиболее широко используемым языком разработки
программного обеспечения. лидирующим и сегодня.
Важно понимать, что разработка С++ нс является попыткой создания нового языка
программирования, а лишь совершенствует и дополняет уже достаточно успешный
язык. Такой подход, ставший новым направлением развития компьютерных языков,
используется и сейчас.
22 Глава 1. Основы языка С#
Internet И появление языка Java
Следующим большим достижением в развитии языков программирования Стал язык
Java. Работа над Java, который изначально назывался Oak, началась в I991 году в
компании Sun Microsystems. Основными разработчиками этого языка были Джеймс
Гослинг, Патрик Нотон, Крис Ворт, ЭД Франк и Майк Шеридан.
Java является структурным объектно-ориентированным языком с синтаксисом и
стратегией, взятыми из языка С++. Инновации Java были обусловлены бурным
развитием информационных технологий и стремительным увеличением количества
пользователей Internet, a также совершенствованием технологии программирования.
До широкого распространения Internet большинство написанных программ компи-
лировались для конкретных процессоров и определенных операционных систем.
И хотя программисты при написании удачной программы практически всегда задавались
вопросом повторного использования кода, этот вопрос не был первоочередным. Однако
с развитием Internet (то есть появлением возможности соединения через сеть компью-
теров с различными процессорами и операционными системами) на первый план вышла
именно проблема легкого переноса программ с одной платформы на другую. Для
решения эггой задачи необходим был новый язык, которым и стал Java.
Нужно отметить, что сначгша Java OTBOJII/IJIaCb более скромная роль, он создавался
как не зависящий от платформы язык, который можно было бы применять при
создании программного обеспечения для встроенных контроллеров. В |993 году стало
очевидным, что технологии, использовавшиеся для решения проблемы переносимо-
сти в малом масштабе (для встроенных контроллеров), можно использовать в
большом масштабе (для Internet). Самая главная возможность Java т способность
создания межплатформенного переносимого кода —~ и стала причиной быстрого
распространения этого языка.
В Java переносимость достигается посредством транслирования исходного кода
программы в промежуточный язык, называемый байт-кодом, который затем выпол-
няется виртуальной машиной Java (Java Virtual Machine, JVM). Следовательно,
Лама-программа может быть запущена на любой платформе, имеющей JVM. A no-
скольку JVM относительно легко реализуется, она доступна для большого числа
платформ.
Использование байт-кода в Java радикально отличается от применения кодов в языках
С и С++, практически всегда компилируемых в исполняемый машинный код.
Машинный код связан с определенным процессором и операционной системой,
следовательно, для запуска С/С++—программы на других платформах необходимо
перекомпилировать исходный код программы в машинный код каждой их Этих
платформ (то есть нужно иметь несколько различных исполняемых версий програм-
мы). Понятно, что это трудоемкий и дорогой процесс. А в Java было предложено
элегантное и эффективное решение — использование промежуточного языка. И это
же решение в дальнейшем было применено в Cit.
Уже говорилось, что в соответствии с новым подходом авторы Java создгши его на
основе С и С++ (синтаксис Java базируется на С, а объектная модель развилась из
С++). Хотя Зама-код не совместим с С или С++, его синтаксис сходен с синтаксисом
этих языков, поэтому большая часть программистов, использовавших С и С++,
смогла перейти на Java 6e3 особых усилий. Как Страустрапу при разработке С++, так
и авторам Java не понадобилось создавать совершенно новый язык, они использовали
Генеалогическое древо языка С# 23
в качестве базовых уже известные языки и смогли сосредоточить внимание на
инновационных элементах. Стоит отмстить, что после ктоявления Зама языки С и C++
ОТЕЛИ общепринятым фундаментом для создания новых компьютерных языков.
История создания языка С#
Хотя язык Java решил многие проблемы переносимости программ с одной платформы
на другую, все же для успешной работы в современном Птегпет-окружениът ему
недостает некоторых свойств, одним из которых является поддержка возможности
взаимодействия нескольких компьютерных языков (многоязыкового программиро-
ван ия). Под .MH020)'l3b/K08blJV! программированием понимается способность кода, напи-
санного на разных языках, работать совместно. Эта возможность очень важна при
создании больших программ, а также желательна при программировании отдельных
компонентов, которые можно было бы использовать во многих компьютерных языках
и в различной операционной среде.
Серьезным недостатком Java является отсутствие прямой поддержки платформы
Windows, являющейся сегодня наиболее широко используемой операционной систе-
мой в мире. (Хотя дама-программы могут выполняться в среде Windows при нгшичин
инсталлированной JVM.)
Чтобы решить эти проблемы, компания Microsoft в конце l990—x годов разработала
язык Cit (главный архитектор языка Андерс Хейльсберг), являющийся составной
частью обшсй стратегии .NET этой компании. Альфа-версия языка была выпущена
в ссрсдинс 2000 года.
Язык С# напрямую связан с широко применяемыми и наиболее популярными во
всем мире языками программирования С, С++ и Java. Сегодня практически все
профессиональные программисты знают эти языки, позтому переход к базирующе-
муся на них C# происходит без особых трудностей. Хейльсберг, так же как авторы
языков С++ и Java, нс «изобретал колесо», а пошел по проторенному пути —
используя в качестве фундамента ранее созданные языки, сосредоточился на улуч-
шениях и инновациях.
Генеалогическое древо C# показано на рис. Ll. Язык Ci? строится на объектной
модели, которая была определена в С++‚ а синтаксис, многие ключевые слова и
операторы он унаследовал от языка С. Если вы знаете эти языки программирования,
то у вас не возникнет проблем при изучении Cit.
Связь между Cit И Java более сложная. Оба языка разработаны для создания
переносимого кода, базируются на С и C++, используют их синтаксис и объектную
модель. Однако между Этими языками нет прямой связи, они больше похожи на
двоюродных братьев, имеющих общих предков, но отличающихся многими призна-
кам. Если вы умеете работать на Java, ЭТО облегчит вам освоение С#, и наоборот. при
изучении Java вам пригодится знание многих концепций С#.
Язык С# содержит многие инновационные свойства, которые будут рассматриваться
в зтой книге. Сразу заметим, что некоторые из его наиболее важных нововведений
относятся к встроенной поддержке компонентов программного обеспечения. То есть
фактически Св создан как компонентно-ориентированный язык, включающий,
например, элементы (такие как свойства, методы и события), непосредственно
поддерживающие составные части компонентов программного обеспечения. Но
24 Глава 1. Основы языка С#
пожалуй, наиболее важная новая характеристика Cit — способность работать в
многоязыковом окружении.
С++
Java C#
Рис. 1.1. Генеалогическое дреео С#
Связь С# с .МЕТ Framework
Хотя Ctr как язык программирования может изучаться изолированно, лучше рассмат-
ривать его во взаимосвязи с .ЛЕТ Framework — средой, в которой он работает. Потому
что. во-первьпх. С# изначально разрабатывался компанией Microsoft для создания
кода .ЛЕТ Framework, во-вторых, ЛЕТ Framework октределяет библиотеки, исполь-
зуемые языком C#. Поскольку они так тесно связаны, важно понимать общую
концепцию ЛЕТ Framework И ее значимость для C11.
Что такое .NET Framework
Отвечая на вопрос, вынесенный в заголовок, видимо, можно сказать, что ЛЕТ
Framework определяет среду, которая поддерживает развитие и выполнение платфор-
монезависимых приложений. Она допускает совместную работу в приложении раз-
личных языков программирования, а также обеспечивает переносимость программ
и общую модель программирования для Windows. Важно отметить, что ЛЕТ Егате-
work не ограничена платформой Windows И написанные для этой платформы про-
граммы могут быть в будущем перенесены на другие платформы.
Язык Cit использует две важные составляющие ЛЕТ Framework. Первая ч это не
зависящая от язьтка среда исполнения (Common Language Runtime, CLR), система,
управляющая исполнением вашей программы, и являющаяся частью технологии
ЛЕТ Framework, которая позволяет программам быть переносимыми, поддерживает
программирование с использованием нескольких языков и обеспечивает безопас-
ность передачи данных.
Вторая еоставлятощаэт — бибтиотека классов ЛЕТ, которая дает программе доступ к среде
исполнения, например, используется для ввода/вывода данных. Если вы начинающий
программист, то вам может быть не знаком термин юшсс. Подробно мы расскажем о
классах немного позже, а сейчас просто скажем, что классом является объектно-ориен-
тированная конструкция, которая помогает организовать программу. Пока ваша про-
грамма ограничена характеристиками, определенными библиотекой классов ЛЕТ, она
может быть запущена везде, где поддерживается среда исполнения ЛЕТ.
Связь C# c .NET Framework 25
Как работает не зависящая от языка среда исполнения
Не зависящая от языка среда исполнения (CLR) управляет выполнением кода .NET.
Расскажем, как она работает. При компилировании СФт-программьт мы получаем не
исполняемый код, а файл, содержащий специальный тип псевдокода, называемый
промежуточным языком Microsoft (Microsoft Intermediate Language, MSIL). ЯЗЫК
MSIL определяет набор переносимых инструкций, не зависимых от конкретного
процессора, то ссть, по существу, MSIL определяет переносимый язык ассемблера.
Обратим ваше внимание на то, что концептуально промежуточный язык Microsoft
похож на байт—код Java, HO между ними имеются различия.
Система CLR гранслируег промежуточный код в исполняемый во время запуска
программы. Любая программа, компилированная в MSJL, может быть запущена влюбой
операционной системе, для которой реализована среда CLR. Это часгь механизма, с
помощью которого в .NET Framework достигается переносимость программ.
Промежуточный язык Microsoft превращается в исполняемый код при иепол ьзованип
ЛТ-компилятора (англ. just in time —~ B нужный MoMeHr). Процесс работает следую-
щим образом: при выполнении ЛЕТ-программы система CLR актитъизирует‘ЛТ-ком-
пилятор, который затем превращает MSIL во внутренний код данного процессора,
причем коды частей программы преобразуются по мере того, как в них возникает
потребность. Следовательно, ваша СИТ-программа фактически исполняется как внуг-
ренний код, хотя изначально она компилировалась в MSIL. Это означает, что время
запуска вашей программы практически такое же, как если бы она была сразу
скомпилирована во внутренний код, но при этом у вас появляется преимущество
MSIL ~ переносимость программы.
Кроме того. при компилировании С#—программы в дополнение к MSIL Bbl получаете
еще один компонент — метаданные, которые описывают данные, используемые
вашей программой, и позволяют вашему коду взаимодействовать с другим кодом.
Метаданные содержатся в том же файле, что и MSIL.
B принципе этих знаний о среде CLR, MSIL И метаданных достаточно для реатизации
основной части задач программирования на языке Cit. поскольку этот язык само‹
стоятельно организует работу надлежащим образом.
Управляемый и не управляемый коды
В целом, когда вы пишите Сгг-програмьту, то создаете так называемый д/правляемиый
код, который выполняется под контролем не зависящей от языка среды исполнения
(CLR). Поскольку программа запускается под контролем CLR, управляемый код
должен соответствовать определенным требованиям, при выполнении которых он
получает множество преимуществ, включая современное управление памятью, спо-
собность совмещать языки, высокий уровень безопасности передачи данных, под-
держку контроля версии и понятный способ взаимодействия компонентов программ-
ного обеспечения. Требования, которым должен соответствовать управляемый код,
следующие: компилятор должен создать МБЦ-файл, предназначенный для С LR, a
также использовать библиотеки .NET Framework (vi то, и другое делает C#).
Альтернативой управляемому коду является не управляемый код, который не выпол-
няется CLR. До появления .NET Framework все Щнтоошз-программьт использовали
не управляемый код, сейчас же оба вышеназванных кода могут работать вместе, и
язык Cit, генерируя управляемый код, способен взаимодействовать c созданными
ранее программами.
26 Глава 1. Основы язьпка С#
Общая языковая спецификация
Все преимущества управляемого кода обеспечиваются CLR. Если же ваш код будет
использоваться программами, написанными на других языках, штя максимальной
совместимости необходимо придерживаться обшей языковой спецификации (Com-
mon Language Specification, CLS ). Эта спецификация описывает набор характеристик,
являющихся общими для различных языков. Соответствие кода обшей языковой
спецификации особенно важно при создании компонентов программного обеспсчс-
ния, которые будут использоваться другими языками. Если в будущем вам придется
создавать коммерческий код, то вы должны будете придерживаться спецификации
CLS, поэтому мы привели информацию о ней в данном разделе, хотя это и не имеет
непосредственного отношения к нашей книге.
C? ежата; арефаееианалам
C3 Bonpoc. Зачем для решения ВОПрОСОВ переносимости, безопасности и про-
граммирования с использованием нескольких языков нужно было создавать новый
компьютерный язык C#‘? Нельзя ли было адаптировать язык С++ для поддержки
.N ET Framework‘?
OTBeT. Да, язык С++ можно адаптировать так, чтобы он создавал .NET—c0—
ВМССТИМЫЙ код, запускаемый под управлением среды CLR. Первоначально компания
Microsoft так и поступила, добавив к С++ так называемые дяправляемькг расширения,
которые сделали возможным перенос существующего кода в .NET Framework. Но
новая разработка .N ET позволяет намного легче выполнить это в C#. Помните, что
язык Ci? специально разрабатывался для платформы .N ET И предназначен для
поддержки развития этой технологии.
‚..„.„.. ‚ я „„.‚.‚....‚< .....‚..-к-› жди.-
my Минутный адским/кум
1. С какими языками язык C# имеет непосредствснныесвязи?
2. Что такое не зависящая от языка среда исполнения (CLR)?
3. Что такос ЛТ-компилятор?
Объектно-ориентированное программирование
Язык C# базируется на принципах объектно—ориентированного программирования
(ООП), и все Сгг-программы являются в какой-то стспени объектно-ориентирован-
ными. Поэтому для написания даже самой простой Сдай-программы очень важно знать
принципы ООП.
ООП является мощной технологией выполнения задач, с которыми приходится
сталкиваться программистам. Со времен изобретения компьютера методы програм-
мирования значительно изменились. На протяжении развития компьютерной науки
1. Язык C# ЯВЛЯТСЯ потомком С и С++, а также имеет родство с Java.
2. He зависящая от языка среда исполнения (CLR) управляет выполнением .N ЕТ-программ.
3. ЛТ- компилятор преобразует MSIL-Kon во внутренний код данного процессора, причем коды
частей программы преобразуются по мере того, как в них возникает потребность.
Объектно-ориентированное программирование 27
специалистам в основном приходилось решать вопросы, связанные с увеличением
сложности программ. Например, на первых компьютерах программирование осуще-
стВЛЯЛОСЬ посредством изменения двоичных машинных инструкций, при этом ис-
пользовались элементы управления передней панели компьютера. Такой метод
хорошо работал, пока объем программы ограничивался несколькими сотнями инст-
рукций. Когда же объем используемого кода вырос, был изобретен язык ассемблера, и
программист, используя символическое представление машинных инструкции, уже смог
работать с более сложными и громоздкими программами. Затем, приспосабливаясь к
росту объема и сложности программ, программисты разработали высокоуровневые
языки, такис как FORTRAN И COBOL. Когда же эти ранние языки достигли предела
своих возможностей, было изобретено структурное программирование.
Заметьте, что на каждом этапе развития программирования создавались технологии
и инструменты, позволяющие программисту решать все более сложные Задачи.
С каждым шагом на этом пути новые технологии вбирали в себя все самое лучшее
от предыдущих. Настал момент, когда многие проекты приблизились к той границе,
где структурное программирование уже не могло соответствовать предъявляемым
требованиям, и возникла необходимость в принципиально новой прогрессивной
технологии, которой и стало объектно—ориентированное программирование.
ООП вобрало в себя лучшие идеи структурного программирования и объединило их
с несколькими новыми концепциями, в результате чего появился новый способ
организации программ. Не вдаваясь в подробности, можно сказать, что программа
составляется одним из двух способов: вокруг своего кода или вокруг своих данных.
При использовании технологии структурного программирования программы обычно
организуются вокруг кода. Такой метод можно рассматривать как «код, воздействую-
щий на данные».
В ООП программы работают иным способом. Они организованы вокруг данных, и
их ключевой принцип можно сформулировать как «контролируемый данными доступ
к коду». В объектно-ориентированном языке вы определяете данные, а также
процедуры, которые могут воздействовать на эти данные (то есть именно тип данных
задаст виды операций, которые могут быть применены к этим данным).
Для поддержки принципов объектно-ориентированного программирования у всех
языков ООП, включая C#, есть три общие черты ~ инкапсуляция, полиморфизм и
наследование. Давайте рассмотрим их в отдельности.
Инкапсуляция
Инкапсуляция — это механизм программирования, связывающий воедино код и
данные, которыми он манипулирует, а также ограждающий их от внешнего доступа
и неправильного применения. В объектно-ориентированном языке код и все необ—
‘(одимые данные могут связываться таким способом, что создается автономная
СТРУКТура — объект. Другими словами, объектом является структура, поддерживаю-
man инкапсуляцию.
В пределах объекта код, данные или и код, и данные могут быть либо закрытыми для
других объектов (рычаге), либо открытыми (public).
Закрытые код и данные известны и доступны из другой части только этого же объекта
(то есть к закрытому коду и данным не может быть осуществлен доступ из той части
ПР0Граммы, которая существует за пределами данного объекта). Когда же код и
28 Глава 1. Основы языка С#
данные открыты (объявлены в рамках какого-либо объекта как public). другие части
программы тоже имеют возможность с ними работать. Обычно части Объекта,
объявленные как public, используются для обеспечения контролируемого интер-
фейса с закрытыми элементами объекта.
В C# основной структурой, использующей инкапсуляцию, является класс, специфи-
цирующий как данные, так и код, который будет работать с этими данными. Язык
C11 применяет спецификацию классов для конструирования объектов, являющихся
экземплярами классов. Следовательно, mace фактически является набором инструк-
ций, в которых указано, как должен быть создан объект.
Код и составляющие класс данные называются членами юпаееа. а данные, определен-
ные классом, называются переменными экземпляра. Фрагменты кода, которые рабо-
тают с этими данными. называются методами гыена класса или просто метода/пи.
В C# термин метод принят для подпрограммы, которую в C/C*+ программисты
называют функцией (поскольку C# является прямым наследником С++, то иногда
используется и термин функция).
Полиморфизм
Полиморфизм (от греческого «множество форм») является свойством, которое по-
зволяет нескольким объектам, имеющим некоторые общие характеристики, получать
доступ к одному интерфейсу и реализовывать упомянутые в нем методы. В качестве
простого примера рассмотрим руль автомобиля. Руль (интерфейс) остается одинако-
вым независимо от того, какой тип рулевого механизма фактически используется. То
есть принцип работы руля всегда остается неизменным (например. поворот рулевого
колеса влево всегда приводит к повороту автомобшя влево), хотя на автомобиле может
быть установлено ручное управление или рулевое управление е усилителем. П реимуще-
етво ут-шфицированного интерфейса состоит в том, что если вы один раз научились
водить автомобиль, то сможете управлять любым типом автомобиля.
Тот же принцип может быть применен в программировании. Например, рассмотрим
стек —— список типа LIFO (last in, first out). Bbl можете работать с программой, которои
требуется три стека различных типов (один для целочисленных значений, один для
значений с плавающей запятой и один дшя символов). В этом случае для реализации
каждого стека используется один и тот же алгоритм, хотя хранящиеся данные
различны. В не объектно-ориентированном языке вам понадобилось бы создавать
три различных набора стековых процедур с собственным именем для каждого набора.
А в C# благодаря полиморфизму можно создать общий набор стековых процедур.
которые подойдут для работы со всеми тремя типами стеков. Таким образом.
определив методы, использующие один тип стека. вы сможете применять их и для
остальных типов.
Обобщенно концепцию полиморфизма можно выразить так: «один интерфейс —
множество методов». Это означает, что для группы похожих процессов можно создать
унифицированный интерфейс. Полиморфизм позволяет уменьшать сложность про-
грамм путем использования единого интерфейса для спецификации общего класса
действий. Применительно к каждой ситуации компилятор сам выбирает специфиче-
ское действие (то есть метод), избавляя вас от необходимости делать это вручную. Вы
ТОЛЬКО должны помнить, какие методы упомянуты в данном интерфейсе, и реализо-
вывать их.
Первая простая программа 29
Наследование
Наследование т это свойство, с помощью которого один объект может приобрстатъ
свойства Другого. При этом поддерживается концепция иерархической классифика-
ции, имеющей направление сверху вниз. Например. можно сказать, что класс
Вкусное Красноедчбтоко является частью класса 5/6.-wku, который в свою очередь
является частью класса (фрукты, принадлежащего классу продукптьк. То есть класс
продукты обладает определенными качествами (съедобные, питательные и так далее),
которые также применимы к его подктассу фрукты. В дополнение к этим качествам
класс фрукты имеет свои специфические характеристики (сочные, сзшдкие и так jlzncc),
ОТЛИЧЗЮШИС его от других продуктов. Ютасс ,</6..-7oKu определяет качества. ко горыс
присущи яблокам (растуг на дереве, не являются тропическими фруктами и так далее).
Класс BIcycHae_KpacHoe_;/ti-70K0 B свою очередь наследует все качества предшествующих
ютассов и определяет только те качества. которые делают его уникальным.
Без использования наследования каждый объект должен явно определять все свои
характеристики; используя наследование, объект должен определить только те качества,
которые делают его уникальным в пределах своего класса. Он можст наследовать общие
атрибуты от своих родительских классов. Следовательно. именно ‘механизм наслелотхаттття
дает возможность одному объекту быть специфическим экземпляром своего класса.
Минутные? практикум
1. Назовите принципы ООП.
2. Что является базовым элементом инкапсуляции’?
ежата: авефееелеяале-
Вопрос. Вы утверждаете, что ООП является эффективным способом управ»
ления большими программами, но создается впечатление, что он может излишне
усложнить относительно небольшие программы. Поскольку вы говорите, что все
СФг-программьт являются в какой—то степени объектно-ориентированными, не во3—
никнут ли неудобства при написании небольших Сд-програмтч?
Ответ. Нет, дальше вы увидите, что для небольших объемов кода испольжъ
вание ООП в С# практически не отражается на сложности программ. Хотя Ci? следует
строгой объектной модели. вы имеете достаточную свободу в определении степени
ее применения. Для небольших программ «объектная ориентированность» едва
ощутима, по мере увеличения объема программы в нее можно ин гетрироватъ большее
количество объектно-ориентированных свойств.
Первая простая программа
Перед ННЧЗЛОМ уГЛубЛСННОГО ИЗУЧ ИЯ ДСТНЛСЙ ЯЗЫКИ СКОМПИЛИРУСМ И 3'd[lyCTVl\l
КОрОТКуЮ И ПрОСТуЮ ПРОПЭЗММУ, ННПИСЗННУЮ на Си.
/*
Это простая программа, написанная на C#.
1. Принципами ООП являются инкапсуляция, полиморфизм и наследование.
2. БЗЪЗОВЫМ ЭЛСМСНТОМ инкапсуляции ЯШПЯСТСЯ КЛЗСС.
30 Глава 1. Основы языка Од
Файл, содержащий код программы, — Example.cs.
*/
using System;
class Example {
// С#—программа начинается с вызова метода Ма1п().
public stat;c void Main() i
Console.WriteLine("npocwaa C#—nporpaMMa.");
}
Существует два способа компилирования, запуска и редактировании: Стг-программ.
Во-первых, вы можете выполнить компиляцию из командной строки, запустив
программу csc . exe. BO-BTOpblX, можете использовать интегрированную среду разра-
ботки Visual C++ (Integrated Development Environment, IDE). Оба эти способа
описаны ниже.
компилирование из командной строки
компилирование из командной строки представляет собой более лсгкий способ
работы с большинством из приведенных в книге Cit-nporpamm, хотя для коммерче-
ских проектов вы, вероятно, будете использовать интегрированную среду разработки
Visual C++. Для компилирования и запуска Сд-программ из командной строки вам
необходимо произвести три действия.
1. Создать файл, содержащий код программы.
2. Компилировать программу.
3. Запустить программу.
Создание файла
Представленные в этой книге программы можно загрузить с Шор-узла компании
Osborne www.0sb0rne.c0m или при желании ввести код программы вручную. При
ручном вводе загрузите программу в файл, используя текстовый редактор (напруямер.
Notepad). ТСКСТОВЫС АЗСП-файлы должны быть созданы без элементов форматиро-
Baum, поскольку такая информация может привести к ошибкам при компилирова-
нии. После ввода программы присвойте файлу имя Example . C5.
компилирование программы
Для компилирования программы необходимо запустить компилятор C# csC.e:-ze,
указав имя исходного файла в командной строке:
C:\>csc Example.cs
Компилятор csc создает файл Example . exe, содержащий MSIL-Bcpcmo программы,
Хотя MSIL Нс является исполняемым кодом, он содержится в исполняемом (axe)
файле. При попытке запуска файла Example . exe не зависящая от языка среда
исполнения (CLR) автоматически активизирует ЛТ-компилятор. Но если вы nonm-
таетесь выполнить файл Example .exe (или любой другой файл, содержащим MSIL)
на компьютере, где не инсталлирована .N ET Framework, программа не будет Pfi6OTaTb,
поскольку отсутствует CLR.
Примечание _
Первая простая программа 31
›_›‚..›„- '\f.‘v\’)"-Jl „а
Возможно, перед запуском компилятора csc . exe вам нужно будет запустить код анд-
ный файл vcvars32 .ba1:, который обычно находится в каталоге \Pr0gram Files\Mic—
твой Visual Studio.NET\Vc7\Bin. B качестве альтернативы можно активизировать
процесс компилирования из командной строки, выбрав элемент Visual Studio.NET
Command Prompt из списка инструментов, приведенных в подменю Microsoft Visual
Studio.NET 7.0, находящемся в меню Пуск I Программы на панели задач.
„ты: .,-. ...:z«.~w-.1--6--: ‚ч: u:---»w¢.>:.-*.a:.--':::v»\.~:4.---.,.,3v-fie.-4-: ‚ <.v-e.<.(.«;-‘--::'=3:;..«’eme.=» .«.....,«:i~<.\-xzrxoz---=.~.e-.~::»..~u«e-2'.-;ai-.=.»-.-av ~wc.:~:.-:.w-.«_e.:-.,.::°~<-::~>«,c-,~s«-»- .. mu‘-r-«::>oc . '.:::: 2
Запуск программы
ДЛЯ запуска программы ВВСДИТС CC ИМЯ В КОМЭНДНОЙ строке, как ПОКЭЗЭНО НИЖСЗ
С: \>Example
Когда программа будет запущсна, на экране появится следующая строка:
Простая C#—uponpaMMa .
Использование Visual C++ IDE
Начиная с версии Visual Studio 7, Visual C++ IDE может компилировать С#—програм-
мы, Для редактирования, компилирования и запуска Сгг-программ с использованием
Visual C++ IDE вам необходимо произвести действия, перечисленные ниже. (Если
на вашем компьютере инсталлирована другая версия Visual C++, возможно, порядок
действий будет другим.)
1. Создайте новый пустой С#—проект‚ выбрав пункты меню File I New I Project. Далее
выберите папку Visual C# Project. a Затем пиктограмму Empty Project, как показано
на рисунке.
:" V: ='—:»: . - :== ’
vI5ua‘Ba5Ic Pmjects ` нрппьаъшг LUHUUILIUYGIY "
Visual C++ PrD]8CtS
е’ 53“ т" °°"‘°®““°“‘ ‘°'°’°“" ASP.NET Web ASP.NET web Web Ccintml
other p"31e':t5 Application Service Library
Visual Studio Solutions
‘ii Щ
Г”
Console
l\-_l
из.
цветами.
:-var--'°~‘ “ ' '
gsnaavxsatvwqigqsozmaxmgia.
flame‘ г . п; {Pioiectl I п
Гссидоштепгз and Settings\PeidaIka\MDM документы“ '
., „
32 Глааа 1. Основы язьпка оп
2. После создания проекта щелкните правой кнопкой мыши на имени проекта в _
окне Solution. B появившемся контекстном меню выберите пункт Add. затем пункт
Add New Item. Ha экране вашего монитора появится следующая картинка.
ДМЕТЦеЫЬпЁЬЁКЗгКРще д 7 7 И дав
_: {без MW, flan .. I 1
Efi Бди
RE_:huud
за Ge, „атм р t Add Neg Item. ‚ Add >
м»? г гщес s - _ _ _ ‚
whey; New Add Блюм; Item Add Brit‘-":0-E
§ Onlme. Community New Роща A-J0 ‘v’-fgb Flercven-:6
Ё г Headlmes Ё mid-W,.1dL..\,., Бот, ‚пир H-:qv:-:'
I Seam” быт", A-Jd Il’1l'rrZ‘V|[Ed Form . '
' ‘у’ Add Qser Control E
Д. Addtnbenzed сщшэ, ‚ 55‘
in“ d Add Compogent, X Remoxe
f" ` Add glass... ' Rename
мы?
Y lu6Pmiect2 - Microsoft Vi... _V
’ Pgopemes
3. Когда откроется диалоговое окно Add New Item, выберите пункт Local Project Items,
затем пиктограмму С# Code File. Изображение на экране ватного монитора будет
следующим.
Add New Item — Project!
‘:":".5:.
Ё] £1
Ё’ '-tlmdovws Form Class C-Jrnoornent
:_ Clais
а д ь Ц)
111 г › ‚ ‚ - „г ' э,
а < д в ’.'.,.-- . .
ё- ‘ :5 ЕЁ
User Control I’).-::ta Farm Data Set
*.r\«':z.ard
n
{в ‹›
ёдМд Schema
Первая простая программа
4_ Загрузите программу и сохраните файл под именем Example.Cs. (Помните. что
программы, приведенные в этой книге, можно загрузить с Wcb—y3.m www.05-
borne.c0m.) Ha экране вашего монитора появится следующее изображение.
':.:-.ggx
Project] мастак VisuaICx.HEI[desmn]-Example.cs* чл.‘ д‘ ‘ЪРц- ~'-3. «av. .-
` ` 1.30:; шпион назв
ч ъ Сгвсчиц
It ан ч
Refeyerxces
jj Ецтрьса
us на S‘-,'s!:em;
class Exeunpie ‘ " " ' _
‹ Ззыипопгщогег "“-%—
двигали: визах: '.-‘cud 11611.0
спал a:|
Cc-ns-Jle . Mr IEEL 1ne1"1'ID'Z‘C'I‘-33 UD|31"F'-3J‘ma
_ ;.:‘1a‘ ^
д I ‘
I Project] — Microsoft VI...
5. Скомпилируйте программу, выбрав пункт Build ИЗ меню Build.
о. Запустите программу„ выбрав пункт Start V ‘этом Debugging ИЗ менпо Debug.
Когда программа будет запущсна, появится окно, показанное на рисунке.
па Projecta — Microsoft Visual Е: мн [design] -Euampiau-r ‘д ъ д‘. ' ‚- г‘
Ё“ W
' кпманднаястроюа = = -I4 ж‘
: \Bo0ks\px~ug)Examp1e - axe
ростая программа _J
велите любой символ
‚1`1”‘.’&’Ё
вщы complete —— о errors, о uanunq:
suuumg sacellxce assemblxes ..
висевшее] .
шт ;;_.g_-gig „д, j
34 Глава 1. Основы языка С#
Для компилирования и запуска программ, приводимых в этой книге, не обязательно
каждый раз создавать новый проект, можно использовать тот же С#-проскт. Для этою
просто удалите текущий файл и добапзьте новый, затем Скомпилируйте его и запустите.
Как уже говорилось ранее, короткие программы, представленные в первой части
книги, проще компилировать из командной строки.
Анализ первой программы
Хотя программа Example . cs достаточно короткая, она включает несколько шпочс-
вых характеристик, свойственных всем СФг-программам. Давайте рассмотрим каждую
часть программы, начиная с ее имени, более внимательно.
В отличие от некоторых языков программирования (например, Java), в которых очень
важно правильно назвать файл, содсржащий код программы, C# ПОЗВОЛЯСТ выбрать
имя программы произвольно. Мы предложили назвать нашу программу Example . cs,
HO для C11 было бы приемлемо и любое другое имя. Например, предыдущий файл
программы можно Назвать Sample . Cs, Test .cs или Даже Х. cs.
По соглашению файлы С#-программ имеют расширение .cs. Но многие програм-
мисты называют файл по имени главного класса, определенного в рамках файла, вот
почему мы выбрали для файла имя Example.cs. Поскольку имя Стат-программы
выбирается произвольно, для большинства программ в этой книги мы не указали
имена файлов, предоставив сделать это вам.
Программа начинается со следующих строк:
/7:
Это простая программа, написанная на C#.
Файл, содержащий код программы, ~ Example.cs.
*/
ЭТИ строки являются комментарием. Cit, как и многие другие языки программиро-
вания, позволяет вводить комментарии в исходный код программы, при этом
компилятор игнорирует содержимое комментария. В комментарии, адресованном
тому, кто читает исходный код, описывается или объясняется операция, вьтполняемая
в программе. В данном случае комментарий описывает программу и напоминает, что
исходный файл должен быть назван Example. Cs. B приложениях комментарии
обычно объясняют, как работают определенные части программы или какие данные
содержатся в какой-либо переменной.
В С# поддерживаются три типа комментариев. Первый, показанный в еамом начале
программы, называется многострочньди колииентарием (то есть можст состоять из не-
скольких строк). Такой тип комментария должен находиться между символами / * и * /.
Весь текст, находящийся между этими символами, игнорируется компилятором.
Следующая строка программы
using System;
указывает, что программа использует пространство имен System. Подробнее о
пространстве имен мы расскажем далее, а сейчас коротко заметим, что пространство
имен обеспечивает способ хранения одного набора имен отдельно от другого. По
существу, имена, объявленные в одном пространстве имен, не конфликтуют с такими
же именами, объявленными в другом пространстве имен. Итак, данная программа
использует пространство имен System, зарезервированное пространством имен для
Первая простая программа 35
элементов, которые ассоциирован ы с библиотекой классов .N ET Framework. исполь-
зуемых C#. Ключевое слово using означает, что программа использует имена в
заданном пространстве имен.
РЗССМОТрИМ СЛСДУЮЩУЮ строку ПРОГРЭММЫ:
class Example (
B этой строке ключевое слово class указывает на то, что объявляется новый mace.
Уже говорилось, что класс является базовым элементом инкапсуляции в Си, вхдгт
р1е —- это имя класса. Определение класса начинается с открывающей фигурной
скобки ({) и заканчивается закрывающей фигурной скобкой (}). Элементы, находя-
щиеся между двумя скобками, называются членами класса. На этом этапе не слшпком
обращайте внимание на детали класса, учтите только, что в C# BCC Процессы
программы происходят в пределах класса. Это один из признаков объектно-ориен-
тированных программ, какими в той или иной мере являются все Сд-програзцмы.
Далее следует однострочный коламентарии:
// С#—программа начинается с вызова метода Main().
Это второй тип комментариев, которые поддерживаются C#. OH начинается с символа
// и заканчивается с окончанием строки. Принято общее правило: многострочные
комментарии программист использует для больших заметок, а Однострочньк: — для
коротких объяснений, не требующих много места.
СЛСДУЮЩаЯ строка КОДЭ ННЧИНЭСТ определение МСТОПЭ Ма1п () I
public static void Main() (
Как уже отмечалось, подпрограмма в С# называется методом. Все приложения на Ci:
начинают выполняться с вызова метода Main т). (Так же как программы на С/С++
начинают выполнение с функции main ().) Полное объяснение роли каждой части
этой строки мы сейчас дать не сможем, поскольку для этого необходимо понимание
тонкостей нескольких других свойств Cit, НО коротко рассмотрим эту строку, по-
скольку она будет использоваться в большинстве программ данной книги.
Ключевое слово public является модификатором (или спецификатором доступа).
Модификатор определяет, как другие части программы могут осуществлять доступ к
члену класса. Если перед членом класса находится ключевое слово pubL;C. доступ к
этому члену имеет код, определенный вне класса, в котором объявлен лгпптьпй члсп.
(НЭЗНЗЧСНИС, Противоположное модификатору public, иисет модификатор pri —
vate, ограждающий член класса от прямого использования кодом, который опреде-
лен вне его класса.) В этой программе метод Main () объявлен как publ;c, поскольку
при старте программы он будет вызван кодом, определенным вне его класса (в данном
случае непосредственно операционной системой).
Лрамечание
.-.v.>.U-2-V «м ‘-->\--1-.A-..x>.'..»« «мм ..: чае „:-.. ,-.
B то время, когда писалась эта книга, в С# фактически не требовалось объявлять
МСТОЦ Main() как public, НО он объявлен таким способом во многих примерах,
Приведенных в Visual Studio.N ET. Кроме того, так предпочитают действовать многие
(ПФ-программисты. Поэтому в данной книге мы использовали этот способ, по не
удивляйтесь, если в других книгах вам встретится иное объявл ().
„ъ .‚.. ._.... . „
m...-W...» ..
36 Глава 1. Основы языка C#
Ключевое слово static позволяет вызывать метод Main () до того, как будет создан
объект его класса, а поскольку метод Main () вызывается при старте программы, то
он обязательно должен быть статическим. Следующее ключевое слово void просто
сообщает компилятору, что метод Main () не возвращает значение. (Далее вы увидите,
что методы могут также возвращать значения.) Пустые круглые скобки, которые стоят
после слова Маш, указывают, что методу Маш () нс передается никакая информация.
(Далее вы увидите, что существует возможность передавать информацию методу
Main () или любому другому методу.) Последний символ в строке, символ открываю-
щей фигурной скобки ({), указывает начало кода мстода Main () . B€Cb КОД, составляющий
метод, помещается между открывающей и закрывающей фигурными скобками.
Приводимый ниже оператор находится внутри метода Ma1n ( ).
Console.WriteLine ("Простая C#-nporpamma . ") ;
Данный оператор выводит строку "простая C#—nporpa:v1Ma. ", затем отображается
новая пустая строка. Фактически вывод выполняется встроенным методом импе-
Line(), а информация, которая передается методу (в данном случае это строка
"Простая Стг-программа . "), называется apeymemnom. (B дополнение к строкам
метод WriteLine () также может использовать другис типы информации для вьт вода
на экран, которые будут рассмотрены далее.) Строка начинается со слова Console,
которое является именем встроенного класса, поддерживающего выполнение опера-
Ции ввода/вьтвода данных. Записывая имя класса Console И имя метода WriteLine ()
через точку, вы сообщаете компилятору, что метод WriteLine() является членом
класса Console. Использование класса для определения ввода с клавиатуры является
еще одним признаком объектной ориентированности языка C#.
Отметим, что оператор WriteLine () ; заканчивается символом точки с запятой, как и
оператор using System,', указанный ранее в программе. В C# все операторы
Заканчиваются этим символом. В нашей программе строки, не Заканчивающиеся
СИМВОЛОМ ТОЧКИ С ЗЭПЯТОЙ, ТСХНИЧССКИ НС ЯВЛЯЮТСЯ операторами.
Первая закрывающая фигурная скобка в программе завершает мстод Мани). а
последняя завершает определение класса Example.
и Ответы дрофессгтвадаш
Вопрос. Вы говорили, что C# поддерживает три типа комментариев, но
упомянули только два. Какой же третий тип?
Ответ. Третьим ти пом комментариев в Cii является ХМЬ-колямснтарий. В нем
используются теги XML для поддержки возможности создания саиодокументировгтн-
ного кода.
Учтите еще, что В языке C# различаются символы нижнего и верхнего регистров, поэтому
ошибка в регистре символа может привести к серьезным проблемам. Например, при
вводе main вместо Main или wrlteline вместо writemne программа не будет работать.
Более того, хотя классы, не содержащие метод Ма1п(), скомпилируются, они будут
выполнены после их инициализации в другом классе, содержащем этот метод. То есть
ЕСЛИ ВЫ BBCLICTC СЛОВО maln С МЭЛСНЬКОЙ буквы, КОМПИЛЯТОО ВСС pauno СКОМПИЛИруСТ
вашу программу. но будет выведено сообщение об ошибке, в котором говорится, что
программа E‘.xample.exe не имеет определенной точки входа.
Небольшое изменение программы 37
Минутныи драки/кум
1. С какого метода начинается выполнение С#-программыЁ’
2. Какие действия выполняет метод Console.WriteL;ne()?
3. Как называется программа-компилятор, Запускаемая из командной строки?
Обработка синтаксических ошибок
Если вы еще этого не сделали, то введите, Скомпилируйте и запустите рассмотренную
программу. Наверное, вам известно, что при вводе кода можно допустить ошибку.
Если введены некорректные данные, компилятор, сделав попытку скомпилировать
код программы, сообщит о синтаксической ошибке. Компилятор C# l'lbIT'c1CTC$l ПОНЯТЬ
ваш исходный код в зависимости от наличия открывающих и закрывающих скобок
или признака завершения оператора, поэтому сообщение об ошибкс не всегда
отражает истинную причину этой ошибки. Например, если в рассмотренной про-
грамме будет пропущена открывающая фигурная скобка после имени метода Main () ,
то при компилировании предыдущего файла из командной строки будет сгенсриро-
вана приведенная ниже последовательность сообщений об ошибках. (Подобные
сообщения об ошибках также генерируются при компилировании с использованием
интегрированной среды разработки IDE.)
Example.cs(l2,28): error CSlOO2: ; expected
Example.cs(l3,22): error CSlSl9: Invalid token '(' in class, struct, or
interface member declaration
Example.cs(l5,l): error CSlO22: Type or namespace definition. or end of
file expected
Очевидно, что первое сообщение об ошибке совершенно ошибочно, поскольку
пропущена фигурная скобка, а не точка с запятой. Следующие два сообщения тоже
сбивают с толку.
Все это мы рассказываем для того, чтобы вы поняли, что не стоит полагаться на
информацию, изложенную в сообщении об ошибках. поскольку она может быть
неверной. Кроме того, необходимо проверить несколько строк, предшествующих той.
номер которой указан в сообщении об ошибке, поскольку иногда сообщение об
ошибке появляется только через несколько строк после нее.
Небольшое изменение программы
Хотя все программы в этой книге используют оператор
uslng System;
на самомтделе В начале первой програмады В нем нет технической необходимости.
В Cit всегда можно полностью опредети/пь имя класса (или метода), указав простран-
ство имен, к которому класс (или метод) принадлежит. Например. строка
Соп5о1е.Иг1сеЬ1пе("Простая С#—программа.");
1. СФФ-программа начинает выполняться с вызова метода Main () .
2. Метод Console .WriteLine () выводит информацию на монитор.
3. Программа-компилятор, Запускаемая из командной строки, называется csc. exe.
38 Глава 1. Основы языка С#
может быть переписана следующим образом:
System.Console.WriteL1ne("HpocTaH С#—программа."):
Следовательно, первый пример можно записать так:
// В этой версии программы не используется оператор
// using System;.
class Example (
// С#—программа начинается c выполнения метода Main().
public static void Main() { fkmnoeonmmewnnemcwmm
Console.WriteL1ne(L
// Здесь метод Console.WriteLine() определен полностью.
System.Console.WriteLine("HpocTaH C#—nporpaMMa.");
)
Поскольку весьма утомительно каждый раз указывать идентификатор System, когда
используется член этого пространства имен, большинство С#-программиетов все
свои программы начинают с оператора using System,'. Но важно понимать. что при
необходимости вы можете определить имя класса или член ютасса явно, указав
использование пространства имен, к которому принадлежит данный класс.
Вторая простая программа
Возможно, оператор присваивания является одним из наиболее важных в программи-
ровании. Переменная — это именованная ячейка памяти, которой присваивается
значение. И это значен ие может быть изменено на протяжении выполнения программы.
То есть содержимое переменной является изменяемым, а не фиксированным.
В следующей программе создаются две переменные, x и у:
// Эта программа демонстрирует использование переменных.
using System;
class Example2 {
public static void Main() (
int х; // В этой строке кода объявляется переменная. 0бЪЯШЮНИС!ШРСМСННЫХ
int y; // B этой строке кода объявляется еще одна переменная.
В этой строке кода (переменной х
X д 100‘ // В этой ст оке ко а пе еменной x<—>
' ‘ р д р присваивается значение 100.
// присваивается значение 100.
Соп5о1е.Иг1ЕеЬ1пе("Переменная х содержит значение " + х);
y = х / 2;
Console-WriteLine("3HaqeHMe переменной y вычисляется");
Console.Write("c помощью выражения х/2 и равно ");
Console.WriteLine(y);
Вторая простая программа 39
ВЫПОЛНИВ программу, ВЫ УВИДИТЕ на ЭКРЦНС MOHHTODH CIIC.-'1)/l()lL1CC C()()'(’)U[CH[-‘I02
Переменная х содержит значение 100.
Значение переменной у вычисляется
с помощью выражения х/2 и равно 50.
В этой программе представлены новые элементы языка С#. Так, оператор
int X; // B этой строке кода объявляется переменная.
объявляет переменную целочисленного типа с именем х. В C# все переменные
должны быть объявлены перед их использованием. Тип значений, которые может
хранить переменная должен быть специфицирован. Принято говорить, что указыва-
ется тип переменной. В приведенной вьпде программе переменная х может храпит ь
Целочисленные значения. В С# для объявления переменной целочисленного типа
перед именем переменной необходимо поместить ключевое слово шк. Следователь-
но, оператор int x; объявляет переменную с именем х, имеющую тип 1 nt.
B следующей строке кода объявляется вторая переменная, у:
int y; // B этой строке кода объявляется еще одна переменная.
Здесь используется тот же формат объявления, который применялся для переменной
х, но имя переменной другое.
В большинстве случаев для объявления переменной используется оператор с таким
синтаксисом:
type var—name;
Здесь слово type указывает тип объявляемой переменной, а var~name — имя
переменной. Кроме типа int B C11 поддерживается несколько других типов данных.
В следующей строке кода переменной х присваивается значение I00:
х = 100; // В этой строке кода переменной х присваивается значение 100.
В С# оператор присваивания обозначается символом знака равенства. Он копирует
значение, находящееся справа от него, в переменную слева от него.
Следующая строка кода выводит значение переменной х, персд которым помещается
ll
строка "Переменная х содержит значение
Console.WriteLine("HepeMeHHaH X содержит значение " + х);
При выполнении этой строки кода в результате применения оператора + ЗНЭЧСНИС
переменной X будет выведено на экран послеспроки "Переменная X СОДЭРЖИТ
Значение Это значит, что, используя оператор +, можно в пределах одного
оператора WriteLine (); связать вмсстс сколько угодно элементов. В слсдуЮШСЙ
строке кода переменной у присваивается значсние переменной х, деленное на 2.
Y = х/2;
То есть значение переменной х делится на 2 и результат помещается в переменную
Таким образом, после выполнения оператора переменная у будет иметь значсние 50-
Значение переменной х останется неизменным. Как и многие другие языки Програм-
мирования, С# поддерживает полный набор арифметических операторов:
+ Сложение
— вычитание
' Умножение
/ Деление
40 Глава 1. Основы языка С#
Рассмотрим следующие три етроки кода программы:
Соп5о1е.Иг1СеЬ1пе("Значение переменной у вычисляется ");
Console.Write("c помощью выражения х/2 и равно ");
Console.WriteLine(y);
Здесь встречаются новый элемент языка и новый вариант использования переменной.
Новый элемент —— встроенный метод Write () , который используется для вывода на
экран строки "с помощью выражения х/2 и равно". За Этой строкой не следует
новая строка. Это означает, что когда будет сгенерирован следующий вывод, он будет
помещен в конец ЭТОЙ же строки. Метод инеем похож на метод WriteL1ne () . за
исключением того, что он не выводит новую строку после каждого вызова.
Новый вариант применения переменной — самостоятельное использование пере-
менной у в вызове метода Writ:eLine () . B C# для вывода Значений любых встроен-
ных типов может использоваться как метод Write () , так и метод выведите () .
Теперь рассмотрим еще одну возможность объявления переменных. В С# можно
объявлять две переменные и более, используя один и тот же оператор объявления.
Для этого нужно просто разделить имена переменных запятыми. Например, пере-
менные х и у можно объявить таким образом:
int x, y; // Для объявления обеих переменных
// используется один оператор.
Другие типы данных
В предыдущей программе была использована переменная типа int, которая может
хранить только Целочисленные значения и не может быть использована для хранения
числа с дробной частью. Ранее мы уже говорили, что кроме типа int C# поддержи вает
Несколько других типов данных. Для чисел с дробной частыо в С# определены два
типа, float И double, которые представляют числа с обычной и двойной точностыо
соответственно. Чаще всего используется тип double.
Для объявления переменных типа double используется оператор, синтаксис которого
представлен ниже:
double result;
Здесь слово result: ~— это имя переменной, имеющей тип double, позволяющий
работать е числами е плавающей точкой (например, она может хранить значение
122.23, 0.034, или -19.0).
Чтобы лучше понять различие между типами 1:1: И dauble. ВЫПОЛНИТС следующую
программу:
/я
В этой программе демонстрируется различие между типами.
int И double.
*/
using System;
class Example3 {
public static void Main() {
Другие типы данных 41
inc ivar; // B этой строке кода объявляется переменная типа int.
double dvar; // B этой строке кода объявляется переменная типа double.
ivar = 100; // Переменной ivar присваивается значение 100.
dvar = 100.0; // Переменной dvar присваивается значение 1ОО.О.
Соп$о1е.Иг1сеЬ1пе("Первоначальное значение переменной 1маг: " + ivar);
+ очаг):
Соп$о1е.Иг1ЕеЬ1пе("Первоначальное значение переменной dvar:
Console.WriteLine(); // Выводит пустую строку. Вышштшшойспюки
// Значения обеих переменных делятся на 3.
ivar = ivar / 3;
dvar = dvar / 3.0;
Соп5о1е.Иг1сеЬ1пе("Значение переменной ivar после деления: " + ivar);
Сопзо1е.Иг1сеЬ1пе("Значение переменной dvar после деления: “ + dvar);
}
}
Ниже показан результат работы этой программы:
Первоначальное значение переменной ivar: 100
Первоначальное значение переменной dvar: 100.0
Значение переменной ivar после деления: 33
Значение переменной dvar после деления: 33.3333333333333
Как видите, после деления значения переменной ivar на 3 результатом является целое
число 33, то есть дробная часть уграчена. А при делении значения переменной dvar Ha
3 дробная часть результата сохраняется. Как Показано в программе, при необходимости
ввода значения с плавающей точкой, она должна быть указана. В противном случае
значение будет интерпретировано как целочисленное. Например, в C# значение 100
является целочисленным, а |О0.О — значением с плавающей точкой.
Заметьте также, что для вывода на Экран пуетой строки нужно вызывать метод
Writ:eLine () без аргументов.
‚. ....v....:..~.=_:.«.-.M»:w-=
~0me9m/ драфесшвяаде ннннн и
Вопрос. Почему в С# для целочисленных значений и значений с плавающей
точкой существуют различные типы данных?
Ответ. С# поддерживает различньтс типы данных для повышения жрфектьнз-
ности программ. Например, операции над целочислснньпми значениями производят-
ся быстрее, чем над числами с плавающей точкой. Поэтому, если вам не т-хужнь!
дробные значения чисел, то нет необходимости производить вьтчьтслеътькя с излишней
степенью точности, то есть работать с такими типами данных, как float И double.
Кроме того, размер памяти, необходимый для хранения одних типов данных. может
быть меньшим, чем размер памяти LUIS! хранения других. Предусматривал различные
типы данных, С# позволяет эффективно использовать системные ресурсы. Учтите
также, что некоторые алгоритмы требуют использования специфических типов данных
(по крайней мере, работают при этом более захректьявно).
‚ ‚ . .... .- :'ъ‹и"-ч- е. .. .. ‚..‚„;„„ ._,_-,;.. :„„ ::—-чмъ—А
_:» ..«.-m~..«..‘..,....«.
г= нанесем-во лыж Av-t=t«~-;—.-.-.-....Mr „тмин -„...„„:-„‚„с- у _ _ _. _ .„
42 Глава 1. Основы языка С#
Проект 1-1. Преобразование значений
температуры
Ё FtoC.cs
Хотя предыдущие примеры программ и демонстрируют некоторые важные свойства
языка С#‚ на практике такие программы не слишком полезны. Несмотря на то. что
на данном этапе ваши знания языка С# довольно скудные, вы все же можете
применить их на практике. В этом проекте мы создадим программу, преобразующую
значение температуры по шкале Фаренгейта в значение по шкале Цельсия.
В программе объявляются две переменные типа double. B одной из них будет
храниться значение температуры по щкгше Фаренгсйта, а в другой -— значенис по
шкгше Цельсия, полученное после преобразования. Возможно, вы знаете, что для
выполнения этого преобразования необходима следующая формула:
C=5/9*(F~32)
где С — значение температуры по щкалс Цельсия (в градусах), а F — значение
температуры по щкгше Фаренгейта (в градусах).
Пошаговая инструкция
l. Создайте новый Сад-файл с именем Рсос. cs. (Если компиляция программы
производится не из командной строки, а с помощью Visual C++ IDE, необходимо
Добавить этот файл к С#-проекту. как было описано ранее.)
2. Введите следующую программу в файл:
/1‹
Проект 1-1
Эта программа ВЫПОЛНЯЗТ преобразование значения температуры ПО шкале
Фаренгейта В значение температуры ПО шкале Цельсия.
Назовите файл FtoC.cs.
*/
using System;
class FtoC (
public static void Main() (
double f; // Содержит значение температуры по шкале Фаренгейта.
double C: // Содержит значение температуры по шкале Цельсия.
f = 59.0; // Переменная f получает значение 59
// (градусов по Фаренгейту).
// Далее выполняется преобразование имеющегося значения
// В значение температуры по шкале Цельсия.
с = 5.0 / 9.0 ‘ (f ‘ 32.0);
Console.Write(f + " градусов по шкале Фаренгейта равны "):
Console.WriteLine(c + " градусам по шкале Цельсия.");
Два управляющих оператора 43
3. Скомпилируйте программу, используя Visual C++ IDE (следуя ранее дшшым
инструкциям), или введите в командной строкс следующую команду:
C>csc FtoC.cs
4. Запустите программу из Visual C++ IDE или из командной строки, для чего
введите после приглашения имя главного класса (содержащего метод mam):
C>FtoC
B результате работы программы будет выведена следующая строка:
59 градусов по шкале Фаренгейта равны 15 градусам по шкале Цельсия.
5. Программа преобразует единственное значение температуры по шкале Фарепгсйта
(в градусах) в значение температуры по шкале Цельсия. Изменяя число, Присваивае-
мое переменной f, вы можете преобразовать любое значение тсагпсргггурьх.
“'9 Минутный практикум
1. Какое ключевое слово в С# используется для целочисленного типа данных?
2. Что обозначает термин double‘?
3. Обязательно ли в Сад-программе использовать оператор using Systerm?
Два управляющих оператора
Внутри метода происходит Последовательное выполнение операторов по направлеи
нию сверху вниз (то есть так, как они были записаны в данном блоке кода). Однако
можно менять этот порядок выполнения с помощью различных управляхопшх
операторов, поддерживаемых Cit. Хотя детально управляющие операторы будут
рассмотрены в книге позже, о двух из них мы расскажем в этом разделе, поскольку
они используются в дальнейших программах.
Оператор if
ВЫ можете избирательно выполнить часть программы с помощью условного опера-
тора if. Этот оператор во многом сходен с оператором if любого другого ЯЗЫК?!-
В частности, он синтаксически идентичен оператору if в языках С, C++ И JHV21.
Самая простая его форма представлена ниже:
15(условие) оператор;
Здесь условие — это булево выражение (имеющее значение true или E-.515:-;-). Если
условие истинно, то оператор выполняется, если условие ложно, то оператор пропус-
кается. Приведем пример использования условного оператора if:
if(10 < ll) Console.WriteLine("lO меньше, чем 11");
В этом елучае число 10 меньше, чем число 11 (условное выражение истинно),
Следовательно, оператор Writ:eLine () ; будет выполняться. Рассмотрим пример:
if(10 < 9) Console.WriteLine("3Ta строка не будет выведена На экран");
1. Ключевым словом для целочисленного типа данных в Св является слово int.
2. double -— это ключевое слово для данных с плавающей точкой двойной точности.
3. Нет, но это удобно.
44 Глава 1. Основы языка С#
В этом случае оператор WriteL1ne(); ВЫЗЫВИТЬСЯ не будет, поскольку число I0
больше числа 9. В С# определен комплект операторы; сравнения, которые могут
использоваться в условных выражениях. Ниже представлены ЭТИ операторы и их
значения.
Оператор Описание
< Меньше чем
<= Меньше или равно
> Бопьше чем
>= Бопьше или равно
== Равно
!= Не равно
Отметим, что оператор равенства состоит из Двух символов Знака равенства.
Ниже представлена программа, в которой используется оператор if:
// B программе демонстрируется использование оператора if.
using System;
Class IfDemo {
public static void Ma1n() {
int a, b, C;
a = 2;
Ь = 3;
if(a < Ь) Соп5о1е.Иг1сеЬ1пе("Значение переменной а меньше, чем"+
" значение переменной b.");
// Строка, которая приведена в качестве параметра
// 5 следующем методе Wr1:eL1ne(), не будет выведена на экран.
if(a == Ь) Console.WriteL;ne("3Ta строка не будет выведена на экрап.");
. _ Испотьзованис опе mo а м.
Console.Wr1teL1ne ();
C = а ~ Ь; // Переменной C присваивается значение ~l.
Соп5о1е.Их1сеЬ1пе("Переменная с содержит значение -1.");
if(C >= O) Соп501е.Их1ЕеЬ1пе("Значение переменной C ~ "+
"не отрицательное число.");
1f(C < O) Console.WriteLine("3HaueHme переменной с 7 " +
" отрицательное число.");
Console.WriteLine();
C - b т a; // Переменной C присваивается значение
‚а ‚‚_.
Соп5о1е.Иг1сеЬ1пе("Переменная C содержит значение
1f(C >= O) Console.WriteLine("3HaqeHMe переменной
"не отрицательное число.");
if(C < О) Соп5о1е.Иг1СеЬ1пе("Значение переменной C — " +
"отрицательное число.");
О
v
+
Два управляющих оператора 45
При ВЫПОЛНСНИИ ЭТОЙ ПРОГРЭММЫ на экран бЫЛИ ВЫВСДСНЫ СЛСЦУЮШИС СТРОКИ:
Значение переменной а меньше, чем значение переменной b.
переменная с содержит значение —1.
Значение переменной C — отрицательное число.
переменная с содержит значение 1.
Значение переменной с — не отрицательное число.
Обратите внимание, что в строке этой программы
int a. b. C;
объявляются три переменные, которые разделены запятыми. Уже говорилось, что
если требуются две или более переменных одного типа, они могут быть объявлены в
одном операторе. Для этого нужно разделить имена переменных запятыми.
Цикл for
Вы можете многократно выполнять какую-либо Последовательность кода (блок).
определив цикл. В этом разделе мы рассмотрим цикл for. B C# ЭТОТ ЦИКЛ работает
так же, как и в С, С++ и Java. Самый простой синтаксис Цикла for:
Ёог(иниЦиализация‚ условие, итерация) оператор;
В общепринятой форме часть Цикла, отвечающая за его инициализацию, присваивает
переменной цикла некоторое начальное Значение. Условие является булевым выра-
жением, проверяющим переменную цикла. Если результатом этого теста является
значение true, ЦИКЛ for выполняется далее, если — false, происходит естественное
завершение работы Цикла. Итерационное выражение определяет, как изменяется
контрольная переменная Цикла при каждом его выполнении. Далее представлена
короткая программа, в которой демонстрируется использование циюта for:
// Программа, в которой демонстрируется использование цикла for.
using System;
class ForDemo (
public StatlC void Main() (
int count;
Eor(count = 0; count < 5; count = count+1) (
int num = count +1;
Console.WriteLine("3To " + num + "—й проход цикла.“);
)
Console.WriteLine("uMKn завершен.");
}
}
H ИЖС ПОКЭЗЭН резул ЬТЭТ В ЫП ОЛ не Н И Я ЭТО Й П ро ГрЦМ M Ы I
Это 1-й проход цикла.
Это 2-й проход цикла.
Это 3-й проход цикла.
Это 4-й проход цикла.
Это 5-й проход цикла.
Цикл завершен.
46 Глава 1. Основы языка С#
B этом примере count Является переменной цикла. Первоначально в части инициа-
лизации цикла for ей задается значение ноль. В начале каждого повторения цикла
(включая первое) выполняется проверка условия Count < 5. Если условие истинно,
то выполняется оператор WriteLine(), а затем инициализируется итерационная
часть циюпа (то есть переменной count присваивается новое значение, равное
coun:+l). Этот процесс повторяется до тех пор, пока значение переменной циюта
перестанет удовлетворять условию. С этого момента выполнение цикла прекращает-
ся. Переменная num была введена в программу только для того, чтобы в выводимых
строках нумерация проходов цикла начиналась не с ноля, а с единицы.
В профессионально написанной С#-программс вы практически не встретите итера-
ционную часть цикла, написанную так, как в предыдущем примере. То есть опера-
торы, подобные приведенному ниже, встречаются в цикле for крайне редко.
count ; count т 1;
Причина этого в том, что в Си есть специальный оператор инкремента, который
выполняет операцию увеличения первоначального значения переменной на единицу
более эффективно. Это оператор, обозначаемый двойным символом знака плюс (++).
Итерационная часть приведенного выше цикла for с использованием оператора
инкремента записывается следующим образом:
count++;
A весь цикл for будет выглядеть так:
for(count = O; count < 5; count++)
Работать он будет точно так же, как предыдущий.
Также в C# имеется оператор декремента, обозначаемый двойным символом знака
минус (——). Этот оператор уменьшает свой операнд на единицу.
Минутные? диктату/и
1. Как звучит полное название оператора if?
2. K КЭКИМ операторам относится for?
3. Какие операторы сравнения имеются в От?
Использование блоков кода
Еще одним ключевым элементом языка С# является Шок кода. Блок кода — это
объединение двух или более операторов, заключенное в фигурные скобки. После
создания блок кода становится логическим элементом, который можно использовать
так же, как одиночный оператор. Блок может использоваться в операторах if и for.
Рассмотрим следующий фрагмент кода:
if(w < h) (
V —w " h;
w д О;
1. if -— это условный оператор.
2. for относится к операторам цикла.
3. В C# имеются следующие операторы сравнения: <, <=‚ >‚ >=‚ == !=
Использование блоков кода 47
Если вданном операторе 1f 3Ha‘{CHHC переменной w МСНЬШС значения персменнои h,
то оба оператора внутри блока будут выполнены. То есть поскольку два оператора
внутри блока формируют логическую единицу, один оператор не может выполняться
без выполнения другого. Это означает, что если вам понадобится логическая связь
между двумя операторами, вы должны поместить их в блок. Блоки кода позвочяк)!’
регшизовать многие алгоритмы так, чтобы они были понятны и выполнялись с
высокой эффективностью. Ниже представлена программа, в которой блок кода
используется для исключения возможности деления на ноль:
// В программе демонстрируется использование блока кода.
using System;
class BlockDemo (
public static void Main() (
int 1, j, d;
1 = 5
1
3=о;
// Этот блок кода относится к оператору if.
if(i != 0) (
Соп5о1е.Иг1сеЬ1пе("Значение переменной i не равно нолю."): ВСШ›Ш0ГбЛ0К
д Z j / д; «кода относится
. . _ _ ' -'“ L
Console.Wr1teL;ne("]/1 равно " + d); k(”mpMOpyl
„ответа; дрдфдссдддддвш ._ . .. ‚
Вопрос. Влияет ли использование блока кода на эффюктивгтость программы
(то есть не увеличивается ли время ее выподитеттияУ?
ОТВЕТ. ИСПОЛЬЗОВЭНИС бЛОКОВ КОДИ НС y13cm—mwzacT ВрСМЯ ВЫПОЛНСНУАЯ про-
граммы. Поскольку они способны упростить кодирование Определенных алгоритмов.
их использование, как Правило, увеличивает Скорость работы программы и CC
Эффективность.
жженная-щи :Ь;:-*—$‘ц„‚ьчх‹АЬ2:а›--а „,„-м‚_:„.- —:
Результат выполнения этой программы представлен ниже:
Значение переменной i не равно нолю.
j/i равно 2
В данной программе к оператору if относится блок кода, а не одиночный оператор.
Если условие, управляющее оператором 1f, истинно (Как в данном случае). T0
выполняются все три оператора в блоке. Попробуйте присвоить псременнои 1
Значение 0 и посмотрите, каков будет результат.
Как вы увидите далее, блоки кода имеют дополнительные своисткза и способы
применения, но основное их предназначение — создание логически неразделпмой
единицы кода.
48 Глава 1. Основы языка С#
Символ точки с запятой и позиционирование
В C# символ точки с запятой означает окончание оператора. То есть каждый отдельный
оператор должен заканчиваться точкой с запятой.
Как вы знаете, блок является набором логически соединенных операторов, заклю-
ченных в фигурные скобки, поэтому сго завершает не точка с запятой, а закрывающая
фигурная скобка.
С# не распознает окончание строки как окончание оператора, только точка с запятой
может завершить оператор. Поэтому не имеет значения, в каком месте строки вы
помещаете точку с запятой. Например, для С# строки кода
x = y;
у = у + 1:
Console.WriteL1ne(x + "
т у):
будут означать то же, что и строка
х = y; y = y + 1; Console.Wr1teLine(x + " " + y):
Более того, отдельные элементы оператора также могут быть помещены в разные
строки. Так, допустимо следующее расположение:
Console.WriteLine("3Ta строка при выводе займет довольно много \п" +
"места, потому что кроме данных " + х + y + 2 +
"\n она состоит из трех строковых литералов.");
Такой способ разбивки длинных строк используется для улучшения читабельности
программ. K тому же можно увидеть, как эта строка будет выглядеть при выводе.
Использование отступов
Вероятно, вы заметили, что в предыдущих примерах некоторые операторы написаны
с отступом. С# является языком, в котором применение различных отступов или их
отсутствие при вводе кода (при вводе операторов) не оказывает влияния на работо-
способность программы. Но поскольку существует общепринятый стиль написания
программ, которому следует данная книга, советуем вам его придерживаться. Для
этого нужно при вводе кода делать отступ (два-три пробела относительно начала
верхней строки) после каждой открывающей скобки и возвращаться обратно после
каждой закрывающей скобки. Исключение составляют операторы, для которых
предусмотрен дополнительный отступ; они будут перечислены позже.
Минутка/д лрактакум
1. Как создать блок кода?
2. Как завершаются операторы в С#?
3. Справедливо ли утверждение, что все Сгг-операторы должны начинаться и
заканчиваться на одной строке?
1. Последовательность операторов помещается между открывающей ({) и закрывающей (})
фигурными скобками. Блок создает логическую единицу кода.
2. Операторы в С# завершаются символом точки с запятой.
3. Нет.
Использование отступов
49
проект 1-2. Усовершенствование программы по
преобразованию значения температуры
E FtoCTable.cs
Используя ЦИКЛ for И оператор if, мы усовершенствуем разработанную в первом
проекте программу, преобразующую значение температуры по шкале Фаренгейта в
значение по шкале Цельсия. В новой версии на экран выводится таблица преобразо-
ванных значений, начиная с 0 градусов по шкале Фаренгейта и заканчивая 99 градусами.
После вывода десяти строк с исходными и преобразованными данными выводится пустая
строка. Это выполняется с помощью переменной counter, используемой для подсчета
выведенных строк (такой алгоритм называется использованием счетчика).
Пошаговая инструкция
1. Создайте новый файл с именем F‘:OCTable.cs.
2. Введите следующую программу:
/-A’
ч
Проект 1-2
В этой программе выводится таблица значений температуры
по шкале Фаренгейта и соответствующих им значений
температуры по шкале Цельсия.
Сохраните эту программу в файле FtoCTable.cs.
using System;
class FtoCTable {
Ъ
public statlc void Main() {
double f, с;
int counter;
counter = о I. 4 Счетчику строк присваивается первоначальное значение OJ
for(f = 0.0; f < 100.0: f++) (
// Преобразование в градусы по шкале Цельсия.
с = 5.0 / 9.0 * (f — 32.0):
Console.WriteLine(f + "( no шкале Фаренгейта равны " r
C т "( по шкале Цельсия.");
COU1'1’Eer++.‘ 4—r/Bennqennc значения счетчика строк на 1 при каждом прохождении Цикла.
// После каждых десяти строк выводится пустая строка.
if (counter 10) { Если значение переменной counter
con5o1e_writeLine(); ршмю10,выыщикяпипаясшока
СОППСЕГ = 0; // Для отсчета очередных 10 строк.
З. Скомпилируйте программу в Visual C++ [DE mm введите в командной строке
следующую команду:
C>csc FtoCTable.cs
50
Глава 1. Основы язьпка С#
4. Запустите программу, используя Visual C++ IDE, ИЛИ Введите в командной строке
имя программы:
C>FtoCTable
Ниже
п
а
а
а
и
и
а
о
кооочочшьшюно
on
ЮН
PO
on
19°
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
Фаренгейта
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
равны
-17.777777777777е°
—17.2222222222222°
—16.6666666666667°
~16.11111l11l1111"
—15.555555555555e"
-15“
—14.4444444444444°
—13.8888888888889°
—13.3333333333333"
—12.777777777777s°
—12.222222222222°
-1l.666666666667“
—11.111111111111"
—1О.555555555556°
-10"
—9.4444444444444"
—8.8888888888889"
—8.3333333333333°
—7.7777777777778°
-7.2222222222222°
Ключевые слова в языке С#
В языке С# определены 77 ключевых слов (табл. Ll), которые нельзя использовать
в качестве имен для переменных, классов и методов.
Таблица 1.1. Ключевые слова C#
ПО
ПО
ПО
ПО
ПО
по шкале Цельсия
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ПО
ШКЗЛЕ
ШКЗЛС
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
шкале
по шкале Цельсия.
шкале
шкале
шкале
шкале
шкале
представлена часть результата работы программы FtoCTable.
ЦЭЛЬСИЯ.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цельсия.
Цель сия .
Цельсия .
abstra
byte
class
delega
event
fixed
if
Ct
te
internal
new
override
readonly
short
struct
try
unsafe
void
as
case
const
do
explicit
float
implicit
is
null
params
ref
sizeof
switch
typeof
ushort
while
Идентификаторы
идентификаторами в C11 являются имена методов, переменных или других опреде-
ленных пользователем элементов. Идентификаторы могут состоять из одного или
base bool
catch char
continue decimal
double else
extern false
for foreach
in int
lock Long
object operator
private protected
return sbyte
stackalloc static
this throw
uint ulong
using vortual
breack
checked
default
enum
finally
goto
interface
namespace
out
publick
sealed
string
true
unchecked
volatile
Библиотека классов C# 51
нескольких символов. Имена Переменных могут начинаться с любой буквы или знака
подчеркивания. Вторым символом идентификатора может быть буква, знак подчер-
кивания или цифра. Знаки подчеркивания используются для улучшения читабель-
ности имени переменной (например, line_coum;). Компилятор различает символы
верхнего и нижнего регистров (то есть myvar и Myvar —— разные ичена). Ниже
приведено несколько примеров допустимых идентификаторов.
Test x y2 MaxLoad
up top my:/ar sample23
Запомните, что идентификатор не может начинаться с цифры. Следовательно, 12:-:
не является действительным именем идентификатора. Хороший стиль програм м иро-
вания предполагает использование имен идентификаторов, которые отражают sna-
чение или особенности применения данного Элемента.
Хотя в C# в качестве имени идентификатора нельзя использовать ключевое слово.
перед ним можно ставить символ @, что позволит использовать ключевое слово в
качестве действительного идентификатора. Например. @for является действитель-
ным идентификатором. Фактически в этом случае идентификатором является for, а
- символ @ будет игнорироваться. Использование ключевых слов C начальным симво-
лом @ не рекомендуется, за исключением тех случаев, когда это необходимо для
специальных целей. Символ @ также может стоять в начале любого имени иденти-
фикатора, но использовать его без особой необходимости не рекомендуется.
Минутный практикум
1. Какое из трех слов является ключевым ~ for, For ИЛИ вон’?
2. Какие символы может содержать идентификатор в языке Сд?
3. Являются ли идентификаторы index21 и Index21 одинаковыми?
Библиотека классов С#
В программах, представленных в этой главе. были использованы два встроенных
метода, WriteLine () И Write () . Как уже говорилось. эти методы являются члсначи
класса Console, принадлежащего пространству имен system. которое в свою очередь
определено в библиотеке классов. NET Framework. Библиотека I<nacc013.N ET Frame-
work используется в С# для предоставления поддержки операций ввода/вьпвода„
Обработки строк, работы в сети И создания интерфейсов GUI. Cit тесно ингегркчроваът
со стандартными классами платформы .N ET. Как вы увидите, библиотека шассов
обеспечивает большинство функциональных возможностей, использующихся в любой
С#-программе. Чтобы стать программистом на C#, необходимо изучить эти стандарт-
ные классы. В этой книге описаны различные методы и элементы библиотеки классов
.NET, HO поскольку библиотека довольно большая, многие ее компоненты вы должны
будете изучить самостоятельно.
1. Ключевым является слово for. B C# все ключевые слова следует писать только строчными
буквами.
2. Идентификатор может содержать буквы, цифры и знаки подчеркивания.
3. Нет, в C# учитывается регистр символов.
52 Глава 1. Основы языка С#
ofimpaxsww W? асы %%%%
1. Что такое MSIL И почему он так важен для Cit?
2. Что такое не зависящая от языка среда исполнения (CLR)?
3. Каковы три основных принципа объектно-ориентированного програм-
мирования?
4. С какого метода начинается выполнение программ написанных на С й?
5. Что такое переменная? Что такое пространство имен?
6. Какое из следующих имен переменных является недействительным в
С#?
а) count
6) Scount
B) Count 27
г) 67соипт
д) @if
7. Как создать однострочный комментарий? Как создать многострочный
комментарий?
8. Запишите синтаксис оператора if. Запишите синтаксис цикла for. g
9. Как создать блок кода?
10. Обязательно ли начинать каждую Сд-программу следующим операто-
ром:
using System;
11. Сила тяжести на поверхности Луны составляет приблизительно I7 про-
центов от силы тяжести на поверхности Земли. Напишите программу,
которая бы вычисляла, каким будет ваш вес на Луне.
‚з
12. Измените проект 1-2 таким образом, чтобы программа выводила табли-
Цу преобразования дюймов в метры. Результат должен вычисляться для
множества расстояний, взятых в диапазоне от 0 до 100 дюймов. После
каждых 12 строк программа должна выводить пустую строку. (Один метр
приблизительно равен 39.37 дюйма.)
Ё
Ё
в
з-
25
9.:
E‘
Ё
f
°w'M.'.-en‘:¢~oo-o¢.bmaao192f2I-esaxx}-aaauen-v°\»2:‘::oomdn --v~§..n-\:.:‘ - -
....:<-at „мы д:=.‹и=..„<.т-‚шмм„.„ш=„_„_ м.
„м. . - ‚мае-чача‘ ‚е.
глава Типы данных и
операторы
С]
С]
С]
Базовые типы данных С#
Литералы и их использование
Создание инициализированных
переменных
Область видимости переменных
Арифметические операторы
Операторы сравнения и логические
операторы
Оператор присваивания
Операция приведения типа, явное и
неявное преобразование типов данных
Выражения в С#
54 Глава 2 Типы данных и операторы
Типы данных и операторы составляют основу любого языка npm pd’vI]VlI/lpOBdl~ll/til ЭТИ
элементы определяют возможности языка и категории задач к которым может быть
применен зтот язык Cit поддерживает большое количество типов данных и опсра-
торов, что делает его удобным для решения многих задач Программирования
Рассмотрение операторов и типов данных требует довольно много времени В этои
главе мы поговорим об основных типах данных и наиболее часто используемых
операторах, а также подробно расскажем о переменных и выражениях
Строгий контроль типов данных в С#
Cit — язык со строгим контролем типов данных Это означает, что все операции в
Cit контролируются компилятором на предмет совместимости типов данных а emu
операция является недопустимои, то она не будет компилироваться Такая строгая
проверка типов данных помогает предотвратить ошибки и повышает надежность Для
возможности осуществления этого контроля всем переменным, результатам вычис—
лении выражении и значениям задан дпределенныи тип (то есть нс сушествуст
псременнои с неопределенным типом) Более того, гип значения опрсдс тяст виды
операции, которые разрешено производить над ним Операция разрспцснпая ччя
одного типа данных, может быть запрещена для другого типа
Обычные ТИПЫ данных
C11 включает две основные категории встроенных типов данных обычные пшпы (или
простые типы) и ссылочные типы K CCI:[J'[0‘{Hbl\d типам относятся классы, о которых
мы поговорим позже Обычные типы, которых в ядре Cit тринадцать, представлены
в табл 2 1
Таблица 2 1 Обычные типы данных
Тип Описание
Bool Значения true/false (истина/ложь)
Byte 8 битовое беззнаковое целое
Char Символ
Вес1та1 Числовой тип для финансовых вычислений
Double Число двойной точности с плавающей точкой
Float Чиспо одинарной точност и с плавающей точкой
Int Целое число
Long Длинное целое
Sbyte 8 битовое знаковое целое
Short Короткое Целое
Umt Беззнаковое целое
Ulonq Беззнаковое длинное целое
Ushort Беззнаковое короткое целое
В С# для каждого типа данных строго специфицированы диапазон значении и
ВОЗМОЖНЫЕ Операции над ними Соответственно требованиям ПСРСНОСП/кмости в C#
Обычные типы данных 55
строго контролируется выполнение этих спецификаций. Например, тнп данных mt
одИНаКОВ для любой среды выполнения программ, поэтому нет необходимости
переПИСЫВаТЬ код для обеспечения совместимости этого типа с каждой отдельном
платформой. Строгая спецификация необходима для достижения переносимости.
хотя на некоторых платформах может привести к небольшой потере производитель-
НОСТИ .
Целочисленные типы
в C# определены девять целочисленных типов данных: char, byte. звуке. ;1~_«..:~_,
„эпохе, 1nt. еще, long И ulong. ТИП Данных char B ОСНОВНОМ применяется для
представления символов и будет описан позже в этой главс. Остальные восемь типов
используются для выполнения операций с числами. В следующей таблице перечис-
лены эти типы, а также указано количество битов, выделяемых для представления
числа, и диапазон возможных значений.
Тип Количество битов Диапазон значений
byte 8 OT О до 255
sbyte 8 От -128 до 127
short 16 OT —32768 до 32767
ushort 16 OT О до 65535
int 32 OT -2147483648 no 214748364?
uint 32 От О до 4294967295
long 64 OT —9223372036854775808 до 9223372036854775807
ulong 64 OT О no 18446774073709551615
Как показано в таблице, в Сд определены как знаковые, так и беззнаковые варианты
целочисленных типов. Различие между ними состоит в способе интерпретации
старшего бита целого числа. Если указывается знаковое целое, то компилятор будет
Генерировать код, в котором предполагается интерпретация старшего бита целого
числа как флага знака. Если (рлаг знака 0, то это положительное число; если флаг
знака I, то число отрицательное. Отрицательные числа практически всегда представ-
лены с использованием метода двоичного дополнения. В этом методе все биты числа
(за исключением флага знака) инвертируются, затем к Этому числу ПрИбаВЛЯСТСЯ
единица, в завершение флагу знака задается значение I.
Знаковые целые важны для очень многих алгоритмов, но они имепот только половину
абсолютной величины соответствующих бсззнаковых целых. Например. запишем в
двоичном представлении число типа short 32767:
01111111 11111111
Поскольку это знаковый тип„ при задании старшему биту ‘значения 1 число будет
интерпретировано как -1 (если использовать метод двоичного дополнения). H0 CC-‘W
вы объявляете его тип как ushort, то при задании старшену биту значения 1 число
бУдет интерпретировано как 65535.
Пожалуй, наиболее часто используемым целочисленным типом является шк. Пере-
менные типа int часто применяют для управления циклами, для индексации
массивов и в различных математических вычислениях. При работе е целочисленными
значениями, большими, чем те, которые могут быть представлены типом :1nt, в C11
используют типы uint, long, ulong, a при работе С беззнаковым целым -— тип ulnt.
56 Глава 2. Типы данных и операторы
Для больших значений со знаком применяют тип long, для еше больших положи-
тельных чисел (беззпаковых целых) — тип ulong.
Ниже приведена программа, вычиеляющая объси куба (в кубических дъоимах)‚ длина
СТОрОНЫ которого равна 1 МИЛО. ПОСКОЛЬКУ ЭТО ЗНЭЧСНИС ДОВОЛЬНО бОЛЬШОС, ДЛЯ СГО
хранения программа использует переменную типа long.
li
Программа вычисляет объем куба (B кубических дюймах),
длина стороны которого равна 1 миле.
(Для справки: В одной миле 5280 футов, Ь одном футе L2 дюймов.
*/
using System:
class Inches {
public static void Ma1n() {
long ci;
long im;
im — 5280 * 12;
ci = im * 1т * 1m;
Console.Wr1teL1ne("O6$eM куба с длиной стороны, равной 1 мила, "1
"\Правен "+ Cl +" кубических дюймов.");
}
НИЖЕ: ПОКЗЗЗН результат ВЫПОЛНСНИЯ ЭТОЙ программы:
Объем куба с длиной стороны, равной 1 миле‚
равен 2543580б1О56ООО кубических дюймов.
Очевидно, ЧТО Такой результат Не может быть помещен в переменную Типа Lnt или
uint
byte И sbyte —— наименьшие целочисленные типы. Значение типа ЬуЬ-з может
находиться в диапазоне от 0 до 255. Переменные типа byte особенно полезны при
использовании необработанных двоичных данных. таких как поток байтов данных,
сгенерированный каким-либо устройством. Для небольших знаковых Целых приые—
няется тип sbyte. Представленная ниже программа использует переменную типа
byte для контроля цикла for, B котором вычисляется сумма всех целых чисел,
находящихся в диапазоне от 1 до 100.
// Использование типа byte.
using System;
Class Use_byte {
public static VOld Main() {
byte x;
int sum;
sum z O;
fOr(x = 1; x <= 100; x++)
sum — sum + х;
.„‚
Обычные типы данных 57
Cons0le.Wr1teL1ne("Cymma всех целых чисел, находящихся
п
"в диапазоне 0'1 1 L10 lCC, \правна г suit):
}
Результат выполнения этой программы следующий:
сумма всех целых чисел, находящихся в диапазоне от 1 до 100, равна 5050
Поскольку в данной программе для управления циклом for используются числа от
1 до 100, находящиеся в диапазоне значений, определенных для типа byte, HCT
необходимости назначать тип переменной, позволяющий работать с гораздо больши-
ми числами.
Для Правильного назначения типа переменной и экономии системных ресурсов
воспользуйтесь приведенной выше таблицей и выберите диапазон значений типа,
которому удовлетворяет Значение этой переменной.
ТИПЫ данных С плавающей ТОЧКОЙ
Как уже говорилось в главе I, ТИПЫ данных с плавающей точкой могут представлять
числа, имеющие дробные части. В Ci? существуют два типа данных с плавающей
точкой, float И double, представляющие числа с одинарной и двойной точностью
соответственно. Для представления данных типа float выделяются 32 бита, что
позволяет присваивать переменным значения чисел, находящихся в диапазоне от
1.5Е-45 до 3.4E+38. Для представления данных типа double выделяются 64 бита, что
расширяет диапазон используемых чисел до величин из диапазона от 5Е—324 до
1.7Е+308. Чаще всего применяется тип double. Одна из причин этого в том, что
многие математические функции в библиотеке классов Ci? (которой является биб-
лиотека .NET Framework) используют значения, имеющие тип double. Например,
метод Sqrt ( ) , определенный в стандартном классе System.Math, возвращает значение
типа double, являющееся квадратным корнем его аргумента, также имеющего тип
double. Ниже в качестве примера приведена программа, в которой для вычисления
длины гипотенузы, заданной длинани двух катетов, используется метод Sqrt: ( ) .
/*
B Программе используется теорема Пифагора, позволяющая найти
длину ГИПОТЭНУЗЫ ПО ИЗВЕСТНЫМ ‚ЦЛИНЗМ катетов.
*/
using System;
Class Hypot {
РиЬ11с sta:1c volci Main() {
double х, у, д;
X - ;
3'14;
Обратите внимание на способ вьпзова метода Sqrc ( ) .
Math . Sqrt (x*X + y*y) : ‹———-__ Имя метода отделено точкой ог имени класса, членом
N
H
КОТОРОГО OH ЯВЛЯЕТСЯ.
Соп5о1е.Ип1сеЬ1пе("Длина гипотенузы равна " + 2);
58 Глава 2, Типы данных и операторы
Результат выполнения этой программы:
Длина гипотенузы равна 5
Отметим еще одну особенность данной программы. Как уже говорилось. метод
Sqrt{) являстся членом класса Math. Обратите внимание на способ вызова ыегода
sqrt, г) — имя метода отделено точкой от имени класса, членоч которого он является.
Похожий способ записи нам уже встречался, когда перед именем метода мысе-
Line и стояло имя его класса Console. Нс все стандартные методы нужно вызывать,
указав вначале имя класса, в котором определен данный метод, но некоторые методы
требуют именно такого вызова.
Тип decimal
Возможно, наиболее интересным числовым типом данных в С# является тип decl-
mal, предназначенный для использования в денежных вычислениях. B типе с1ес1та1
для представления значений, находящихся в диапазоне от 1Е-28 до 7.9E+28, ИСПОЛЬ-
зуется 128 битов. Вы, конечно, знаете, что в обычных арифметических вычислениях,
производимых над числами с плавающей точкой, неоднократные округления значе-
ний приводят к неточному результату. Тип данных deC1.ma1 устраняет ошибки,
возникающие при округлении, и может представлять числа с точностью до 28 ДССЯ-
тичных разрядов (а в некоторых случаях и до 29 разрядов). Эта способность пред-
ставлять десятичные значения без ощибок округления особенно полезна, когда
рассчитываются фи нансы.
В качестве примера рассмотрим программу, которая ДЛЯ ДСНСЖНЫХ ВЫЧИСЛСНИЙ
ИСПОЛЬЗшЛ`ТИП данных declmal. npOFpaMNm.BHHMCflflCT баланс ПОСЛС начисления
процентов
/›\—
Программа демонстрирует использование типа decimal
для финансовых вычислений.
*/
using System;
class UseDecimal {
publlc static vold Ma1n() {
declmal balance;
decimal rate;
// Подсчет нового баланса
balance = 1000,10m;<—~———————————————___—
rate = 0.1m;
balance = balance * rate + balance;
При 13130110 JHH‘{CHI/[Si ТИП?! decimal ДОЛЖНЫ
ЗЗКННЧИВЗХЬСЯ СИМВОЛОМ П’! ИЛИ
Соп5о1е.Иг1ЬеЬ1пе("Новый баланс: $" т balance);
}
Эта программа ВЫВОДИТ СЛСДУПОЩИЙ pC3yJ]bTaT2
Новый баланс: $11ОО.11
е.“
?
59
Обычные типы данных
Ответы профессионале‘
Вопрос. Ранее я работал с языками программирования, в которых пс было
типа данных с1ес1та1 Является ли он уникальным м присущим только C11’?
OTBeT. Да, этот тип данных уникальным: такими языками, как С, C++ или
Java, он не поддерживается.
с с;
мамаши-мм
Отметим, что после десятичных констант (то есть констант, имеющих тип declmal)
должен следовать суффикс т илм М, иначе эти значения будут интерпретированы как
константы с плавающем точкои, не совместимые с типом данных с1ес1та1 (Далее в
этом главе мы подробно рассмотрим, как специфиимруются числовые константы )
Лтщттыдтшттшют
[. Какие Целочисленные типы данных существуют в Cit?
2. Назовите два типа данных с плавающей точкои.
3. Почему тмп данных аес1та1 так важен для финансовых вычислении"
Символы
В отличие от других языков программирования (таких, как С++‚ в которых д'ля
представления символа выделяется 8 битов, что позволяет работать с 255 символами)
в C# используется стандартный набор символов Unicode, B котором представлены
символы всех языков мира. В Cit char — беззнаковыи тип, которому для представ-
ления данных выделено I6 битов, что позволяет работать со значениями, находящи-
мися в диапазоне от О до 65535 (то есть с 65535-ю символами). Стандартным 8-битовыи
набор символов ASCII является составной частью Unxcode И находится в диапазоне от
0 до 127. Таким образом, АЗСП-символы остаются деиствительными в C#
Для того чтобы присвоить значение символьной переменном, нужно заключить в
Одинарные кавычки символ, стоящии справа от оператора присваивания. Ниже
показан синтаксис операторов, при выполнении которых переменном ch присваива-
ется значение X.
Char ch;
ch :_ ухи;
Для вывода символьного значения используется оператор Wr1teLlne () . Следующая
Строка выводит значение переменном ch.
COnsole.Wr1teL1ne("3HaqeHMe переменной ch — " + ch);
ТИП char, ОПрСДСЛСННЫИ в СФЗ как целочисленным тмп данных, никогда не может
быть свободно совмещен с целочисленными типами, предназначенными для
1. В С# определены следующие целочисленные типы данных: byte, short, mt, long, soyte,
usho rt, Ulnt и ulong. Тип данных char также технически является целочисленным, но в
основном он используется для хранения символов.
2- ТИПЫ данных с плавающей точкой — float и double
3- Данные типа declmal используются Для финансовых вычислений, поскольку они не
подвержены ошибкам округления.
60 Глава 2. Типы данных и операторы
ПРСДСТЗВЛСНИЯ ЧИССЛ, ПОСКОЛЬКУ ОТСУТСТВУСТ НВТОИНТИЧССКОС преобразование H5 ЦС-
лочисленного'типагзспаг;|1апример„еледуКцции‹ррагмент кода НСДСЙСТВИТСАСНЁ
char ch;
ch — 10; // Данный оператор недейсцвителен.
Причина этого в том, что значение 10 A целочисленное и нс будет автоматически
конвертировано в тип char. Следовательно. операнды данной операции присваива-
ния представляют собой несовместимые типы. Если вы попытаетесь скомпилировать
этот код, компилятор сообщит об ошибке.
Далее в этой главе мы расскажем, как можно обойти это ограничение.
джема; врвевееевлвлв
Вопрос. Почему в Cit используется Unicode?
O'rBeT. B задачи разработчиков Ci? входило создание такого Компьютерного
языка, который позволял бы писать программы. предназначенные для использования
во всем мире. Поэтому авторы воспользовались стандартным набором символов
Unicode, B котором представлены все языки мира. Конечно, использование этого
стандарта неэффективно для таких языков, как английский, немецкий, испанский
или французский, символы которых могут быть представлены 8 битами, но это Цена,
которую Приходится платить за Переносимость программ в глобальном масштабе.
Булев тип данных
I ля булева типа данных, bool, B C# определены два значения true И false (истина
и ложь). Следовательно, переменная типа bool или логическое выражение будут
иметь одно из этих двух значений. Более того, не существует способа преобразования
значений типа bool B целочисленные значения. Например, значение I НС преобра-
зуется в значение true, a значение О — в значение false.
Приведем программу, демонстрирующую использование типа данных boo}.
// Программа демонстрирует использование
// переменной, имеющей тип Ьоод.
ц51п9 System;
class BoolDemo {
pUbl1C statlc мода Ma1n() {
bool b;
Булева переменная может управлять
b falsz уыцшнымопщшицюм1Е
_ L;
Соп5о1е.Иг1сеЬ1пе("Переменная b имеет значение " + b);
b — true;
Соп5о1е.Иг1СеЬ1пе("Переменная b имеет значение " + b):
// Булева переменная может управлять условным оператором if.
1f(b) Console.Wr1tCL1ne("3woT оператор будет выполнен."): ‹_“““
b = false;
lflb) ГОПЧПТР Wr1 f{=T1nr={"'4r1|r\rr* ппдпд-к-пп их: Бишь-п n:_nIr\rIHPH."):'
п?
обычные типы данных 61
// Условныи оператор аозвращае булево зьачение
Console WrlteL1re("(lC > 9)—э1о + (l0 > 9H
}
Ниже представлен результат, сгенермрованныи ЭТОИ программом
переменная о имее1 значение False
переменная b имеет значение True
Этот операгор буде. выполнен.
(10 > 9) д1о True
Обратите внимание на некоторые особенности использования булсвом переменном
во-первьтх, при выводе значения типа bool C ПОМОЩЬЮ метода дшееьщпе () на экран
выводится слово true ИЛИ false BO-BTOpbI\, I/ICl'lO'1b3_V$l переменную типа ко‹1
можно управлять оператором г‘ Если усчовмеи выпо тненмя оператора f явтяется
истинность переменном (в данном программе переменном b) то нет необходимость:
записывать оператор ЫЕ так
1f(b —— true)
достаточно ботсе короткого Выражения
1f(b)
В-третьих, результатом выполнения оператора сравнения, такого как <‚ является значе-
ние типа bool BOT почему выражение то > 9 выводит на экран значение true Далее,
дополнительная пара скобок, в которые заключено выражение 13 > 9. является необхо-
димом, поскольку оператор + имеет большим приоритет, чем оператор >
Минутами? дракам/кум
I Что такое Unicode’
2 Какие значения может иметь персменная типа boo1”
3 Каков размер данных типа char в битах’
Форматирование вывода
Прежде чем продолжить рассмотрение типов данных п операторов, сделаем НСбОТЬт
Шое отступление До этого момента при выводе списка данных мы раздетятп часш
СПмека с помощью знака птюе, как показано ниже
Console Иг_сеЬ1пе('Было заказано " + 4 + ' кн/ту то 4' + 3 +
‘за единицу товара ')‚
ЭТО Удобно. но такои вывод числовом информации не позвотясп контролировать
СПОсоб появления данном информации на экране Например нетьзя контролировать
выводимое число десятичных разрядов для значении с п таваюшеп точком Рассмот-
РИМ следующим оператор
Console Wr1teL1ne("10/3 — " + 10 С/3 O),
1 Umcode —» это набор международных символов, каждый из которых представлен 16 бтттами
2- Переменная типа Ьоо1 может иметь значение либо true, либо false
ты 3 Данные типа char представлены 16 битами
62 Глава 2. Типы данных и операторы
После его выполнения на экран будет выведена слелутоцкая егрока:
10/3 3.33333333333333
Для некоторых целей вывод такого большого числа десятичных разрядов отправдан,
но в других случаях он может быть неприемлем. Например. в финансовых вычисле-
ниях обычно требуется вывод только двух десятичных разрядов.
Для форматирования числовых данных необходимо использовать перегруженный:
метод шкщсеЬшпе () (перегрузка методов будет рассматриваться в главе 6), в один из
параметров которого можно внедрить форматирующуто информацию. Синтаксис
этого метода выглядит так:
Иг1:еЬ1пе("формааируюшая строка", arg0, argl, ... , argN);
B ЭТОЙ версии метода WrJ_teLJ_ne()apFyMCHTbl разделены запятыми, а нс шакамн
плюс. форматирующая строка состоит из стандартных вьнзопьтмьтх без изменения
символов и спецификаторов формата. Синтаксис спецификаторов формата обычно
выглядит так:
{argnum, Wldth: fmt}
Здесь элемент argnum _VKZ13blBaCT HOV!Cp аргумента (начиная с ноля). который должен
быть выведен на экран в данном месте строки. Элемент Width определяет минималь-
ную ширину поля, предоставляемого данному аргументу, а элемент fmt: специфици-
рует используемый формат вывода.
Когда при выполнении в форматирующей строке встречается спецификатор формата,
программа заменяет его соответствующим аргументом, который и вьшодъттся на
экран. Следовательно, позиция спецификатора формата в (рорматирутоцъей строке
указывает место, где на экране будут выведены соответствующие данные. Элементы
Width И fm: являются не обязательными. Следовательно, в евоеи простейшей форме
спецификатор формата указывает аргумент, необходимый ДЛЯ вывода на экран.
Например, спецификатор (О) указывает на аргумент argi), спеннфикатор (1)
указывает на аргумент argl и так далее.
Теперь расемотрим простой пример. Оператор
Console.WriteL1ne("B феврале {О} или {1} дней."‚ 28, 29);
выведет на экран следующую строку:
В феврале 28 или 29 дней.
Как видите. епецификатор {O1 заменяется значением 28, а спеннфикатор {1‘ м
значением 29. Обратите внимание. что аргумент ы метода ивы едите () pIU£lC.'l$HO'lCH
запятыми, а не знаками плюс.
Следующий пример демонстрирует усовершснсттзованттую версию предыдущего оне-
ратора, в кото ой спеии )ици ована минимальная щи ина поля:
D P
C0ns0le.WriteL1ne("B феврале {0,10} ИЛИ {1,5} дней."‚ 28, 29);
Результат выполнения данного оператора следующий:
В феврале 28 ИЛИ 29 дней.
ЕСЛИ ВЫВОДИМОС ЧИСЛО МСНЫПС ЗВДЗННОЙ ширины ПОЛЯ, ТО НСИСПОДЬЗОВЁПКНЫС ‘КИСТИ
ПОЛЯ ЗЭПОЛНЯЮТСЯ ПрОбСЛНМИ. ПОМНИТС, ЧТО ШИрИНН ПОЛЯ -- ‘ЭТО \l|rlHHM2lJ]bH()C
КОЛИЧЕСТВО ПОЗИЦИЙ, ПЫЦСЛСННЫХ ДЛЯ ВЫНОЦИИОГО ЗНИЧСНИЯ. ЕСЛИ ДЛЯ ВЫВОД?) ЧИСЛА
потребуется ООЛЬЦТСС КОЛИЧССТВО ПОЗИЦИЙ, ОНИ буДуТ НЫДСЛСНЫ ЕПЗТОМЭТИЧССКРТ.
Обычные типы данных 63
в предыдущих примерах к самим значениям форматирование не применялось, по
безусловно, используемые спецификаторья формата должны контролировать отобрав
жение данных. Чаще всего форматируются значения с плавающей точкой и значения,
имеющие тип decimal. Одним из простейших способов спецификации формата
является написание шаблона, который будет использован методом Wr;teL1ne() нрн
форматировании выводимых значений. Шаблон создается следующим образом: в спе-
дификаторе формата указывается номер аргумента, затем после двоеточия при помощи
символа # указываются позиции выводимых Цифр. Так, послс выполнения оператора
Console.Wr1teL1ne("l0/3 > {O:#.##}", 10.0/3.0);
программа выведет отформатированное значение, являющееся результатом деления
10.0 на 3.0‚
10/3 = 3.33
B этом Примере шаблон создается последовательностью символов # . M, с почохныо
которых методу WrJ_teL1ne () сообщается о необходимости вывода двух десятичных
разрядов. Если программой выводится число, у которого Целая часть содержит более
одного знака, то во избежание потери данных метод Wr1teL1ne () будет выводить
более одной цифры слева от запятой.
В on ac необходимости вывода значения в долла ах и центах использ йте спеши ›и-
P У
катор формата с. Например,
decimal balance
balance = 1232З.О9ш;
Сопсо1е.Иг1сеЬ1пе("Текущий баланс равен: (О:С}"‚ balance);
B результате будет выведено следующее сообщение:
Текущий баланс равен: $12‚323.09
Проект 2-1. Разговор c Марсом
Ш Mars.cs
Наименьшее расстояние между Марсом и Землей составляет примерно 34 миллиона
Миль. Предположим, что на Марсе находится человек, с которым нам необходимо
поговорить. Для этого нужно написать программу, которая могла бы вьячислъп ь.
сколько времени потребуется сигналу на преодоление этого расстояния. Скорость
Света равна примерно 186000 миль в секунду, следовательно, для вычисления времени
Задержки сигнала (времени между отправкой и приемом сигнала) необходимо разде-
ЛИТЬ расстояние на скорость света. Значение времени должно быть выведено в
секУндах и минутах.
Пошаговая инструкция
1- Создайте новый файл и назовите его Mars . cs.
2- Поскольку значение времени задержки сигнала будет содержать дробные части,
для него необходимо использовать значения с плавающей точкой. Перечислим
Переменные, которые будут использоваться в программе:
double distance;
double lightspeed;
Глава 2. Типы данных и операторы
double delay;
double delay“1n_m-n;
Задайте переменным distance и llghtspeed начальные значения:
dlstance 2 34000000; // 34000000 миль
llghtspeed = 186000; // 186000 миль в секунду
ДЛЯ ВЫЧИСЛСНИЯ вреиени задержки СИГНЗЛЭ рЖПЮЛИ1С ЗНЭЧСНИС ПСрСИСННОИ
dlstance назначение переиеннои 1-9ппзреео.[Трупэтоь4будегполучекк›3паче—
HHS задержки СИГНЭЛЭ В C€KyH[lZ1x. Присвойте 310 ЗНЭЧСНИС переиеннои delay И
выведите результаты следующим образом:
delay = distance / llghtspeed;
Console.Wr1teL;ne("Paccronume между Землей и Марсом сиънал " +
"пройдет за \n" 4 delay + секунд.");
Для получения значения результата в минутах разделите значение результата в
секундах, которыи содержится в переиеннои delay, на 60; выведите результат на
экран, используя следующие строки кода:
ае1ау_1пАт1п = delay / 60;
Console.Wr1:eL1ne("BpeM5 задержки сихнала Сосиавляет " 4 0elay_1n_w1n
Т минуг.");
Ниже приведен полныи листинг программы Mars . -:5‘
/at
Проект 2-1
"Разговор С Марсом"
Назовите этот файл Mars.cs
*/
и51п9 System;
class Mars {
publlc static VOld Ma1n() {
double dlstance;
double llghtspeed;
double delay;
double delay_ln_m1n;
dlstance — 34000000; // 34000000 миль
llghtspeed — 186000; // L86,000 МИДа s секунду
delay — dlstance / llghtspeed;
Console.Wr1:eL1ne("PaccToHHme между Землей и Марсом сихнал " 4
"пройден за \п" + delay + " секунд.");
delay_;n_m1n : delay / 60;
Conso1e.Wr1teL1ne("BpeMH задержки сигнала составляет " +
delayN1n_m1n + " минут.");
шШвшщ‚. _ „_. 55
6 CKOMHH ll.1p\HT(. и 5.111\LT11'1L 11p1)1p11\1\1\ PL5\ 11 к.кк LL ккьккко IHUHIH ох [L1 (,1L1\|()11111\1
Pacc L>;«11/L мим. '3 к г, я L. ц
время дд 1 ккк к —1 с! я 1 г
7 Конечно 6o11,1111111L1110 ккодькк 11L110L11p10o1L HL L1 1 11\1111101L L 111L11\11 „к о
щишк (‚пкшкохк \111()1O 1LL11111 111111\ p11p>11011 [п \ 1\111L1111 11 1UL 111111. 11 111
водихкого рьл кьгккк прокркхшы ;1\1L11111L L00111L1L111\101111L o11Lp1 оркк
Line () , l1pC,1L’I 111 lLHHbl\ll.l 1111;1L
COnSO..e N LL I ( Ia L. с». /-- ч >\,.1‘, э L ~./ к»
Ъ +41. С! "f‘7 ‘н’
Con<olL Д; ьы Гм! н „к 34,4 а we м"
миг-куч с х \
8 Повторно (.l\()\H|H 111p\111L 13111\L1111L 1pn1p1\1\1» lL11Lh1 1 1 )1\p111 0\1L к 11L \”
0T(1)0p\1c11 Hp()IhHIHIl1l pL и кк к кк шк к 1L L111111
Расс оянис между ш с. [.1 1 ._к
182 796 кекукд
время зддсржку C1/1 Hala LL 1-1 4»: 1 к.-‘ 1.
В ЭТОМ C'1\1‘1\L Hi )l\P П! НЫПО UHLH (О П l\() PH 1L.LllHHHl\P дрЯ ll
Литеральк
В C# iume/Jazawu (ккк 1Im1cma/m1(.1111) 111311111101Q1111L11p011111H111_ 11 1 11.111151 npL1L1111
ленные в укобнои „шя 11L1111s1 11)0p\1L (111111p11\1Lp 111L 10 НЮ якккныык кпьр о\к)
B 001-1o1111o\1 1111cp11_11,1 11 Ln0L0611 их 11L11o11 ‚ок кккккк кк 1L о кк I\() 11011 11111 кко \ккк к 1
ОООбЫХ объясккьнкки ккричсня ки их 11 гон и 111 1111011 1)1op\1L 110 щи прь 1I11\ шк\ крок р Ш
мах Теперь жь p1C(.1L1‘;1<L\1 о них o01LL кко кроокко
В C# штормы могут оыкь зккккчыккпкхккк ккоооко кккккк ок кхокороко 511 1L11 L11oL0o
“P'311LTa131LH11s1 мкждоко ]И"‚р(1 к l\11\ \/KL n110p1110L1» L11\110111111L ШИК! кккккк 511 Ю
чаются в одикккрккыь ь 11111 11111 ккк кк ккккякокы L|l\1Ii()I ккк кхкккк 1 Ц Н 1\I1
L[efTO‘~II/1Ll(.llHblL Hl'{Lp1l1hlLllLl1ll(i)lllU1p\l()1£$I 1\U\ 111L110L1 крооккокк 11L111 lI111p11\11p
10 и —l()011131>111)rL>111L1o111L1L111111\111<011L11111 \111 1\1)11L1111T11 L 111111111>111L11 ко ккюкк
Требую-гири ккаккккшкккккк 11L11011 10111111111 1LLs1111111011 1011\11 1 к ко 0p011L 1L1\L1 кроокк кя
Часть ‘киша H111p11\1Lp ll I23 1111111L1L11 КОНЬ‘ ккккокк L 11111110111L11 кокмъкк В (
Рдреииьтькк тккжь 11Ln011 11111111. )l\(.HOHLHIllI к 11110L ккр 1L1111L1111L 111LL 1 L 11 111 по
Щеи точкои
Поскочькх C11 —— язккь L0 L1p0111\1 l\0Hlp() 1L\1 11111011 ‘IIILDLIH 111L;1\L о 1pL11 кко L11
Как ПРИННкыыккккккь 1L 1\11\0\1\ 11110111 11111\ Ч кко к щ 1u11111\11u 111p\111L11111 ккрп
ОПРСДСЧЦкИкк ккккккк l\lIK10l0 111L10110 о 111Lp1 1 ( L\ мы! кок L кк кк ьккппь
Прдкзи ккк
T”n0\1L1L1O111L1L111111\ 1111Lp1101111111;1L1L;1 11 111\1L111 11111 ц ок 1L L к ккккк ккккк 111 к 111111
С lnt l\()TOpIIH L11oLo6L11 \pdHHll 1111110L 111L ко ( 1L 101111L 11 кко к 111111L11\11L111 о!
ВЁЧИчккны чиькк гиккохк 11L1()111L1<.HHb1\ 1111LpL1011 \1())I\LI 01111 Н Ш
‘110г9 Ликсрдлы L I'|'h1Bd}OHlLl/1 ко {КОИ 11\1L101 тип 1
Если вас, нс y(.Ip1lI/H1dLF гип опрьдскеннкш 11 C# кко y\/10ll(“ll’”0 кккк хкожьгь зкЬпь
НУЖНЫИ гип I11/IlCpcU1d явно ккоьрьдстноюк ккобдшкьнизк ьукрфкккььк Чгобьк Vk кмгь кип
66 Глава 2 Типы данных и операторы
литерача long, ДОбаВЫС к числу букв) _ или I. (например, число 20 имеет тип п‘.
а 12L — тип lcwg). Для спсцифнкатттттт бсззнакового целочисленного ‚тнтераттт
добавьте букву 1 илтт ь (например. число 100 имеет тип ‚вы а IOOL — тип аьгт)
Для спецификации длинного беззнакового це.тото прибавьте к константе суффикс
ml или UL (например, литерал 984375LL HMCCT Tun 1,1-':'*1v;1) Чтобьт специфицировать
литерал типа Нов”, лобавьте к константе символ Е или F (например, литерал I0 ЮГ
имеет тип float). Для спецификации ли термов титта оесгтат добавьте к значению
букву т или ‘Ч (9 95М является литералом. имеющим тип засипел).
Хотя целочисленные ттитералы по умолчанию создаются как значения, имеющие титт
цттд u1r1L. long или ьЬЭго. они могут быть присвоены Переменным гипа Купе.
еоусе ьЪтогь или 1431“! rt. HO только в гом е тучтте, если ттристшттттаехтое значение
вообще может представляться згим типом. Пслочпсленттьттт литерал всегда может
быть присвоен переменнотт титта Дсгт;
Шестнадцатеричньте литерал Ы
Иногда в программировании вместо лесягттчнои легче применять тттестттапттатерттч-
ную систему. использующую цифры ог 0 до 9 и символы от А до F, которым
присвоены значения от Ютто I5 соответственно. Например, шестнадцатеричное число
I0 соответствует 16 в десягттчттои системе исчисления Учитывая Достаточно частое
применение шестнадцатеричных чисел„ Ст‘: позволяет использование целочисленных
констант в шесгтталттагерттчттохт формате. ППестнгщцатсричньтс лигераты ‚толжттьт
начинаться е символов ах (ноль и х) Принсаем несколько примеров
count — ОХЕЬ: // 255 5 деснгичной сис-емс
1nCr ~ Oxla; // 26 B десятичной системе
СИМВОЛЬНЫЭ escape-nocnep.oBaTenbnocm
B Сд большинство вьтттолттмьтк символьных констант можно затстючать в одинарные
кавычки, но применение некоторьт\ символов (например, символа возврата каретки)
является проблематичным Кроме того. часть символов (например, символы оди-
нарньтх или двоинык кавычек) имеют в C11 СПСЦИЭЛЬНОС значение, поэтому их прямое
использование недопустимо. По этим причинам в С# предусмотрены специальные
cscapc-/10c,1e006an:ezmmcmu, которые перечислены в табл. 2.2 Эти ттослецоттатегтьносттт
используются вместо символов, которые отти представлятот
Например. TdKH\1 06p£l30\1 HCpC\1CHHOH т ‘ I1pll(.HdH|l:lC1C$l Ll1\lH().l l‘£l6y.l5lllHll
ch — '\:';
А Tdk ПСРСЧСННОИ С ’l YIPHCHZIHBAIOICSI 0,IHHdPHbIC l\dHbl'lKH
ch '\";
Таблица 2 1 Символьные езсаре-поспедоватепьности
Еэсаре-последовательность Описание
\а Предупреждение (звонок)
\Ь Возврат на одну позицию (backspace)
\f Переход на новую сграницу (formfeed)
\П Переход на новую строку (lmefeed)
\r Возврат каретки
питерапы 67
i‘ T“? г"
"—"* б“
Едсаре-последовательность Описание _
и Горизонтальная табуляция О
W Вертикальная табуляция
ю Ноль
\- одинарные кавычки
\-- ДВОИНЫЭ кавычки
“ Символ обратнои косой черты
СТрОКОВЫЭ Л mepan Ы
В C# l'1OIllICp)KHl3dC[‘(.$l CUIC ОДИН THU птгсратов ~ СТРОКА, ЯВППОЦЩЯЬЯ Hd00D()\(
СИМВОЛОВ, ЗАКТЕОЧСННЫХ B [ВОИНЫС l\dBhl‘H\H НИЖС HDHB(.,.1l.H пример (.IpOl\H
"Небольшои проверочныи щеке
Вы уже встречали примеры шкич строк, применявшиеся во многих опсрагорах
Wr;LteL1ne() ‚ Предыдущих программ
Кроме обычных симво юн строковые Читсратьт мог) г содержать очи) и ш пссьо тько
езсаре-посцтсдохха-гстьностеи В качестве примера рассмотрим программх, которая
использует езсарс-пос чедоваке кьнос ги “ а и `\ *
// B программе демоне pvpye „я исгользование escape 1ОС1СД0Ва-ЬЛэгО„ ед
// в строковых лигералах
и$1п9 System,
“(J10 lh30lSdHlrlL (AL lpi. ПОС ll. [OH UL [ЬНОЫИ I
ДЛЯ HL[')L\(),.l1 на НОВУК) (.[p0l\\
class StrDemo I
publlc stat-c vo,d Na n() I
Console Wr1LeL1ne("Hepbau с1рокд \1В1оран с оока ")‚
Console Wr*teL+U5('A\CB\EC"): Иыкпьювмпц тбшшцииццяиыршнпшшия
Console wr*te1_,lne ( "D\tE\tF") I выводимых симво 1015
Ъ
ЭТИ программа ВЫВОДИТ L (C11)/l’OL[Il[(. строки
Первая строка
Вторая С1рОка
A B {_
D E ь
Ё дтаетьг лшфаееаеаала
€32!
Bonpoc Я знаю, что в язьткс C++ разрешается спсиифътпнротзать пперМЫ B
ВОСЬМСРИЧНОИ системе Разрешены ‚пи восьхасричньте пператы в Сд’
Ответ Нет. в C11 'iM’lCpd’Ib[ можно специфицировать тотььо в [ссятнчпоп п
ШЁСТНШЦаТерИЧНоИ форме B современном программировании восьмсрпчттая форма
встречается редко
тку
3aMC'1b'[e, Ч ГО (.S(.dpC-HOL ЮНОНА П, lbHOL ГЬ IILIIO [Ь 3VCT(Jl ДЛЯ псрс \О Ш На НОН) КО L lP0l\\
ЧТООЫ рАЗОИТЬ ВЫВОДИМЫС ДАНПЫС НА HC(.k(LIbKO С’! рОК‚ НС O65U<lTC']hH0 H(.HO'H;3OB.1H;
68 Глава 2. Типы данных и операторы
множество операторов эсщъешпе U :. достаточно поместить езсаре-послсдователь-
ность an в пределах шинной строки там, где должна начинаться новая строка.
В дополнение к вышеизложенному добавим, что в Св имеется еще возможность
специфицирования копирующегося строкового литером. Он начинается с символа @,
за которым следует строка в кавычках. Содержимое строкового литерала, взятого в
кавычки, принимается без модификации и может охватывать две и более строки.
Следовательно, в этом случае можно использовать символы перехода на новую
строку, табуляции и так далее без применения CSC2lp€-fIOCJ1C[lOH.’:1TC."lbHOCTCl/L Единст-
венное исключение состоит в том, что при необходимости получения в выводимых
данных символа двоиных кавычек (") m нужно ввести дважды попрял (""). Следующая
программа демонстрирует использование копируюшихся строковых лптералов.
// B программе демонстрируется ИСПОЛЬЗОВаНИе копирующихся СТРОКОВЫХ литералов.
using System;
Brorr копирующийся строковый литерал содержит
вложенные символы перехода на новую строку.
Class Verbatim {
public s:at1c VOLG Ma1n() {
Console.Wr1teL;ne(@"3To конирующийся строковый литерал,
который охватывает
несколько строк.
") ;
ConsoLe.Wr;CeL1ne(@"3uecb также используется табуляция:
1 2 3 4 fiom копирующемся строковом литералс содержатся также символы табуляции.)
6 7 8
Соп5о1е.Ик1сеЬ1пе(@"Программисты говорят: ""C# ~ интересный язык.""");
}
Результат выполнения этой программы выглядит следующим образом:
Это конируюцийсн строковый литерал,
который охватывает
несколько строк.
Здесь также используется табуляция:
1 2 3 4
5 6 7 8
Программисты говорят: "C# ~ интересный язык."
Обратите внимание на то, что копируюшисся строковые ‚литералы выводятся в
программе точно так же, как они были введены. Но если коттирующийсэт литерал
Занимает несколько строк и еимвольт выводятся на экран с самого начала строки, то
нарушается читабельность программы, поскольку не соблюдается стиль ввода кода с
использованием отступов. Поэтому мы не используем копирующиеся строковые
литерашы в программах данной книги, но вы должны знать, что это Прекрасное
решение во многих случаях, когда требуется форматирование.
переменные и их инициализация 69
минутным? лрактакум
1 Каков 11111 1111111111 ()’ Ix мы ш п 11)1 10 U
2 C111L111d)111111p\11r1 31111111111 H M11111 ( 111111111111 1111 111 11111111111 I111) ь ш
3 ‘I10 r11<o1
Ётеетег ггрефеггеднгеэге
Bonpoc Ян 11111111 111 111111111 11111111 1111p 1 111\1 111311111 1.011112111111115101111101111111
cv1M1101(11a11p11\11p 11 1 )’
OTBeT H11 Не mT111r1 11p111111 111\1111111\111 C11\111111111b1 1 111111111 111111111111
ляет еднни кнын 111\111o1T11r11 C грош ьодьржнцая 111 11 ко о 1111 б кщ 1111 111111111
остается строьон ‘(0111 111101111 111110111 111 111\11;o1o11 они 11\111111 1p\r1111 гип
моим
|epeMeHHb1e И ИХ 1/1H1/111.1/1anv13a11.m=1
О l'lCpC’\/1LHHbI‘( \1Ы УЖЕ КОРОТКО ГОВОРИЧИ В ЕЧШХ 1 BAH И EBLLIHO TO I"(.pL\lCHHblL
O6'bHBJ'IfHOT(.5[ С ПОЧОЕЦЬЮ 0r11.p1T0pd H\lLK)ll11lO L IL 1\}0LllHl'l Ll/lHTE1l\LVl(.
type Var 1111111
где супе — это гпн 111p1\1111111111 1 — ц 11\111 Вы \1o/11111 061111111111
переменные 1106010 1111111111T11111111o 111111 1111111111 p111\111rp111111,11 11111111 троп 11
типы При (‚ошании r11p1\1111111111 „шпиц: 31131111111113 L1 11111 11110111111 ню 110)
можности 111p1\111111o11o1111141'11110111 11 1111111\1 H 111111111111 111p1\1111111>1111111 111
может 14c11o11,30111r1111 1111 \p1111111111 3111111111111 11 111111111111 01111111 b11111 юго 11111
объявлении 111p1\1111H011 11 11111 1}11111111p\11111 11 H5\1LH5Hb 110 11111111 H11p11\11p
Переменная тнпъ 111 ножи 11p111111111T1)1 я в 111111\1111H\111 111111
Все 11epC\111111b1111C1’¢ 10111111111 61111 1)o111111111b1,11111*< 111111111 11111 11111 31011106x1)111\10
flOCKO'1bI\y ko\1n11111111p\ H\/KHO 111111 11 1 1111111111 111111111111 1111111;11111 111p1\1111111>1 11
того как оп 111 11111 k0\111111111)011 111 11111131101111 111111)111\10111111 )1\ r11p1\111111\11)
B C11 onp111111111 11111111111111 11111111111111 11111111 1111)1\111111 111 H1p1\111111111 11111111111
HC1'IOJlb30l3c1ll1Ll 11 HllJJl.1\ 111101p1\1\11\ н) мою \10\111111 111111111111 111 10/11 I 1 I
переменными юьм) п м 11111111111 1 1111 1111 11 111111111 11111\1111111 \1 1111
инициализация переменной
ПРСЖЩ 111\1 11111111 юнг} 111111\111111\111 111 11 ‚з 1 1 111111111 111 111 к 1111 C 11 1
ЭТО можно шиш 11111111011111 Ножны 1111111111111 11 1111111 1111111111 11111 11 111 11 11111\1
°”eDdr0p1 1111111 11 06111111111111 \ 1111111111 111111 но 1 1 1111\1 юнцы 11111 и:
объявит T1111 111p1\11H111111 11 1111111 111111 111 11111111111 L11 1111 1111 11 11111 к 111111111
"9PC\111111o11111\111111 111 нм 1111111111111 (111111)111111 1111111 11 11111 1 1,11111\1 11111111111
BHCMO1. 311111111111 ( l.lHT'1l\LHL он 1111011111 1111111 1111111111 111111 11111 ни 11
type var
1 10 имсьт лип 1 1 100 имссп шп ч
2 для укшання числа 100 как 111111111 пишешь п 11111 но ними) J111111111 их 1001
/L \k1111111s11111111l001<111 ЦПкр к 11111111 лип 1 111111\>1\1111 11111111r1 K\k l(1()L
3 Q1111 1 рующииьяыроьоныи 111111111
70 Глава 2 Типьт Данных и операторы
Здесь жатве — это значение, которое присваивается переменнои во время ее
создания. Значение должно быть совместимо с указанным типом.
ПрИВСДСИ НССКОЛЬКО примеров ОбЪЯВЛЁНИЯ ПСРС\1СННЫ\ С одновременным присватт-
ВННИСИ И“ значении‘
Ant Count lJ; /’ 1epeMe~1oV coLnL 3aLaC4CH ”dH3H:1QG дг81ОНИВ ‘C
char Ch ‘Х’ // переменной ch IpNCdaldaG_Ch „ИМЬОА Х
float t 1.2F; // переменной 2 црис:аивао-ся значение _.2
При объявлении двух и более переменных одното типа можно разделить переменньтс
в списке запятыхти и присвоить о‚тнои или нескольким из ни\ начальное значение.
Например:
lnt a, b — 8, C — 19, 0; // ьерсменным b И С присвоено начальное значение
В ЭТОМ СЛУЧПС ИНИЦИИЛИЗИрОВННЫ ТОЛЬКО ПСрСЧСННЫС С И K‘
Динамическая инициализация
В предыдущих примерах переменным прттсвгтттвастттсь только конкретные числовые
значения (то есть значения, которые тте бьт.ти получены в результате вьтчттштенття
какого-либо выражения) Но в C17 можно иниттиализттровгтть переменные и динами-
чески, используя любое деиегвительное выражение. Например, ниже приведена
короткая программа, которая вычисляет объем Цилиндра по заданному радиусу
основания и высоте:
// В программе демонстрируедсн использование
// динамической инициализации пс сменной.
P
us-ng Sys;er;
class Dynlnlt
public ьсаьтс V010 Ma;n()
double rad;us — 4, he;ght A 5;
Переменная УО1ЦШ6 иницшыишруется ДНННИИЧССКН
ВО ВрСМЯ ВЫПОЛНЕНИЯ H[)OlpdMMbl
// динамически инициализиРУеьсн переменная мо1щпе ‘(/////
double моьопе — 3.1416 * као-о5 * raa_us * he-qh;;
Console.Nr1teL1ne("O6beM цилиндра равен: ~ модное];
т
В awn программе объявляются три локатьньтс переменные — пакты 5. '1?-Lg"! и
тет me. nCpBblC Две. гасить и т е4 , ттниниализттротзаны конкретными значения-
ми. Переменная voume HHMl1L1d_"IVl3ll])yCTC5l динамически — еи присваивается mane-
НИС. соответствующее объему Цилиндра. Ктючеттьтм моментом является то, что н
выражении инициализации может быть использован любои элемент, которыи уже
был до этого обьявлен (то есть деиствтттельньттт тта время инициализации данном
псрсметтнотт). Таким элементом может быть метод. Lip)! ая переменная или лтттерат
Область видимости и время жизни переменных
Все переменные, которые мы использовали по этого момента. ОбЫПЪМ-“ИСЬ В Начале
ИСТОДа Ma1n()- НО в C1? можно объявлять локальные Переменные И ВНУТРИ блока.
область видимости и время жизни переменных 71
как уже говори гтось в т таве I 61o1< НдчИНдСТсЯ открьтваюшсттея фигмрнои скобкоп и
заканчивается закрывающем фигурном скобкои Он определяет обтспть видимости
которая зависит от того, имеет ли танньти бток втожснттьте бтоки Каящыи раз
создавая блок вьт создаете новмю обтасгь видимости оттредетяюттшо время житии
объявляемых переменных
Наиботее важными об тастякттт втттихтосттт в C3 яв тяются тс воторьтс оттрсдс „поты;
классом и метотокт Па данном этапс мы рассмотрим H)Hl\O об mun втттттмосттт
опредс тяехпю мсто Юч
Область видимости, оттрстс тясмая Mun том ттачинастся с отврывттотттетт фтттхрттотт
скобки Отнако emu этот метот имссг ттдтрамстрьт они таким тис иоч тются в об т тсть
видимости метода Общее правито состоит в том, что обьявтсттная внхтри об mun
видимости переменная явчяет ся неттидихтотт (то есть недостмттнотт) для вода которым
определен за grow O6"l<l(_Tb}O СЧСКПШГСПЬНО, при объявтснтттт ттеременнотт в предс та\
ее области видимости вы “токмилетс JTV переменную и затттицтаетс се от ттеразре
шенното доступа и мо тнфивацтти Можно сказать что прави та васаютцттсся об mun
видимости обеспечивают фундамент 1 тя итткапсютятттттт
Область видттмосттт может быть в тоженнои Например каждым раз созтавая б ток
кода, вы создаете новую втоженттио об mun. lxIl.Jll\1()(.TH и при этом внешняя об тасть
становится Bl/I£1HV1OH.llSlB'IO)K(_HH()l/I06 mun Это означас-г что ОбЬСКГЫ обьявтсттннс
во внешнси обтасти видимости, будут тштимы ътя кота ш вниренттеи об тасти
видимости, но обьектьт объявтенньте во внмтреннеи обтасти видимости нс буди
видимы для кода внешнси обгтасги видимости
Чтобы понять это прави то, рассмотрим степ» roux» то програмхп
// В прот рамме демоне рируе cs 3aHV'\.I/IKVXOCID возможт ос ь доступ к
// переменном от обласги видимое и, в ко орои она быта ОбЬЯВЛСНд
us цв; Sysr em
class ScopeDerIo
publlc static vo1d Ma1.n() с
lnt х, // Перемещая х известна всему коду
// в пределах медода Мести
х 1C.
lf(X ~ 1C) т //U71naL1 Я новая Old" т: витимсс и
Ant y ZC, // 3 a теремсытая Gym: видна ст ‹ в оамчах
‚’/ з ото оттока
// B даННОМ OJ оке видны Обе. тетэсмы ьые, \ к 3
Conso e Wr1.teL1ne( Видны х и у + х т т v)
X У * 2' y ?l()llCpCM(.HNJH обьятыснн тя во цтожсттнотт
т обтасти ви тнмосттт ттоэтощ ттьсь онт нс видн!
// y ‘CC, и” Ошибка‘ Переметная у 3,4 be 1:‘./lU'i
// Вы отъне (‚я дсст т ‹ I:1€ML.hlI и х ‹с. ан Овца с LH Л
Ъ I
// в „этом облас и видимости
Сопзоц. И1сСеЬ1пе( дндчстие с, смен и х раыто х)
72 Глава 2. Типы данных и операторы
Как указано в комментариях, объявленная в методе Mam () переменная х доступна
всему коду в пределах этого метода. Переменная у объявляется в рамках блока 1f.
Поскольку блок определяет область видимости, переменная у является видимой только
в пределах самого блока. Вот почему использование переменной у в строке у = 100:
вне своего блока является ошибкой, о чем и говорится в комментарии. Если вы
удатите начальный символ комментария, произойдет ошибка компилирования, так
как переменная у невидима за пределами своего блока. В рамках блока Ц может
использоваться переменная х, поскольку в блоке (вложенной области видимости) код
имеет доступ к переменным, объявленным во внешней области видимости.
Внутри блока переменная может быть объявлена в любом месте, после чего она
становится действительной. Следовательно, если вы определите переменную в началс
метода, она будет доступна всему коду в рамках метода. И наоборот, если вы объявите
переменную в конце блока. то не сможете использовать ее до момента объявления.
поскольку код не будет иметь к ней доступа.
Обратите внимание на еще один важный момент: переменные создаются при их
объявлении в какой—либо области видимости (при входе в данную область) и
уничтожаются при выходе из этой области. Это означает, что переменная не будет
сохранять свое значение при выходе из своей области видимости, то есть переменная,
объявленная в пределах метода, не будет хранить свои значения между вызовами
этого метода. Также переменная, объявленная в рамках блока, будет терять свое
значение при выходе из блока. Следовательно. время жизни переменной ограничено
ее областью видимости.
Если при объявлении переменной одновременно происходит ее инициализация, то
переменная будет повторно инициализироваться каждый раз при входе в блок, в
котором она объявлена. В качестве примера рассмотрим следующую программу:
// E программе демонстрируется использование правила,
// определяющего время жизни переменной.
using System;
class VarIn1tDemo {
public statlc void Ма1п() {
int X;
for(x — 0; х < 3; x++) {
int y = -1; // Переменная y инициализируется каждый раз
// при входе В блок.
Соп5о1е.Ик1ЬеЬ1пе("Значение переменной у равно: " + у);
// Всегда выводится значение —1.
у — 100;
ConSolc.WriteL1ne("Tenepb значение переменной у равно: " + y);
X
Ниже показан результат выполнения этой программы:
Значение переменной у равно: 1
Теперь значение переменной у равно: 100
Значение переменной у равно: —1
Теперь значение переменной у равно: 100
Значение переменной у равно: 1
Теперь значение переменной у равно: 100
73
область видимости и время жизни переменных
Как видите, переменная у инициализируется значением -1 каждым раз при входе во
внутренний Цикл for, затем еи присваивается значение I00. которое прттчттттаетыт
при завершении Цикла.
заметим, что между языками С# и С есть отличие в работе е областямти видимости
Несмотря на то, что блоки могут быть вложенными, переменная, обьяттлеттная во
внутренней области видимости, не может иметь такое же имя, как переменная.
объявленная во внешней области видимости Следующая программа. в которотт
делается попытка объявить две переменные с одинаковыми имеиттхттт. нс бутет
скомпилирована:
l-k
В этой программе Делается попытка обьявить во вну-реннсй обласши
ВИДИМОСТИ переменную С чем же ИМЕЁНЁМ, ЧАЭ 1/‘ y переменном,
объявленной во внешней области видимости.
*** Эта программа не будет скомпилирована. ***
*/
uslng System;
Class Nestvar {
publlc Static Vold Ma1n() {
int Count;
Переменную count 06b$us:mTb нельзя, поскольку переменная
с таким именем уже объявлена в методе Ма1п()
for(count — O; count < 10;
Console.Wr1teL1ne("CueT проходов цикла:
count : count+1) I
" + count);
lnt count; //
for(count = O;
Это объявление недействительно'!'
Count < 2;
Console.Wr1LeL1ne("B протрамме есть ошибка'"):
coun:++)
}
Если у вас есть опыт программирования на С/С++, то вам извест но. что в ат их язьткги
нет ограничений для имен переменных, объявляемых во внугрениеи области види-
Мости. То есть в С/С++ объявление еще однои переменном court B uumc 1
является допустимым, во при этом данное объявление скроет внсииттото перементи то
Чтобы скрытие пере/пенной не приводило K ошибкам Программировднпя. pd}p(I6() гчи-
ки C# запретили такое деисгвтте
Манутньш практикум
L Что определяет область видимости?
2. Где в блоке могут объявляться переменные‘?
3. Когда в блоке создается переменная? Когда она уничтожается?
Область видимости определяет возможность доступа K переменной и время ее жизни
Область видимости создается при создании блока.
2- Переменные могут быть объявлены в любом месте блока
Внутри блока переменная создается при объявлении этой переменной Псреметтттая уттич-
тожается при выходе из блока.
74 глава 2 Типьт данньтх и onepaT0P‘:'
тераторьт
В Ci? предусмотрен большон ттыбор операторов Оттердтором ятыяется символ,
юобщаюшитт комттимтгош о ттеобкоттихтоеттт тзыпотнеттття сттеттттфттчеекотт \1dFC\l£I'lH—
теекои u_1u.1onmcu<on uncpamm B C3 L)Iucuu_\'K>1 чет ыре основных класса оттердтто-
‚от; (дрифтшптические. поди/пошив, тог/иксами’ тт опсраттторьт (разлитая). Ll также ттеско.тт‚ко
тотто_тттите.тьттьт\ оттератот)отт.т.тэт обрцбогктт ттекоторьтх сттетттььтьттьтх етттмттттттт B JIOH
шве бутут рдсемотреттьт (трнфметнчеекттс. тотпчсектте ттттетътторьт тт оттер.т горы ьрдтт-
теттия Кроме (mo. мы расскажем об оттердторе ПРПСПЦПВЦППЯ Нобттготтьте тт трупе
‘ттеттттальттьте операторы будут рассмотрены ноз/ке
рифметические операторы
З C33 оттределеттьт с.те‚тутоттттте аттттфметттчссктте оттератоттьт
шератор Значение
Сложение
вычитание (также унарный минус)
Умножение
Деление
а Взятие по модулю (остаток от деления)
+ Инкремент
i Декремент
трифметттческтте оттергтготэьт сложения т‹). тзьт-ттттятттттзт (-). умножеттття (т) и „тепе-
ття (и) работают в Сд так же, как 3511060“ прутом языке ттрогр.тммттротьтнття. они
[my применяться к ‚ттобым тзетросттттым чиелоттьтм тнттахт данных
.отя ‚тсттетвттс арифметических операторов mm \ор()шО известно. некоторые спеши-
тьные ситуации требуют пояснения При ттрттметтентттт оператора дететтття к тте_то-
ттстенттым данным ‚тюбои остаток будет отброшен (Например. т; ттело-тттетенттом
е.теттитт 10/3 будет ттгттзно 3) Остаток этото деления можно ттояуптть Пртт ттспотыо-
анни оператора взятия по модутто ( ) Он работает т; Г?! T(”’\ AC, как т; -1p_\In\
xmkax, ~ no.IyIz1cr остаток от тте точнслеттттоттт ‚те теттия (wk, [U "c Ч будет рдтито l)
Ci’ оператор ттзятття тто модлто может ттрттменятьея к.тк к тте точттелеттттьтм ‚ыттттым.
тк и к значениям е ттмтттгттотттетт точкон (Н) О "Е 3 () будет также pamm I). n от.ттт-ттте
т C/C++. тпе операцию взятия тто \t0,1_\.n0 рмретттетто ттроттзтзодтттт) тотько C
е точисленттымтт типгтмтт ‚т2тнттт‚т\
.lC,l)IOI_l[d$l Hp0Ipd\I\hl .'1C\l()HClpMp)CT HPHHCHCHHC ()nCpdf();7d ВЗЯТИЯ ПО \|().[).ПО
Ё I.[QC-I PCXMMG‘ .‘.{Cr«1(7hC;pI/1p_\'F‘; ч l"CI оиьзд «ax Vt’ OI £‘fJd.C'I\a '-‘jH-l/‘1 I13 MC ,'.‘."I;( .
33: Уоцьето
рьоыкс ьсаьтс vo;d Ма„пт)
Ln: -1еЬЬ_д‚ „ген;
ёоооес дпеьи-Е, arc";
1resuA: ‚О / 3;
ткет r 10 F 3;
„е ._....-..‚..„..‚...‹—......‚_.„....‚„.»„ .‚‚„_...‚... .../ <._...,. . _._.
75
операторы
/-—/“”"—‘:' ц‘ А’ V А
огевищп v lC.C / 3.0;
ткет 10.0 2 5.3;
~ur.sole.Wr;:eL-ne("Pc3\'Jxu:ar ;1cJ13u/:J1em—u.:\ ..£3|l€‘Hl/Sm 10/3 равен:
._ге5щ;1 т "\n<;-C'.d-oK равет: " + ,:c");
3::bo-c.w:A:eL-ne("Peaynbra .' деления чисел, имеющих Am. QO.JDJ.e, ›
n,
-- AL)_0/3,3 раэег: Хт." т охс5ь1: ~ "`\п оецатоь: равен: " Ф сгег);
протрахпттт выводит c.xc.1_\ IOLLLHH результат.
'_Lt3JIOlU/CJh_lH.'.,‘lL‘- деления ZS 3 gage}: g
331 _>-\.-
› еж радел: 1
;L,v1.»1a; деления чисел, имеющих аи. Gouole, ..\.}.{.\'I_‘).C резец:
3.3.\§i3;uJ35333333 0c-auo1< равен: 1
Kilk IHLIHTC. ()UCpZl'l0p ВЗЯТИЯ ПО \10,1_v.11o BO3BpZlIH.rlCI $HZi‘lCHHC I как ПРИ рПбОГС С
119.10‘!HC.lCHHhl\1Ll 0HCpdH.’l£i\(H, ТИК И при работе С ‚ЦХНПЫПП. H\lClOlL[l1\Hl HUI ‘I ¢i\*e.
Операторы инкремента и декремента
Об операторах ттнкрехтента (++) тт декремента (——) хты уже _Vn0\mHa.m т; mane I. У ‘эт их
операторов есть некоторьте спештатьттьте стюисттза. которые летают их очень полез-
Ньтхттт пртт лтданитт алгоритма изменения переченттои шт КА?! (ттаттртптер, в иикле tcr).
Расехтотрихд как функционируют операторы ттнкрехтента и дскречен га.
Оператор инкрехтента добавляет единицу к етзоехту операттду, а оператор декремента
тзычтттает единицу. Следовательно. оператор
х-х 1;
ВЫПОЛПЯСГ ТУ /KC Cd\l_\l0 TIOCJICJOIBEIICJLHOCTI; ДСИСТВПП. ‘П О П оператор
x+ ,-
а оператор
т а .
.. __ д ‚
ПЬППППЯСТ ТУ ЖС C:'.l\1)K) UOC.lCIl0l§£lTC.'IbHOCfb LLCHCIBHII, ЧГО П ОПСРЕПОр
Хе.
I
Оба mu оттергпора хтогут указываться ‚тттбо до операнда (как ттрефтткс). ‚тттбтт поете
(как поегфикс). Например. оттерагор
у ‚
А:
“НАДО {ПППСНТЬ ГИК:
`7 С). eparop ).’1\'d'3bH:>d<.‘J.‘C>~. ДО 01 ераттда
тт ттт так:
/' оператор УКдЗЫЗЗСТСН I.OLLJic Oi сраьда
В лохт примере оператор ттнкрехтента указан двучя споеобахттт. хтеждт) которычп HCT
тттткакотт схтыетотзотт разницы. Но когда оператор ттнкречента тт.ти декремента иеттоль-
5\L‘ICsI как Часть большего тзыражеттття. то от расположеиттзт оттераторгт по ОтттОШСШПО
т. оттеранду заттзттсит гитгортттхт пьтполнстттттт блока кода. Fc.m оттсратор указьтттастея
‘тред операндохт. Св‘ утъелтт-титзает значение Псрсчсттиотт (оттераттда) do итого. как эта
"Чбсхтеттнагт будет ттсттользоттана оставтттеисзт частью ттыргтжснтттт. Ъсли оператор ука-
ЪЫППСТСЯ ПОСЛЕ’ операнда, ГО C13 _\'HC.lH‘IHll£lCF ЗПЦЧСПИС ПСрСПСПНОП n()(‘.I(’ moan, Как
76 Глава 2 Типы данных и операторы
0L'l¢ll3lLld5lL$l ЦК, ‘L пыр! I\L,HH§I HLIIU II £\L I ) \ IlL[‘L\|klH[\l()]£LBOH\0llLpI|1lII\ Р !LL\IOT~
[HIM L K lV}OlIlHH H[‘ll\1xp
‘( u
т Y
B лохт ь nu к пьрышпшп m Lu :1pmumHn ш шьппк Н
По LL H kt) l бх кг п mun {H H'\
'\
|ОШШ Н к пьрывыптоп ›\ [Ll щ пышны) ш 1 шпц Н) lSlIL\1 sn 1 шпц I ьрывьнпой
бх ты mu Ill шш пт L1HHHll\ В поп п к l\ rmx mtrunu mpu«unmn бхцег:
us; П! mm [О 1 I p ю ш um L()Lll)lH I0 Iblxl) по НрьПЫЪН мп 11 ‚ш при поп ты
Ь игопрч назншнтпт ыъоиьтптхт опьрпорп ппкрикпгд п пкришпт пп роьо ис-
потьм юты: прп форхптропптпът lnopunmu
Операторы сравнения и логические операторы
B крчппцх ampmnop ([7u(>’HLI(lI}I п т тис! !!!1‹)П(,/(!Н!(1[)к. юно c/mrmumc om man 0uCH
OJHOIO 5H«l‘lLHH§l no LDIIEHLHHIO ь 1р\1п\т l - mno 1()([(‘lu.I\Ll1( — L|1()LL)6 l\0TOpb[ I
\lO2kH0 СВЯНТЬ знтчсния п (пиши п '10/Kb) B шцмжьппп I]0(,}\O.nb
pc3\ IhT1[0\1 l;bm01HLHm1 опьръгороп ьравпыппг зпзтэпопя бх швы знпчьнпя, этЕ
OI1(pdT0pbl‘lc1(,"lO работают commune L то п кькппп 0nLp'1 гор um Ho)m\1\ H\ V10)KHu'
рдсысдгривать висеть
Нижь при [станины опкрпорп ьрппцппя
Оператор Значение
—— Равно
1- Не равно
> Больше чем
< Меньше чем
>— Больше или равно
<= Меньше или равно
Jane пьрьчпь mm огпчььык ощрл пры
Оператор Значение
& AND (И)
I OR (или)
^ XOR (исключающее ИПИ)
&& Short CIFCUII AND (бЫСТрЫИ оператор И)
" Short clrcun OR (быстрым оператор ИПИ)
I NOT (HE)
PL3_\1bTdFO\1 l3blF[0IHLHll$1 OHLD IIODOB (‚ППЪЪКПНЯ И IOFH‘lLL}\H\ 0F[Lpd'l()p()H 5lB"I$HOTc
ЗНАЧЕНИЯ [МПА ‘
B Cr“ Ompalopm И no w применяться м) вы“ объьктдхт us: nx LPJBHLHMH H А
прыщ: рапьпыш и ш ньрпзьпьпы Опьрлторьп ьртьпьзтътя приисним^
тонко к пьрьчньтяытьпт ппшч 1dHHll\ мпорыь \П()рЯ10ЧЦ!Ь| в mow crp\;<Ty',
(пдпримьр хпорядочьнттдът L pwnpi шьы I 7 Ч п мы пты и ш m0p;l10-numb
Er“
oneP3T°F""
77
Имвочы букв агтфавита). C.!IC11OBaTC."lbHO, все операторы сравнения могут при менять-
СЯАКО всем числовым типам данных. Однако значения типа cool МОГУТ сравниваться
с .
Тшько на предмет равенства или неравенства. поскольку значения пгце и false не
упорядочены. Например, выражение true > false B языке C# HC имеет смысла.
для логических операторов операнды должны иметь тип bool. Результатом логиче-
ских Операций также являются значения. имеющие тип bool. Логические операторы
8” д’ A и ! поддерживают базовые логические операции AND, OR, XOR И NOT
COOTBCTCTBBHHO. результаты выполнения которых соответствуют значениям, приве-
денным в следующей таблице истинности.
р q p&q р ч р^ч
false false false false false true
true false false true true false
false true false true true Lruc
true true true true false false
Например, из таблицы видно, что результатом выполнения оператора XOR будет
значение true, если только один его операнд имеет значение еще.
Рассмотрим программу, в которой демонстрируется несколько операторов сравнения
и логических операторов:
// В программе демонстрируется использование логических операторов
// и операторов сравнения.
using System;
class RelLogOps {
publlc static vo1d Main() {
1nt i, 3;
pool bl, b2;
1 - 10;
j - ll;
lf(1 < 3) Console.WriteLine("1 < 3");
1f(l <= j) Console.WrlteLine("i <2 3");
if(; 1; j) Console.Wr1teLine("i Ев j");
lf(i -- J) Console.WriteLine(“3Ta строка не будет выведена.");
lf(l >- j) Console.WriteLine("3Ta строка не будет выведена.");
дЁ(1 > 1) Console.WriteLine("3Ta строка не будет выведена."):
bl — Lrue;
b2 — false;
lf(D; & D2) Console.Wr1teLlne("3Ta строка не будет еыведена."):
д5(!(Ь1 & b2)) Console.WriteLine("Peaynswawom вычисления"+
" выражения !(b1 & b2) будет значение true.");
-г -1
~*(0- 1 02) Соп5о1е.И:1пеЬ1пе("Результатом вычисления"т
выражения bl I b2 будет значение true.");
;f(D1 ^ b2) Console.WriteLine("Pesynswawom вычисления"+
" выражения bl ^ b2 будет значение true.")i
.,
78 Глава 2. Типы данных и операторы
РСЗУЛЬГЭТ ВЫПОЛНСНИЯ программы СЛСДУЕОП1ИИЁ
1 < ]
1 <— т
L ‘~ j
Pc3vnb_d1oM 5u1V-nc-MH nupdxekvn то- а 03‘ EVA: зтачетяк сене
Резульдсъсм вычисчеьун вьтрджот ля :;-- х 2 b.J.LL _ з: иЧк H,-‘e е: и .
Результаеом вычислетия выражения од b2 буде. зьчче Ио rue.
Быстрые логические операторы
В Cir npc;1_wv101 реньт епенштльньте бьтсптрьте версии лот ически\ оттсраторотз 1-‘.13 тт
при мепользоватттттт которых программа будет вьтнолняться быстрее Рассмотрим. как
они работают. Если при вы полненин оператора AND первьтм операнд имеет значение
ts‘. результатом будет fa 1 ее, независимо ог значения вгорого операнда Если при
выполнении оператора OF. первым операнд имеет значение ttut. результатом будет
значение Егье, независимо от значения второго операнда Следовательно, в ›гнх
случаях нег необходимое гм оценивать вгоротт операнд. н соответственно ттротрлммтт
будет выполняться быстрее
Бьтстрьтм оператор AND обозначае гся символом ь ;‚ а быстрьтн оттсра гор ГР. — симво-
лом | Единственная разница между обычном м быстром всреттямтт операторов состоит
в том, ч то обьтчньте операторы всетда оценивают и первьти„ м втором оттерандьт. а быстрые
операторы оценивают втором операнд только при необходимости.
Ниже приведена программа, в котором Демонстрируется использование быстрого
оттератора AND Программа определяет является ли значение переменном а кратным
значению переменном г Это осуществляется е помощью оператора взятия тто
модулю Если остаток деления п, d равен Homo, то значение переменном т является
делителем значения переменном п. Однако посколвк; оттсраштя взятия тто модулю
npC,H}C\laTpHBdCT ВЫПОЛПСННС ‚ЦЁЦСПИЯ. В Hp0Ipd\l'\lC HCIIO.H:5)C'TC$l 6bl(,IpblH ОПСрЁПОр
3*.1\'_ Штя предотвращения ‚деления на ноль.
// B Upo1paMMe ДемОнсдрируе-ся нстользовамис бысьрото овераьоюа AND.
us-ng Syster;
5-555 SCOps т
риЬ1Ас зсдсъс мота Ma1n() {
An: n, G;
n 1C;
d 2;
*f(d! 0 и (п а о) ~« Ц)
Соп5о1е.Игь:еЬлпе(о f " является делителем числа " n);
d 0: // Теле ь т исваиваем Le еменной d значение О.
P P
// Поскольку переменная о имее- зтачеьуе О, второи операнд
// ‚
‘ “С 0”‘-“V-5de+C’* - [lpu-ucncnuc 6bI(.lpOl() оттсратора
лЕ(ё ' О && (n G) С) преютщмцметдсшнпенаноц
Сопзо1е.ИгАсеЬ1пе(о — " явлнеген делителем числа " ~ н);
/* Теьерь в выражении условия Операдора ей Dyna: MCLOflb303aH
обычный оператор AND, чго приведет к делению на ноль.
ч
‚мицрптэвпбшддп чаи-впаян ‚ ‚
операторы 79
*1; (d ' О & (п о d) O)<—~[O11I.HMmuo1ux оба опсранда но приводит к астснию на ночь I
Console их *gL;ne(o L ' HbnHL1CH дели слем числа " т п),
)
11}! HpCLl0TBp<lLL[CHIl5l ДСЧСНИЯ Nd НОЧЬ ВН i‘ld К, оператор IIDOBLDSILT HL РАВНО IH
}u)H0 ЗЧАЧСНИЬ ПсрСЧ&ПН0П ELIH pdBHO [O бЫЬ1рЬП|ОПьрЦ10р х fl OLllHdPTHBlL1
1: ILHLHUICL выпо тнспис оттсратора и „тс тспис по мо 1\ no пс ш шо шястся Носкотькх
д; тропраммс в первом стщас значснис псрсмсннои равно 2 выиотнэтстсэт опсрапття
в; пия по могц по Во втором с mm; псрсмсинои присваивается заве юмо нспри-
„мтсмос дначснис О и опсрация взятия по мо птто нс выполняется в pus» ты пе чего
прс тот вращается те тсние па но n, Накоисн в трет ьсм с цчас пспо тьлется обычныи
дпсрагор AND При этом оцениваются оба операнда что приводит к ошибке выпо т-
псния программы при попытке Детсния на ноть
Проект 2-2. Вывод таблицы истинности
логических операторов
ЕЁ LogIcOpTablB cs
B JFOM проекте рассматрнвастся создание программы которая выводхтт па экран
табтицу истинности погичсских операторов B программе быти испотьзотзаньт эте-
мснты C# — одна из езсаре-постедовате тьностеи и тох ичееьие операторы, описанные
в дтои главе В проекте также демонстрируется раз тичие в приоритстностп межах
арифметическим оператором + и тогичеекими операторами
Пошаговая инструкция
| Создаите новыи фаит и назовите его ‚а J J_'\lI т 1
2 Цтя выравнивания сготбцов табтицы испотьзх итс езсарс-пос пс товате тьпость
которая вставляет симво ты табутяиии в КАЖДУЮ строю вьтвоча Например прсдь
ставтенныи ниже 0l‘|CpdTOpE р сё‘ те () вьпъочттп затотовов ПЯ табтпЦы
Console Wr1teL1ne("P\tQ\tAhD\t0R\tXOk\tNOl').
‚д:
Для каждом следующеи строки табтицы использхите симвотьт габх тяции, чтобы
поместить результат вы потнения каждом операции под соответствующим за: о гтовком
4 Н иже пре тсташен по тныи код программы которыи нпкно ввести в фант ЬЁ9щ`
а1ОрТгЬ1е cs
/it
Проекх 2—2
Выведи- на печаль “аблуцу I/L, VH1 LC 1/ oivuec-<1/nc D ера ODL5
*/
uslng System,
class Log1calOp"able {
publlc static V0-0 Ма‚п() т
bool p, q
80 Глав?! 2 ТИПЫ данных и onepampbj
4—»» ”?" ————""
Ponsg (з. и, д} {L [\ Q\LP«.'\I Ъ. 4 Г J
р 1
ь п
„с д
Lo ь L I )
„им, к Ir L [мы ) (г м) )
(,1 :.O L. $1 L I К
[L 1 эк, U
к И &
( AL L I
L д: (I \ q )
1ь<-‹ их (К\ь I )
их р )
Ъ (шипы mpII11L I IIIIILIII L llp()lpl\l\I\ B рыу 11 IL LL 1111110 111ь1111я б) 1L1 BURL-
1LIII L IL lVI()lI1Ll$l 115111111
О Ar ы Ж \Ln I\-'_-
I к. шик, L L. L 1 .3 ‘L эк,
I rd д к:
„д к, 1 L L I
i 1 [1 L Ч L I ь 11 e
6 Обр IIIIIL BHHV1LlHH(. -11о 10111 как IL 0lL}‘1l1()pIl \l\l3llHlL\1[)lLH\1LlU1l\
H II "д 1) H КА ILLIBL HUD ЛИК IP01} ilk H()‘lLHbl Ii Бру! HJIL (,I\06l\H 910 }lL()U'
\o1I1\1o U0(.kO]bl\\ 11 С ‘I OIILDHOD n\1LL бокс 1зь1ьо1<1111 1IpI10pII1L1 ЧЬИ 10111 IL-
LIN IL 01 Lp¢III)p1,I
7 П011ь1111111ььь L I\I0LImI1L 1110 \101111{›и1111ров11ь процъших l1l}\H\/I образом 1110611
11.1 зьрдп 111‚1но111111ьь LI шю 111 I H I) ImLLI0 L 11111 L и B03\l()/KHO 310
31 111111 оьмхьця 1LILI.II0 НПО ыш1х11п11'
Ё"? Ответы трофессаомала
3
Вопрос 3IIL\I 111. щ 11111 061111111. (IIILpI1LIpII 11 LL III II HL,}\() (Ц)! 1\
ь I‘ ч шк быьгрыь о11ьр110р11 51111 1101151 бо ILL IIMILILIIIIIIIIMIII’
Ответ И11о1 11 H oIILpIInp1\ и IpL6I11L,IoI1LImII 061 oI1LpIII 1.1
Э п 11 IIIuLIpIIpyL1LsI L IL1\I0II1LII npo1pI\I\IoII
Ьцк 0.12 ршраимд ч 1 V д м p/pyL ч „азы/ц ч
y:-I‘I»1r‘I(.1I/v б I F’ ч „ч пр в xI\
Чуъ L
I а с. rf s
‚д. l( 1.1.11‘/I
д штанин-чают 1 .
«тупим
Оператор присваивания 8 1
т- в дыдджьъии \,.. для о сэра 0,3 L -< теъсм 11 о
F1/MCI Lid ы L,d11 F’ /"‘Kpe[\«19} 1, -Ч"(Г‘ е \ С‘ 11 Ж} Н
‘ fa ье и < и ))
Conso-e (Ar е; "e( 3 а икота не дуде ‚Ас
/" CJ1G,..lyICLLU/‘M o1epa1opo1v1 буде аычеденс
/ 3]-'l‘—1el ие ХССЭЫЕНЪОИ l, РАБЫ Ё
.31 о-е дг-сеь_п (‘Ьсш от ера р 1/141<pr~w=1 с бут т (н,
' терсмстчая _ трите- з, амтте 11,
' И э-ом случае < теркметьоу те by e {VIM ‚ ттт/ч
v11<peME.111a, тоо-сот усу 1./1-1 С} aye ч оьтстгт И :1 П
„Цаыье && (— _ к -.‚ъ))
Сопьоте Ик1:е1„пе("Э-а с рожа ш оуи чт
/ Следующим от ерадором вновь буще шчо ‹
// значение теремах-твой 1, равное 1
’.._)'1SO.LC Vvr1LeL-ne("EcJ11/1 от ера ор инкремантч о»; т т‹ т
.. . )
переменная l [[31/MP1 '?{"i€=1 ие
тхохтхтстттарии _\1<a31,md101, -1T0 тт выражении и топия портит п оттсртторт тт к ттсрс-
щннои 1 будет Применена оттердттия итткрехтснта нетаттттетпто ог нкттнтттш пн тчырдт-
тьння Но при использовании быегрото оттсраторд значение нсрехтсттттотт те бтттет
н; тнчстто на единицу. поекотьку пьрпыи оттсртшт пчьет зттдттстттте |о есть
3 пт нрииср показываег, что ее ти в коде нсобхоцихто 1111 то тттттть нртчкпч 1 1~111p.111111,
Hl\(L1$lU1(’l"OC$l справа от оператора Ка". вы ‚то тжтш [дно -I или т пбц ”|V1() форму
‚ ого оператора (то же касается оператора 1)?)
т? Минутный лрактикум
у” л 1 Какие деиентия производит оператор ттаятт в 11111111 то! )' к т ттщ 111 111\1
Q ) 4" данных он хтожет бып. нрихтеттетт’
‘^ 2 Kdkl/1C ГИПЫ данных M0131 нетто тьтоваттеэ ¥\ I 11- к отит нт т \ 1111111 11\
операторов’
3 Всегда ли быетрыи оттермор (зтттчшвтт об: с ‚их откр 111’
Оператор присваивания
\1ы уже неоднократно иенотыотм ттт (тперцтор TIDHLBJHHIIHHH в 11pnv1cp.1\ lIp()Ipd\1\I
нон кпит и, а теперь рассмотрит сто бо тсе подробно Оператор присваивания хказы-
‚мстит как одинарным знак равенства (п) B C11 он работает практически шк же, как
3 Оператор взятия по модулю возвращает остаток цслочисченното детения Он тиожег бы гь
применен ко всем числовым типам данных
2 Операттдьт логических операторов должны иметь тип по 1
3 Her, быстрый опсрагор оценивает второй оттератттт тннть тот 1.1, кот .1 р‘ ц -11 т. 111,1p1>1\11-11:1
не тьзът опрсдстить. ттеходя ш исгинттоеги то п ко нерьото ттттгр ш ш
82 Глава 2 Типы данных и операторы
в любом другом языке программирования. Оператор присваивания имеет следующий
синтаксис:
Va: - GXPIESSJOH,‘
Тип переменной должен быть совместим с типом выражения
Оператор присваивания имеет одно очень ингересттое своиство„ с которым вы,
возможно, еще не знакомы. — он позволяет создавать «цепочку ттрисваиванииы
В качестве примера рассмотрим фрагмент кода:
lnt х, y, Li
х — y — 4 ~ 100; // Переменным х, y, 4 присваивается значение 100.
B ПрИВСДСННОМ ВЫШС фрагменте С ПОМОЩЬЮ ОДНОГО ОПСрдТОрЦ ПСрСМСННЫМ X, y И 2
присваивается значение 100. Такая последовательность переменных и операторов
ДОПУСКЭСТСЯ, ПОСКОЛЬКУ оператор : присваивает ПСрСИСННОЙ, ННЧОДЯШСИСБ! СЛСВИ ОТ:
НСГО, ЗНЭЧСНИС ВЬЦЭЭЖСНИЯ, НЗХОДЯШСГОСЯ справа. СЛСДОВЭТСЦЬНО, ВЫрНЖСНИС Z = 1О0›
будет иметь значение 100, которое присваивается переменном у, а затем — ттеремен—
НОИ X. ИСПОЛЬЗУЯ «ЦСПОЧКУ присваиваттии», МОЖНО ОДНИМ БПНЧСНИСЧ ЛСГКО ИН ИЦИ8‚—=
лизировагь группу переменных. I
З
Составные операторы присваивания
В С#‚ как и в С/С+*‚ имеются составные операторы присваивания, в которых-
арифметические операторы совмещены е операторами присваивания. Рассмотрим
применение составного оператора присваивания на примере. Выражение
х — х + 10;
может быть записано с использованием составного оператора присваивания
x +—— 10;
Пара операторов += указывает, что компилятор должен ттрисвоигь переменной х-
значение выражения х + 10. `
Приведем еще один пример. Выражение
X = х ~ 100:
можно записать как
х = 100;
у
Оба оператора присваивают псременнои х значение х 1C’J .*
Для всех логических операторов (то есть операторов, требующих два операнда) сущест-
вуют составные операторы присваивания. Синтаксис этих операторов следующии: `
Var op = ехрге$51оп;
Таким образом, в Си имеются следующие составныс операторы присваивания:
+= _= x- /=
%- &= [н *=
Составные операторы присваивания в сравнении с их «обычными» аналогами имеют ДВЗ
преимущества. Во-первых, они более компактны, а во-вторых, их использование по3в0-_
ляет ускорить компилирование кода (так как операнд оценивается только один раз). ‚
По этим причинам составные операторы присваивания часто используются в пр0-`
фесеионально написанньтх Сй-программах.
paws 33
one _.1E_V-,-4. „и _‚
Z
преобразование типа В ОПЭРЗЦИЯХ присваивания
В „ртра\1\п1р0ваппп часто применяется присваивание значения переменном одного
„т, Щрсченнои триого ш на Папричер вы иожсхе присвоить нтачсние птсрсщн-
Но” „ша псрсчсннои тина t1 г. как показано ниже
lf‘L
f-LJ
l
f рЕ/ ЬСЬ} ИО ir.3‘i€1*'|/‘R .1/Illd lflt ITCPGMEHHOM ;И З f4_Oé\r
Emu в онсрании нрнстзаьтвании используется совчсспшьке шны ‚ннных I0 1ПП
псрсчсшюи н.1\‹)вннснся справа 01 оператора шхт0иапичссм| нрсобразхстся в гин
псрсчспнон наш тяшснся с шва от нсго С тсшковатетьно в нрс кышщсм прпчсрс тип
псрсхясннои прсобрапстся втип f4_(:‘j . а затеи значение персчснногл 1 нрисваивасгся
псрсисннсътя Но носко льну (‚д яшястсэт языком со строгих: ьонтротсч гинов то нс
все его гины шнных ив ннотся совиестихнлии, и стедовате rum), нс все типы преобразо-
вании разрсхнсньп Например типы Данных 1,001 и шк нссовисстииьт
при ПрПСН‹1ПВ‹1ПРЕП ОЁНОГО ТИПа laHHbI‘( flCpC\{C}-IHOI/I другого ТИПА автоматическое
преобразование ТИПА ПрОПСХОЦИТ. ССЛИ
О два гипа данных совместичьт,
О конечным гнн (стева) ботьшс исюдного типа (справа)
При НЫПО IHLHIIH 1_lB\.\ )1 ИХ УЬТОВИИ I1pOl/I(.‘(OllI/IT[)(ILLtlL([)}lH)L£(€(1 прсобразовтгние Hdllpl/1-
‘\1Cp }\OIIl‘[C(.IB¢1 6|/HOB, ВЫ[С1СПН!›1\ ДЛЯ Tlllld ДАННЫХ int B(,CI;ld ДОСЛсПОЧНО ДЛЯ
XpdH(.HH$I BLCX IL‘!/1(.'IBH'lC lbHbIX 5Hd'[CHl/HI типа " A. I e, d IIO(.l\O IbKV 06d )[II TM1'ld
llL'I()‘Il1LlL.IIHI)IC ТО 1-х НИИ \lO1K€T бЫТЬ прииснсъто dBIO\1dIl/1‘ICCKOC HpCO6pd3OBdHl/1C
ДПЯ ВЫПО ЕНСНИЯ раснтиряюннях преобразовании BCC ‘H/IL IOBHC IMIIH, Bl\'1IO‘{d5[ L[C'IO-
‘II/1(.'|CHIII>IC :[:lHHb[C И ДдННЫС (, I'l’ldBd}O1JICl/I 'lO‘{KOI/I, ЯВЛЯХОТСЯ COB\/1CCT]/[M bI'Vll/1 Hd1'lpM-
Мер, В с IL’ l\/l0lIlCl/I I[pOFpd\1MC ВЫПОЧНЯСИОС HpCO6pd3OBdHl/IC [LC]/l(.TBl/lT6fIbHO, ПОСКОЧЬКУ
Преобразование “ЦЕННЫХ ТИП?! Long B dabble $IB'I5lC'J СЯ РЭСПЕИрЯЮПЦИМ И ВЫПОЧНЯСТСЯ
aBTO\1dTH‘lLLKII
H Ь Цыцчммс ‚юмомдрирустся автоматическое треобразование ина
// 11 теснин из сот; н ооигде
Цздгч ‘gm
C dS~.
D“ At C vo d M11n() (
Ч L,
1 Е,
L ‹ _ Автомапичсское прсобраъование тина значения
из lorg n lcuble
сиг С011г<_("1_.1 D "+L ""+D),
о
т" Цшсетвует авгоматическое преобразование т ипа пере-иеннои из long B болоте,
Ратное автоматичес кое преобразование осуществить нельзя, поскольку оно не будет
Выполнение операции приведения типа между
несовместимыми типами данньтх ’
84 Глава 2 Типы данньтх и оператор
рьЕСПЦПРЯЮШИМ CfIC,l0I5d'[CIIbHO. '[dKd$I версия ПрСДЫДУЩСИ ПрОГрЭИМЫ будет НСДСЙ
НИ I С {ЬНОИ
"' 3 d ре рамма ее bx; C< M и ьрчздъа '**
т 1 sys-e'*1/
"-ass LtoD
puo--c <ta:1c vo_a Na;n()
O L
mg Нстьзяантоматичсски прсобразонать
-0lJb*e 3 ncpcmummo [Mild coubl; в титт long
D 00-23283 O,
L D, / такое преобразование недеис1ни-ельно '
Сопъосе Иг_сеЬ_пе("Ь и L ' + L + " ' + D),
I
B тонолнеттис к уже указапньтм ограничениям необходимо добавить, что не суше
вмст автоматического преобразования между типами declmsu. и floaL или doub
тибо преобразования пере-пенных чистового т I/1["ldB1‘lCpCV1CHHblC типа ст at и.ти boo 1
Псреметптьте типов т ч: и т 1 такяе несовместимьт арут с другом `
Автоматические преобразования очень улобпьт, но они не отвечают всем потребн
стям программирования, поскольку в них допускается расширяющее преобразова Г
только между совместимыми типами данньтх Во веех друтих случаях приходи д‘
применять операцию приведения титта Приведение типа — это инструкция комп
тятору для преобразования одного типа данных влрутои Такое преобразование тип от
l-lHIlI>l‘< SIIYISICTLSI ЯНПЫИ Операция при ЮЦСНИЯ типа |l'\/ICCT С ЛСДУПОЦЦИИ (‚ИППАКСИШ -
’L1LgL?'LyP9) expression
Конечным тип (target-type) yl\d3blBdCT, к какому гипу должно быть приведено выраже
Пак, cum необходимо преобразовать выражение к/у к гитту „от, следует написать‘
аоцр-е х, y,
//
(mz) (x/ y)
B mm примере приведение типа преобразует результат вьтчисления выражения
тип) м, хотя переменные х и у имстог тип cobble Круттьте скобки, в КоТОРы
зактючено выражение х/у. явтяются обязательными В противном стучас приведеНИ
K типу т ° будет применено только для переменнои х. а не для значения рсзульта
‚тетения Здесь применяется оттерапия приведения типа. поскольку автоматическ
ПРСОбргНОтИНИе данных типа то Jule B ТИП шт не может бьтть выполнено
Когда в результате приведения типа происходит су жшощее преобразование. иттформа
пия может быть утеряна Например, ее ти при приведении данньтх типа long B Ш
значение переменном, имеющем тип long, вьтидег за диапазон значении. допустим ‘
для типа тис, то биты старшего разряда будут удалены, сгтсдовательно, информаЦ '
Ь1Х
НеНИе one аЦИИ Приведения Типа Межд НЭСОЁМЭСТИМЫМИ TW1aMI/I Данн
выП0п‘_ _ __ т? __ __ __7 I
гг
„ьряпд Еши значьниь ь п шпюикьи [очкоп пршюшця к l1(l()ll[Ll\,HI1()\1\
шобныс KO\1lIOHLHTblVIpd‘IMBdI()I(.$I IIO(.l\O IbKV при I mm IIpLo6p I mu 1Ч111| они
Пдпричьр CL 111 мы шшь 1 2? прьобршык I Is |IL.1()'lIlL KHHHI1 тип
будь!
1uII\
mop ILI ШПОН)!
ых п IIp\I0IIII1\I бх ш 3H1‘ILIII[L 1 а 1р0бшш HUI U 23 бх ш Иьрчш
Р
1 R 111117О1)1\1\11 [L\!0HLT‘)||)\|()III15l Hkl*()I0‘HL 1311111 11 кОПЧНО 111111
Нить их It 1 It I I I 1
С ныло: ;0nIIIIIL\I опер ILIIII1 Ilpllm ILHWI Ш ш
/ M I» I я I
U5, I 1
С1аа
р д I V’ ы P 11n()
\
”pH:l\lTpL()hpl:1| пни; шин и T) 1
gnu mIoIIIm ЦК п т ш о щи IIIL H J
к //
E /
) (3/y) Bbpa)KC'Hl/€ УМ ‘с хек о '=
‚’ к 11/ y п
L. I е... п‹„( „е [ан ‘id ч рауен ч =: v ч 1
2,1&Ll IIHHIIL НС, ТСРЯКПСЯ IIOLKO ll: ¥Hll(HH—L П l\31H 51 1 П K Hk
D ‚д ,J.\Il\(.l|‘l'\1b|\ JHA ЦНИИ OIIPL lL LHHHX IH 111 11. )
LL И L. 1 1 ёЧЕ‘) I'I, r NJ
B этом ‹‚ [V не информтция тьряьня носит п к) $1 I IuII:”\" Пни ll l\.}| 31 1
b I 4?, IIDLICIAMM 1!/llII"H()Hd ,.[0I])(.lHMblX ПНЦНИП ш p(,1LlLHHlI\ шя IHIII
C к L пс (‘значение гьремеъ и L р II э ›
Г‘ дьца rye ’\SC- ‘cane ‚ц я ы/мц „тю Х а )
(‚Н а! ) О ъьнис опьрчпии Ilpvmcl пня пи | MI ж ц ньышмььп шшн 'llIlll’s{|1—|
Г ы ( ч)
1
}
Ре „
3\11111 1111110 IHLHIISI IIpO[pZ1\l'\IbI НЫГ 151 [ИТ L К. 1\ П11111Ч‘ 31 10“
Це
“а” ь; зжгъин х у p3.:I—a Ч
ЗНаче/ъу к п
€‘MJhCI/ ‘ч oa-I40 Оь
знача 1:.)-‘PO.’ D раыо 1
Ch )._
B дт
OH ,
пр‘ 1117011) l\I\It, IIpHBL 1LHHLpL3\ Tl 1'1T1I1I)|H1LILIllI$IlH|pl/¥\(_ 11151 К 111 IV
ПИ r
mp ‚Ш‘ '\ О op ILMIHHIIIO lp06IIOII ‘[dLTIl II IIOFLDK 1|llfI)()] \I1IIIIII Ч nut In II
Lu
ma‘ ‘1 “О! пръяиъаннпыы! значении IOU I1II([>0p\I.IIIIII HL 'lLp$lLIL$I lI()kl\0 ll}~ ) и
1С111
о] ‘L ’”\01l||t5I Н lH{1l‘Icl5OHCJ.0I1\LfH\1lI)\ 3H l‘[LIIl1ll 0IIpL IL генных LI}! I III 1 K
1 L
чацир‘ Нпьпм прпывшпь пьрсмьпнон дн тишь 237 иронии шт пшьря пнфпр
П I
Лен" 1()1О\1\ 1ГО IIIL 10 237 llpkllbllll lLl' '\‘l<lKLl1\’[x1 11 110 IOIIVL1 l[‘\1()( iH1lLIH1k UHPL IL
Од
Типа ll 1111111! .’ И Hd}\OH(.Il при ПрИьВАИН 1111111 Uh] КНПЯ 111Г11 I'lLPL\IL 1111011
1111(1)Ор\1‹1111151 HL TLp$lCI(.$l IIOLKO |bKV MLHU 11 “Ь ЬЯ ПрПНь ILHHL I/H11
M.
86 Глава 2. Типы данных и onepaBF:t_>|_
Что такое операция приведения типа?
Можно ли без выполнения операции приведения типа присвоить значение
типа short переменной типа ;n:? A значение типа byte — переменной
гипа char?
3. Запишите оператор х = х + 23; другим способом.
Приоритетность операторов
В табл. 2.3 показан порядок приоритетноети всех операторов Сд от наивысшего к
самому низкому. В таблицу включено несколько операторов, о которых будет
рассказано позже.
Таблица 2.3. Приоритетность операторов в С#
Наивысший
О П ++(постфикс) --(постфикс) checked new sizeof typeof unchecked
! ю (cast) +(унарный) -(унарный) ++(префикс) -›(префикс)
" / %
+
<< >>
А
v
A
II
v
II
ЕЕ
>9е
= op:
Самый низкий
Выражения
Операторы, переменные и Литсралы являются компонентами выражений. Выражени-
ем в C# является действительная комбинация этих компонентов. Если вы уже
занимались программированием (или хотя бы изучали алгебру). то, вероятно, имеете
представление о синтаксисе выражений. Однако при работе с ними нужно учитывать
несколько важных моментов, о которых мы сейчас поговорим.
Преобразование типов в выражениях
В пределах выражения СУПЦССТВУСТ ВОЗМОЖНОСТЬ СОВМСШСПИЯ ДВУХ И ООЛСС рЁГЗЁППЧНЫХ
ТИПОВ ЦЕННЫХ при УСЛОВИИ, ЧТО ОНИ ЯВЛЯЮТСЯ СОВМССТИМЫИИ. Например. ВЫ ЧОЖСТС
l. Операция приведение типа — это явное [преобразование типа данных.
2. Да. Нет.
3. Оператор можно записать как х += 23;.
›—‹ „и... _. ‚.
mm -.~»..-.»- M чист. 1 ‚
выражения 87
/’‚___ ____
„отшстцатть в выражсттигт танньте гитта «Р I и J ‚ поскотьку оба зги типа яв тятотся
„щ говыми ы ти в выражении совмстттатотся разтичныс тины татттгьт\ отти преобра-
„готся в 0 титт и тог же ттггт на основе а тторитма пошагового преобразоватггтът (то Len.
I, L()()II1LILTl%LII1 с гтриоригегностью вытто тттсттття операттитт)
Нрсобразотзантгя выттотняются посредством использования принятых в C3 ттравит
аыппгимнического преобразования типов в выражения) Ниже представ тен атгоритм,
оттре те тенныи )гими гтрави тами штя арнфмегических оттерапии
) I с ти о 11111 опсратщ имеет тип одетое, го второи операнд автоматически
ттреобразхется к тину зе Ira‘ (за исктюченисм етутаетз когда он имеет гип
и ти р в этом с тучае проиъоитет ошибка)
) I с ти о тин из оттератгаов имсег титт а т т, второтт оттерагтд авгомагнчсскгт
преобразуется к типу 11^1е
) I с нт один операнд имсет тип г] DC г второи опсратщ автоматически ттреобразх-
ется к тип) т а’
с) Fe ти о1ин оперант имеет титг uiu:*q второи операнд автоматически преобразу-
стся к гипу 11101; (за исктючением с тучаев, котаа он имеет гип ~.':-jtn год
гг ити щ"г`ч‚ в эгих стучаяк произоиает отнибка)
) h1110r11111 OnCpdH'111\ICC1 гитт lcr о вгорои операттд автомагттческгг преобразуется
к титп аэг g
) I L1H один операнд имеет тип гг а второи операнд — тип это: по. <1^т или
*, то оба операнда авгоматически преобразуются к тину Lcr-.3
J I с ти один оперант имеет гитт т‚11’1т.‚. второи операнд автоматически преобразуется
к типу тз1г*
) Ести ни одно из вьтттзеуказаггньтх правил тге применячось. оба операнда преобра-
зутотея к типу 11 t
Обратите внимание на некоторые моменты, касающиеся правит автоматического
преобразования типов Не все типы данных могут совмещаться в выражснттях
И частности невозможно автоматическое ттреобразовантте данных тигта теса: и ти
в тигт ас ^ тат и невозможно совмещение данных титта 1 п; с тюбым трхгим
тпттом знаковых не точис генных данных) Совмещение этих типов требует испотьзо-
‚тгтття операции явного ттриведенття титта
Втнгмате тьно Прочи гаите постеднее прави то, в котором говорится что ее ти ни ОДНО
111 I1blILlCIICpC‘II/I(, ICHHbIX 1‘lpdBI’I'l НС применялось, то все операнды преобразуются к
титт\ ере Стедовагетьно, все значения, имеющие Тип снтгг, s1,».~e, h с э 1r,-rt И
1:1 , в выражении преобразуются к тигту тпс для выпотненивт вычгтсчентттт Гакая
тронеаура называется (I6m()II(/I?7!,{‘4€Ckll и преобразовагшеи к цеточыс теннопъ шит ЭГО
тике означаег что резутьтат всех математических отгерашти fix 5101 иметь тип.
котором» ття хранения значения ВЫЛС тено не меньшс битов чем ттггтх
B1,k11o ттонтгмать, что гтвгомагичсское прсобразование гиттов ттримсттяется к значсни-
т\т (отгеранаам) то тько тот га кот та выражение вычис тяется Например се ти внхтри
зыражеттття значение переменнои типа byte преобразуется к тигп яг то за преде-
‘амтт выражения переменная со\раняет титг I ‚иге
‘чгите, что автоматическое преобразование типа может привести к неожиданному
Резутьгату Например, когда арифметическая (терапия проводится е двумя значе-
ниями типа Lyra. ВЫПОЛНЯС гея такая посзтедоватетьносгтз аеиствии внача те операн-
lb! Ь‘ ее преобразуются к тигту 11‘, а затсм вычистяется выражение, резутьтат
которого тоже 63,101 иметь тип Jnt Реп гьтат операпигт ттад двумя значснгтяхтгт г) то
88 Глава 2 Типы данных и операторы
бУПСТ ИМС Tb 1 ИП J ht \3IO,10H().'IbH() HC())¥\MJIilIlII()C СЛСЦСГНИС ВЫПОЛНЕНИЯ BblUJCyKElJdHHOrO
правила, ПОЭТОМУ ПрОЛрНЦЧИСГ ЛОЛЖСН контролировать ГИП HCDCMCHHOH, КОГОрОЙ
6_\’.LlCI Tip]/ICBOCH результат “pl/IMCHCHHC правила ЦНТОМЭТПЧССКОГО преобразования K
I1CIIU‘IHC.'1CHHO‘\1y ТИПУ рассматривается В L.'IC,'1yl>Oll[C]/I программе:
г/ в прет рдммг: демоне рирусгся применение правила автоматического
я преобразования И немочь/г 1E‘l1rC'My [и V,
- ] ass Pro" Dex о
рек еще ::dt-u: v т а М2_п () t
bl/' C‘ “I K ре тулыату не применяется операция приведения типа, поскольку после
цт; -; атиоматичсското преобратования к целочисленному типу обоих оттератщов
результат уже будет иметь mu mt
b — 10,
tr ‘- I
A b h; /‚ В данном случае не требуец CH явное приведение типа .
“Здесь необходимо выпо тнить явное приведение типа, так как результат
b 1 J’ НЫЧИСЛСННЯ ньтражсния имеет тип мкг, а переменная b — um руке
b — (byte) (b ‘ с) ; ‚’ ' Тип резулдчзгч должен быть приведен
// K 1Иму переменной D.
CobSo-e. N:1teL1ne("3:—auem4s-1 т ‚дрема пых е v b: " * _ * " " + о);
Ж
Приведения типов не требуется, котла результат вычисления выражения b * b
присваивается Переменнои т, так как переменная .0 автоматически преобразуется к
типу кг при вычислении выргтжения Но сии вы попытаетесь присвоить результат
вьтчиеления выражения г т в перемеинои п. необхолттмо будет выполнить операцию
приведения типа обратно в тип гз-утп‘ Вспомните am, если получите неожиданное
сообщение об ошибке. возникшеи из-за несовместимости типов в выражении.
Такая же ситуация возникает при проведении операции е Данными типа char.
Например. в следу ющем фратметтте кода необходимо выполнить приведение типа
результата вычисления выражения обратно к типу с^аг. поскольку в выражении
переменные fin; И ч 2 Преобразуются в Тип ц :2
char chl — 'a', ch2 ~ 'b';
„и: (char) /chl сиг);
ЬСЗ Up!/lBC,[CHH$I ТИПЭ рСЗУЛЬТЭТ СЛОЖСННЯ ЗНЭЧСНИИ ПСрСМСННЫХ CI]: И СПЁ I/IVICCT ТИП
1n‘ И НС МОЖСТ бЫТЬ присвоен ПСрСЧСННОИ ТИП?! char
Приведение типов применяется не только лля преобразования типов при присваи-
вании Например. в следуюшеи протрамме для получения цробнои части результата
вычисления выражения результат приводится к типу dc Ible, поскольку без операции
приведения он имел бы тип Ln“ и его пробная часть была бы утеряна
1,1’ B lpOI рамме Д9МОНСЧрИ}\е1СЧ {pl/lb/Ct C-1*]/E OI ераьии „РИЗЭЫСНИЯ айда
I
К рез1‚'тть.зду вычигдшттия вьтрахеъич.
чьих; Зузеетт;
сдаьь UseCdst '
выражения
оцо1;с зсастс vold Ma1n() (
дн; 1;
LQr[l — C; ; < 5; -т*)
соп5о1е.И:_ЕеЬ1пе("целочисленный резульцац вычисления выражения "
~1+"r3: " + - / З);
Соп$о1е.Иг1ьеЬ-пе("Резульга- вычисления выражения, зЫ5ОЦиМЫЙ" т
"C дробной частью: тЭ:#.*#}",(ёсыо_е) 1 ' 3);
Console.Wr1:eL1ne():
Ниже показан результат выполнения non программы:
ЦЁЬЁЧИСЛЁННЫЙ результат вычисления выражения О/3: С
резулдгаг вычисления выражения, выводимый с дробной частью:
Целочисленный результат вычисления выражения 1/3: О
Резулацат вычисления выражения, выаодимый с дробной ЧаСД=ЮЁ .33
Целсчисленный результат вычисления выражения 2/3: C
Реьули_ат вычисления выражения, выаодимый С пробной часаью: .67
Цеточисленный результат вычислеьия выражения 3/3: 1
Результат вычисления выражения, выводимый с дробной часцзю: 1
велочиеленный результаг вычисления выражсьин 4/3: 1
Pc3;Lu:am вычисления выражения, выводимый С дробной ЧаС1вЮ1 1.33
ответы врофесеедвеуте
Вопрос. Происходит ‚ти преобразование титтов в выражениях С унарными
оттераторами, такими как унарный минус?
Ответ. Да. Для унарньтх операции операндьт. 3 KOTOpb[)\ диапазон Допуегитмьтх
5НЦЧСНИИ меньшим, чем y mt (такие, как т усе, звуке, sn::: и тстгг ), преобра-
зуются к типу mt. Операнц типа cm: Ii1K)KC преобразуется K типу ц- Кроме того,
CL.Il1 операнду ТИПЗ Ь1Г.Ё ПрИСВЦИННСХСЯ OFpl!IlilIC_IbIIOC JH(1‘lCIH/IC. OH I|[)CO6p.\J)/C”lC$I
k fill!) long.
Использование пробелов и круглых скобок
.1 тя улучтттения читабельности программы выражения в CS могут содержать символы
Ъ-бутяции и пробелы. Например, следующие Два выражения аналогичны, но второе
мтрдзцо легче ДЛЯ чтения:
3‘у*(127/х);
:0 т‘ у * (127/X);
km I‘.1blC СКОбКИ повышают приоритет оттераторотт, Содерякттттттпея внутри тттт\ (скобки
ЁКПОЛЬЗУЮЧСЯ точно ‘так же, как в гьттебрс). Использование Доттотнитецьньтх крутых
ккобок не приведет к отттибкам и.ти заметшснито вьтчттегтсттття выражения, поэтому W4
‘Южно применять для определения точного порядка действии и, следовательно, ДЛЯ
90 Глава 2 Типы данных И опергТоры
3 IV‘lLIJLHl/151 читабетьноеш программы Например о невидно что второе выражение
читается проще чем первое
к y J 94*terp+ 2/
Х (у 3) (34"1'-WP) 14
Проект 2-3. Вычисление суммы регулярных
ВЫПЛЗТ ПО кредиту
§'Z}RegPaycs
Как Уже говори Юеь тип паяных тет ггэт особенно хлобно иеио тьзов тть в денежных
внчиегеъпиш Преттагаемая программа вычиетяет L)\I\I\ pLIVI$lDIIbD( вып шт по
кредип (например на покупку автомобип) Принимая иататьпьте пиные срок
кре 1ит1 шею п гагежеи за 101 и ветичину Процентов по кредит» программа
вы гиетгег размер о тнопо иттпежа Поекотьк) это финиковые впчггетения Inn
IIpLl(.TdI3'1§,Hl/15! '1dHHbl\ имеет емнех иепопьзовать тип а: 1Па B этом проекте
гемоиетрируетея применение операции приведения типов а также метотт Э д к) из
бибтиотеки Ci:
:11}! ВЫЧИСЧСЧИЯ pd3V1tpd I-[’ldl‘C/Kd I/1(,flO'Ib5\Cl‘L5I (.IC[1)}0llld5l (I)0p\l\ Id
ImRa1e~(Prmupal / РауРыУеаг)
1 (НтКате / Pm PCFYLAI’) + 1) "“’Pe'Y""" N‘”“Y“"°
Раутепт =
где в перемепиои ‘а ч указывае rm! процент вып HI no kpL 1ип в иеремеииои
Pr rwr рад. еодержитея значение стартового бапанеа в перемепнои “а, V ‚п:
указывае гея чис по птатежеи в под а в переменном NLr“1ea1 s задаетея срок погашения
кредита в годах
Отметим что в знаменатете формх ты вы дотжны возвести в етепеиь еоответеп виошее
Значение Дтя этого иепочьзуетевт математическим метот М. г E () Вот как он
ввязывается
remut Уайт Pow(Dase exp)
N1CT(),..l :L'1() возвратиает значение ОеНОН1НИЯ етеиени ( p) mum ILHHOIO в ио-
казатеть егепени (a.-'1) ApI\'uCHrbI \1LYO'ld “JV () LIOIIKIIII имеп тии а те и
возвращаемое \1е го том значение также бх тет име гь тип г; Это ози 1чает что
l5d\1 HLO6‘(O‘1l/IVIO I/lCI10'Ib}OBdTL» привегтение ГИПОН ДЛЯ преобртзования ЧИП! "'.,1 l(-Z‘ B
ТИП O’-J д"
Пошаговая инструкция
1 Созааитс новыи фант и назовите его «ера ь
ru
ИСПОТЬЗ) H1'(. B IIpOIp‘l'\1\1(. L'IL,l\/IOIJIHL IICpL'\H,HIlbIL
пес, о- [1 пс т, J. 3} тче we a по 6.: ь а
do а п Pa е. Проще вы т о кре ‚- к а ум I L
ее ..'"-fl; '3ayPu:Year и ‘(с учет до 1 a €>'\\_I/L а ‚а
оеедпац Nu'IVeaY:; / Срок ко; ашы ия кредита
ОСЫ-НЧ Pd) ЭПГ // Размер тгщежа
Бьдражения
// Вспомогательные переменные.
// Основание и показатель степени
// для вызова метода Pow().
dec;mal пышек, denom;
double b, е;
(Поскольку большая часть вычислений будет осуществляться с использованием
данных типа deC:mal. большинство переменных имеет тип оес1та1).
Обратите внимание, что за каждым объявлением персменнои следует коммента-
рий. объяснякнций ее использование. Это покнлает понять, какие функции
выполняет каждая переменная. Хотя мы не включали такие комментарии в
большинство коротких программ. приводимых в этои книге, нужно отметить, что
с увеличением объема и сложности программы эти комментарии часто становятся
необходимыми для понимания алгоритма.
Добавьте в программу строки кода. спсцифицирующис информацию о кредите.
В программе заданы следующие данные: значение стартового баланса равно
510000, проценты по кредиту составляют 7.5%, число выплат в год т 12, а срок
нопниенгнткрслитаб лег
Pr_nc;pal — LOOO0.0Cm;
1nLRate = 0.075m;
PayPerYear : 12.0m;
kumfedrs т 5.0m;
Добавьте строки кода. в которых производятся финансовые вычисления:
numer = IntRate * Prlnclpal / PayPerYear;
е — (double) —(PayPerYear * NumYears);
г (double) (IntRate / PayPerYear) + 1;
scnom = 1 — (declmal) Math.Pow(b, e);
Payment пышек / denom;
(Обратите внимание, как нужно использовать приведение типов данных для
передачи значений методу Pow() И преобразования возвращаемого значения.
Помните, что в C# НС существует автоматического преобразования между типами
плнных decimal И double.)
Завершите программу оператором, который выводит значение ежемесячной вы-
нлатьп
:onsole.Wr1teLine("Paamep ежемесячной выплаты: (O:C)", Payment);
”I1)K€ приведен полный текст программы RegFay.CsZ
x
Проект 2-3
Про1рамма вычисляет размер ежемесячной выплаты по кредиту.
Назовите этот файл RegPay.Cs.
us;ng System;
92 Глава 2 Типы данных и операторы
__,__ ____,_..,_ %_*
с-аьь ЬьбРау 1
‚по - .>Cq ~/\/4. [а ГО
1e 1 £1‘ К: ;511dHL.Hl/L ь :Ao;O~s_J i‘-.1 11
<.L Пс. I па к. ‚и (‚к во ' Q «Lg я‘ ‘.
гшримьр J -.- 3
з та- в уди/км- К^ЛИнььдиО 1:1 ежы/ :2 д
gm a- N "Lax- Срьт " Lc3._\J€r‘/)1 деду с:
зссцч Edy ел‘ Pcljlvluf IJ1a1e>a<a
ас цтаь nu er (lr:."O Ьспомоха елыые первые: ьые
г щэ1е к, е, (штанам/е ь показащьль enemy
дЛА вызова Me Ода РЫЛ”
1 pa;
“Ra ь —— 7
9:1-gt <.r"<_¢1
гидрата
хм ь- la r a—[\/ м; я ядд[ь Ка-
C1 up;-a) Lc1)tLI, an ‘ \1 Хьа ь)
о ( un_e) 1xa*e L<.«)LCL" at 1
мы — _ L dL [пинг ы‚„м(‚_ к.)
La "кг п. „г " ielo
ьцпь-„ьс и; к, J.1\.( Рыдмьр ьжьмк ч ь к зьп щ ы С ‚ неуч. ),
В [IL Л П RIIL BHHO [H£HH5[ HD0111 1\1\H)I (H [kl HLlBL,_lLHz1L'ILl\K)Hl1$ILTDOKJ
is д -‘KL к. дн г‘ вь и ._ [ы
Прогьыпрппь э!) прохршщ прьжць Чип пьрыпн ь пьшыппо ьыыпьпшыо
хшьрнди (ввьппь раличпьль зндчьпия ихшы кредита, сроков погашения и
прошитом; но крщиц)
контрольные вопросы
10.
ll.
12.
_ддш.‚ ›.‘
Почсчу в C33 ьгрох о ьпецифипъидж хоты: диапазоны топхьгьппцх шачьнпи
и мтрамсръъьпътки его простых шпон’
Что прсдиашясг ьобои ammo lbHblH "Hill B Сд п чсч он 01 Iwmuux 01
ышвольного типа испотьзхыюхо в других языках прокрахшировжания’
Справедливо m \TBCp/lx.1CHI/IC -no llCpL\IL,HHd5I ПНИ k д \I()/KLT хранить
чюбос значении поскольку любое HCHV [СНОС значишь ms шыся пыин-
ньш’
Иьпопьзуя (цну upon КОДА и С\Ь‹1РС-ЪПОЬ1С‚1ОПЗАЪЬ1ЬПОЬГИ, напишите
опсрагор иске} по () ‚. п рез» плац выпотнспия которого букуг mm
во юны счсдыощис при ыроьи
Первая
В-орая
Тре-дя
Какая ошибка ьодержигся н цаннои фрашснш кода’
fo r(- — O, L < -0, Lfr)
1n: зап,
sum ~ suw * д,
Conso-e WKAILLlFc("LyMMa озона + bur),
O6b$ILHMTC рапичис между поыфикыгои (нь) и прсфпмнои (+Td)
фориачи оператора инкрсиьнга
Напъшкитс фрашснт кода, в коюроч пя предогврапшнпя ошибки
дечсния на ночь испочьзовавя быирыи опсрапор ~N
K KdKO\4) чипу преобразуются липы г ‚Гед д ъ т‘ в вырамьнип’
KdKOl/I ИЗ НПЖСПрИВСАСННЫЧ ТИПОВ ПС \40/KCT L()B\1CLU.dT bL}I Ь Bblpd/kLHl1Vl
LO 5НдЧСННС\[1ИПд Ёь„\П11
d) :1,2t
6) L1*
B) LI
F) "I 1‘ е
Когда необходичо прписняпь привьденис чипов’
Напишияс програччу, которая находит чье ПрОьТЫС um и в Шаплзопк
от l по 100
Сачосгояге хьно псрспишихс програхпп ппрсдъмвъпачьннуъо 1 m вывода
Таблицы HCTHHHOLHA (UDOCKF 2—2), гамш образом чхобы в Hm l£\lC(.TO
CSLd|)C—flO(. |С.’10В<1ТС1ЬНО(.|СП ишотыоцза mu) mnupuommux LIp()I\OBI>IC
lHILpE1'H>lL BIO/KCHHH\|H iHd}\d\Hl ld6\ UHUIH H kH\ll’.() [АЦП HOBOH L lD0l\I1
93
Управляющие
операторы
С!
ВВОД СИМВОЛОВ С КЛЗВИЗТУРЫ
Cl
Детальное рассмотрение операторов if
И for
C‘! Оператор switch
C‘! ЦИКЛ while
C] Использование цикла do—while
C] Оператор break
П Использование оператора continue
С!
Оператор goto
Ввод символов с клавиатуры 95
В этой главе будут рассмотрены операторы, которые управляют процессом выполне-
ппя программы. Существуют три категории управляющих операторов V опериторы
„ыбора (if И Switch), иптериционные операторы (Ее-г, while, do—while И foreach)
и операторы переходи (break, continue, goo: И return). Здесь мы подробно
расскажем обо всех перечисленных управляющих операторах, за исключением опе-
раторов return И ft:-reacn, которые будут описаны далее в этой книге.
' ВВОД СИМВОЛОВ С клавиатуры
Прежде чем перейти к изучению управляющих операторов, сделаем небольшое
отступление и расскажем о том, каким образом в Ci? можно работать в интерактивном
режиме. В предыдущих главах нашей книги рассматривались программы, только
выводящие информацию на экран, теперь же в них будет предусмотрено и получение
данных от пользователя.
Для чтения символов с клавиатуры необходимо вызвать метод Console.Read() ,
который ожидает ввода символа с клавиатуры, а затем возвращает результат. Результат
возвращается как целочисленное значение, поэтому он должен быть преобразован в
тип char, чтобы переменной был присвоен этот же тип, По умолчанию строка,
вводимая с Клавиатуры, помещается в буфер, и для того чтобы введенные символы
были переданы программе, нужно нажать клавишу [Enter]. Представленная ниже
программа демонстрирует, как считываются символы, введенные с клавиатуры.
.' "5 программе демонстрируется считывание СИМВОЛОВ, ВЕОДИМЫХ С
клавиатуры .
Js;ng System;
:Lass Kbln (
public static void Main() {
Char ch;
COns0le.Write("HaxMMTe символьную клавишу, после чего нажмите"+
" клавишу ENTER: ");
Чтение символа, вводимого с клавиатурьь}
ch 4 (char) Conso1e.Read();// Считывание символа.
Console.WriteLine("Bm ВВЕЛИ символ: " + ch);
«мы - м-тгчичмщчсиыиьщиымрмдднтт
Ниже приведен пример работы программы.
_ Нажмите символьную клавишу, затем нажмите клавишу ENTER:
Ё am ввели символ р
Ё
Иногда у программистов вызывает недовольство то, что строка ввода метода Read ()
, Помещается в буфер, поскольку это немного усложняет работу. Ведь при нажатии
4 шавиши [Enter] во входной поток вводится последовательность символов перехода
па новую строку, и эти символы остаются в буфере ввода до тех пор, пока не будуТ
Ё считаны. Поэтому при работе с некоторыми приложениями (в этой главе вам
Ё встретится такой пример) действительно возникает неудобство — перед новым вво-
‘ дом приходится сначала удалять эти сизивольт из буфера посредством их считывания.
:10 Глава з. Управляющив операторы
_.„__.„_ . ._.___..—_____._________._._.. .
Д Минутньл? гдэжггяшгяздгм
ч
г" I. l<Zll\ C'I¥l’1‘blH;iC|\‘}i тихими. н\э<,1(|(((\‚1|1 е папавнагургц‘?
‘=
2 Чш одннчаш НОЦЫНСНН.) шроки н 63(])cp‘.’
<’>ma-pamp if
(‘s-':Lpzuop : г _\/KC pu¢c.\1;1'1pm;_:_1c:1 в ыаве I. кенерь чы расекажеч о неч пкодробнсед
{анпнксис опсранора 15 е ело ншрой чаегыо . „в выШЯЕшт сгяедпукнннм образом:
д 1’ \Lx:.'.'CI'.-"E 1 U,-1) :-‘L'cxEc!nun.C-' .
е - ‚к- ‚сшила;
‚Здесь при минодъясъънн }c_mrsm: nncpzuupa : .‚ инициируется только один oncpamp,
L‘C,lH же 3сцовие не вьннывпзпечеэц, ю какжс ининивлизируетея только один оператор,
ппшьиптпхси к к ноченоиу шов; г . ‚1 ш онергнора ; : не обялпельп-ло указывать.
cm внъруш чнеть ._ K Оператор) i ‚ ~ \101_\'1 огноеииьеэп также блоки онера- '
mpuu_()6umi1cnmaxcuc онсргнора д : е ненольховаънчси бдокоъъ огжергхгоров выглядит ‚У
едсцучошъаха образом:
(';undLL1Q;‘.)
A 1-3 CA; J.l',‘,‘-d‘L'I_,JlL>l1\:LJ'i';: -(.1 [ератороп
ЬОЁЛСДЫ. 5:‘; ‘ЭЛЬ: гЁС
l!(}\.‘.'IC,1()l5ElICJLIIOCTL 0flL‘[)2iI(7pi)H, 'L‘()()l'RCTL‘I‘B3’lOIJUIX ()lICp£i'IT)p)' L] . CCJH ЖС )'С.`КОВН()С ‘-
ВЫрд/КСНИС ПРИНИМНСТ ЗНАЧЕНИЕ Ё ч, ц-ё, Н) при НЗЦИЧИН опердпори I: е ПЬПТОЛПЯСТСЯ
ПОСЁКСЛОВН’!СЛЬНС)С'Г1› (IIlL.‘pEiT()p0B. COO’! |5CI'C'[ |i)‘I0lL1iUI ‘IHCTH е ‚З-З. ОбС ЧИСКП ОПСРНТОрЗ, `
L‘: H (Ч 3. ПС МОГУТ ПЫПОЛНЯТЬСЯ ОЦНОВРСЧСППО. УСДОВНОС ПЫрдЖСППС. _\"lIp'(ilL'l}HOULCC 1
k'zHCpdl'0p()\1 1: ДОЛЖНО B05BDl1l1l'(X ГЬ pC3)'.|L;l‘dT ТИПЫ 3332 1.
Ними условное ньпргцкснис (г ё‘ J. .-) нриничаеп мха-шине то ныпогшнястсяд
.Lm ,-'1c\umcIpaLnm рыбогы онсрьпора 1: P2‘.Jp.i6()TiiC\1 Программку нроеяон икры на‘
униньнзпние. которнэн нрсиъцаънцчена ELLA маленьких ‚петеи. В нервои версии игры
npolpzmua иросиг шрокн _\'l'&L'1El'l Ь букв; в диапазоне оч А :10 7.. Если игрок верно’
ньябирасп букву на шшънагурс, нропргшча выводил на экран сообщение '
М. Ниже приведен чеке: ‘мои нрогрнихнл.
дрых там игра ".~'J'a,u..~.w1 Q; ma} ",
. u-\“t4_>/Ll1—l-
.1::;‘.—:x вата , \ " :‚._р ‚ш.
I. iLl}{ ‘ГГСПИЯ СИМВОЛ?! НСОбХОДИМО ВЫЗВИТЬ МСТОЦ -',‘.0nsoLe . хеао .‘ .
2. При вводе ст рока номсишасгся в буфер, поэтому необходимо нажать шавншу [Enter], IIpc)K11€
чем введенные символы будут переданы программе.
х оператор if
console .WriteLine("BBe,uw1-e символ. ") ;
Ch = (char) Console.Read()I//Cuwrbxsae-r символ, введенный с клавиатуры.
,_f(Ch :—_ answer) Console.WriceLine("** Правильно **").'
Программа ПрОСИТ ВВССТИ СИМВОЛ С КЛЕПЗИНТУрЫ И СЧИТЫВНСТ СГО. ДЭЛСС НСПОЛЬЗУСТСЯ
Оператор if ДЛЯ проверки СООТПСТСТВИЯ ВВСДСННОГО СИМВОЛН ‘ЗНЗЧСНИКО I1CpCMCH}-(OH
рулей. в данном случае букве К. Если игрок вводит букву K, на экран вьивгздится
сообщение ** Правильно **. При вводе должны использоваться символы верхнего
регистра.
Игровую ктрогргкмгиу МОЖНО yCOBCpLLlCHCTBOBaTb. B CIIC.-'1)"K)lllCi'l ПСрСИИ ИСПОЛЬЗУСТСЯ
вторая часть оператора if—else Для вывода сообщения о неверной попытке.
/,' программа "Угадай букву" версия УЗ.
using System;
class Guess2 (
puolic static void Main() (
char ch, answer = ‘K’;
Console.WriteLine("yPa,uaI71Te букву алфавита, \"спрятднную\" в программе. ") ;
Console .WriteLine ("Введите символ . ");
ch = (char) Console.Read();// Считывает символ, введенный с клавиатуры.
;f(ch =; answer) Console.WriteLine("** Правильно **");
else Console.WriteLine (" . . .He угадали. .") ;
Вложенные операторы if
Вложенньлй оператор if используется для детльнейвтейё птровсркъа ‚ванных после того,
как условие предыдущего оператора 1f принимает '3Ha‘(CHnC ‘сто (То есть вложен-
нын огператор гкрименястся в тех случаях. когда ДЛЯ вьтполтпеттьтя Действия требуется
соблюдение сразу нескольких условий„ которые не могут бьП ь указаны в одном
УС-Юпноли выражении.) В программировании вложенные оператор ы if используктгся
достаточно часто. Необходимо помнить, что во вложенных операторах if—else
Вторая часть оператора (else) всегда относится K ближайшему оператору if, за
УСдовньцт выражением которого следует оператор ; или блок операторов. В приве-
денном ниже примере указаны два Ключевых слова else. относящихся к разным
O“9P'd'I‘0paM 1f.
A эта вторая ‘шасть
lf (; ,_ 10) ( <_ оператора :f—e1se
‚ 0lHOCM'IC}| K ЭТОМУ
4 U < 20) a : b;
Эта вто ая часть опс ато ‚а 2 («еще Оператор) 1 Е.
Р Р P
дЩк > 100) с — Q; (_.__o'ru()cnrcyz |\;«)1oMyoucp;n()py1f_ j——j—-——:)
else a 2 с; // Это ключевое слово else относится к if(k > 100).
e1~ .
‘>9 а = d; // Это ключевое слово else относится к 1£(1 ~~-— 10) -““’"‘
Глава 3. Управляющие 0Перат°ры
В комментарии указано.
„то „еедеднее Кто-швов слово else He ассоциировано с '
оператором if (j < 20) . поскольку оно находится за блоком кода, относящимися к -
0”Cp.n.0pV ifu __ 10) ТО есть Посчсднее Клюцепос слово else относится к
С е ‘т’ А . . . ’
оператору if (1 г: 10) . а пс к оператору г. г (3 < 20) . хотя этот оператор расположен ;
ближе к ключевому слову.
-- - .. ‹ * тыенствотзаътия Ё
Вложснныи оператор Lf ‘МОЖНО использовать, для дачьнсипъсго усовсР
програмгиы «Угадай букву» В новой версии обеспечивается 05D21TH?1’| СВЯЗЬ С ИГРО‘ ‘
ком — программа подсказывает‘, в каком направлении слсдуСТ ИСКНТЬ бУКВУ-
// Программа "Угадай букву" версия N3
using System;
class Guess} (
public static void Main() {
char ch, answer ~ 'K';
Console.WriteLine("
Console . WriteLine ("Введите символ ‚ ");
ch = (char) COnsole.Read();// Считывание символа.
if(ch =~
else {
Console.Write("
answer) Console.WriteL1ne("**‘ Правильно “*");
Попытайтесь поискать ");
Вложенный оператор if.
// Вложенный оператор if.
if(ch < answer) Console.WriteLine("Bame no алфавиту."):
else Console.WriteLine("HMxe по алфавиту."):
, . е." ' 7
угадайте букву алфавита, \"спря1анпую\" 5 программ )‚ д
т ‘A
1
Пример выполнения программы показан ниже.
Угадайте букву алфавита,
Введите символ. В
Попытайтесь пои
"спрятанную" в программе.
скать выше э алфавите.
Цепочка операторов if-else-if
B программировании часто используется цепочка операторов if-else-if — KOHCT‘ .
рукпия, еостояитая из вложенных операторов if. Выглядит она следующим 05P«'130M- ’
1f(condition)
statement;
else if(Condition)
statement;
else if(condition)
statement;
else
statement;
д операм’ "
Усдонньпе выражения оцениваются сверху m-ms. Как готвко найдено условие, при-
нимающее значение Егпе, вьпполттяетея ассопи и рованны и с этим условием оператор
(5v—atement.), а остальная часть пепочки пропускается. Если пи одно из условий не
„ршшмает значение true. ТО выполт-пяется послеппии оператор е Ъзе, который можно
расечатритзать как оператор по умолчанию. Если же последним оператор else
OTC)-Tc'I‘ByCT, а все условные выражения принимают значение false, то программа не
выполнэтет никаких действии.
в Сдсдупошедт программе [показано использование Цепочки операторов ii~F:1,se—i f.
// B „рограмме демонстрируется использование цепочки операторов 1t~else—if.
u5;nq System;
class Ladder (
pu‘-1c statlc VOLG Ma1n() {
11".’; X}
for(x»0;x<6;x+*)
1f(x—:1)
Соп$о1е.Иг1:еЬ1пе("Значение переменной х равно 1.");
else ii(x——2)
Console.WriteLine("3HaueHMe переменной x равно 2.");
else if(x:=3)
Console.WriteLine("3HaueHme переменной х равно 3.");
Этот оператор
вьп|о1п1яется
else if(X::4) поумошшнию.
Соп5о1е.И:1ЁеЬ1пе("Значение переменной x равно 4."):
else
Hr
ConsoLe.Wr1LoLine( Значение переменной х не входит 5 "w <-————
1
"диапазон значений от L до 4.");
}
Программа выводит следующие строки:
Значение переменной х не входит в диапазон от 1 до 4.
Значение переменной х равно 1
Значение переменной х равно 2
Значение переменной х равно 3
Значение переменной х равно 4
Значение переменной х не входит Е диапазон от 1 до 4.
Как ПИДИТС, ОПСрЦТОр €156) ВЫПОЛНЯСТСЯ 1`0ЛЬК0 ТОГЦН, КОГДП НП ОДНО ИЗ предшест-
ВИСЛЦИХ УСЛОВНЫХ выражении оператора if ПС ПрИНР1М21СТ` 3H2l‘IClll«1€ true.
минутными драк/лукум
|. Какого типа должен быть результат условного выражения оператора if?
2. С каким из операторов if всегда ассоциируется оператор е] ее?
3. Что такое попочка опергггоротъ 1f—eise~if_'.’
L Результат условного выражения оператора if должен иметь тип ‘cool.
Оператор else всегда ассоциируется е ближайшим оператором if. после блока операторов
Которого (или единственного оператора) он находится.
3- Цепочка операторов if—else~1f является последовательносгьто вложенных операторов
else—i£
100 Глава 3. Управляющие операторы
Оператор switch
Оператор switch ЯВЛЯЕТСЯ вторым оператором Выбора в Cit, Этот оператор позволяет
программе выбрать нужное действие из перечня вариантов. Принципы работы
вложенных операторов Ьг-едзе И 0fl€P‘<\T0P0B switch pa3.m\ra10Tcs1_ причем во
МНОГИХ СИТуаЦИЯХ HCHO.'Ib3()B'dHl«1C ОПСрЗТОрН Switch является более 3d)(1)cKT”nHbIM_
Оператор switch работает следующим образом: значение вьтрджоиття последователь-
но сравнивается C каждой константой из списка. При совпадении диштоттшт о одттотт
из Констант выполняется ассоциированная с ней поододова-гсльътость операторов.
Общая (Ьорма синтаксиса оператора switch сгяедуютиди;
sw;tch(expression) ъ
case constant l:
statement sequence
break;
case constant 2:
statement sequence
break;
case constant 3:
statement sequence
break;
default:
statement sequence
break;
Э
Выражение (expression) B операторе switch должно быть Цодочиолсттнттттт типа
(char, byte, short или int) либо типа string (который описан далее в этой книге).
Выражения с плавающей точкой запрещены для Иеподтьзованття в опораторо Switch
Очень часто в качестве выражения, управляющего опордтооотт относи‘ „оттодттзуототт
просто переменная. Константы (сспзташ) ветвей газе оператора swi ‚чолжпьп
бытьлитсралами и иметь тип, совмссгихилй с выражситтодд Кроис того, ‚т одттом одоко
оператора switch они нс могут имсть одипаковьпс 3H;/1I1C1{“;1_
n()CJ1C£l()BaT€JlbH()CTb операторов (statement sequc;-nr:e)_ ОТНОСЯЩИХСЯ к „отит, де_
fault, выполняется тогда. когда ни одна из констант Вотоотт Case но Соответствует
выражению. Ветвь default HC обязательна. Если при до отсутствии в Оттсргтторс
switch ни одна из Констант нс соответствует выражению, то никакие Действия нс
будут выполнены. Если соответствие обнаружено, то Выполняется ттослсдоттатслтг
ность операторов, ассоциированная с этои ветвью case, Затем о Помощью Оператора
break контроль над вьшолнснием программы передастся конструкциьд сдсдутотдеда
за оператором switch.
ГТривеЦсм пример программы, лсмонстртпрупощси применение оператора Swltch
// B программе демонстрируется использование оператора swiLCh_
using System;
class Swi:chDemo (
public static void Main() {
int i;
Оператор switch 101
Еог(1—О;1<10;1++)
switch(i) (
case O:
Соп5о1е.Иг1сеЬ1пе("Значение переменной i равно 0.");
break;
case 1:
Соп$о1е.Иг1сеЬ1пе("Значение переменной i равно 1.");
break;
case 2:
Соп$о1е.Иг1ЕеЬ1пе("Значение переменной i равно 2.");
break;
case 3:
Соп$о1е.Иг1сеЬ1пе("Значение ьерсменной i равно :.");
break;
case 4:
Console.Wr1teLine("3HaqeuMe переменной i равно 4.");
break;
default:
Console.Wr1teLine("3HaqeHMe переменной i больше или равно 5.");
break;
Б
Ниже показан результат Выполнения этой программы.
Значение переменной 1 равно O.
Значение переменной i равно
Значение переменной 1 равно
Значение переменной
Значение переменной равно
Значение переменной больше или равно 5.
i равно
i
1
Значение переменной i больше или равно 5.
i
i
i
JD-LAJf\)|-'
Значение переменной больше или равно 5.
Значение переменной больше или равно 5.
Значение переменной больше или равно 5.
Как видите, каждый раз при прохождении Цикла выполняется оператор, ассоцииро-
ванный C ветвью case, константа которой совпала со значением переменной д. Всс
дРУГие‹эператорьипропускакпся.1(оцца3наченис переменной:1больЦк:илЬ1раВно 5,
ЪН4 одна ИЗ КОНСТаНТ НС СООТВСТСТВУСТ ЭТОМУ ЗНВЧСНИКД ТО ССТЬ В ЭТОМ случае
выполняется Последовательность операторов. определенных ДЛЯ ветви default.
B Upenbunluem приькцю оператор SwiLCh упраплялся псрсчсппои типа lnt. Yxm
говорилось, что управлять оператором swltch МОЖНО с помощью пьпражснътут любого
Цслочисленноготипа.Стсдуюиипппрограммадюмкпкприруетиспользование выраже-
НИЯ типа char И Константьквствей са$е‚такжС имсклцих тип char:
// B Программе для управлением оператором switch используется выражение,
// имеющее тип char.
using System;
Class SwitchDemo2 (
public static void Main() {
Char ch;
fOr(ch:'A';ch <2 'E';ch++)
нм Глава з. УПраВЛяющив операторы
switCh(ch) (
case ‘A’:
Соп5о1е.Иг1сеЬ1пе("значением переменной ch является символ A.");
break;
case ‘B’:
Сопзо1е.Иг1пеЬ1пе("значением переменной ch является символ В.");
break;
case 'C':
Conso1c.Wr;teL1ne("3uaqeHueM переменной ch является символ C.");
break;
case ‘D’:
COnSOle.WriLOLiU€("3HaH€HM€M переменной ch является символ D.");
break;
case 'Е':
Соп$о1е.Иг1ЕеЬ1пе("значением переменной ch является символ Е.");
break;
}
Результат выполнения этой программы показан ниже:
значением переменной ch HBHHGTCH СИМВОЛ А.
значением переменной ch ЯВЛЯЕТСЯ символ B.
значением переменной ch является символ С.
значением переменной ch является символ D.
значением переменной ch является символ Е.
Обратите внимание, что в данной программе отсутствует ветвь default, которая не
является обязательной и может быть пропущена, если в ней нет необходимости.
Последовательность операторов, ассоциированная с одной ветвью case, не должна
передавать управление программой следующей ветви case (ЭТО называется правилом
запрещения передачи управления программой между вептвями case), такое действие в
Cii рассматривается как ошибка. Во избежание этого последовательности операторов
ветвей case заканчиваются оператором break. (Предотвратить передачу управления
можно и другими способами, но чаще всего для этого Используется именно оператор
break.) Если оператор break расположен после последовательности операторов
ветви case, это приводит к выходу из всего оператора SW1 tch и передаче управления
программой следующей конструкции за пределами Switch. Оператор default также
Должен следовать Этому правилу, И Поэтому обычно Заканчивается оператором break.
Недопустимо, чтобы одна последовательность операторов ветви case передавала управ-
лен ие программой операторам другой ветви, но две и более ветви case могут быть
связаны с одной и той же последоватсльноетыо кода, как показано в следующем примере:
switch(i) (
Case 1 : I ЭТИ ВСТВИ case BhI3I>IB3K1T
ОДИН И ТОТ ЖС оператор.
case 2:
case 3: Соп$о1е.Иг1пеЬ1пе("Переменная i имеет значение 1, 2 или 3");
break;
case 4: Соп$о1е.Иг1сеЬ1пе("значение переменной i равно 4");
break;
}
Если переменная 1 имеет значение 1, 2 или 3, то выполняется первый оператор
WriteL1ne () ;. Если значение переменной равно 4, то выполняется второй оператор
WriLeL1ne () ;. Возможность использования одной последовательности операторов
несколькими ветвями case не нарушает правило, запрещающее передачу управления
Ё
’.‘
V.
E
а
I
опвратор switch 103
НрОГПИММОЙ \1С)!(Ду ВСТВЯМИ case, И ПССЬМИ ЧИСТО РКЁПОЛЬЗУСТСЯ Н програмтитровании.
']`;|к_ н C,’IC[[ylO[L1Cl1 ПрОГрНММС ОН?! L1CFl0.'Ibj)’C'I'C}I ДЛЯ D2l3;"1C.'lCIII1}l букв {ЬПЬННИТЦ на
LIHCIILIC И СОГЛНСНЫС. (JLWSI корректной рЦбОТЫ программы СЛСЦУСТ ВНОПЪГГЬ СИМВОЛЫ
нижнего регистра.)
'/ B программе демонстрируется использование НЕСКОЛЬКИМИ ВЭТЕЯМИ case ОДНОЙ
I /
/x последовательности операторов для разделения букв на гласные и согласные.
н51п9 System;
class VowelsAndConsonants (
public statlc Vold Main() E
char ch;
Console.Wr;tC("BaeuMTe букву: "):
ch — (char) Console.Read();
sw1tch(ch) {
case ' '
case
case
case
case
case 'y
Console.WrlLeLine("3To гласная буква.");
break;
default:
Console.Wr;teLine("3To согласная буква."):
break;
}
Если бы этот пример был написан без использования вышеназванной технологии,
то запись одного и того же оператора W1:1teLir1e () ; необходимо было бы повторить
шесть раз.
ножевая: вж$еетеввлее‹ --
Вопрос. В каких случаях вместо оператора switch нужно использовать
цепочку операторов 1Е—е15е—1Е?
Ответ. Общее Правило Таково: Цепочка операторов 1f—else—if используется
в тех случаях, когда выражение условия может иметь много значений (то есть когда
в выражении условия удобнсс применить оператор сравнения 1f (3 «I 0)) или
требуется сравнение нескольких переменных. В качестве примера рассмотрим сле-
дующую цепочку операторов:
if(x < 10) // ...
else if(y E; 0) // ...
else if(ldone) //
Такую последовательность нельзя записать с помощью оператора sw~.tch, поскольку
все три условия основаны на различных переменных и различных типах. Также
Цепочка операторов if—else—if используется для проверки значений C плавающей
точкой или для проверки объектов других типов, которые нельзя применять В
выражении оператора switch.
...». .,¢._..-,,..e«-_-.-w.»»»..
1 04 Глава 3. Управляющие операторы
Вложенный оператор switch
Оператор switch MO)KCT быть частыо внешнего оператора sw1'.:Ch. Внутренний
оператор swltch называется вложенным. Коттстатггьт ветвей case внутреннего и
внешнего операторов зидьсп могут иметь одинаковые значения, и это не вызовет
конфликта при выполнении программы. Например, следутоттлтй фрагмент кода
вполне допустим:
sw1tch(chl) {
Case ‘A’: Console.WriLeLine("3Ta константа ‘A’ ЯВЛЯЕТСЯ частью "т
" внешнего оператора swi:Ch.");
switch(ch2) {
case ‘A’:
Console.Wr1teLine("3Ta константа ‘А’ является частью "+
"внутреннего оператора sw;:ch.");
break;
case ‘B’: // ...
} // Закрывающая скобка внутреннего оператора sw;tch.
break;
case ‘B’: // ...
„ответе: ввафеееаевваа
‚
{:3 Bonpoc. B языках С, C++ И Java последовательность операторов одной ветви
Case МОЖЕТ передавать управление программой Последовательноетът операторов
следующей ветви case. Почему это нс разрешено с Cit?
OTBeT. B C11 правило запрещения передачи управления программой между
ветвями case принято по двум причинам. Во-первых. это правило позволяет компи-
лятору организовать порядок перечисления операторов ветвей case наиболее onw-
мальным образом. Это было бы невозможно, если бы flOCJIC,[1OBa'I'C_‘IbHOCTb операторов
одной ветви case могла передавать управление следующей последовательноети
операторов. Во-вторых, в Cir каждая последовательность операторов должна закан-
чиваться оператором break, что также не позволяет передавать управление програм-
мой между ветвями case.
Минутная? драктакум
1. Какого типа должно быть выражение, управляющее оператором switch?
2. Что происходит, когда выражение оператора вышел совпадает‘ со значением
одной из констант ветви case’?
3. Может ли последовательность операторов, ассоциированная с одной ветвью
case, передаватъ управление программой сдтедтутотътетт ветви вазе?
l. Выражение оператора switch должно быть целочисленного типа, либо типа string.
2. При совпадении проверяемого значения со значением константы выполняется последова-
тельность операторов, ассоциированная с этой ветвью case.
3, Нет, поскольку с Сд для оператора switch существует правило, запрещающее передачу
управления между ветвями case.
Оператор switch 1 05
проект 3-1. Построение простой справочной
системы С#
5,7} HeIp.cs
B этом проекте мы построим простую справочную систему, которая вьтводргг общую
форму синтаксиса для управляющих операторов Си.
Программа выводит‘ меню, содержащее список управляющих операторов, и ожидает‘,
› пока вы выберете один из них. После того. как выбор сделан. на экран выводится
общая форма синтаксисаэтого оператора. В первой версии программы будет‘ Доступна
только информация о синтаксисе оттераторотз 1f и зытссй. Общие формы синтаксиса
‚тругих управляюьцьтх оттсраторов будут добавлены в елелутоптьтх проектах.
Пошаговая инструкция
|. Создайте файл и назовите его Help . -:3.
2. Программа начинает‘ работу с вывода на экран отелутотцсго меню:
; Справка по синтаксису:
1. Оператор ;f.
2. Оператор sw;tch.
Введите порядковый номер оператора:
ПОЭТОМУ вам НСОбХ0ЦИМО ИСПОЛЬЗОВЭТЬ Т3КуК)ПОСЛСДОВ8ТСЛЬНОСТЬ oncpaTopoB:
Console ‚видевшие ("Справка по синтаксису: ") ;
Console.Writ:eL;ne(" 1. Оператор if.");
Console.Wr'1teLine(" 2. Опера-гор switch. ") ;
Соттзо1е.1^1г1'се("Введите порядковый номер оператора: ");
Lu
Программа получает ннфориаттттто о выборе пользователя. вызывая метод Соп-
cc-ie .Read(). Как показано ниже:
choice — (char) Conso1e.Read();
ё
т
4. После получения иттформацьтрт о выборе пользователя программа использует
оператор switch, представленный ниже, чтобы вывести на экран синтаксис
выбранного оператора.
switch(choic е) (
case ' 1' :
Console.WriteL;ne("C1/11111-a1<c1/1c оператора if:\n");
Console.WriteL1r1e("if (condition) statement; ") ;
Console .Wri:eLir.e ("e;se statement; ");
break;
Case '2':
Сопзо1е.Иг1сеЬ1пе("Синтаксис оператора switch:\n");
Console.WriteLir1e("switch(express1on) 1'") ;
Console.WriteLine(" case constant");
Console.WrJ_teL1r1e (" statement sequence ");
Console.WriteL1ne(" break; ") ;
Console.WriteL:ne(" // . . . ") ;
Console.WrJ.teL;ne (" } ");
break;
default:
Cor1sole.Wr1te("f}op;1m<osb117: номер указан невернод‘);
break;
1 06 Глава 3. Управляющие операторы
Отметим, что ветвь default flCpCXBL1TblBLlCT uHq)opMaum0 о неправильно указан-
ных порялковьпх номерах. Напригиер, если пользователи, введет значение 3, не ’
еогъпндакъкнес ни е одной п; копстшп оператора с. ~ , ТО эго приведет к вьппол-
НСНЪПО ОПСр8ТОрЫ„ОПРСДСЛСПНОГОЕЦП!ВСТННьЪЗЁаЧЁЁ.
э. Н иже предетавгкен полный листинг программы F.e1p..-:3:
/* Проект 3-1
Простая справочная система.
*/
using System;
class Help {
public static VOld Main() (
char choice;
Consoie.WriteLine("cnpauxa но синтаксису:");
Console.WriteLine(" 1. Оператор if.");
Console.WriteLine(" 2. Оператор switch.");
Console.Write("BBennTe порядковый номер оператора: ");
choice = (char) Console.Read();
Console.Wr1teL1ne("\n");
switch(choice) {
case '1':
Соп$о1е.Иг1пеЬ1пе("Синтаксис оператора it:\n"):
Console.WriteLine(";f(condition) statement;");
Console.WriteL;ne("else statement;"):
break;
case '2':
Сопзо1е.Иг1пеЬ1пе("Синтаксис оператора switch:\n");
Consoie.WriteLine("switch(expression) {");
Console.Wri:eLine(" case Constantz");
Console.WriteLine(" statement sequence ");
Console.Wr;teLine(" breaki");
Console.Wr1tcLine(" // ...");
Console.WriteLine("}");
break;
default:
Соп$о1е.Ип1Ее("Порядковый номер указан неверно.");
break;
3
1
д
}
H H)KC приведен Пример выполнения лои програмхяьп.
Справка НО СИНТЗКСИСУ:
1. Оператор if.
2. Оператор swltch.
Введите порядковый номер оператора: 1
Синтаксис оператора if:
if(condition) statement;
else statement;
_ ‚гшещиеРннА-‘ёйтд- г -— и и ч: we-v=v-:'-«.-—»9e4-ivt~v«A'.4*-v14-4?
Цикл for 1 07
цикл for
C первой главы этой книги в Программах используется простейшими форма цикла ; и r_
Теперь мы расскажем обо всех возможностях этого оператора, и вы увидите его мощь
ll гибкость. Начнем С рассмотрения ОСНОВ ЦИКЛЫ for И его наиболее традиционных
форм.
ООЬЦИЙ СИНТЭКСИС ЦИКЛ?! for ДЛЯ повторения ОДНОГО ОПСРНТОрЗ [ЗЫПЧЯДИТ ТНКЁ
;„г(1п1с1а112ас1оп; condition; iteration) statement;
,"L’I${ ПОВТОрСНИЯ бЛОКЭ ОПСрЗТОрОВ ИСПОЛЬЗУСТСЯ ЦИКЛ, ЗНПИСЬ КОТОРОГО ОТЛИЧЭСТСЯ
(Л предыдущей ТОЛЬКО НЗЛИЧИСМ ОТКрЫВЭКЛЦСЙ И ЗЭКрЫНЭЮШСЙ СКОбОК, МС)КДу
КОТОрЫМИ И НЗХОДИТСЯ бЛОК ВЫПОЛНЯСМЫХ ОПСрЭТОРОВ.
;ог(1п1с1а112ас1оп; condltion; iteration)
statement sequence
При инициализации управляющей переменной цикла ( 1п1пща112аг1оп — первая со-
ставляющая Цикла), которая действует как счетчик ‚для управления притом, ей
присваивается начгшьное значение Для этого обычно используется оператор при-
сваивания. Составляющая Цикла condition — это выражение условия, имеющее тип
13001. от гдстинттострт которого (при проверке измененного значения управляющей
переменной) зависит, будет ли выполнен еще один проход Цикла. Составляющая
цикла lteratlon щ это выражение, определяющее величину (щаг), на которую будет
изменяться значение контрольной переменной при каждом повторении Цикла.
Отметим, что эти три основных Элемента Цикла должны быть разделены точкой е
запятой. Цикл for повторяется до тех пор, пока в результате проверки выражения
условия будет возвращаться значение true. Если будет возвращено значение false.
ниш завершится и управление программой будет передано оператору, следующему
за циклом for.
B ПРИВСДСННОЙ НИЖС np0rpaMMC ЦИКЛ ft}! ИСПОЛЬЗУСТСЯ ДЛЯ ПЬПЁОЦН DC3yfH)T'dTOB
вычисления квадратных корнеи чисел от I до 99. Программа также выводит ошибки
ОКРУГЛСНГДЯ ДЛЯ КЙЖДОГО рСЗУЛЬТЗТЭ.
Программа Выводит результаты вычисления квадратных корней чисел
/‚' от 1 ДО 99.
using System;
:;ass SqrRoot {
publlc static vold Ma;n() {
double num, sroot, rerr;
for(num 2 l.O;num < lO0.0;nums+)
sroot = Math.Sqrt(num);
Сопзо1е.Иг1пеЬ1пе("Квадратный корень числа " т num + " равен " ~ srOOt)i
// Вычисление ошибки округления.
rerr — num — (sroot * sroot);
Console.WriteLine("owmfixa округления равна " + rerr);
Console.WriteLine();
1 08 Глава 3. Управляющие операторы
Обратите внимание, что ошибка округления вычисляется следующим образом: воз-
водится в квадрат значение квадратного корня числа, затем этот результат отнимается
от первоначального значения. Следоватсльъто. значение разности и будет составлять
ошибку округления. Иногда ошибка округления будет происходить еще и при
возведении в квадрат значения квадратного корня, го есть будет округляться сама
ошибка округления. Этот пример показывает. что если в выражениях гдспользукътся
числа с плавающей точкой, то эти вьпчислсния могут быть недостаточно точными.
Переменная цикла может не только увеличивать, но и уменьшать свое Значение,
кроме того, контрольная переменная может изменяться на любую величину при
каждом проходе цикла. Например, следующая программа выводит числа в диапазоне
от 100 до —100 с шагом уменьшения переменной ниша, равным 5:
// Цикл с отрицательным шагом изменения переменной Цикла.
using System;
class DecrFor (
public Static void Main() {
int x;
Переменная цикла уменьшается на 5
fOr(x ‘-’ l00;X > “l00;x '— 5) при каждом прохождении Цикла.
Console.WriteLine(x);
1
J
Следует отметить, что в никле fior выражение условия всегда проверяется в начале
ЦИКЛЭ. Это означает, что если изначально выражение условия принимает значение
false, код внутри Цикла не будет вьнтолнен ни разу. Например,
for(count : lO);COunt < 5;count++)
х += count;// Этот оператор выполняться не будет.
Оператор ЭТОГО ЦИКЛЭ НС бУдЁТ НЫПОЛНЯТЬСЯ НИКОГДЭ, ПОСКОЛЬКУ СГО УПрЭВЛЗПОЩЭЯ
ПСрСМСННЭЯ count HMCCT ЗНЭЧСНИС бОЛЬШС 5 B Ha‘l2UlC ЦИКЛН, ЧТО ИЗНЭЧНЛЬНО ЛСЛНСТ
УСЛОВНОС ВЫРЭЖСНИС ССНПЁ < 5 ЛОЖНЫМ.
Некоторые варианты цикла for
Цитат for является одним из самых разносторонних операторов. Например, в нем
могут использоваться несколько управцтяюпптх переменных Цикла. Рассмотрим сле-
дующую программу:
// Использование запятых в управляющих частях цикла for.
using System;
class Comma i
public static void Md:n() {
int i, j:
r—~4v u~—«- \__——м——
ёОбратите внимание, что ниш имссП
fOr(i:O, ]:lO;i < j;i+‘, j——) <——————————ЁЁсупЕЁЁЁошиЁпщюьшнные I
Соп$о1е.Иг1ЕеЬ1пе("Значения переменных i И j: " + i + " " + 3);
I
Ниже показан результат выполнения этой программы.
Циюпог 109
Значения переменных 1 И 3 О 10
Значения переменных i И j 1 9
значения переменных i И j 2 8
значения переменных i И j 3 7
значения переменных i И j 4 6
Здесь два оператора инициализации и два итерационных выражения разделены
запятыми. Когда цикл начинает свою работу, обеим переменным присваитъьнотся
начальные значения. Каждый! раз при гзрохожденнгт цитата значение переменной j
_vuc_'nmmsz1eTcs| Ha свинину. а значение переменном 3 у\1снь11‚|ае’гся на единицу,
Управление циклом for C F)O.\lOlLlh}0 нескольких гхеременньхх часто бывает- удобным
м упрощает алгоритмы, реализуемые с помощью этого цикла. В цише вы можете
использовать любое число операторов инициализации и итерационных операторов,
но на практике применение более двух переменных для контроля цикла for делает
еГ0 слишком громоздким.
Условие, управляющее цителом„ может быть любым действительным выражением,
которое принимает значение типа bool. Условие не обязательно должно включать
управляющую переменную цикла. В следующем примере цикл продолжает выпол-
няться до тех пор, пока пользователь не введет символ S.
Цикл выполняется до тех пор, пока не будет введен символ S.
using System;
class ForTest {
public static void Main() {
Lnt 1;
Cons0le.WriteLine("Hnn выхода из программы нажмите клавишу S.");
for(i s O;(char) Console.Read() != 'S';i++)
Console.Wri:eLine("Upoxou M " + i);
Недостающие части цикла for
B Ci? можно создавать интересные варианты Цикла for, оставляя пустыми все или
некоторые части инициализации. условия и итерации. Например, рассмотрим сле-
дующую программу:
// Некоторые определяющие части цикла for могут быть пустыми.
н51п9 System;
class Empty (
publ;c static void Main() {
int i, count = 0;
for(i = 0;: < 10;) { ‹———————————{Йтцшционносвыражениспропуцшна
count++:
Ccnsole.WriteL1ne("npoxon цикла N " + count);
i++:// Увеличение управляющей переменной цикла на единицу.
I и и Глава 3- Управляющие операторы
Здесь рттерацъпонное выражение цикла for пропущено. Вместо этого контрольная
переменная цикла i увеличивается на единицу внутри тела цикла. Это означает. что
каждый раз при выполнении ниша вьнтолъпътстсэт проверка значения переменной 1
(значение переменной сравнивается со значенгтеги I0), HO дальнейших действий не
происходит. (Переменная L:.oL1r‘.L введена только для того. чтобы счет проходов цикла
H2i‘lHll'cL'{C$I с цифры I, а не О.) Г1оекотяьку иззпененне переменной 1 происходит внутри
тела циюпа, он работает нормгш ьно, выводя на экран слсдуютцуто информацию:
Проход Цикла N 1
Проход Цикла Ш 2
Проход цикла W 3
Проход Цикла M 4
Проход цикла У 5
Проход Цикла N 6
Проход цикла Ш 7
Проход цикла M 8
Проход цикла M 9
Проход Цикла М 10
В следующем примере инициеьчизтдрутощее выражение также вынесено за пределы
определяющей части цикла for.
// B этой программе инициализирующее выражение также вынесено за
// пределы определяющей части цикла for.
using System;
class Empty2 {
public static void Ma;n() (
int ii Count 4 О; ИНИЦИЭЛИЗИРУЮЩСС вьтраженис ВЫНСССНО
за нрсдшьл Цнюпа.
i — 0; // Инициализирующее выражение находится вне цикла for.
for(;i < 10;) {
count++;
Cons0le.WriteLine("Upoxon M " + count);
i++;// Увеличение управляющей переменной цикла на единицу.
В данной версии программы тшипрягьчьязания переменной 1 происходит до начала
цикла. и это выражение не является частью цикла. Однако чаше контрольная
переменная цикла инициализируется внутри определяющей части цикла for. Обычно
операция инишдализатщии выносится за пределы Цикла только в тех случаях. когда
начальное значение управляющей переьтенной является результатом вычисления
сложного выражения, которое нельзя разместить внутри оператора for.
Бесконечный цикл
Используя оператор for е пустым условным выражением. вы можете создать беско-
нечный цикл. Например, в следующем фрагменте кода показан способ создания
бесконечного цикла:
for(;;) // Бесконечный цикл.
ммщщшммтштащщжл
объявление управляющих переменных цикла внутри цикла tor 1 1 1
Такой цикл будет работать вечно. Хотя для выполнения ьтскоторых задач программиро-
„дния (например, для регшизацтпья командных процессоров операционной системы)
псобхоххимы бесконечные циклы, существуют специальные средства, при помощи
Которых можно прекратить вьнтшшсние большинства таких циклов. B КОНЦС этой
главы мы расскажем, как можно остановить бесконечным цикл. (Подсказка: это
выполняется с помощью оператора break.)
Циклы, не имеющие тела
В Ci: тело цикла for (ИЛИ любого другого цикла) может быть пустым. Это возможно,
гютому что Hy/zeeoiz оператор синтаксически является дспствитсльным. Циклы без
тела используютсядостаточно часто. Например, в следуюшси программе такои пи K.-l
применяется для суммирования чисел от I до 5.
Тело Цикла может быть пустым.
using System;
class Empty3 {
public static void Main() {
int i;
lnt sum = O;
// Вычисляется сумма чисел от 1 до 5.
for ('1 = 1; '1 <= 5; sum +~ i++) ; 4 В этом птиклс тело отсугствуст!
Console.Wr1teLine("CyMMa равна: " + sum);
Ниже показан результат выполнения этой программты.
Сумма равна 15
Отметим, что вычисление суммы производится полностью в пределах оператора for,
следовательно, в теле оператора нет необходимости Особое внимание обратите на
итерационное выражение
sum += i++
Такие операторы выглядят несколько непривычно, тем нс менее, они часто исполь-
зуются в профессионально написанных Сй-программах. Описать работу приведен-
ного вышс оператора можно следующим образом: «добавить к псрсьтеъпгпои s-.:::~
значение переменной sum плюс значение переменной 1, затем увеличить значение
переменной i Ha единицу». То есть предыдущий оператор аналогичен последователь-
ности операторов
sum = sum + i;
i++;
Объявление управляющих переменных циюта
внутри цикла for
ОЧСНЬ ЧЗСТО управлякпцая ЦИКЛОМ for ПСРСМСННЭЯ НСООХОДИМЭ ТОЛЬКО ДЛЯ СЗМОГО
ЦИКЛЭ И бОЛЬШС НИГДЕ НС ИСПОЛЬЗУЕТСЯ. В ТПКИХ СЛУЧНЯХ МОЖНО ООЪЯВЕЛЯТЬ ПСрСМСННУЮ
1 1 2 Глава 3. Управляющие операторы
внутри ИНИЦИЗЛИЗИрУПОЦКЗЙ ЧЕСТИ ЦИКЛЭ. Например, П|)СДСТЭП.ЧС|П-!ЗЯ НИЖС програгима `
вычисляет как сумму, так и факториал чисел от I ДО 5. В этой программе управляющая
переменная 1 объявляется внутри шпата for.
// B программе управляющая переменная объявляется внутри
// инициализационной части цикла for.
usxng System;
class Forvar {
public stat;c vold Ma1n() {
Lnt sum = C;
lnt fact 2 1;
// Вычисление суммы И факториала чисел от 1 до 5. 'Гшрсмсннш;1обыпшясшя
for(int i д l;i <= 5;i++) 1' B“YTP“ °"CP3T°P3 f0I- 7
sum +2 1:// Областью видимости переменной i является весь цикл.
fact *= i;
// B этом месте программы переменная i не видна.
Cons0le.WriteLine("CyMMa чисел от 1 до 5 = " + sum);
Cons0le.WriteLine("®aKT0pMan числа 5 = " + fact);
}
При объявлении переменной внутри цикла for ее область видимости ограничена
рамками Цикла (то есть область видимости переменной ограничена циклом for). за
его пределами переменная прекращает свое существование. Следовательно, в преды-
дущем примере переменная 1 недоступна за пределами цикла. Если вам необходимо
использовать управляющую переменную ниша в другом месте программы, то объя-
вите эту переменную за пределами Цикла for.
Прежде чем перейти к изучению следующей темы, потренируйтесь в использовании
различных вариантов Цикла for. Вы увидите, что он имеет огромные возможности.
«go Минутный практикум
' 1. Могут ли части оператора for быть пустыми?
И, 2. Напишите синтаксис бесконечного цикла for.
3. Где заканчивается область видимости переменной. объявленной в пределах
I
оператора for?
1. Да, все три части цикла for — инициализирующая, часть условия и итерационная — могут
быть пустыми.
2- fOr(;;L
3. Область видимости переменной. которая объявлена в пределах оператора for. Ограничена
рамками цикла. За пределами цикла эта переменная неизвестна.
Цикл while 1 13
Цикл while
Llum инд le также широко применятетсзп в Сй-програзнтах. Его синтаксис выглядит
сдрдутопцтмт образом:
Иддде (condit;on) statement;
Здесь statement \i0)KCT быть одиночным оператором или блоком операторов, а
gcfifiltlon является условием, которое управляет циклом, и может быть любым
действительным булевым выражением. Оператор выполняется в том случае, если
условие принимает значение true. Если же условие принимает значение false,
управление программой передается строке кода, которая следует сразу за циклом.
Приведем пример программы, в которой шиш while используется для вывода на
экран символов латинского алфавита.
// B программе демонстрируется использование цикла while.
us;ng System;
1
class WhiieDemO §
public static void Main() (
char ch;
// Использование цикла while для вывода на экран символов
// латинского алфавита.
ch = 'a';
wh;le(ch <= '2') {
Console.Write(ch+ " ");
Ch++;
3.’1CCb символ а присваивается Переменной сн в качестве начального значения.
Каждый раз при прохождении цикла значение переменной сн выводится на экран,
'3 ЛПЁМ УВСЛИЧИВЗСТСЯ на СДИНИЪЦЛ ЭТОТ ПРОЦССС продолиаются ДО TCX HOD, flOK3
значение переменной сн не превысит 2.
Как и цикл for, ЦИКЛ while проверяет условие в начале ниша, и это означает, что
КОД Циюча может вообще не выполняться. В приведенной ниже программе демонст-
рируется использование цикла иные для вычисления целочисленной степени числа 2.
// В программе используется цикл while для вычисления целочисленной
// степени числа 2.
us;ng System;
class Power {
Public static void Main() {
int e;
int result;
for(int i=C;i < lO;i++) {
result = 1;
e = i;
while(e > 0) {
result *= 2;
Глава 3. Управляющие операторы
C0nsole.WriteL1ne("2 B " r i + —й степени — " + result):
}
РСЗУЛЬТЗТ ВЫПОЛНЕНИЯ ЭТОЙ ПРОГрЗММЫ ПОКЗЗЗН НИЖС.
2 в Оей степени 1
2 н 1-й степени — 2
2 В 2-й степени = 4
2 В 3—й степени 8
2 B 4-M степени I 16
2 В 5-й степени = 32
2 В 6-й степени I 64
2 в 7-й степени = 128
2 в Вей степени = 256
2 в 9—й степени = 512
Заметьте, что Цикл while выполняется только в том случае, если значение перемен-
ной е больше 0. Поскольку при первом выполнении циюта for значение переменной
1, а значит и переменной е, будет равно О, ЦИКЛ шт 1е выполняться не будет.
Циюг do-while
Взавершение этой темы рассмотрим цикл do-wile. Вотличие от циклов for И иные,
где условие проверяется в самом начале, в цикле do~while условие проверяется в
конце цикла. Это означает, что цикл do—while всегда выполняется как минимум
один раз. Синтаксис цикла do—whi].e следующий:
do (
statements;
} while (condition);
При наличии в ЦИКЛС только одного оператора фигурные скобки не обязательны, но
их часто используют для улучшения читабельности конструкции цикла dO~wh1le,
поскольку его лсгко спутать C циклом whlle. Если при проверке условие принимает
значение true, цикл сто-иные выполняется еще раз.
В следующей программе цикл продолжается до тех пор, пока пользователь нс введет
символ q.
// В программе демонстрируется использование цикла do—wh1le.
using System;
class DWDemo (
public static void Main() {
char ch;
do {
Console.Write("BBenMTe символ, после чего нажмите
" клавишу ENTER: ");
Ch = (Char) Соп$о1е.Ееаб();//Считывание символа.
} while(ch != 'q');
. „м“ ‚(‚‚_.______ .‚..
шмыщ
115
цикл do—whi|e
ИСПОЛЬЗУЯ ниш do~while, мы можем усовершенствовать разработанную в начале
Ггшшд гдрограиму «Угадай букву». Теперь программа будет выполняться до тех пор,
пока буква не будет угадана.
программа "Угадай букву" версия Ш4.
u;:;nq 55 stem;
3,455 GUess4 i
uubL-: s:at;c мода Ма;п()
char Ch, answer ‘K’;
GO 5
соп5о1е.Иг1[еЬ1пе("Угадайте букву алфавита, \"снрятанную\"
"в нрограмме.");
Соп5о1е.Иг1ееЬ1пе("Введите символ: "):
./ В цикле считывается символ, но игнорируются
// езсаре+послсдовательности \п и \r, соответствующие клавише ENTER.
do {
ch (char) Cons0le.Read();// Считывание символа.
} wh1le(ch ша '\п' ch 2: '\r'):
if(ch 1: answer) Console.WriLeLine("** Правильно ‘*");
else {
Соп5о1е.Иг1Ее("Попытайтесь поискать "):
if(ch < answer) Console.WriteLine("Bume B алфавите.");
else Console.Wri:eLine("Hmxe В алфавите.\п"):
)
while(answer !— ch);
Ниже показан один из возможных результатов работы программы:
Угадайте букву алфавита, "спрятанную" в программе.
Введите символ: А
Бонытайтесь поискать выше в алфавите.
Угадайте букву алфавита, "спрятанную" в программе.
Взедите символ: Z
'аЙшесь НОИСКЗТЬ НИЖЕ В ЭЛФЗВИТЕ.
7=ь„аише букву алфавита, "спрятанную" в программе.
Начните символ: Е
" Правильно **
Обратите внимание на интересную особенность ЭТОЙ программы. ПредСТаВ-ЧСРШЫЙ
ниже цикп do~while считывает вводимый символ, пропуская символ возврата
каретки или новой строки, которые могут быть во входном потоке.
I
x B ЦИКЛЕ СЧИТЫВЗЭТСЯ СИМВОЛ, НО игнорируются
/' езсаре—носледовательности \п И \r, соответствующие клавише ГЕПЁЗГ]
со {
Ch V (char) Console.Redd();// Считывание символа.
ыН;1в(сп "ц.' ch '\r'):
Этот Цикл был введен в программу ътменно для того, чтобы она игнорировала
ненужные символы. Как уже говорилось ранее, при вводе Данных с клавиатуры строка
ввода помещается в буфер, поэтому прежде чем символы будет переданы программс,
необходимо нажать шавишу [Enter]. При этом генерируются символы возврата
. п, Глава з. Управддющие операторы
каретки и новой строки, которые остаются во входном буфере. Цикл dO~while
отбрасывает эти символьн и продолжает считывание до тех пор, пока не встретится
какой-либо другой символ.
- двигать: ллефееслелеае
<3 Bonpoc. ЦИКЛЫ обладают очень большой гибкостью. Как правильно выбрать
цикл для определенной задачи?
Ответ. Используйте цикл for. когда известно число повторений цикла, и ЦИКЛ
do—while, когда необходимо хотя бы одно прохождение цикла. Применение цикла
while наиболее эффективно в тех случаях, когда число повторений цикла неизвестно.
Минутные’ практикум
Каково основное различие между циклгтми while И do—whi1e?
l
2. Справедливо ли утверждение, что условие, управляющее циклом wmle,
может быть любого типа?
Проект 3-2. Совершенствование справочной
Ё He$p2.cs
B этом проекте расширяется справочная система C#, созданная в проекте 3-1, —
добавляется синтаксис циклов for, while И do—while. Программа также проверяет
правильность выбора пользователя, продолжая выполнение цикла до тех пор, пока
не будет введен корректный символ.
Пошаговая инструкция
I. Скопируйте код файла I-ielp.cs п новыи файл под таазвантасм Help2.cs.
2. Измените фрагмент программы, выводящей на экран меню выбора, так, чтобы
ИСПОЛЬЗОВЭЛСЯ ПрЁДСТЭВЛСННЫЙ НИЖЁ ЦИКЛ.
do (
Console.WriteLine("CnpaBKa no синтаксису: ");
Console.writeLine(" 1. Оператор if");
Conso1e.WriteLine(" 2. Оператор switch");
Console.WriteLine(" 3. Цикл for");
Cons0le.WriteLine(" 4. Цикл while");
Console.WriteLine(" 5. Цикл do—while\n");
Console.Write(“BBenMTe порядковый номер оператора или цикла: ");
do (
choice 2 (char) Console.Read();
Ъ wh;1e(choice -1 '\n' . choice == '\r');
}wh;le( choice < '1' | choice > '5');
1. Цикл while проверяет свое условие в начале цикла. Цикл do-while проверяет свое условие
в конце цикла. Следовательно, цикл do—while всегда выполняется как минимум один раз.
2. Нет. Условие должно иметь тип bool.
Цикл do-while
На.)
4.
Здесь, как и в предыдущей программе. иикл do-whJ,].e ьпепользуется для того,
чтобы игнорировать символы возврата каретки и новой строки, которые могут
присутствовать во входном потоке. После произведенных ьтзченсъпий программа
будет выполнять цикл и выводить на экран чет-по до тех пор, пока пользователь не
введет корректный символ — Цифру из диапазона от I до 5.
Pz1cI.uMpbTc оператор switcn, включив в него синтаксие Циклов for, иные и
.;1.:‚—шш1е, как ЭТО показано ниже.
switch (choice) Е
case '1':
Consoie.WriteLine("Onepawop if:\n");
Consolc.Wr1teLLne("Lf(Cond;t1on) statemcnt;");
Consoie.Wr;teLine("else sCatemenL;");
breax;
case '2':
Console.WriteLine("Ouepawop swiCCh:\n"):
Console.WriteLine("swiCCh(expression) (");
ConsOle.WriCeL1ne(" case constantz");
Console.WriteL1ne(" statement sequence");
Console.WriteLine(" break;");
Console.WriteLine(" // ...");
Console.WriteLine("}");
break;
Case '3':
Console.WriteLine("uMxn for:\n");
Console.Write("for(init;condit;on;iteratlon)"):
Console.WriteLine(" statement;");
break;
case '4':
Console.Wr1teLine("1mKn while:\n");
Console.Wr;teLine("wh1le(Condi -0n) staLcmen:;");
break;
case '5':
Console.WriteLine("HMKn do—while:\n");
Console.Wr;teLine("do (");
Console.WriteLine(" statement;");
Console.Wri:eLine("} wh;ie (Cond;tion);");
break;
)
Отметим, что в этой версии оператора зщгссп отсутствует ветвь default. ЦИКЛ,
предназначенный для отображения меню, гарантирует ввод пользователем дейст-
вительного символа, поэтому ветвь default для обработки некорректного поль-
ЗОВЭТСЛЬСКОГО ВВОДН НС Hyxuxa.
Ниже п едставлен полный листинг п ог аммы Не; 2 .СЗ2
D P
/* Проект 3-2.
Улучшенная справочная система,
обработки пользовательского ввода цикл while.
mcnonuaywman для
'/
using System;
class Help2 (
publlc static void Main() {
char choice;
do 1
и I О глава 3. Управляющие onepaTOPbI
Conso1e.WriteLine("cnpasxa по синтаксису: ");
C0ns01e.WriteLine(" 1. Оператор if");
Conso1e.writeLine(" 2. Оператор switch");
Console.WriteLine(" 3. Цикл for");
Conso1e.writeLine(" 4. Цикл while");
Console.WriteLine(" 5. Цикл do—while\n");
Сопзо1е.Ик1пе("Еведите порядковый номер оператора или цикла: "):
do (
choice = (char) Conso1e.Read();
) while(choice :> '\n' 1 choice ~~ '\r');
) while( choice < '1' I choice > '5');
Console.WriteLine("\n");
switch(choice) {
case '1':
Console.WriteLine("OuepaTop if:\n");
Console.WriteLine("if(Condition) statement:");
Console.writeLine("else statement;");
break;
case '2':
Сопзо1е.Ик1пеЬ1пе("Оператор switch:\n");
Console.WriteLine("switch(expression) (");
Console.WriteLine(“ case constant:");
Console.writeLine(" statement sequence");
Console.writeLine(" break;");
Console.WriteLine(" // ...");
Console.writeLine("}");
break;
case '3':
Console.Wr1teLine("uMxn for:\n"); I
Console.Write("for(init;condition;iteratlon)");
Console.wr1teLine(" statement;");
break;
case '4':
Console.WriteLine("HMKn while:\n“);
Console.WriteLine("while(condition) statement;");
break;
case '5':
Console.WriteLine(“uMxn do~whi1e:\n“);
Console.WriteLine("do (");
Console.WriteLine(" statement;");
Console.WriteLine("} whlle (condition);");
break;
.‚ ... -..‚‹т›т.н мицелия:
Использование оператора break для выхода из
ЦИКЛа
Использование оператора break предоставляет возможность принудительного и
Немедленного выхода из цикла без выполнения оставшегося кода в теле Цикла и
Проверки условия цикла. То есть ЦИКЛ завершает свою работу. когда оператор break
использование оператора break для выхода из цикла 1 19
встречается внутри пьнкла, при этом управление программой передастся оператору,
“дующему за циклом. Приведем простой пример.
‚д В „рдграмме демонстрируется использование оператора break
if для выхода из цикла.
„5дщ System;
;,ds;= Breakbemo (
pubLLc static мода Ma1n() (
;nL num:
—„* ‚СО;
ЦИКЛ r3LnI:UJIHSviv'2'-'CS1 ДО 'i'(3X нор, люка Квадрат ЗНЦЧСНИЯ
// переменной ; не нревыси значение переменной num.
Lox (in: ъ О; i < num;1++) ( Использование оператора break для выхода из цитат
;r(i*1 >2 num) break;// Если выражение 1*1 >— 100 примет
// значение true, будет выполнен оператор
// break, что приведет K выходу из цикла.
Console.Write(i + " ");
)
Console.WriteLine("UmKn завершен.");
Эта программа выведет следующую строку:
г ч
и ц. 3 4 5 6 7 8 9 Цикл завершен.
Цикл for ДОЛЖСН повториться I00 раз, но выполнение оператора brea}: приводит к
досрочному’ прскрандсниъо работы Цикла. Это происходит, когда значение переменной
д, позволенное в квадрат, становится большим или равным значению переменной nurn.
Оператор break может иепогяьзоватьея в любом ЦИКЛС, вкшочая преднамеренно опре-
деленные программистом бесконечные ниш ы. Спедутоцъазт программа просто считывает
мводигньке полызователегн еимвольк по тех пор, пока не будет нажата клавиша q.
т’ Программа считывает ВВОДИМЫЕ СИМВОЛЫ ДО тех пор,
ПОКа не будет нажата клавиша q.
„эдак; System;
ciass Break2 (
public static void Main() {
Char ch;
Рабога этого бесконечно-
го Цикла прекращается с
{помощью
fort ;.') (‘v
ch — (Char) Conso;e.Read();// Считывание символа.
if (Ch т‘ 'C1') break-' < unepampu break.
L___..j___,....::
}
Console.WriteLine("Bbx нажали клавишу ql");
ЕСЛИ оператор break ИСПОЛЬЗУЕТСЯ ВНУТРИ ПЛОЖСННЫХ ЦИКЛОН. ТО OH HDCKDZIUIEICT
работу только внутреннего Цикла. Например:
.’/ Использование оператора break во вложенных циклах.
пзлщ System;
120
Глава 3. Управляющие операторы
class Break3 1
public static void Main() Ё
дпс countl ~ C;
ConsoLe.writeLine("B
"бьш вьпзолъхен каждый из циклоид):
„ро1рамме подсчитывается, сколько раз \п"+
for(;nt 1»C;i<3;i++)
coun:l++:
Соп5о1е.Их1ееЬ1пе("Количество
проходов внешнего цикла: " + c0untl);
Console.Write(" Количество проходов внутреннего цикла: ");
in: С ~ 1;
Инден < 10G) (
_f(t em 11) break; // Оператор прекратит выполнение Цикла,
зьачение 1-.
// если переменная L
п п);
примет
Console_Write(£ +
t++;
,
Console.WriteLine();
}
Console.WriteL;ne("Baemumfi цикл завершеп.");
}
B результате выполнения этой программы на
ш «hop МН ц и л:
будет выведена следующая
В ьрограмме
был выполнен каждый из циклов.
ПОЦСЧИТЫВдеТСН ‚ СКОЛЬКО раз
КОЛИЧЕСТВО НРОХОДОВ ВНЕШНЕГО ЦИКЛЗ: 1
Количество проходов внутреннего цикла: 1 2 3 4 5 6 7 З 9 10
Количество проходов внешнего цикла: 2
Количество проходов внутреннего цикла: 1 2 3 4 5 б 7 8 9 10
Количество проходов внешнего Цикла: 3
Количество проходов внутреннего цикла: 1 2 3 4 5 б 7 8 9 10
Внешний цикл завершен.
Как ПИДИТС‚ ИСПОЛЬЗОВЦНИС Оператора break BO BHyTpCHHCh1 ЦПКЛС ПрИВОДИТ К
остановке только впуч рен него ЦИКЛЫ. Внеппппт Цикл гтродолжгтет‘ работать ло полного
заверппения.
тЁгЭЁЁЁЁЙЁЁЁ 2?§§§v%g$§£@§§§:«:§
ф} Вопрос. В Java операторы break И continue Moryl HCnOflb.3OBaTbCiI C .\lCTKE1\1M.
Поддерживается ли такое использование операторов в C13‘?
Ответ. Нет, разработчики C# НС наделили операторы break и con tmue
TdKH’\1l1 возможностями. эти операторы в Cit работаю: так же. как в языках С п С+т.
Пшо в тон. что п языке Java HC r1o:u1cp>1<uI;zIc'I‘csI оператор g<»:c-. fIO£[,ElCp)I<Ml3Z1C\Iblii Н
С“. Поэтому В Java операторан break 11-ССПЕ1ПЫе псобходихпл лополнитсльньш
Нозиожгтостьт для компенсации отсутствия оператора got-'3.
Необходимо запомнить еще две особенности использования оператора break. Во-
первых, в цикле может встречаться больше одного оператора brea k. Будьте внима-
тельны, слишком большое количество операторов break И неправильно определенные
.›- »'u~qe:..'.-.;.'(~¢n- m...-mew. '
Оператор goto 121
VCJIOBIIHC BbIp'd)KCi-IMSI для этих операторов могут привести к т-текорттекггтойт работе
программы. Во-вторых, оператор break, останавливаюший работу оператора swi сел,
влияет только на этот оператор switch И не влияет на работу внешних ЦИКЛОВ.
использование оператора continue
в C# существует возможность «заставить» цикл принудите.тьт-то перейти к следующей
итерации (следующему выполнению последовател ьноети операторов, определенных
в теле mama). Это осуществляется с помощью оператора сс-пыгъце, который является
догтолгтснием к оператору break. Например, в следующей программе оператор
ggrxtlnue используется для вывода на экран четных чисел из диапазона от О до I00:
// B программе демонстрируется использование оператора continue.
ub;nq System:
;-де5 C0nLDcmo {
public static void Main() {
int i;
// Вывод на экран четных чисел,
// находящихся В дидпдздне от О до 10О_ [Клизншшниепешлшнной1 неюпющ
fOr(i L 0;i<:10C;i++) { выпшцшешя‹люршорсопп1пце.
if((i%2) != O) continue; // Следующая итерация.
Console.Wr1teLine(i+ " ");
B ЭТОЙ програмьк:на ЭКРЭН ВЫВОДЯП51ТОЛЬКО ЧСТПЬЮ числа,поскольку ПрИСВОСНИС
ПСр6МЁ}П1ОЙ 1 НСЧСТНОГО ЗНЗЧСНИЯ ПрИВСДСТ К ВЬПТОЛНСНРПО СЛ6ДуКЛЦЁЙ ИТСр8ЦИИ
ЦИКЛЗ С ПрОПУСКОМ ВЫЗОВЭ МСТОДН WriteL1ne ().
B циклах while и do—while использование оператора Continue приводит к передаче
управления непосредственно условному выражению, после чего выполнение цикла
продолжается. В цикле for сначала выполняется итерационное выражение цикла,
‘затем оценивается выражение условия, а уже после этого продолжается выполнет-тис
цикла.
Cr?‘ предоставляет возможность большого выбора циклов, которые удовлстворятот
требованиям большинства приложений. Если же вы столкнетесь с редкой ситуацией,
Когда необходимо в определенный момент прекратить выполнение данной итерации
цикла, то сможете сделать это, используя оператор contmue.
Оператор goto
Оператор goto в C# является оператором безусловного перехода. Если он всгречается в
коде, контроль над выполнением программы переходит в то место, которое указано
оператором goto. Ранее многие программисты избеггьти использования этого оператора,
поскольку в результате его применения программы етагтовильтсь неструктурнымьт и
слишком запутанными. Но иногда оператор goto может быть очень полезным.
Необходимо признать, что в программировании обычно отсутствуют ситуации,
1 22 Глава 3. Управляющие операТОры
КОТОРЬЦЗТрСбуКП`ОбЯ3НТСЛЬНОГО иепольдовнньппэтого‹)псрнтора,екорее,опппредстав-
ляет собой Цополненьпе к nHcTpy.\IcHTL1p|no программиста. B Hznucii KHHIC оператор
331’ DZ1CC\1‘d'I'])HHHCTCSI TO.'lbKO ll ЭТОЙ ГЛНВС И бОЛЬЦКС HHHIC ПС llCl1().lb'3)'L‘TC}l.
Для оператора »;;-;»t:> ДОЛЖНЫ быть определена меткам причем ес определение должно
быть выполнено в рамках того же метода, в котором она используется оператором
доке. Метки представляет‘ собой дсйствитсльнькй идентификатор, за которым следует
двоеточие. Например, запись Цикла, который должен выполнить 100 ъктсраций, при
использовгпнъпи оператора gate И метки выглядит следующим образом:
Хт_7
1оОр1:<
хгт;
if (X < 100) 9010 100101 :%нтроль над управлением программой переходит к метке 1оорц
Оператор дат: можно эцкрсктъпвгю использовать для выхода из глубоко вложенной
процедуры. Вот простои пример его использования:
/~l<
B программе демонстрируется использование оператора goco.
Jr
/
using System;
class Use_goto {
public static мода Main() (
int 1—O, дев, k;C;
for(i—O;i < 10;i++) g
for(j—O;j < 1C;j++ ) (
for(k—O;k < 10;k++) (
Console.WriteLine("i, j, k: " + 1 + " " + j + " " + k);
if(k :— 3) goto stop;
Stop:
Соозо1е.Иг1пеЬ1пе("Завершение выполнения цикла! i, j, k: " + 1
+1!’ u+j+uu+k),.
)
НИЖС представлен рсзульт ат выполнения этой программы.
i, 1, К: О О О
;, j, k: C О 1
i, j, k: O C 2
i, j, k: O O 3
Завершение выполнения цикла! i, j, k: О, О 3
Если бы мы истючигяи из программы оператор goto, то пришлось бы трижды
использовать операторы if И break (то есть в данном примере оператор goto
упрощает код). Этот пример приведен для того, чтобы вы могли представить себе
ситуации. в которых применение оператора goto оправданно.
Оператор 9ою 1 22
д’ минутным? драхма/кум
I. Что происходит при выполнении оператора break в пределах Цикла‘?
2. Какие действия выполняются оператором continue?
3. Какой оператор в Ct? является оператором безусловного перехода?
у проект 3-3. Завершение создания справочной
= 1 системы С#
шнырасз
B ЭТОМ проекте мы завершаем создание справочной системы Ctr, Над которой начали
работать в предыдущих проектах. В последней версии добавляется синтаксис опера-
торов break, Continue И goto, а также создается возможность многократного
обращения к справочной системе. Осуществляется ЭТО посредством добавления
впсгцнсго Цикта, который выполняется до тех пор. пока пользователь не введет
‚ символ q.
Пошаговая инструкция
1. Скопируйте код файла Не1р2 .cs в новый файл, назовите этот файл Help?» . cs.
2. Поместите весь код программы внутрь бесконечного Цикла for. Для прерывания
Цикла используйте оператор break, который должен выполняться при вводе с
клавиатуры символа q. Поскольку весь код программы помещен внутрь цикла
for, выход из цикла будет приводить к завершению работы программы.
э. Измените цикл, предназначсннькй для отображения меню, следующим образом.
" do {
п C0nsole.WriteLine("Cnpasxa по синтаксису: ");
Console.WriteLine("
Console.Wr1teL1ne(" 2
Console.WriteLine(" 3. Цикл for");
Console.WriteLine(" 4. Цикл while");
Console.WriteLine(" 5. Цикл do—while");
6
7
6-‘
Оператор if");
. Оператор switch");
Console.WriteLine(“ . Оператор break");
Console.WriteLine(" . Оператор continue");
Console.WriteLine(" 8. Оператор goto\n");
Cons0le.WriteLine("Baenume порядковый номер оператора или Цикла: ");
Соп5о1е.Иг1СеЬ1пе("Дпя завершения работы программы введите символ q.");
.... ‘печи.
do (
choice = (char) Console.Read();
} while(choice == '\п' | choice := '\r'); _
) while( choice < '1' l choice > '8' & cholce != 'q');
l. Выполнение оператора break B пределах цикла приводит к остановке цикла. Управление
программой передается первой строке кода, следующей за циклом.
2. При выполнении оператора continue происходит немедленный переход управления К
следующей итерации цикла без выполнения оставшегося кода.
3. В С# оператором безусловного перехода является оператор goto.
Глава 3. Управляющив операторы
Обратите внимание, что тспсрь в этот цикл включена возможность выбора
СИНТЭКСИСЭ Ql1CpHTOp0Bbr€:ak, Continue И goto. KpQ.\1C ТОГО. В НСМ УЧИТЫВЦСТСЯ
[ХОЗМОЖНОСТЬ ВВСЛСНИЯ ПОЛЬЗОНЗТСЁХСЁИ СИМВОЛЫ q.
4. R‘acump1>1c оператор swltch, чтобы он вюпочгш ветви case, отображающие
синтаксис ОПСраТОрОВ break, contlnue и gate, как это Показано НИЖС.
case ‘Б’:
Соп5о1е.Иг1сеЬ1пе("Оператор ю:еак:\п");
Console.WriEeL:nc("Dreak;");
break;
case '7':
Conso1e.WriteLine("ouepawop continue:\n");
COnsOle.WriCeL1ne("COnLinue;");
break;
Case ‘Е’:
Соп$о1е.И:1пеЬ1пе("Оператор goto:\n");
Conso1e.writeLine(“goto label;“);
break;
э. Далее представлен полный ‚чистинг програимьт Help3
/ir
Проект 3 3
Заключительная версия справочной системы С#‚ к которой можно обращаться
многократно.
*/
using System;
class Help3 {
public static void Maln() I
char choice;
for(:;) {
do {
Console.WriteLine("Cupaaxa no синтаксису: ");
Console.WriteLine(" 1. Оператор if");
Console.writeLine(" 2. Оператор switch");
Console.WriteLine(" 3. Цикл for");
Console.writeLine(" 4. Цикл while");
Console.writeLine(" 5. Цикл d0—wh1ie");
Console.WriteLine(" 6. Оператор break");
Console.Wr1teLine(" 7. Оператор continue");
COnsOle.WriteLinE(" 8. Оператор goto\n");
Cons0le.WriteL1ne("Bueumre порядковый номер оператора или цикла: ");
Cons0le.WriteLine("fluH завершения работы программы " +
“введите символ q.“);
do {
choice — (char) Console.RCad();
} While(choiCe => '\n' I choice г‘ '\r');
}while( choice < '1‘ I choice > '8' в ChOlCe Е: 'q');
i£(choice ;— 'q') oreak;
Con5ole.WriteL1ne("\n");
Ё
«2
Ё
Ё
оператор goto
sw1tch(choice) (
case '1':
Console.Wr;teLine("ouepdwop if:\n“);
Conso1e.WrLteLine("if(concit1on) s:a:emen:;");
Console.WriteL1ne("else sta:ement;");
break;
case '2':
C0nsole.WriteLlne("ouepamop switch:\n");
Console.WriteL;ne("sw;tch(express1on) {");
Console.WriteL1ne(" case constantz");
Console.Wr;teLine(" statement sequence");
Console.Wri:eL1ne(" break;");
Console.Wr1teLine(“ // ...“);
Console.WriteLxne("}");
break;
case ‘3‘:
Console.WriteLine("HMKn for:\n");
Console.Write("f0r(init;condition;iteration)");
Console.WriteLine(" statement;“):
break;
case '4':
Console.WriteLine("uMxn while:\n");
Console.Wr1teL1ne("whi1e(conc; ion) statemen:;");
break;
case '5‘:
Consore.Wr1:eL1ne("HmKx do—wh;lc:\n");
Console.Wr1:eLine("do (");
ConsoLe.WriLeL1ne(" statement;“);
Console.Wr1teL;ne("} while (Condit1on);");
break;
Case '6':
Console.Wr1teLine("Oneparop break:\n");
Console.writeLine("break;");
break;
case '7‘:
Console.Wr1teLine("OHepaT0p C0ntinue:\n"):
Conso1e.WriteLine("cont;nuc;");
break;
case '8':
}
Console.WriteLine("Onepawop goto:\n");
Console.WriteL1ne("goto label;");
break;
Console.WriteLine();
}
НИЖС ПОКВЗНН ОДИН ИЗ ВОЗМОЖНЫХ pC3_VIIbTdT()B BblH()."1HCHll}l HpOTp21M\Ibl.
Справка ПО СИНТВКСИСУ!
O\U\J:>L;.)f\J|—‘
Оператор if
Оператор swiLch
ЦИКЛ for
Цикл while
Цикл do—while
Оператор break
Оператор continue
1 26 Глава 3- Управляющие onepefigpg
8. Оператор goto
Введите порядковый номер оператора или цикла:
Для завершения работы программы введите символ q: l
Оператор if:
;L(cond;ti0n) 5:atemenc;
else statement; -
Справка по синтаксису:
. Оператор if
Оператор sw;[ch
Цикл for
. Цикл whi;e
Цикл @о—шЬ11е
› ‚
. Оператор break
. Оператор continue
Оператор goto
Введите порядковый номер оператора или цикла:
Для завершения работы программы введите символ q: 6
ED " Ф U’! Ь U.‘
Оператор break:
break;
Справка по синтаксису:
1. Оператор if
. Оператор switch
. Цикл for
ЦИКЛ whiLe
Цикл 0о—ип;;е
Оператор break §
. Оператор continue
Оператор goto
введите порядковый номер оператора или цикла:
Для завершения работы программы введите символ q: q
JL~L.LJl-J
П‘) `З ОХ LJ'1
Вложенные циклы
Как пилно из предыдущих примеров. один иикл можно вложить нпугрь другого.
Вложсппльяс пикгхы ьяспользуяотсза ДЛЯ рсппсвшзп разнообргхзньъх задач и янляктгся
мощным инструментом в программированим. Позтонму. прежде чем 'зги;ерхиитл‚ опи-
сание операторов C11, рассмотрим еще один пример вложенного цикла. B следующей
программе используется шхоженньтй ЦИКЛ for для япахождения делителей чисел,
находящихся в диапазоне от 2 до I00:
I/it
B программе демонстрируется ИСПОЁПЬЗОВЗНИЁ ВЗ1ОЖЁННОГО ЦИКЛЕХ .LJIH H:3XO)I(J_iGHT/Iii
делителей целых чисел, находящихся в диапазоне от 2 до 100.
ч
us;ng SysLem;
Class F;ndFac Q
public static мода Ma1n() {
V
Я
Вложенные циклы
f0r(int i:2;i <—~
ConsoLe.Write ("Делители числа
Eor(in:
if( (i%j)
*—= О)
127
lOO;i++) 4
" * i + "= ");
< i;j+*')
Console.Write(j r
.. п);
Console .WriteL1ne() ;
В
Ниже представлена часть результатов выполнения программы.
Делители
Делители
‚ЫСЛИТЕЛИ
,J'c;m'1'eJ11/1
J ll/1'1‘ ели
Делители
делители
Делители
Делители
Делители
Делители
делители
Делители
Делители
Да}! ьители
Делители
Делители
целители
делители
В этой программе при
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
ЧИСЛЕ
2:
ш
Ш
ч:
4510
ВЫПОЛНЕНИИ внешнего ЦИКЛЗ значение переменной i yBC.flM-
чивается от 2 до 100. Внутренний цикл успешно проверяет все значения от 2 до
ЗНЭЧСНИЯ, КОТОрОС В ДЭННЫЙ момент ИМЁСТ переменная 1, ВЫНОДЯ на Экран те ИЗ НИХ,
на которые значение переменной 1 делится без остатка.
128
10.
Глава 3. Управляющие операторы
ЁИЁМЁЖЁЁ ада ЁЁЫ
Напишите программу, которая считывает символьт, вводимые с клавиа-
туры, до тех пор, пока не будет введен символ точки (.). Программа
должна сосчитать количество введенных пробелов и вывести это число
в конце программы.
Может ли последовательность кода одной ветви Case передавать управ-
ление Программой другой ветви case B операторе змеев‘?
Напишите синтаксис цепочки операторов 1f—else-1f.
C КИКИМ оператором ;£ ассоциирован последний оператор else B КОДС
if(x < 10)
if(y > 100) {
if(!done) х 4 z;
else y + Z;
}
else Conso1e.Wr1teLine("OmM6Ka!");//K какому оператору if
// принадлежит его часть else?
Запишите пример Цикла f-:-r, в итсрационном выражении которого
число 1000 должно уменьшаться до 0 с шагом -2.
Является ли действительным следующий фрагмент кода:
for(int i — О; 1 < num; i++)
sum += i;
count = i;
Какие действия выполняет оператор break‘?
Рассмотрите следующий фрагмент кода. Какая информация будет вы-
ведена на экран после выполнения оператора break?
for(1 - 0;; < lO;i++) {
while(running) {
if(x<y)
//
break;
}
Consolc.Wr;:eLine("EmKu while завершил свою работу.");
}
Console.WriLeLine("UMKn for завершил свою работу."):
Как будет оформлен вывод на Экран значения переменной д‘?
for(int L 2 0:1 < lO;i++) {
Cons0le.write(i + " ");
if((i%2) ~— О) continue;
C0nsole.Wz;LeLine();}
MTCDEILIHQHHOC выражение В ЦИКЛС for HC ВССГДЭ ДОЛЖНО ИЗМСНЯТЬ КОН"
ТрОЛЬНУ}О IICPCMCHH)/}0 ЦИКЛ?! Hi! (l)MKCHpOBaHHy}0 ucmmmly. Контрольная
переменная цикла может ьтзметчяться произвольно. Напишите программу,
в которой ЦИКЛ for используется шт вывода на экран геометрической
HPOTPCCCHI4 1. 2, 4, 8, I6, 32, 64 И так далее.
.. .„-..‚.„‚ ...--.... ..»¥ ’>-»' w.
К
ё
Ё
'«¢~t-1-2/'-.':>n'\\\/;#.%:v/-.>.{>¢~" м» '-17›-'ч›/' ‚
ё
Ё
‚ бы: н:
... .......-cm
Контрольные вопросы 14:
П. Числовые значения символов нижнего регистра в коде ASCII o'rm1-ImoT-
ся от значений символов верхнего регистра на величину 32. Следова-
тельно, для конвертирования символа нижнего регистра в символ верх-
него регистра необходимо вычесть из его значения число 32. Используя
эту информацию напишите программу, которая читает съшвольл. иво-
димьле с кяавьтатурьл. Программа должна конвсртиротъать все симпольп
нижнего регистра в сиътволь! верхнего регистра и наоборот, ял-мзодгп на
экран результат. При этом все остатгьньпе символы остаются неизмен-
ными. Программа должна прекращать работу, когда пользователь вводит
символ точки (.). В завершение работы программа должна вынести
информацию о количестве измененных символов.
Классы, объекты
и методы
ШШШШШШШШШ
Определение класса
Создание объектов
Создание методов
Передача методу параметров
Возвращение методом значений
Использование конструкторов
Оператор new
Деструкторы и «сборка мусора»
Ключевое слово this
,.„‚_‚„ ,.., . „мы; um: .„ пища ‚толь. ‚мм
...ь
Ф!
...ь
Основные понятия класса
Г———-——————————
Прежде чем продолжать изучение языка C#, необходимо рассмотреть основную
Структуру объектно-ориентированного программирования ‹— класс. Клддс ‚шттотся
фупдамстттохт, на котором построен весь язьтк Ch‘. B массе определены ддтдтьто и Код‘
дчаботатонти и с этими данными. Это очень обширная и важная тс ма‚ поэтому „ттщтатодтьно
д; ;_\-Imc данную главу. Только поняв. что нрсдетгтттлятот собой классы. объекты H „от-оды
‚вы сможете нисать более сложные н совершенные гтротргпптьт на Си,
ОСНОВНЫЕ ПОНЯТИЯ класса
„„...м . ц
Весь активный процесс Сфг-ттротргтхягхтьт происходит в пределах Iclacczl, поэтому с самого
начала в программах этой книги использовались ктаееы. Понятно„ что они вши самымтт
нростьтмт-т и вы смогли познакомиться только с мшои частью их во3\1ожностсп_ кдассьд
рассматриваемые дгыьшс, обладают гораздо большей мотныо. В этом разлете мы под-
робно остановимся на основных поттятттях тетаееа.
Класс является основой„ для создания объектов В классе опредтелятотст данные и
код, который работает с этими данными. Объекты являются эк3ел1п.а;:рд‚ш4 класса
Непосредственно инициализация переменных в объекте (псрелтеннь/х 3];3g_,1"7_.'];[pg)
происходит в конструкторе. В классе могут быть определения несколько Конструкто-
ров. то есть класс является набором проектов, которые опрсделятот, как строить
обьскт. Очень важно понимать тэазницу между ютассохт и объектом: теща; ярдяс-гся
логической абстракцией до тех нор, пока не будет создан обьект и „г пошштоя
физическая реализация этого класса в пахтяттт компьютера.
\ICTOHbl П переменные. COCTZIBHSIXOIUVIC КЛАСС. HZI3hIB'cHOTC$I ‘l,l(’Il(I.r\lLI K.-'1ilCC;L
Общий синтаксис класса
При оттределенитт клаеса объявляются данные, которые он содержит, и код работаю_
Hum е Этими данными. Самые простые ютасеы могут содержать только коддди только
данные, но в реальных программах Классы включают обе эти составляющие.
‚Ханныс содержатся в переменных экземпляра. которые оттрелелсньт тстассохд а код
С‹1.'1ер›кится в методах. Важно е самого начала отметить. что в Ctr‘ ();[p()L'1(j_-'[c}{b1
несколько специфических тэазновидностей членов класса. Это т нерсметтныо ;)K3C'\1_
нляра, статические переменные, константы, методы. коттструкторьд дшруК-торьд
ннлексаторьт. события, операторы и свонегтва. На данном этапе мы (титаны-дымы
оттисанием переменных экземпляра и методов, являхотцттхсэт основными моментами
клаееа, Позже в этой главе будут рассмотрены конструкторы и деетрукторьд Q других
типах членов класса мы расскажем в следующих главах этой книтн.
При создании (определении) класса вначале указывается ключевое слово ¢1as3_
Ниже представлен общий синтаксис определения класса, содержащий только пере-
ЧСННЫС ЭКЗСМПЛЯРН И МСТОДЫ.
'ез55 Classname {
' объявление переменных экземпляра
access курс vari;
access курс var2;
/ / . . .
access type шахт;
// Объявление методов
access ret~type methodl(parameters){
132 Глава 4. Классы, объекты и методы
‚’/ тело меч-ода
}
access ret«type method2(parameters){
// тело метода
}
/,‚' /
I
дССЁ55 ret~type methodN(parameters){
// тело метода
Обрдтитс внимание, что объявление каждой переменной и метода пачннаегеэт с
„омтановочтиъго слова access, вместо которого объя властен модификатор. указы-
ваюьппЁ-т, как осуществляется доступ к данному члену класса. Как уже говорилось в
главе 1, использование модификатора private yKZl3blB€t€T на то, что члены класса
доступны только для членов этого класса, а использование модификатора public —
„д то, что они открыты [um кода, определенного за пределами данного класса. При
определении члена тетасса модификатор указывать не обязательно, по умолчанию его
отсутствие означает, что ЭТОТ член класса имеет модификатор private. Члены класса,
объявленные как prlvate, могут использоваться только Другими членами этого
класса В программах Данной главы все члены класса определены как имеющие тип
„Чосгугта рврыс. Это означает, что они могут быть использованы любым ‚другим
кодом, даже определенным за пределами класса. Подробнее мы рассмотрим моди-
фикаторы в следующих главах.
Не существует строгого синтаксического правила, ограничивающего сферу Действий,
выполняемых классом, или разнообразие сохраняемых данных, но в хорошо напи-
санной программе класс определяет одну и только одну логическую сущность.
Например, класс, в котором хранятся имена пользователей и их телефонные номера,
как правило, не должен хранить данные фондовой биржи, информацию о среднеме-
сячных температурах в Антарктиде, расписание авиарейсов либо другую логически
пс связанную с телефонным справочником ннформатцию.
Jo сих пор используемые нами классы имели только один метод — Main (). B этой
главе мы рассмотрим, как создавать другие методы. Необходимо отмеТИТЬ, ЧТО
определение метода Ma1r1() не является неотъемлемой частью синтаксиса класса,
Этот метод необходим только в классе, с которого должна начинаться программа.
Определение класса
Любую тему легче всего изучать, используя практические примеры. Мы разработаем
класс. который инкапсулирует информацито о транспортных средствах, таких как
легковые автомобили, фургоны и грузовики, и назовем его Vehicle. Этот класс будет
хранить дан ные о транспортных средствах — количество пассажиров, которое спо-
собен перевозить автомобиль. объем топливного бака и расстояние (в милях), которое
Этот автомобиль может проехать. используя один галлон топлива.
Первая версия класса Vehlcle, B котором определены три переменные экземпляра
Раззевоегз, fuelcap И mpg, представлена ниже. Обратите внимание. что класс
“.’ел1с1е не содержит никаких методов, то есть Эта версия класса содержит только
данные. (Hence B следующих версиях будут добавляться и методы.)
‚...„.. щитам-чьим!‘
. „д .‚.›„ ..„
дин-днем-
133
ОСНОБНЫЭ ПОН ЯТИЯ КПЗССЗ.
Class Vehicle {
public int passengers; // КОЛИЧЕСТВО пассажиров_
publ;c int fuelcap; // ЕМКОСТЬ топливного бака (B галлонах)
р„Ь11с int mpg; // Расстояние (в милях), которое
// автомобиль может проехать, используя один
// галлон топлива.
|
ПрИНСДСННОС ВЫШЕ ОПРСДЁЛСНИС Переменных экземпляра можно рассматривать как
общий способ объявления персмснных. Синтаксис объявления переменных экземн-
‚тяра таков:
access type var—name;
Здесь ПОДСТЗНОВОЧНОВ СЛОВО access —-3T0 MOflH¢MKaTOp!CflOuO type —-тип перс-
ненной, а словосочетание маг-Паше — имя переменной. Таким образом, синтаксис
объявлент-хя переменной экземпляра аналогичен синтаксису объявления „локальной
переменной, за исключением гиодитрикатора. В классе вен 1633 объявление перемен-
HbI.\ ННЧИНЦСТСЯ С MOL1Ir1(I)VTKZlT0[)€1 public. ЭТО ПОЗВОЛЯСТ ПСРСМСННЫМ бЫТЬ ДОСТУПНЫ-
МИ IIJUI КОДЫ, ОПРСДСЛСННОГО BHC ДНННОГО КЛНССЗ.
С помощью ключевого слова class создастся новый тип данных. В нашем случае
новьтй тип данных называется Vehicle. Это имя используется для объявления
объектов типа Vehicle. Помните, что объявление класса с подношью кдюьтеного слова
:Lass даст только описание типа, но физический объект при этом не создается.
Поэтому выполнение приведенного выше кода Не приведет к возникновению объек-
тов типа Vehicle.
Пл я того чтобы создать объект типа уешсте, необходимо использовать оператор new.
o котором мы расскажем далее в этой главе. Например,
Vehicle minivan = new Veh1cle(); // Создание объекта Tuna vehicle,
// КОТОРЫЙ Называется minivan.
После выполнения этого оператора будет создан экземпляр класса Vehicle —~ объект
m;nivan.
При создании экземпляра класса создается объект, который содержит собственную
копию каждой переменной экземпляра, определенной классом. Следовательно, ка-
ждый объект класса Vehicle будет содержать свои собственные копии переменных
экземпляра passengers, fuelcap и mpg. Для доступа к этим переменным использу-
cm: оператор точка (.). Этот оператор связывает имя объекта с нменем члена класса.
Приведсът общий синтаксис данного оператора:
гЬ]есс.тетЬег
Как видите, имя объекта указывается слева от точки, а имя члена класса — справа.
Например, для присваивания переменной fuelcap объекта „диоды значения 16
испогтьзуется следующий оператор:
T-nivan.fuelcap = 16;
Оператор точка (.) может использоваться для получения доступа как к переменным
жзезтпляра, так и к гутетодам.
Ниже представлен весь код программы, в которой используется класс Vehicle.
B программе демонстрируется использование класса veh1C1e_
Назовите этот файл UseVehicle.cs
вч-г Глава 4. Классы, объекты и методы
*/
Ц51п9 SysLem;
class Vehicle (
public int passengers; // Количество пассажиров.
public int fuelcap; // Емкость топливного бака (в галлонах).
public int mpg; // Расстояние (в милях), которое данный автомобиль
// может проехать, используя один галлон топлива.}
” В этом классе создается объект класса Vehlcle.
1 I
z
class VehicleDemo 1
public sta:1c void Ma1n() Т
yCh;Cle mlnlvan _ new vCnlc;e(); ‹__„__„__3“1ЙР0“1ШХШС9ШШ°Г3КЮМ”дЩ7ЮШС%
\/еп1с1е, которым называется m1n1van.
lnt range;
// Переменным объекта minivan присваиваются значения.
minivan.passenqers — 7
miniv n ;Uel a 1,_ Обраппсвниманиснаисшппвшшниеопешпораточка()
_ ‚ а ' С р к О’ IIJl>l1l0CTyllaK‘iJICHy класса.
m1n1van.mpg = 21;
// Вычисление максимального расстояния, которое может проехать
// данный автомобиль, имея полный топливный бак.
range = m1n1van.fuelcap * minivan.mpg;
Сопзо1е.Иг1сеЬ1пе("Миниавтобус может перевезти " +
minivan.passengers r " пассажиров на расстояние " +
range + " MMMb.");
}
B этой программе содержатся два класса \/’еп1с1е и \/еп1с1еЭегпо. Внутри класса
vemcleoemo Н методе Main () создается экземпляр класса Vehicle. ттазыпаемый
т: nivan. Затем код, определенный в методе Mam (), получает доступ к переменным
экземпляра (объекта min lvan), присваивая им значения и используя эти значения в
вычислениях. Важно понимать, что Классы Velucle И Vehi<,leDemo являются
различттыми, самостоятельными классами. Единственная связь между ними состоит
в том. что в одном классе создается экземпляр другого класса. Хотя эти классы
являются самостоятельными, код внутри класса VehicleDemo может иметь доступ к
членам класса Vehicle, поскольку переменные класса Vehicle объявлены как
public. Без указания этого модификатора доступ K переменным passengers,
fuelcap И mpg мог бы осуществляться только кодом, определенным в пределах класса
Vehicle, а класс VehicleDemo не имел бы возможности их использовать.
Рассматриваемая программа была помещена в файл Usevehicle . cs, поэтому при ее
компилировании будет создан файл Usevehicle .ехе. Оба класса, Vehicle И Vehi-
emo. автоматически станут частью исполняемого файла. Результат выполнения
этой программы будет следующим:
Миниавтобус может перевезти 7 пассажиров на расстояние 336 миль.
Классы Vehicle И VehicleDemo НС обязательно должны находиться в одном и том
же исходном файле. Можно поместить каждый mace B отдельный файл, назвав их,
например. '\/eh1cle.cs и уешстеоето . Cs. B этом случае нужно задать в командной
строке команду
csc Veh;cle.cs VehicleDemo.cs
„др.
Основные понятия класса 1 35
при вы пол нен ин которой оба файла (оба класса, содержантиеся в них) будут не только
„(онпнлированьк, но и объединены.
при работе в интегрированной среде \/i.suz1| C+TH)li IlC()6.\(),’1H\[0 Добавить к про-
грдхнте оба эти фаила, а штем их построить.
прежде чем перейти к изучению еледутоцтего \12ncpmL'|a, СШС раз вернсхтея к основ-
“(my принципу создания объектов —— каждый объект имеет собственные копии
переменных экземпляра, определенных их классом. Следовательно. значения нере-
хтснньтх одного объекта могут отличаться от значений переменных другого объекта.
Нанричер. если существуют два объекта Vehicle, то каждый из них имеет свои
собственные КОПИИ Переменных passengers. ГЦ :ар и rnpq, ‘значения которых МОГУТ
p:1;.n1-I2rrbc>L Следующая нрограхаз-ха дсухонстрирует‘ использование дтого принципа.
3 этой ьрограмме создаются два объекта класса Veh-g-c.
Ls:ng System;
class Vehicle {
publlc int passengers; // Количество нассажиров.
рцЬ1:с int fuelcap; // ЕМКОСТЬ эонливного бака (в галлонах).
public int mpg; // Расстояние (в милях), которое данный автомобиль
// может проехать, используя один галлон топлива.
В этом классе создаются два объекта класса Veh;C;u.
55 Twovehicles Ё
рао;1с static void Main() {
Vehlcle minlvan — new Vehicle();
Vehlcle sportscar 2 new Veh1cle();
int rangel, ranqe2; Помннтц'по|юремснныспцп1чапи
ьрохксаг ссылаются на различные объекты.
// Переменным объекта minivan присваиваются значения.
min1van.passengers L 7;
min1van.fuelCap : 16;
minivan.mpg ~ 21;
// Переменньш объекта sportscar присваиваются значения.
spor:scar.passengers 2 2;
sportscar.fuelcap — 14;
sportscar.mpg — 12;
// Вычисление максимального расстояния, которое может проехать
// каждый из автомобилей, имея полный топливный бак.
rangel — minivan.fuelcap * minivan.mpg;
range2 = sportscar.fuelcap * sportscar.mpg;
Console.WriteLine("MMKpoa5TobyC может перевезти " +
minivan.passengers +
rangel + " МИЛЬ.");
пассажиров на РЗССТОЯНИЕ
Соп5о1е.Иг1сеЬ1пе("Спортивный автомобиль может перевезти " +
sportscar.passengers т " пассажира на расстояние \n" т
range2 + " МИЛЬ.");
‚ _ I лава 4. Классы, объекты и методы ,
Н И ЖС пр И ВСДС Н резул ЬТЕХТ ВЫ ПОЛ НС НИ Я ЭТО П рограм МЫ.
Миниавтобус может перевезти 7 пассажиров на расстояние 336 миль. 5
Спортивный автомобиль может перевезти 2 пассажиров на расстояние 168 миль.
Данные объекта minivan полностью независимы от данных, содержащихся в объекте ‚
soortcar. На рис. 4.! представлена структура этих объектов.
_ passengers 7
minivan > fuelcap 16
"ЮЗ 21
passengers 2
sportscar fuelcap 14
.”.‘P9 ..,1.2...
Рис. 4.1. Переменные экземпляра одного объекта независимы
от переменных экземпляра другого объекта
Минутный игра/газики»:
Из каких компонентов состоит класс? '~
I
L 2. Какой оператор используется для поутучения доступа к членам класса?
3 Собственные копии чего имеет каждый объект? д‘
(ак создаются объекты
В предыдущей программе для объявления объекта типа Vehicle была использована
следующая строка кода:
Vehicle minivan ‹ new Vehicle();
B нсй выполняются два действия. Во—первых, объявляется переменная типа Vehicle,
называемая minivan. Эта переменная нс определяет объект (то есть не является
непосредственно объектом), она только ссылается на объект. Во-вторых, при выпол-
нении оператора new создается физический объект, а переменной minivan присваи-
вается ссылка на этой объект. Следовательно, после выполнения данной строки кода
Переменная minivan будет ССыЛаТЬСЯ на объект типа Vehicle.
Оператор new ()Lma_Muw€cIcu (BO время работы программы) выделяет память для объекта
и возвращает ссылку на Эту область памяти. То есть ссылка является адресом памяти,
выделенной объекту оператором new. B дальнейшем эта ссылка хранится в перемен-
ной. Следовательно, всем объектам в С# память должна выделяться динамически.
Два шага, совмещенные в предыдущем операторе (строке кода), для большей нагляд-
ности могут быть переписаны в двух операторах:
r.m.-,-.m...».._._., 7_¢..-.—'»'-,~'»e#.g-pee:-re _ ‘
Vehicle minivan; // Объявление ссылки на объект.
minivan — new Vehicle()i // Выделение памяти для объекта Vehicle и возвращение
// ссылки на этот объект переменной minivan.
I. KJIBCC СОСТОИТ ИЗ кода И ‚ЦЕННЫХ.
_._,;. q_..,_- меня "-.-n ‚
1. Для доступа к членам тстасса используется оператор точка (.).
3. Кахщый объект имеет собственные копии переменных экземпляра.
переменные ссылочного типа и оператор присваивания ш
В первой строке кода переменная minivan объявляется как ссылка на объект типа
Vehicle. То есть переменная mmivan MO)KCT ссылаться на объект, но не является
самим объектом На этом этапе Данная Переменная хранит зт-тачел-тие nul 1, O3H’<l‘IaIO-
Щсс, что связь между ней и объектом отсутствует. В следующей строке кода создается
„оный объект класса мер 1с1е, а переменной mmivan присваивается ссылка на этот
Объект, теперь переменная minivan CIl$l3€1HZl С объектом.
поскольку доступ к объектам ктасса осуществляется е помощью ссылки, классы
иначс называют ссылочнььии типами. Ключевое отличие между обычными и ссылоч-
Ны-пи типами состоит в значениях, хранимых переменными каждого типа Перемен-
пая обычного типа сама хранит значение. Напртнмер. после выполнения операторов
Ln’. Х
,_.
I
;~:— Or‘
переменная х хранит значение ХО, поскольку х является переменной типа int, то
есть переменной обычного типа. Но после выполнения оператора
Vehicle minlvan 2 new Vehicle();
переменная minivan самостоятельно не хранит объект, а хранит только ссылку на
ПСГО.
Переменные ссылочного типа и оператор
присваивания
При выполнении операции присваивания переменные ссылочного типа действуют
иначе, чем переменные обычного типа (например, типа int). Когда значение одной
переменной обычного типа вы присваиваете другой переменной обычного типа, то
ситуация проста. Переменная, находящаяся слева от оператора присваивания, полу-
чает копию значения переменной/т, указанной справа. Когда же значение одной
переменной ссылочного типа вы присваиваете другой переменной, ситуация гораздо
сложнее, поскольку вы присваиваете переменной ссылку на другой объект. Напри-
мер, рассмотрим следующий фрагмент кода:
Vcnlcle carl 2 new Veh1cle();
Vehlcle car2 — Carl;
Может показаться, что переменные Carl И (:ar2 относятся к различным объектам,
но это не так. На самом деле обе переменные carl и car2 CCHIIZUOTC51 Ha один и тот
же объект, При присваивании переменной car2 значения переменной Carl, ncpc—
менной car2 присваивается есылка на тот же объект, на который ссылается пере-
мснная Carl. Следовательно, доступ к объекту можно осуществлять как с помощью
переменной Carl, так и с помощью переменной/т car2. Например, после выполнения
операции присваивания
;ar1.mpg — 26;
Оба оператора Wri teLir1e () выведут одинаковое значение А 26.
Console.WriteLine(car1.mpg);
Console.WriteLine(car2.mpg);
Переменные Carl И car2 He связаны каким-либо способом, хотя и ссылаются на
один объект. Например, после выполнения следующего фрагмента кода переменная
r:ar2 получает ссылку на объект, на который ссылается переменная (:ar3.
| лава 4, Кпассы, объекты и методы
Vehicle Carl — new Veh1cle();
Vehicle car2 — Carl;
Vehicle Car3 ъ new Veh1cle();
35:2 „ car}; // Теперь переменные car2 И car3 ссылаются
// на один и тот же объект.
Объект, на который есгллаетея переменная Carl, остается без изменении.
Е ‚ёйияутягэхи трактату/и
Ф‘ Р" I. Что происходит, когда значение переменной ссылочного типа присваивает
ея другой переменной ссылочного типа‘?
Гх)
Предположим. что создан класс с именем МуС1а55. Покажите, как создать
объект С именем ob.
I Чем отличаются переменные ссылочного типа от ггсрсменных обычного
т и п а?
Четоды
Как уже говорилось, переменные экземпляра и методы являются двумя основными
составляющими классов. До этого момента в нашей программе класс vehmle
содержал только данные и не содержал методов. Хотя классы, содержанию только
данные, являются действительными, большинство классов содержит н методы,
Методы — это подпрограммы, которые управляют данными, определенными в
классе, и во многих случаях обеспечивают доступ к данным. Обычно остальные части
Програмгмы взанмодсйствугот е гсгассом при помощи его методов.
Метод содержит один и более операторов. В профессионально написанном СЁЁ-коде
каждый метод выполняет только одну задачу. В качестве имени метода может
использоваться любой действительный идентификатор. Ключевые слова не могут
быть именами методов, имя Main() является зарезервированным для метода, е
которого Начинается выполнение програмгх-хы.
B TCKCTC программы МСТОЛЫ ОбОЗННЧНЮТСЯ CIICII)/}OI!,l|«l.'\l образом: IIOCJIC ИМСНИ MCIOIIEI
следует пара круглых скобок, Например, если имя метода дейтаг, то при его вызове
п Программе он будет написан как уеётах (). Такая форма глапнсаггия примепясгеъг
4.15! ТОГО. ЧТОбЫ В ПрОГрНЗНМИХ МОЖНО ОЫЛО [)Н3ЛИЧ21'1`Ь ИМСНН ПСРСПСННЬЕХ Н МСТО[[()Н.
Общин синтаксис метода выглядит‘ так:
access ret—type name(paramcCer1list) {
// ТЕЛО метода
}
Здесь подстановочное слово access v это модификатор, который указывает, какие
ЧНСТИ ВЭШСЙ программы МОГУТ ИМСТЬ ДОСТУП К МСТОДУ. KKK УЖО говорилось, модг-хфи-
КНТОр ПС ЯВЛЯСТСЯ ООЯЗЕПСЛЬПЫМ, ССЛИ OH ОТСУТСТВУСТ, ‘VICTOR ДОСТуПСН ТОЛЬКО B
I. l<OI‘H.£l ЗННЧСНИС ПСРСМСННОЙ ССЫЛОЧНОГО ТИПЫ присваивается ДрУГОЙ ПСРСМСПНОЙ ССЫЛОЧНОГО
ТИПЫ, ООС ПСрСМСПНЫС буЦУГ ССЫЛНТЬСЯ на ОДИН Н ‘ГОТ ЖС ОбЪСКТ. При ЭТОМ KOHHPOBEIHHH
ООЪСКТН ПС ПРОИСХОДИТ.
I.)
NyClass ob new MyCldsS();
3. Переменная обычного типа непосредственно хранит присвоенное ей значение, переменная
ССЫЛОЧНОГО ТИПЭ ХРЭНПТ ТОЛЬКО ССЫЛ Ку на ОбЪСКТ.
Методы | J2!
прСЛСЛНХ класса, в котором он обьятълен. Пока мы будем обтмвлзтть все методы как
„д; 1_п:. ПОЭТОМУ ОНИ МОГУТ бЫТЬ НЬКЗПЦПЬ! КОЦОИ H5 ДПОООГО .\ICCT‘rl Il])OI‘[)(l.\!\1hI.
E“_l0B()CO'IL‘T'(lH MC fetptl ype Н СННТНКСИСС УКНЗЬПШСТ` ТИП данных, I103BpElLIJ.E\C:\1bl,\' МСТО-
ддж Эти могут быть как данные любого действительного типа, так и объекты любого,
созданного вами класса. Если метоД не возвращает никакого значения, он Должен
быть указан как т-тх-тетоший тип void. Вместо слова name указывается имя метода.
Это может быть любой Действительный идентификатор. не повторятотдигт имена,
Ко-горьтмьт были названы Другие члены ютасеа в той же область: видимости. После
„цепи метода следует словосочетание ра rameter—list (СПИСОК параметров) — по-
сдтецтовательность разделенных запятыми пар «тип-идентпфпкатор». То есть е помо-
пп-то парамегротт методу передатотея из программы значения, гпп которых обя5:1'т‘е;тт‚—
„о должен быть указан. Парамстрамтс в оеттопноч являются перемстнтые. которые
npnHH.\I'cll0T Значения иргулшнптпв, передаваемых методу при ето вызове. Если метод
не имеет параметров, то список параметров будет пустым.
Добавление метода K классу Vehicle
Мы уже говорили ранее, что методы класса обычно работают C Данными и обеспе-
чпвптот доступ к данным класса. Теперь вспомним, что в методе Main () из преды-
дуптето примера для вычисления расстояния. которое может проехать автомобиль с
полным топливным баком, Значение расстояния, которое автомобиль может про-
ехать, используя один галлон топлива. умножатлоеь на значение емкости топливного
бака. Хотя технически мы все вьптолттилтт правильно, этот способ не является
отттимальным при выполнении такого вычисления. Макситиазтьную датьность поезд-
кн лучше всего вычислять в самом классе Vehicle. поскольку максимальное рас-
стояние, которое может проехать автомобиль, зависит от емкости его топливного
бака И расхоДа топлива, а обе эти величины инкапсулированы в классе Vehicle.
Кроме того, при Добавлении к классу Vehicle MC'I‘0LlZ1, в котором вычисляется
дальность поездки, улучшается объектно-ориентированная структура класса.
Для того чтобы Добавить метод к классу Vehicle, его нужно определить при создан ин
класса. Например, слслутотпая версия класса Vehicle содержит метод range () ,
I5l;II10.'I$II,LlL1ii £1'c1HHbIL‘ о расстоянии, пройДенном автомобилем:
/Ё В Этой программе В классе Vehicle определен метод range.
using System;
class Veh;c}_e {
public int passengers; // Количество пассажиров.
public int fuelcap; // ЕМКОСТЬ топливного бака (а галлонам).
public int mpg; // Расстояние (Ь милях), которое данный
// автомобиль может проехать, используя один
// галлон топлива.
// Определение метода range.
public Void range() { NkTm1range()upMHwwmxuTKmwcyveh1c1a
COnsole.WriteLine("3ToT автомобиль может проехать с полным "+
" топливным баком "+ fuelcap * mpg + " миль.");
_ Й |
Обратите внимание, что переменные [ие1сар и mpg используются 5
непосрсдсгнсттно без использования оператора точка (.). ]
glass AddMeth {
. -....... ч. плассы, ооъекты и метод
public statlc void Ма1п() {
Vehicle minivan — new Vehicle();
Vehlcle sportscar - new Vehicie();
// Значения присваиваются переменным объекта minivan.
minivan.passengers > 7;
minivan.fuelcap — 16;
m1n1van.mpg — 21;
// Значения присваиваются переменным объекта sportscar.
sportscar.passengers — 2:
sportscar.fuelcap L 14;
sportscar.mpg = 12;
Соп5о1е.Иг1пе("Микроавтобус может перевозить " + minivan.passengers +
" нассажиров.\п");
ш1п1мап.гап9е(); // Вывод на экран значения максимального расстояния,
// которое может проехать микроавтобус.
Соп5о1е.Иг1Ее("Спортивный автомобиль может перевозить " 4
sportscar.passengers + " пассажиров.\п");
SpOrtSCar.range(); // ВЫВОД на экран значения максимального расстояния,
// которое может проехать спортивный автомобиль.
f
Результат работы этой программы будет следутотпътм:
Микроавтобус может перевозить 7 пассажиров.
Этот автомобиль может проехать с полным топливным баком 336 миль.
Спортивный автомобиль может перевозить 2 пассажиров.
Этот автомобиль может проехать с полным топливным баком 168 миль.
Теперь рассмотрим все ключевые элементы программы. Начнем с метода range ().
BOT начало объявления метода range () 2
public VO1d range() {
B этой строке кода объявляется метод с именем range, He zmeK)u_u1 й параметров. Он
определен как public. что позволяет вызывать его в любой другой части программы.
"же ~ ТИП нозвраъпаемого им значения. то есть метод 1fange() не 1103нра111ас1‘ п
Программу никакого значения. Строка заканчивается открывающей фигурной скоб-
кой, за которой определяются операторы метода.
Тело гиетода range () содержит единственную строку
Console_WriteLine("3ToT автомобиль может проехать с полным "+
" топливным баком "+ fuelcap * mpg * “ миль.")'
I
Этот‘ оператор вьпзодттт значение максимальной дальности поездки, вычисляя его
Путем умножения значений переменных fuelcap И mpg. Поскольку каждый объект
типа Velucle имеет собственные копии переменных fuelcap И mpg, при вькзове
метода range () для вьтчисленьтя дальности поездки используются копии переменных
нызы ваюшего объекта.
Метод range () заканчивается закрывающей фигурной скобкой. При этом управле-
ние передав-тел в точку программы, из которой метод был вызван.
__ ‚ ‚ ...,....-._-_-...... .. „
..„д-;„ -9. .-,-p.n»-:-new «м.
-‚‚.—............щ4‚-.‚„. .. .. _
.. ..„е. ;›а‹ ъ. щ ‹
Методы 1 41
Обратите внимание на оператор
шдп1мап.гап9е():
Внутри метода Main () . Этот оператор вызывает метод range ( ) для объекта minivan,
„осле чего управление передастся этому методу. То есть вьпзьпвается метод range () ‚
Который будет работать с переменными объекта minivan. Для этого указывается имя
Объекта, за которым следует оператор точка (.). Когда метод выполнит свою работу,
Vnp2\B.'lCHl/IC будет передано в точку программы. из которой метод был вызван.
Ёьптолисение программы продолжается со строки кода, следующей за вызовом метода.
B этом случае вызов метода minivan. range () выводит инфоргиацию о максималь-
How расстоянии, которое может проехать автомобиль minivan, а вызов метода
5portscar.range () выводит инсрортиацию о максимальном расстоянии, которое
может проехать автомобиль sportscar. Ka>K,z1bn'-'1 раз при вызове метод ‘:ange()
работает с переменными объекта, указанного слева от оператора точка (.).
Обратите внимание на очень важную особенность определения метода range () :
переменные экземпляра fuelcap И mpg указываются непосредственно (для иденти-
фикации переменной не используется оператор точка (.) и не указывается имя
объекта). Когда метод работает с переменной экземпляра, которая определена в том
же кстассе, что и сам метод, он обращается к переменной без явного указания объекта
и без использования оператора точка (то есть в пределах метода нет необходимости
указывать имя объекта второй раз). Это означает, что используемые переменные
fuelcap И mpg неявно относятся к копиям переменных, которые определены в
объекте, вызывающем метод range ().
Bo3BpaT управления из метода
Управление возвращается из метода вдвух случаях. Это происходит, изо-первых, когда
встречается закрывающая фигурная скобка этого метода, во-вторых, когда выполня-
ется оператор return. Существует две формы оператора return, одна форма
используется в методах, имеющих тип void (которые не возвращают значения), а
вторая — в методах, возвращающих значения.
В методах, имеющих тип void, ВЫПОЛНСНИС оператора return приводит к немедлен-
ному завершению работы метода. Поэтому обычно этот оператор указывается после
условного вьпражения, поскольку при определенных условиях выполнение оставших-
CH операторов метода может не иметь смысла. Оператор return записывается
следующим образом:
return;
Когда он выполняется. управление передается в точку программы, откуда метод был
вызван, при этом весь оставшийся в методе код пропускается. В качестве примера
рассмотрим следующий метод:
public void myMeth() {
int i;
for(i=O; i<10; i++) {
if(i == 5) return; // Метод прекращает свою работу, когда переменная i
// принимает значение 5.
Console.WriteLine();
142 Глава 4. Классы, объекты и методь
Здесь цикл Бог выполнится только 5 раз, потому что, когда Значение Переменной i
CTHHCT равным 5, буттст выполнен оператор гееагп.
В рамках одного метода допускается начичне более одного опера гора 1"t3IZ‘L1t’:".. Чаше
всего такие операторы Hcn();lb3y101csI в тех методах, которые ПМСЮ г несколько
условпьтх вьтражетттпн. Например.
дс \/0+0‘ :uyMe;h-’,)
//
1Е(соп‹31С1оп_1) xetuxrn;
ll
д.
if (condition_2) return;
statements;
i
BO3BpHT УПРЦВЛСНИЯ И} ЭТОГО МСТОДН ОСУТНССТТЪТБТСТТЁБ! Н ТрСХ СЛУЧАЯХ: ПОСЛС ВЫПОЛПСПИЯ
'_i3:lKJl0‘lVlTCflbHMX ОПСрНТОрОВ МСТОДН, ПОСЛС ВЫПОЛНСНИЯ первого УСЛОВНОГО выраже-
ПИЯ И ПОСЛС НЫПОЛНСПИЯ ВТОРОГО УСЛОВНОГО выражения. HO CC.-H-1 .\1CTO;'.l HMCCT
CJHIUKOM МНОГО ТОЧЕК ВЫХОДП. ЭТО ДССТруКТурИруСТ KO,-'1. ПОЭТОМУ СЛСЦУСТ ИЗОСГЦТЬ
ЧРСЗПСРНОГО ИСПОЛЬЗОВННИЯ ОПСрЦТОрОВ return.
Возвращение методом значений
ХОТЯ МСТОДЫ, РПТЁТОЩИС ТИП void, BCTpC‘ldl'OTC5l ЛОСТЭТОЧНО ЧИСТО, ООЛЬЪЫИПСТВО
МСГОДОВ В C# BO3Bp'c1Ul3lOT 3H'c1‘lCHl/15!. ФЭКТИЧССКИ СПОСОбНОСТЬ НОЗВРЕПЦНТЬ ЗНЭЧСНИС
ЯВЛЯСТСЯ ОДНИМ ИЗ наиболее ВПЖНЫХ СВОЙСТВ МЁТОЦЗ. ВЫ УЖС встречали ПрИМЁП
ВОЗВРЁПЦСНИЯ МСТОЦОЁИ ЗННЧСЪТИЯ, КОГДЭ МЫ HCI10flb3()B:l,-“I И (ЬУТ-ХКПИЮ Hiath . Sqtft () ДЛЯ
ПОЛУЧСНИЯ ЗНЗЧСНИЯ КВЗЛРЗТНОГО КОРНЯ ЧИСЛЕ.
возвращению значении в программировалтип используется для различных Целен.
В одних случаях, например, при использовании функции I‘-I.:_1th.Sv:1rt (), возвращае-
мое значение является результатом некоторого вьтчттсттеттиът. В других случаях возвра-
щаемое значение может указывать на успех или нсудачту операции (возвратнается
булево значение). Иногда возвращаемое значение может содержать код состояния
(возвратнается заранее опрсделенттое число ~ -I. О и так далее). Использование
возвращаемых методом значений * неотъемлемая часть программированизт.
Мс голы возвращают‘ значение вызъптатотисй подпрограмме, используя сгтсдутьэтиуто
цторму оттератора re „а
тел:
гсьцкп value;
3.'1CCb подстановочное СЛОВО value —- BO3B])£1LIl'<1CM()C значение.
СПОСООНОСТЬ МСТОДОВ ВОЗНрЗХЦНТЬ ЗННЧСНИЯ МОЖНО ИСПОЛ ЬЗОНЕГГЬ ДЛЯ УСОВСрЬНСНСТВО-
[ЩНИЯ УЖЕ ЗНЗКОМОГО НЭМ МСТОПЦ ‘CEHCJE . ВМЕСТО ВЫВОДП HH(l)OpM'cHlHH О МЗКСИМНЛЬ-
ПОП рЦССТОЯНПП. КОТОрОС ИОЖС’! ПрОСХНТЬ ЦЕННЫЙ ЭВТОМООИЛЬ. НОВЫЙ вариант ЦСТОДИ
' i:'iL}€: ) 6)/flC'l' ВЫЧИСЛЯТЬ ЗПЗЧСНПС ЧЕХКСПМЦЛЬНОГО РЦССТОЯНИЯ П НОЗВРНЦХНТЬ CFO
нрограмие. ОДНИМ ИЗ ПрСПЧуЦЦЁСТВ ТНКОГО ПОЛЛОЦЕ! ЯНЛЯСТСЯ HO3,\10)KHOCTb ИСПОЛЬЗО-
ВННПЯ ВОЗНРНТЦНСЧОГО ЗНЗЧСНИЯ ДЛЯ других ВЫЧИСДТСРТИЁТ. B C.'lC.;1)’lOII_lC.\1 вариатгге
ПРОГрЕПППзТ МОЛТЦЬИЦНрОВНННЫЙ МСТОД rar1ge() HC ВЫВОДИТ на ЭКрНН СООТВСТСТВУТО“
ШУЁО ннформацито О ДНПНОМ ЗНТОМООИЛС, а возвращает ВЫЧИСДТСПНОС ЗННЧСНИС.
//I B программе ДЕМОНСТРИРУЁТСЯ ИСЦОЛЬЗОВЗНИЭ МЭТОДЗ,
// возвращдющего ВЬХЧИСЛЭННОЭ ЗНЗЧЭНИЭ.
Методы __14ЁЁ
using System;
;;ass Vehicle {
public int passengers; // Количество пассажиров.
public int fuelcap; // EMKOCTb топливного бака (э галлонах).
public int mpg; // Расстояние (в милях), которое данный
// автомобиль может проехать, используя один
// галлон топлива.
xx Определение метода, возврацаюшсго значение типа 1п:.
public in: ranget) \
return mpg * Еце1сарн ‹—————4ЁЕрсш.мспл1гап9е()нощюашаспзншшниш
'-aSs RetMeth {
public static void Main() {
Vehicle minivan = new VehiCle();
Vehicle sportscar е new vehicle();
int rangel, range2;
// Значения присваиваются переменным объекта minivan.
min1van.passengers = 7;
ivan.fuelcap = 16;
minivan.mpg = 21;
mi
// Значения присваиваются переменным объекта sportscar.
sportscar.passengers : 2;
sportsCar.fuelCap = 14;
sportsCar.mpg ~ 12;
// ВЫЧИСЛЭНИЕ МаКСИМаЛЬНОГО РЕАССТОЯНИЯ, которое МОЖЕТ проехать
// данньш автомобиль.
ran Cl mi iv r Ч Переменным range-1 M mnqel присваиваются
4 Ь т . — -4—-———-
9 n an a“9“ )' шцнтпшшмысзншюннж
range2 = sportscar.range();
Соп5о1е.Иг1сеЬ1пе("Микроавтобус может перевезти " + minivan.passengers +
" пассажиров на расстояние " + rangel + " МИЛЬ.");
Соп5о1е.Иг1СеЬ1пе("Спортивный автомобиль может перевезти " +
sportscar.passengers + " пассажиров на расстояние " 4 range2 + " миль.");
Результат выполнения этой программы следующий:
Ёикроавтобус может перевезти 7 пассажиров на расстояние 336 миль.
Спортивный автомобиль может перевезти 2 пассажиров на расстояние 168 миль.
В этой программе вызов метода range () помещен Справа от оператора присваивания.
СЛСВЭ ОТ НСГО ННХОДИТСЯ HCDCMCHHZUI, КОТОРОЙ бУДСТ ПрИСВОСНО ЗННЧСНИС, u03upau1zxc—
“ОС МСТОДОМ range () . CJIC[1OB'<1T€flbHO, ПОСЛС ВЫПОЛНСННЯ СТрОКИ KOI13
fangel = m1nivam.range();
ПСрСМСННОЙ rangel ПРИСВЗИВЗСТСЯ 3ПЫЧСНИС,ПЛЯ ВЫЧИСЛСНИЯ КОТОрОГО ИСПОЛЬЗОНЫ-
ЛИСЬ переменные объекта min; van.
1 44 Глава 4. Классы, объекты и методы‘
3..
д
Теперь МСТОП range () возвращает ЗПаЧСНИС ТИПЭ int. ОЧСНЬ ВЫЖНО ПрЗВИЛЬНО УКЭЗНТЬ If
ТИП [ЗОЗНРЦЩИСМОГО МСТОДОМ ЗНЭЧСНИЯ (ССЛИ НОЗНИКНСТ НСОбХОДИМОСТЬ НОЗНРЭТа
МСТОПОМ ПНННЫХ ТИП?) double, ПрИ ОбЪЯНЗТСНИИ МСТОДЗ СЛСПУСТ УКИЗЗТЬ ИМСННО ЭТОТ
ТИП HO3Bp'c1UlE‘lC-VIOFO ЗННЧСНИЯ).
Хотя синтаксис гпредстатзлснътойё выше программы не содержит ошибок. она написана
недостаточно Эёрцтектитъно. В частности, отсутствует необходимость в переменных
rangel И rar1ge2, поскольку метод range () может быть вызван непосредственно в
операторе Wrj_teLine () ;, как это показано Ниже.
Соп$о1е.Иг1СеЬ1пе("Микроавтобус может перевезти " + min;van.passengers т "
пассажиров на расстояние " т minivan.range() т " миль."):
В этом случае при выполнении оператора WriteLine () ,- автоматически вызывается I
метод minivan.range(), а его значение передастся методу WriteL1ne (). Кроме ‘
ТОГО, ВЫ МОЖСТЁ ВЫЗВЗТЬ МСТОД range В ЛПОбОМ MCCTC программы, Где НСОбХОДИМО
ИСПОЛЬЗОВЦНИС ЗНЭЧСНИЯ максималы-того рЭССТОЯНИЯ ДЭННОГО объекта КЛНССЭ Vehicle,
Например, в еледутощей строке кода производится сравнение значений, возвращае- ‘
МЫХ МЕТОДОМ range .
if(v1.range() > v2.range())
Соп5о1е.Иг1сеЬ1пе("Автомобиль Vl c полным топливным баком может проехать"+
"\п большее расстояние, чем автомобиль v2.");
ь „лишим
- «ответы ‚гглдфдесгюлале-мв
Вопрос. Я слышал, что компилятор C# может обнаружить код, который не
будет выполнен ни при каких условиях. Что это означает?
Ответ. Это действительно так, если компилятор обнаружит в созданном вами
методе код, который не может быть выполнен, он выдаст предупреждающее сообще-
ние. Рассмотрим пример.
public void m() (
char a, b;
// ...
if( ==b) {
Соп5о1е.Иг1СеЬ1пе("Переменные а и b равны.");
return;
) else {
Сопзо1е.Иг1сеЬ1пе("Переменные а и b равны.");
return;
)
Console.WriteLine("3Ta строка кода никогда не будет выполнена.");
I
I
Здесь возврат управления из метода т() всегда будет происходить до выполнения
последнего оператора WriteLine() ,-. Если вы попытаетесь скомпилировать этот
метод, будет выдано сообщение, что в программе обнаружен код, который никогда
не будет выполнен. Появление в программе невыполнимого кода —- это Результат
ошибки программиста, поэтому к таким сообщениям всегда следует относиться
серьезно.
аи и v- -‘»--.»x.x-.-, "L“"'\-ac:-Vrr-vx." a\ur..::v‘.' -.2-»« x'v¢- ....>-.»-my-«.._~....; „э. ...... ,.-...». мы»
методы 1 45
Z/_____ _ ,_
использование параметров
при вызове метода ему можно передавать одно или несколько значений. Как уже
говорилось, передаваемое методу Значение называется аргументом. Переменная
внутри метода, которая принимает аргумент, называется параметром. Параметры
Объявляются внутри круглых скобок, следующих за именем МСТОЦИ. Синтаксис
Объявления параметров аналогичен синтаксису объявления переменных. Параметр
находится в области видимости своего метода. Назначение параметра — при выпол-
пенни метода гтринимать значение аргумента, но в целом он Действует, как любая
другая локальная гпеременная.
Далее приведена простая программа, в которой для передачи методу значения
используется параметр. Внутри класса CkhNum MOTOR 1sEven () возвращает значение
где, если передаваемое ему значение является четным. В противном случае он
‚дозвращаст значение false. To есть метод isEven () возвращает значения типа bool.
// Простая программа, в которой демонстрируется использование параметра для
// передачи методу значения.
using System;
class ChkNum (
// Определение метода, который возвращает значение тина bool.
1‘. Public bool i5EVe“(i“t X) { Hemmwnuanxswnumturmpammpom
ь if((x%2) == O) return true; MeKmaisEven()n1mmerTMuinL
else return false;
class ParmDemo {
5 public static void Main() {
2' ChkNum e = new ChkNum();
if(e.isEven(10)) Console.WriteLine("qMcno 10 четное.");
if(e.isEven(9)) Console.WriteLine("qmcno 9 четное.");
if(e.isEven(B)) Сопзо1е.Иг1сеЬ1пе("Число B четное.");
B результате выполнения этой программы будут выведены следующие етроки:
Число 10 четное.
, Число В четное.
Метод isEven () вызывается три раза и каждый раз ему передастся новое значение.
' Рассмотрим этот процесс более подробно, при этом обратим внимание на способ
вызова метода isEver1() — аргумент указывается внутри круглых скобок. Когда
метод isE\/en () Bb13b[B21CTC$I первый раз, ему передается значение 10 (при выполне-
нии метода параметр х будет иметь значение 10). Во втором вызове аргументом
является значение 9 (Параметр х будет иметь значение 9). При третьем вызове
Параметр х получает значение 8. То есть параметр метода, Переменная х, получает
значение, которое передается как аргумент во время вызова метода isEven () .
14b Глава 4. Классы. объекты и методы 1
1
IVICTOIJ. МОЖСТ ИИСТЬ НССКОЛЬКО ПНРНМСТРОН. О6ЪЯНЛЯСМЫС H2'lp2l.\1C'l‘Pbl p21'iL1C.7l5H0TCS1
3am1'rbI:\m. Например. Класс Factor C01I.Cp>KHT метод 15 Расы ': U . в котором опрсде—
ляется, является nu первый Параметр 5lc.m'r€,'Ic.\1 второго параметра.
uslng System;
class Factor {
public бое} 1sFac:or(int a, int b) { Эгогшпеднмешчшапщшмета
if( (b % a) 4: O) return true;
else return false;
1
void Main() 1
4 — new Eacuorth
;f(x.1sFactor(2, 20))
Console.writeLine("QMcno 2 является делителем числа 20.");
if(x.isFactor(3, 20))
Console.Wri:eLine("3Ta строка не будет выведена на экран."):
—._и
}
Обратите внимание, что при вызове метода isFa<:tor () аргументы тоже разделены
запятыми. Используемые параметры могут иметь различные типы. Например такой
фрагмент кода
int myMeth(int a, double b, float C) (
1/ 1/
HC является ошибочным.
Дальнейшее усовершенствование класса Vehicle
Можно продолжить усовершенствование класса Vehicle и добавить возможность
вычисления кол ичеетва топлива, необходимого автомобилю Шя преодоления задан-
ного расстояния. Новый метод, который будет иметь имя fuel наедет! ( ) , принимает
в качестве аргумента Значение расстояния. которое необходимо преодолеть автомо-
билю, и возвращает значение необходимого количества топлива (в галлонах). При-
встхеги определение метода fuelneededfl
publlc double fuelneeded(int mlles) {
return (double) miles / mpg;
J
Обратите внимание, что метод возвращает значение типа double. Это весьма
уместно, поскольку значение необходимого количества топлива не обязательно будет
ЦСлоч и слс н н ы м .
Далее представлен весь код класса Vehicle, B котором определен метод fuclncedcd () .
l й
ДЛЯ ВЫЧИСЛЕНИЯ КОЛИЧЕСТВЗ TOHJII/I56, НеОбХОДИМОГО аЕТОМОбИЛЮ, ЧТОбЫ ПРЕОДОЛЕТЬ
указанное pEiCC'J.‘O}{H1/le, B программе ИСПОЛЬЗУЕТСЯ МСШОД С параметром.
9‹
/
меТ 1 47
If
u5;n9 5У55еШ7
,.;_,3<;; Vchlcle Ё
V int passengers; /K Количество пассажиров.
;nC fuelcap; ‚/ Емкость топливного бака (в галлопах).
Е in: mpg; /1 Расстояние (в милях), которое данный
// автомобиль может проехать, используя один
галлон топлива.
won возвращает значение максимального расстояния, которое может
' ' ‚‚ проехать автомобиль с полным топливным баком.
pupils int ranQ€() 1
return mpg * fuelcap;
, Метод вычисляет количество топлива, требуемое для преодоления
расстояния, значение которого передается методу в качестве параметра.
pusllc double fuclneededtint miles) 1
‘ return (double) miles / mpg;
class CompFuel {
publlc static void Main() {
Vehicle minivan = new Vehicle();
Vehicle sportscar = new Vehicle();
double gallons;
ll int dist е 252;
// Значения присваиваются переменным объекта minivan.
‚‚ ш1п1уап.ра5$еп9ег$ ~ 7;
n;van.fuelcap : 16;
minivan.mpg = 21;
// Значения присваиваются переменным объекта sportscar.
sportscar.passengers — 2;
Н sportsCar.fueicap = l4;
j sportscar.mpg : l2;
é gallons : minivan.tuelneedcd(dist);
F
ъ .
Ё Соп$о1е.Иг1СеЬ1пе("Чтобы проехать " r dist т " мили, микроавтобусу " +
Ё "требуется " L gallons + " галлонов топлиьа.");
5 gallons = sportscar.fuelneeded(dist);
Соп5о1е.Иг1:еЬ1пе("Чтооы проехать " + dist + " МИЛИ, спортивному " +
"автомобилю требуется " т gallons т " галлон топлИва.")2
Ниже представлен рсзугкьтат выполнения этой программы.
Чтобы проехать 252 мили, микроавтобусу требуется 12.0 галлонов топлива.
Чтобы проехать 252 мили, спортивному автомобилю требуется 21.0 галлон топлива.
’_ :.е.‹...«„ъ
плана 4. Классы, объекты и методы
-‘-'9 " ‚
‘т минутных: практикум -
I. B каких случаях для доступа к переменной экземпляра или к методу нужно
указывать имя объекта и использовать оператор точка (.)? В каких случаях
ИМЯ переменной ИЛИ МСТОДЭ МОЖСТ ИСПОЛЬЗОВЭТЬСЯ НСПОСрСЛСТВСННО?
Объясните разницу между аргументом и параметром.
3. Назовите два способа возврата управления из метода.
Проект 4-1. Создание справочного класса
Ё HelpClassDemo.cs
Классы необходимы большой программе в качестве строительных блоков. Для этого‘
каждый класс должен представлять собой единый (законченный) функциональный
блок, выполняющий четко определенные действия. Классы должны быть небольшими
насколько это возможно, но в то же время и не слишком маленькими. Потому что классы,
для которых заданы некоторые второстепенные функции, запутывают‘ и деструктурируют
код, а классы, которые выполняют недостаточно функций. не предоставляют программе
необходимую функциональность. Золотая середина находится там, где наука о програм- _
мировании переходит в искусство программирования. Поэтому чем больше у програм- ,
миста опыта, тем быстрее он находит оптимальное решение. л
Попробуем для тренировки преобразовать справочную систему из проекта 3-3 (см. пре-
дыдушую главу) в справочный класс. Сначала рассмотрим, чем хороша эта идея.
Во-первых, справочная система представляет один логический блок. Поскольку {-
задачей является вывод на экран синтаксиса операторов Cit, выполняемая функция
блока компактна и четко определена. Во-вторых, помещение справочной системы в
класс облегчает работу программиста —~ если в какой-либо программе понадобится
предложить пользователю справочную систему, то достаточно будет создать объект,
являющийся экземпляром данного ктасса. Наконец, поскольку справка инкапсули-
рована, ее можно обновить или дополнить, Причем это не приведет к Побочным
эффектам в программе, использующей справочную систему.
Пошаговая инструкция
1. Создайте новый файл и назовите его Не lpClassDemo . cs. Чтобы не вводить код
заново, вы можете скопировать код программы из файла Help3 . cs B новый файл ё
1. Для доступа к переменной экземпляра из кода, не являющегося частью класса, в котором
определена эта переменная, необходимо указать имя объекта, оператор точка (.) и имя
HelpClassDemo .cs.
if
ё
переменной. Для доступа к переменной экземпляра из кода, являющегося частью класса, в
котором определена данная переменная. можно непосредственно указать имя переменной. ~
Эти же правила справедливы для методов. ’
2. Аргумент -—- это значение, которое передается методу при его вызове. параметром является
определенная методом переменная, которая принимает значение аргумента.
3. Возврат управления из метода может осуществляться с помощью оператора return. Если
метод имеет тип void, то возврат управления в то место программы, из которого метод был
вызван, осуществляется при достижении закрывающей фигурной скобки. Методы, возвра-
щающие значение какого-либо типа (не-пою), должны использовать оператор return для
возврата значения указанного типа, то есть возврат управления из метода при достижении
закрывающей фигурной скобки невозможен.
5
.¥~9flI9\n-u-s-1s-- ‘гам
' методы
149
2_ ддя создания класса нужно точно определить составляющие справочной СИСТСМ ы.
Например. в проекте не 1р3 . Cs определен код для вывода на экран меню, ввода
пользователем запроса, проверки корректности запроса и вывода на экран и нфоре
мации о выбранном элементе меню. Программа продолжает выполнять цикл до
ты пор, пока пользователь не введет символ q. ОЧСПИЦНО, что вывод меню.
проверка правильности ввода и вьпвод информации являются неотъемлемыми
„нитями справочной системы. А получение ктрограммой пользоватсльского ввода
п определение ситуаций, когда необходимо обрабатывать птовторньпе запросы
пользователя, — это вопросы. которые пе являются неотъемлемыми»: компонента-
хш справочной системы. Следовательно, в нашу задачу входит создание класса,
вьтодятцего меню, провергпоццего корректность запроса и выводящего запраши-
ваемую информацию, назовем эти методы showmenu (), isval1o() И helpon ()
COOTBCTCTBCHHO.
‘La
Создайте метод helpon () ‚ как показано ниже:
рцЬ11с void helpon(char what) 1
switCh(what)
C358
Console.
Console.
Console.
oreak;
case '2':
Console.
Console.
Console.
Console.
Console.
Console.
Console.
break;
case ‘З’:
Console.
Console.
Console.
break;
case '4‘:
Console.
Console.
break;
case '5':
Console.
Console.
Console.
Console.
break;
case '6‘:
Console.
Console.
break:
case '7':
Console.
Console.
break;
case '8‘:
Console.
1
(
Иг1сеЬ1пс("Оператор i£:\n");
WriteLine("1f(condition) statement;");
WriteLine("else statement;");
WriteLine("OnepaTop switch:\n");
WriteLine("switch(expression) {");
WriteLine(" case Constant:");
WriteLine(" statement sequence");
writeL1ne(" break;");
WriteLine(" // ..."):
WriteLine(")");
WriteLine("HMKn for:\n");
write("for(init; condition; iteration)");
WriCeLine(" statement;");
Wr1LeLine(“uMKn while:\n");
Wr1teLinC("wh1le(condition) sLatemcnt;");
Wr1.eLine("uMxn d0—while:\n");
writeLine("do {");
writeLine(" statement;");
writeLine(") while (condition);");
WriteLine("OnepaTop break:\n");
WriteLine("break; or break label;");
WLiteLine("OuepaTop con:1nue:\n");
WriteLine("continue; or continue label;");
WriteLine("OuepaTop goto:\n");
ICU
4.
ConsOle.WriCeLine("qOtO label;");
break;
Console.WriteLine();
}
СОЗДНЙТС метод showmenu () следующим образом:
public void showmenu() {
Console.WriteL1ne("Cnpasxa по синтаксису: ");
Console.WriteLine(" l. Оператор ;f");
C0nsoie.WriLeLine(" 2. Оператор 5и1сСЬ"5:
Console.writeL;ne(" 3. Цикл for");
Consoie.WriteLine(" 4. Цикл whi;c”);
Console.Wri:eL1nc(" 5. Цикл do~while");
Console.Wr1teLine(" 6. Оператор break");
Consoie.WriteL1ne(" 7. Оператор ContLnue");
Console.WriteLine(" 8. Оператор goto\n");
Console.WriteLine("Baenmwe порядковый номер оператора или цикла: ");
ConSole.WriteLine("HnH завершения работы программы введите символ q: ");
Б
Создайте метод 1svai1d() . как показано ниже:
pub1;c bool 1sval1d(char ch) {
if(ch < '1' I ch > ‘В’ & ch 1:
else return true;
}
'q') return EaLse;
Создайте из этих трех методов класс Help:
class Help {
public void helpon(char what) {
switch(what) {
case '1':
Console.WriteLine("Ouepafop if:\n");
Console.WriteL1ne("if(condi:ion) statement;");
Console.Wr;teLine("else statement;"):
break;
case '2':
Console.Wr;:eL1ne("Ouepawop swi:ch:\n");
Console.Wr1teLine("switCh(cxpress1on) {");
Conso1e.WriteL;ne(" Case Cons:ant:");
Console.writeLine(" statement sequence");
Conso1e.WriteLine(" break;");
Console.WriteLine(" // ..");
Console.WriteLine("}");
break;
Case '3':
Console.WriteLine("umKn for:\n");
condition; iLeraLion)");
statement;");
Console.Writc("for(init;
Consoie.Wr1teL;ne("
break;
case '4':
Console.Wr;teLine("UMKn while:\n");
ConsoLe.WriteL;ne("while(condition)
break;
'5':
ConsoLe.WriteL;nc("umKu do—whilc:\n");
statcmenL;");
C636
Глава 4. Классы, объекты и методы
Методы 1 5 1
5 Console.WriteL1ne("do {");
Console.Wr1:eL;ne(" statement;");
Consoie.WriLeLinC("f whxle (condi:1on);");
break;
case '6':
5 Соп3о1е.Их1ЕеЬ1пе("Оператор break:\n");
Д ' ConsoLe.Wri:eLine("break; or break Label;");
break;
Case '7':
Г 15оДе.Иг11сЬ;пе("Оператор C0ntinue:\n");
Lonso;c.W1;teL;ne "con;;nue; er conLinue iahc-;");
1’ океан;
› Se "':
Consolc.Wr1teL1ne("Onepawop goto:\n");
ConsoLe.Wri:eL1ne("go:o iabel;");
break;
}
Console.Wri:eL1ne();
pLOL;C void showmenu() {
Console.WriteLine("Cupaaxa по синтаксису: ");
Console.Wr;:eLine(" 1. Оператор 1f");
Console.WriteLineL" 2 Оператор 5и1псп");
Console.WriteL;ne( 3 Цикл for");
Console.Wr1teLine(" 4. Цикл while");
Conso1e.WriteLine(" 5
Con5ole.Wr1teL;ne(" 6.
Цикл do~wh1le");
Оператор break");
Conso;e.WriteLine(" 7. Оператор continue");
Console.Wr1teLine(" 8. Оператор goto\n");
Console.WriteLine("Baenmwe порядковый номер оператора или Цикла: ");
Соп5о1е.Иг1:еЬ1пе("Для завершения работы программы введите символ q: ");
fl‘ public boo; 1sval1d(char ch) (
iftch < '1' I ch > ‘З’ & ch Ее 'q') return false;
else return true;
1
п
Тспсрь необходимо переписать метод Маш () из проекта 3-3 таким образом чтобы
в нем использовался новый класс Help. Назовем главный класс (то ссть класс, в
котором находится \|CTO,1T-1.:11r1 () ) He-pClassDemo . Cs. Полный flHC’l‘HHl"f1p01‘pE1.Vl-
§ мьтНе1рС1аз5Юето.сзтцшдспшлштниже
‘t ‚
‚ Проект 4—1.
На основе справочной системы из проекта 3*3 создан класс Help.
us-ng System;
class Help L
public void hcLpon(char what) {
‚‚‚‚‚ .. ш-аыдш, попив-Ш " ""“"’“~°‘
‚__________г__„„___,_„_`_
switch(what) {
case '1‘:
Console.WriteLine("onepawop if:\n");
Console . Wrj.teLine ("if (condition) statement: "):
Console.WriteLine("else statement;");
break;
case '2':
Console.WriteLine("OuepaTOp swi:ch:\n");
Console.WriteLine("switch(expression) {");
ConsOle.WriteLine(" case COnstanC:")i
Console.WriteLine(" statement sequence");
Console.Wr1teL1ne(" break;");
Console Wr1teLine(" // ...");
Console.wr1teLine(")");
break;
Case '3':
Console.WriteLine("HMKn for:\n"); !
Console-Write("for(in1t; conditlon; 1teration)");
Console.WriteLine(" statement;");
break;
case '4':
Console.WriteLine("umKn while:\n");
Console.WriteLine("while(condition) statement;");
break;
case '5': :\
Console.Wr1teLine("HmKn do-while:\n"):
Console.WriceLine("do {");
ь. ‚ш! ь... „ь- .u...a.....g.: .—..zh¢4d
Console.WriteLine(" statement;");
Console.WriteLine("} while (condition);");
break;
case '6‘:
Console.WriteLine("onepawop break:\n“);
Console.WriteLine("break; or break label;"):
break;
case '7':
COnsOle.WriteLine("Onepawop cont;nue:\n");
Console.Wr1teL1ne("cont1nue; or contlnue label;");
break;
case '8‘:
Console.WriteLine("Onepawop goto:\n");
Console.WriteLine("goto labe:;");
break;
}
Console.WriteLine();
public void showmenu() (
Conso1e.WriueLine("cnpabxa no синтаксису: ");
Console.WriteL1ne(" Оператор 1f");
Console.WriteLine(" 2. Оператор switch");
Console.WriteLine(" 3. Цикл for");
COnsole.WriteL1ne(" 4 Цикл while");
Console.WriteLine(" 5. Цикл do—while");
6
7
8
V-'
Console.WriteLine(" . Оператор break");
Console.WriteLine(" . Оператор Continue");
Console.WriteL1ne(" . Оператор goto\n“);
IIIIIlIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIlllllllllnul-----UI
153
Конструкторьт
Сопзо1е.Иг1сеЬ1пе("Введите порядковый номер оператора или Цикла: ");
Сопзо1е.Ип1пеЪ1пе("Для завершения работы программы введите символ q: ");
)
puDllC bool 1svalid(char ch) (
if(ch < '1' I ch > '8' & ch Е; 'q') return false;
else return true:
class He1pClassDemo {
public static void Main() {
char choice;
Help hlpobj = new Help();
for(::) (
do 1
hlpobj.showmenu();
do (
choice = (char) Console.Read();
} while(choice =: '\n' I cholce =2 '\r');
) while( !h1pobj.isval1d(choice) );
if(choice == 'q') break;
Console.WriteLine("\n");
hlpobj.helpon(choice);
Тестирование ДЗННОЙ программы ПОКЗЗЫВЗЕТ, ЧТО ВЫПОЛНЯЕМЫЕ ЕЮ ФУНКЦИИ
остались прежними. ПРЕИМУЩЕСТВО ЖЕ НОВОГО ПОДХОДЭ. СОСТОИТ В ТОМ, ЧТО ТЕПЕрЬ
В распоряжении программиста ЕСТЬ справочная СИСТЕМЕ, КОТОрВЯ МОЖЕТ ИСПОЛЬ-
ЗОВЗТЬСЯ ПОВТОрНО‚ КОГДЭ. В ЭТОМ ВОЗНИКНЕТ НЕОбХОДИМОСТЬ.
Конструкторы
B предыдущих примерах переменным экземпляра каждого объекта класса Vehlcle
f Необходимо было присваивать значения вручную, используя следующую последова-
тельность операторов:
П1п10ап.раз$еп9егз = 7;
П1п1чап.Еце1сар = 16;
Ч-п1чап.шр9 = 21;
Такой способ никогда не используется в профессионально написанных программах
C?».‘, потому что, во-первых, при ручном вводе всегда существует вероятность случай-
Ной ошибки (вы можете забыть присвоить значение одной из переменных), во-вто-
рых, в C# предусмотрен более эффективный способ выполнения этой задачи ——
Использование конструктора.
154
Глава 4. Классы, объекты и методы
K()IlC'NIpyKIIIOp КЛаССа MHHLIHHIIHBMDYCT O6-bCKT ПрИ CFO СОЗЦНННИ. OH ИМССТ ТО ЖС ИМЯ,
ЧТО И CFO KIIEICC, И СИНТИКСИЧССКИ НОХОЖ на МСТОП- ОЦННКО В K0HcTpy:<'r0pax ТИП
ПЪОЗПЪрЗПЦНСЬКЭГО ЗНДЧСНИЯ ПС yK?l3bll’>'(1CTC$l ЯВНО. ОбЬЦИ CHHTEIKCHC КОНСТПУКТОРЗЁ
Ciass—namC() {
// код конструктора
Как ПРАВИЛО, КОНСТрУКТОрЫ ИСПОЛЬЗУПОТСЯ ДЛЯ ПрПСВЫПВЦНПЯ H2l‘llL'1bHblX ЗНЫЧСПИЙ
l"lCDC.\1CHHbl_\-1 ':)K3C.\1I'L-'[$lpE1, ОПРСДСЛСННЫМ K.'l¢':lCCOM, ИЛИ ‚ЁШЯ ВЫПОЛНСННЯ ЛЮбЬЦ ДРУГИХ
процедур ПНИЦГ121ЛИ32111Ъ1И, НСОбХОЦИМЬП ДЛЯ СОЗДЗПИЯ ПОДПОСГЫО оформировпнпого
объекта.
BCC КЛЗССЫ ИМСХОТ КОНСТруКТОрЫ НСЗЫВИСИМО ОТ ТОГО. ОПРСЦСЛСН OH ИЛИ HC'l_ По
умолчанию в Сд предусмотрено наличие кот-тструктора. которыи присваивает нулетъые
ЗНЦЧСПИЯ BCCV1 l'lCpC.\1CHHb{\1 ЭКЗСМПЛЯрН (ДЛЯ ПСРСМСННЫХ ОбЫЧНЫХ ТИПОВ) И ЗПЕЩСНИЯ
x‘-.-.11 L (для переменных ссылочного типа). Но если конструктор явно опрсделен в
классе, то конструктор по умолчанию использоваться нс будет.
Рассмотрим простой пример:
// Пример простого конструктора.
using System;
class MyClass {
public int x;
pUbllC MyClass() { KOHmDyHDpKflm£flMyCldS&
10;
X:
class ConsDemo {
public static void Main() {
Myclass tl — new MyClass();
MyClass t2 — new MyClass();
Console.Wr1teL;ne(t1.x + + t2.x);
}
B ЭТОМ примере используется конструктор класса Шушазз
public MyClass() {
х v 10;
}
Отметим, что конструктор определен как public. Это сделано потому, что конструк-
тор будет вызываться из кода, определенного за пределами его класса. Конструктор
присваивает переменной экземпляра х класса MyClass значение 10. Этот конструктор
вызьтвастся оператором new при создании объекта. Напрписр„ в строке кода
Myclass tl < new MyClass();
конструктор МуСЬаЗЗ () вызывается для создания объекта t1, присваивая перемен-
ной L1 ‚х значение 10. То же происходит и для объекта :2 н после вызова конструк-
тора Переменная t2..v. также получает значснис I0. ТЗКИМ образом, в результате
выполнения программы будут выведены следующие значения:
10 10
Конструкторы 1
KoHCTpyKTOpbI C параметрами
В ирсдылуцъсаа примере был использован конструктор без параметров. Иногда это
Г npll(.‘\1.1C.\|O, НО ‘l'c1LLIC l1pI.'1.\1CH$lCTC$l КОНСТРУКТОР C ОДНИ“ ИЛИ НССКОЛЬКИМИ парамет-
Pawn. ”2‘1])L1.’\1CTpbl ИСПОЛЬЗУЮТСЯ В конструкторе ТЗК ЖС, КНК B MCTOIIC. C.-'lC.’J.}’lOUJ.C.\1
[|]‘JH\l\‘,pC l'lpCiLCT'<||K.'ICl'l KJEICC Myxflass, CO;'1C|))K21llll1i1 конструктор С параметрам И.
исьользоваьие конструктора C параметрами.
дд
„дола System;
g;dss MyClasS f
puo1;c ;nt x;
‘ ;'.~ubLi\: MyC';ass(inL i) {
х — ii
class ParmConsDemo {
public static void Main() {
Myclass tl = new MyClass(10);
Myclass t2 new MyClass(88);
Console.WtiteLine(tl.x 4 " " + t2.x);
Ниже показан результат вьтполнеьтия этой программы.
до 88
B данной версии класса МуС1а55 конструктор MyClass() ИМССТ один параметр
(переменную 1 типа тик), который используется для птриеваитзания начального
значения переменной экземпляра х. Следовательно, после выполнения оператора
MyClass tl = new MyClass(lO);
'3H2l‘lCHMC 10 передается параметру 1, а затем оно присваивается переменной
1 ё б '
до авление конструктора к классу Vehicle
Ё; Можно усовершенствовать класс Vehicle, Добавив к нему конструктор, которыи при
создании объекта будет автоматически инициализировать переменные passengers,
.¢_ iuelcap И mpg. Обратите особое внимание на строки кода. в которых создаются
объекты класса Vehicle.
I B программе используется новая версия класса Vehicle, В которой определен
конструктор.
h5;nq System;
.;dss Vehicle {
public in: passengers; // Количество пассажиров.
public in: fuelcap; // ЕМКОСТЬ топливного бака (в галлонах).
Public int mpg; // Расстояние (в милях), которое данный автомобиль
// может проехать, используя один галлон топлива.
// Конструктор класса Vehicle.
public Vehic1e(in: р, in: f, int m) { Konmpyknm wmccavehlcle
passengers : p;
$
ё
156
Глава 4. Классы, объекты и методы
fuelcap = f;
mpg — m;
// Метод возвращает значение максимального расстояния, которое может
// проехать автомобиль с полным топливным баком.
public int range() т
return mpg * tuelcap;
необходимое ДЛЯ ПРЭОДОЛЭНИЯ
В качестве параметра.
// Метод вычисляет количество топлива,
// расстояния, значение которого передается методу
public double fuelneeded(int miles) {
return (double) miles / mpg;
Class VehConsDemo {
public static void Main() {
// Создание двух объектов класса Vehicle. Гюршшчахщщкшришикаш
Vehicle minivan new Vehicle(7, 16, 2;); д 10M00W”'0m£KU/WW9“
Vehicle sportscar ~ new Vehicle(2, и, 12); Vehlcl" °““""""-‘°"“““““
1, спэконструктора
double gallons;
int dist : 252;
gallons 2 minivan.fuelneeded(dist);
Console.WriteLine("HTo6u проехать " + dist + " МИЛИ, микроавтобусу " +
"требуется " + gallons + " галлонов топлива.");
gallons 2 sportscar.fuelneeded(dist):
Сопзо1е.Иг1пеЬ1пе("Чтобы проехать " + dist + " мили, спортивному " °
"автомобилю требуется " + gallons + " галлон топлива.");
)
При создании объектов minivan и sportscar конструктор Vehicle () присваивает
переменным этих объектов начальные значения. Каждый объект инициализируется
указываемыми аргументами. Например, в строке кода
16, 21);
Vehicle minivan = new Vehicle(7,
значения 7, 16 и 2! передаются конструктору при создании объекта (выполнении
оператора new). Следовательно, копни переменных passengers. fuelcap И mpg
объекта ттпшап будут содержать Значения 7, 16 и 2| COOTBCTCTBCHHO. а результат
вьтполнения этой программы будет таким же, как в предыдущей версии.
M’:/Hymflmf I?;9£iA’I}7£//(3//*¢'i
I. Что такое конструктор? Когда он выполняется?
2. Указывается ‚ти в конструкторе тип возвращаемого значения’?
1. конструктором называется метод, используемый для иттипиштттзапии создаваемого объекта.
Конструктор вьптодтътястся при создании объекта его класса.
2. Нет.
деструкторы и «сборка мусора» 1 bi
оператор new
Теперь. когда мы уже достаточно знаем о классах и их конструкторах. подробно
рассмотрим оператор new. Этот оператор имеет следующий синтаксис:
Cla55—\/ar — new Class~name();
ЗДССЬ словосочетание съэззчяаг — создаваемая переменная, тип которой (class)
V}(a3b[BaCTC$I перед именем переменной, а словосочетание class—name — это имя
тстасса, экземпляр которого создается. (Можно сказать, что вместо подстановочньях
шов class И съэззтате указывается одно и то же имя класса. экземпляр которого
создается.) Имя класса, за которым следует пара круглых скобок, является конструк-
тором класса. Если класс не имеет своего конструктора, оператор new будет исполь-
зевать конструктор по умолчанию, предоставленный C#. Следовательно, оператор
fie‘.-J может использоваться для создания объекта любого класса.
Поскольку размер памяти ограничен, существует вероятность, что оператор new He
д I сможет выделить для объекта нужное количество памяти. Если это произойдет, то
возникнет исключительная ситуация выполнения программы. (Как обрабатывать
_ исключительные ситуации, вы узнаете из главы 9.) При выполнении программ этой
5 п книги вам не нужно беспокоиться о недостаточном объеме памяти, но в будущем при
},: создании своих Программ всегда учитывайте вероятность этого.
Ётеете: дааевеесвэвеве-ж ннннннн н
Вопрос. Почему отсутствует необходимость использования оператора new
К.
gun создания переменных обычного типа (таких. как mt или float)?
I OTBeT. B Cii переменная обычного типа сама хранит свое значение. Память для
',- хранения этой переменной автоматически выделяется при компилировании программы,
и нет необходимости явно выделять память C ПОМОЩЬЮ оператора new. Что касается
переменных ссылочного типа, то они хранят только ссылку на объект. Память для
хранения объекта выделяется динамически (во время выполнения программы).
‚ Превращение переменных обычного типа в переменные ссылочного типа может
‘5 заметно снизить производительность программы. При использовании переменных
ссылочного типа происходят непрямые обращения к значениям, что снижает ско-
роеть выполнения программы.
:ъ›ы-:;.-1=ж:еёг!ё‹6‘=\жсйМ-5ке!. ' ‚Т :,..'_f
Для эксперимента можно применить Оператор new, чтобы создать переменную
обычного типа
int 1 = new int();
При этом будет вызван конструктор по умолчанию для типа Lnt, который присвоит
Переменной i начальное значение О. Однако следует заметить, что при этом память
ПС будет выделена динамически. Большинство программистов не используют опера-
Юр new для создания переменных обычного типа.
2-». ч
'“ „гм
jg деструкторы и «сборка мусора»
Вы уже знаете, что при использовании оператора new память для объектов выделя-
СТся динамически. Но поскольку объем памяти ограничен, существует возможность
Нсудачътой попытки создания оператором new необходимого объекта. Поэтому ОДИН
1 за Глава 4. Классы, объекты и методы `
из ключевых компонентов схемы Динамического вьтделеттия памяти механизм;
освобождения памяти, занимаемой неиспользуемыми объектами, для того чтобы ее”
можно было выделить под вновь создтатзасхтьтс объекты. Во многих языках програм-
мированття освобождению ранее вьтдедтеттгтотт памяти осуществляется вручную (напри-
мер, в C++ ДЛЯ освобождения памяти используется оператор ее? и). C33 располагает
для этого более этрфектитъттьпт механизмом, назьнъаехтьтхт «сборкой .u_v('0_IJu».
Когда в C#3—nporp;m мс отсутствуют обращения к объекту. то он расематриваеетсзт как
нс использующийся более, и система автоматически без участия программиста
освобождает занимаемую им память, которая в дальнейшем может быть выделена
под новые объекты.
«Сборка мусора» периодически осуществляется в ходе всей программы, причем для
начала этой операции должны выполнзггься два условия V во-псрвых, наличие
объектов, которые можно удалить из памяти, а во-тгторьтх, необходимость такой
оттерации. Поскольку ттроцесс «сборки мусора» занимает‘ некоторое время, система
исполнения программы осуществляет‘ ее только в случае т-теобходимосттт, причем
программист не может точно определить момент, когда это произойдет.
Деструкторы
В С# предусмотрена возможность создания метода, который вызывается непосред-
CTBCHHO ПСрСД УЦЭЛСНИСМ ОбЪСКТН ИЗ ПАМЯТИ ПрИ ПОМОЩИ! ОПСрЗЦПИ «СбОрКИ мусора». `
Этот метод называется дес/прукторолт и может использоваться для гарантии коррект-
ного удаления объекта из памяти. Например, с помощью деструктора можно удосто-
вериться, что файл, к которому имело место обращение из данного объекта, будет
закрыт. Деструктор имеет следующий синтаксис:
~class—name() {
// код десшруктора
}
Здесь словосочетание Class-name — ЭТО имя класса. То есть дсструктор объявляется
так же, как конструктор, за исключением того, что Вдсструкторс перед именем класса
используется символ тильда (”). Отметим, что при определении деструктора не
указывается тип возвращаемого значения.
Для добавления деструктора к классу нужно просто добавить его оттредсленис к коду
Этого тстасса (то есть деструктор является обычным членом класса). Внутри дсструю
тора назначаются действия. которьте должньт быть выполнены перед возтъргттттсттттем
памяти, вьтдСлет-тттой для этого объекта. Деструктор вызывается во всех случаях, когда
объект его класса Должен быть удален из памяти.
Важно понимать, что деструктор вызывается непосредственно псрсд выполнением
операции «сборки мусора». (Он не вызывается, например. когда объект класса
выходит из области видимости. Этим ДСсТрукТОрЫ в Cit отличаются от деструкторов
в С++, где они вызыванттся, когда объект выходттт за пределы области вндим‹›ст‘и, в
которой этот обьскт был создан.) Это означает, что невозможно точно оттредельгть,
когда будет выполняться код деетруктора. Кроме того. программа может завсртитггь
работу до начала операции «сборки мусора», в этом случае деструктор не будет
вызываться вообще.
'- з. м „т:
деструкторьл и «сборка мусора» 159
проект 4-2. Демонстрация работы деструкторов
Q pestructDemo.cs
мы уже говорили, что объекты не обязательно удаляются из памяти, если они больше
не нектользуютсгк в программе. «Сборка мусора» производится только тогда, когда она
о „Шкет быть выполнена эёрфективно (обычно [L151 нескольких объектов оцнохърехяегтгто).
‚ (дсдопъагсльно, mm цегькоътетрапъги работы нсструктора необходимо создать и у: пшггожгггь
` г большое колгшество объектогз. Именно этому мы посвятим данный проект.
ПОШЗГОВЗН ИНСТРУКЦИЯ
|_ Создайте новый файл и назовите его Destr'uc'L.cs.
2. Создайте класс Destruct, как показано ниже:
›1 class Destruct 1
Ё’ Lnt X;
public Destruct(int i) i
xei;
// Вызывается при удалении объекта из памяти.
~Destruct() I
к
Соп5о1е.Иг1сеЬ1пе("Удаление из памяти объекта " г x)‘
r
fl Метод Создает объект, который сразу будет уничтожен.
r_ public void generator(1nt i) (
Destruct о : new Destruct(i);
г
Конструктор присваивает переменной экземпляра х значение, передаваемое кон-
структору в качестве аргумента. В этой программе аеструктор C помощью пере-
иенной x выводит на экран порядковый номер уничтожаемого объекта. Особое
внимание обратите на метод generator g) , который создает и затем сразу унич-
тожает объект типа Destruct. B C.1CnyK)l11CM пункте инструкции вы увидите. Как
это осуществляется.
:‘.:v.s:w€—-ii: r~#’F‘M'W”£§'* f.:‘-T". ‚Д, _ .
‘-4
Создайте класс Des:ructDem0, как показано ниже:
class DestructDemo {
pub;1c static vo1d Main() {
int count;
Destruct ob = new Destruct(O);
/* Далее с помощью цикла for создается большое количество объектов.
После использования всей свободной памяти выполняется операция
«сборки мусора». Необходимое для этого количество объекте: зависит
от объема установленной на компьютере памяти, поэтому, возможно,
придется увеличить число, используемое в условном
выражении цикла (количество проходов Цикла).
*/
Д. .‘ ‘ wxfiza-an-=-vafltfi 97*» т
for(CounC~1; count < 100000: counL%+)
ob.generator(Count):
}
}
Вначале в этом классе создастся объект Ob типа Destruct. Затем с помощью этого
объекта и вызова определенного в нем методы оепекайогН создается 100000
объс кто в. В результате на некотором эта пс выпол непия программы объекты етануг
пе только создаваться, но и уничтожаться, причем на различных этапах будут
происходить операции «сборки мусора». Как часто и когда именно это будет
птроисхолить, зависит от нескольких факторов ~ ъкнчального объема свободной
памяти, типа операционной системы и так далее. Но с определенного момента на `
экране начнут появляться сообщения. генерируемые леструкторохт. Если такие
сообщения не выводятся, попробуйте увеличить число создаваемых объектов
путем увеличения значения переменной count B ЦИКЛС for.
4. Hmxc представлен полный листинг ктрограмиы DeC‘r.ruCtDem«).CS.'
И
1
Проект 4~2.
B этой программе демонстрируется работа деструкшора.
х
/
using System;
class Destruct (
public int х;
public Destruct(int i) {
Xwi;
// Вызывается при удалении объекта из памяти.
~DestruCt() (
COnsOle.WriteLlne("Destructing " + х);
// Метод создает объект. который сразу уничтожается.
public void generator(int i) {
Destruct о ~ new DeSEruCE(i)i
class Destruc:Demo {
public static void Ma;n() g
Ln: Count;
Destruct ob = new Destruct(O):
/* Далее с помощью цикла for создается большое количество объектов.
После использования всей свободной памяти выполняется операция
«сборки мусора». Необходимое для этого количество объектов зависит
от объема установленной на компьютере памяти, поэтому, возможно,
ПРИДЁТСЯ УВЕЛИЧИТЬ ЧИСЛО, ИСПОЛЬЗУЕМОЕ В УСЛОВНОМ выражении ЦИКЛЗ
(количество проходов цикла).
ч
’ Ключевое слово this 161
Еог(соЦпС—1; Count < 100000; cOUnt++l
ob.generator(count);
Ключевое слово this
5 давершение этой главы рассмотрим особенности использопгпкия клпочсного слова
„д При вызове метода ему автоматически передается неялшыи аргумент. который
является ссылкой на нызывахотпьхй объект (то есть на объект, C данными которого
будет работать метод). Эта ссылка называется сгцз. Чтобы понять, как работает ссылка
3.. рассмотрим программу, н которой создается масс Ewr, npC,1Hz1JH2l‘ICHnblii для
вычисления результата возведения числа в некоторую пслогтислськттуяо степень.
иЬ11с double b:
г ii int е;
pub;ic double val;
public Pwr(dOub1e пот, int exp) {
о = num;
е — exp;
val 1;
;‘(exp=:O) return:
for( ; exp>O; exp——) val ~ Val * b;
puolic double get_pwr() {
return val:
C;4SS DemoPwr {
pzblic statlc Void Main() i
Риг х 2 new Pwr(4.0, 2):
Риг y I new Pwr(2.5, 1);
Pwr z = new Pwr(5.7, O);
fh?U¢£mAm«--7
Console.WriteLine(x.b + " В " + X.e + "—й степени 2 " + x.ge:_pwr());
Console.WriteL1ne(y.b + " 5 " + y.e + "—й степени — " + у.9её„риг()):
„ Console.WriteLine(z.b + " 5 " + z.e + "~i степени — " L z.qetApwr());
if ‚
Как вы знаете в пределах метода доступ к другим членам класса иожст осуппССТН-
Ё -'i5"YbC$1 без указания имени объекта или масса, то есть непосрсдсттчснцко. Слецопъа-
Гшьно. внутри метода get рык при выполнении оператора
g _
g Lcturn val;
Э
бщет возвращена копия переменной val, ассоциированная с вызывающим объектом.
По этот же оператор можно написать следующим образом:
дгсцгп this.val;
1 bz Глава 4. Классы, объекты и метод
Здесь ссылка this указывает на объект, для которого был вызван мстод getvpwr (;
Следовательно, переменная this . val является коп исй переменной val панно;
объекта. Например, если метод 9е‘с___риг() вьтзваът для объекта х, то ссылка this
I1pC,-’1bI,1_VlLLC.M операторе указывает на объект :-:. Можно сказать, что запись опсратод
без использования кдиочсвого слова this является сокращенной формой записи,
Ниже представлен весь ютасе Pwr, }l2HlHC21}lHlalH C HCFlO_'1b30|3ElHHCM ключевого слов
tlus.
uslng System;
class Pwr { '
public double b;
public int e;
public double val;
public Pwr(double num, int exp) (
this.b Ь num;
this.e — exp;
this.val 4 l;
if(exp;—0) return:
for( ; ехр>0; ехр——) this.val this.val * this.b;
public double get_pwr() {
return this.val;
}
Скорее всего, ни один опытный программист на языке C# НС напишет класс Pwr
подобным способом‚ поскольку никакого преимущества такая форма записи не дает.
Но в некоторых случаях ссылка this может быть полезна. Например. синтаксис С#
ПОЗВОЛЯЕТ ИСПОЛЬЗОВНТЬ ОЦИНЗКОВЫС ИМСНН ДЛЯ параметров ИЛИ локальных псремсн—
НЫХ И ДЛЯ ПСРСМСННЫХ ЭКЗСМПЛЯрЗ. КОПШ ЭТО ПрОИСХОДИТ, ЛОКЗЛЬНОС ИМЯ скрывает
переменную экземпляра, и доступ к ней можно получить, используя ссылку this.
Например, приведенный ниже конструктор Pwr() является сингаксически действи—
ТСЛЬНЫМ. ХОТЯ ТЗКОЙ СТИЛЬ ПрИМСНЯТЬ НС рекомендуется.
public Pwr(double b, int е) {
this.b < b;
th1s.e = е;
val é 1;
1f(ev:O) return;
for( ; e>O; e~-) val л val * b:
Ъ
B этой версии конструктора Pwr () имена параметров и имена Переменных экземп-
ляра одинаковы, поэтому псрсменныс экземпляра будут скрыты. А если вы исполь-
зуете ссылку this. то можете «открыть» псрсмстттауго экземпляра.
. Контрдльньле вопросы 163
ёеёееешаааге еде же;
I. B чем состоит различие иежду классах: и объектом’?
2. Как определяется класс?
3. Собственные копии чего имеет каждый объект‘?
4
E
Покажите, как объявить объект с именем Counter класса MyCounter,
используя два отдельных оператора.
‚ э. Запишите определение яиетода myMeth () , который возвращает значение
типа double И имеет два параметра а и b типа 1 r.:_.
6. Как из метода возвращается управление, если метод BO3Bp21lllaCT3lli!‘lc-
Hue?
7. KHKOC имя имеет конструктор?
8. Какие функции выполняет огператор new?
1 9. Что такое «сборка мусора»? Как ‘эта операция выполняется? Что такое
деструктор‘?
` I0. Объясните предназначение ключевого слова this? `
-'- ~:-.1¢>-X.'«!c«\§rL:.,,~.v»‘qx:—: мы ¢>—.<\—~ .:»~_\~ _ з: х_:.:___:.к -2; -.s.‘-,..-A:.:...- ..~,——~,.»:.-;,: :‘
‘l
э
Подробнее о типах
данных и onepampai
DDDLJD
Массивы
Объекты типа string
Цикл foreach
‚з. . ‘ ._-.. ‘х .‚—'а‚..‹›.-
побитовые операторы
Оператор ?
„щи-м..ь„..м.„
165
массивы
В этой главе мы вернемся к описанию типов данных и операторов Сд, рассмотрим
ктасспвьд данные типа зпшпо, побитовые операторы и тернарный оператор ?. а также
Юдробъто расскажем о цикле foreach.
массивьп
Массив это коллекция переменных одного типа. При обращении к массиву
указывается его имя и индекс элемента, которому было присвоено значение данного
типа. Массивы могут быть как одномерными, так и многомерными, хотя чаще
используются одномерные массивы. Массивы применяются для решения многих
задач, поскольку ктредоставлятот удобные средства группировки переменных одного
типа. Например, вы можете использовать массив для хранения информации о
среднесуточной температуре каждого дня месяца или для хранения комплекта сим-
волов либо коллекции объектов с данными о ваших однокурсниках, а также для
других подобных целей.
Данные в массиве организованы так. что ими легко манипулировать. именно в этом
заютючается главное достоинство массива. Например. если массив хранит информа-
цию об оценках, полученных абитуриентом, то Путем обработки элементов массива
в цикте легко вычислить средний балл экзаменусмого. Кроме того, эти данные легко
сортируются.
В Cr можно использовать массивы точно так же, как в других языках программиро-
вания, но здесь у них есть одна особенность. Она состоит в том, что массивы в Сд
реатнзованы как объекты, поэтому вданной книге сначгша рассматриваются объекты,
а затем массивы. Благодаря такой реализации массивов у Сд-ттрограмм появляются
дополнительные возможности, например, удаление из памяти неиспользуемых мас-
сивов при «сборке мусора».
Одномерные массивы
Одномерный массив — ЭТО список однотипных переменных. В программировании
такие списки применяются достаточно часто. Например, одномерный массив можно
использовать для хранения номеров счетов активных пользователей ести или для
Хранения текущего рейтинга баскетбольной команды.
Для объявления одномерного массива применяется следующий синтаксис:
ЁУРЫЁЗ array—name : new type-{size};
Здесь слово type указывает тип массива, то есть тип данных каждого элеметтга, ИЗ
K0'10pbrx и состоит массив. Обратите внимание на квадратные скобкн, которые
сдСдуют за ключевым словом, указывающим тип элементов массива. С помощью этих
скобок объявляется одномерный массив (далее в этой главе будет показано, как
Обьявляется двухмерный массив). Количество элементов, которые могут быть поме-
‘limb: B массив, определяется величиной 51:63. Поскольку массивы реальязоваттьт как
Объекты, процесс создания массива состоит из двух этапов. На первом этапе объяв-
дЯСгся имя массива — имя переменнойссылочного типа. На втором этапе для массива
‘”›1.1с‚чяется память и переменной массива присваивается ссылка на эту область
памяти. То есть в С# с помощью оператора new память массиву выделяется дина-
мнчееки.
1 66 Глава 5. Подробнее о типах данных и оператщ
› v~ „шьем „м,
›„,. „ -«..„„. -= .›-‚„‚. __„
„м ‚ш мм ‚д. ка" .-.> ;=..---›ь..„›---
Лримечаниеш
Если у вас есть опыт Программирования на С или С++‚ то обратите особое внимани
на способ объявления массива в C#. B ЧЕСТНОСТИ на то, ЧТО в Cit KnaI1paTHb1c скобк“
следуют за ключевым словом, указывающим тип, а не за имснем массива. ~‘».
Следующая строка кода создает массив типа lnt, состоящий из десяти элементов,
ссылку на него присваивает переменной ссылочного типа sample.‘
int[] sample = new int[lO];
Массив объявляется так же, как объект. Переменная sample хранит ссылку на адре‘.
памяти, выделенной оператором new. Размер выделенной памяти достаточен -`
храпения ДССЯТИ ЭЛСМСНТОВ ТИП?! int. к
Объявление массива (так же как объявление объекта) можно выполпътть е помощьтд:
двух операторов. Например, ‘
int[] sample;
sample — new int[lO];
B этом случае после создания переменная массива sample не будет ссылаться ндё
какой-либо физический объект. Только после выполнения второго оператора перед:
МСННОЙ присваивается ссылка на массив. г
Доступ к отдельному элементу массива осуществляется с использованием индексов
С его помощью указывается позиция элемента в пределах массива. Первый элемент
всех массивов в Cir имеет нулевой индекс. Поскольку массив sample состоит из 10Ё
элементов, значения индексов элементов этого массива находятся в диапазоне от 0.
до 9. Для индексации массива необходимо указать количество его элементов
квадратных скобках. Таким образом, первый элемент массива sample указываетсяд
как sample [O] . a последний элемент — sample [9]. Например. в приведенной ниже’
программе в массив sample помешаются значения от О до 9.
// В программе демонстрируется использование одномерного массива.
using System;
class ArrayDemo {
publlc static void Main() {
int{] sample = new int[lO]; «
int i; _5
for (i = О; i < 10; 1 ч 1+1) 4————————-{l‘1cpumI'il элемент ьшссива имеет индекс 0.J .
sample[i] 4 i; 1’
for(i = O; 1 < 10; 1 = 1+1)‹___„___„_______________ Ы
Соп5о1е.Ик1СеЬ1пе("Элементу sample[" + i + "] присвоено значение: " +
sample[i]);
ч
д
В результате работы программы будут выведены следующие десять строк:
Элементу 5атр1е{0] было присвоено значение: О
Элементу sample[l] было присвоено значение: 1
Элементу $атр1е[2] было присвоено значение: 2
Элементу sample[3] было присвоено значение: 3
Элементу sample[4] было присвоено значение: 4
массивы
167
элементу sample[5]
элементу sample[6]
элементу samp1e[7]
элементу sample[8]
3ueMeHTY 5ample[9]
было
было
Сзьядхкэ
было
было
присвоено
присвоено
присвоено
присвоено
присвоено
Значение:
значение:
ЗНЭЧЭНИЭ:
значение:
ЗНЗЧОНИС:
СХСМНТИЧССКИ МИССИИ sample ВЫГЛЯДИТ СЛСДУЮЪЦИМ ОбрНЗОМЁ
Г О J 1 I 2 I 3 I 4 I 5 I 6 I 7 8 9
ЁЁЕЕЁЕЁЁЕЁЁ
Ф Ф Ф Ф Ф Ф Ф Ф Ф Ф
Ti Ii ii ii Ti ii Ti ii Ti ii
E E E E E E E E E E
m m m m m m m m m m
m ш ш ш ш ш ш ш ш ш
гудассивьп широко используются в программпровапии, Поскольку дают возможность
легко управлять большим количеством однотипных переменных. Например, привс-
денная ниже программа с помощью обработки элементов массива nums в Цикле for
НаХОДИТ МЭКСИМИЛЬНОС И МИНИМНЛЬНОС ЗНЦЧСНИЯ ЭЛЕМЕНТОВ ЭТОГО массива.
// Программа находит минимальное и максимальное значения элементов массива.
using System;
class MinMax {
public static void Main() {
;nt[]
int min, max;
numS[O] — 99;
nums[l] ~10;
nums[2] — 100123;
nums[3] = 18;
nums[4] : -978;
nums[5] = 5623;
nums[6] = 463;
nums[7] = —9;
nums[8] ч 287;
nums[9] — 49;
min = max щ nums[O];
for(int 1:1; i < 10:
1f(nums[i] < min)
if(nums[i] > max)
}
Соп5о1е.Иг1сеЬ1пе("Минимальное
PL‘ 3yJ1bTaT выполнения этой программы будет следующим:
nums : new int{10];
шт) {
min 2 nums[i];
max numsii];
значение:
"значение: " + max);
п
+ min
M .
“ипимальное значение: —978, максимальное значение: 100123
Инициализация массива
максимальное
В предыдущей программе значения элементам массива nums задавались вручную с
Использованием десяти отдельных операторов присваивания. Существует болсс эф-
фСКтивньки способ выполнения этой операции. инициализировать массив можно
1 68 Главв 5. Подробнее о типах данных и операт а
сразу при его создании Общий синтаксис инициализации одномерного масси
вы глядит так:
type[] array—name 2 (vall, val2, val3, ... , valN};
Здесь начальные значения заданы величинажии от vall до из 1:1. ЭЛСМСНТЭМ массива
значения присваиваются по очереди слева направо, начиная е элемента с индексом од:
В С# массиву автоматически выделяется объем памяти, достаточный для хранения
указываемого количества значений, при этом нет необходимости явно использован:
оператор new. Ниже показан более эффективный способ написания программы-
MinMax: V‘
// E программе используется инициализация массива при его создании.
using System:
class MinMax {
public static void Main() (
int[] nums L 1 99, -10, 100123, 18, A978,4———
5623, 463, —9‚ 287, 49 };
Значения,приснаиваемькзэлсментаи %Ё
массива при его создании. ‘
int min, max;
min : max 4 nums[0]; i?
for(int i=1; 1 < 10; 1+*) f
if(nums[i] < min) min — пцт$[1};
if(nums[i] > max) max : numsfi];
}
Соп5о1е.Иг1сеЬ1пе("Минимальное значение: + m;n + ", максимальное +
"значение: " т max);
}
Хотя в этом нет необходимости, вы можете в экспериментгшьньпх цслях использовать’?
для инициализации массива оператор new. Например, способ инициализации маем:
еива nums, показанный ниже, не противоречит правилам, но использование опера- -
тора new B данном случае вовсе не обязательно.
г:
int[] nums = new intf] { 99, -10, 100123, 18, -978,
5623, 463, ~9, 287, 49 1; ‘;
Такой способ иницигшизации можно использовать для присваивания ссылки на д-
новый массив уже существующей переменной ссылочного типа:
in:[] nums;
nums W new in:[] { 99, ~10, 100123, 18, -9HL
5623, 463, -9, 287, 49 E;
B этом случае переменная массива nunxs объявляется в первом операторе, а инициа-
лизпруется во втором.
Жесткий контроль над граничными значениями индексов
В Сд строго контролируется обращение к элементу массива. Следует указывать _- а
Индекс. не превышающий граничные значения индексов этого массива, в противном `
случае возникнет ошибка выполнения программы. Чтобы убедиться в этом, протес-
ТИРУЙте следующую программу, в которой специально указывается несуществующий
Индекс (то есть производится попытка обращения к несуществующему элементу).
169
массивы
/_' b ;;pO1‘palv!M€ ПРОИЗВОДИШСЯ попытка обращения К НЭСУЩССТВУЮЩСМУ 3Jl€MeH'i'y
I
‚т; мыссклва-
ЦдАдд System;
; ArrayErr {
ic sta:;C void Ma;n() {
;n::} sample new 1n:[1O];
_П: 1,
Согласно условному выражению цикла for переменная i МОЖЭТ принять
значение, ьревыщающеё верхнее граничное значение индексов массива
s3mp;e.
;Qf{L — C; т < 100' i W ‘+‘)
sdmple[i] W i;
11ревьннсно всрхнестраничнос значение
И НДС КСОВ МЦССНВЦ samp l е .
E
Ha определенном этапе выполнения программы переменной 1 присваивается значе-
ние 10. При попытке обращения к элементу с индексом 10 программа будет закрыта
и будет сгеттерттрован объект-ртсключсние In-;iexOutGfRanqeE>:ception (ПОДрОбНСС
об пом см. в главе 10).
Маму/иных; практикум
1. С использованием чего осуществляется доступ к элементу массива?
2. Как объявить массив типа char, в котором содержится 10 элементов?
3. Справедливо ли утверждение, что во время выполнения Сд-програмзмы не
осуществляется контроль над граничными значениями индексов массива?
Проект 5-1. Сортировка массива
И Bubble,cs
Поскольку в одномерном массиве данные расположены в индексируемом линейном
списке, они легко поддатотся сортировке. Вероятно, вы знаете, что существует
Множество различных алгоритмов сортировки. Среди них можно выделить алгоритм
быстрой сортировки, пузырьковый алгоритм и алгоритм сортировки методом Шелла.
Наиболее известным, простым и понятным является пузырьковый алгоритм. Хотя он
НС очень Эффективен (фактически неприемлем штя сортировки больтних массивов),
Н“ может с успехом применяться для сортировки иебольнтттк массивов.
Пошаговая инструкция
1.
'\
Создайте файл и назовите его Bum LC .-
Пузырьковый алгоритм сортировки получил имя в соответствии е принципом своей
работы. Рассмотрим его подробно. Предположим, имеется массив, состоящий из 20
ДОСТУП К ЭЛЕМЕНТ)’ МЗССИВЗ ОСШЦССТВЛЯСТСЯ С ИСПОЛЬЗОВЦНИСМ HHJICKCZI.
—» Массив типа char, содержащий 10 элементов, объявляется следующим образом:
Shari} a —— new cha:f1C] ,—.
ь:
Нет. B C11 во время вьитолттеттия программы строго запрещено обратишься к несуществую-
щим элементам массива.
1 70 Глава 5. Подробнее о типах данных и операторах
элементов, которые нужно отсортировать в порядке возрастания. Сравниваются
значения двух находящихся рядом элементов. Если значение второго элемента
окажется меньше значения первого, то некоторой промежуточной переменной
присваивается значение второго элемента, второму элементу присваивается 3на`
чение первого элемента, а первому элементу присваивается значение промежу-
точной переменной. Можно сказать, что элементы меняются местами, причем
элемент с наименьшим значением перемещается к левой границе массива. Это
можно сравнить с перемещением пузырька воздуха в воде — из глубины к
поверхности, поэтому алгоритм сортировки и получил такое название. Для того
чтобы в массиве из 20 элементов сравнить все соседние элементы (первый со
вторым, второй с третьим и так далее) необходимо выполнить 20 — | = [9
сравнений, а поскольку наименьшее значение может оказаться у последнего
элемента (двадцатого), то для его перемещения в самое начало массива нужно
выполнить 19 перемещений. То есть нужно организовать два Цикла — внешний
и внутренний, каждый из которых должен выполняться |9 раз.
Ниже приведен фрагмент кода, составляющий ядро пузырькового алгоритма
сортировки. Массив, в котором сортируются элементы, называется nums.
// Это код пузырькового алгоритма сортировки.
for(a=1; a < size; a++)
for(b=size—l; b >: a; b——) {
if(nums[b-1] > nums[b]) { // Если значение предыдущего элемента больше
// Значения последующего элемента, то они
t = nums[b-1]; // "меняются местами".
nums[b—l] = nums[b];
nums[b] = t;
}
Чтобы вы хорошо усвоили материал. изложенный в этом абзаце. еще раз коротко
повторим принцип работы пузырькового алгоритма. Алгоритм построен на двух
циклах for. Значения стоящих рядом элементов массива проверяются во внут-
реннем цикле. Когда обнаруживается пара элементов, значения которых распо-
ложены не в порядке возрастания, они меняются местами. При каждом проходе
элементы с наименьшим значением Передвигаются в положение, соответствующее
их значению. Внешний цикл повторяет этот пропссе до тех пор, пока все элементы
массива не будут отсортированы.
3. Далее приведен код всей программы Bubble.
/iv
Проект 5-1.
В программе демонстрируется использование пузырькового алгоритма
сортировки элементов массива.
*/
using System;
class Bubble 1
public static void Main() {
int[] nums н i 99, -10, 100123, 18, «978,
5623, 463, —9, 287, 49 };
int a, D, t;
int size;
size = 10; // Количество элементов массива.
Многомерные массивы 1 71
// Отображение значений элементов первоначального массива.
Соп5о1е.Иг1пе("Значения элементов первоначального массива: \п");
for(int i=0; i < size; i++)
Console.Write(" " + nums{i]);
Console.WriteLine();
// ЭТО код нузырькового алгоритма сортировки.
for(a—l; a < size; a*T)
for(b=size—l; b >; а: b~~) {
д 1f(nums[b—1] > nums[b]) ( // Если значение предыдущего элемента
’; // больше значения последующего элемента,
t nums[b-1]; // TO ОНИ "меняются местами".
nums[b—l] — nums{b];
nums[b] = t;
}
}
// Отображение значений элементов отсортированного массива.
-» Соп5о1е.Иг1Ёе("Значения элементов отсортированного массива: \п");
Q‘ for(int i—O; i < size; ir+)
Console.Write(" " + nums{;]);
Console.Wr1teL1ne();
Ниже показан результат выполнения этой программы.
к‘ ЗНЗЧЭНИЯ ЭЛЭМЭНТОВ ПЭРВОНЗЧЭЛЬНОГО МЗССИВЗС
99, —10, 100123, 18, —978‚ 5623, 463, -9, 287, 49
ЗНЗЧЭНИЯ элементов отсортированного МЗССИВЗ:
—978‚ —10, —9‚ 18, 49, 99, 287, 463, 5623, 100123
Хотя сортировка данных в соответствии с пузырьковым алгоритмом вполне
приемлема ДЛЯ небольших массивов, она становится Hc3(])<beKTmsno‘v'1 при работе
с большими массивами. Наилучшим универсальным алгоритмом сортировки
является алгоритм быстрой сортировки. Но он основан на тех свойствах языка
Сд, которые вам еще не знакомы, об этом алгоритме мы расскажем в главе 6.
Многомерные массивы
Оцт-томсрньте массивы являются самыми распространенными в программировании.
H0 И многомерные массивы встречаются не так уж редко. Многомерньтм называется
пассив, который имеет две или больше размерностей, а доступ K отдельному элементу
"СУЩествлястся в нсм с помощью указания двух и более индексов.
двухмерные массивы
Самым простым из многомерных массивов является двухмерный массив. В НСМ
расположение конкретного элемента определяется двумя индексами. Двухмерный
Пассив можно рассматривать как информанионнуто таблицу ОДИН ННДСКС КОТОРОЙ
хказываст‘ номер строки, а другой V номср столбца.
‚ . .._==r.v;»=. га. __-a. „э
д.
г
„Ё
Двухмерный целочисленный массив table C размерностью 10><2О объявляется сле.
дующим образом:
iI1t[:] table
new int[10, 20];
Заметьте, что при объявлении данного массива две указываемые размерности массива
разделены запятой. В первой части оператора с помощью запятой в квадратных
скобках 1, 1 указывается. что создается переменная ссылочного типа для двухмерногё
массива. ВыЦелет-тис памяти для массива с помощью оператора new происходит при
выполнении второй части объявления массива:
new 1n:[10, 20] Й
‘u
При этом создается массив е размерностью 10>< 20. Обратите внимание, что значения;
размерности разделены запятой.
Для получения доступа к элементу двухмерного массива необходимо указать два
индекса. разделив их запятой. Например, для присваивания элементу массива па-
ble[3, 51 значения 10 необходимо использовать следующий оператор:
5} 10;
ё
д
Lable[3, 5
Ниже представлена программа, в которой элемснгахт двухмерного массиъза притсваи-
ВЦЮТСЯ ЗННЧСНИЯ ОТ I ДО I2, ‘(I ЗНТСМ ЧТИ ЗННЧСНИЯ ВЫПОЦЯТСЯ НИ ':)K[)£lH.
I
B программе демонстрируется использование двухмерного массива.
using System;
class TwoD (
public static void Main()
{
int t, i;
int [I у table new int {3 I д} I. ‹ Объявление двухмерного массива
с размерностью 3><4.
Еог(с=О; t < 3; ‘TC) {
Eor(i<O; 1 < 4; ++i) {
table[t,1] ; (t*4)+i+1;
ConsOle.WriLe(table[t,i) + " "):
3
Console.WriteLine();
Ь
В этой программе элемент table (Е), О} будет иметь значение 1, элемент table [0, 1] ——
значение 2. элемент паЬТ-1Е›‚{О‚ 2] —— значение 3 и т. д. Элемент table [2, 3] будет иметь
значение I2. Ниже приведено схематическое изображение массива.
О 1 2 3 ‹——правый индекс
О 1 2 3 4
1 5 6 7 8
2 9 10 11 12
neemfitnuexc
table[1, 2]
Рис. 5.1. Схематическое представление массива table
„„.ц.„„дтмыЕЕ:г
ты 1 73
rip!//"“""“””"‘.’&. то с \ \ . .\ „ ‚
Если У “ac “CT” От“ Программироваътття на С, С++ или Jzwu, ОбрнТНТС внимание на
то’ чТО Объявление Him доступ к элсмсъктам многочсрътых массивов в Сп ОСУШССТВ-
дистся нь ТдК- ШК В Этих языках. В них каждая размерность массива и индексы
указываются В ОТПС-Чьньтх квадратных скобках. а в Cr: yK213blB21CMb|c размерности
ррщДСЛ Я ЮТС Я ЗНП ЯТЫ M И _
МаССИВЬН Имеющие более двух размерностей
в Сд’ P'rUD€I1I'dcTcsr использование массивов, имеющих более двух разисрирстеи Н иже
прсдСППШСН ОбШИИ Синтаксис объятвяпсньтях такого многомерного массива;
ЁУРеЁ""’1 name ; new type[size1, size2,...,sizeN];
' д C П И , д. ‚ ‚ » ‚ ` .‚
Ht} п р И \f р, U D’ ВЫ ПОЛНЬНИИ СЛСЦУЮЩСГО ОПЬрАТОрА бУДСТ СОЗДАН Цддцочислсътн ы И
ТРС^“Ч3"Ь'И мдшив С размерностью 4х10х3:
;n{_f,,f multidlm : new дюн‘ то’ 3]_
, - ‚
‘Йббы "'ГѓО"‘ТЬ Эдсмснту массива multldim [З 4, 1] §Hauer-me I00 необходимо
ВЫПОЛНИТЬ СЛСДУЁОЦНП] опърнтор. а
шьД2;о;:п[2, 4, 1} „ ~ ,,
-Ou;
”””LW‘3""‘33“-“H многомерных массивов
ваш» о ' .. _ ‚ .‚ ..
ИЁИ” ' ‘Зир шт‘) многомерным массив можно, поместив список значении каждои
РЭШЧЭНОСТ" B 0TD~€«'1bHbn7l блок, заключенный в фигурные скобки. Например ниже
‚ . ’ ’
П°К"3аН Синтакшс “Ницигщизации двухмерного массива.
Е;реЁ‚] array;name ;
{га}, val, val,”
‚ _ _ val},
-V51, val, va;,__ Val},
(val, val, val,__ Val)
' I
-' I
3.-1CCb чтово 1 ' » м .‚
L’ Va O005HauaeT присваиваемое значение. Каждым внугреннии блок
присваивает 3”a“e“””I элементам соответствующей строки B пределах строки первое
Указываемос зна -
и По во“ 316 LICHHC бУДЁТ храниться В НУЛСВОМ ЭЛСМСНТС СТРОКИ, вТорос Значение -—
F Mg р ' ‘ Мите И MK далее. Обратите внимание, что блоки. содержащие значения,
ад; э ы п ‚ .. .. .. ‚
_ _ “JUL” За “HM”, d за закрывающси фигурнои скобкои блока со 3„а„ши‚„‚‚и
“то” pa3Mep"OCT“ C~TC£wcT точка с запятой
B nmsc СННОЙ
КОТ] О HI] Ниже Програмхю инициализируспитмассив 5ог$.кажд0д„;3лсменту
о го исва I . ‚ U ‚ ‚ ‚ '
P P “Basra; значение из диапазона 1—10 и квадрат этого значения,
В п I
Po 'paMMe демонстрируется инициализация двухмерного массивд_
„51п9 System;
Class Squares (
public static void Main‘) {
int{,] sqrs = {
i 1, 1 i, ‘_________(_,,——~J
{ 2, 4 },
Обратите внимание, что каждая
строка имеет свой блок „значений.
ц zluuu д. I -uupuunuu 0 mnax данных И операторах
4
‹ 3, 9 ›‚ -5
( 4, 16 ;,
(5‚ 25 Ъ
( 6, 36 ;‚
( 7, 49 ;‚ 1
1 8, 64 ;‚ ‘
{ 9, 81 },
4 10, 100 1
1.
int i, j;
for(i=O; 1 < 10; :++) I _
Еог(]—0; j < 2; j++) 2
Conso1e.WriLe(sqrsIi,j} + “ "): Ё
Console.WriteLine(); '
M)KC ПОКЗЗЗН РСЗУЛ ЬТЗТ ВЫПОЛНСН ИЯ ЭТОЙ программы.
H ко 00 -J Ф» U1 ‚Ь ш Ю н I -
CD М! Р" Ф -D !-'
5-’ U1 Ch
...-DI! \¢Y...Inl¢-r'.!v.:..~L-4Ar..- ‘at v --.-44 к
О 100
„мы :mnaM.L.m.v.m¢'at..~..qew
Невыровненные массивы
В предыдущей программе демонстрировалось создание двухмерного массива, кото-
рый иначе можно назвать прямоугольньжлт ‚массивом. В табличном представлен ии
прямоугольный массив — это двухмерный массив, у которого длина строк свдинакотза.
Кроме этого, в C# можно создавать специальный тип двухмерного массива, который ‘
называется невыровненн ым массивом. Невыровненный массив — это внешний массив,
состоящий из внутренних массивов разной длины. Следовательно, невыровненный
МНССИВ МОЖНО ИСПОЛЬЗОВЗТЬ ДЛЯ СОЗДЗНИЯ ТЗбЛИЦЬД СОДСРЖЗШСЙ СТРОКИ РЗЗЛИЧНОЙ
ДЛИНЫ.
При объявлении невыровненных массивов для указания каждой размерности исполь-
зуется отдельная пара квадратных скобок. Например, двухмерный невыровненный
массив объявляется следутошиат образом:
type[][] array—name = new type[size][];
3D.€Cb слово size обозначает количество строк в массиве. Размерность строк не "
указывается, и память для них не вьтдслястея. Для каждом строки необходимо
выполнение отдельного оператора new. Такая технология позволяет определять
строки различной длины. Например, в следующем фрагменте кода при объявлении
массива jagged память вьяделяетея только для внешнего массива, а потом при
-..u-no-n:-v «-1- -‚ ..
„ч.
Невыровненные массивы 1 1 9
ВЫПОЛНСНИИ трех операторов FIRM ЯТЬ ВЫДСЛ Я СТСЯ ОТДСЛЬНО ДЛЯ КНЖДОГО ВНуТрСН НСГО
МИСС И ва:
ЦШШ Ё] jagged ’ new int-[3] И;
„ач9ед[0} 2 new int[2};
13gged[1] — new 1nt[3};
Jdgqed[2} new 1nt[4];
HUCIIC ВЫПОЛНЕНИЯ ПОСЛСДОВНТСЪЁТЫКОСТИ ЭТИХ ОПСрНТОрОХЗ НС|П›|рОВНС!*!НЬ!Й МИССИИ
‚„‚н_1я5111'т следующим образом:
| iagged НПО] | | iagged[1l[1] j | J'a99ed[1l[2] |
|1agged [гнет] развесили} |:aggedI2u21j |1aggeo [2l[3l
Расстиотрев эту схему, вы поймете. почему нсвыровнептъжьпй массив гъолучидт такое
название.
Когда нсвыровнеглньпдё массив создан, доступ к сго элементам можно гполучить, указав
каящый индекс в паре квадратных скобок. Например, чтобы присвоить значение I0
‘элементу массива jagged [2] [1] ‚ необходимо использовать следующий оператор:
jagged[2][1] = 10;
Вспомните, что для доступа к элементам прямоугольного массива применяется иная
форма записи.
Ниже представлена программа в которой используется невыровненньпйй двухмерный
массив. В массиве сохраняются данные о количестве пассажиров, пользующихся
услугами маршрутного такси, чтобы добраться до аэропорта. Поскольку маршрутное
такси совершает неодинаковое Количество рейсов в различные дни недели (десять
рейсов в рабочие дни и два по субботам и воскресеньям), для хранения данных можно
использовать невыровнснный массив riders. Заметьте, что вторая размерность для
первых пяти строк имеет значение I0, a для оставшихся двух строк — 2.
I
’ B программе демонстрируется использование невыровненного массива.
using System;
_;ass Ragged (
public static void Main() {
int[]i] riders = new int[7][];
ЗДССЬ внутренние МИССИВЫ
СОСТОИТ ИЗ ЭЛСМСНТОВ.
riders[O] = new int[10];
riders[1] ; new i“t[1O}7 Шшсьвнуцшнниемащтвы
riders[2] e new int[10]; щжпжтиз2элщшнюв
riders[3] = new int[10};
riders[4] « new int[lC];
riders[5] = new int[2};
riders[6] I new int[2};
int i, j;
// Злементам массива присваиваются Некоторые произвольные значения.
Еог(1=0; i < 5; i++)
for(j=O; j < 10; j++)
riders[i][j} 1 i + 3 + 10;
I лава з. I подроонее о типах данных и операторах д
Еог(1—5; i <
for(j;C; 3
riders[i]
Console.WriteLine("Konmqecwso пассажиров, перевезенных за один рейс“+
"\пв будние дни: "); Ё’
Еок(1=0; i < 5; i++) {
КОЛЁС; j < 10; з“)
Console.Wr1te(riders[i][j} + " ");
Console.WriteLine();
}
Conso;e.Wr;:eL;ne();
Соп$о1е.Их1:еЬ1пе("Количество пассажиров, перевезенных за один рейс"+
"\пн выходные дни: ");
for(l45; 1 < 7; 1++) i
for(j—0; j < 2; j++)
Console.Write(riders[i][j} w " ");
Console.WriteL1ne();
}
НСВЬЦЭОВНСННЫС массивы рсдко используются в приложениях, но в некоторых
ситуациях их применение может оказаться весьма эффективным. Например, если вам
требуется очень большой двухмерный иасси в. который будет заполнен не полностью
%T,=.*.*r..v*-vv«..»r.\-Ida‘-'-:»'~ 5-1;.’-Ъ-чв‚тз:„1>5-_е-.6':-ь дм ь ‚щ-„д-„цшаддд ‘j а - ‚т
(TO CCTb B HCM ИСПОЛЬЗУКПСЯ НС ВСЁ ЭЛСМСНТЬП. ТО уД0бН0 ИСПОЛЬЗОВЗТЬ ИМЕННО _
невыровненный массив.
Минутка/д драхма/кум
1. Как указывается каждая размерность для прямоугольных многомерных
массивов?
2. Может ли отличаться длина каждого внугреннего массива в невыровненном
массиве‘?
3. Как инициализируются многомерные массивы?
Присваивание ССЫЛОК на МЗССИВ
Когда значение одной переменной ссылочного типа, которая ссылается на массив,
присваивается другой переменной такого типа, то второй переменной присваивается
ссылка на тот же массив, на который ссылалась первая Переменная. При этом не
создается копия массива и не копируется содержимое одного массива в другой.
B качестве примера рассмотрим следующую программу:
// Присваивание значения одной Переменной ссылочного типа, которая ссылается
// на массив, другой переменной такого же типа.
using System;
1. Размерности, разделенные запятыми, указываются в одних квадратных скобках.
2. Да.
3. Для инициализации многомерного массива список значений каждой размерности помеща-
ewes; в отдельный блок, заключенный в фигурные скобки. Вся эта конструкция указывается
после оператора присваивания.
177
использование свойства Length
class AssignARef (
public static void Main() (
int ii
Lnt[‘ numsl = new 1nt[1O];
intj} nums2 = new int[10];
for(i—O: 1 < 10; i++) numsl{ij е -‚
for(i~O; i < 10; i++) nums2[i‘ = —i;
Соп5о1е.Иг1Ее("Значения элементов массива numsl: ");
for(i=O; i < 10; i++)
Console.Wr1te(nums1[i] + " ");
Console.WriteLine();
Соп$о1е.Иг1Ее("Значения элементов массива nums2: ");
for(i=O; i < 10; i++)
Console.Write(nums2[i} +
Console.WriteLine();
п н);
nums2 = numsl; // После выполнения этого оператора переменная nums2
// ссылается на тот же массив, что и переменная numsl.
Соп5о1е.Иг1Се(“Значения массива, на который ссылается переменная nums2, “+
“\ппосле присваивания ей ссылки: ");
for(i=O; i < 10; i++)
Console.Write(nums2[i] + " ");
Console.WriteLine();
// Теперь можно изменять значения массива, на который ссылается
// переменная numsl, используя переменную nums2.
nums2[3] = 99;
Соп5о1е.Иг1Ее("Значения злементов массива numsl после изменения," +
" выполненного с помощью \ппеременной nums2: ");
for(i=O; i < 10; i++)
ConsOle.Write(numsl[i] + " ");
Console.WriteLine():
Результат выполнения этой программы следующий:
Значения элементов массива numslz O 1 2 3 4 5 6 7 8 9
Значения элементов массива nums2: 0 —1 —2 —3 -4 -5 -6 —7 -8 -9
Значения массива, на который ссылается переменная nums2
досле присвоения ей ссылки: О 1 2 3 4 5 6 7 8 9
Значения элементов массива numsl после изменения, выполненного с помощью
Переменной nums2: O 1 2 99 4 5 6 7 8 9
Как видите, после присваивания значения переменной numsl переменной nums2 обе
Переменные массива ссылаются на один и тот же объект.
Использование свойства Length
B C# массивы реализованы как объекты, и это дает Определенные преимущества при
работе с массивами. Одно из них — возможность использования свойства Length.
‚Щ
178 Глава 5. Подробнее о типах данных и операторах
КНМШЫЙ МЗССИВ ИМССТ ЗССОЦИИрОВЗННОС С НИМ СВОЙСТВО Length. B КОТОРОМ c0[1Cp— I
жится информация о максимальном количестве элементов гиассива (то есть в массиве
(имеется в виду класс) определено поле, содержащее информтиию 0 ЛЛИНС массива)-
Ниже представлена программа, в которой используется свойство Length.
// B программе демонстрируется использование свойства Length.
using System;
;;ass LengthDemo ( ‘
public static void Ma1n() (
int[] llst 2 new int[l0]: :'
int[] nums = { 1, 2, 3 Ъ;
int[][] table = new int[3][]; // Невыровненный массив table.
// Выделение памяти для внутренних массивов. ,j
table[O] = new 1nt[] {l, 2, 3]:
table[l} = new int[] (4, 5]; „
table[2] = new int[] [6, 7, 8, 9]; Ч
Сопзо1е.Ик1сеЬ1пе("Длина массива list = “ + list,Length); :_
Соп5о1е.Ик1ЕеЬ1пе("Длина массива nums " + nums.Length);
Console.WriteLine("HnnHa массива table = " + table.Length);
Соп5о1е.Иг1сеЬ1пе("длина массива table[0] = " + table[0].Length);
Console.WriteLine("nnnHa массива table[1] " + table[l].Lenqth);
Console.WriteLine("nnMHa массива table[2] " + table[2].Length):
Console.WriteLine();
Н
П
Ц
// Использование свойства Length для инициализации массива list.
for(int i=0; i < 1ist.Length; 1++)
list[i] = i * i;
Console.Write("3HaqeHnH элементов массива list: ");
// Использование свойства Length для отображения значений элементов
// массива list.
for(int i=0; i < 1ist.Length; i++)
Console.Write(list[i] + " ");
Console.WriteLine();
1
I
~ —w=r»v'\-=«;:vM»r.v"-It'9"¢&.'
Ниже приведен результат выполнения этой программы.
Длина массива list = 10
Длина массива nums = 3
Длина массива table = 3
Длина массива table[0] = 3
Длина массива table[l] = 2
Длина массива table[2] — 4
Значения элементов массива list: O l 4 9 16 25 36 49 64 81
Особое внимание следует обратить на использование свойства Length B двухмерном
невыровненном массиве table. МЫ уже говорили, что двухмерный нсвыровнснный 3‘
массив — это массив массивов. Следовательно, при использовании выражения
table.Length
использование свойства Length 179
мы получим количество массивов, которые составляют массив tabie (в Данном
C_-,y.,-ac это количество равно 3). Для получения значения штины каждого штутрснк-тсго
пассива во внешнем массиве table необходимо использовать следующее выражение:
'_e.b1e[0]-Length
B данном случае мы получим значение длины внутреннего массива, имеющего индекс О.
обратите внимание на то, что в классе LengthDemo B условном выражении цикла
:-gr используется значение свойства li st . Length. Поскольку каждый массив хранит
информацию о своей длине, ее можно использовать в выражениях. Это позволяет
создавать код, который может работать с массивами различной длины. Значение
свойства Length не имеет ничего общего с количеством элементов, фактически
хранящихся в массиве. Поле Length содержит информацию о числе элементов,
которые может хранить массив.
Использование свойства Length упрощает работу многих алгоритмов, делая более
эффективныгяи некоторые операции с массивами. Например, в следующей програм-
Me при копировании значений одного массива вдругой свойство Length используется
для предотвращения возможности выхода за граничные значения индексов массива
(что может привести к исключительной ситуации во время выполнения программы):
// B программе демонстрируется использование свойства Length для копирования
// одного массива в другой.
using System;
class Асору (
public static void Main() (
int i;
int[] numsl = new int[lO];
int[] nums2 new int[10];
П
‚н.
ч-
for(i=O; i < nums1.Length; i++) numsl[i]
// Копирование массива numsl В массив nums2.
if(nums2.Length >= nums1.Length)
for(i = O; i < nums2.Length; i++)
nums2[i] = nums1[i];
for(i=0; i < nums2.Length; i++)
Console.write(nums2[i] + " ");
heck, свойство Length является ключевой составляющей выполнения двух функций.
Во-первых, значение свойства Length используется для выполнения проверки того,
позволяет ли размер массива nums2 скопировать в него все Элементы массива numsl.
Во-вторых, свойство Length используется в условном выражении цикла for, с
помощью которого осуществляется копирование. Конечно„ это очень простой при-
“Op, НО такая технология может применяться и в гораздо более сложных алгоритмах.
180 Глава 5. Подробнее о типах данных и операторах
Минутами драк/лукум
1. Справедливо ли утверждение, что при присваивапии значения одной перс-
мснной ссылочного типа, которая ссылается на массив, другой такой же
переменной элементы первого массива когшругтотся во второй.
2. Что собой представляет свойство Length’?
Проект 5-2. Класс Queue
&{Odemo_cs
Как вы знаете, структуры данных отличаются по способу организации данных.
Простейшей структурой данных является массив, представляющий собой линейный
список. Для доступа к элементам массива указывается его имя и индекс элемента.
Массивы часто используются в качестве основы при построении более сложных
структур данных, таких как стеки (stacks) И очереди (qucucs). Стек — это список,
доступ к элементам которого осуществляется только по принципу FH.O (НМ in,
last out ~— «ПСрВЫМ вошел, последним вышел»). Очередь — это список, доступ к
элементам которого осуществляется только по принципу FIFO (firsl in. first out —
«первым вошел. первым вышел»). То есть стек можно сравнить со стопкой тарелок —-
самая нижняя тарелка будет использована последней, а очередь можно сравнить с
очередью в банке —— кто первым пришел, того первым обслужат.
Стеки и очереди удобны тем, что в них средства хранения информации объединены
с методами, которые обеспечивают доступ к этой информации. Таки м образом, стеки
и очереди представляют собой механизмы доступа к дигтнылт, в которых хранение и
извлечение данных обеспечиваются самой структурой данных без помощи програм-
мы. Такое объединение, безусловно, соответствует конструкции класса. В этом
проекте будет создан простой класс Queue.
B целом Очереди поддерживают два базовых хтетода — put (поместить) и get
(извлечь). При выполнении каждого метода put элементу массива, находящемуся в
конце очереди (то есть имеющему индекс, соответствующий текущему состоянию
очереди), Присваивается некоторое значение. При выполнении каждого метода get
ИЗ элемента массива, находящегося в начале очереди, считывается значение. Значе-
ние Элемента может быть считано только один раз. Очередь считается заполненной,
если отсутствуют свободные элементы для хранения новых значений, и считается
пустой, если все значения элементов очереди считаны.
Существуют два основных вида очереди — круговая и некруговая. В круговой очереди
при считывании значений «освободившиеся» элементы используются повторно. (На
самом деле элементы не «освобождаются» — просто в соответствии с алгоритмом
работы круговой очереди им «позволено» присваивать новые значения.) В некруговой
очереди этого не происходит, поэтому такая очередь в итоге исчерпывает номера
индексов своих элементов. Для простоты в данном примере мы создаем некруговую
очередь, но, приложив небольшие усилия, вы сможете преобразовать ее в круговую.
1. Нет. Меняется только ссылка.
2. Length —— это свойство, которое имеется в каждом массиве. Оно содержит информацию о
количестве элементов, которые могут храниться в массиве.
использованиесвойстваьеп9П1 181
‘ пошаговая ИНСТРУКЦИЯ
1, Создайте файл и назовите его QDemo . cs.
7 Cyl_L1CCTBy}OT различные способы создания класса Queue, но в данном проекте в
основе структуры очереди лежат массив и методы, npc;1H213nmc1m ые д'ля работы
‘ с Элсиетггаьяи массива. То есть зъшчения, ктоменшемьте в очередь, будут храниться
в Элементах массива. Доступ к Э;1е\1сн'гам этого массива будет осуществляться с
кгоиощью двух индексов. Индекс put]-:..:: уКЕЦЫВНСТ, какому элементу массива
будет присвоено следующее значение, а индекс jretlarui‘ yKEI3bll¥£lC’I, нз какого
элемента массива будет считано следующее значение. B соответствии с конструк-
нисй некруговой очереди после считывания значения какого-либо элемента
повторное считывание его значения не допускается. Создаваемая в данном случае
очередь будет Хранить символы, но такая же конструкция может использоваться
для хранения объектов любого типа. Начните создание класса Queue со следую-
щих строк кода:
71
class Queue (
public char[} q; // ЭТОТ символьный массив является основой очереди.
public int putloc, getloc; // Индексы, указывающие, какому элементу массива
/! будет нрисваиваться значение и из какого
// элемента Значение будет считываться.
Ниже представлен конструктор QL1eue(), который создаст очередь заданной
длины:
Р)
public Queue(int size) 1
q 4 new Char[sizev1]; // Выделение памяти ДЛЯ очереди.
putloc 1 genloc — O;
1
Обратите внимание, что длина создаваемой очереди на единицу больше. чем это
ё указано в переменной size. Такое увеличение длины необходимо для реализации
а. алгоритма некруговой очереди, согласно которому один элемент массива не будет
к. использоваться. Следовательно, для соответствия указы васмой длины очереди с
Ё ее истинной «емкостью» размер массива должен быть на единицу больше. Индек-
сам putloc И geloc изначально присваивается значение О.
4. Добавьте в код класса метод put(), C ПОМОЩЬЮ которого элементам массива
присваиваются значения.
// Помещает символ в очередь.
public void put(char ch) {
1f(putloc=:q.Length—l) {
Console.WriteLine(" т Очередь заполнена.");
return;
putloc++;
q[putl0c} = ch;
}
Метод начинается е оценки условного выражения. Ц K01'0p(m проверяется, зато; —
нена ли очередь. Если значенис переменной putloc равно максимальному
индексу массива q, это означает, что в массиве болынс нет элементов, которым
з можно было бы присвоить значение. Если же в массиве еще есть неиспользован-
‘ H ЫС элементы, то значение переменной putloc увеличивается на единицу, а новое
. .,_ I лава э. I Ionpoonae о типах данных и операТораХ
значение, передаваемое методу put B качестве аргумента, присваивается элементу 3
с индексом, равным значению переменной putloc. CIIC11OB3TCJIbHO, переменная
pUt;lOC всегда Является индексом последнего помещенного в массив элемента.
5. Для считывания значений элементов используется метод get ( ) , представленный
ниже.
// Считывает значения элементов массива (очереди).
public char get() (
if(getloc == putloc) (
Console.WriteL1ne(" т Очередь пустая.");
return (char) 0;
getloc++;
return q[getloc];
}
Обратите внимание, что в самом Начале с помощью условного выражения
проверяется, помещено в очередь какое-либо значение или нет. Если переменные
getloc И putloc имеют одинаковые значения (то есть являются индексами
одного и того же элемента), то очередь является пустой. Поэтому конструктор
Queue () присваивается значение О обеим этим переменным. Затем переменная
getloc увеличивается на единицу, и с помощыо оператора return значение
соответствующего элемента возвращается подпрограмме, вызвавшей метод. Таким
образом, значение переменной getloc всегда указывает позицию последнего
извлеченного Элемента.
6. Ниже представлен весь код программы QDemo. cs.
/it
Проект 5-2.
на»: м ,2
B программе демонстрируется использование класса Queue.
предназначенного для работы с символами.
*/
using System:
Ё
ъ
Ё
class Queue {
public char[] q; // Этот символьный массив является основой очереди.
public int putloc, getloc; // Индексы, указЫвающе‚ какому элементу массива
// будет присваиваться значение и из какого
// элемента будет считываться значение.
public Queue(int size) (
Ч Z new Char[size+l]; // Выделение памяти для очереди.
putloc = 9еп1ос 4 O;
// Помещает символ в очередь.
public void put(char ch) {
if(putloc==q.Length—l) {
Console.writeLine(" — Очередь заполнена.");
ГЗЁЦГП}
putloc++;
q[putloc] = ch;
м мм
Ь.___„
,.-.. ....vu-«» .....—._..-...m
Использование свойства Length
183
// Считывает значения элементов массива (очереди).
public char 9et() {
1f(get1oc == putloc) (
Console.WriteLine("
return (char) O;
Очередь пустая.");
)
getloc++;
return q[getloc];
// Демонстрируется использование класса Queue.
ciass QDemo (
public static void Main() {
Queue bigQ = new Queue(100);
Queue smallQ = new Queue(4);
char ch;
int i;
Console.writeLine("Knacc bigQ используется для хранения символов "+
" алфавита.");
// Помещение 26 символов английского алфавита в очередь bigQ.
for(i=O; i < 26; i++)
bigQ.put((char) ('A' + i)};
// Считывание и вывод на печать значений элементов очереди b1gQ.
Console.write("conepxmmoe очереди bigQ: ");
for(i>O; i < 26; i++) (
Ch = bi<3Q-get();
if(ch != (char) 0) Console.wr1te(ch);
Console.writeLine("\n");
Console.writeLine("TecTMposaHMe очереди smallQ.");
// B ДВННОМ ЦИКЛЕ ВЫПОЛНЯЕТСЯ ПОПЫТКЕ! ПОМЕСТИТЬ значение
// в заполненную очередь.
for(i=O; i < 5; i++) {
Console.write("HonmTKa поместить в очередь символ " + (char) (‘Z'
sma1lQ.put((char) ('2' - i));
Conso1e.writeLine();
Ъ
Conso1e.WriteLine();
~ 1));
// B данном цикле выполняется попытка считать значение из пустой очереди-
Соп5о1е.Ик1се("Содержимое очереди smallQ: ");
for(i;O; i < 5; i++) (
ch 4 smallQ.get();
if(ch != (char) O) ConSole.Write(ch);
1 U4 Глава 5. Подробнее о типах данных И операторах
7. B pc,3y.-'nn'a'rc работы этой программы на экран будут вы веден ы следующие строки;
Класс b.LgQ используется для хранения символов алфавита-
Содержимое очереди одош: двспваондцкьмвогевдтсуихущ
Тестирование очереди SmalLQ. S;
Попытка поместить Ц очередь символ Z :3
Попытка помести- В очередь символ Y
Попытка поместить В очередь символ Х
в очередь символ W
в V
Попытка помес' Tb
Очередь заполнена .
ПОПЫТКЕ ПОМЕСТИ!’ очередь СИМВОЛ
Содержимое очереди smd;;Q: ZYXW Очередь пустая.
8. Попробуйте самостоятсльно молифиттиронать mace пени таким образом. чтобы
в псм использовались другие типы Данных. Наприътсдх пусть значснпщ помешае-
мые в очередь, будут ихтегь тип Lm; или сйоныс.
Цикл foreach
O том, что в C# ОПрСДСЛСН цикл Еогеасп, мы упоминать! в главе 3, а сейчас расскажем
о нем подробно.
Цикл foreach используется для обработки элементов /C()./'I.'l(’I(l.(L(!l. K()J'L']CKL1H$l —— это
группа объектов. B Си определены несколько типов когтлскткттгт, олпни и5 которых
эпзлястлсзт массив. Синтаксис ци юта йогов-ц: выглядит так:
к av-v‘?-.15 0-’,'-"-5.“s‘«v"‘Sfi'v.-o§sa!W:'-jklfizr.‘-'..~x>. ..
fo1each(type Var-name in collection) statement;
Здесь словосочетание type var—r.ame — это тип И имя ztrne/)aI4u0m/oil переменной,
которая будет принимать значение элементов из коллекции при выполнении цикла
foreach. Циклически обрабатываемая коллекция указана словом collection.
B приводимых ниже программах использует/той коллекцией будет массив. Следова-
тельно„ тип type ДОЛЖСН бьпьтакихт же (или совместимым), как базовый тип массива.
Обратите внимание, что при работе с массивом итерационная переменная доступна
только для чтения. Таким образом, вы не можете изменить содержимое массива путем
присваивания итераштоътттоёт переменной нового значения.
I
Ниже представлена простая программа. в которой используется цикл г-жеасл.
Вначале создастся массив целочисленного типа„ элсмстттам которого присваилчатотся
начальные значения. Затем эти значения выводятся на Экран, и одновременно
вычисляется их сумма.
// В программе демонстрируется использование Цикла foreach.
using System;
Class Foreachbemo {
publ;c static void Main() i
int sum r C;
inti] nums new 1n:[lCE;
// Элементам массива nums присваиваются некоторые начальные ЗпаЧ@НИЯ.
for(int х е O; 1 < 10; ;++)
nums[1] i;
__‘.-#1,‘... 9.,‘ _ V,
// Использование цикла foreach для вывода на экран значений
Цикл foreach 185
"/' BCCX элементов массива И ПОДСЧЕШЗ ИХ суммы.
ioreach (LU: X in nums) { Обработка массива nums с помокцью цикла E'uLe:3<:h.
ConsoLc.WriteL1ne("3uaueHMe элемента массива ~ " L X);
sum +~ xi
Cgnsole.WriteLine("CYMMa значений всех элементов массива " + sum);
l)‘_~5y_Ih’l.'IT libll‘lOJ]HCHH$l ЭТОЙ ПрОГрЗПМЫ ВЫГЛЯДИТ С.Т|СЦ}/`ЮЦ1И\1 ОбрЭ-ЗОЧЁ
элемента массива — О
элемента массива ; 1
элемента массива — 2
элемента массива — 3
n-JHEHMC элемента массива - 4
‚ьецие элемента массива 5
элемента массива ; 6
элемента массива — 7
элемента массива 8
'.3чение элемента массива = 9
;ymMa значений всех Элементов массива — 45
Как видите, обработка элементов массива с помощью цикла foreach производится
в порядке возрастания их индексов.
С помощью Цикла foreach можно также обрабатывать Много-исрньпс массивы. При
этом ЗНЭЧСНИЯ ЭЛСМСНТОВ возврацппотся ПО строкам «— от первого до последнего
'_)_1C\1CH’I2l в каждом строке.
B гкрограмме демонстрируется ИСПОЛЬЗОВЗНИС Цикла fofeach для обработки
ЭЛЕМОЬТОВ двухмерного массива.
-h§ System;
c-nss ForeachDemo2 {
publlc static void Main() {
int sum 2 О;
;nt{,] nums 2 new int{3,5];
// 3H€M€HTaM массива nums присваиваются некоторые значения.
for(int i — О: ; < 3: i++)
for(1nt J;C; 3 < 5; 3+*)
nums{i,j] — (1+1)*(j+l);
// Использование цикла foreach ДЛЯ вывода на экран значений
// всех элементов массива И подсчета их суммы_
£ureach(in: х LU nums) {
Console.Wri:eL1ne("3HaucHMc элемента массива — " T x);
sum +4 x:
1
д
Conso1e.WriteL;ne("CyMMa значений всех элементов массива — " + Sum):
)\ т — › . “ . ‚ ‚ ‚ . .
I K5}/..7IbTaT BbIIlOJHClHrI}l ЭГОИ npOIpdM.\lb1 dHd.IOT ИЧСН ПРСПГ›ПД_УП‚ПС\1У.
Ёьачение элемента массива I
’ элемента массива
элемента массива I
элемента массива I
Значение элемента массива «
.I;uL;.)l\Jr’
U’?
186
Значение
Значение
Значение
Значение
Значение
Значение
Значение
Значение
Значение элемента массива —
Значение элемента массива 15
Сумма значений всех элементов массива 90
элемента
злемента
элемента
элемента
элемента
элемента
элемента
элемента
массива
массива =
массива =
массива
массива
массива
массива —
МЗССИВВ
Н Ф m ш H m m Ь Ю
N
Поскольку с помощью цикла foreach массив можно обрабатывать только от начала до
конца. может сложиться впечатление, что возможности этого цикла ограничены. Но это
не так. Многие алгоритмы требуют именно такого механизма обработки. Ниже пред-
ставлен вариант класса MinMax, C которым мы уже работали в этой главе. Напомним,
что он предназначен для нахождения минимального и максимального значений элемен-
тов массива.
/* В программе демонстрируется использование цикла foreach для нахождения
минимального и максимального значений элементов массива.
*/
using System;
class MinMax (
public static void Main() (
intl] nums = ( 99, —10‚ 100123, 18, —978,
5623, 463, -9, 287, 49 };
int min, max;
min = max = nums[O];
foreach(int val in nums) {
if(val < min) min = val;
if(val > max) max = val;
}
Console.W:iteLine("MMHMManbHoe значение = " + min +
" Максимальное значение = " + max);
}
B этой программе ЦИКЛ foreach работает прекрасно, поскольку нахождение мини-
мального и максимального значений требует проверки всех элементов. Кроме того,
ЦИКЛ foreach используется ДЛЯ вычислений среднего значения. поиска значения или
элемента и копирования массива.
<99 Маму/плыл тракте/кум
I. Какие функции выполняет Цикл foreach?
Можно ли с помощью цикла foreach осуществлять доступ к элементам
массива в обратном порядке — от наибольшего индекса к наименьшему?
Можно ли использовать Цикл fo reach для присваивания значений элементу
массива‘?
С помощью Цикла foreach осуществляется обработка элементов массива.
Нет.
Нет,
2.
3.
Глава 5. Подробнее о типах данных и операторах _`
зим.
C/T£.fl_*_ 187
строки
С точки зрения решения повседневных задач программирования одним из наиболее
важных в C# ЯВЛЯСТСЯ тип данных string. Во многих языках программирования
ст рока — это массив символов, но в C# строки являются объектами. Следовательно,
mu ЦЕННЫХ string В C# ЯВЛЯСТСЯ ссылочным типом.
Объекты Типа string использовались в Программах с Первой главы этой книги,
потому что при создании строкового ‚Читераяа фактически создавался объект типа
5- ': mg. Например. в следующем операторе
console.WriteL1ne("B C# строки являются объектами.");
строка В С# строки являются объектаьпъ автоматически становится объектом
гппа string. Следовательно, Класс string В предыдущих программах использовался
НСЯВНО. В ЭТОМ разделе МЫ расскажем, как оперировать такими данными ЯВНО. HO
поскольку класс string достаточно большой, здесь будут рассмотрены только его
основньп:характеристики.
Создание объектов класса String
Самым простым способом создания объекта типа string является использование
строкового литерала. Например, в представленном ниже операторе переменная str
является переменной ссылочного типа string, которой присваивается ссылка на
строковый литерал.
string st: = "Зто строковый литерал.";
В этом случае переменная str инипиализируется символьной последовательностью
Это строковый литерал.
Вы также можете создать объект типа string ИЗ массива типа char. Например,
П
ддаГ[] Str (.t.I .e.’ ‚З.‘ .t.};
s:ring st: = new string(str);
После создания объекта типа string его можно испол ьзовать в любом месте вашей
программы, где разрешено применение строки в кавычках. Например, можно ис-
пользовать объект типа string B качестве аргумента метода WriteLine(). Как
показано в следующей программе.
B программе демонстрируется создание объектов типа string.
ising System;
Lass StringDemo I
public static void Ma1n() (
char{] charray : (нэп, IT!’ loll I ч’ гсг‚_гтг, при, го!’ тку’ на!’ I н},
string strl = new string(charray);
string str2 = "Еще одна строка.";
Console.Wr1teLine(strl); CoumHuco6baaoBTnnastringnamammna
COnSole_writeLine(Str2); ппшснакиизешощшопдлшсшша
1 88 Глава 5. Подробнее о типах данных и операторах -
B результате работы данной программы будут вывсденьк следующие строки:
Это строка.
Еще одна строка.
Операции со строками
КЛНСС string СОДСрЖИТ ДОСТНТОЧНО ООЛЫЫОС КОЛИЧССТВО ПСТОЦО8 ДЛЯ РИООТЫ C0
СТрОКНМ И. Hcpclmc_.'m_\1 некоторые ИЗ НИХ.
g ‚(т)‘ Возвращает копию переменной str
' н I Возвращает число меньше ноля, если значение вы-
:9’ зывающей строки меньше значения переменной sir;
3; возвращает число больше ноля, если значение вы-
- ЗЫВЗЮЩЭЙ строки больше значения переменной str;
.. возвращает ноль, если строки равны
int IndexOf (string str) Выполняет поиск в вызывающей строке подстроки,
указанной в качестве параметра str. Возвращает
индекс позиции в вызывающей строке, где первый
раз была обнаружена подстрока, или значение -1,
если подстрока не была обнаружена
1n t LastIndexOf (string str) Выполняет поиск в вызывающей строке подстроки,
указанной в качестве параметра str. Возвращает
индекс позиции, где последний раз былаобнаружена
подстрока, или значение -1, если подстрока не была
обнаружена
Объекты типа string имеют также свойство Length, содержащее информацию о
длине строки.
Для получения значения отдельного символа строки необходимо использовать его
Индекс. Например,
string str - "test";
Conso1e.WritrLine(str[O]);
B рсзулвппе ВЫПОЛНСНИЯ ВТОРОГО оператора ОУДСТ ВЫВСДСН СИМВОЛ t. Как И В
массивах, ПСрВЫЙ ЭПСМСНТ СТрОКИ ИМССТ ИНДСКС 0.140 ИСПОЛЬЗОВНТЬ ПНДСКС ДЛЯ
ПРИСВОСНИЯ СИМВОЛУ В пределах СТрОКИ НОВОГО ЗНЭЧСНИЯ НСЛЬ3Я.'4НДСКС МОЖЮТ
ИСПОЛЬЗОВЭТЬСЯ ТОЛЬКО ДЛЯ ПОЛУЧСНИЯ СИМВОЛЭ.
Для сравнения двух строк можно использовать оператор ==. Обычно, когда этот оператор
Применяется к ссылкам на объект, он определяет, на один ли объект они ссылаются.
С объектами типа strinq дело обстоит иначе. Когда оператор == применяется к двум
ссылочным переменным типа strlng, то проверяется содержимое самих строк. То
же самое справедливо для оператора !:; когда он используется при сравнении
объектов типа string, то сравнивается содержимое самих строк. Однако другие
Операторы сравнения (например, < или >:) сравнивают ссылки, как и при работе С
объектами иных типов.
Ниже представлена програмзта. в которой демонстрируется использование несколь—
ких операций со строками.
7/ В Нрограмме демонстрируется использование некоторых операций со строками.
using System;
Строки
189
;LdsS Strcps {
public s:aLic void Ma1n() {
string strl ~ "Применение некоторых операций со строками.":
s:ring.Copy(strl);
о строковый литерами";
sLring SE12
str;ng s:r3 2
LUZ result, дох:
оки sir; т +5пг1.Ьеп9пЬ
Соп5о1е.Иг1СеЬЬпе("Длина с‘
// С помощью цикла for на экран выводится строка sL:1
/,' по одному Символу за проход цикла.
£or(;nt 120; i < str1.LengLh; iT+)
Console.Writc(strl[1});
Cunso1e.Wr1tcLine();
;f(strl 4/ SEr2)
Cunsole.WriLeLine("strl ее 5пг2");
else
Consule.WriCeL;ne("strl !— str2"):
1f(strl —: s:r3)
Cunsule.WriteLine("str1 ее str3");
else
Console.WriteL1ne("strl != s:r3");
resuit : strl.CompareTo(s:r3);
if(result —— O)
Conso1e.WriteLinc("Cw оки 5Ьг1 и s:r3 3впы.");
Р Р
else i£(resul, < О)
Соп5о1е.Иг11еЬ;пе("Строка strl меньше, чем str3.");
else
Console.WriteLinc("cwpoxa зап; больше, чем s:r3.");
// Присвоение нового строкового литерала переменной sLr2.
str2 : "Один Два Три Один.":
Соп$о1е.И:1СеЬ1пе("Один Два Три Один.");
idx 2 5Ег2.1пёехОЕ("Один");
Соп$о1е.Щг1ЕеЬ1пе("Индекс первого вхождения подстроки Один " 4
"в строку str2: "г idx);
дох 2 s:r2.LastlndexOf("OumH");
Соп5о1е.Иг1ссЬ;пе("Индекс последнего вхождения подстроки Один " +
"в строку 5:: " - lax);
Результат выполнения программы следующий:
Ёдина строки 5:г1 = 42 символа.
.Эимеьение некоторых операций со строками.
; 1 == str2
Lrl Ее s:r3
эока 3:г1 меньше, чем 5пг3.
‘мин Два Три Один.
*1екс первого вхождения подстроки Один в строку s:r2: О
дйдекс последнего вхождения подстроки Один в строку str2: 13
СИМВОЛЫ .
"):
1 90 Глава 5. Подробнее о типах данных и оператора}
С помощью оператора + вы можете выполнить конкатенации) строк (состыковать их
вместе). Так, при выполнении блока операторов ‚
string strl = "Конка"
string str2 = "тен";
string str3 = "ация“;
string str4 = strl+str2+str3;
переменной strfl. присваивается строковый литерал конкатенации.
Массивы строк
Как И 11pyrMe ТИПЫ ДЗННЫХ, СТРОКИ МОГУТ бЫТЬ ЗНИЧСПИЯМИ ЭЛСМСНТОВ массива
Например, в следующей программе значениями элементов массива str являются
ЧСТЫрС СТрОКОВЫХ литерала.
// В программе демонстрируется использование массива, значениями элементов
// которого являются строковые литералы.
using System;
Строковый массив.
class StringArrays {
public static void Main() (
stringl] Str : (“Это“‚ "пример", "строкового", “массива.“};
Соп$о1е.И:1пеЬ1пе("Первоначальный массив: ");
for(int i=0; i < str.Length; i++)
Console.Write(str[i] + “ ");
Console.WriteLine("\n");
// Изменение значений строкового массива.
str'1] — "тоже";
str[2] : "строковый";
str[3] = “массив.“;
Соп$о1е.И:1пеЬ1пе("Измененный массив: ")‚
for(int i4O; i < str.Length; i++)
Console.Write(str{i] + " ");
Ъ.
РСЗУЛЬТЭТ ВЫПОЛНСНИЯ программы ВЫГЛЯДИТ СЛСДУЮШИМ o6pa30M:
Первоначальный массив:
Это пример строкового массива.
Измененный массив:
Это тоже строковый массив.
Неизменность строк
При работе со строками следует обратить внимание на очень важную характеристику
объектов типа string — однажды созданная последовательность символов в строке
не может быть изменена. Хотя такое ограничение на первый взгляд кажется серьез-
ным недостатком, на самом деле это не так. Когда вам потребуется строка, отличная
от уже существующей, то вы просто создадите новую строку. содержащую необходимые
изменения. Поскольку неиспользуемые строковые объекты автоматически удаляются из
памяти во время «сборки мусора», старые строки будут незаметно для вас удалены.
«мы».
Строки 1 91
Зндцение переменной ссылочного типа, которая ссылается на объект типа string,
безусловно, можно изменить. Но после создания нового объекта типа string его
Содержимое изменить уже нельзя.
gm изменения строки используется метод Subst ring ( ) , возвращающий новую
строку, которая содержит копию указываемой части вызывающей строки (строки,
Которая указывается слева от оператора точка (.)). Поскольку создается новый объект
типа st ring, содержащий подстроку, то первоначальная строка остается неизменен-
Hon. Далее мы будем использовать следующий синтаксис метода Substring ():
по 5ubstring(int startindex, int len)
5L1--
ЗДССЬ слово startindex — индекс элемента, с которого метод будет начинать
когпированис значений (символов). А с помощью слова len указывается длина
коннрустяои подстроки. Ниже представлена программа, в которой демонстрируется
использование метода Subs t ring ( ).
// B программе демонстрируется использование метода Substring().
using System;
class Substr (
public static void Main() (
string orgstr = "Регистрация";
В этом операторе создастся новая строка,
содержащим;необходимук)подстроку
// construct a substring
string substrl = orgstr.Substring(O, 7);
string substr2 L orgstr.Substring(6, 5);
Соп$о1е.И:1ЕеЬ1пе("Строковый литерал orgstr: " + orgstr);
COnsole.WriteLine("C'I'po1<oBI=HZ литерал substrl: " + substrl) ;
Console.WriteLine("CTpoKoBufi литерал substr2: " + substr2);
fr
Результат выполнения этой программы следующий:
Строковый литерал orgstr: Регистрация
Сдроковый литерал substrl: Регистр
Скроковый литерал substr2: рация
Первоначальная строка orgstr остается неизменной, а переменные substrl И
';:bstr2 содержат подстроки.
Мину/ладьи? лрактакум
1. Справедливо ли утверждение, что в C# все строки являются объектами?
2. Каким образом можно получить значение длины строки?
3. Какие функции выполняет метод Substring ( ) ‘.’
Да.
Значение длины строки можно получить е помощью свойства Length.
Метод Substring() конструирует (возвращает) новую строку, являющуюся подстрокой
вызывающей строки. '
ьэ!и_.
192 Глава 5. Подробнее о типах данных и оператор .
‚ юшка...
-ь...›......-..д ‘
i?%“7_: ответы лрвфессееееаа-«ПП-
<3 Bonpoc. ВЫ сказали, что после создания объекты типа вещие; остаютдя:
неизменными. Понятно, что с практической точки зрения это не слишком серьезное;
ограничение. Но все-таки, существует ли возможность создавать строку, которую
..-w().)4(,'H() изменят ь?
Ответ. Это возможно, В Ci? предусмотрен класс Зсщпевщ lder, находящий-‘
ся в пространстве имен s;stem.'1“ext. C помощью этого ютасса создаются строковые
объекты, которые можно изменять. Однако для рсцпения большинства задач ивам,
понадобятся объекты класса зггтпд, а не Str1nqBu1lde:.
Побитовые операторы
B главе 2 были рассмотрены арифметические операторы, операторы сравнения и
логические операторы C#. Эти операторы используются чаше всего, но в C#
предусмотрены и дополнительные операторы, расширяющие возможности этого
языка — побитовые операторы. Побитовые операторы работают непосредственно с
битами своих операндов. Опсрандами побитовых операторов могут быть только
цслочисленн ые значения. Эти операторы не могут использоваться для значений типа
bool, float или double либо для объектов каких—нибудь классов.
Такие операторы называются побигповььии, поскольку используются для проверки,
установки или сдвига битов, которые Составляют целочисленное значение. Побито-
вые операции важны шхя решения многих задач программирования на системном
уровне (например, когда необходимо гтолучение информации о состоянии устройст-
ва). Побитовые операторы перечислены в табл. 5.1.
Побитовые операторы AND, OR, XOR И NOT
ПО6ИТОВЫС операторы AND, OR, XOR И NOT обозначаются соответственно, а, д, ^ и ~.
ОНИ выполняют те жс операции, что и их булевы логические эквиваленты, описанные
в главе 2. Отличие состоит в том, что побитовые операторы воздействуют на операнды
на уровне битов. В приведенной ниже таблице представлспьт результаты выполнения
каждой операции с использованием единиц и нолсй.
р q p&q plq р^ч ~p
О О 0 0 О 1
1 0 О 1 1 О
О 1 0 1 1 1
1 1 1 1 О 0
Побитовый оператор AND можно представить как средство для сброса битов. То есть
если какой-либо бит в одном из операндов имеет значение 0, то соответствующий
ему бит в результате всегда будет имсть значение 0. Например,
11010011
10101010
10000010
. ‚. Пни
- жмешь, .
„побитовые операторы
таблица 5.1. Побитовые операторы
оператор Описание
д‘ Побитовый оператор AND
l Побитовый оператор OR
А ПобитовЬ|Й оператор XOR
>7 Оператор сдвига вправо
<< Оператор сдвига влево
„ Унарный оператор NOT
X!
B сдеДуЮШСЙ программе демонстрируется использование оператора &. Программа
преобразует символы нижнего регистра в символы верхнего регистра с помощью
персустановки шестого бита в 0 (сброс шестого бита). Как определено в стандартном
наборе символов Unicode/ASCII, значение символов нижнего регистра больше соот-
ветствующих значений символов верхнего регистра на число 32. Следовательно,
чтобы преобразовать символы нижнего регистра в символы верхнего регистра,
достаточно сбросить шестой бит, как это показано в программе.
I
‚д программа преобразует символы нижнего регистра в символы верхнего
// регистра.
using System;
class UpCase (
puoLic static void Main() (
char ch;
for(int i:O; i < 10; i++) {
ch = (char) ('a' + i);
Console.Write(ch);
// При выполнении этого оператора сбрасывается шестой бит.
ch = (char) (ch & 65503); // Теперь значением переменной ch ЯВЛЯЕТСЯ
// символ верхнего регистра.
Console.Write(ch + "
H);
B результате ВЫПОЛНСНИЯ ЭТОЙ программы на экран будут выведены СЛСДуЮШИС пары
символов;
ад DB CC dD еЕ fF gG ЮН ii jJ
Число 65503, используемое в операторе AND, имеет следующее двоичное представле-
ние: 1111 1111 1101 1111. Следовательно, операция AND оставляет без изменений
Все биты значения переменной ch, кроме шестого, который устанавливается в 0.
Операция AND также используется, когда необходимо определить, установлен или
Сорошен бит. Например, в приведенном ниже операторе производится проверка.
Зстановгкен ли четвертый бит в значении переменной status.
<f(status & 8) Соп$о1.Иг1СеЬ1пе("Четвертый бит установлен.");
Ёначение 8 в этом операторе используется потому, что в двоичном представлении
Него числа установлен только четвертый бит (0000 1000). Следовательно, оператор
- ач I лава а. I юдроонее о типах данных и операто
if M0)KCT иметь положительный результат только в том случае, если в значен
переменной status также установлен четвертый бит. В следующей программе так
подход используется для вывода на экран двоичного Представления значения ти
byte. F»
// программа выводит на экран двоичное представление числа, имеющего тип byte.
Using system;
class Showflits {
public static void Main() (
int C; Ч
ауре val;
B цикле [проверяется каждый бит числа 123;
val = 123;
form-_:128; t > O; t 2 t/2) {
if((val & t) != О) Console.Write("l ");
else Console.Write("O "); Ё
1 ъ
Результат выполнения этой программы следующий:
0 1 1 1 1 0 1 1
В циюте for с помощью побитового оператора AND проверяется каждый биТ значени
переменной val. Если бит установлен, то на экран выводится 1; ссли бит сброше
на экран выводится О. В Проекте 5—3 вы увидите, как можно расширить эту базову _
концепцию опроса битов для создания класса, в котором бы определялись И ВЫВО‚
дились На Экран биты любого Значения целочисленного типа.
В противоположность оператору AND ПОбИТОВЫЙ оператор OR может использоватьс:
для установки битов. Если хотя бы в одном из операндов бит установлен, Tq
соответствующий бит в значении переменной также будет установлен. '
11010011
10101010
11111011
ilim преобразования символов верхнего регистра в символы нижнего регистра мы
можем задействовать оператор он, как это показано ниже:
I.
I’ Программа преобразует символы верхнего регистра в символы нижнего регистра.
Uslng System;
Class LowCase {
Public statlc void Main() 4
char ch;
for(int i=0; i < 10; i++) {
ch = (char) ('A' + i);
C0nsole.Write(ch);
// При выполнении этого оператора устанавливается шестой бит.
Ch ‘ (Char) (ch I 32): // Теперь Переменная ch содержит символ
// нижнего регистра.
‘подитовые операторы 195
console.Wr1te(ch + " ");
}
Ннжс ПОКНЗЗН результат ВЫПОЛНСНИЯ программы.
Ад во Сс Dd Ее Ff Gg Шт Ii Jj
B программе операция OR применяется к каждому значению символа и значению 32,
которое в двоичном представлении имеет вид 0000 0000 0010 0000. Следовательно, в
двоичном представлении числа 32 yCT’clHOBJCH ТОЛЬКО шестой бит. Если операндамьт
побитового оператора OR являются число 32 И любое Другое число, то в результате
мы получаем значение, в котором шестой бит установлен, а все остальные биты
остаются неизмененньтми. В результате выполнения описанных вьнце действий в
Этой программе каждый символ верхнего регистра превращается в соответствующий
символ нижнего регистра.
При выполнении операции XOR бит устагтатппнвастся только в гом случае, если
сравниваемые биты различны. Например,
01111111
10111001
А ...................... --
11000110
Оператор XOR имеет очень интересное свойство, которое позволяет использовать его
для шътфровки сообщений. Если этот оператор применить к значениям X И Y, a затем
к полученному результату и значению Y (вновь), то после выполнения второй
операции будет получено значение Х. То есть в последовательности операторов
Rl—X"Y;
элемент R2 имеет то же значение, что и X. Таким образом, результатом выполнения
последовательности двух операций XOR с использованием одного значения будет
первый операнд. Этот принцип можно использовать при создании простой програм-
мы для шифрования сообщений, в которой некоторое целочисленное значение будет
являться ключом, используемым как для шифровки, так и для расшифровки сооб-
Шсния с помощью оператора XOR. Для шифровки сообщения оператор XOR приме-
няется первый раз, в результате мы получаем шифрованный текст. Для расшифровки
оператор XOR применяется во второй раз, в результате мы получаем первоначальный
Гскст. Ниже приведен код этой программы.
{I B программе демонстрируется использование оператора XOR для шифровки и
' расшифровки сообщения.
ШЗ1п9 System;
C;3SS Encode {
Public static void Main() {
string msg : "Совершенно секретно! Апекс Юстасу.";
string encmsg = "";
string decmsq = "";
int key — 88;
С0П5о1е.Иг1Ее("Первоначальное сообщение: ");
1 Sb Глава 5. Подробнее о типах данных и оператора
Console.WriteLine(msg);
// Шифрование сообщения. Ё
for(int i=0; i < msg.Length; i++) 5
encmsg = encmsg + (char) (msg[i] ^ key);
Соп5о1е.Иг1Се("Зашифрованное сообщение: ");
Console.WriteLine(encmsg); т
// Расшифровка сообщения.
for(int i=0; 1 < msg.Length; ;~+)
decmsg = decmsg + (char) (encmsgiil ^ key); i
Соп5о1е.Иг1се("Расшифрованное сообщение: “); _
Console.WriteLine(decmsg); Ч
} 1
В результате выполнения этой программы на экран будет выведено как зашифрован-
ное, так и расшифрованное сообщение:
Первоначальное сообщение: Совершенно секретно! Алекс Юстасу.
Зашифрованное сообщение: ????ИА????хЙ??И?К??ухш???Йх?ЙК?ЙЛм
Расшифрованное сообщение: Совершенно секретно! Алекс Юстасу.
Как видите, в результате выполнения двух операции XOR с использованием одногсд
и того же кгяюча мы получаем расшифрованное сообщение. Ё
Унарный оператор NOT изменяет все биты операнда. Например, если некое ucno-mc-3|
ленное значение A имеет битовое представление I001 OI 10, в результате выполненизё
операции «А мы получим значение е битовым представлением 0110 1001.
B следующей программе демонстрируется работа оператора NOT. Ha экран выводитсгд
двоичное представление числа -34 и двоичное представление значения, полученного;
в результате применения K этому числу оператора NOT.
// E программе демонстрируется использование унарного оператора NOT.
using System;
class NotDemo {
public static void Main() {
sbyte b L -34;
for(int t=l28; t > O; t A t/2) {
if((b & t) !: O) Console.Write("1 ");
else Console.Write("C ");
I
Console.WriteL1ne();
// Изменение значений всех битов числа на противоположные.
b = (Sbyte) ~b;
for(int t=l28i t > Oi E = t/2) {
if((b & t) != 0) Console.Write("l ");
else Console.Write("O ");
„ь г у... дчсщддота. „
__ _‚.‚‚.„„.
б побитовые операторы
13/
Pe3y_1bTaT выполнения этой программы имеет вид:
операторы сдвига
В Св существует возможность сдвигать биты, составляющие значснис, влево или
„право на заданное число позиций. В Сд определены два оператора сдвига битов:
<< Сдвиг влево
,> Сдвиг вправо
Общий синтаксис этих операторов следующий:
Удддд << num~bits
va;ue >> num—b1ts
ЗДССЬ слово value — значсние, в котором биты сдвигаются на опрсделеннос число
позиций, указанное вместо словосочетания num—-bits.
Сдвиг влево Приводит к перемещению Всех битов указанного Значения влево на одну
позицию, при этом освободившиеся младшие биты заполняются нолями, а соответ-
ствуюшее количество старших битов теряется. Сдвиг вправо приводит к перемеще-
}ЛПО ВСЕХ бИТОВ Ha ОДНУ ПОЗИЪЦПО ВПРЭВО, при STONL ССЛИ вправо СДВИГЭКЛСЯ бИТЬ
беззнакового значения, старшие биты заполняются ногтями, а соответствующее
КОЛИЧЕСТВО МЛаДЦ1ИХ бИТОВ ТСрЯСТСЯ. В СЛУЧЭС сдвига вправо бИТОВ ЗНЗКОВОГО ЦСЛО-
ЧНСЛСННОГО ЗНЭЧСНИЯ ЗНЭКОВЫЙ бИТ сохраняется (СТЗрЦЛИС бИТЫ ЗЗПОЛНЯЮТСЯ (ЕДИНИ-
цахти). В представлении отрицательного числа старший бит всегда имеет значение I.
B следующей программе демонстрируется результат выполнения операции сдвига
битов как вправо, так и влево. В первой части программы переменной типа int
задается начальное значение 1, это означает, что установлен только его младший бит.
Далее к этому значению применяется восемь операций сдвига, после выполнения
которых на экран выводятся младшие восемь битов. Во второй части программы этой
же переменной присваивается значение 128, к которому тоже применяется серия из
восьми сдвигов, но уже вправо, с выводом результатов выполнения на экран.
B программе ДЕМОНСТрИРУЕТСЯ ИСПОЛЬЗОВЗНИЕ операторов сдвига << И >>.
uS;Hg System;
C.ass ShiftDemo {
public static void Main() {
int val = 1;
for(int i = 0; i < 8; i++) {
for(1nt t~l28; t > O; t = t/2) {
if((va1 & t) E: O) Console.Write("1 ");
else Console.Write("O ");
}
Console.WriteL1ne();
val : val << 1; // Сдвиг влево.
)
Console.WriteLine();
val — 128;
for(int 1 : O; i < 8; 1++) {
| Лава 5- Подробнее о типах дгННЫХ И One!-'>3T0F
t 4 t/2) i
Conso1e.Wr1te(“1
");
for(int t=128; t > О;
if((va1 & t) !~ О)
else Conso1e.Write("0
}
Conso1e.WriteLine();
val val >> 1; // Сдвиг вправо.
H):
}
H1-‘l)KC показан результат, демонстрирующий порядок выполнения операций сдвига
ООООООО1
ОООООО1О
00000100
00001000
00010000
00100000
01000000
10000000
10000000
01000000
00100000
00010000
00001000
00000100
00000010
00000001
ответы лрофессаоналд
(3 Bonpoc. Двоичное представление данных основано на степени числа
Могут ли Операторы сдвига применяться для умножения или деления целочисленных;
значений на 2?
.' VI:“- *-3..:v-.\>‘«*¢.»'<~3-.-.$1"
Q
„мы
Ответ. Да. Побитовые операторы могут использоваться для выполненияё
очень быстрого умножения или деления на 2. Сдвиг влево удваивает значение. Сдвиг1
вправо уменьшает значение в два раза. Учтите, что результат‘ будет верным, если при `
выполнении операции сдвига биты не сдвигаются за пределы двоичного представле- ч
ния этого числа.
.ц‹=т›\:;‚т-;4>„к --
Составные побитовые операторы
Все двоичные побитовые операторы могут использоваться одновременно C операто-
ром присваивания. Например, в обоих операторах, представленных ниже, перемен-
ной х присваивается результат операции XOR, операндами в которой выступают
значение переменной х и число I27.
= Х ^ 127;
х
X 127;
^=
и
' побитовые операторы 1 99
poem‘ 5-3. Класс ShowBits
5howBitsDemo.cs
B :)TO\’I проекте создается ютасс ShowBits, позволяющий выводить на Экран двоичное
Представление любого целочисленного Значения. Такой класс может успешно ис-
„оль3отз21ться в программировании. Например, если вы занимаетесь отладкой кода
“шивера устройства, То. используя ‘этот класс, сможете наблюдать поток данных в
‚ШОНЧНОМ предета ВЛСПИ И.
пошаговая ИНСТРУКЦИЯ
1_ Создайте файл и назовите его ShOWE-,‘.CSD€I’1"O.CS.
2_ Начните создание шасса 5помВ1с$ с определения конструктора этого класса:
class ShowBits {
public int numbits;
public ShowBits(int п) {
numbits = п;
В классе ShowBits создаются объекты, предназначенные для отображения ука-
занного количества битов. Например. для создания объекта, который будет
выводить младшие восемь битов какого-либо целочисленного значения. необхо-
димо использовать следующий оператор:
ShowBits b = new ShowBits(8);
Число битов, которые должны быть отображены. указывается конструктору в
качестве аргумента. Его значение присваивается переменной numbits.
Ь)
Для вывода битового представления числа в классе showBits определен мстод
злом ( ) ‚ код которого показан ниже.
рцЬ11с vold show(ulong val) {
ulong mask = 1;
// Сдвиг маски (числа 1)
mask <<: numbits—l;
B нужную позицию.
int spacer = O;
for(; mask != О; mask >>= 1) {
if((val & mask) != 0) Console.write("l");
else Console.Write("O"):
spacer++;
if((spacer % 8) Ф: O) {
Console.Write(" ");
spacer = O;
}
Console.writeLine();
}
Обратите внимание, что метод show() имеет один параметр типа ulong. Но это не
означает, что методу show () всегда нужно передавать значение типа ulong. „ОСКОЛ ь-
Ку в C# предусмотрено автоматическое приведение типов, методу show () MO)KCT быть
передано значение любого целочисленного типа. Колътчесттзо выводимых битов
. ........ ... . .u.....zuur1ee о типах данных и операто ›
указывается значением переменной numbits. После каждых восьми битов мет
show() выводит пробел для облегчения чтения двоичных значений длины
I4 ‘
бИТОВЫХ комбинации.
}4иЖсгЦюДСТашпи|вссьКоД np0rpaMMbIShowBitsDemo.
/t
Проект 5~3.
Класс, который предназначен Для отображения двоичного представления
ЦЕЛОЧИСЛЗННОГО значения.
*/
using System;
class ShowBits {
public int numbits;
public ShowBits(int n) {
numbits : n;
public void show(ulong val) (
ulong mask = 1;
// Сдвиг маски (числа 1) B нужную позицию.
mask <<= numbits-li
int spacer = O;
for(; mask != O; mask >>: 1) i
if((val & mask) != O} Console.Wr;te("l");
else Console.write("O");
spacer++;
if((spacer % 8) == О) {
Console.Write(" ");
spacer 2 O;
}
Console.WriteLine():
// Использование класса ShowBits.
class ShowBitsDemo 1
public static void Main() {
ShowBits b = new ShowBits(8);
ShowBitS i = new ShowBits(32);
ShowBits li — new ShowBits(64);
Console.WriteLine(”EBoMqHoe представление числа 123: ");
b.show(123);
Console.WriteLine("\nflHoMqHoe представление числа 87987: ");
i.show(87987);
Console.WriteLine("\nflsomuHoe представление числа 237658768: "
li.show(237658768);
камеди I! .
.. к; ...„.. т“.
.0пера|ир .’
// C помощью объекта b класса ShowBits МОЖНО вывести на экран
// младшие биты любого целочисленного значения.
Соп5о1е.Иг1сеЬ1пе("\пМладшие 8 битов двоичного представления числа " +
" 87987: ");
b.show(87987);
5_ Ниже показан результат выполнения программы зпошвьсеэегпо.
Двоичное представление числа 123:
01111011
двоичное представление числа 87987:
СООСОООО 00000001 01010111 10110011
двоичное представление числа 237658768:
00000000 00000000 00000000 00000000 00001110 00101010 01100010 10010000
Младшие 8 битов двоичного представления числа 87987:
1О11ОО11
gr; Mawymfls/17 /ma’/rim/Kym
I. C какими типами данных могут работать побитовые операторы?
2. Какие функции выполняют операторы >> и <<?
3. Какая ошибка допущена в данном фрагменте кода?
byte val = 10;
val = val << 2;
Оператор ?
Одним из наиболее интересных операторов в C# является оператор Р, который часто
используется вместо цепочки операторов if-else, имеющей следующий синтаксис:
lf(condition)
var = expressionl;
e;se
var = expression2;
'3;lccb значение, присваиваемое переменной var, зависит от оценки условия (condi-
_-:-n), которое управляет оператором н.
Оператор ? Называется тернарным, поскольку ему требуется три операнда. Синтаксис
010 выглядит так:
5»-p1 ? Exp2 : Exp3;
побитовые операторы могут работать с целочисленными типами данных.
Оператор >> производит сдвиг битов вправо, а оператор << производит сдвиг битов влево.
При осуществлении операции сдвига влево значение переменной val преобразуется в тип
int. Для обратного присваивания этого значения типа int переменной типа byte сначала
необходимо выполнить приведение типа.
£.2I.).—-
I пана а. | подробнее о типах данных И оператор
Здесь элемент Ехръ является булевым выражением, а элементы Exp2 и ЕХрЗ -—- э
выражения, типы которых должны быть одинаковыми. Обратите внимание `
использование и расположение двоеточия.
Рассмотрим пример, в котором переменной absval присваивается абсолютн`
значение переменной val.
absval н val < O ? val; // Получение абсолютного значения переменной val,
если значение больше или равно нолю. Если Значение переменной val отрицатель
Hoe, то переменной absval будет присвоено значение переменной val, взятое б
знаком минус. (В результате выполнения оператора «унарный минус» отрицательн
значение станет положительным.) Тот же код можно написать с использование Ё
цепочки операторов 1f~e1se:
if(val < O) absval — val;
else absval ; Val:
B представленной ниже программе с помощью оператора ? выполняется делени
двух чисел, но при этом запрещается деление на ноль. л
// B программе Демонстрируется использование тернарного оператора ? Для
// предотвращения деления на ноль.
using System;
class NoZeroDiv {
public static void Main() (
int result;
for(int i = —5; i < 6; i++) (
re5u;t s i 3: Q э 100 / 1 д O; 4_._.__.——“1pumTmwuwHnenmwnnnHarman}
if(i E: O)
Console.Wr1teLine("lOO / " + i + " : " + result);
}
Ниже представлен результат выполнения этой программы.
100 / ~5 : -20
100 / -4 2 —25
100 / -3 = -33
100 / -2 4 -50
100 / ›1 ь -100
100 / 1 ь 100
100 / 2 ; 55 1
100 / 3 = 33
100 / 4 = 25
100 / 5 > 20
Особое внимание обратите на еледуюпдупо строку кода:
result J i !— O ? 100 / 1 : O;
"опергюр 7
203
Здесь переменной result присваивается результат деления числа I00 на значение
Переменной Однако это деление производится только в том случае, если значение
Переменной i не равно нолю. B противном случае деление не выполняется, просто
Переменной result присваивается значение О.
Фактически нет необходимости присваивать значение, получающееся в результате
Выполнения оператора ?, какой-либо переменной. Это значение можно использовать
В качестве аргумента при вызове метода. Либо, если все выражения имеют тип bool,
Оператор ? можно использовать в качестве условного выражения в Цикле или
операторе if. Например, ниже представлена усовершенствованная версия предыду-
щей программы. Результаты выполнения обеих программ одинаковы.
11 or амме демонст И уется использование те на ного опе ато а ? для
// B P P
/II ;1pe,uo'1-Bpau.LeHI/m деления на ноль.
using System;
class NoZeroDiv2 {
public static void Main() {
for(int i » -5; i < 6; i++)
if(i != О ? true : false)
Console.WriteLine(“lOO / " + i +
" 2 " + 100 / i);
}
Обратите внимание на использование оператора if. Если значение переменной 1
равно нолю, то результатом оценки условного выражения оператора if будет значе-
НИС false. ТЗКИМ образом ПрСДОТВрЭШЭСТСЯ ДСЛСНИС на НОЛЬ И НС ВЫПОЛНЯСТСЯ МЕТОД
wruiteLir1e () . BO BCCX ОСТЭЛЬНЫХ случаях ДСЛСНИС ВЫПОЛНЯСТСЯ.
"" "Ч
иланаэ.подроонееотипахданныхиопеР3Т
5*’
10.
11.
12.
Покажите, как объявить одномерный массив, содержащий двенадцать
элементов типа double.
Покажите, как объявить двухмерный массив 4><5 типа int.
Покажите, как объявить нсвыровненньхй двухмерный массив, в котором‘
ВНСПЦНГАЙ МЗССИН СОСТОИТ ИЗ ПЯТИ ЭЛСМСНТОВ.
ПОКНЖИТС, КНК ИРПАЦГАНЁЖПЗНРОВЭТЬ ОДНОМСрНЫЙ ЦНССИВ ЗННЧСНИЯМЬЖ ОТ I
до э.
Объясните, как работает цикл fo reach. Покажите его обший синтаксис.
Напишите программу, в которой для нахождения среднего значения из
десяти чисел типа double применяется массив. Используйте любые
десять чисел.
Измените сортировку в проекте 5-1 таким образом, чтобы производи-
лась сортировка массива строк. Продемонстрируйте работу такой про-
граммы.
B ЧСМ состоит различие между методами классов string Indexof () И
Lastlndexof ()'?
Усовершенетвуйте предназначенный для шифрования mace Encode
таким образом, чтобы в качестве ключа использовалась 8—символьная
строка.
Могут ли побитовые операторы работать с данными типа double?
ПОКЗЖИТС, как можно переписать цепочку if—else
if(x < С) y = 10;
else y = 20;
используя оператор ?.
Является ли оператор & логическим оператором в следующем фрагменте
кода‘? Почему?
bool a, b;
//
1f(a & b);
Детальное
рассмотрение
методов и классов
Управление доступом к членам класса
Передача объектов методу
Возвращение объектов методом
Использование параметров ref И out
Перегрузка методов
Перегрузка конструкторов
Возвращение значений методом Main()
Передача аргументов методу Main()
Рекурсия
CICICJCICJCJCJCJCJCJ
Ключевое слово static
див Глава 6. Детальное расемотрвнив методов и классов‘
‚т
Е
‘I
B этой главе мы продолжим изучение ютаесов и методов. Сначала рассмотрим ‘r
вопросы управления ДОСТУПОМ К ЧЛСНЭМ КЛЭССЭ, ЗЭТСМ расскажем О ПСРСДЭЧС МСТОДаМ '
ОбЪСКТОВ И ИХ ВОЗВрЭТС, перегрузке МЕТОДОВ, ДИЛСС проанализируем рЗЗЛИЧНЫС формы h
синтаксиса метода Mair. () , поговорим О рекурсии И (by'm<L1m1x КЛЮЧСПОГО слова ‘-
static. т‘ _.
‘правление ДОСТУПОМ К членам класса
Ктасс выполняет две основные функции, обеспечивающие инкапсуляиию данных,
Во—первых‚ он связывает данные с кодом, который манипулирует ими. Преимущество
такой связи в классе мы начали использовать еще в главе 4. Во-вторьтх, класс
обеспечивает средства управления доступом к членам класса. Эту характеристику
ютасеа мы рассмотрим в данном разделе.
В C# доступ K членам класса осуществляется немного сложнее, чем вдругих языках
программирования. B основном используются два базовых типа членов класса к
открытые (public) И закрытые (private). Доступ K члену класса, объявленному как
public, может свободно осуществляться кодом, определенным за пределами этого
класса. До настоящего момента в примерах этой книги встречались члены класса е
типом доступа (модификатором) public. Доступ к членам класса, объявленным как
private. может осуществляться только другими методами этого же класса. Код,
определенны й за пределами ютаеса, может получить доступ к закрытому члену класса,
только используя открытые методы того же шасеа.
Ограничение доступа к членам класса является основным принципом объектно-ори-
ентированного программирования, поскольку это ограничение защищает объекты от
неправильного использования. Разрешая доступ к закрытым членам класса только
посредством строго определенного набора методов, мы предотвращаем присвоение
данным некорректных значений. Код, определенный за пределами класса, не может
непосредственно присваивать значение закрытому члену класса. Способ И время
использования данных в пределах объекта также можно строго контролировать. ,.-:-
Следовательно, если класс правильно реализован, то он создает «черный ящик»,
который можно использовать, но вея его внугренняя работа защищена от нссанк-
Ционированного вмешательства.
д“ Ы 3.40-'.»‘.'r."'v' т”
лодификаторы в С#
УПРШШСНИС доступом к членам класса осуществляется посредством четырех модифи-
каторов: public, private, protected И internal. B Этой главе мы рассмотрим
только модификаторы public и private. Модификатор protected применяется
лишь в тех случаях, когда задействовано наследование; подробно об этом модифи-
каторе мы поговорим в главе 7. Модификатор internal используется в основном
для компоновки программы (файла), проекта или компонента. Краткое описание
модификатора internal будет дано в главе I2.
Если для члена класса иеполыуется модификатор public, то доступ к этому члену
класса может осуществляться любым иным кодом программы. включая методы,
Определенные внутри других пстассов.
ч. -«.:.,- an-A „
ЕСЛИ ДЛЯ члена ктасса указан модификатор private, то доступ к такому члену класса
может осуществляться только другими членами этого же класса. Таким образом, у
управлвнив доступом к членам класса 4'-ll
МСТОДОВ одного класса отсутствует возможность доступа к закрытому члену другого
кдасса. Как уже говорилось в главе 4, при отсутствии модификатора член класса
автоматически становится ‘закрытым в своем классе. Следовательно, при определении
закрытых членов класса использование модификатора private не является обяза-
тельным.
В спецификации члена класса модификатор указывается псрвым (то есть объявление
ЧДСНЭ КЛЭССЭ ДОЛЖНО НЭЧИНЭТЬСЯ С модификатора). Приведем НССКОЛЬКО ПрИМСрОВЁ
public string errMsg;
prlVaC€ double bal;
p;_vate bool isError(byCe status) { //
Чтобы вам легче было понять различие между модификаторами public M private,
рассмотрим следующую программу:
// В программе демонстрируется использование модификаторов public M private.
using System;
class MyClass (
private int alpha; // Модификатор private указан явно.
int beta; // Модификатор private назначается по умолчанию.
public int gamma; // Объявляется переменная с модификатором public.
/* Определение методов, осуществляющих доступ к переменным alpha M beta.
Члены класса (методы) имеют доступ к закрытым членам этого же класса
(переменным).
ч
public void setAlpha(int a) (
alpha = a;
public int getAlpha() (
return alpha;
public void setBeta(int a) (
beta = a;
public int getBeta() (
return beta;
flass AccessDemo {
public static void Main() (
Myclass ob = new MyClass();
/* доступ к переменным alpha M beta возможен только c помощью методов
класса MyClass.
*/
ob.setAlpha(-99);
ob.setBeta(l9);
Console.WriteLine("3naueHMe переменной ob.alpha = " + ob.getAlpha());
Console.WriteLine("3HaqeHMe переменной ob.beta = " + ob.getBeta());
„щ, | лава 6. Детальное рассмотрение методов и
// Два оператора, приводимые ниже, недействительны.
// ob.alpha = 10; // Неверно! Переменная alpha
// объявлена как private! '
// ob.beta - 9; // Неверно! Переменная beta
// тоже закрытая!
Ошибка! Переменные alpha и bet-
являются закрытьтми.
// А следующий оператор действительный, поскольку возможен непосредстве„„
// доступ K переменной, объявленной как public.
ob . gamma = 99; ‹————-———— Верно, поскольку переменная gamma была объявлена
) с модификатором public.
)
Внутри класса МуС1а$з переменная alpha объявлена как private, переменная be
имеет модификатор private no умолчанию, а переменная gamma объявлена I
public. Поскольку переменные alpha И beta являются закрытыми, ДОСТУП к ни у
помощью кода, определенного за пределами их класса, невозможен. Следователь
внутри класса AcCessDemo НИ к одной из двух указанных переменных не
обращаться непосредственно. Доступ к ним можно осуществлять только с испол ‘Ё
ванием открытых методов, таких как setAlpha () И ge:Alpha(). Допустим, е
удалить символы комментария в начале оператора
// ob.alpha = 10; // Неверно! переменная alpha объявлена как private!
то эта программа не будет скомпилирована, поскольку в ней осуществляется попы _
непосредственного обращения к закрытой переменной. Но, хотя доступ к перемен};
alpha He может осуществляться кодом, определенным за пределами класса МуС1а ‘
эта переменная доступна для методов, определенных внутри класса МуС1аЗз‚ та
как setAlpha () и getAlpha () . То же самое справедливо по отношению к перем
ной beta.
Чтобы продемонстрировать использование модификаторов доступа для реше сад,
практических задач, рассмотрим следующую программу, в которой создается мас _~
типа int. B нем реализована защита от обращений к несуществующим элеме д,
массива, то есть обрабатываются ситуации, когда указывается индекс, выходящий
граничные значения индексов данного массива, — < О или > Length-1. Апгор ‚
такой обработки мы в дальнейшем будем называть алгоритмом прсдотвраше Ё
сбоев. поскольку он предупреждает возникновение ошибки Выполнения програм
при которой система генерирует соответствующее сообщение. после чего выполне
программы прекращается. Реализация этого алгоритма осуществляется посрсдс "Ё
инкапсуляции массива как закрытого члсна класса, при этом доступ K масс Ч;
разрешен только методам, являющимся членами данного класса. При таком под д Ё
можно предотвратить любую попытку доступа к элементу массива по недействи '
ному индексу. Далее представлен код класса FailSoftArray, В котором регШИЗО
упомянутый выше алгоритм. =
// В этом классе реализован алгоритм предотвращения сбоев.
using System;
Закрытые переме
экземпляра.
class FailSoftArray {
private 1nt[] a; // Объявление ссылки на массив.
private int errval; // Объявление переменной, которой будет Присвоено
// значение, возвращаемое методом get() В случае
// выхода за граничные значения индексов массива.
вление доступом к членам класса 209
.; ' n Ch’ Пе еменная Len th объявлена как ublic.
b c lnt Le 9 I Р 9
P” *‘
_ Конструктор создает массив, получая в качестве параметров значение длины
/ массива и значение, возвращаемое методом get() В случае
Выхода за граничные значения индексов массива.
в!
/ . . .
publ;c FailSoftArray(1nt s;ze, Lnt errv) (
a —_ new int[si2e];
errval = errv;
Lenggh 2 size;
// метод возвращает значение элемента с индексом, переданным ему в
// качестве параметра.
publlc int get(int index) (
д{(оК(1поех)) return a[index]; <
return errval;
// Метод присваивает значение элементу С УКЗЗЗННЫМ
- - Вьипшение недействительного
// индексом. Если индекс деиствительныи‚ метод
ИНДСКСВ .
// возвращает булево значение true, B противном
// случае ~ значение false.
public bool put(int index, int val) (
if(ok(index)) I 424‘
a[index] = val;
return true;
Ъ
return false;
// Метод возвращает значение true, если индекс не выходит за граничные
// значения индексов массива.
private bool ok(int index) {
1f(1ndex >= О & lndex < Length) return true;
return false;
)
// демонстрируется использование массива, в котором реализован алгоритм
// предотвращения сбоев.
class FSDemo (
Pub;;c static void Ma1n() {
Fa;lsoftArray fs = new Fai1SoftArray(5, -1);
Ln: x;
fi Согласно определению метода put() при попытке обращения к
// несуществующим элементам массива он просто возвращает значение false.
Сопзо1е.Иг1сеЬ1пе("Использование алгоритма предотвращения сбоев.");
for(int i=0; i < (fs.Length * 2); 1+т)
fs.put(i, i*10);
Соп5о1е.И:1се("Значения элементов массива: ");
forunt i=0; i < (fs.Length * 2): i++) ц
X = fs.get(i);
if(x != -1) Console.Write(x т " ");
— ч- ,.«..-uvvvuuipunvrw IVIVIVM‘-"-' " ""“"V
)
Conso1e.WriteLine("");
// Если при выполнении условного выражения в операторе 1f имеет место
// попытка обращения к несуществующим элементам массива, то выводится
// соответствующее сообщение.
Соп5о1е.Ик1сеЬ1пе("\пОбработка ошибки с выводом сообщения.");
for(int 1:0; 1 < (fs.Length * 2); i++)
if(!fs.put(i, i*lO))
Соп5о1е.Их1пеЬ1пе("Индекс " + i + " выходит за граничнью значения " +
"индексов данного массива.");
... м. ... ‚-_ а .. .... -_._....-. -_-
Сопзо1е.Иг1пе("Значения элементов массива: “):
for(in: :90; i < (fs.Length * 2); 1т+) {
х ~ fs.get(i);
if(x E2 vi) Console.Write(x + " ");
else
Сопзо1е.Иг1пе("\пИндекс " + i +
"индексов данного массива."); ‚
выходит за граничные значения " +
1
Результат выполнения этой программы:
Использование алгоритма предотвращения сбоев.
Значения элементов массива: 0 10 20 30 40
Обработка ошибки с выводом сообщения.
Индекс 5 выходит за граничные значения индексов данного массива.
Индекс 6 выходит за граничные значения индексов данного массива.
Индекс 7 выходит за граничные значения индексов данного массива.
Индекс 8 выходит за граничные значения индексов данного массива.
Индекс 9 выходит за граничные значения индексов данного массива.
Значения элементов массива: O 10 20 30 40
Индекс 5 выходит за граничные значения Индексов Данного массива.
Индекс 6 выходит за граничные значения индексов данного массива.
Индекс 7 выходит за граничные значения индексов данного массива.
Индекс 8 выходит за граничные значения индексов данного массива.
Индекс 9 выходит за граничные значения индексов данного массива.
Давайте внимательно разберем этот пример. Внутри класса FailSoftArray объяв-
лятотся три закрытых члена класса. Первый член класса ~ переменная а, содержит
ссылку на массив, в котором будут храниться данные. Второй член ютасса ——
переменная errval, хранит значение, которое будет возвращено при обращении
четода get () K несуществующему элементу массива. Третьим членом класса является
метод ok(), который определяет, не выходит ли индекс за граничные значения
индексов данного массива. Все эти три члена класса могут использоваться только
Другими членами класса Fa1lSoftArray. Остальные члены класса имеют модифи—
катор public, то есть могут использоваться любым другим кодом программы, в
которой применяется класс FailSoftArray.
При создании объекта типа FailSoftAr:ay нужно указать размер массива и значе-
ние, которое будет возвращено, если метод get() обратится K несуществующему
элементу массива. Возвращаемое значение должно явно отличаться от значений,
сохраняемых в массиве. После создания и массив, на который ссылается перемен-
ная а, и возвращаемое значение переменной errval становятся недоступными (то
' управление дишупшм к членам класса ‘ ' '
есть защищенными от неправильного использования) для пользователей объекта типа
F,__;1Lg3ftArray. Так. пользователь не может непосредственно указать индекс Эле-
“спта массива а, поскольку этот индекс может оказаться ошибочным. Доступ к
объСКТУ ВОЗМОЖЕН ТОЛЬКО С ИСПОЛЬЗОВЭНИСМ МСТОДОН get И put () .
B данном случае метод ок () объявлен как private не из практических соображений,
а Mm“, для демонстрации работы модификатора. Этот метод без всякого ущерба для
программы можно было определить как public, так как он не изменяет объект. Но
поскольку его использование предусмотрено только внутри класса Га11ЗоЁпАггау‚
Оп чоЖСТ быть определен и как private.
Обратите внигиание, что переменная экземпляра Length ОбЪЯВЛСНа как public, что
позволяет непосредственно обращаться к этой переменной для получения значения
длины массива FailSoftArray.
цтобы присвоить значение элементу массива FailSoftArray, необходимо вызвать
метод put( › и указать для него в качестве параметра индекс элемента. Чтобы извлечь
из пассива значения Элемента, необходимо вызвать метод get() И указать индекс
элемента. Если указанный индекс находится за пределами допустимых значений, то
метод put () возвращает значение false, а метод get () —— ЗНЭЧСНИС переменной errval.
Поскольку по умолчанию члены класса определяются как закрытые, нет необходи-
мости явно использовать для них модификатор private. Поэтому далее в нашей
книге при объявлении закрытых членов класса модификатор privaté использоваться
не будет. Просто запомните, что если при объявлении члена пстасса модификатор
опущен, то этот член класса будет закрытьтм.
;:-
Waaamsz flfie$§€’€’&'0fi&'A€**~W*~*-’"A~~*
(Е) Вопрос. Рассмотренный выше массив, использующий алгоритм предотвра-
щспия сбоев, защищен от доступа к несуществующим элементам массива, но это
достигнуто за счет изменения обычного синтаксиса индексации (обращения к эле-
ментам) иасеива. Существует ли более эффективный способ создания массива,
реализующего подобный алгоритм?
Ответ. Да Из следующей главы вы узнаете, что в Ci? имеется специальный
Тип члена класса, индексатор, который позволяет индексировать объект класса так
же. как массив. Кроме того, в главе 7 рассматривается более совершенный способ
Создания переменной Length И работы с ней посредством превращения ее в свойство.
3- .- - . ..._ \‚. .-..‘: ...... .::.
Минутный практикум
1. Назовите модификаторы доступа в С#.
2. Для чего используются модификаторы private И public?
3. Какой доступ к члену класса устанавливается по умолчанию, если при
объявлении члена класса отсутствует модификатор доступа?
1- модификаторами доступа В С# ЯВЛЯЮТСЯ prlvate, public, protected и internal.
м Доступ K члену класса, объявленного как private, имеют только другие члены его класса.
Член класса, объявленный с модификатором public, доступен и для кода, который
определен вне данного класса.
3‹ По умолчанию член класса определяется как private.
д ‚д Гпава 6. Детальное рассмотрвние методов и класс
Проект 6-1. Усовершенствованный класс Queue’
EZ}Queue.cs
Модификатор private можно использовать для усовершенствования класса Queue’ ;
разработанного в главе 5 (проект 5-2). В предыдущей версии все члены класса Queue:
являются открытыми, что приводит K определенным проблемам. Раз все члены Класса
объявлены как public, то код программы, использующей класс Queue, может;
получать доступ к массиву этого класса непосредственно и имест возможность
обращаться к элементам массива вне очереди. Поскольку основная задача очереди ..
обеспечение доступа к элементам по принципу FIFO, то обращение вне Очереди _‘:
H€)KCIIaTCIIbHO. Также следует учитывать возможность злонамеренного изменения д
значений индексов putloc И getloc, что приведет к неправильной работе очереди, },;
Все эти проблемы можно леп<о решить, сделав часть членов класса Queue закрытыми “C
Пошаговая инструкция
1. Скопируйте код класса Queue ИЗ проекта 5-2 в новый файл с именем Queue.cs.
2. В классе Queue удалите модификаторы public ИЗ операторов объявления масси- д
ва q И переменных, предназначенных для хранения индексов putloc и getloc, г.
как ПОКЗЗаНО НИЖСЁ
// Усовершенствованная версия класса Queue.
class Queue 1
// Теперь эти члены класса по умолчанию становятся закрытыми.
char[] q; // Этот Символьный массив является основой очереди.
int putloc, getloc; // Индексы, указывающие, какому элементу массива
// будет присваиваться значение и из какого
// элемента значение будет считываться.
public Queue(int size) (
q = new char[s1ze+l]; // Выделение памяти для очереди.
putloc = getloc = O;
)
// Помещает символ в очередь.
public void put(char ch) (
if(putloc==q.Length-1) (
Console.WriteLine("— Очередь заполнена."):
Ё
т
Е‘.
-*3
Ё
return;
)
putloc++;
q[putloc] = ch;
)
// Считывает значения элементов массива (очереди). Ё
public char get() ( Ы
if(getloc == putloc) { Ё
Console.WriteLine("— Очередь пустая.");
return (char) О;
getloc++;
return q[getloc];
П
Пе
I
3.
/Е
и
I
using
CldSS
ередача объектов методу
Преобразование модификатора членов класса q, putloc И getloc ИЗ открытого
в закрытый не влияет на программу. в которой используется класс Queue. B НОВОЙ
„срсии мы защищаем класс Queue OT возможного неправильного использования.
Так. приводимые ниже два оператора, в которых производится попытка непосред-
ственного доступа к членам класса, являются недействительными:
дддре test — new Queue(l0);
‘X’; // Ошибка!
—lOO: // Ошибка!
;es;.q[0]
;e-:.putlOC
Теперь члены класса q. putloc И getloc закрытые, а класс Queue строго обес-
печивает порядок доступа к элементам очереди по принципу FIFO.
редача объектов методу
До этого момента в программах нашей книги в качесгвс параметров методов исполь-
зовались обычные типы значений, такие как int или double. Однако методу можно
передавать и объекты. Рассмотрим простую программу, в которой сравниваются
параллелспипсды, значения размеров которых хранятся в объектах класса Block.
B программе демонстрируется передача методам объектов в качестве
параметров.
System;
Block 1
b. C;
volume;
‚п‘:
;nt
а!
public Block(int i, int j, int k) 1
d
‘D:
C
у
у
т\д-Н
v ;
volume = a * b * с;
Возвращает значение true, если в объекте ob содержатся
’/ те же значения размеров параллелепипеда,
'/ что и в объекте, метод которого был вызван.
FUD
lic bool sameBlock(Block ob) 1 <
if((Ob-a == а) & (ob.b 2; b) & (ob.c == с)) return true;
eise return false; Ишюлшошшиеодкш
В K‘c1‘lCC’l'B€ параметра .
//
//
//
public bool sameVolume(Block ob)
if(ob.volume :4 volume)
else return false;
Возвращает значение true,
объекта ob такой же, как объем параллелепипеда
объекта,
если объем параллелепипеда
метод которого был вызван.
1 4
return true;
- липа ч. p..\ulcuIunUu раССМОТреНИе МБТОДОВ И КПас
class Passob {
public static void Main() (
BLOCK obl ; new В1осК(10, 2, 5);
Block ob2 — new Block(lO, 2, 5);
Block ob3 — new Block(4, 5, 5);
_‚ _,...-.-
Соп5о1е.Иг1сеЬ1пе("Утверждение: объект obl имеет те же значения " + 5
"переменных, \пчто и объект ob2 w " +
obl.sameBlock(ob2)); <— g
Соп5о1е.Иг1пеЬ1пе("УтвержДение: объект obl имеет те же значения " +
"переменных, \пчто и объект ob3 И " + Псршшчамеищу‘
obl.sameBlock(ob3)); <————————*————*————*—Объект‘
Соп$о1е.Иг1пеЬ1пе("Утверждение: объем параллелепипеда объекта obl " +
"\правен объему параллелепипеда объекта ob3 Й " +
оЬ1.5атеЧо1оте(оЬ3));<
)
Результат работы программы следующий:
Утверждение: объект obl имеет те же значения переменных.
что и объект ob2 — True
Утверждение: объект obl имеет те же значения переменных,
что и объект ob3 Ч False
Утверждение: объем параллелепипеда объекта obl
равен объему параллелепипеда объекта ob3 - True
B методах sameBlock () И samevolume () Происходит сравнение Значений перемен-
ных вызывающего объекта со значениями переменных объекта, переданного иМ вы‘
качестве аргумента. Метод sameBloCk () сравнивает размеры паргшлштепипсдов И
возвращает значение true при полной их идентичности. Метод sameVolume()
сравнивает объемы параллслепипедов, то есть результаты произведений значений
всех трех переменных. В обоих случаях обратите внимание. что для параметра ob b‘
класс Block yKa3aH как его тип. Как видно из этого примера, синтаксически объекты _
.7_....-_ д а... ;.
ПСрСПНЮТСЯ МСТОДЭМ TCM ЖС CflOCO6OM, ЧТО И ОбЫЧНЫС ТИПЫ ДЭННЫХ. Ч 5
Как передаются аргументы д,
Как показано в предыдущей программе, передача объектов методу является доста- ; Ё
точно простой задачей. Однако в определенных ситуациях передача объекта может
заметно отличаться от передачи обычных аргументов. Чтобы понять это, необходимо п‘. _
рассмотреть два способа передачи аргумента подпрограмме.
Первый способ называется вызови/п по значению. При его использовании копию
значения аргумента получает формальный параметр подпрограммы. Следовательно,
изменение параметра подпрограммы не оказывает влияния на аргумент. который
используется при ее вызове. Второй способ передачи аргумента называется вызовом
по ссылке. В этом случае параметру передастся ссылка на аргумент (а не значение
аргумента). Внутри подпрограммы эта ссылка используется для доступа к фактиче-
скому значению аргумента, который был указан при вызове метода. Это означает,
что изменения параметра будут влиять на аргумент, используемый при вызове
Подпрограммы. В С# применяются оба ЭТИ способа передачи аргументов. у
при передаче методу значений обычных типов, таких как int или double, используется
способ передачи по значению. Следовательно, изменение параметра. получившего
215
передача объектов методу
аргумент, не влияет на аргумент за пределами метода. В качестве примера рассмотрим
шсдупотдую программу:
,‚ ц программе аргументы простых типов передаются по значению.
Ыддпд System;
Class Test Ё
gr выполнение этого метода не приведет к изменению переданного ему
аргумента.
‘х
public void n0Change(int i, int j) {
1 i+J':
J":-j:
class Cailayvalue 1
pub;1c static void Main() {
Test ob = new Test();
так а 15, b = 20;
Соп5о1е.Иг1пеЬ1пе("Значения переменных а M b перед вызовом метода: " +
a + u И u + b);
ob.noChange(a, b);
Соп5о1е.Иг1сеЬ1пе("Значения переменных а M b после вызова метода: " +
a + " M " + b):
Ъ
Результат выполнения данной программы будет следующим:
Значения переменных а и b перед вызовом метода: 15 и 20
Значения переменных а M b после вызова метода: 15 и 20
Как видите, операции, происходящие внутри метода noChange(), не влияют на
значения переменных а и b, используемых в качестве аргументов при вызове метода.
При передаче методу ссылки на объект возникают некоторые трудности. Технически
сама ссылка на объект передается по значению, следовательно, параметр получает
коттию ссылки. что по логике не должно изменять сам аргумент. (Например, передача
параметру ссылки на новый объект не изменит объект. на который ссылается
дРГумснт.) Но. и это весьма суитественно. излтенение объекта, на который ссылается
Шраметр, приводит K изменению объекта, на который ссылается аргумент. Давайте
Рассмотрим, почему так происходит.
Вспомните, что при создании переменной типа класса (ссылочного типа) вы создаете
ссылку на объект, а не сам объект. Объект создается в памяти (ему выделяется память)
С помощью оператора new, а ссылка на эту область памяти присваивается переменной
сСыдточного типа. При использовании переменной ссылочного типа в качестве
дРГумента для метода параметр принимает ссылку на тот же объект, на который
Ссылается аргумент. Следовательно, и аргумент, и параметр будут ссылаться на один
И тот же объект. В действительности это означает, что объекты передаются методу с
216 Глава 6. Детальное рассмотрение методов и класс V"
использованием вызова по ссылке. Изменения объекта внутри метода влияют
объект, используемый в качестве аргумента. Рассмотрим следующую программу;
// В программе демонстрируется передача объектов по ссылке.
using System;
class Test {
public int a, b;
public Test(int 1, lnt j) {
a — i;
b — j;
}
/* Передача методу объекта. Теперь значения переменных ob.a M ob.b В
объекте, используемом при вызове метода, будут изменены.
*/
public void change(Test ob) {
ob.a = ob.a + ob.b;
ob.b = —ob.b;
class CallByRef {
public static void Main() {
Test ob : new Tes:(l5, 20):
Соп$о1е.Ик1сеЬ1пе("Значения переменных ob.a M ob.b перед вызовом метода:
+ 0b.a + " M " + ob.b);
ob.change(ob);
Console.WriteL1ne("3HaqeHMH переменных ob.a M ob.b после вызова метода:
+ ob.a + " M " + ob.b);
}
B результате работы программы будет выведена следующая информации:
Значения переменных ob.a M ob.b перед вызовом метода: 15 и 20
Значения переменных ob.a M ob.b после вызова метода: 35 и —2О
объекта, а значит, и сам объект, используемый в качестве аргумента.
Итак, при передаче методу в качестве аргумента ссылки на объект сама ссылка7
передается с использованием вызова по значению, следовательно, создается копия ;.
ЭТОЙ ссылки. Но поскольку передаваемое значение ссылается на объект, копия этого ‚ н
значения ссылается на тот же объект, на который ссылается соответствующий м
аргумент. д -
Использование параметров c модификаторами _-E
ref И out I
По умолчанию обычные типы значений, такие как щпй или char, передаются методУЁ
по значению. Это означает, что изменение параметра, который принимает обычный,
тип значений, не повлияет на аргумент, используемый в вызове. Но если применить
. ._. ......-„.‚...чтцитмщць-тщечгтчггч'ъ
Использование параметров с модификаторами ref И out 217
‚адоцевыс слова ref M out, то можно передать любое значение обычного типа по
Ссыдкд что позволит методу изменить аргумент, используемый в вызове.
Прежде чем Перейти к рассмотрению механизма использования ключевых слов ref
и дыр, необходимо выяснить, зачем может понадобиться передача значений обычных
“mow. no ССЫЛКС. В этом возникает необходимость в двух случаях: во-первых, когда
Надо позволить методу изменять содержимое своих аргументов, во-вторых, когда надо
позволить методу возвращать более одного значения. Давайте рассмотрим оба эти
Варианта подробно.
Очень часто возникает необходимость в использовании метода, способного изменять
Значения передаваемых аргументов. Наиболее типичным примером такого метода
является метод swap(). Поскольку по умолчанию в С?! обычные типы аргументов
передаются по значению, нельзя создать метод, который персставит местами значе-
mm ДВУХ аргументов типа int. Для решения этой проблемы применяется модифи-
катор ref.
Как вы знаете, оператор return позволяет методу возвращать значение вызывающей
подпрограмме. Однако метод может возвращать только одно значение при каждом
его вызове. Если же перед вами стоит задача возвратить две и больше единицы
информации, то ее нельзя рещить с помощью такого метода. К примеру. когда
требуется создать метод для вычисления площади прямоугольника и определения,
является ли этот прямоугольник квадратом, нужно, чтобы метод возвращал две
единицы информации ~ площадь прямоугольника и значение, указывающее на
равенство всех сторон. Для решения этой проблемы применяется модификатор out.
Использование модификатора ref
B Cfl использование модификатора параметра ref приводит к созданию вызова по
ссылке вместо вызова по значению. Модификатор ref указывается при объявлении
метода и при его вызове. Давайте рассмотрим простой пример. В следующей
Программе создается метод sqr() , возвращающий значение своего аргумента, воз-
веденное во вторую степень, которое замещает начальное значение аргумента.
Обратите внимание, что при объявлении метода модификатор ref предшествует типу
параметра, а при вызове метода ключевое слово ref указывается непосредственно
“Сред аргументом.
/1" b программе демонстрируется использование модификатора ref для передачи
Н МёТОду значения обычного типа по ссылке.
Usmg System;
C4138 RefTeSt {
ЭТОТ МЕТОД изменяет значение СВОЕГО аргумента.
pisllc Void sqr(ref int i) { 3necb модификатор ref y|<a3bmacTc:}
i * 1; B начале объявления ятараметра.
СЧЗЗЗ RefDemo {
P'JbliC static void Main() {
RefTest ob = new RefTest();
шк а = 10;
. „аш- ч. дшальное рассмотрение методов И классов `
СОп$01е.Ик1ЕеЬ1пе(“Значение переменной а перед вызовом метода: " + а); r
Здесь модификатор ref
ob sqr ( ref д) г предшествует аргументу. -
L
Соп$о1е.Ик1сеЬ1пе(“Значение переменной а после вызова метода: “ + a); ’
;. -.
Результат выполнения этой программы. предстанлснньнд ниже по‚чтвсрж;1аст‚ что т
метод sqr() действительно модифииировадя значение аргучсптгп — псрсмсннои a.
.,:xs- -
Значение переменной а перед вызовом метода: 10
Значение переменной а после вызова метода: 100
„т. ..
Использование модификатора ref делает возможным создание метода, который ‚д
будет менять местами значения своих аргументов (значения обычного типа). Ниже =
представлена программа, которая содержит метод swap ( ) , меняющий местами зна. Ё ‚
ЧСНИЯ ДВУХ ЦСЛОЧИСЛСННЫХ ЗРГУМСНТОВ, ПСРСДЗВНЁМЫХ СМу при ВЫЗОВС.
-1 ‚‚
..`,_.„‚ к.
// Программа меняет местами значения двух переменных.
using System;
class Swap (
// Этот метод изменяет значения двух своих аргументов.
public void swap(ref int a, ref int b) {
Lnt t;
t = а;
а = bi
b Д t;
class SwapDemo {
public static void Main() {
Swap ob = new Swap();
в
R
int х = 10, у = 20;
Console.WriteLine("3HaqeHMH переменных х и y перед вызовом метода: " +
X + и И n + Y);
ob.swap(ref x, ref y); Q
Console.WriteLine("3HaqeHMn переменных х и y после вызова метода: “ +
х+"и"ту):
}
Результат выполнения ЭТОЙ программы:
Значения переменных х и y перед вызовом метода: 10 и 20
Значения переменных х И y после вызова метода: 20 и 10
Обратите внимание что значение аргументу, передаваемому с модификатором ref.
ДОЛЖНО быть присвоено до вызова метода. Это важно. поскольку принимающий такой
аргумент MCTOI1 предполагает, ЧТО параметр ссылается на ДСЙСТВИТСЛЬНОС значение. '
Следовательно, применяя модификатор ref, нельзя использовать метод для присваи-
вания 3.pI‘yMCHTy НЗЧЗЛЬНОГО ЗННЧСНИЯ.
j-——-j
Использование параметров с модификаторами ref M out 219
использование модификатора out
иногда параметр ссылочного типа испогтьзуется для получения значения из метода,
д „с для передачи ему значения. Метод может выполнять некоторую функцию
шдпример, открытие сокета) и возвращать в параметре ссылочного типа какоддибо
3H2NCH Me, определенное для указания успешного или неудачного выполнения опера-
mm. Для этой цели в С# предусмотрен модификатор параметра out. При объявлении
метода в круглых скобках указывается не информация, которую нужно передать
методу, а информация, которая будет получена M3 метода. Необходимость использо-
вания еще одного модификатора, отличающегося от ref. объясняется тем, что
параметр с модификатором ref должен быть инициализирован до вызова метода.
Слсдковатсльно, только для выполнения данного условия необходимо присвоить
аргументу некоторое (произвольное) значение.
Отличие между модификаторами ref M out заключается в том, что параметр с
модификатором out может использоваться только для передачи значения из метода.
При этом нет необходимости до вызова метода присваивать переменной начальное
значение. Более того, внутри метода параметр с модификатором out всегда рассмат-
ривается как не имеющий начального значения. Метод обязан присвоить значение
параметру, прежде чем передаст управление программой вызываюкдей подпрограмме.
То есть после вызова метода параметр out всегда будет иметь значение.
В качестве примера рассмотрим программу, в методе которой используется параметр
с модификатором out. Метод rectlnfo () возвращает значение площади Прямо-
угольника, для которого заданы размеры его сторон. В параметре issquare МЕТОД
возвращает значение true, если прямоугольник является квадратом, и значение
геле в противном случае. Таким образом, метод rectlnfo () возвращает подпро-
грамме, которая его вызывает, две единицы информации.
// В программе демонстрируется использование модификатора параметра out.
using System;
class Rectangle 1
’/ Переменные содержат значения длины сторон прямоугольника.
;nt sidel;
;nt side2;
public Rectangle(int i, int j) 1
sidel = i;
side2 = j;
// МЕТОД возвращает Значение площади ПРЯМОУГОЛЬНИКЗ И определяет, ЯВЛЯЕТСЯ
’/ли он квадратом.
, _ V _ Передача информации из метода
pub;1c lnt rectInfo(out D001 issquare) { C.wnOnEmBmflwMrmmmeflM
if(sidel=;side2) lssquare 2 true; cnnmM¢Mxmoum4ouL
else issquare = false;
return sidel * side2;
440 Глава 6. Детальное рассмотрение методов и классе
class SwapDemo {
public static void Main() {
Rectangle rec: = new Rectangle(l0, 23);
int area;
bool issqr;
area — rect.rcctlnfo(out lssqr);
f(LsSqr) Соп5о1е.Иг1сеЬ1пе("Данный прямоугольник является квадратом.");
9159 Соп5о1е.Иг1сеЬ1пе("Данный прямоугольник не является к5адратом.");
Qgn5oie.WriteL1ne("Hnomaub ПрЯМОуГОЛвНИКа — + area т ".");
\
I
Обратите внимание, что до вызова метода ‘cectlnfo () параметру 1sSqr HC приеваи-
дается начальное Значение. Если бы параметр метода rectlnfo () имел модификатор)
ref‘, ЭТО было бы невозможно. После завершения работы метода параметр lssqr.
содержит либо значение true, либо значение false B зависимости от того, является!
прямоугольник квадратом или нет. Значение площади прямоугольника возвращается.
с помощью оператора return. Результат выполнения этой программы выглядит
следующим образом:
Данный прямоугольник не является квадратом.
Площадь прямоугольника = 230.
Минутка/д практикум
I. Чем различается вызов по значению и вызов по ссылке?
3. Какие функции выполняет модификатор ref? Чем он отличается от моди-
фикатора out? ' ‘
жаждете: арафеесаеяаламщ
Вопрос. Могут ли модификаторы ref И out применяться с параметрами
ссылочного типа, например, при передаче ссылки на объект?
Ответ. Да. При использовании модификаторов ref и out для параметров
ссылочного типа сама ссылка передастся по ссылке. Это позволяет методу изменить
направление ссылки, то есть после выполнения этого метода переменная будет 1
ссылаться уже на другой объект. Рассмотрим следующую программу:
// B программе демонстрируется использование модификатора ref при вызове
I.-' HO CCbUIKE.
us;ng System;
class Test {
public int a;
1. При вызове по значению подпрограмме передается копия аргумента, а при вызове по т
ссылке ~ ОСЫЛКа на аргумент. `
2. В C# аргументы обычных типов нерсдгпотся но значению, а объекты — no ССЫЛКС.
3. Модификатор ref СОЗДЗСТ вызов по ссылке для параметров обычных типов. Модификатор
out также создает вызов по ссылке, но он нс используется для передачи информации методу.
Использовании переменного количества аргумвнтое 221
public Test(int i) {
aéli
Р
‚д при выполнении этого метода аргумент не будет изменен.
Ь„в;;с void noChange(Test о) {
А Test newob = new Test(O);
О 4 newob: // Выполнение данного оператора не оказывает влияния на объект
// "о" вне метода noChange().
/J После выполнения этого метода переменная "о" будет ссылаться на другой
/' объект.
рцрддс void change(ref Test 0) i
Ё Test newob — new Test(O);
Q н newob; // ЭТОТ оператор изменит аргумент.
‚драк-г- , . .‘\...... ..
class CallObjByRef {
public static void Main() {
Test ob A new Test(100);
„т: - „г“...
Соп5о1е.Ик1сеЬ1пе("Значение переменной ob.a после инициализации объекта: "
* ob.a);
_ ob.noChange(ob);
® Соп$О1е‚Ик1СеЬ1пе("Значение переменной ob.a после вызова метода " +
§ "noChange(): " + ob.a):
ob.Change(ref ob);
Console.WriteLine("3HaueHMe переменной ob.a после Вызова метода " +
"Change(): " + ob.a);
HM/‘KC представлен рСЗУЛЬТЭТ ВЫПОЛНЕНИЯ ЭТОЙ программы.
; Значение переменной ob.a после инициализации объекта: 100
Значение переменной ob.a после вызова метода noChange(): 100
Значение переменной ob.a после вызова метода Change(): 0
В ЭТОЙ программе переменной о присваивается ссылка на новый объект внутри
зястода noChange () , что не влияет на объект ob внутри метода Main () . Однако внутри
метода change () , B котором используется параметр с модификатором ref, присвое-
нпс переменной о ссылки на новый объект приводит K тому, что внутри метода
E43 1 r: () переменная ob будет ссылаться уже на другой объект.
"1" VP" .\....-»-,- ..... r'::x ..»~. ., , ., ч . ,...,.. .... .;...~..-,- .. ....;. .... ....-;_-:;»,. . .. ...... -.....:
Использование переменного количества
аргументов
ПРИ создании метода обычно заранее известно количество аргументов, которые будут
euy I1€pCl1aBaTbCH. Ho ЭТО бывает не всегда, иногда требуется метод, которому можно
“Средавать произвольное количество аргументов. В качестве такого примера рассмот-
РИМ метод, в котором производится поиск наименьших значений. Этому методу
Может быть передано два, три, четыре значения и т. д. С использованием обычных
222 Глава б. Детальное ассмот ение методов И КЛас
P D .
б‘!
параметров его создать нельзя. В данном случае необходимо задействовать парамет]
с модификатором params, C ПОМОЩЬЮ которого можно передавать методу произволд,
нос количество параметров.
уиодификатор params используется для объявления в качестве параметра массива
который будет способен принимать ноль и больше аргументов. Число элементов,
массиве будет равным числу аргументов, передаваемых методу. Следовательно, дд,
получения аргументов программа должна будст обращаться к массиву.
B приведенном ниже примере используется модификатор parems для созданш
,\1C1‘0:121:n1n‘v'a‘. (), который возвращает минимальное Значение из нескольких, пере
данных в качестве аргументов.
.-
//' B программе ДЁМОНСТРИРУЕТСЯ ИСПОЛЬЭОЕЗНИЕ МОДИФИКЗТОРЗ params.
uslfig System?
c‘ass Vin { '—~*“———————*“”'*—*——“—-w
‘ А Создан ие параметра переменной
. › - г 1 ' г ‹
pUb‘lC lnt mlnVa‘(paramS lnt'] nums) { дшинькеишкшьжцщниемькщи-
in‘: mt‘ фикат ора pdrcuns. '-
i£(nums.Length == O) {
Console.WriteL1ne("omméxat Для метода не указаны аргументы.");
return O:
m — nums[0];
for(int i=1; i < nums.Length; i++)
if(nums[i] < m) m = nums[i];
return m;
Class ParamsDem0 {
рые1;с static void Main() 1
Min ob = new Min():
int min;
int a = 10, b = 20;
// Вызов метода с двумя аргументами.
т1п ~ ob.m1nVal(a, b);
Conso1e.WriteL1ne("Mmnnmanbuoe Значение = " F m1n);
// Вызов метода с тремя аргументами.
min : ob.minVal(a, b, -1);
Console.WriteLine("Mmummanbuoe значение 4 " Ь min):
ff Вызов метода С пятью аргументами.
min = ob.minVal(18, 23, 3, 14, 25);
Соп$о1е.Ик1СеЬ1пе("Минимальное значение = " 4 min);
// Методу в качестве аргумента также может быть передан целочисленный
// массив.
int[J args = { 45, 67, 34, 9, 112, 8 };
min 2 ob.minVal(args);
Console.WriteLine("Mmnmmanbnoe значение = " + min);
...m.a.c..s.-u.:..u...=..-as.-.ae«....a.».. .‚
‘ч
ё
д.
Ё
...-._s „у .-
чип- " т›-'-'
4
223
гиспдльзование переменного количества аргументов
Программа выводит следующий результат:
Мидимдльное значение ~ 10
минимальное значение ~ ~1
минимальное значение — 3
‘диниматтььтое значение — 8
Каждый раз при вызове метода minval () ему передаются аргументы с помощью
мдсщпш nums. Длина массива равна количеству элементов. Следовательно. метод
тшдд; () можно использовать для поиска минимального значения из любого коли-
Чсствл аргументов.
хотя параметру с модификатором pa rams можно передавать любое число аргументов,
„ее они должны быть совместимы с типом массива, определенного в качестве
параметра. Например, вызов метода min\/al ()
ob.minVal(1, 2.2);
min
является неверным, поскольку Значение double (2.2) не конвертируется автоматиче-
cm в тип тпь, который имеет массив mums B методе т1п\/а1 ().
При использовании параметра с модификатором params необходимо контролировать
число элементов массива, поскольку параметр с эти м модификатором может прини-
мать любое число аргументов (даже ноль). В частности, приведенные ниже операто-
ры. в которых вызывается метод minval (). являются синтаксически правильными.
min ob.minVal();
min — ob.minVal(3);
// Вызов метода без аргументов.
// Вызов метода с одним аргументом.
Поэтому в методе mi nVal () перед попыткой обращения к первому элементу массива
проверяется, есть ли в массиве хотя бы один элемент. В случае отсутствия такой
проверки при вызове метода minVa1() без аргументов возникнет исключительная
ситуация, поскольку произойдет ошибка выполнения программы. (Далее при описа-
нии исключительных ситуаций мы расскажем о более эффективном способе защиты
от такого рода ошибок.) Код метода minval () написан таким образом, что разреша-
ется производить его вызов с одним аргументом. При этом возвращается этот
единственный аргумент.
Метод может иметь обычные параметры и параметр переменной длины. В следующем
Примере метод showlxrgs () принимает один параметр типа string, а Затем параметр
Переменной длины (с модификатором рапатз) —~ массив типа int:
/3 E программе демонстрируется использование обычного параметра и параметра
// переменной длины.
us:Lq System;
Class MyClass {
Puolic void showArgs(string msg, params int[] nums) {
2onsole.Wr1te(msg + "; ");
foreach(int in nums)
Conso1e.Write(i + “
J.
Этот метод имеет один обычный
параметр и один параметр е мо-
дификатором params.
u);
Console.WriteLine();
c‘;_
‘доз ParamsDemo2 {
LL‘? I лвва о. детальное рассмотрение методов И Класс
public static void Main() t
MyClass ob = new MyClass();
ob.showArgs("B этом методе кроме строки есть еще 5 аргументов: ‚
1, 2, 3‚ 4, 5);
oD.showArgs("A B ЭТОМ методе кроме строки есть еще 2 аргумента: ",
17, 20);
Результат выполнения программы следующий:
В этом методе кроме строки есть еще 5 аргументов: 1 2 3 4 5
А в этом методе кроме строки есть еще 2 аргумента: 17 20
В тех случаях, когда метод имеет обычные параметры и параметр с модификатором.‘ _
ракатз, параметр переменной длины Должен в списке параметров располагаться" '
последним. Кроме того, в любом случае у метода может быть только один параме гм
с модификатором рагатз.
Минутные: практикум
1. Как создать параметр, который принимает Перемен ное количество аргументов?
2. Может ли метод иметь более одного параметра params?
3. Справедливо ли утверждение, что параметр params может располагаться в
любом месте списка параметров?
Возвращение объектов
котором определен строковый массив. Каждая строка Представляет собой краткую- `
характеристику ошибки. На основании кода ошибки, полученного в качестве аргу-
мента, метод g'etE.rrorMsg () возвращает значение элемента массива — объект типа
string.
// B программе демонстрируется возврат объектов.
using System;
class Errormsg {
string[} msgs = {
"Ошибка вывода.",
"Ошибка ввода."‚
"Недостаточно свободного места на диске."‚ ‘
"Выход за граничные значения индексов массива." ”
L Для создания параметра, который принимает переменное количество аргументов, исполь- `
зуется модификатор рагашз.
Нет.
3. Нет. Параметр params должен стоять последним в списке параметров. "
воздрдщш дно ии „и.“ аса
// Возврат сообщения об ошибке.
public string getErrorMsg(in1: 1) t
iL(: >=O & i < msgs.Length)
return msgs{i};
e;se
return "Введен неправильный код ошибКи.";
class ErrMsg {
puQl;C static Vold Main() {
Е:гогМ59 err = new ErrorMsg();
Console.WriteLine(err.getErrorMsg(2));
Ccnsole.WriteLine(err.getErrorMsg(l9));
I
B результате работы Этой программы будут выведены следующие строки:
Недостаточно свободного места на диске.
Введен неправильный код ошибки.
Таким же образом можно возвращать объекты создаваемых вами ктассов. Например,
ниже представлена обновленная версия предыдущей программы, в которой создаются
два класса, предназначенные для обработки ошибок. Один класс, инкапсулирутопдий
сообщение об ошибке с кодом «серьезности» ошибки, называется Err. Второй класс
содержит метод get Errorlnfo ( ) , который возвращает объект типа Err. Этот класс
нмсНшЛСяЕггок1пЕо
/; B программе демонстрируется возврат объектов, определенных пользователем.
us;ng System;
class Err {
public string msg; // Сообщение об ошибке.
public int severity; // Переменная, содержащая код серьезности ошибки.
public Err(string m, int s) {
msg = m;
severity 2 s;
Zlass Errorlnfo {
string[] msgs = {
"Ошибка вывода.",
"Ошибка вьода."‚
"Недостаточно свободного места на диске."‚
"Выход за граничные значения индексов массива."
; Возв антенне объекта типа Err.
ПСЫ howbad = 4 3, 3, 2, 4 ;,- р
public Err getErr0rInfo(int i) (
if(i >›О & i < msgs.Length)
return new Err(msgs§i], howbad[i]);
else
return new Err("Baeneu неправильный код ошибки."‚ O);
‘до Глава б. Детальное рассмотрение методов и кла
class Errlnfo {
public s:at;c vo1d Ma1n() {
Ezrorlnfo err — new Errox1nfo();
Err e;
е — err.getErrorlnfo(2);
Console.Wr;teLine(e.msg +
Серьезность ошибки: 1 e.sever1Ly):
e » err.getErrorInfo(l9);
Console.WriteLine(e.msg Ъ
Серьезность ошибки: т C.severity);
ч
ЭТИ программа ВЫВСДСТ НН ЭКРНП СЛСЦУЁОЪЦУЮ HH(I)Op.\1ZiL1HK)I
Недостаточно свободного места на диске. Серьезность ошибки: 2
введен неправильный код ошибки. Серьезность ошибки: О
Каждый раз при вызове метода getErrorInfo () создастся новый объект типа Err,
а ссылка на него возвращается вызывающей программе. Затем этот объект исполь-
зуется в методе Main() ДЛЯ вывода на экран сообщения об ошибке и кода «серьез-
ности» ошибки. Объект, возвращенный методом, существует до тех пор. пока он
упоминается в программе. его удаление будет выполнено только при «сборке мусора».
Таким образом, объект не может быть уничтожен только потому, что создавший его
метод возвратил управление программой вызывающей подпрограмхте.
Перегрузка метода
Данный раздел познакомит вас с одним из самых замечательных свойств C# -—
перегрузкой методов. В этом Языке два и более методов в пределах одного класса
могут имсть одинаковые имена при уеловии, что объявления параметров в методах
различаются. (Необходимо, чтобы в методах были указаны параметры различных
типов, или методы должны различаться количеством параметров.) В этом случае
методы называются nepeep_v.>xc'eH1tbl/uu, а процесс определения метода с таким же
именем называется перегрузкой мт11‹)ди. Перегрузка методов является одним из
способов реализации полиморфизма в Cit.
B общем случае для перегрузки метода вам достаточно объявить другую его версию,
а компилятор позаботится обо всем остальном. Безусловно, псрегружасмые методы
могут отличаться и по типу возвращаемого значения, по этого недостаточно, чтобы
два метода можно было считать различными. Типы возвращаемых значений не
содержат достаточного количества информации для того, чтобы компилятор мог
определить, какой из методов необходимо использовать. При вызове перегружаемого
метода выполняется тот метод, параметры которого совпадают е аргументами.
Пнже представлена простая программа, в которой выполняется перегрузка метода.
// В программе демонстрируется перегрузка метода.
using System;
U_a5S Overload I
ц Перегрузка метода Z2 I
public void ovlUemo() Ё Пстшявщшнямспша
Consoie.Wri:eLine("3wow МОТОД не имеет параметро5.");
/ Перегруженный метод ovlDemo, версия с одним параметром типа int.
publ‘-C “Did °V1De“‘°‘i“" а) *
Console.writeL1ne("MeTou с одним параметром: " + а);
/I Перегруженный метод OvlDemo, версия C двумя тараметрами типа int.
pu5;;C ДНЕ оч1Вето(1ПЁ а‚ in: b) { Трсшянцшиямспща
соп5о1с.шг1:еЬ1пе("Метод с двумя параметрами: " + а v " И " + b);
1cLurn d + b;
// Перегруженный метод ovlDemO, версия с двумя параметрами типа double.
pubLic double ovlDemo(double d, double b) { Чспюрпшдюрсттмшпдц
Ё Соп5о1е.Ик1ЕеЬ1пе("Метод С Двумя тараметрами типа double: " +
` a+llMlI+b);
return a + b;
class Overl0adDemo 1
public static void Main() {
Overload ob : new Overload();
int resl;
double resD;
1/ Вызов всех версий метода oVlDem0().
0b.oviDemo();
Conso1e.WriteLine();
ob.ovlDemo(2);
Console.WriteLine();
resl ob.ovlDemo(4, 6);
Соп5о1е.Иг1сеЬ1пе("Результат выполнения метода ob.ovlDemo(4, 6): " +
resl);
Console.WriteLine();
resD ob.ovlDemo(l.1, 2.32);
ConsOle.Wr;teLine(”Pe3ynbTaT выполнения метода Oo.ovlDemO(1.l, 2.32):
rcsD);
B результате выполнения вссх этих методов программ ы будет выведена слсдуюпцая
информация:
Ётот метод не имеет параметров.
H
метод с одним параметром: 2
Ёетод C двумя параметрами: 4 И 6
РЁЗУЛЬТЗТ выполнения метода ob.ov1Demo(4, 6): 10
_„„‚ __._..__. ‚ддддщццдвппв ппопидип И ЦЛНС
Метод C двумя параметрами типа double: 1,1 И 2,32
Результат выполнения метода Ob.ovlDemo(1.1, 2~32): 3.42
B данной программе метод ovlDemo () перегружен четыре раза. Первая версия „
принимает ни одного параметра, вторая версия принимает один целочисленный
параметр, третья версия принимает два целочисленных параметра, а четвертая версия’
принимает два параметра типа doubie. Обратите внимание, что первые две версии
имеют возвращаемый гип void, а вторые две ч возвращают значения. Такая
программа является действительной. Но мы уже говорили. что перегруженные методы.
отличаются не только типом возвращаемого значения, поэтому попытка использо-
вания следующих двух версий приведет к ошибке:
// Если в программе присутствует только один метод ovlDemo(int),
// она будет работать.
public Void ovlDemo(int a) {<7
Console.Wr1teLine("MeT0u c одним параметром: " + а);
Типы возврашаемьтх значений .
не используются для оттредеде
/" Ошибка! Два метода ovlDemo (int) ДОЛЖНЫ ОТЛИЧЭТЬСЯ ни); различия между псрсгру-
не только типом возвращаемого значения. Женнымиештшшми
/at
public int oVlDemO(inC a) { 4,
Cons0le.Wr1teLine("MeTon C одним параметром: " + а);
return a * a;
B комментарии говорится, что различие в типе возвращаемых значений является
недостаточным для перегрузки метода.
В главе 2 мы рассказывали. что в С# обеспечиваются автоматические преобразования _ _
определенных типов данных. Эти преобразования также применяются к параметрам
перегруженных методов. Рассмотрим следующий пример:
// B программе демонстрируется автоматическое преобразование типов аргументов.
using System;
к
class Overload2 [
public void f(int х) {
Соп5о1е.Иг1СеЬ1пе("Значение переменной х внутри метода f(int): " + х);
}
Ь
д.
public VOld f(double х) {
Соп$о1е.Ик1ееЬ1пе("Значение переменной X внутри метода f(double): " + X);
I
Class TypeConv 1
publlc static void Main() [
Overload2 ob = new Overload2();
int L е 10;
double d = 10.1;
byte b : 99;
short s = 10;
float f — ll.5F;
Ob-f(i); // Вызывается метод ob.f(int)
перегрузка метода 44:1
ob.f(d); // Вызывается метод ob.f(doub1e)
Ob_f(b); // Вызывается метод ob.f(int) И выполняется автоматическое
// преобразование типа аргумента.
Ob_f(s); // Вызывается метод ob.f(int) И выполняется автоматическое
// преобразование типа аргумента.
ob.f(f); // Вызывается метод ob.f(double) И выполняется автоматическое
// преобразование типа аргумента.
Ниже представлен результат выполнения программы.
значение переменной х
Значение
Значение
Значение
Значение
переменной
переменной
переменной
переменной
Х
Х
Х
Х
внутри
внутри
внутри
внутри
внутри
метода
метода
метода
метода
метода
f(1nt): 10
f(double):
f(int): 99
f(int): 10
f(double):
10,1
11,5
B ЭТОМ примере определены только две версии метода f (). B одной из них параметр
имеет тип int, а в другой —— double. Однако в С# существует возможность передать
методу f() значение типа byte, short или float. При передаче методу значения
типа byte или short C# автоматически преобразует его в тип int. При передаче
значения типа float его тип преобразуется в double И вызывается метод f (double).
ВПЖНО понимать, что автоматическое преобразование типов применяется только при
несовпадении типов параметра и аргумента. А вот еше одна версия предыдущей
программы, в которую добавлен метод f () с параметром типа byte.
// Еще одна версия предыдущей программы,
using Sys
ciass Ove
tem:
rload2 [
public void f(byte X) [
Соп5о1е.Иг1сеЬ1пе("Значение переменной х внутри метода f(byte):
public void f(int x)
Console.WriteLine("3HaqeHMe переменной х внутри метода f(int):
{
public void f(double х) {
Соп$о1е.Ик1ЕеЬ1пе("Значение переменной х внутри метода f(doub1e):
Ъ
glass Ty
peconv {
public static void Main()
Overload2 ob =
int i : 10;
double d 10.1,
byte b = 99;
short s : 10;
float f = 11.5F;
ob.f(i);
ob.f(d);
{
new Overload2();
B которую добавлен метод f(byte).
т х):
..
ъ x);
п + Х);
// Вызывается метод Ob.f(inC)
// Вызывается метод ob.f(doub1e)
сап Глава 6. Детальное рассмотрение методов И КЛЗССОВ
ob. f (b) ; // Вызывается метод ob. f (byte) . Теперь автоматическое
// преобразование типа не выполняется.
ob_f(s); // Бызьвается метод ob.f(1nL) И выполняется автоматическое
// преобразование типа аргумента.
ob.f(f); // Вызывается метод Ob.f(double) И выполняется автоматическое
// преобразование типа аргумента.
\
|
Результат‘ выполнения этой программы отличаются от предыдущего только тем, что
в третьей строке упоминается метод f (byte) , а не метод f (LDC).
Значение переменной х внутри метода f(int): l0
Значение переменной к внутри метода f(doublel: 10,1
Значение переменной х внутри метода f(bytC): 99
Значение переменной X внутри метода f(int): 10
Значение переменной x внутри метода f(double): li,5
ТЙК КНК СУЦЮСТВУСТ ВСРСИЯ МСТОДН‚Ё(Ь]Ье),!1рИ ВЫЗОВС МСТОДН f() C аргументохятипа
' н? ВЫЗЬПШСТСЯ МСТОЦ f (Ьупе) 603 H[)C()6D2iJOB£lHHSI ТИПИ ЗНЗЧСНИЯ ЕЦЛУМСНТЫ В lnt.
При перегрузке методов также могут использоваться модификаторы ref И out.
B следующем фрагменте кода определены два разных метода:
рцо1;с void f(int х) {
Сопзо1е.Иг1СеЬ1пе("Значение переменной x ВНуТрИ метода f(int): " т x);
public void f(ref int x) {
Consolc.WriteLine("3HaqeHMe переменной x внутри метода f(ref int): " 4 х);
Ё
Таким образом, оператор
ob . f (i) ;
вызывает метод f (две х) , но оператор
ob.f(ref i);
вызывает мстод f (ref int х) .
Перегрузка методов поддерживает полиморфзизм, поскольку является одним из
способов реализации в C# ПРИНЦИПЫ «один интерфейс, множество методов». В тех
языках, которые не поддерживают перегрузку методов, каждому методу необходимо
присвоить уникальное имя. Но довольно часто возникает ситуация, когда требуется
применить один метод для различных типов данных. Рассмотрим функцию возврата
абсолютного значения. B языках, не поддерживающих перегрузку, имеется три и
более версий такой функции с немного отличающимися именами. Так, в С функция
ans () возвращает абсолютное значение целого числа, функция lass () и абсолют-
ное значение длинного целого числа, а функция fabs () — абсолютное значение
числа c плавающей точкой. Поскольку в С перегрузка не поддерживается, каждая
функцътгх должна иметь собственное имя, хотя эти функции аналогичны. Такой
Подход очень усложняет работу: несмотря на то, что основные действия, выполняе-
мые каждой функцией. одинаковы, программисту все равно необходимо помнить три
имени. В Ci? подобная ситуация исключена. так как все методы, предназначенные
для вычисления абсолютного значения, могут иметь одно имя. Стандартная библио- I,
тека классов в C# COL1€p)Kl/IT метод Abs (›, возвращающий абсолютное Значение. Он
Перегрузка метода 4.5 I
перегружается классом Зуз tem _ Math для обработки веех числовых типов. А компилятор,
исходя из типа аргумента, определяет, какую версию метода Аёлз ( ) следует вызвать.
Прсимуцгсстгзо перегрузки заключается в том, что опа позволяет. используя обшес
mm, получать доступ к методам, выполнягогцихг одинаковые дсиствия с различными
типами данных. Таким образом, имя Мл; представляет идише (rb1nu_u1;1c.11oe (Jam-nmue,
Выбор необходимой версии метода для указанного типа данных производится ком-
пилятором, программист же обязан помнить лишь общее действие, выполняемое
‚ганным методом. Благодаря реализации полиморфизма вмесго нескольких имен
пспоггьзугстся только одно. Этот пример с методом Аьсг) {достаточно простой, но
использование псрегрузкгг позволяет решить гораздо более сложные задачи.
„балдеть; лрафееееадааатч
д} Вопрос. Мне встречался Термин подпись, который испол ьзуют программисты
на С. Что он означает?
Ответ. В Ci? ПОДПИСЬ — это имя мс-гола со списком его парамогрогъ, Следо-
вательно, при перегрузке двух Mc'1‘0;1011 одного и того же класса опн не могут и мсгь
одинаковую подпись. Обратите внимание, что подпись нс включает тип возвращае-
мого значения, поскольку он пс используется компилятором Cit для различия
перегруженных методов.
::,::-...... —--,--1.-...:n'.::><‘.. «.»»...M. ...\»~:...::.: ...\..-.... .::: ;- ‚.„::‚... .............:--,-..- :„‹:.. .-.. ‘ч : .» . .:.„
При перегрузке гметода каждая из версий может выполнять л юбые необходимые действия.
Нет правила. которое бы определяло, что перегруженные методы должны выполнять
одинаковые или похожие действия. Но стиль написания программ в Ci: предполагает
что перегрузка методов должна быть связана с прсдыдугггсгт версией метода. Следова-
тельно, хотя у вас есть возможность использовать одно имя для перегрузки мстодов„
выполняющих в результате абсолютно разные действия, делать этого нс следует. Так,
можно использовать имя s-qr ДЛЯ создания метода. возвращающего квадрант ггелочис-
ленного значения, и метода, возвращающего квадра/пныи корень Значения с плавающей
точкой. Но создав два принципиально различных метода, программист только усложнит
понимание программы и увеличит вероятность появления ошибок. Поэтому перегружать
рекомендуется только тесно связанные операции.
Минутка/г? /ipa/(ma/ry/91
I. Какое условие должно вьггголггяться, чтобы метод мог быть перегружен?
2. Необходимо ли псрсгруженному методу иметь тип возвращаемого значения,
отличающийся от предыдущей версии?
3. Как выполняется автоматическое преобразование типов данных в Cit при
перегрузке?
I. Для перегрузки метод должен отличаться от своей предыдущей версии по типу и/или
количеству параметров.
‚ц
Нет. Тип возвращаемого значения перегруженных методов может отличаться, но это не
является необходимым условием для перегрузки метода.
3. Когда отсутствует прямое совпадение типов мехсцу набором аргументов и набором парамет-
ров, используется метод C наиболее близко совпадающим набором аргументов, если типы
этих аргументов могут быть автоматически преобразованы к типам, имеющим параметры.
232 Глава 6. Детальное рассмотрение методов и классов
Перегрузка конструкторов
Также как методы, конструкторы могут быть перегружены, что позволяет конструш
ровать объекты различными способами. В качестве примера рассмотрим следующую
программу:
// B программе демонстрируется использование перегруженного конструктора.
using System;
class Myclass {
public int х;
public MyClass() {
Console.WriteLine("3ToT оператор определен" +
" B конструкторе MyClass().");
public MyClass(int 1) {
Console.WriteLine("3ToT оператор определен" +
" B конструкторе MyClass(1nt).");
Конструирование
} объектов различ-
ньшитспособами.
public MyClass(double d) (
Console.WriteLine("3ToT оператор определен" +
" 5 конструкторе MyClass(double).");
X I (int) d;
public MyClass(int i, int j) [
Console.Wr1teLine("3ToT оператор определен" +
" B конструкторе MyClass(int, int).");
x i * j;
class OverloadConsDemo {
public static void Main() (
Myclass t1 = new MyClass();
Myclass t2 > new MyClass(88);
Myclass t3 = new MyClass(17.23);
Myclass t4 : new MyClass(2, 4);
Console.WriteLine("3HaqeHMe переменной t1.x: " + t1.x);
Console.WriteLine("3uaqeHMe переменной t2.x: " т t2.x);
Соп5о1е.Ик1сеЬ1пе("Значение переменной t3.x: " + t3.x);
Console.WriteLine("3HaueHMe переменной t4.x: " + :4.x);
1
1
PC3yJ'l bTdT ВЫ П ОД НС Н ИЯ П рогра М М Ы TEI KO BI
Этот оператор определен E конструкторе MyClass().
Этот оператор определен В конструкторе MyClass(int).
Этот оператор определен в конструкторе MyClass(double).
Перегрузка конструкторов 233
этот оператор определен в конструкторе MyClass(int, int).
Значение переменной t1.x: O
значение переменной t2.x: 88
значение переменной t3.x: 17
Значение переменной t4.x: 8
Конструктор MyClass () может быть перегружен четырьмя способами, при исполь-
зовании каждого из них объект конструируется по-разному. Выбор компилятором
необходимого конструктора осуществляется на основе параметра, указанного при
выполнении оператора new. Благодаря перегрузке конструктора появляется возмож-
ность гибкого выбора способа конструирования объекта и создания именно такого
объекта. который необходим в конкретной ситуации.
Использование перегрузки позволяет конструктору одного объекта инициализиро-
вать другой объект. Например, рассмотрим программу, в которой используется класс
Summation для вычисления суммы всех чисел от ноля до значения, указанного в
качестве аргумента конструктора этого клаеса.
// В зтой программе один объект используется для инициализации другого.
using System;
class Summation {
public int sum;
// Для создания объекта конструктор использует параметр типа int.
public Summation(int пот) [
sum = O;
fortint i=1; i <2 num; i++)
sum +› i;
// Для инициализации объекта данный конструктор использует другой объект,
// переданный ему в качестве параметра.
public Summation(Summation ob) { ‹—————————————«———
sum 4 ob.sum;
Один объект используется для
инициализации другого объс та.
class SumDemo {
public static void Main() {
Summation s1 4 new summati0n(5);
Summation s2 : new Summation(s1);
Console.WriteLine("CyMMa чисел от 0 до 5 (значение переменной s1.sum) 2 "
+ s1.sum);
Соп5о1е‚Иг1ЕеЬ1пе("Значение переменной s2.sum: " + s2.sum);
PC3yIlbT'(1T ВЫПОЛНСНИЯ программы ПОКНЗНН НИЖС.
Сумма чисел от О до 5 (значение переменной $1.5цш) r L5
Значение переменной s2.sum: 15
Как видно из приведенного выше примера, использование одного элемента для
создания другого позволяет гораздо эффективнее выполнять инициализацию созда-
ваемого объекта. В данном елучас при конструировании объекта 52 отсутствует
необходимость повторно вычислять сумму всех чисел от О до 5.
i
Глава б. Детальное рассмотрение методов и классов‘
г :
234
'1
ВЫЗОВ перегруженного конструктора
с использованием ключевого слова this
При вызове перегруженного конструктора в некоторых случаях бывает позтезноё
использовать вызов одного конструктора другим. В Ci: это рщътъмуется при помощи д
еще одной формы кчгочевого слова '.:h1:3. Общий сингаксътс такого конструктора!
прслстапзлсн ниже:
i
›
сОп5сгисСог—пате(рагатесег-1155) : this(argument~l;st) i
// ... тело конструктора, который также может быть пустым
'.“-an „ч...
При вызове этого конструктора (который можно назвать вызывающим) первым?
выполняется перегруженный коъчструктор, в котором список параметров совпадает
со списком apeymemnoe. Далее выполняются операторы, опрсделснньте внутри вызы-
шношего конструктора, если таковые имеются. Ниже представлена программа, где;
используется ключевое слово кош. д
,_‚
В программе демонстрируется использование ключевого слова this для вызова Q
одного конструктора из Другого.
:1
using System; ;
Class XYCoord ( ’
public int к, у;
public XYCoord() : this(O, O) (
Console.WriteL1ne("3Tow оператор определен В конструкторе XYC0ord()");
publxc XYCoord(XYCoord obj) : this(obj.x, ob].y) {
Console.WriteLine("3Tom оператор определен в конструкторе " + `
"XYCoord(XYCoord ooj)");
public XYCoord(int L, ;nt j) { 1
COns0le.WriteLine(“3Tow оператор определен а конструкторе “ + :
"XYCoord(XYCoo:0(;nL, 1n:)"); 51
X Ы i, ?
У =1"; д
Ъ 5‘.
Class OverloadConsDemo (
public static void Main() {
XYCoord L1 4 new XYCoord();
XYCoord L2 = new XYCoord(8, 9);
XYCOOKG L3 — new XYCoord(t2);
mwf”T
4
Соп5о1е.Иг;сеЬ1пе("Значение переменных :-.x И Ll.y: " * ’-
г
3-
ч
. ‘e.u351.I”'." .
' t1.y):
ConsOle.WriteLine("3HaueHme переменных t2.x И t2.y: " т ;Z.x L ", "
+ t2.y);
Cons0le.WriteLine(“3uauenMe переменных t3.x И :3.y: " r L3.x + “, “
+ t3.y);
__, _
Перегрузка конструкторов 235
В рс3уЛБППС ВЫПОЛНСНИЯ ЭТОЙ программьттш экран будут ВЫВСДСНЫ СЛСДУКЛЦИС
Строк И:
оператор определен
дператор определен
5 конструкторе XYCoord(XYCoora(;nL, ;n:)
S
оператор определен в конструкторе ХУСооко(ХЕСоого\тпс‚ int)
5
В
конструкторе XYCoord()
. - оператор определен конструкторе ХУСоого(ХУСоога(щпс‚ 1пЬ)
Этот оператор определен
значение переменных t1.x И t1.y: О, О
3„дчение переменных t2.x И t2.y: 8, 9
Знддение переменных t3.x И t3.y: 8, 9
конструкторе XYCoord(XYCoord obj)
Рассмотрим как работает эта программа. В классе XYCoord единственным конструк-
тором. который фактически инициализирует поля х и у, является конструктор
3:-ii (mt, lflt) . OCTdJ1bHbIC два конструктора, используя тстточсвос слово 73115,
просто вьтзьтвают конструктор IxI‘;'CC'u01"ii(1r.-T , 1 ч. Так. при создании объекта :1
вызывгтетсэт его конструктор ;›<\’Сс-огг5 ( ) ., который в свою очередь вьтзьтвает коттструк-
тор 1 :~. (O, O). То есть ключевое слово this yK213bmuC'rcs1 BMCCTO полного имени
конструктора XYCoord (O, O) . Создание объекта t,.; Происходит таки м же Образом.
Одна из причин использования ютючсвого слова Ыцз для вьтзова перегруженных
конструкторов — возможность избежать дублирования кода. В предыдущем примере
без использования ссылки this пришлось бы повторять последовательность инициа-
лизируюших операторов (х = 1; y = 3;) во всех трех конструкторах. Еще одним
преимуществом применения ключевого слова this является создание конструкторов
с «аргумснтамтт по умолчанию», которые применяются, если эти аргументы не заданы
явно. B ‘laCTHOCTI/I, для предыдущей программы можно создать еще один конструктор
A r“d()Z
puulic XYCoord(int x): this(x, х) i}
который автоматически присваивает координате у то же значение, что и координате х.
Учтите, что «аргументы по умолчанию» необходимо использовать осторожно, по-
. скольку их неправильное определение может привести к ошибкам при выполнении
программы.
Проект 6-2. Перегрузка конструктора Queue
И ODemo2.cs
B ЭТОМ проекте мы модернизируем класс Queue путем добавления к нему Двух
Коттетрукторов. С помощью первого конструктора из одной очсреди будет создана
‚Чругая очередь. Второй конструктор мы используем ‚для создания очсрсли и присваи-
вання начальных значений се элементам (инипиализатнттт). Добавление этих лвух
конструкторов позволит использовать данный ктасс более эффективно.
Пошаговая инструкция
1. Создайте новый файл с именем орг-гида . :3 И скопируйте в него код класса -Queue
ИЗ проекта 6-1.
Id
Добавьте в программу представленный ниже конструктор, который создаст оче-
редь из другой очереди.
// Конструктор создает объект типа Queue на основе другого объекта типа Queue-
public Queue(Queue ob) {
235
Глава 6. Детальное рассмотрение методов и кла
м.)
putloc = ob.putloc;
getloc Ob.g€tlOCi
q new char[ob.q.Length];
// Копирование элементов.
for(int i~getloc+1; i <3
ЧП]
ры:1ос; 1ё+)
OD. q т 1] ;
)
Давайте внимательно рассмотрим этот конструктор. Он присваивает переменным”
putloc И getloc НЗЧЭЛЬНЫС значения, содержащиеся в параметре (объекте) ob_
Затем он создает новый массив для хранения элементов очереди и копирует их из
массива объекта ob в этот массив. После конструирования новая очередь будет
точной копией начгшьнодт (используемой для инициглптзаьхитит) очереди, но обе они
будут совершенно разными объектами.
“%ё%ЫЫ#ШщшЁмтфьчыг-Ё“
Добавьте конструктор. который инъшиализътруе’г очередь из массива символов,
ПСрСДЗВЗСМОГО B КЗЧССТВС параметра.
// Конструктор сознает объект типа Queue И инициализирует его.
public Queue(char[] a) {
putloc Z 0;
getloc = O;
q 2 new char[a.Length+1];
;_ „к; :...., Lt‘-55$‘.
for(int i = O;
}
i < a.Length; i++) put(a[i]);
ww¥%%¢akawKkw1%
Этот конструктор создает очередь, длина которой позволяет ‘(ранить вее символы
из массива а, а затем помещает эти символы в массив очереди. Для корректной
работы алгоритма очереди ее штина (длина ее матссиват) должна быть на единицу
больше длины массива, передаваемого в качестве параметра.
-мфтфйщ
Ниже представлен полный код обновленного шасса Queue BMCCTC с кодом класса
QDemo2.
// Версия класса Queue c двумя Дополнительными конструкторами.
using System;
class Queue (
// Теперь
char{] q;
int putloc,
ё
Ё
.
4.
эти члены класса
//
//
//
//
по умолчанию становятся закрытыми.
Этот символьный массив является основой очереди.
Индексы, указывающие, какому элементу массива
будет присваиваться значение и из какого
getloc;
элемента значение будет считываться.
// Создает пустую очередь по заданному размеру.
public Queue(int size) {
q 4 new char[size+l]; // Выделение памяти для очереди.
putloc — ge:;oc — O;
// Конструктор создает объект типа Queue на основе другого объекта типа Queue.
public Queue(Queue ob) (
putloc = Ob.putloC;
getloc = ob.getloc:
q = new char[ob.q.Length];
Мм и. „
Перегрузка конструкторов 237
// Копирование элементов.
for(int i=getloc+l; i <= putloc; l++)
q[iJ —— ob-q[i1:
// Конструктор создает объект типа Queue И инициализирует его.
public Queue(Char[] a) {
putloc г O;
getloc Ь O;
q = new char[a.Length+l];
for(int i = O; i < a.Length; i++) put(a[ij);
}
// Метод помещает символ в очередь.
public void put(char ch) {
if(putloc==q.Length—l) {
Console.WriteLine("— Очередь заполнена.");
return;
putloc++;
q[putloc] 4 Ch;
// Считывает значения злементов массива (очереди).
public char get() {
if(getloc == putloc) {
Console.WriteLine("- Очередь пустая.“);
return (char) O;
getloc++;
return qigetloc};
}
// B этом классе демонстрируется использование класса Queue.
class QDemo2 {
public static void Main() {
// Создается пустая очередь из десяти элементов.
Queue ql = new Queue(10);
Char[] name = {'Т'‚ 'о'‚ 'm'};
// Создается очередь на основе массива.
Queue q2 = new Queue(name);
char ch;
int i;
// B очередь помещаются некоторые символы.
for(i=O; i < 10; i++)
q1.put((char) ('A' + i));
// Создается очередь на основе другой очереди.
Queue q3 = new Queue(ql);
// Отображение значений элементов очереди.
дао Глава 6. Детальное рассмотрение методов и классов .
.
é
Соп$о1е.Иг1се("Содержимое очереди ql: ");
for(i~O; i < 10; i++) {
ch + ql.get():
Console.Write(ch);
Cun5o1c.Wri:eL-nc("\n");
");
Н;
СОп5о1е.ИГ1Ес("Содержимое очереди q
£or(i—O; i < 3; i++) { д
Ch —. q2.get()7
Console.write(Ch);
Console.WriteLine("\n"); д
Соп5о1е.Иг1ее("Содержимое очереди q3: "); Ч?
for(;~G; L < 10; 1+4) ( Ё
Ch — q3.get(); if
Consoie.Write(Ch);
5. Ниже представлен результат вьтполнсния программы.
Содержимое очереди ql: ABCDEFGHIJ
Содержимое очереди q2: Tom
Содержимое очереди q3: ABCDEFGHZJ
Метод Main()
До Настоящего момента мы использовали только одну версию метода Мала (). Но B
Сд существует несколько перегруженных версии этого метода. Некоторые из Них
могут использоваться Для возвращения значении, а Другие могут принимать аргумен-
ты. Канская из этих версии будет рассгхтатриваться в данном разделе.
Возвращение значений методом Main()
При завершении работы программы метод Main() может возвратить значение
вызывающему процессу (часто операционной системе). Для этого используется
следующий синтаксис метода Маш () :
public StatlC int Ma1n()
Обратите внимание, что вместо типа возвращаемого значения void в этой версии
истода Maln () указан тип ше. Следовательно. возвращаемое этим методом значение
будет иметь тип mp.
Обычно возвращаемое значение метода Ma1n() указывает способ завершения про-
граммы. По соглашению. если возвращается значение (). то программа завершена
корректно. Все другие значения указывают на ошибку, возникшую при выполнении
программы.
метод Main() 239
передача аргументов методу Main()
многие программы Принимают так называемые apeymelmzbz Ic().-u(m()H0z2 строки. Аргу-
мент‘ командной строки —— это информация, которая следует непосредственно за
„мснсм программы в командной строке. В Стпрограммах такие аргументы переда-
югся методу Main(). Чтобы этот метод мог принимать аргументы, необходимо
использовать следующий синтаксис:
public static void Ma1n(string[] args)
¥1JlL1
pUEAiC statlc ;nt Ma1n(s:r1ng[] args)
B первом варианте метод Main () имеет гип возвращаемого Шачсния \/Old; второй
„дриант можно использовать для возвращении целочисленного значения, как уже
описывалось в предыдущем разделе. B обоих случаях аргументы командной строки
хранятся в виде строк в массиве типа stmng. который передастся методу Main () .
Например, В СЛСДУЮЩСЙ программе на Экран выводятся ВСС аргументы, С которыми
ЭТИ программа ВЫЗЫВЗСТСЯ.
;/ Программа отображает все аргументы, заданные в командной строке.
us;ng System;
class CLDemo {
public static Vold Main(string[] digs) {
Console.Wr1teL1ne("flporpamme были переданы " т аг95.Ьеп9сп т
" аргумента командной строки.");
Сопзо1е.Иг1пеЬ1пе("Список аргументов: ");
for(int i=0; i<args.Length; i++)
Console.WriteLine(args[i]);
Если в командной строке ввести имя программы CLDemo И задать три аргумента
Сьюето один, два, три.
на экран будут выведены следующие строки:
Программе были переданы 3 аргумента командной строки.
Список аргументов:
Един,
два,
цри.
Чтобы понять механизм использования аргументов командной строки„ рассмотрим
следующую программу. Она принимает один аргумент командной строки — mm
ПОЛЬЗОВЗТСЛЯ. Затем производится поиск этого имени в двухмерном строковом
массиве. Если такое имя найдено, на экран выводится телефонный номер этого
I юл ьзовател я.
’: Простой автоматизированный телефонный справочник.
qsing System;
class Phone {
public statlc int Main(string[} args) {
string[:] numbers = {
( "Владимир", "555—3322" }‚
{ "Людмила", "555—8976" },
ь-ц, Глава 6. Детальное рассмотрение методов и классов
_..____._`. _
{ "Сергей", "555—1О37" )‚
{ "Александра", "555—14ОО" 2,
{ " "‚ "" } // Определена нолевая строка для случая, когда не будет
// введено никакого имени.
ii
ti;
р
„ада
1f(args.Length E: 1) I
Console.WriteLine("ma6noH ввода: Phone <ИМЯ>");
return 1; // Значение, указывающее на неправильный ввод аргумента и
// преждевременное завершение программы.
›‚ ~:'.u ‘\‘:.G4«'i1:‘l"3!.—y,¢v"
Ъ
else { Ё
Еог(1—О; numbers[i, O] I; ""; 1+‘) { Ё
if(numbers[1, O] == args[O}) 1 3
Console.WriteLine(numbers[i, О] + ": " +
numbers[i, 1]);
break;
}
}
if(numbers[1, O] == "")
Console.WriteLine("MMH не найцено.");
}
return 0;
›
Ниже приведен пример работы этой программы.
D:\Work\C#\programs\ch_C6>Phone Людмила
Людмила: 555-8976
Рассмотрим данную программу подробнее. Во-первых, заметьте, что перед продол-
жением выполнения программы проверяется наличие аргумента командной строки.
Это очень важный момент. Если корректная работа программы зависит от количества
введенных аргументов, всегда необходимо вначале проверить количество аргументов
командной строки, передаваемое такой программе. Невыполнение этого условия
часто приводит к аварийному завершению работы программы.
Во-вторых, обратите внимание на то, как программа возвращает код завершения
работы. Если программе не было передано требуемое количество аргументов, то
возвращается значение 1, что указывает на аварийное завершение программы. Если
программе было передано нужное число аргументов, то при завершении программы
возвращается значение О.
Минутный лрактакум
I. MO)KCT ли конструктор принять в качестве параметра объект своего тстасса?
2. Для чего применяются перегруженные конструкторы?
3. Покажите синтаксис метода Main(), который принимает аргументы ко-
мандной строки, но не возвращает значение.
1. Да.
2. перегруженные конструкторы применяются для обеспечения удобства и гибкости при
использовании классов.
3. public static void Main(string[] ахов)
рекурсия 241
д Рекурсия
в C31 метод может вызывать еам себя. Этот процесс называется рекурсией, а метод
который can еебя вызывает, называется рекурсивным. Ктючевым компонентом
рекурсивного метода является оператор для вызова этого же метода.
Класеичеекйги примером рекурсии является вычисление факториала числа. Факто-
рпгшом числа N Ha3blBaCTC$l произведение всех целых чисел от 1 до N (скажем.
фпкториал числа 3 равен произведению l><2><3 = 6). B следующей программе
представлен рекурсивный метод, предназначенный для вькчисления факториала
числа. Для сравнения в программу вютючен равнозначный метод, выполняющий те
же функции без использования рекурсии.
“ В программе демонстрируется использование рекурсии для вычисления
// факториала числа.
using System:
class Factorial {
// Рекурсивный метод.
public int factR(int n) {
int result;
if(n==1) return 1;
result = factR(n—1) * n;<———————lPcxypcum-lbw”! ВЫЗОВ метода factR().
return result;
// Равнозначный метод, в котором вместо рекурсии используется цикл for.
public int factI(int n) {
int t. result;
result = 1;
for(t=1; t <= n; t++) result '= t;
return result;
class Recursion (
public static void Main() {
Factorial f = new Factorial():
Console.WriteLine("¢aKTopMan числа, вычисленный C помощью рекурсивного" +
" метода.");
Соп$о1е.Иг1ееЬ1пе("Факториал числа 3 = " + f.factR(3));
Console.WriteL1ne("eaxmopman числа 4 = " + f.faCtR(4));
Console.WriteL1ne("¢aKTopMan числа 5 = " + f.factR(5));
Console.WriteLine();
Console.WriteLine("®aKTopMan числа, вычисленный с помощью обычного " +
"метода.");
Соп5о1е.Иг1сеЬ1пе("Факториал числа 3 " + f.factI(3));
Console.WriteLine("¢aKTopman числа 4 — " + f.factI(4));
Console.WriteLine("¢aKTopMan числа 5 " + f.factI(5));
H
11
242 Глава 6. Детальное рассмотрение методов и класс
Ниже показан результат выгтоггнсния этой программы.
Факториал числа, вычисленный C помощью рекурсивного метода. f
Факториал числа 3 2 6
Факториал числа 4 s 24
Факториал числа 5 120 ’
ФЗКТОРИЗЛ ЧИСЛЗ, БЫЧИСЛЭННЫЙ С ПОМОЩЬЮ ОбЫЧНОГО метода.
Факториал числа 3 1 6
\
Факториал числа 4 = 24
Факториал числа 5 2 120 Ё
Алгоритм работы обычного метода fact () достаточно прост. В нем пспользуетсЁ
цикл, где последовательно умножаются вес Целые числа от 1 до числа. фактори
которого нужно вычислить. , Ё
Работа рекурсивного метода fact}? () немного сложнее. Если вызвать метод (‚заев ()
аргументом 1, метод возвращает единицу, которая при возврате управления п редыдущ 5
му методу factR () будет использоваться в выражении Еассв (гад › *n. При выполнени 3'
Этого выражения метод f асы? ( ) Bbl3blBaCTC$X c аргументом n~1. Этот процесс повторяетсд.
до тех пор, пока значение Переменной п не будет равно 1. Так, при вычисленигЁ, ‘
факториала числа 2 первый вызов метода fact}? () с аргументом 2 приведет ко вгоро. ` "
вызову этого же метода, но уже с аргументом 1. При получении этого аргумента метод)
возвратит число 1 , которое затем будет умножено на 2. То есть ответ ы значение 2. Moxaiq
поместить оператор WriteLine () ; B метод factR () , который будет выводить на экран .
аргумент каждого вызова и промежуточные результаты. 5
Когда метод вызывает сам себя, в стек помещаются новые локашьньте переменные
параметры, а код метода выполняется с этими новыми переменными с самого начала.‚
Рекурсивные вызовы не создают новую копию метода, новыми являются только,
аргументы. После возвращения рекурсивным методом значения старые ‚токальньтед ‘
переменные и параметры удаляются из стека, а управление программой псрсдаетсяЁ:
оператору, следующему за тем, из которого был в вызван метод. 1‘ ‘
Рекурсивные версии многих программ могут выполняться медленнее своих итеративных
(использующих обычные методы и Циклы) аналогов, так как при дополнительныхд
вызовах метода (появляются непроизводительные издержки. В результате слишкомё
большого количества рекурсивных вызовов создается много новых локальных перемен-ъ ‘T
НЫХ и параметров, а это может перегрузить стек, что приведет к возникновению
исключительной ситуации. Обычно это происходит, если в программе неправильно
определено условие: метод вместо вызова самого себя должен вернуть Значение.
:1fi;.- ‚н, _
м ,;‚ а:
Основное преимущество рекурсии заключается в том, что некоторые алгоритмы
рекурсивно могут быть реализованы проще и эффективнее, чем их итеративный
вариант (например, алгоритм быстрой сортировки достаточно сложен при его итера-
тивной реализации). Рекурсивные решения требуются и при решении некоторых
Задач, связанных с разработкой искусственного интеллекта.
Как правило, для возврата из метода значения без выполнения рекурсивного вызова
используется условный оператор, такой как if. УЧТИТС. что при неправильном
определении условного выражения после вызова метод будет бесконечно вызывать
сам себя. У начинающих программистов такая ошибка при использовании рекурсии
встречается достаточно часто. Мы рекомендуем для контроля над выполнением
программы как можно чаше использовать операторы WriteLine(); и прерывать д;
программу в случае обнаружения ошибки. '
‚(точеное слово static 243
Ключевое слово static
Иногда требуется определить член класса, который будет использоваться независимо
от дюбых объектов этого класса. Обычно доступ к члену шасса осуществляется е
„снользотзаньтезт объекта этого класса, но существует возможность создать член
класса, который может применяться самостоятельно без ссылки на созданн ы и объект
какого-либо тстасса. При объявлении такого члена класса используется ключевое
сдоно .‘=‘c::iC. Если член ктасса определяется с модификатором static, ОН стано-
Bum; доступным до создания объектов этого класса и без ссылки на какойъчттбо
объект. Ключевое слово stat: с может использоваться для объявления как методов,
так и переменных. Наиболее известным членом класса с модификатором static
является метод Маш (), который объявляется как static, поскольку должен вызы-
ваться операционной системой при запуске программы.
Для использования члена класса с модификатором s‘::.3I:1,': за пределами класса
следует‘ указать имя его класса, за которым следует оператор точка (.). В дтом случае
создавать объект нст необходимости. Фактически доступ к члену класса C модифи-
катором static нельзя осуществить, указав идентификатор объекта. доступ к нему
осуществляется только с использованием имени класса. Например, если переменной
static count, являющейся частью класса Тштек, необходимо присвоить значение
10, нужно использовать следующий оператор:
Timer.count = 10;
Этот синтаксис аналогичен синтаксису, используемому для доступа к обычной
переменной экземпляра, за исключением того, что вместо идентификатора объекта
Применяется имя класса. Метод с модификатором static также вызывается с
использованием имени его класса и оператора точка.
Переменная, объявленная с модификатором statlc. фактически является глобаль-
ноп переменной. При объявлении объектов ее класса не создается копия такой
ттеременной, вместо этого все объекты класса совместно исттользуют одну перемен-
ную с модификатором static, которая иниииализируется при загрузке ее класса.
Если начальное значение не указано явно, по умолчанию такой переменной при-
сваивается значение О (ссли это переменная числового типа), значение null (если
это переменная ссылочного типа) и значение false (если это переменная типа bool).
Таким образом, переменная с модификатором statlc всегда имеет значение.
Различие ктсжду‘ методом с модификатором Static И обычным методом состоит в том,
‘H0 “СТОП С ЭТИМ МОДРКЬИКНТОРОЁЧ МОЖСТ бЫТЬ ВЫЗВЗН С MC[']O."Ib30BflHl’YCM MMCHH СГО КЛЭССЗ
065 создания какого-л ибо объекта этого метода. Ранее нам уже встречался пример такого
вьтюва — метод Sqrt i’) C модификатором s1;at1c класса зузьетмапн.
H HIKC представлена программа. в которой создаются переменная и метод с модифи-
каторами static.
З программе демонстрируется использование модификатора statlc.
<3;ng System;
3~dSs StaticDemo {
’/ Объявление статической переменной.
Public static int val = 100;
// Объявление статического метода.
244 Глава 6. Детальное рассмотрение методов и классоду
public static int valDiv2() [
return val/2;
Class SDemo {
public static VO1d Main() {
Conso1e,writeL1ne("nepsouaqanbuoe значение переменной StaticDemo.val = "
+ StaticDemo.val);
StaticDemo.Val = 8;
Сопзо1е.Ик1пеЬ1пе("Значение переменной StaticDemo.val = " +
StaticDemo.val);
Сопзо1е.Ик1сеЬ1пе("Значение этой же переменной иосле выполнения метода " +
"StatiCDemo.valDiv2() = " + StaticDemo.valDiv2());
}
B результате работы этой программы будут выведены следующие строки и значения:
Первоначальное значение переменной StaticDemo.val = 100
Значение переменной StaticDemo.val = 8
Значение этой же переменной после выполнения метода StaticDemo.valDiv2() = 4
Как видно из результата работы программы, переменная с модификатором static -
инициализируется в начале работы программы до создания какого-либо объекта ее
класса.
Методы с модификатором static обладают некоторыми ограничениями:
О метод с модификатором static не имеет ссылки this;
O метод, объявленный как static, может непосредственно вызвать только другой
статический метод (метод с модификатором static). OH He может вызвать метод
экземпляра своего класса, потому что методы экземпляра класса могут работать
с данными экземпляров этого класса, а статические методы не могут;
О метод с модификатором static I/IMCCT прямой доступ только к статическим
данным, он не может использовать переменную экземпляра, поскольку не
работает с экземплярами своего класса.
Например, в приведенном ниже фрагменте метод valDivDenom () с модификатором
static является недействительным:
class StaticError {
int denom = 3; // Обычная переменная экземпляра.
static int val = 1024; // Статическая переменная.
/* Ошибка! Статический метод не может непосредственно обращаться к
обычной переменной.
*/
static int valDivDenom() {
return val/denom; // Этот оператор не будет скомпилирован.
}
)
Обычная переменная экземпляра, доступ к которой не может быть осуществлен ИЗ
метода с модификатором static, указывается с помощью параметра denom. ОДНаКО
использование Переменной val разрешено, поскольку это переменная статическая.
245
Ключевое слово static
в следующей программе та же проблема возникает при попытке вызова обычном)
исхода nor.Sta‘::1CMe‘::h(}, определенного в классе AnothetE.‘tat1cEr, rt д из методы
эхдсмепг () ‚ объявленного как stat; . И тоже определенного в этом классе.
;5ДП9 System;
c-:ss AnotherStatiCError {
1/ Обычный метод.
void nonStaticMeth() {
Console.WriteLine("Onepamop метода nonSta:;cMe:h().");
‚* Ошибка! Статический метод не может непосредственно обращаться К
обычному.
‘/
static void stat1CMeth() {
nonStaticMeth(); // ЭТОТ оператор не будет скомпилирован.
ъ
В этом случае попытка вызова обычного метода (то ссть метода экземпляра) из метода
с модификатором Static приводит к ошибке компилирования программы.
Обратите внимание, что метод с модификатором static может вызывать методы
экземпляра и получать доступ к Переменным экземпляра своего класса, но осушсст-
влять это необходимо, используя объект данного класса (то есть для доступа к утетоду
или переменной экземпляра нужно указывать название объекта). Приветтснт-тьтйт ниже
фрш мент кода является дстёсттзтттельньтгьт.
с‚а5з MyClass {
I/ Обычный метод.
void nonStaticMeth() {
Console.WriteLine("onepamop метода nonStaticMeth().“);
'* ИСПОЛЬЗУЯ переменную ссылочного типа, можно вызнать обычный метод из
статического метода.
*/
public static void s:aticMeth(MyClass ob) {
0b.nonStat1cMeth(); ff ЭТО действительный вызов метода.
минутных? практикум
Дайте определение рекурсивного метода.
Объясните различие между переменными с модификаторами statlt. и
переменными экземпляра.
3. Может ли метод с модификатором static вызывать обычный метод одного
с ним шасси без использовании: ссылки на объект.
I» Рскурсивным называется метод, который вызывает сам себя.
2» Каждый объект класса имсет свои собственные копии переменных экземпляра, определен
ные классом. Все объекты класса совместно используют одну копию переменной с моди—
фикатором static.
3. Нет.
э
246 глава 6. Детальное рассмот ение методов и классе
D B
~—-——\
ъ
Проект 6-3. Алгоритм Quicksort
Ea QSDemo.cs -' К;
В главе 5 был представлен простой алгоритм сортировки. называемый пузырьковым, г“;
а также было сказано, что существуют более эффективные методы сортировки. В этом Ё -
проекте мы разработаем версию одного из лучших способов сортировки Оцйскзоц
(алгоритм быстрой сортировки). Этот алгоритм не опиеывгьтся в главе 5, поскольку ‚_ _
его работа основана на рекурсии. Версия, которую мы будем разрабатывать. предна- 5’
значена для сортировки символьного массива. но принцип работы этого алгоритма
может быть применен для сортировки объектов любого типа.
Принцип работы алгоритма быстрой сортировки состоит в следующем — большой
объем данных делится на более мелкие части. которые успешно и быстро обрабаты-
ваются. Поскольку шгоритм Quicksorl применяется для сортировки элементов мас-
еива, то логично при делении этого объема данных на чаети использовать значение
ДЛИНЫ массива. Из значения крайнего правого индекса вычитается значение крайнего
левого индекса и делится на 2. Полученный результат является индексом элемента
(в дальнейшем мы будем называть его средней точкой), значение которого становится
ориентиром на первом этапе сортировки. Все элементы, имеющие значения большие
или равные значению средней точки, перемещаются в правую часть массива, а
Элементы, имеющие значения меньшие чем значение средней точки, перемещаются
в левую часть массива. То есть в результате выполнения первого этапа сортировки
массив как бы делится на две части. Затем этот же процесс повторяется с каждой
частью до тех пор, пока весь массив не будет отсортирован. Например, если в качестве
средней точки используется элемент со значением d, то после первого этапа сорти—
ровки элементы символьного массива f e d a c b будут перераспределены
следующим образом:
Ь
и
Первоначально f e d a с b
После первого этапа сортировки b c a d e f
Ё
.‚ _ "
3aTCM ЭТОТ ПРОЦССС ПОНТОрЯСТСЁТ ДЛЯ КЁОКДОИ ЧЕСТИ MHCCHBZL ТО ССТЬ ДЛЯ [я С .3 И ё г‘
Выбрать среднюю точку можно двумя способами A l‘IpOH3BO.’1bHO и путем нахождения
среднего значения небольшой группы знаккеньягя, взятых из массива. Лля огггигиальной
сортировки необходимо выбрать значение, находящееся в середине диапазона зна-
чений элементов массива, но для многих наборов данных это осуществить не очень
просто. B худшем случае выбранное значение будет одним из крайних значений (либо
минимальным, либо максимачьньпм). Но даже в этом случае алгоритм Quicksorl будет
работать корректно. B разрабатываемой нами версии программы в качестве средней П
точки выбирается средний элемент массива.
Пошаговая инструкция
I. Создайте файл и назовите его обод. ^
'Чщмдй
2. Создайте класс Qu1c1k:s.)rt, как показано ниже.
// Простая версия алгоритма Quicksort.
using System;
Class Quicksort {
Ключевое слово static 247
ж’
;/ Метод предназначен для вызова другого метода gs, B котором
// непосредственно реализован алгоритм Quicksort.
public statlc void qsort(char[] items) {
q5(i:ems, C, iCems.Length—l);
Рекурсивная версия алгоритма опщскзогп, предназначенная для сортировки
// символьного массива.
зсапьс vo;d qs(char" items, ;n: left, Lnt right)
;nL i. ]:
char x, y;
1 le t; j : right;
х 2 items{(left+r;gnt)/2];
do {
whiie((iLems[i} < x) && (i < right)) 1++;
while((x < :nems[j}) за (j > left)) ]~—:
if(; <: j) 4
y — itemsfij;
items[;] И ;Lems[jj;
ikems[j] y;
i++; 3'“;
1
} while(1 <: j):
if(left < j) qs(i:ems, Left, j);
1f(i < right) qs(;tems, i, right);
:'l:m упрощения использования класса Quicksort B НСМ объявлен метод qsort () ,
предназначенный для определения параметров и вызова метода qs (), который
непосредственно выполняет сортировку элементов. Это позволяет вызывать метод
едцэгпП, указывая только имя массива, который необходимо отсортировать.
Поскольку метод qs () используется только внутри класса, по умолчанию он
определяется как рштаце,
3. Ч гобы использовать mace еще 1531511, нужно вызвать метод щъцекгог: . от ( ) .
Поскольку метод qs ц г) определен как static, OH .\1()>KCT Bbl3blI3’dTbC$I c ИСПОЛЬ-
шванием имени его класса, а не объекта, следовательно, нст необходимости
создавать объект типа Счдщскзогг. После возвращения методом Значения массив
будет отсортирован. Помните, что эта версия проГраммы предназначена для
сортировки только символьного массива, но принцип работы атгоритма можно
использовать для сортировки маееива любого типа.
Ниже ктредстагзлена программа, демонстрирующая работу гыгоритма Эвз-‚экзогй.
Простая версия алгоритма Опьскзогс.
чъдпщ Sys:er;
9id5S QULCKSOIC
// Метод Предназначен для вызова другого метода gs, B котором
248 Глава 6. Детальное рассмотрение методов и классе
// непосредственно реализован алгоритм Quicksort.
public Static void qsort(char[] items) {
qs(items, О, items.Length—1);
// Рекурсивная версия алгоритма Quicksort, предназначенная для сортировку
// символьного массива.
static void qs(char[] items, int left, int right)
{
int l, j;
char x, y;
i = left; j = right;
X = items[(left+right)/2];
do {
while((items[i] < х) && (i < right)) i++;
while((x < items[j]) && (j > left)) j--;
if(i <= j) {
y = items[i];
items[i] : items[j];
items[j] = у;
i++; j——;
}
} while(i <; j);
if(left < j) qs(items, left, 3);
if(i < right) qs(items, i, right);
class QSDemo {
public static void Main() {
char[] a I { 'а', 'х'‚ 'а', 'r', 'р'‚ 'j', 'i' };
int 1;
Сопзо1е.Ип1Ее("Первоначальный массив: ");
for(i=O; i < a.Length; i++)
Console.Write(a[i]);
Console.writeLine();
// Выполняется сортировка массива.
Quicksort.qsort(a);
Соп5о1е.Иг1пе("Отсортированный массив: ");
for(i=O; i < a.Length; i++)
Console.Wr1te(a[i]);
Контрольные вопросы
-\.\- «(чана „щ -...а..›...мм„.. „мм r\u.':.\'>«\- „... „к ..«.... „м, зим swam-v\~)\ .r.».n~¢w~...- r>m~).- мы .
=›- ьм/„т-‚мдтш т`чьэшшшьеаъчущ‚„ .. мысы
249
wimpwsww выгрыз: и
1. Используя фрагмент кода
class X {
int count;
ОПрСДСЛИТС,ЯВЛЯСТСЯ.ЛИ корректньшлследукидий КОД:
class Y {
public static void Main() {
Х ob = new X();
ob.count = 10;
2. Где должен располагаться модификатор при объявлении члена класса?
'.v~o:-N’
3. B стеке используется принцип LIFO (last in, first out - «последним
зашел, первым вышел»). Стек часто сравнивается со стопкой тарелок —
тарелка, первой поставленная на стол, будет использована последней.
Создайте стек с именем Stack для хранения символов. Методы, осуще-
ствляющие доступ к стеку, назовите push () И pop(). Необходимо дать
пользователю возможность указывать размер стека при его создании. Все
остальные члены класса stack должны иметь закрытый доступ (private).
Подсказка: в качестве модели используйте класс Queue, B котором необ-
ходимо только изменить способ доступа к данным.
4. Задан следующий ютасс:
class Test {
int a;
Test(int i)
.- /-'<:u
{ a = i; 1
I
Напишите метод (swap ( ) ), меняющий местами содержимое двух объектов
типа Test, на которые ссылаются две переменные ссылочного типа.
5. Является ли корректным приведенный ниже фрагмент кода?
class X {
int meth(int a, int b) { ... }
string meth(int a, int b) { ... }
6. Напишите рекурсивный метод. предназначенный для вывода строки в
обратном порядке.
тми- -.~.~.
7. Как объявлять переменную, которую должны совместно использовать
все объекты класса?
8. Какие функции выполняют модификаторы параметров ref И out‘? Чем
они отличаются?
9. Напишите четыре варианта синтаксиса метода Main () .
I0. Какие вызовы будут действительными для заданного фрагмента кода?
void meth(int i, int j, [] args) { // ...
а) meth (10, 12, 19)
б) meth(10, 12, 19,
B) meth (10, 12, 19,
г) meth (10, 12)
params int
100)
100, 200)
«ищи, как: x.-.v4¢-«yr мм» -.м„<-‚идме„-т=„ш. ;:A’.\v\)</-. .
Перегрузка операто
индексаторы и L}
свойства
Е] Основные сведения о перегрузке
оператора
Перегрузка бинарных операторов
Перегрузка унарных операторов Ё,
С]
С]
С] Перегрузка операторов сравнения
Е] Применение индексаторов
С]
Создание свойств
Перегрузка операторов 251
/Z
В ГДШВС рассматриваются ТРИ СПСЦИШКЬНЫХ ТИПЗ ЧЛСНОВ КЛЗССЗЁ ПСрСГруЖЦСМЫС
Опсрдторы, ИНДСКСЗТОРЫ И СВОЙСТВЗ. С ИХ ПОМОЩЬКО МОЖНО СОЗДЗВЗТЬ ТИПЫ КЛИССОВ,
aHa_r[0I'H‘lHblC BCTpOCHHl>l.\1 ТИПЗМ.
перегрузка операторов
В с: можно определять действия. выполняемые оператором по отношению к
Кдкоцунттбутдь классу. который вы создаете. Это называется перегрузкой или переоп-
рег)с_кч/ис_и оператора. Функции оператора можно полностью контролировать, но
следует учитывать, что они различаются в зависимости от того, к какому классу
применяется оператор. Например, в классе, определяющем связный список, опера-
тор + используется для добавления объекта к списку, в классе, реализующем стек,
этот оператор применяется для помещения объекта в стек, в других классах он может
выполнять иные функции.
При выполнении перегрузки исходное предназначение оператора не теряется. Просто
к его арсеналу добавляются новые операции. которые будут применяться по отно-
шению к определенному классу. Поэтому, например, перегрузка оператора + для
обработки связного списка не приведет к потере его исходного предназначения —
выполнению операции сложения Целых чисел.
Основным преимуществом использования перегрузки оператора является то, что при
этом в программную среду можно легко интегрировать новый тип класса. Если для
класса определены операторы, то действия с объектами Этого класса можно выпол-
нять, используя обычный синтаксис (то есть вместо применения отдельного метода,
предназначенного для работы с переменными этого класса, достаточно ввести,
например, такую строку кода: объект т“ объект). Кроме того, объекты можно
использовать в выражениях, содержащих данные других типов.
Процесс перегрузки операторов очень похож на процесс перегрузки метода. Пере-
Гружая оператор, вы используете ютючевое слово Operator, определяющее метод
оператора, который задает его действие.
г Синтаксис метода оператора
В основном в С# используются два вида операторов —— унарные и бинарные (опера-
ТОРЫ. для которых указываются два операнда). Для их перегрузки применяют два
Вида методов оператора, синтаксис которых представлен ниже.
таксис метода, используемого для перегрузки унарного оператора.
_ scatic ret—type operator op(param—type operand)
Ghepaumm
//
CVIHTBKCI/IC M€‘I‘OlIr3, используемого ДЛЯ перегрузки бинарного оператора.
рч. .
оЁ°+—С 5сас1с ret—type operator op(param—typel operandl, param~typel
€rand2)
{
V Операции
252 Глава 7. Перегрузка оператора, индексаторы и свойст;
51
Перегружаемый оператор (например, ъ или /) подставляется вместо слова орд
Вместо словосочетанття гее-еуре указывается тип значения, возвращаемого пр
выполнении данного метода. Это значение может принадлежать к любому выбра
ному пользователем типу„ но обычно оно имеет тип масса. для которого выполняещ
перегрузка октераторгт. Поэтому перегружаемыс операторы могут использоваться
вьтражениях. Методам, переопределяющим унарные операторы, операнды передаю-д
ея с помощью параметра operand, а переопределяющим бинарные операторы, щ
помощью параметров operandl И operar1d2. 3
Для перегрузки унарных операторов операнд должен принадлежать к типу клас
для которого определяется оператор. Для перегрузки бинарных операторов тип ›
минимум одного из операндов Должен совпадать е типом ютасса, для которо
переопределяетсзт оператор. Таким образом, в C# невозможно перегружать операто
оператор + для гипа in: ИЛИ strlng. Кроме того. параметры оператора не дол q .
включать модификаторы ref ИЛИ Out. =
Перегрузка бинарных операторов
ном пространстве. Перегруженный оператор + выполняет сложение соответствую
координат двух объектов класса Тпгеев. Перегруженный оператор — вычитает‘ коо"
динаты одного объекта из соответствующих координат другого объекта.
// В программе демонстрируется использование перегруженных операторов.
using System;
// B классе содержатся три переменные x,y,z — Координаты объекта.
Class ThreeD {
int х, y, z; // Координаты объекта.
public ThreeD( ) {х : y = Z = 0; }
public ThreeD(1nt i, int 3, lnt k) { х 1 i; y — j; Z 2 k; }
// Перегрузка бинарного оператора +.
public static ThreeD operator +(ThreeD opl, ThreeD op2)
{
Three”) result ‘ new ThreeD U " L—{ficpc1py:u<a оператора + для объектов типа Three
/* Сложение значений двух соответствующих координат и возвращение
результата.*/
resul:.x : ор1.х + ор2.х; // В этих трех строках кода выполняется ‘Ё
resuL:.y — ор1.у + ор2.у; // операция сложения целых чисел,
result.z — ор1.х + op2.z; // для которых оператор + сохраняет
// свое исходное предназначение.
return resuli;
„а
[Перегрузка опсрагора v для объектов тина Three
// Перегрузка бутнарього оператора —.
public stafijc ThreeD operator —(ThreeD Opl, ThreeD op2)
„же. г‘:
4_‚___„‚„‚._.г„..„-.‚щ„.— ‚ V
перегрузка операторов
253
тпгеев result = new ThreeD();
/w обратите внимание на порядок следования операндов — ор1 является
левым операндом, а ор2 — правым. */
;esult.x 2 opl.x — ор2.х; // Оператор применяется к значениям,
resu1t.y = ор1.у — ор2.у; // имеющим тип int.
resu1t_z 2 opl.z — op2.z;
return result;
1
// Вывод значений координат Х, Y, Z.
public void show()
{
Console.WriteLine(x + ", " + у + ", " + 2);
}
}
class ThreeDDemo (
public static void Main() {
ThreeD a = new ThreeD(l, 2, 3);
ThreeD Ь = new ThreeD(lO, 10, 10);
тпхеев с 2 new ThreeD();
Console.Write("KoopnMHaTm объекта а:
a.show( );
Console. WriteLine();
Console. Write("KoopnMHaTu объекта b:
b.show( );
Console.WriteLine():
C 2 a + b;
C
C.show();
Console.WriteLine():
— а + Ь + с;
С
с.5пои();
Console.WriteLine();
Ь i C a; // Вычитание объекта а из объекта с. <——————————————————*—
Соп3о1е.Иг1Ее("Результат выполнения операции вычитания с — а: ");
.show( );
Console.Wr1teLine():
C ~ с b;
Console.Wr1te("Pe3ynbmaT ВЫПОЛНЕНИЯ операции
C.show( );
Console.WriteLine();
// Операция сложения объектов а и Ь.«—————————————————————
оп3о1е.Иг1се("Результат операции сложения объектов а и b:
// Операция сложения объектов a,
onsole.Write("Pe3ynbTaT операции сложения объектов а,
// Вычитание объекта Ь из объекта
");
Применение в выражениях
объектов класса ThreeD,
а также операторов + и —.
н);
");
Ь и с. ‹—————————————
Ь И с: ");
с.‹———————————————————
вычитания с ~ b: ");
254 Глава 7. Перегрузка оператора, индексаторьп и сво
Результат выполнения программы:
Координаты объекта a: 1, 2, 3
Координаты объекта b: 10, 1C, 10
Результат операции сложения объектов а И b: 11. `2‚ 13
Результат операции сложения объектов а, b И с: 22, 24, 26
Результат выполнения операции вычитания с — a 21, 22, 33
— b: 11, 12, 13
Результат ВЫПОЛНЕНИЯ операции ВЬЪЧИТЭНИЯ C
Проанализируем текст программы с момента перегрузки оператора т. K двум об
там типа Threeb применяется оператор +, в результате чего значения соответств '
щих координат складываются так, как определено в методе rater + (). О
использование данного метода не приводит к обновлению значений перемен
каждого операнда. После выгюлнсния метода возвращается новый объект
Тдгеев, содержащий результат выполнения операции. Чтобы понять, почему one
ЦИЯ сложения нс изменяет содержимое объекта, представьте дспствие стандарт“
арифгиетической операции сложения, например, в вьпражсниьи 10 + 12. Результа
выполнения операции будет число 22, но ни число |0_ ни число I2 не изменяю м
результате ее применения. Хотя не существует правила, запрещающего опера ’
после перегрузки изменять значения одного из операндов, желательно, чтобы п‘
перегрузки оператор сохранял свое первоначальное предназначение.
Как говорилось ранее. Метод operator + () возвращает объект типа ThreeD.
метод может возвращать значение любого допустимого в Cit типа, то, что опер
возвращает объект типа тьгеед, позволяет применять этот оператор в выраже
состоящих из нескольких операндов, например, a+!;-+r:.. Здесь, в результате вычи
выражения a+b будет возвращено Значение, принадлежащее иному типу, подо
выражение будет недействительным.
Внутри метода оре касок + () сложение значений отдельных координат сводите я
сложению Целых чисел. Отдельные координаты х, у и 2 представлены значениЁд
принадлежащими нелочисленному типу, поэтому перегрузка оператора + для 0
тов масса тькерв не окажет влияния на действия, производимые этим оператд _
над с целыми числами.
Рассмотрим метод -iiper-:1tOt — () . Оператор - фух-ткнионируег так же. как оператор
но при его использовании важен порядок расположения параметров. Напомним, _
сложение коммуггггивгко. а вычитание нет (то есть выражение ,-I — I? не '1‘O)K£1CCTBC
выражению В -A). Для всех бинарных операторов первый параметр в методе opera
ЯВЛЯЕТСЯ левым операндом, второй параметр —- правым. При перегрузке некоММУГа '1
НЫХ операторов необходимо учитывать взаимное расположение операндов. '
Перегрузка унарных операторов
Перегрузка унарных операторов реализуется аналогично перегрузке бинарных
раторов, но при этом используется только один операнд. В следующем при
рассматривается метод. перегружающий в классе Three-D оператор упарного МИ
// Перегрузка унарного
public static ThreeD operator —( ThreeD op)
Перегрузка операторов 255
Тддддо result new ThreeD();
‹.х A -op.x
.y ‘OP-Y
»,: — —op.z
re;u:n resulL;
}
K 3”;]'{L‘![ll}l.‘\1 O6'bCKTE1, l'lCpCLlEll*‘lHOTO B K'c\‘lCCTBC параметра ИСТОЛУ 096151015 т ()‚
ПрИМСПЯСТСЯ ОПСРЗТОр УНИрНЫЙ МИНУС (ТО ССТЬ ПОЛОЖИТСЁПЬНЫС ЗЕШЧСНИЯ СТННОВЯТСЯ
ОтриЦНТСЛЬН ЫМИ). ПОЛУЧСННЫС ЗНЗЧСНИЯ ПРИСВЗЪПЗЗЮТСЯ переменным СОЗДЗННОГО
ОбъСКТН [‘rE‘SL1lt, 32lTCM ЭТОТ ОбЪСКТ ВОЗВРЗТЦЗСТСЯ ОПСрЦТОрОМ return; B КЗЧССТВС
ре3у‚‘[Ь'12[ТЁ\. ООРЗТИТС ВНИМЭНИС, ЧТО ОПСРЗНП ОСТИЛСЯ НСИЗМСННЫИ. ЭТО НС противоре-
›[ИТ ООЫЧПОЧУ ПРСЦНИЗНЗЧСНИКО унариого wmyca. Например, B выражении
ОПСрИНДУ E. присваивается ЗНЭЧСНИС операнда b CO £HE1KOM МИНУС. НО ЗНЗЧСНИС
ОПСрЭНПН Ь при ЭТОМ НС ИЗМСНЯСТСЯ.
Однако в двух случаях (при использовании операторов инкрсмента и декремента) в
методе r-aerator должно быть изменено значение операнда. Поскольку обычным
предназначением операторов ++ и ~~ ЯВЛЯСТСЯ увеличение или уменьшение соответ-
ственно значения операнда на единицу, то перегруженные версии названных опера-
торов тоже должны вьтполнять эти действия. То есть при перегрузке оператора
инкремепта или декремента опсранд должен быть изменен. В качестве примера
рассмотрим переопределение метода орегапог ++ () для класса ТЬгееЕ.
H Перегрузка унарного оператора ++.
рвЬ11с static ThreeD operator ++(ThreeD ор)
I
// Выполняется изменение аргумента.
op . X+ r ;
Op_y~+; ‹ Опешпор++ьвменш
Op _ Zfl ’_ свои операнд.
‘return Op: // Возвращается измененный операнд.
Ниже приводится расширенная версия предыдущей программы. демонстрирующая
Использование уиариых операторов — и ++_
// .
Ц _В Г-Ртграмме демонстрируется использование перегруженных операторов.
sing System;
// _
Щ К“дСС‚ содержащий значения координат объекта в трехмерном пространстве-
Ё55 ThreeD {
in:
Х‘ У: Z; // Координаты объекта.
ThreeD() Ё Х ~ y : z z O; ‚
ThreeD(int i, in: j, ;nt k) { х — i; y 3; z 2 k; Е
// Перег
Рузка бинарного оператора +.
*iC static ThreeD operator +(ThreeD ор1‚ тнкеео ор2)
Du'D1 :
Т
Thr€eD result = new ThreeD();
256
/* Сложение значений соответствующих координат двух объектов
и возврат результата -
resui:.x 2 op1.x + op2.x;
resul:.y — ор1.у ор2.у;
:esulL.¢ op1.z op2.z;
return result;
// Перегрузка бинарного оператора —.
publ1c statlc ThreeD operator —(ThreeD opl,
Х
Threeb result :
/* Обра MTG внимание на порядок следования операторов.
находится слева, а ор2
resul:.x т ор1.х — ор2.х;
result.y : opl.y ~ op2.y;
result.z е ор1.2 — ор2.2;
return resulfi;
// Перегрузка унарного оператора —.
public static ThreeD operator —(ThreeD
I
ThreeD result ~ new ThreeD();
result.x : -op.x;
result.y = 'ор.у;
rcsul:.z = —ор.2;
return result;
I ‚
‚ Перегрузка унарного оператора тт.
public static ThreeD operafior +*(ThreeD op)
{
new ThreeD();
Глава 7. Перегрузка оператора, индексаторьп и свой
объекта ге5щ1с. '/
// В этих трех строках кода выполняется
// сложение ЦСЛЫХ чисел, для которых
// оператор — сохраняет свое исходное
‚ / предназначение.
ThreeD op2)
операнд ор1
справа. */
// Из одного целочисленного значения
// вычитается другое целочисленное
// значение.
ор)
v~.- т“ I u{"I"- v flvT'“€».’t‘9W.-3
// Выполняется изменение аргумента.
ор.хт+;
ор.у*+;
ор.2++;
return op;
pub-; void show()
Consoie.wri£eL;ne(x + ,
...
class ThreeDDemo (
Вывод значений координат Х, Y,
Z.
n+y+u’u+
„мы.-. ‚
г
ь
г
ь
Ъ
257
Перегрузка операторов
р„р11с static void Main() (
тпгееБ а - new ThreeD(l, 2, 3);
ThreeD b = new ThreeD(l0, 10, 10);
тпгеев с ~ new ThreeD();
;дп5о1е.Иг1се("Координаты объекта а: ");
д_5Нои():
‘gnsoie.Wr1:eL;ne();
.gnso1e.Wr;te("KoopumHawm объекта Ь: ");
_ snow();
Ctnsole.WriceLine();
5 ; a + b; // Операция сложения объектов а и b.
Console.Write("Pe3ynbTaT операции сложения объектов а и b:
.__- , show () ;
;0nso1e.WriteLine();
");
С Ё а + b + C; // Операция сложения объектов а, b И C
JQn5ole.Write("Pe3ynbTar операции сложения объектов а. b И с: ");
:.show();
Console.WriteLine();
г с — а; // Объект а вычитается из объекта b.
Соп5о1е.Иг1пе("Результат выполнения операции вычитания с — а: ");
c.show();
Console.WriteLine();
с L с — b; // Объект b вычитается из объекта с.
Соп5о1е.Иг1Ее("Результат выполнения операции вычитания с — Ь: ");
c.show();
Console.WriteLine();
C ; —a; // Объекту с присваивается значение объекта а,
// взятое со знаком минус.
// значения объекта а, взятые со знаком минус.)
Соп5о1е.Ик1се("Результат выполнения операции —а: ");
c.show();
Console.WriteLine();
(Переменным объекта с присваиваются
a++; // Выполнение перегруженного операторатт
Соп5о1е.Иг1Ее("Результат выполнения операции а++:
a.show();
");
PC 5_\'IlbT3T ВЫПОЛНСНИЯ ПрОГрЗММЫЁ
дрдинаты объекта а: 1, 2, 3
5 3рДинаты объекта b: 10, 10, 10
операции сложения объектов а и Ь: 11, 12, 13
операции сложения объектов а, b И с: 22, 24, 26
Ё53УЛЬТаТ выполнения операции вычитания с — а: 21, 22, 23
Езультат выполнения операции вычитания с — b: 11, 12, 13
дао Глава 7. I перегрузка оператора, индексаторы и своисш
I
|
Результат выполнения операции —а: —1‚ -2, ~3 ,
Результат выполнения операции a++: 2, 3, 4
Лсгко заметить, что операторы ++ и —— имеют префиксную и постфиксную фор
Например, обе формы записи оператора _
++0;
и
O++;
являются правильными. Однако в результате перегрузки оператора ++ или —— дд
обеих форм (постфиксной и префиксной) вызывается один и тот же мстод. Пр
выполнении перегрузки невозможно указать разницу между префиксной и постфикдд
пои форгиами этих операторов. ‚
Минутный практикум
I. Что происходит при перегрузке операторов? Какое ключевое слово при это
используется?
Каков синтаксис перегружаемого бинарного оператора?
3. Что придает уникальность псрегружаемым операторам ++ и ——'?
Дополнительные возможности класса ThreeD
Метод оператора может быть перегружен для любого класса или оператора. Давай ~
вернемся к классу ThreeD. Мы уже рассматривали перегрузку оператора +‚ с помощь:
которого суммировались координаты двух объектов, принадлежащих к классу ThreeD
Однако это не единственный способ изменения значения каждой координаты объек д
класса ThreeD. Еще, например, можно использовать метод, в котором к каждо р
значению координат прибавляется целочисленное значение. Для выполнения подобной
операции может понадобиться вторичная перегрузка оператора + (как показано ниже)?
// Перегрузка бинарного оператора +‚ первый операнд которого является
// объектом класса ThreeD, a второй имеет тип int.
public static ThreeD operator +(ThreeD ор1‚ int op2)
t’
ThreeD result : new ThreeD():
result.x = ор1.х + ор2;
result.y Opl.y + op2;
result.z = ор.2 + ор2;
В этом методе переопределяется оператор +
для выполнения операции сложения между
операндами, один из которых имеет тип
ThreeD, а другой принадлежит типу int.
I!
return result;
I. [1ри перегрузке для оператора определяклся действия. которые он будет выполнять по
ОТНОШСНИЮ К СОЗДЯВЭСМОМУ КЛЭССУ. ИСПОЛЬЗУСТСЯ КЛЮЧСВОЁ СЛОВО operator.
2. Перегрузка бинарного оператора имеет следующий синтаксис:
public static ret~type operator op(param~type1 operandl, param~type operand2)
(
//операции
)
3. Когда перегружается оператор ++ или ——, методом operator изменяется значение операнда
При перегрузке других операторов значение операнда не изменяется.
перегрузка операторов 259
Обратите внимание, что второй параметр принадлежит K ЦСЛОЧИСЛСННОМУ типу.
Поэтому в приведенном выше методе допускается сложение значении каждого поля
(Переменной, определенной в классе) объекта типа ThreeD C целочисленным значе-
ние“ Эта операция допустима. поскольку при перегрузке бинарного оператора один
„3 операндов должен принадлежать к типу класса, для которого перегружается
данный оператор, а второй операнд может принадлежать к любому другому типу.
Ниже приведена версия программы, включающая два метода, которые перегружают
Оператор т В классе Th reeD.
/« в классе ThreeD перегружается оператор + для пары операндов, имеющих тип
ThreeD, И для пары операндов, один из которых имеет тип ThreeD.
a другой тип int.*/
uglnq System;
‘ ¢an4)u.».«..mL».x..«~ .. a м. ., ..
// Класс, содержащий координаты объекта В трехмерном пространстве.
class ThreeD {
дн: х, y, 2; // Координаты объекта.
public ThreeD() { x = у = 2 = O; }
public ThreeD(int i, int J, int k) { x : i; y = j; z 2 k; }
// перегрузка бинарного оператора + для пары операндов,
// принадлежащих к типу ThreeD.
public static ThreeD operator +(ThreeD ор1, ThreeD ор2)
‹
ThreeD result = new ThreeD();
/* Сложение значений соответствующих координат двух объектов
и возвращение результата. */
result.x = ор1.х + ор2.х; // В этих трех строках кода выполняется
result.y = ор1.у + ор2.у; // операция сложения целых чисел, для
// которых оператор + сохраняет
result.z = ор1.2 + op2.z; // свое исходное предназначение.
return result;
,,.,.rn..-A
// Перегрузка бинарного оператора +‚ операндами которого в данном
// случае являются объект класса ThreeD И переменная типа int.
public static ThreeD operator +(ThreeD ор1, int ор2)
ThreeD result 4 new ThreeD();
result.x — ор1.х 1 op2;
result.y V opl.y + ор2;
result.z = ор1.2 + ор2;
У
return result;
// Отображение координат Х, Y, Z.
public void show()
{
COnSOle.WriteLine(x + ", " + y + " " + Z);
I
.i4___}l
260 Глаза 7. Перегрузка оператора, индексаторы и свойса
class ThreeDDem0 i
publlc static void Ma1n() i
ThreeD a : new ThreeD(1, 2, 3):
ThreeD b — new ThreeD(1C, 10, 10);
ThreeD C : new ThreeD();
Соп5о1е.Иг1Се(“Координаты объекта а: "):
(1.show();
Cnnsole.WriteLine():
Соп5о1е.шг1Ее("Координаты объекта b: "):
b.show();
console.Wr1teL1ne();
C W a + b; // Объект + объект.
Соп5о1е.Иг1се("Результат выполнения операции сложения " +
"объектов a И D: "); I
g.show(); I
Console.WriteLine();
с = b + 10; // ООЪЕКТ - Целое число.
Сопзо1е.Иг1Се("Результат выполнения выражения b + 10: ");
c.show();
Е
Результат выполнения программы: 7
координаты объекта а: 1, 2, 3
Координаты объекта b: 10, 10 10
Результат выполнения операции сложения объектов a И b: ll, 12, 13 Р
Результат вычисления выражения b + 10: 20, 20, 20
ВЫВОДИМЬПС результаты подтверждают. если оператор + применяется к двум объектам,
то их координаты суммируются. Если оператор + применяется к объекту и Целочис-
лснному значению, координаты объекта увеличиваются на это целочисленное зна-
чснис.
Хотя при перегрузке оператора v класс Тпгеев приобретает некоторые полезные .
свойства, mm нормальной работы оператора требуется сго вторичная перегрузка.
11ричипь1ЭтоП)вточ.что:ип1мст0Даорег3ьтг+(ТлгееЭ‚ 1п;)Д0ПустИм0ЬшП0ль—
зованис опсраторош ПОДОбНЫХ указанному пшже:
ц-ш. ‚„;.‚з...в„ мед. .;.:sr.m.,...z~:q.-.1.
obl a ob2 4 10;
НО ДЛЯ ЭТОГО МСТОДЦ НС ДОПУСКНСТСЯ ТНКЦЯ ‘ЗНПИСЬ опсратора:
obi 10 + ob2;
Дело в том, что в этом операторе Цслочис:псннь1й аргумент располагается слева от
оператора +, а метод C-perat-:.-r т ( ) ожидает. что он будет находиться справа. Для
того чтобы можно было использовать обс формы записи оператора, потребуется
перегрузить оператор + сшс раз. При этом первый операнд должен принадлежать к
типу mt, а второй ~ К тппу тпггеглэ. То есть должна существовать возможность
ПрИМСНСНИЯ оператора + KHK ДЛЯ ОПСрНЦПИ объект+1‚4с’:1(›т/испс/‚пкт 3H(lll(’llIl(7, TZIK И ЛЛЯ
.. —\-W W W.../-.\,....
перегрузка операторов . 261
Операции целочишенное 3начение+0бЬекп1. Таким образом, {хвойная перегрузках опера-
TOW r (или любого другого бинарного оператора) обеспечит использование встро-
енного типа (в Данном случае mt) как в лспои, так и в правой части оператора. Ниже
ПОКЦЗПНЦ ВСрСИЯ КЛЗССН ThreeD, B КОТОрОИ оператор ° перегружается COl'_.‘IaCHO
on ИСПЕППОМУ ЦЛГОрИТМУЁ
/, „ереррузка оператора т ДЛЯ операций "объект + объект“,
"Объект * Целое число" и "целое число + объект". '/
uslfig System;
;JCC, содержащий значения координат объекта Ь трехмерном
транстве.
Стдзз 3hreeD i
;n: х, y, 2; // Координаты объекты.
рцодзс ThreeD() { x y Z C; )
public ThreeD(int i, lnt j, in: k) { x = 1; y 2 j; z ; k;
ух Перегрузка бинарного оператора + для операции "объект + объект".
puolic static ThreeD operator +(ThreeD ор1, ThreeD op2)
ThreeD result = new ThreeD();
5‘ Сложение значений соответствующих координат двух объектов
и возврат результата. */
result.x = ор1.х + ор2.х; // В этих трех строках кода выполняется
result.y = opl.y + op2.y; // Сложение целых чисел, для которых
result.z = opl.z + op2.z; // оператор + сохраняет
// свое исходное предназначение.
Г1срегру3ка 0перат0ра‚в результате которой
можно выполнять операцию объект + цело-
ЧЦСЛЕННОЕ ЗНПЧЁНЦЕ.
return result;
/1 Перегрузка бинарного оператора т для операции "объект +
‚
‚ целочисленное значение".
public static ThreeD operator +(ThreeD opl, int ор2)
ThreeD result 4 new ThreeD();
result.x ор1.х + op2;
result.y = opl.y + op2;
result.z = ор1.2 + ор2;
Персхрузка оператора, в результате которой
можно выполнять операцию целочисленное
значение +-обьекпъ
return result;
'/ Перегрузка бинарного оператора + для операции
// "целочисленное значение * объект".
public statlc ThreeD operator +(;nt opl, ThreeD op2)
ThreeD result = new ThreeD();
result.x = ор2.х + opl;
result.y I op2.y + opl;
rcsul:.Z I op2.z T opl;
I
262
return result;
// Отображение координат Х, Y, Z.
public void show()
{
Consoie.WriteLine(x + ", " + y + ", " + z);
}
}
class ThreeDDemo {
public static Void Main() {
ThreeD a = new ThreeD(1, 2, 3);
ThreeD b — new ThreeD(10, 10, 10);
ThreeD с е new ThreeD();
Соп$о1е.Иг1ке("Координаты объекта а: ");
a.show();
Console.WriteLine();
Соп$о1е.Иг1пе("Координаты объекта Ь:
b.show();
Console.WriteLine();
");
с 4 a + b; // Операция объект + объект.
Соп5о1е.Иг1се("Результат выполнения операции а + Ь:
c.show();
Console.WriteLine();
");
с = b + 10;
Console.Write("Pe3ynbTaT выполнения операции b + 10:
c,show();
Console.WriteLine():
// Операция объект + целочисленное значение.
");
C 2 15 + Ь: // Операция целочисленное значение + объект.
Соп5о1е.Иг1пе("Результат выполнения операции 15 + b: ");
c.show();
)
P63)/fl bT3T ВЫПОЛНЕНИЯ ПрОГрЗММ Ы:
Координаты объекта а: 1, 2, 3
Координаты объекта Ь: 10, 10, 10
Результат выполнения операции a + Ь: ll, 12, 13
Результат выполнения операции b + 10: 20, 20, 20
Результат выполнения операции 15 + b: 25, 25, 25
Перегрузка операторов сравнения
Операторы сравнения, такие ка == или <‚ также могут быть перегружены, причем
ЭТОТ Процесс является очень простым. Как правило, перегруженные операторы
сравнения возвращают значение true или false. B результате остается возможность
обычного использования этих операторов, а перегруженные опергТ0РЫ сравнения
могут использоваться в условных выражениях. Если возвращается какой-либо иной
Глава 7. Перегрузка оператора, индексаторы и свойст _.‘
перегрузка операторов 263
тип результата, то изменяется первоначальное предназначение Данного оператора.
„го нежелательно.
ЪП1ЖС описана версия класса Threelk B которой ВЬНЧОЛНЯСТСЯ перегрузка операторов
.: п > Согласно перегруженным версиям ЭТИХ операторов один объект считается
больше другого, если все координаты Данного объекта больше соответствующих
К0орДИНаТ ВТОр0Г0 ОбЪСКТ8 (И ОДИН ОбЪСКТ СЧИТИСТСЯ меньпл:лругого,еели ВСС CFO
координаты меъпяие координат другого объекта)
I’ B программе демонстрируется использование
;‘ перегруженных операторов "<" и ">".
us;ng System;
I
// Класс, содержащий значения координат объекта в трехмерном
// пространстве.
class ThreeD {
int х, y, Z; // Координаты объекта
public ThreeD() { х — у = 2 = О; )
public ThreeD(int 1, int j, int k) { х e 1; y = j; 2 — k; )
// Перегрузка оператора "<".
public static bool operator <(ThreeD opl, ThreeD ор2)‹——————————
I
if((Opl.x < op2.x) && (opl.y < op2.y) && (opl.z < op2.z))
return true;
е 1 se Возвращает значение true,
return false; если объект ор1 меньше,
чем объект ор2.
// Перегрузка оператора ">".
public static bool operator >(ThreeD opl, ThreeD ор2)‹Ь————————
if((op1.x > op2.x) && (opl.y > op2.y) && (opl.z > op2.z))
return true;
else
return false;
Возврацшетзначение true,
если объект ор1 больше,
чем объект ор2.
// Отображение координат Х, Y, Z.
public void sh0w()
Console.WriteLine(x + ", " т y + ", " + 2);
Class ThreeDDemo (
public static void Main() {
ThreeD a : new ThreeD(5, 6, 7);
ThreeD b ; new ThreeD(lO, 10, 10);
ThreeD c e new ThreeD(l, 2, 3);
Соп5о1е.Иг1се("Координаты объекта а: “);
a.show();
Соп5о1е.Иг1ее("Координаты объекта b: “);
b.show();
С0П$О1е.Иг1Ёе("Координаты объекта с: ");
C.show();
Console.WriteLine();
г golf Глава 7. Перегрузка оператора, индексаторы и свойс ‘
С) Соп5о1е_Иг1сеЬ1пе("Выражение
1f(a > a > С верно.");
д;(д < с) Соп5о1е.Иг1ееЬ1пе("Выражение a < с верно.");
1[(а > b) Conso1e_Wr1:eL;ne("Bmpaxeume a > b верно."):
;fi(a < b) Console.WriteL;ne("Bupaxeume a < 0 верно.");
Pc5),1bI2lT выполнения программы
ты объекта a: 5 Б 7
дрду“ р I
_ др ы объекта b: „О, дБ, 1C
К@ординл.ы Объекта C: 1, 2, 3
вьдрзжч; из а > с верно.
выражскле a < b верно.
Перехрузку операторов сравнения необходимо осуществлять попарно (наприме
„д, перегружается оператор «с, нужно также перегрузить оператор > и наоборот
в Cs, как н в любом Другом языке программирования, существуют следующие пар
операторов:
< >
(з >—
52
{Яри/мечение д?
Если перегружается пара операторов := И !=, то обычно требуется переопределениеё
методов obj ect.Equals () И ObjeCt.GetHashC0de (). Эти методы и технология
переопределения рассматриваются в главе 8.
Основные положения и ограничения при перегрузке i
операторов
Действие перегруженного оператора. применимое к объекту класса. для которого он
определен, не связано со стандартным использованием оператора для встроенных“
Типов Св. Во избежание усложнения чтения программы по возможности перегру-
жайте Операторы так, чтобы они выполняли действия, для которых изначально
предназначались. Например, оператор +, перегруженный для работы с объектом
шасси тпгеео, подобен оператору +, определенному для целочисленных типов
‚данных. Если по отношению к какому-либо классу определить действия оператора +
так. что вместо операции сложения будет выполняться операция деления, это просто
Приведет к путанице, не давая никаких преимуществ.
“Срсгрузка операторов связана с определенными ограничениями. Так, невозможно
изменить приоритет операторов. Также запрещено изменение количества операндов,
“ Которых нуждается оператор. Кроме того, существует множество операторов,
К°1°Р1‚:с не могут быть перегружены. В первую очередь к ним относятся оператор
Прпсваивания и составные операторы присваивания, такие как т=. Ниже приводится
“Чрсчень других операторов, которые не могут быть перегружены.
дм
G I I I J new is
' ` ‘::-. Typeof ? -> _ —
‚т
1‘
Иддексаторьт 265
Минутный практик/гул:
1_ Что требуется сделать, чтобы для переопределенного оператора была возмож-
ность указывать первым операндом переменную типа 1111, а вторым — объект
необходимого класса и наоборот?
I Какой тип данных обычно возвращают персгруженньте операторы сравне-
пия‘?
3. Можно ли перегружать оператор присваивания?
Ётеетег арэфаеееевваа
3 Bonpoc. Вы говорите, что составные операторы присваивания перегружать
нельзя. Что произойдет. если в качестве операнда для составного оператора присваи-
Baum: +: указать объект класса, для которого был определен оператор +?
Ответ. В общем случае, если какой-либо оператор был переопределсн, то при
его использовании в составном операторе присваивания вызывается метод перегру-
женного оператора. Таким образом, оператор +: автоматически использует пользо-
вательскую версию метода operator ч). Например. если при работе C классом
T;c:==,~:D воспользоваться строками кода
ТЬгесЮ а = new ThreeD(l, 2, 3);
Thx'eeD b = new ‘EhreeD(lU, 10, 10);
b -—' a; //BblI1OJlHHE3"i'CS-I операция сложения объектов а и b
ПРОИСХОДИТ НВТОМНТИЧССКИЙ ВЫЗОВ МСТОДЗ operator + ДЛЯ объекта типа ThreeD,
причем при его выполнении переменной b будут присвоены координаты ll, I2, 13.
IMHp.eKcaTopbI
Вы знаете, что индексация массивов производится с помощью оператора [ _- Этот
оператор может быть перегружен для создаваемых вами классов, но в таком случае
не будет использоваться метод operator. Вместо этого метода создается индексатир.
В результате к содержащемуся в объекте массиву можно осуществить доступ, указав
имя объекта, а не имя массива. Основная область применения индексаторов —
Поддержка создания специализированных массивов, на которые накладывается одно
или несколько ограничений. Индексаторы позволяют работать с любыми значения-
чи. используя почти тот же синтаксис, что и при работе с массивами. Индексаторы
могут иметь одно или несколько измерений.
НЦЧНСМ рассмотрение ТСМЫ С ОДНОМСрНЫХ ИНДСКСЦТОрОВ, КОТОРЫС ОПрСДСЛЯЮТСЯ С
ЧСПОЛЬЗОВННИСМ СЛСДУЮЩЁГО СИНТЭКСИСНЁ
„степе-Суре thj_s[in: lndex] {
// Метод доступа get.
чес (
// Возврат значения, указанного с помощью параметра index.
I. Оператор / требуется перегружать двумя методами, причем в одном случае объект должен
быть вторым параметром, в другом случае — первым.
Операторы сравнения обычно возвращают значение тн па bool.
HGT.
pa (J
266 Глава 7. Перегрузка оператора, индексаторы и свойс
// Метод доступа set
set {
// Присваивание (например, элементу массива) значения,
// указанного с помощью параметра lndex.
„и
}
Здесь вместо словосочетания element—Cype указывается базовый тип индексаторд
В рсзштьтате каждый элемент, доступ к которому осуществляется с помощью индек,
сатора. Должен принадлежать к типу element—type (то есть будет вьтполнятьсд
доступ к массиву, Элементы которого имеют указанный тип). Вместо параметра indeg
указывается индекс Элемента, к которому производился доступ. Этот параметр на
обязательно должен принадлежать к типу int, HO поскольку индексаторы обычно
применяются для индексирования Элементов массива, чаще всего используется
именно целочисленный тип,
В составе индексатора определены два метода доступа, get: И Set. Структура метода
доступа подобна структуре обычного метода за исключением того, что для метода
доступа не указывается тип возвращаемого значения и он не имеет параметров. Оба
метода доступа при использовании индексатора вызываются автоматически и при—
нимают в качестве параметра значение, указанное вместо слова index. Если индею
сатор находится в левой части оператора присваивания, вызывается метод доступа
set, a значение второго операнда присваивается элементу, указанному с помощью
параметра index. Значение передается методу доступа с помощью неявного парамет‹
ра value (B приводимой далее программе обратите внимание на синтаксис приме-
нения этого неявного параметра — переменная value НИГДС нс объявляется, и ее тип
нигде не указывается). Если индексатор находится в правой части оператора при-
сваивания, то вызывается метод доступа get, B результате выполнения которого
возвращается значение, ассоциированное с параметром index.
Одно из преимуществ, обеспечиваемых индексатором, заключается в том, что он
гарантирует возможность точного контроля над доступом к массиву (предотвращается
некорректный доступ). Например. использование индексаторов является наилучшим
вариантом реализации массива с применением алгоритма предотвращения сбоев,
созданного в главе 6. (При этом в код программы внедряется условный оператор if,
проверяющий. не выходит ли указываемый индекс за пределы массива, то есть просто
не допускается возникновение ошибочной ситуации.) Это достигается путем приме-
нения индексатора, обеспечивающего доступ к массиву с помощью обычного син-
таксиса, используемого при работе с массивами.
// Для доступа К элементам массива В программе применяется индексатор,
// в котором используется алгоритм предотвращения сбоев.
using System;
class FailSoftArray (
int[] а; // Ссылка на массив.
public int Length; // Переменная Ьепосп объявляется как public.
public bool errflag; // Переменная возвращает значение, указывающее,
// выходит индекс, передаваемый индексатору в качестве
// параметра, за пределы массива или нет.
Гшдексаторь. 267
// Создание массива заданного размера.
public FailSoftArray(int size) {
д L new int[si2e];
Length = size;
;/ индексатор Для класса FailSoftArray.
I
Pub1;c int this[iI’1t index] { M}mcKcaT0p для класса Fa1lS<)ftArray.]
// Метод доступа get.
get {
if(ok(index)) {
errflag = false;
return a[1ndex];
} else {
errflag = true;
return O;
1
x, Метод доступа set.
set (
if(ok(index)) (
a[index] = value;
errflag = false;
}
else errflag = true;
// Возвращает значение true, если индекс находится в заданных
// границах массива.
private bool ok(int index) {
if(index >= О & index < Length) return true;
return false;
'/ Показана работа с массивом c применением алгоритма предотвращения сбоев.
C;ass ImprovedFSDemo {
public static void Main() {
FailSoftArray fs = new FailSoftArray(5);
int х;
// При указании индекса, выходящего за пределы массива,
// выводится соответствующее сообщение.
Console.WriteLine("HpMMeHeHMe алгоритма предотвращения сбоев.");
for(int i;O; i < (fs.Length * 2); i++)
fs[i1 = i*l0;< "$Bbm0uMcnmanomyuaseL
for(int ieO; i < (fs.Length * 2); i++) (
х = fs [i] ; 4 J]BI)x3oB метода доступа get.
if(x != -1) Console.Write(x + " ");
}
Console.WriteLine();
// Задается условие, приводящее к ошибке.
268 Глава 7. Перегрузка оператора, ИНд9КСаТ°рЬ' И Свой
Соп5о1е.Иг1сеЬ1пе("\пСбой с сообщением об ошибке."\;
£or(int i—O; i < (fs.LengLh * 2); 1++) (
fs[;] — 1."lC;
;f(fs.errflag)
Console.Wr;teLine("Hpm обращении к элементу fs[" + i +
превышены граничные значения индекса массива.");
for(int i;O: 1 < (fs.Length * 2); i++) {
x : fs[i};
if(!fs.errflag) Console.Write(x + " ");
else
Console.Wri:e(“\nHpm обращении к элементу Е5[" т i + Ъ
"] превышены граничные значения индекса массива."); Ё
} x
Ъ
Результат выполнения программы:
Применение алгоритма предотвращения сбоев.
О 10 20 30 40 O О О О О Ё
Сбой С сообщением об ошибке. ‚
При обращении к элементу fs[5] превышены граничные значения индекса массива
При обращении к элементу fs[6] превышены граничные значения индекса массива
к элементу fs[7j превышены граничные значения индекса массива
При обращении к элементу fs[8] превышены граничные значения индекса массива
При обращении к элементу fs[9] превышены граничные значения индекса массива
О 10 20 30 40
При обращении
При обращении
При обращении
к элементу fs{5] превышены граничные значения индекса массива
к элементу fs[6] превышены граничные значения индекса массива
При обращении к элементу fs[7] превышены граничные значения индекса массива
При обращении к
При обращении к
элементу fs[8] превышены граничные значения индекса массива
элементу fs[9] превышены граничные значения индекса массиваа
ЭТОТ РСЗУЛЬТНТ ННИЛОГИЧСН рСЗУЛЬТНТУ ВЫПОЛНЕНИЯ ПРОГрНММЫ ИЗ главы В ДННН
версии используется индсксатор, предотвращающий превышение граничных значё
нии индекса массива. Теперь подробнее рассмотрим организацию индексаторё-
Объявление ннлсксатора Начинается co следующей строки:
public int this[int index] {
ЗДССЬ объявляется индексатор, который производит операции с Элементами, имею-
щими тип int. Индексатор объявляется как public И может использоваться колом,
не относящимся к его классу. Ниже показан код метода Доступа get:
get (
if(ok(index)) {
errflaq = false:
re:urn a[;ndexj;
} else {
errflag — true;
return О;
1
Благодаря ИСПОЛЬЗОВННИЮ МСТОДН ДОСТУПЕ get предотвращаются ОШИбКИ, ВОЗНИКЭЮ’
ЩИС при превышении граничных ЗНЗЧСНИЙ ИНДСКСН маееива. ЕСЛИ УКНЗНННЫЙ ИНДСКС
‘индексаторы 259
Находится внутри границ, возвращается Элемент, соответствующий этому индексу.
Если ИНДСКС ВЫХОДИТ 321 ПрСДСЛЫ МЗССИВЦ, ТО ВЫПОЛНЯСТСЯ СТрОКЗ КОЛИ return Ci,
To есть вместо элемента масеива возвращается значение 0. B данной версии класса
_ д‚3:5пАггау переменная errflag COI[Cp>KMT результат проверки (то есть провс-
‘Ёстся, выходит ли указываемый индекс за пределы массива). Это поле (переменная)
“Ожст проверяться после выполнения каждои попытки доступа для оценки ее
рспшдности и для выведения информации об ошибке. (B главе IO будет рассмотрен
ёшс один способ обработки ошибок с помощью подсистемы исключений С#‚ но на
данном этапе достаточно использования флага ошибки, то есть булевой переменной
e;y:iag)
Ниже приводится код метода доступа зев. в котором также предотвращается превы-
ШСППС граничных ЗНЕЧСНИЙ ИНДСКСЗ HEICCI/IBZI.
Se:
;i (ok(index)) (
a[index] = value
errflag = false;
e;se errflag = true;
}
Если параметр lndex не выходит за пределы массива, значение, передаваемое
неявным параметром value, присваивается соответствующему элементу. В против-
ном случае параметру errflag присваивается значение true. Как вы помните, в
методе Доступа используется неявный параметр value, который содержит присваи-
ваемое значение. Этот параметр не требует объявления.
Методы доступа get и set He должны поддерживаться индексатором одновременно.
Можно создать индексатор, предназначенный лишь для извлечения Элемента, вы-
Полнив реализацию только метода доступа get. Можно создать индексатор. предна-
значенный лишь для присваивания значения элементу массива, реализовав только
метод Доступа set.
Инлексатор не обязательно должен работать с массивом. Просто для тех, кто
использует индексаторы, способ работы с элементами индсксатора должен быть
Похож на способ работы е массивом. Например, в следующей программе используется
индексатор, подобный массиву, который содержит степени числа 2 (от нулевой до
пятнадцатой) и предназначен только для чтения. Однако обратите внимание на то,
что массива в данном случае не существует. Вместо этого индексатор просто вычис-
ляет подходящее значение, исходя из указываемого индекса.
г’ Индексаторы Не обязательно должны работать с массивами.
„:;п9 System;
C-ass PwrOfTwo {
’* Доступ к логическому массиву, который содержит степени числа 2
(от нулевой до пятнадцатой). */
public int Chis[int index} {
// Вычисление И возврат степени числа 2.
get (
if((index >= О) && (index < 16)) return pwr(;r1dex);
else return -1;
... V Глава 7. Перегрузка оператора. индексаторы и свойст
// Отсутствует метод доступа set
int pwr(int Р) {
int result = 1;
for(int i=0; i<p; i++)
result *= 2;
return result;
class UsePwrOfTwo {
public static void Main() {
PwrOfTwo pwr = new PwrOfTwo();
Соп5о1е.Иг1Се("Первые восемь степеней числа 2: ");
for(int 1:0; i < 8; i++)
Conso1e.Write(pwr[i] + " ");
Conso1e.WriteLine();
Console.Write("UpM указании индексов —1 и 17 возвращены "+
"результаты: ");
Cons0le.Write(pwr[-li + " " - pwr[l7});
} ‘Ё
РСЗУЛЬТНТ ВЬЕПОЛНСНИЯ ПрОГрНММЫЁ
Первые восемь степеней числа 2: 1 2 4 8 16 32 64 128
При указании индексов —1 и 17 возвращены результаты: —1 —1
Заметьте, что в индексаторе для класса PwrOfTwo присутствует метод доступа get,
НО отсутствует метод доступа set. Это означает, что индексатор предназначен только
для чтсния. Следовательно, объект класса Pwrof Two МОЖСТ находиться в правой части
опсратора присваивания, но не может находиться в левой. Например, будет неудач-
ной попытка использования в предыдущей программе оператора
pwr[O] = 11; // Этот оператор не может быть скомпилирован.
ИСПОЛЬЗОВННИС ЭТОГО ОПСрНТОрН ПрИВСДСТ К ОШИ6КС КОМПИЛЯЦИИ, ПОТОМУ ЧТО ДЛЯ
индексатора НС ОПРСДСЛСН МСТОД 110cTyna set.
При ИСПОЛЬЗОВННИИ ИНДСКСНТОрОВ СУШССТВУС’! ОДНО ВНЖНОС ОГрЭНИЧСНИСЁ ДЛЯ НИХ Не
определена ВОЗМОЖНОСТЬ ПРИМСНСНИЯ ПЗрНМСТРН ref ИЛИ out.
Многомерные индексаторы
Индексаторы могут создаваться и для многомерных массивов. Рассмотрим двухмер-
ный массив (и двухмерный индсксатор), в котором используется алгоритм предот-
вращения сбоев. Обратите внимание на способ объявления индексатора.
// В программе демонстрируется применение двухмерного индексатора,
// использующего алгоритм предотвращения сбоев.
using System;
class Fai1SoftArray2D (
int[,I a; // Ссьшка на двухмерный массив.
нддксаторьп 2 I 1
int rows, cols; // Ряды и колонки двухмерного массива.
Ublic int Length; // Переменная Length объявляется открытой.
P
public bool errflag; // В переменной errflag фиксируется результат
// попытки доступа к элементу массива.
„ Создание массива по заданным размерам.
ЬЫЬДЁС FailSoftArray2D(int r, int C) i
rows e r;
gels : c;
д z new int[rows, cols];
Length з rows * cols;
3
2
// индексатор для класса FailSoftArray2D.
public int this[int indexl, int index2] ( flnwmwpumfixuuwxunop
// Метод доступа get.
A.".I'.‘-l‘I'W"‘fi - -
чей {
if(ok(indexl, index2)) I
errflaq = false;
return a[indexl, index2];
} else (
errflag : true;
return O;
ъ // Метод доступа set.
set (
if(0k(indexl, index2)) {
a[indexl, 1ndex2] 2 value;
errflag 2 false;
]
else errflag — true;
// Возвращение значения true,
II/' ЕСЛИ ИНДЕКСЫ не ВЬХХОДЯТ за ГРЗНИЧНЬХЕ значения индексов МЗССИВЗ.
prlvate bool ok(int indexl, int index2) (
if(indexl >= О & indexl < rows &
index2 >= O & index2 < cols)
return true;
return false;
/= Использование двухмерного индексатора.
Class TwoDIndexerDemo (
public static void Ma1n() {
FailSoftArray2D fs : new FailSoftArray2D(3, 5);
int x;
// При выполнении следующего цикла for происходит обращение к
// несуществующим элементам массива, то есть возможно возникновение
// ошибочной ситуации. Однако такая ситуация была обработана ранее A
// при ее возникновении переменной errflag присваивается значение true И
// возвращается значение О.
Console.WriteLine("npM выходе за границы массива возвращается значение О.");
. пива 1. перегрузка оператора, инцексаторы и свой
f0r(int i;C; 1 < 6; i++)
fs[i, 1] = 1*lO;
for(int i=0; i < 6; i++) (
х = fs[i,i];
if(x != -1) Console.Write(x + " ");
}
Console.Wr;teLlne():
// Теперь переменная errflag используется как флаг ошибки, то есть
// является индикатором необходимости вывода сообщения.
Сопзо;е.Иг11еЬ;пе("\пБыхоц за границы массива с сообщением об "+
" ошибке.”);
for(1nt 1:0; l < 6: 1+т) {
fs[i,i] = i‘lO;
1f(fs.errflag)
C0nsole.WriteLine("\nUpM обращении к элементу fs[" + i + "‚
" + 1 T "] превышены граничные значения индексов массива.");
for(1nt i~O; 1 < 6; i++) (
х 4 fs{i,i};
1f(!fs.errflag) Cons0le.Wr1ce(x + " ");
eLse
Console.WriteL1ne("\nHpM обращении к элементу fs{" + i + ",
" 4 i + "] превышены граничные значения индексов массива.");
}
Результат выполнения программы:
При выходе за границы массива возвращается значение О.
С 1C 20 O О О
Выход за границы массива с сообщением об ошибке.
При обращении к элементу fs[3, 3} превьщюны граничные значения индексов массива.
При обращении к элементу fs[4, 4} превышены граничнье значения индексов массива.
При обращении к элементу fs[5, 5] превышены граничные значения индексов массива.
O 10 20
При обращении к элементу fs[3, 3] превышены граничные значения индексов массива.
При обращении к элементу fs[4, 4] превышены граничные значения индексов массива.
При обращении к элементу fs[5, 5} превышены граничные значения индексов массива.
Минутные? практикум
1. Как называются методы доступа в индексаторе, и какие функции они
выполняют’?
2. Можно ли определить индексатор, как предназначающийся только для
чтения?
3. Можно ли создать индексатор для двухмерного массива’?
1- МЕТОДЫ Д0С| yna H83l>lB8IOTCFI get И set. Первый СЧИТЫВЗСТ ЗНЗЧСНИС УКЭЗЗННОГО ЭЛСМСНТЗ
массива, ВТОРОЙ ПРИСВЗИВВСТ ему ЗНЗЧСНИС СООТВСТСТВУЁОЩСГО ТИПЗ.
2- Да.
3. Да.
Свойства
Вопрос. Могут ли быть перегружены индексаторы’?
Ответ. Да. Вызывается та версия, которая имеет наибольшее соответствие
Типов между его параметром и аргументом (или аргументами), используемым в
Качестве индекса.
/
9
свойства
Еще одним специальным типом членов класса, имеющим сходство с индексаторами,
является свойство, в котором поле связано с методом, обеспечивающим доступ к
полю. В некоторых предыдущих программах возникала необходимость создания
поля. хотя и доступного из других объектов, но такого, для которого выполнялся бы
контроль над операциями с его значением (контроль того, какие операции с его
значением возможны, а какие нет). Например, может потребоваться ограничение
диапазона значений, присваиваемых этому полю. Конечно, для достижения подобной
цсдти можно использовать закрытую переменную в сочетании с методом, обеспечи-
вающим доступ к ее значению, но проще и лучше во всех отношениях использовать
свойства.
Свойство состоит из имени и методов доступа get И set. Методы доступа использу-
ютс» для получения значения и присваивания его переменной. Основным достоин-
ством свойства является то, что его имя может применяться в выражениях и
операциях присваивания подобно обычной переменной, хотя на самом деле при этом
автоматически вьхзьхвахотся методы доступа get И set. Такое использование свойства
похоже на автоматический вызов методов get И set B индексаторе.
Синтаксис свойства выглядит следующим образом:
type name {
get (
// Код метода доступа get.
set (
// Код метода доступа set.
Здесь параметр type указывает тип свойства (например. тип int), а параметр name -
имя свойства. После определения свойства любое использование идентификатора,
Указанного вместо name, приводит к вызову соответствующего метода доступа. Метод
доступа set автоматически получает параметр value, содержащий значение, которое
Присваивается свойству.
Свойство управляет доступом к полю. Однако в свойстве поле не объявляется,
Поскольку оно должно быть создано вне свойства.
В приводимой ниже простой программе определяется свойство myprop, которое
ООССПСЧИВНСТ ДОСТУП К ПОЛЮ prop. B ЛЕННОМ случае СВОЙСТВО ‚ПОПУСКНСТ ПРИСВНИВЭНИС
ПОЛЮ ТОЛЬКО ПОЛОЖИТСЛЬНЫХ ЗННЧСНИЙ.
’/ В программе демонстрируется использование простого свойства.
using System;
I лапа 1 . | Iupcsl рудпа оператора, ИНДЭКСЁТОрЫ и садист“
class SimpProp { _
int prop; // Поле, доступ к которому осуществляется с помощью метода _г
// myprop, B котором также определяется, какие именно
// значения могут быть присвоены данному полю. ‘g
public SimpProp() [ prop а O; } ‘Ё
/* Свойство, обеспечивающее доступ к закрытой
переменной экземпляра prop. Оно позволяет присваивать полю
только положительные значения. */ ‚
public int myprop {4——~————~———————{§EEEEE;Q;:;E;;;;FE;B;;;;:EE;EE§E]1_
get 1 '
return prop;
)
set К
if(value >2 0) prop = value;
// Использование свойства.
class PropertyDemo (
public static void Main() 1
SimpProp ob = new SimpProp();
Console.WriteLine("nepaouaqanbuoe значение свойства 0b.myprop: " +
Ob -I“)/PIOP) F poficruo myprop используется подобно переменной
ob.myprop = 100; //Свойству присваивается значение. Ё
Соп5о1е.Ик1сеЬ1пе("Значение свойства ob.myprop: " + ob.myprop); Ы
// Полю prop невозможно присвоить отрицательное значение.
Соп5о1е.Ик1сеЬ1пе("Попытка присвоить свойству ob.myprop "+
"значение «10."):
ob.myprop = ~10;
Соп5о1е.Иг1ЕеЬ1пе("Значение переменной ob.myprop: " + ob.myprop);
}
РСЗУЛЬТЭТ ВЫПОЛНСНИЯ ПРОГрНММ Ы:
Первоначальное значение свойства ob.myprop: О
Значение свойства ob.myprop: 100
Попытка присвоить свойству ob.myprop значение —10.
Значение переменной ob.myprop: 100
Теперь тщательно проанализируем эту программу. В ней определяется одно закрытое
поле prop И свойство myprop, управляющее доступом к этому полю (в свойстве не
объявляется поле, а только происходит управление доступом к нему). Таким образом,
свойство неразрывно связано с полем, объявленным в классе, где определено это
свойство. Более того, Поскольку поле prop является закрытым, доступ к нему может
осуществляться только с помощью свойства myprop.
Свойство myprop определяется как риЬ11с, поэтому доступ к нему может осущсст- .-;
вляться из других объектов. Такой подход является правильным, поскольку свойство
обеспечивает доступ к полю prop, которое является закрытым, Метод доступа get
просто возвращает значение поля, а метод доступа Set присваивает переменной prop
свойства 275
Значение только в том случае, если оно является положительным. Таким образом,
„юиство myprop контролирует значения, которые можно присвоить полю prep.
СвоЙСТВО1ПурКОр ЯВЛЯСТСЯ СВОЙСТВОМ1«ЧТСНИС-3аПИСЬ»‚ПОСКОЛЬКУЕЗНСАТОПРСДСЛСНЗ
ВОЗМСОКНОСТЬ И получения, И ПРИСВОСНИЯ 3ННЧСНИЯ.'(рОИС ТОГО, МОХОЧ0 СОЗДНВЭТЬ
С„оиства‚так же КНК ИНДСКСНТОРЬД ПРСЦННЗННЧСННЬКЁТОЛЬКО ДЛЯ ЧТСНИЯ ИЛИ ТОЛЬКО
для 5аПИСИ.‚ЦЛЯ СОЗЦЭНИЯ свойства, предназначенного ЛИЪЦЬ ДЛЯ ЧТСНИЯ‚ НУЖНС
ОпршшлитьтолькохютоДЦоступа9ес.Дляопрыюлениясвойспш„функиионируюЦю-
ГО 1,1(;K.-'IIO‘ll/|T€I1bHO B pC)KMMC ЗЭПИСИ, НУЖНО ОПрСДСЛИТЬ ТОЛЬКО MOTOR ПОСТУПИ Se t.
Свойства можно использовать Для угяучшсния программы, в которой обрабатывалась
возможность возникновения ошибочных ситуаций. Ранее уже говорилось о том, что
Все чнссивы ассоциированы со свойством Length. При этом в классс ЁатЪЗОЕЕАггау
использовалось открытое целочисленное Поле Length. На самом ДСЛС это решение
Не является оптимальным, поскольку оно позволяет присваивать полю Length
некоторое Значение, отличное от Длины массива. (Например. такое значение может
быть Преднамеренно искажено.) Эту проблему можно решить путем преобразования
свойства Length B свойство, предназначенное только Для чтения, как это показано
в следующей программе.
// В программе демонстрируется использование свойства Length.
using System;
class FailSoftArray (
inti] a; // Ссылка на массив.
int len; // Длина массива.
public bool errflag; // В переменной errflag фиксируется результат
// попытки доступа к элементу массива.
// Конструирование массива заданного размера.
public FailSoftArray(int size) {
a : new int[s1ze];
Len = size;
7/ Свойство Length, предназначенное только для чтения.
public int Length {<———————{I9wpbLengthammcmucmmcnwm,aneummm.J
get (
return len;
// Индексатор для FailSoftArray.
public int this[int index} {
// Метод доступа get.
get {
if(ok(index)) (
errflag а false;
return a{index];
) else f
errflag = true;
return О;
(lb Глава 7. Перегрузка оператора, ИНДЭКСЗТОРЬ‘ И СВОЙ
// Метод доступа set.
set (
if(ok(index)) (
a[index] = value;
errflag = false;
I
else errflag = true;
// Возвращает значение true, если индекс не выходит за границы массива.
private bool ok(int index) {
if(1ndex >4 О & lndex < Length) return true;
return false;
// Использование свойства Length. V
class Impr0vedFSDemo I
public statlc void Main() (
FailS0ftArray fs = new Fai1SoftArray(5);
int х; „
// Можно считывать значение свойства Length.
for(int i=0; i < (fs_Length); i++)
fs[i] = i*lO;
for(inc i=0; i < (fs.Length); i++) [
x = fs[i];
if(x != —1) C0ns0le.Write(x + " ");
}
Console.WriteLine();
// fs.Length = 10; // He являясь комментарием, этот оператор будет
// недействителен, поскольку для свойства не определен метод
// доступа set.
)
Итак, теперь Length является свойством предназначенным только для чтения. Вы
можете проверить его недоступность для изменения. Чтобы сделать это, удалите
символ комментария, предшествующий оператору
// fs.Length = 10;
ПОСЛС ПОПЫТКИ СКОМПИЛИРОВНТЬ ЦЕННЫЙ КОД ПрОГРНИМН ВЬКВСДСТ С0061ЦСНИС О ТОМ,
ЧТО СВОЙСТВО Length ПрСДННЗННЧСНО ТОЛЬКО ДЛЯ ЧТСНИЯ.
Ограничения в использовании свойств
Свойствам присущи некоторые ограничения, на которые следует обратить особое
внимание. Во-первых, свойство не может быть передано методу в качестве параметра
ref или out. Во—вторых, оно не может быть перегружено. (Вы можете определить
два свойства, имеющих доступ к одной и той же переменной, но лучше не пользо-
ваться этой возможностью.) Наконец. в-третьих, свойство не должно изменять
277
свойства
‘ 3H;1|1CHMC базовой переменной, Доступ к которой контролируется свойством„ при
Выдове метода Доступа get. (Хотя создание такого метода доступа не запрещено
компилятором, метод get ЦОЛЖСН соответствовать своему первоначальному предна-
значению — только считывать данные.)
5, Минутные? практикум
I. Можно ли объявить поле в свойстве?
2. Какие преимущества дает использование свойства‘?
Ь.)
Можно ли передать свойство методу в качестве параметра гей‘?
проект 7-1. Создание класса Set
SetDevo.cs
Как уже говорилось, перегрузка операторов, а также использование индекеаторов и
свойств облегчают создание ютассов, полностью интегрированных в среду програм—
мирования языка C#. Обратите внимание, что с помощью переопределения требуе-
мых операторов, индексаторов или свойств обеспечивается использование в програм-
мс объекта, для клаеса которого и было выполнено переопределение, практически
таким же образом, как используется встроенный тип. Работа с объектами такого
класса выполняется при помощи операторов и индексаторов, причем эти объекты
могут использоваться в выражениях. Вютючение свойств в mace обеспечивает интер-
фснс, совместимый со встроенными объектами C#. Данный проект иллюстрирует
создание нового класса и его интеграцию в среду C#. B проекте создается mace set,
пчитирующий множество, а также для этого класса перегружаются операторы,
позволяющие выполнять оттерации над множествами (объединение, разность, добав-
ленис элемента и его удаление).
Исходя из целей данного проекта определим множество как коллекцию уникальных
Элементов. Уникальность означает, что никакие два Элемента множества не могут
совпадать. Порядок следования Элементов множества нс имеет значения. Таким
Образом, множество
в, с)
Нлснтич но множеству
C. B}
\IHo>KecTu0 также может быть пустым.
-[ля множества могут определяться операции. В нашем проекте остановимся на
выборе следующих операций:
J добавление элемента в множество;
J удаление элемента из множества;
J объединение множеств;
J разность множеств.
Нет, переменная должна быть объявлена в классе, в котором и определено свойство,
Использование свойства позволяет применять для вьтзова метода доступа тот же синтаксис,
что и при обращении к переменной.
Нет.
м:
278 Глава 7. Перегрузка оператора. индексаторы и свой
Добавление и удаление элементов множества нс требуют особых объяснений, поэто ‘ '
подробно рассмотрим оставшиеся две операции. С?
Объединением двух множеств называется множество, которое вючючает все элемент;
обоих множеств (конечно, в этом случае не допускается дублирование элсментовЁ
Для объединения множеств в проекте используется оператор +.
Разностью множсств называют множество, содержащее Элементы первого М ноже .
которые не входят в состав второго множества. Для вычисления разности Щ
хшожсств в проекте используется оператор —. Например, если рассматривать разнос-й,
двух множеств SI И SI , то этот оператор удаляет элементы множества S2 из множсстщ
SI, ПОМСШЭЯ результат в множество S3:
53 < s1 — s2
Если множества S1 И S2 совпадают, множество S3 будет пустым.
КОНСЧНО, ДЛЯ МНОЖССТВ МОГУТ 6ЫТЬ ОПрСДСЛСНЫ И другис операции. НСКОТОрЫС H3
НИХ РНССМНТРИВНЮТСЯ В рНЗДСЛС «Контрольные ВОПРОСЫ». ВЫ МОЖСТС ПОПЫТНТЬся
СЭМОСТОЯТСЛЬНО определить ОПСРНЦИИ. которые будуТ ПОЛСЗНЫ ДЛЯ работы C МНОЖе.
СТВЭМИ.
С ЦСЛЫО упрощения проекта КЛНСС Set 6)/IICT СОСТОЯТЬ ИЗ ННбОрОВ СИМВОЛОВ, НО
ПрХ/ЖНЦИПЫ, ИСПОЛЬЗУСМЬПС ПРИ определении ДНННОГО КЛНССН, МОГУТ ПРИМСНЯТЬСЯ И При
СОЗДННИИ КЛНССЭ, ГТрСДННЗННЧСННОГО ДЛЯ ХРННСНИЯ ЭЛСМСНТОВ ДРУГИХ ТИПОВ.
Пошаговая инструкция
1. Создайте новый файл с именем SetDemo. cs.
2. Начните создание класса Set следующим образом:
class Set (
char [] members; // ЭТОТ массив содержит символы.
int len; // Количество элементов массива.
K Ka)K,.[[0My ИЗ СИМВОЛОВ, СОДСрЖНЩИХСЯ B MHCCMBC, МОЖНО обратиться, yKa3aB ИМЯ
МНССИВН И номер ЭТОГО ЭЛСМСНТН. ЧИСЛО, УКЗЗЫВЁПОЪЦСС КОЛИЧССТВО ЭЛСЁИСНТОБ
массива, хранится в переменной len.
3. Добавьте в определение класса Set следующие конструкторы:
// Создание пустого множества.
public Set() (
len = O;
// Создание пустого множества заданного размера.
public Set(int size) {
members = new charisize}; // Выделение памяти для множества.
len = O: // Множество еще пустое.
// Создание множества на базе другого множества (объекта класса Set).
public Set(Set s) {
members Z new Charis-len]; // Выделение памяти для множества.
for(int i=0; i<s.len; i++) members[i} = sii];
len = S-len; // Количество элементов.
279
свойства
Для создания множества определены три конструктора. Используя первыи, можно
создать пустое множество. В нем не создастся массив, содержащий элементы
ЦНОЖССТВЭ, И ТОЛЬКО ИНИЦИЕЦТЪКЗИРУСТСЯ Переменная len. При ИСПОЛЬЗОВННИИ
вгорого конструктора СОЗДЗВТСЯ ПуСТОС МНОЖССТВО ЗЭДНННОГО размера. И НаКОНСЦ,
МПОЖССТВО МОЖНО СОЗДНТЬ на OCHOBC ДрУГОГО МНОЖССТВЭ. В ЭТОМ случае Два
МНОЖССТВЭ СОДСрЖЭТ ОДНИ И TC )KC ЭЛСМСНТЫ, НО ЯВЛУПОТСЯ различными ОбЪСКТЭМИ.
4_ Добавьте СВОЙСТВО Length И индексатор, предназначенные ТОЛЬКО ДЛЯ ЧТСНИЯ.
;/ Определение свойства Length, предназначенного только для чтения.
public int Length I
get{
return len;
Определение индексатора‚ предназначенного только для чтения.
public char this[int idx]{
get (
if(idx >= О & idx < len) return members[idx];
else return (char)O;
Свойство Length используется для указания длины массива. Индексатор возвра-
щает элемент множества по указанному индексу. Проверка границ массива
выполняется с целью предотвращения превышения граничных значений массива.
Если индекс некорректен, возвращается символ О.
э. Добавьте метод find(), код которого приведен ниже. Этот мстод определяет,
содержит ли множество символ, переданный ему C помощью параметра ch. Если
этот элемент найден, метод возвращает индекс элемента, если нет т значение -1.
/*Если элемент входит в состав множества, метод find возвращает индекс
элемента, если не входит — возвращается значение —1‚ */
LDC find(char ch) {
int i;
for(i~O; i<len; i++)
if(members[iJ ее ch) return i;
return -1;
}
5- Для выполнения операции добавления элемента в множество перегрузите опера—
гор + для объектов класса Set, как это показано ниже.
// Добавление уникального элемента в множество
public static Set operator +(Set ob, char ch) {
Set newset = new Set(ob.len + l); // Увеличение числа элементов
// нового множества на единицу.
// Копирование элементов.
for(int i=0; i < ob.len; i++)
newset.members[i} I ob.members[i];
// Переменной 1еп возвращаемого объекта присваивается Значение
// Переменной len копируемого объекта, то есть передается значение
// размера множества.
newset.len — ob.len;
280 Глава 7. Перегрузка оператора, индексаторы и свой
// Проверяется наличие в множестве добавляемого символа.
if(ob,find(ch) 2: —1) ( // Если элемент не найден,
// он добавляется в новое множество.
newset.members[newseL.len] A ch;
newset.len++;
)
return newset; // Возврат нового множества (объекта класса Set).
атм
)
Рассмотрим работу перегруженного оператора подробнее. Во-первых, создаваемё‘
новое множество содержит элементы исходного множества, которое передаетсдёс
помощью параметра ob, и новый элемент, который передается параметром д,
Обратите внимание, что новое множество содержит на один элемент больше, чад.
первоначальное множество ob, то есть добавление нового элемента учтено. Защ
исходные элементы копируются в новое множество, а переменная len, collepxfi.
щая значение размера нового множества, становится равной переменной
исходного множества. Добавление элемента ch B НОВОЕ множество происходйп
только в том случае, если этот элемент еще не содержится в данном множестёе
(то есть вначале вызывается метод find()). При добавлении каждого нового
элемента происходит приращение значсния переменной len Ha единицу. no;-4,J
выполнения этой операции возвращается новое множество — объект newesi,
причем исходное множество нс изменяется.
7. Выполните перегрузку оператора ~, B ходе выполнения которого должен удаляться:
элемент множества. Код перегруженного оператора приведен ниже.
// Удаление элемента ИЗ МНОЖеСТВа.
public static Set operator —(Set ob, char ch) (
Set newset = new Set();
int i = ob.find(ch): // Если элемент не найден,
// переменной i будет присвоено значение «l.
// КОПИРОВЗНИЭ ОСТЗВШИХСЯ ЭЛЭМЭНТОБ С ИСПОЛЬЭОБЭНИЭМ перегруженного
// оператора +.
for(int j:0; j < ob.len; j++)
if(j 1: i) newset 2 newset + ob.members[j];
return newset;
Е
Вначале будет создано новое пустое множество, затем вызван метод ЫпсНЪ
позволяющий определить, входит ли символ, передаваемый параметром ch, Bl
состав исходного множества. Как вы помните, метод f ind() возвращает значе-
ние -!‚ если символ, передаваемый параметром ch, НС входит в состав множества.
Затем все элементы исходного множества добавляются в новое множество, за!
исключением элемента, индекс которого равен значению, возвращаемому мето—
дом find ( ) . Таким Образом, результирующее множество содержит все элеменТЫ
исходного множества, исключая символ. переданный параметром ch. Если символ
ch не является частью исходного множества, то возвращенное множество будеТ
идентично исходному.
8. Теперь для выполнения операций объединения и разности множеств вновь
перегрузите операторы + и —‚ как показано ниже.
// Объединение множеств.
public static Set operator +(set obl, Set ob2) {
Set newset = new Set(ob1); // Копирование первого множества.
GB
281
дйства
// Добавление уникальных элементов из второго множества.
[Or(int i:0; i < ob2.len; i++)
newset Д newset + ob2[i];
return newset; // Возврат нового множества.
РаЗНОСТЬ МНОЖЕСТВ .
с static Set operator ~(Set obi, Set ob2) (
— new Set(ob1); // Копирование первого множества.
ье: newset
// вычитание из Первого множества элементов второго множества.
f0r(int i=0; i < ob2.len;
newset = newset — ob2[i];
l++)
return newset; // Возврат нового множества.
Как видите, для упрощения выполнения соответствующих операции эти методы
используют ранее перегруженные версии операторов + и —. При объединении
шюжеств новое созданное множество вначале содержит элементы из первого
множества, затем к ним добавляются Элементы второго множества. Поскольку
операция + выполняет добавление элемента в множество только в том случае, если
он не является частью этого множества, результирующее множество представляет
собой объединение двух множеств (без дублирования элементов). Оператор
удаляет соответствующие элементы.
Ниже приводится полный код класса Set и использование классом SetDemo
перегруженных операторов и объектов, имитирующих множество.
х
Проект 7.1
В программе создается класс Set, имитирующий множество и выполняющий
операции над множествами.
и I
using System;
:Lass Set (
char[] members; // Массив, содержащий символы
int len; // Количество элементов множества.
(имитирующий множество)
// Создание пустого множества.
public Set() (
len = O;
...
// Создание пустого множества заданного размера.
public Set(int size) {
members 4 new char[si2e];
len = О; // Элементы не были сконструированы
// Распределение памяти для множества
// Конструирование множества на базе другого множества.
public Set(Set s) (
members 4 new char[s.len}; // ВЫДЕЛЕНИЕ памяти для множества.
282 Глава 7. Перегрузка оператора, индексаторьп И CBO
for(int i=0; i < s.len; i++) members[i} = s[i];
len 4 s.len; // Количество элементов множества.
// Определение свойства Length, предназначенного только для чтения.
public int Length (
9€t(
return len;
// Определение индексатора‚ предназначенного только для чтения.
public char this[int idx](
get {
if(idx >: O & idx < len) return members[idx];
else return (char)O;
/*Если элемент входит в состав множества, метод find возвращает индекё
элемента, если не входит, то возвращается значение —1‚ */
int find(char ch) {
int i;
for(i:O; i < len; i++)
if(members[i] == ch) return ii
return —1;
// Добавление в множество уникального элемента.
public static Set operator +(Set ob, char ch) (
Set newset : new Set(ob.len + 1); // Увеличение числа элементов
// нового множества на единицу.
// Копирование элементов.
for(int 1:0; i < ob.len; i++)
newset.members[i] = ob.members[i];
// Переменной len возвращаемого объекта присваивается значение
// переменной 1еп копируемого объекта, то есть передается значение
// размера множества.
newset.len = ob.len;
// Выполняется проверка наличия в множестве добавляемого символа.
if(ob.find(ch) == —1) ( // Если элемент не найден,
// он добавляется Б новое множество.
newset.members[newset.len] = ch;
newset.len++;
1
return newset; // Возврат нового множества (объекта класса Set).
// Удаление элемента из множества.
public static Set operator -(Set ob, char ch) (
Set newset = new Set();
Войства 283
‚О
int i ob.find(Ch); // Если элемент не найден,
// переменной i будет присвоено значение —1.
// Копирование оставшихся элементов с использованием перегруженного
// оператора +.
fof(int jvo; j < ob.len; 3++)
if(3 E: i) newset : newset т ob.members[j];
return newset;
// Объединение множеств.
public static Set operator +(Set obl, Set ob2) (
set newset = new Set(obl); // Копирование первого множества.
// Добавление уникальных элементов из второго множества.
for(int i=0; i < ob2.len; i++)
newset — newset + ob2{i];
return newset; // Возврат нового множества.
// Разность множеств.
public static Set operator —(Set obl, Set ob2) (
Set newset = new Set(obl); // Копирование первого множества.
// вычитание ИЗ ПЕРВОГО множества ЭЛЕМЕНТОВ ВТОРОГО МНОЖЭСТВЗ.
for(int 1:0; i < ob2.len; i++)
newset = newset — ob2[1];
return newset; // Возврат нового множества.
/r Использование класса Set.
class Setbemo {
public Static void Main() (
// Конструирование пустого множества.
Set sl 2 new Set();
Set s2 = new Set();
Set 53 = new Set();
s1 Ь 51 + ‘А’;
51 sl + ‘B’;
51 = 51 + 'С':
И
Соп5о1е.Иг1пе("Множество 51 после добавления символов А, В и С: ");
for(int i=0; i<s1.Length; i++)
Console.Write(sl[i] + " ");
Console.WriteLine();
sl = 51 — ‘B’;
Соп5о1е.Иг1пе("Множество 51 после удаления символа B: ");
for(int i=0; i<s1.Length; i++)
Console.write(sl[i] + " ");
284
Глава 7. Перегрузка оператора, индексаторы и свой
Console.Wr1ueL1nc();
51 51 — 'А';
Соп5о1е.Щг1ъе("Множество 51 после удаления символа А: ");
LOr(1nL дно; 1<51.Ьеп9сЬ; 1тт)
Console.WriCe(sl{i] + " ");
ConsoLe.WriLeLine();
51 — 51 A ‘C’;
Соп5о1е.Иг1Се("Множест5о 51 после удаления символа С: ");
:or(;n: i—O; :<sL.Leng:h; L++)
Console.Wri:c(s1[;] 2 " ");
Console.Wr;teLine("\n");
sl 51 4 ‘A’;
sl И 51 + ‘B’;
sl 2 51 + ‘С’;
Соп5о1е.Иг1:е("Множество 51 после добавления символов A, B И С: "L
for(int i—O; i<sl.Leng:h; i++)
C0nsole.Write(sl[i] + " ");
COnsole.WriteLine();
52 52 т ‘А’;
52 4 52 + 'X';
s2 е 52 + ‘W’;
COnsole.Write("MHOxecT5o 52 после добавления символов А, Х и W: "L
for(int i=0; L<s2.Length; ;++)
Console.WrLte(s2[i] т " ");
COns0le.Wr1teLine();
53 51 + 52;
Соп5о1с.Иг1Ее("Результат объединения множеств 51 и 52: ");
for(;nt i=0; i<s3.Length; i++)
Cons0le.Write(s3{i] + " ");
Console.WriteLine();
53 = 53 — 51;
Соп5о1е.Иг1пе("Множество 53 после выполнения операции 53 — 51: ");
for(1nt 1~O; i<s3.Length; i++)
Console.Write(s3[i] + " ");
Console.WriteLine("\n");
5 52 52; // Удаление всех элементов множества 52.
‘2 — 52 + ‘C’; // Дооавление символов А, В и С 5 обратном порядке.
2 — s2 + ‘B’;
2 s2 т ‘A’;
Соп5о1е.Иг;се("Множество 5‘ теперь содержит символы: ");
for(1nt i—G; 1<sl.Length; ;'Ц
Consolc.Wr;te(s1[ij + " ");
Conso;e.Wr-LcLine();
Соп5о1с.Нг1Ее("Множестно 52
fOr(1nt i~C:
Con.
Zepb содержит символы: ");
1<s2.Leng;h; 1 ~)
- Н п).
Console.WriteLine();
Соп5о1е.Иг1Се("Множество 53 теперь содержит символы: ");
for(int i=0; i<s3.Length; i++)
C0nsole.Write(S3[1] + " ");
Console.WriteLine();
резу] ЬТНТ ВЫ ПОЛНСНИЯ ПРОГрЭМ M ЫЁ
51 после Добавления символов А, В И С: А В С
51 после удаления символа B: A C
Мшжество 51 после удаления символа A: C
.
ЩЮЖССТЕО 51 после удаления символа С:
шюжество 51 после добавления символов А, В И С: А В С
Шюже. 52 после добавления символов A, X M W: A X W
Wgyn Объединения множеств 51 и S2: А В С Х W
шюжество 53 после выполнения операции 53 - sl: X W
шюжество 51 теперь содержит символы: А В С
Шюжество 52 теперь содержит символы: С В А
шюжество 53 теперь содержит символы: Х W
286
10.
ll.
Глава 7. Перегрузка опвратора, индексаторы и сво
"4 i
Предложите общую форму синтаксиса, используемого при перегрузке?
операторов. Каков должен быть тип параметра метода operator?
Что нужно учесть при перегрузке оператора, чтобы его операндам
могли быть объект необходимого класса и встроенный тип?
Может ли быть перегружен оператор ? '.’ Можно ли изменить приоритет‘;
оператора?
Объясните, что такое индексатор. Приведите общую форму его синтак-
сиса. '
Какие функции выполняют методы доступа get И set B индексаторе?
Что такое свойство? Каков его синтаксис?
Можно ли объявлять переменную в свойствс?
Может ли свойство передаваться в качестве аргумента ref или out?
Для созданного в проекте 7-1 класса set определите операторы < и >
таким образом, чтобы они проверяли, является множество подмноже-
ством или супермножеством для другого множества. Причем оператор <
должен возвращать значение еще, если множество, указанное слева от
оператора, является подмножеством множества, находящегося справа от
оператора, и возвращать значение false, если это условие не выполня-
ется. Оператор > должен возвращать значение true, если множество,
указанное слева от оператора. является супермножеством для множест-
ва, находящегося справа от оператора, и возвращать значение false,
если это условие не выполняется.
Для класса Set определите оператор а, выполняющий операцию пере-
сечения двух множеств.
Попытайтесь определить другие опсраторы my класса set. Например,
попробуйте определить оператор |. выполняющий операцию «исклю-
чающсе ИЛ И» для двух множеств. (Множество, являющееся результатом
выполнения операции «исключающее ИЛИ», состоит из элементов, не
принадлежащих пересечению этих множеств.)
Наследование
C]
C]
C]
C]
DUDE]
Основы наследования
Использование модификатора protected
ВЫЗОВ конструкторов наследуемого
класса
Использование ключевого слова base
Создание многоуровневой иерархии
классов
Ссылки на объекты наследуемого и
наследующего классов
Создание виртуальных методов
Использование абстрактных классов
Применение ключевого слова sealed
Класс object
288 Глава 8. Наследовани
Напомним, что С# является объектно-ориентированным языком и поддерживает;
основные принципы ООП‚ одним из которых является наследование. Используя”?
наследование, вы можете создавать новые классы, представляюгдие собой расширез
пис уже существующих классов. То есть класс может наслсдоваться другими класса.
ми, обладающими некими уникатьътьнхпт своиспъаии, которые добавляются к уже
имеющимся характеристикам и возможностям. Например, можно создать класс
автояиобиль, имеющий такие общие характеристики, как моьцноспть двигателщ расход
топлива на 100 километров пробега, лшксимальная скорое/ль, которую может развить
ав/пояиобиль. Этот класс могут наследовать два других шасса —— грузовик, в котором
появится характеристика грузоподъемность, и автобус, в котором появится характе.
рис гика количество перевозилтьос пассажиров.
В языке Ci? KJIEICC, переменные и методы которого автоматически становятся членами
пового создаваемого класса, называется наследуемььи (базовым) классом. Ютасс, кото-
рый K имеющимся членам наследуемого ютасса добавляет (определяет) новые члены
класса, называется последующим классом. Таким образом, наследуюгдии класс явля-
ется специализированной версией наследуемого класса. Наследующий класс насле-
дует все переменные, методы, свойства и индексаторы, определенные наследуемым
ктассом, добавляя при этом свои собственные уникгшьньяе элементы.
Основы наследования
B C11 при объявлении наслсдующего шпасса указывается имя наследуемого ютасса.
Рассмотрим небольшую программу, в которой демонстрируются ключевые свойства
наследования. B ней создается класс TwoDShape, хранящий значения ширины и
высоты двухмерного объекта, и наследующий класс Trlangle. Обратите внимание
на способ объявления класса Triangle.
// B программе создается простая иерархия классов.
using System;
// Класс, содержащий значения размеров двухмерной геометрической фигуры.
class TwoDShape {
public double width;
public double helght;
public void showDim() {
Сопзо1е.Иг1:еЬ1пе("Значения ширины и высоты геометрической фигуры = " +
width + " И " + height);
// Класс Triangle, наследующий класс TwoDShape.
C1885 Triangle : TwoDShape { KJ[3CC'I'r1dI1gle наследует класс ТИОББЬдре.
Обратите внимание на используемый синтаксис.
puul
public double area() {
return width , height / 2; ‘________}Qmc€Tr1ang1e MOXCfCCunan£flHaHHUflKflm£a
l"woD:hape ТЕК же, как если бы они были объявле—
I’ ны непосредственно в классе Trlangle.
s;I;ng style;
r
public void sh0wStyle() 1
ConsolC.WriteLine("BMn треугольника — " + style);
}
ОСНОВЫ НЗСЛЭДОЭБНИЯ
,ass Shapes I
¢ub;;c static
void Main()
I
TrLangle ' new Tr1angle();
Triangle :2 — new Tr;ang;e();
31-Wldzh ’ 4'05 Вш:шшньтюишсатъ1ап91е‚птом-ншлси1с‚щло-
;1 . height; — 4 . O ; ‹?———-—— рыс были унаслстщонаны из масса тиоьэзпаре, дои
:L.Style = "равн0бедренНый."; тупньтдптобылповтнпа1ч1ап91е
t2.wldCh = 8.0;
'2.heignt : 12.0:
E2.style — "прямоугольный.":
Соп5о1е.Иг1ЕеЬ1пе("Информация об
;l.showStyle();
tl.showDim();
объекте LL:
");
Console.Wr1teLine("Unouanb
— Т
t;.area());
Conso1e.WriceLine();
Сопзо1е.Иг1пеЬ1пе("Информация об объекте ‚
t2.showStyle();
t2.sh0wDim();
Console.WriteLine("Uuomanb —
t2: ")
+ :2.area());
Pc5y.r1bTaT ВЫ Пол нения программ ы:
E gopmaumfl об объекте Cl:
M треугольника
равнобедренный.
„ЗЧЭНИЯ ШИрИНЫ И ВЫСОТЫ геометрической ФИГУРЫ — 4 И
Т„ощадь 8
формация об объекте E2:
“АД треугольника
прямоугольный.
- R
ачения ширины и высоты геометрическом фигуры
.эщадь 48
1 ’)
I/1
I классе Tw-:.DShape определены характеристики некоторой двухмерной геометриче-
ккой фигуры. которой можст быть квадрат, прямоугольник и т. д. В классе T:1.3r.-.jle.
который наследует mace TwoDShape. определяется специфический тнп геометриче-
ккой фигуры, изданном случае трсугольник. B шасс Tr; a e включаются все члсны
Миасса TwoDShape И добавляется поле style, а также методы area () И sh-owstyle ().
Название вида треугольника хранится в поле style. метод area () вычисляет и
гаозвращаст значение площади треугольника, а мстод s1~.owsty1e() отображает на-
пвание вида треугольника.
mg;
Обратите внимание на то. что синтаксис, используемый при наследовании, доста-
гпчно прост. Имя наследуемого ютасса следует за нмсттем наслсдуютцсго класса, а
‘тсжлу ними ставится двоеточие.
Поскольку в класс Triangie ВКЛЮЧСНЫ все члсны ттаслсдуеиого класса T'w-3IWS'naE':-.3,
По метод area () имеет доступ к значениям персменньпх шаек и ‘ne;gm. Кроне
290 Глава 8. Наспед
того, указав с использованием оператора точка (.) имя объекта t1 или
переменную width или height, можно непосредственно внутри метода Ma
обратиться к копиям этих переменных. На рис. 8.1 показано, каким образом
TwoDShape ВКЛЮЧЗСТСЯ в класс Triangle.
Несмотря на то, что класс TwoDShape ЯВЛЯСТСЯ наследуемым для класса Tria
это полностью независимый автономный класс, который может использо
самостоятельно. Например, действительным является следующий код: ‘
TwoDShape shape 2 new TwoDShape();
shape.widch = 10:
shape.height = 20;
shape.showDim();
Объект класса TwoDShape HC имеет доступа к любым членам наследующих
Синтаксис объявления класса, наслсдующего другой класс, приведен ниже.
class deriVed—Class—name : base~class—name {
// тело класса
}
width
TwoDShape height
showDim()
Triangle
style
area()
showSty|e()
Рис. 8.1. Схема класса Triangle
Для каждого создаваемого наследуюшего класса можно указать лишь один на
мый класс (наследование нескольких классов в C# He поддерживается. в отл U:
языка С++‚ поэтому при преобразовании кода С++ в код C# будьте очень в I
ТСЛЪНЫ). Однако в C# есть возможность создавать иерархию наследования, в к п
наследующий штасс сам может становиться наследуемым для другого клас =7
наследовать сам себя mace не может.
Огромное преимущество НЭСЛСДОВЗНИЯ зашп ючастся B TOM, ‘ITO ПОСЛС СОЗДЗНИЯ
создания любого количества наследуюших классов со специфичными характе` и
ками. Каясдый наследующий класс может разрабатывать свою собственную д
фикацию. Например, ниже приведен код еще одного класса, наследуюшего "7
TwoDShape.
// B этом классе, который наследует класс TwoDShape, определена новая
// характеристика, присущая прямоугольникам.
Class Rectangle : TwoDShape {
public bool isQguare() {
if(width >2 height) return true;
return false;
вы наследования 291
f, но
uD;;c double area() {
return width * height;
)
В mace Rectangle включены все члены шасса TwoDShape, а также метод is—
mare (д определяющий, является ли прямоугольник квадратом, и метод area () ,
H,mc:1;110u1L1ii площадь прямоугольника.
В .
„ступ K членам класса при использовании
наследования
В главе 6 уже мы уже говорили, что члены класса часто определяются как закрытьпс
для прслотвраъцеътизя их несанкиионировцнного нспольжлзания. При наследования
классов ограничения, имеющиеся при доступе к Закрытым членам класса, не снима-
ются Таким образом, метод наследуюшего класса не имеет доступа к членам
наследуемого класса, если они являются закрытыми. Приведем код новой версии
класса тиоозваре, в котором переменные width И height no умолчанию определя-
ются как private, Следовательно, метод area() класса Triangle HC ИМССТ к ним
Доступа.
// Эта программа не будет скомпилирована.
Using System;
// Класс, содержащий значения размеров двухмерной геометрической фигуры.
Class TwoDShape {
double Width; // Теперь эти две тлеременные объявлены как закрытьхе.
double heig t;
public VO‘.d showDim() {
Console.WriteLine("3HaqeHI/zn ширины и высоты геометрической фигуры : " +
width + " и " + height);
// Класс Triangle наследует класс 'l‘woDShape.
C1555 " ‘iangle: TwoDShape E
public string style; Метод не имеет достунакзакрытым
членам наеледуемою класса.
'0 couble area() (
‚согл width * heiqht / 2; // Ошибка, метод не имеет доступа к этим
// переменным.
‘та: vo;d showStyle() {
C0-'15'?le.WriteLine("BI/1:1 треугольника ~ " т style);
}
Ta
Программа не будет скомпилирована, поскольку при попытке обращения метода
are
E03:(-‘~OnpeI1e;1eHH0ro в ютассе Triangle, к закрытым переменным width И height
зак ”K”CT ошибка доступа. Как только члены класса weight И height становятся
ь
ассчьпии, доступ к ним могут осуществлятъ только другие члены их собственного
“ч 11 доступ членов наследующего класса становится невозможным.
292 Глава 8. Наследова
Закрытый члеп класса недоступен для любого кода, определенного вне этого клас
n'r0\1 числе п кода наслсцугощих классов.
На первый взгляд данное правило кажется серьезным ограничением. но в
супгествуют различные решения, позволяющие его обойти. Во-псрвьпх, есть возмо ›
ноет ь использования членов ъспасса, имеющих тип Доступа ргосесгео (объявлен '
g \1();lI/l(bHK’clTOpOM p-rQteC:e-1'). OHM будут описаны в слелуютпехт разделе. Во-втор *
для получения доступа к Закрытым данным можно воспользоваться свойстващё
обьяштенпыми как pub] 1c. B предыдущих главах уже рассказывалось о том, ч“,
программисты на Сд обычно обеспечивают ДОСТУП к закрытым члеътаи mace‘
пспо_ть3уя методы или преобразуя Закрытые члены класса в свойства. Ниже при ведчн
мы еще одной версии Класса ';~..-,-.9:-5.1: эре, в Которои члены Класса шд-ЁСГЕ и ‘ne1gh”{._
преобразованы в свойства. -:5
5 программе демонстрируется использование свойств ДЛЯ доступа к закрытым
членам класса.
;:;ng System;
Класс, содержащий значения размеров двухмерной геометрической фигуры.
;1аз5 TwoDShape {
ооЦЬ1е priiwidth; // Объявление закрытых переменных.
double pri7he;ght;
/ Свойства Width И he;ght. Ё
public double w:dth {
ч
get ( return pr1_width; 1
set ё рг1_ш1о:п — value: }
public double height {
get { return priiheight; }
set { pri_height = value; E
ya
publLc Vold showD;m() {
Console.WriteLine("3uaucHmH ширины и высоты 1еометрической фигуры " +
width + " и " т he;ght);
7/ B этом классе, Наследующем класс TwoDShapC, определены характеристики,
// присущие треугольнику.
class Triangle : TwoDShape [
public string style;
pubLic Qouble area() [
return width ж height ‚и 2; ‘____V_£1oI1ycKz1c*rc>1 использование свойств w1c'th и nelght,
поскольку они были объявлены как огкрьггые.
public void showSLyle() {
Console.WriteL;ne("BMu треугольника — " ~ sLy;e);
class Shapes2 I
Определение свойств width И nelght. f-»
осдовьп наследования 293
„р„1с static void Ma1n() {
3Liangle tl = new Triangle();
fr;angle :2 Й new TLiangle();
,Z.w;dth — 4.0;
-;.he1ght 2 4.0;
~ .5tyle — "равнобедренньЙ.";
‘.wid:h - 8.0;
2.height — 12.0;
' style — "прям0уг0льныЙ.";
'сп5о1е.Иг1сеЬ1пе("Информация об объекте tl: ");
“L.sh0wSty1e();
1.showD;m();
:3nsole.WriteLine("Hnomaub — " + t1.area());
Console.Wr1teL1ne();
Qonsole.WriteL1ne("MH@opMauMH об объекте :2: ");
' .showStyie();
: showD1m();
Соп5о1е.Иг1сеЬ1пе("Площадь — " + t2.area());
атеизм: гзрофаесаолеаеп-дь
С) Вопрос. B программировании на Java используются термины «супсркласс» и
«полшасс». Употребляются ли эти термины в Се?
Ответ. Класс, который в Java называется «суперкяасс», в Cit получил название
«наследуемый масс». Класс, именуемыи в Java «подкласс». в C# называется «насле-
цукядий mace». Конечно, эти термины являются взаимозаменясмьпми и могут
использоваться в обоих языках, но в данной книге мы и далее будем причинять
Стандартные термины C#. B ЯЗЫКЕ: С++ также используются термины «наслсдусмьнт
класс», «наследующий mace».
минутными лрактикум
1. Как и где указывается наследусьаьтгт класс при объявлении наслсдуюшсго
ютасса?
Включаются ли в наследующий класс члены наследуемого класса‘?
.t\.)
3. Может ли наследующий класс Получать доступ к закрытым членам своего
наследуемого класса?
5 Имя наследуемого класса указывается после имени наслсдупощсго класса и отделяется от
него двоеточием.
Да.
Нет.
294 Глава 8. Наспед ж
Использование модификатора protected
B предыдущем разделе уже рассказывалось о том, что члены наследующего ma ‚
имеют доступа к закрытым членам наследуемого класса. Исходя из этого, ‚‚
Предположить, что когда требуется обеспечить доступ к некоторым членам насл
мого шпасса со стороны наследующего класса, Эти члены класса обязательно до "
быть объявлены с модификатором public. Но в таком случае данные члены .‘
становятся доступными для любого кода, определенного вне наследуемого д?
что в некоторых случаях совершенно нежелательно. К счастью, подобные Bu‘
несколько преждевремснны. B C11 существует возможность создания Защцщ’
члена класса, что делает его открытым во всей иерархии наследования данного п
но оставляет закрытым для кода, определенного вне этой иерархии.
ные как protected, становятся открытыми Для кода класса Б. но остаются Закр
для любого другого кода. определенного вне данной иерархии (то есть если "
наследует класс Б, то его код получает доступ к защищенным членам класса А).
Ниже приводится простая программа, в которой используется модификатор
tected. ‘V
// В программе демонстрируется использование модификатора protected.
Using SY5tem-' Для полей i и j указан
модификаторркоеесееа.
class E {
protected int i, j; // Эти переменные является открытыми для членов
// класса D.
publlc void set(1nt a, lnt b) {
publlc void show() {
Console.WriceL1ne(i + " " + j);
class D : В {
int k; // Закрытая переменная.
//Метод класса D имеет доступ к переменным i И j, определенным В клас
риЬ11с void setk() {
Метод, определенный в классе в, имеет доступ
7 ' й '
. — A. - а . .
к 1 3 ’ к переменным 1 и 3 , поскольку они яшхяются
Р защищенными, но не закрытьтми.
publ1c void showk() {
Console.WriteLine(k); Г
——
class ProtectedDemo (
295
струкТОрЫ И Hacnep.oBaHme
ublic static void Main() [
D Op — new D();
Ob Set(2, 3); // Метод seL() доступен из объекта класса D.
b 5how(); // Метод show() доступен из объекта класса D.
О .
Ob дддкт); // Эти обращения к методам экземпляра гакже действительны.
0b_showk();
Ъ
Поскольку в этой программе класс в наследуется шассом D, а переменные 1 И j
О6ЪЯВЛСНЫ в Классе В как protected, метод setk() имест к ним доступ.
Как И при использовании тиодификаторон public И private, модификатор pro-
tected остается присвоенным члену класса независимо от количества задействован-
flux урОВНСЙ наследования.
онструкторы и наследование
В иерархии наследования допускается, чтобы наследуемые и наследующие классы
одновременно имели свои собственные конструкторы. Но при этом возникает
вопрос, какой конструктор (наследуемого ютасса, наследующего класса или оба) будет
отвечать за создание объекта наследующего класса. Ответить на этот вопрос можно
так — каждый из ‚конструкторов создает (инициализирует) свою часть объекта. Это
разделение функций имеет смысл, поскольку наследуемый mace не имеет доступа к
какому-либо члену наследующего масса, в результате объект создается по частям.
В предыдущих примерах автоматически вызывались конструкторы, заданные по
умолчанию, но это не лучший способ создания объектов. Как правило, в большинстве
классов существуют свои конструкторы.
Если в наследующем классе определен Конструктор, процесс создания объекта не
Представляет особых сложностей, просто конструируется объект наследующего клас-
са. Часть объекта, определенная в наследуемом классе, создается автоматически с
ПОМОЩЬЮ его конструктора, заданного по умолчанию. Ниже приводится модифици-
Р0ВанНая версия класса Triangle, B котором определен конструктор. При этом
°°3дается закрытое поле style, инициализируемое с Помощью конструктора.
H „ Ы . „ ‚
‘В этом программе в класс Triangle добавлен конструктор.
Wing Shsrem.
I L’ I
//
cllgmbc, содержащий значения размеров двухмерной геометрической фигуры.
ass Tvsoushape {
d . . .
ЁЩЩЦЁ Рг1*и1ёсЬ; // Объявление закрытых переменных.
°”D~€ pr;_height;
// . \4
pu »C°“=V-tuna width И height.
bile Gouble width {
get return priiwidth; }
se~ i prigwidth — Value; }
pub‘ - .
ll? Qouble height {
Se:
{ return prifiheight; }
296
Глава 8. Наследо
/
clas
class Shapes3
)
Конструктор класса Tr
THIOKL‘ наследуемьяс члены класса Тыс-
set J pri/height » value; Э
‹
public void showD;m() ‘
Соп5о1е.Иг1сеЬ1пе("Значения ширины и высоты геометрической фигурыъ" ъ
width " И " + height): '
г
’/ B этом классе, наследующем класс TwoDShape,
(двойственные треугольник .
TwoDShape {
определены характеристики,
I I ‘
5 Triangle
r
s: ing style; // Закрытая переменная.
/ 1‘
I
Конструктор.
определенных в наследуемом клас?
Qoub;e h)
// Инициализация свойств,
public Triangle(string s,
width — w;
double w,
height 3 hi Конструирование части объекта, которая
была определена в лстассе TwoDShape. I
style = S; // Инициализация переменной, дпределенной в Наследующем класф
public double area() {
return width * height / 2;
(
Conso1e.Wr1teLine("BMn треугольника —
public void showStyle()
" + style);
r
1
public szatic
Triangle tl
Triangle t2
voic Main() {
new Тг1ап91е(“равнобедренный.“‚
new Тг1ап91е("прямоугольный."‚
4.0);
12.0);
4.0,
8.0,
Сопзо1е.Иг1сеЬ1пе("Информация об объекте с1:
cl.showSty1e();
tl.showDim();
Соп$о1е.Иг1:еЬ1пе("Площадь
"):
п
~ t1.area());
Conso1e.Wr1teLihe();
Соп5о1е.Иг1:еЬ1пе("Информация об объекте C2:
t2.showSty1e();
:2.showDim(); Т‘
Соп5о1е.Иг;сеЬ1пе("Площадь = " т :2.агеа());
");
jmgle mHmma;m3up_veT ОбЪЯВЛСННОС в нон поле styl9_
таре.
Если конструкторы определены и в наследуемом, и в наслсдующси классах, то пр0 `
создания объекта несколько усложняется, поскольку в таком случае вызываются Е;
конструктора. В подобной ситуации нужно примснять ключевое слово разе, КОТЁ
- " структорьп и наследование 297
тнуетея и ччя вызова конструктора наследуемого класса, и для обеспечения
o.- › . .. _ _
ИСП ‚ш к члену наследуемого класса, если он был скрыт при объявлении: члена
CT) ‘ ' ‘ '
OC,[Cr1\'1olH,C!‘O юпасса.
на‘ "
Вызов конструкторов наследуемого КЛЗССЗ
Исподшуя расширенную форму объявления когпструктора наелелуюхпхеуо Kjlzlcczl. в
Орем указывается ключевое слово г, . можно вызывать конструктор. опредет
Кодньш в его наеледуемозт классе. Ниже представлен сингакеис этой расширенной
':;Op\1 ы об ья вле ни я.
def; уик!
/‘/ ТЕЛО :\'\JHC'l'pyK’1'OpE£.
.hnstructor(pdramcnervilsé) : base(arg—lisf)
Ъ
В перечне a::‘_;~lis-E уКЗЗЫНаЮТСЯ любые аргументы, необходимые конструктору
Насдсдуктхтого класса. Обратите внимание на то. как используется двоеточие.
для приобретения навыков применения ключевого слова base используем в слепую-
щей программе еще одну версию класса Twobshape, в котором определен конструк-
тор, инициализирующий свойства wl-:'1:h И helqhz.
Ц В п; тамме используется новая версия класса TwoDShape, B который добавлен
H KOh
пат;
Н Класс, содержащий значения размеров двухмерной геометрической фигуры.
dass Twobshape {
double pri_width; // Объявление закрытых переменных.
doublc pri_height;
‘"pyKTOp класса TwoDShape.
FwoDShape(d0uble w, double h)
W;
he::h; : h;
// C Цйстза wldth И height.
L-; double width {
ЧС1 return pr1_width; }
391 { priiwldth ч value; }
>-_ double height {
9L { return pr; height; 1
рг1„Пе19ПС = value; )
к - y
«-3 void snowDim() 5
5Нзо1е.Иг1сеЬ1пе("Значения ширины и высоты геометрической фигуры — " *
Width + " и " + helght);
Эдом классе, наследующем класс TwoDShape, определены характеристики,
рйдущие треугольнику.
298
Глава8.Наспедован‘
class Triangle : TwoDShape {
string style; // Закрытая переменная.
// Вызов конструктора наследуемого класса.
public Tr1angle(str1ng s, double w, double h) : base(w, h)
style д s;
{
. . ‘ . Применение ключевого слова base для
1„ .
p”D**C Gouble area‘) ‹ _ nmnouaxoncuwxumaIworshape
return w1d:h ‘ пехоте / 2:
public void showStyle() {
Console.WriteL1ne("BMn треугольника — " + style);
aawwwwxm щтищъ„.’?
class Shapes4 {
public static vold Main() {
Triangle Cl = new Тг1ап91е("равнобедренныЙ."‚ 4.0, 4.0);
Triangle t2 = new Тг1ап91е("ПрямоугольныЙ."‚ 8.0, 12.0);
Соп5о1е.Иг1сеЬ1пе("Информация об объекте tl: ");
t1.showStyle();
t1.showDim();
Console.WriteLine("Hnomaub = " Р tl.area());
Console.WriteLine();
«pawl
Соп5о1е.Иг1сеЬ1пе("Информация об объекте t2: ");
t2.showStyle():
t2.showDim();
Console.WriteLine("Hnomaue W " + t2.area());
B этой программе конструктор класса Triangle () C помощью ключевого слова base
Bbl3blBflCT конструктор класса TwoDShape с параметрами w и h. B результате выпол-
нения конструктора base (w, h) происходит инициализация свойств wldth И height
указанными аргументами. То есть теперь конструктору класса Triangle HC нужно
иницигьтизировать эти свойства. требуется лишь выполнить инициализацию пере-
ионной style, которая определена только в Наследующем ютассе.
Наследуемый класс может иметь несколько конструкторов. каждый из которых
можно вызывать с помощью печючевого слова base. Вызывается тот конструктор‚
список параметров которого соответствует указанному списку аргументов. Ниже
приводится код программы, в которой используются расширенные версии классов
TwoDShape И Triangle. B ЭТИХ классах определены конструкторы по умолчанию И
конструкторы, принимающие один или несколько аргументов.
// В программе используются расширенные версии классов TwoDShape И Triangle-
using System;
class rwoDshape {
double prifiwidth; // Объявление закрытых переменных.
Конструкторы и наследование 299
3ощЬ1е рг1_пе19Пс;
/' Определение конструктора по умолчанию.
public TwoDShape() (
width L height = 0.0;
/ Конструктор с двумя параметрами.
public TwoDShape(double w, double h) {
width Ч W;
height = h;
s/ Конструктор, предназначенный для создания объектов, у которых свойствам
// width И height присваивается одно и то же значение.
public TwoDShape(double x) {
width = height = х;
/н Определение свойств width И height.
public double width {
get { return pri_width; }
set { pri_width = Value; )
public double height {
get { return pri_height; )
set { pri_height = value; }
}
public void showDim() {
Console.WriteLine("3HaueHMq ширины и высоты геометрической фигуры = " +
width т " и " + height);
// B этом классе, наследующем класс TwoDShape, определены характеристики,
// свойственные треугольникам.
class Triangle : TwoDShape {
String style; // Закрытая переменная.
/‘ Конструктор по умолчанию. Он автоматически вызывает конструктор ПО
умолчанию класса TwoDShape.
у I
/ . .
_ Использование юхючсвого слова
public Triangle() ( Ьазе1шявыжшаржшичныхкон—
style = "нуль _ " ; структоров класса ’I'woDShape.
// Конструктор с тремя параметрами.
public Triangle(string s, double w, double h) : base(w, h) {
style = s;
}
// Конструктор, предназначенный для создания объекта, в котором определены
// характеристики, Свойственные равнобедренным треугольникам.
public Triangletdouble x) : base(x) (
OUU
Style И "равнобедренный. I
douD;e area() Ё
›сд„гп w;dth ' ne-gh: 4;
D;\ _с V010 showS:yie() {
;„п5о1е.Иг1ьеЬ;пе("Вид треугольника " т style);
z«« Shapesfi i
puDL;C static voia Main() {
‚г angle :1 — new Tr1ang;e();
I -anq;e t2 2 new Тг1ап91е("нрямоугольный.", 8.0,
Ttzangle t3 — new Triang1e(4.0);
;¢ E2;
Соп5о1е.Иг1:еЬ1пе("Информация об объекте :1: ");
:1.showSLyle();
:1.showDim();
Соп5о1е.Иг;пеЬ1пе("Площадь — " t1.area());
ole.Wri:cLine();
'гп5о1е.Иг;ЕеЬ1пе("Информация об объекте t2: ");
L;.showstyle():
:2.showDim();
Console.WriteLine("Hnomanb
u
t2.area());
Console.Wr1:eL:ne():
Console.Wr;:eL;ne(”Mh®opMauMA об объекте :3: ");
;3.showS:y4e();
L3.showD1m();
Соп5о1е.Щг1ЕеЬ1пе("Площадь ~ " + t3.area());
C0nsoLe.Wr1teLine();
РСЗУЛЬТЦТЫ ВЫПОЛНЕНИЯ ПрОГРЗММЫ ПрИВСДСНЫ НИЖС.
Ппформация об объекте :1:
:д треугольника — прямоугольный.
чепия ширины и высоты геометрической фигуры г 8 И
щддь 48
-зрмация об объекте :2:
l '.';S-E3yl'x3J."_:hMKa 1:p;mcy1 C-Jl':.HblI7. .
.ачеиия ширины и высоты геометрической фигуры — 8 И
Площадь 2 48
Информация об объекте E3:
Вид треугольника И равнобедренный.
12
L2
окрытие переменных и наследование д” ‘
„доения ширины и высоты геометрической фигуры 4 И 4
3.
площадь = 8
при уКНЗЗНИИ B конструкторе НаСЛСДУЮТЦСГО КЛЭССЗ КГПОЧСВОГО СЛОВ?! base Bbl3blB'c1"
дтся КОНСТРУКТОР НаСЛСДуСМОГО класса. TO CCTb КЛ ЮЧСНОС СЛОВО base ВССГДН ССЫЛНСТСЯ
На НЦСЛСЦУСМЫЙ КЛНСС, КОТОРЫЙ B ИСрНрХИИ ННСЛСДОВЭНИЯ НЦХОДИТСЯ НСПОСРСЛСГНСННО
над въызываюшихт классом (вызываетеэп конструктор класса. который непосредствен-
HO был гпаследован вьтзьжваюпнтм классом). Это справедливо даже для многоуровневой
„Срдрхрт и. Передача аргументов наследуемому конструктору осуществляется путем
VKEUIIHHSI ЭТИХ НрГуМСНТОВ B CKO6K'c1X ПОСЛС КЁПОЧСВОГО слова base. ЕСЛИ B конструкторе
вЫЗЬНЦПОЩСГО класса ОТСУТСЁГВУСТ КЁПОЧСВОС СЛОВО base, ТО ННТОМНТИЧССКИ ВЫЗЫННСТСЯ
Конструктор наследуемого класса. определенным по умолчани ю.
M1/flymms/J практикум
I. Каким образом нз конструтсгора ше.тедуяошсго класса вызывается конструк-
тор наследуемого класса?
2. Могут ли параметры передаваться конструктору наследуемого класса’?
Всегда ли ключевое слово base ссылается на конструктор непосредственно
наследуемого класса’?
Ёкрытие переменных И наследование
В Ct: члены наеледующего и наследуемого классов могут иметь одно и то же имя.
В гаком случае член наследуемого класса скрыт для других членов наследующсго
кщеса, и компилятор выводит пкредугкрежденне об этом на экран, Если вы намеренно
CKPBIBEICTC ЧЛСП НЫСЛСДУСМОГО КЛНССН И ПС ХОТИТС, ЧТОбЫ КОЁИППАЛЯТО}? ВЫВОЛИЛ ПрСДу-
пяреждатошее сообщение, то укажите перед членом наеледуюгпего класса ключевое
слово new. Имейте в виду, что предназначение (вьъполняемьпе функции) ключевого
слова new Н 11EiHHOM СЛУЧНС ОТЛИЧНСТСЯ ОТ CFO предназначения при СОЗДННИИ ЭКЗСМП-
ляра объекта.
Ниже приводится программа, в которой демонстрируется скрытие переменной при
наследовании.
‘ В программе демонстрируется скрытие переменной при наследовании.
>j;ng System;
Lass А {
public int i а C;
Переменная 1 в классе в скрывает переменную 1,
объявленную в классе А. Обратите внимание на ис-
пользование ключевого слова new.
/ Наследующий класс.
Lass B : А {
new int i; // Эта переменная i скрывает переменную i объявленную в классе A.
public B(int b) i
1. Для вызова конструктора наследуемого класса в конструкторе наследующего ъсшсса указы-
вается ключевое слово base.
302 Глава 8. Наспедова
i — b; // Инициализация переменной i B классе B.
а
г
public void show() { Ё
Console.WriteLine("3HaqeHMe переменной i B наследующем классе: " + i);7
} а
} ь
class NameH;ding { З
public static void Main() { Ч
В ob : new B(2);
ob.show(); i
)
г ‚д;
ё
Обратите внимание на использование ключевого слова new. Фактически оно указщё
вает компилятору, что программист при создании новой переменной 1 намереннЪ
скрывает переменную L, определенную в наследуемом классе А. Если ключевое словф
new HC будет указано, то компилятор выведет гтредупреждатогпее сообщение. °
Результат работы этой программы выглядит следующим образом:
Значение переменной i B наследующем классе: 2
„им-гр,
После определения переменной 1 в классе В будет скрыта переменная 1, определенн
в классе А. То есть при вызове метода show () для работы с данными объекта, имеюще Ё
тип В, будет отображаться значение переменной i, определенной в классе В.
Использование ключевого слова base для доступа
K скрытой переменной
Ранее уже говорилось, что ключевое слово разе применяется также для доступа к
скрытым членам наследуемого класса. Используется следующая форма синтаксисах
м 7 за.
‚1
base.member
Здесь слово member представляет метод или переменную экземпляра. При таком
использовании шючевое слово base выполняет практически те же функции, что и
ючючевое слово this, за исключением того, что слово base всегда ссылается на
наследуемый класс.
Использование ключевого слова base для доступа к скрытой переменной демонст-
рируется в следующей программе:
// В программе демонстрируется использование ключевого слова base для
// доступа к скрытой переменной.
using System;
class A (
public int i = О;
// Создание наследующего класса.
class B : А {
new int i; // Эта переменная i скрывает переменную i, объявленную в классе А.
public B(int a, int b) (
303
Ытие переменных и наследование
от
base.i = а; // Обращение к Переменной i, определенной в классе A,
// c помощью ключевого слова base И оператора точка (.).
i 2 b; // Инициализация переменной i, определенной в классе В.
}
puo;ic void shOw() {
/5 Отображение переменной i, определенной в классе А.
I
gunsole.WriteLine("3HaqeHMe переменной i B наследуемом классе: " + base.i);
Отображение переменной i, определенной Ь классе B.
соп5о1е.Иг1пеЬ1пе("Значение переменной i B наследующем классе: " + i);
dass UncoverName {
puDllC static void Main() {
B ob = new B(l, 2);
ob.sh0w();
РЁЗУЛ ЬТНТ ВЫ ПОЛНСНИЯ програм М Ы:
течение переменной i в наследуемом классе: 1
Шшчение переменной i E наследующем классе: 2
Хотя переменная экземпляра 1 B объекте ob скрывает переменную 1, определенную
в классе А, использование ключевого слова base обеспечивает доступ к перемен-
ной 1, определенной в наследуемом классе.
С ПОМОЩЬЮ КПЮЧСВОГО CHOB3 base ТНКЖС МОЖНО ВЫЗЫВНТЬ СКрЫТЫС МСТОДЫ. Например,
// В программе демонстрируется вызов Скрытого метода с помощью ключевого слова
// base.
“Sing System;
Class A {
public int i = O;
./ Метод show(), определенный в классе А.
public void show() {
Соп5о1е.Иг1ЕеЬ1пе("Значение переменной i E наследуемом классе: " + i);
'7 Создание наследующего класса.
3—ass B ; А {
new int i; // Эта переменная i скрывает переменную i, объявленную в классе A.
public B(int a, in: b) {
baSe.i 4 ai // Обращение к переменной i, определенной в классе А, с помощью
// ключевого слова base.
i = b; // Инициализация переменной i, определенной В классе B.
304 Глава 8. Наследов
/ / этот метод show () скрывает метод show () ,
/.-’ определенный в классе А. Метод show () скрывает метод с таким же именем,
пе“ public „Ода Show U { 4._..._’—..-— 0flpC11CJ'lL’5HHblM В классе А. '-
разе . 5 owl) ; // Вызов метода show () , определенного а классе А. 1
Этот оператор ккызьпнаст‘ скрытый метод show ( ) . д
/,’ Вывод значения переменной ;, инициализированной в классе В. д.
:.:onso;e.Wr1teL1ne("3Hauem4e переменной i B Наследующем классе: " + i);
<:'_ass UncoVerName I
pub;ic stat;c моде M-ain() {
B ob ь new ВЦ, 2):
oD.shoW().'
y
Ре з)‘. к ьтат выпол I не н ил программ ы:
Зддчсние переменной д в наследуемом Классе: 1
Значение переменной i в наследующем классе: 2
Оператор base.sh0w(),' вызывает метод show(). определенный в наследуемом
классе.
Ктхочевое слово new указывает компилятору, что при создании нового метода
.:‘:‘.' ш () программист намеренно скрывает метод s..ow(). определенный в насле-
луемоч классе
Мену/пни? практикум
I. B Наследующем классе объявляется переменная, которая будет скрывать
переменную е таким же именем, определенную в наследуемом классе. Какое
гспоченос слово указывается перед именем переменной и се модификатором
при ее объявлении’?
2. Какое ключевое слово используется для обращения к скрытой переменной
(скрытому методу) наследуемого всласса‘?
Можно ли из наеледуюълего класса вьвывать скрытьпй метод, определенный
в наследуемом классе‘?
Ь.‘
Проект 8.1. Расширение возможностей класса
Vehicle
Ш TruckDemo.cs
B этом проекте мы займемся усовершенствованием и наследованием класса Vehicle,
разработанного в главе 4. Напомним. что переменные ктасса Vehicle содержат
слсдуюцлупо информацию об автомобиле —- количество пассажиров, которое может
дкрытие переменных и наследование 305
Перевезти автомобиль, запас топлива, а также расстояние (в милях), которое этот
автомобиль может проехать, используя один галлон топлива. Воспользуемся щасщщ
„г; как базовым для разработки более епециачизированньпх классов. наприхаер
„я разработки клаееа Truck, который бы хранил информацию о таком типе
ашомобгьтп, как грузовик. [Зажнейшедй характеристикой грузовика является грузо-
„ддьсмноеть, поэтому для создания класса Truck необходимо только наеле.=1она‘гь
Класс Veh;cle и добавить в определение класса конструктор, иниЦигыизирупопшг:й
ПсрсИСННУЮ‚ в которой будет храниться значение грудоподьеипоети. Чтобы усовер-
шспетвоъзать класс Vehicle, объявим его переменные закрытьпми и определим
свинства для доетупа к этим переменным.
nomaroaan ИНСТРУКЦИЯ
1, Создайте файл ТгосКЭото . с5 и скопируйте в него код последней версии класса
fchlcle и31лавь|4
Y-\J
Создайте класс Truck, как показано ниже.
// Определение класса Truck, наследующего класс Vehicle.
glass Truck : Vehicle {
int pri_cargocap; // Грузоподъемность (в фунтах)-
// Конструктор класса Truck.
public Truck(inL p, Lnt f, ;nt m, int C) : base(p, t, m)
(
cargocap д C ,'
// Свойство, обеспечивающее доступ к переменной pr;_cargocap.
public int cargocap {
get { return pr1Acargocap; }
set I pri_cargocap = value; )
}
I
Класс Truck наелсдуетъопше Чеп1с1е‚причем Цобавляеплпепоиство cargocap.
Таким образом, в mace Truck включена вея игпрорпигпциэ: о транспортном сред-
етве, определенная е Помощью класса Vehicle. Добавлять в класс Truck СЛСЛУСТ
только те члены класса, которые придают ему специфические характеристики,
Целая его уникальным.
3. Объявите переменные класса Vehicle закрытькми, а затем переименуйте их.
int ppi_passengers; // Количество пассажиров.
int pri_fuelcap; // Емкость топливного бака (в галлонах).
int pri*mpg; // Расстояние (в милях), которое данный автомобиль
// МОЖЕТ проехать, ИСПОЛЬЗУЯ ОДИН ГЗЛЛОН TOIIJIMBEI.
Ф-
Добавьте свойства, определяющие доступ к этим Переменным.
// Свойства.
public Ln: passengers {
get { return pri_passengers; }
set { pr;_passengers = value; }
}
public int fuelcap (
306 Глава 8. Наследоваш
get { return pri_fuelcap; }
set { pri~fuelCap = value; } д
public int mpg (
get { return pri_mpg; D
set ( priampg е value; }
э. Ниже приведен полный код программы, в которой на основе класса Vehicle д,
помощью наследования создается новый класс Truck. '
/‘k
Проект 8.1.
B программе демонстрируется ИСПОЛЬЗОВЭНИЭ НЗСЛЭДОВЗНИЯ ДЛЯ СОЭДЭНИЯ
НОВОГО КЛбССа.
*/
using System;
class Vehicle {
lnt pr1‘passengers; // Количество пассажиров.
int pri_fuelcap; // Емкость топливного бака (В галлонах).
int pri’mpg; // Расстояние (в милях), которое данный автомобиль
// может проехать, используя один галлон топлива.
// Конструктор класса Vehicle.
public Vehicle(int р, int f, int m) {
passengers = р;
fuelcap = f;
mpg ‘ mi
// Метод возвращает значение максимального расстояния, которое может
// проехать автомобиль с полным топливным баком.
public 1nt range() (
return mpg * fuelcap;
// Метод вычисляет количество топлива, необходимое для преоцоления
// расстояния, значение которого передается методу в качестве параметра.
public double fuelneeded(int miles) {
return (double) miles / mpg;
// Свойства.
public int passengers (
get ( return pri_passengers; )
set ( pEi*paSSengers = value; Ъ
)
public int fuelcap {
get { return privfuelcap; )
set ( pri_fuelcap = value; )
}
скрытие переменных и наследование 307
public int mpg {
get { return priwmpg; }
set E prilmpg — value; }
Определение класса Truck, наследующего класс Vehicle.
class Truck : Vehicle {
„пс rivcargocap; // Грузоподъемность (в фунтах).
/у Конструктор класса Truck.
public Truck(int p, int f, int m, int c) : base(p, L, m)
cargocap 2 с;
'/ Свойство, обеспечивающее доступ к переменной рхтфсагоосар.
public int cargocap {
get ( return prificargocap; }
set { pri_cargocap = value; }
class TruckDemo {
public static void Main() (
// Создание некоторых объектов класса Truck.
Truck semi 2 new Truck(2, 2C0, 7, 44000); // Автофургон.
Truck pickup L new Truck(3, 28, 15, 2000); // Пикап.
Qouble gallons;
int dist = 252:
gallons = semi.fuelneeded(dist);
Соп5о1е.Иг1ЕеЬ1пе("Грузоподъемность автофургона " + semi.cargocap +
" фунтов_");
Console.WriteLine("HTo6m проехать " + dist т
"требуется " + gallons + " галлонов топлива.\п");
" мили, автофургону " +
gallons 2 pickup.fuelneeded(dist):
Соп5о1е.Иг1сеЬ1пе("Грузоподъемность пикапа г " + plCkup.CaEgOCap т
" фунтов.");
Сопзо1е.Иг1сеЬ1пе("Чтобы проехать " + dist + " МИЛИ, пикапу " +
"требуется " + gallons + " галлона топлива.\п");
(3- РСЗУЛЬТНТ ВЫПОЛНСНИЯ ПрОГрНММЫЁ
Грузоподъемность автофургона = 44000 фунтов.
Чтобы проехать 252 мили, автофургону требуется 36 галлонов топлива.
Грузоподъемность пикапа = 2000 фунтов.
Чтобы проехать 252 мили, пикапу требуется 16,8 галлона топлива. |
Создание многоуровневой иерархии классов я;
308 Глава В. Наспедо f
7. Класс Vehicle могут наследовать многие другие типы классов. Наприме
следующем фрагменте кода приведены Первые строки определения ютаеса, к
рый содержит информацию о клиренсе внедорожного автомобиля. °
// Создание класса, содержащего характеристики внедорожного автомобилд„{
class OffRoad : Vehicle {
int groundclearance; // Величина клиренса (в дюймах).
//
I
I
Изучив вьщтеизложснньтй материал, сделаем выводы. Итак. используя наследова
вы можете создавать новый класс, в котором определены общие черты какого-л
объекта, эти черты могут быть унаследовапы новым классом, в который добавля о
его уникальные характеристики.
До настоящего момента мы использовали простую иерархию классов, состоящуку
наследуемого и наследующего классов. В С# можно также создавать иерар _
содержащую любое количество уровней наследования. Ранее говорилось, что мо ‘
использовать наследующий масс как базовый mm дальнейшего наследования. В ‚
чсствс примера рассмотрим три класса — А, В и С. Пусть класс С наследует класс;
который в свою очередь наследует класс А. В подобной ситуации каждый наследухо-
щий класс наследует все черты, характерные для его наследуемого класса. В дают
случае КЛПСС С ННСЛСДУСТ ВСС характеристики КЛЗССОВ В И A.
B следующей программе демонстрируется исподтьзотзание многоуровневой иерархии.
Здесь класс Triangle уже является базовым при создании класса ColorTriang1e.
который наследует все характеристики классов Triangle И Twobshape. B классе
C0lorTr1angle объявлена новая переменная color, содержащая информациюд
цвете треугольника.
// Многоуровневая иерархия классов.
using System;
class TwoDShape {
double prigwidth; // Объявление закрытых переменных.
ёоцьде pri_height;
// Определение конструктора по умолчанию
public TwoDShape() {
width = height A 0.0;
// Конструктор с двумя параметрами.
public TwoDShape(double w, double h) {
width = w;
height = h;
I
// Конструктор, предназначенный для создания объектов, у которых Свойствди
// width И height присваивается одно и тоже значение.
public TwoDShape(double x) {
. 003
дание многоуровневой иерархии классов 309
„,а;н 4 height ~ х;
д„эедслеыие свойств wldih И hCl9hi~
„Цд‚_: double w;dCh Ё
до; 1 return pri_wid:h; Р
n: 1 pr; w;cth = value; }
рд5„_с double helght _
~: { return pr;ihe1gh;; }
I priiheigh: — value; }
1 A; voxd showD1m() i
Соп3о1е.Иг1СеЬ;пе("Значения ширины И высоты геометрической фигуры
width т " и " + height);
H 5 этом классе, наследующем класс TwoDShape, определены характеристики,
Д дйственные треугольникам.
mas: Triangle : TwoDShape 1
s::_ng style; // Закрытая переменная.
J’ Конструктор по умолчанию. Он автоматически Ьызываещ Конструктор
no умолчанию класса TwoDShape.
;c Triangle()
szyle - "нуль";
Еонструктор с тремя параметрами.
P: -ic TrLangle(string s, double w, double h) : base(w, h) {
;yle — s;
Конструктор, предназначенный для создания объекта, в котором определены
характеристики, свойственные равнобедренным треугольникам.
Р.;;1с Triang;e(double X) : base(x) {
5;у1е — "равнобедренны
р
Е..Д1с double area() i
urn width * height / 2;
-;с уощс showS:yie:) 1
5onsole.Wr1teLinc("BMu ТреУГОЛЬНИКа ~ " style);
:=. ` 'Т‘ ' ‘I
‘асс COLOr*rlang‘e’ Krracc Co1orTI1angle наследует класс Trianql о.
даСЛедУЮЩИЙ КЛЗСС Triangle. xonmufiiuwmtmcmmbnmmwwcrwmccrwomfiupa
5 co1orTriangle д Triangle {4_____uomomyuxnmmccolorhnangleupuQmcmym1um
M ring Color; члены классов Trlangle и ‘Гиоюъпаре,
310 Глава В. Наслвдов
public ColorTriangle(string c, string s,
double w, double h) : base(s, w, h) (
color 2 с;
// Вывод информации О цвете треугольника.
public void showColor() (
Console.WriteLine("uBeT треугольника т color);
I
О
class Shapes6 (
public static void Main() (
ColorTriangle tl =
new Со1огТк1ап91е("голубой"‚ "прямоугольный", 8.0, 12.0);
ColorTriangle t2 =
new Со1окТг1ап91е("красный"‚ "равнобедренный", 2.0, 2.0);
Соп5о1е.Иг1пеЬ1пе("Информация об объекте tl: ");
tl.showStyle();
tl.showDim();
t1.showColor();
Соп5о1е.Иг1пеЬ1пе("Площадь 2 " + tl.area());
Console.WriteLine();
Соп5о1е.Ик1сеЬ1пе("Информация об объекте t2: ");
t2.showStyle();
t2.showDim() ; 4____.___:.
C2.showColor();
Соп5о1е.Иг1сеЬ1пе("Площадь = " + t2.area());
Объект класса Co1orTr1ang1e может вызывать методы,
определснньте в нем, а также в его наследуемых классах.
) .
Результат выполнения программы:
Информация об объекте tl:
Вид треугольника — прямоугольный
Значения ширины и высоты геометрической фигуры = 8 И 12
Цвет треугольника _ Голубой
Площадь = 48
Информация об объекте с2:
Вид треугольника — равнобедренный
Значения ширины и высоты геометрической фигуры E 2 и 2
Цвет треугольника — красный
Площадь = 2
Поскольку при создании класса ColorTriangle применяется многоуровневое На’
следование, объекты этого класса могут использовать все члены классов Triangle
И TwoDShape. При определении класса ColorTriang1e добавляется только строковая
переменная, необходимая для его специфической ЦСЛИ — возможности указать ЦВ“
треугольника. Наследование обладает тем преимуществом, что позволяет повторно
использовать код.
ОТМСТИМ, что данная программа демонстрирует еще одну важную особенность ’
ключевое слово base всегда ссылается на конструктор ближайшего наследуемого
ызываются конструкторы 31 1
дылда
‚даешь Если югючсвое слово base указывается в массе Colo rTr1angJ,e, то вызыва-
em, конструктор масса Triangle. Если же ключевое слово base указывается в
Классе т: idngie. то вьвзьпзается конструктор класса TwoDShape. Когда конструктору
Ндследуехюго класса необходимы какпс-лъпбо параметры, то в соответствии с прави-
дами создания мпогоуровнсвогт иерархии ютассов всенаследуюгдис классы должны
Псрдтавать эти параметры «вверх» на соответствутошии уровень, причем вне зависи-
\l0C11| О"! ТОГО, НУЖНЫ ЛИ ЭТИ ПНрНМСТрЫ самому ННСЛСДУПОШСМУ KJIHCC)/.
Когда ВЫЗЫВЗЮТСЯ КОНСТРУКТОРЫ
при изучении Предыдущей темы мог возникнуть вполне закономерный вопрос о том,
Каков конструктор при создании наследуюшего класса вьтзьнгзается первым — конст-
руктор наеледуюшего юпасса или конструктор наследуемого класса. В иерархии
‚шасеов конструкторы вызываются в том порядке, в котором выполнялось наследо-
Ванне от наследуемого класса к наследуюшему. Более того, этот порядок остается
неизхвенньтм независимо от того, используется ключевое слово base или нет. Если
это ключевое слово не указывается, вызывается заданный по умолчанию конструктор
(без параметров) каждого наследуемого класса. Порядок вызова конструкторов де-
монсгрируется в следующей программе:
H B программе демонстрируется порядок вызова конструкторов.
using System;
H Создание наследуемого класса.
class A {
p\.'bLiC А() {
Console.WriteLine("MeTou, определенный в классе A.");
Создание класса, наследующего класс А.
ass B : A {
Public B() {
Console.WriteLine("MeTon, определенный В классе B.");
I" ‘:Q3LLaHI/[8 класса, наспедующего КЛЭСС В.
Ёдааз С : B {
Fublic C() {
Console.Wr1teLine("MeTon, определенный в классе С.");
"is Orderofconstruction 1
ь
iubl:c Static void Main() {
C c : new C();
31 2 Глава В. Наслецова
В резулнгатс выполнения этой программы будут выведены слсдуюцшс строки:
Метод, определенный В классе А.
Метод, определенный в классе B. g
Метод, определенный в классе С.
.~_....£~;:E._ ._ '
Как видите. конструкторы вызываются в порядке наследования, и это оправдана“
посколькулюбая инициализация, выполняемая в наследуемом классе, может служит-Ё. э;
основой для инициализации, выполняемой наследуюшим классом. Поэтому кони;
руктор наследуемого класса должен вызываться первым, ' ='
Ссылки на объекты наследуемого и
наследующего классов
Как вы уже ‘знаете, C1: является язьтком. требующим строгого соблюдения тип ‚
(ггапрътмср, при выполнении операции присваинаггия). Автоматическое прсобразоваёё‘ _
НИС Tuna, Ismnomlmolucccsr при работе е гтерсмснныхти обычных гипов. не распре-Ё
странястся на Переменные ссылочного типа. То есть ссылочная ггерсменная одного}
класса нс может ссылаться на объект другого класса. Рассмотрим следующую npo-=" “
грамму: ’
// Эта программа не может быть скомпилирована. ffi
class Х { lf
int a: 7
public X(lflt i) { а = i; }
class Y {
Lnt a;
:4
public Y(inL i) { a ; i; }
v%3mfiw$
class Incompatib1eRef {
publxc static vold Main() i
X х new X(lC);
X x2;
Y y « new Y(5);
X2 « х; // Эта операции присваивания действительна, ьоскольку в ней
// участвуют ссылочные переменные одного типе.
x; е y; // Ошибка, типы ссьшочных переменных не совпадают.
1 ЭТИ ССЫЛОЧНЫС ПСрСМСКНЫС HCCOHMCC1HMb(-‘I а
B ‘этой программе, даже если шассы Х и Y физически совпадают, ссылочной
переменной типа х невозможно присвоить ссылку на объект типа Y. To есть
ссылочная переменная может ссылаться только на объекты одного типа.
Однако существует важнос исключение в строгои типизации C#. Ссылочной пере‘
мснной наследуемого класса может быть присвоена ссылка на объект любого Haema-
flylOU_1CI‘O класса. Рассмотрим, как выполняются эти операции.
ССЫЛКИ на объекты Hacnenyemoro И НЗСПЭДУЮЩЭГО КЛЭССОВ
‚г
/-1 Сддьтцточлаая переменная НЗСЛСДУЕМОГО класса МОЖЕТ ССЫЛЗТЬСЯ на объект
I
д
u5;."1=4'
ьддледующего класса.
System;
C;(1SS~
рдгД;с Ln: a;
Fu,-,; X(;n: 1) {
А:
сдадз Y : Х {
puD-Lc Ln: b;
public Y(1nt i, int j) : оа$е(]) (
о
сдаьз BaseRef (
pub;1c static VOld Main() (
X х — new X(lO);
Х х2;
Y у = new ‘НЕ, 6):
x2 = x; // Это Действительная операция, поскольку задействованы
// переменные одного и того же типа,
Сэп5о1е.Иг1пеЬ1пе("Значение переменной х2.а: " + х2.а);
ПОСКОЛЬКУ КЛЗСС Y HaCIICD.yC'I‘ K}
X, ССЫЛОЧНЗЯ HCDCMCHHZH X2 М‘
жет ссылаться на объект у.
// поскольку класс Y наследует класс Х.
Соп5о1е.Иг1СеЬ1пе("Значение переменной х2.а: " + х2.а);
х2 = y; // Также действительная операция,
// Ссьшочная переменная типа Х "знает" только о членах класса Х.
х2.а = 19: // действительное обращение.
1
I: x2.b = 27; // Ошибка, В классе X не определена переменная b.
I
B пой программе юпасс Y наследует mace X; таким образом, переменной х2 моя
UNI!) ПрИСВОСНИ ССЫЛКИ на ОбЪСКТ у.
Важно понимать. что возможность доступа к членам ютасса зависит именно от ти
ссылочной переменной, а не от типа объекта. на который она ссылается. То есть ес.
“Сылочнотй переменной. имеющей тип наследуемого масса, присваивается ссыл
“I1 объект наследуюшего масса, то с помощью этой переменной можно буд
"брашаться только к тем членам шасса, которые были определены В ННСЛСЛУСМС
‘Насос. Именно поэтому ссылочная переменная х2 не имеет доступа к перемеННС
Экземпляра b 1I3)KC после того. как гтеременной х2 была присвоена ссылка на объет
класса Такое ограничение имеет смысл, гюскольку‘ наследуегиыйт класс ничего ъ
v ЛИСТ» о тех членах класса. которые были Добавлены при огтредегяеътътьт наследуюшс!
Класса. Поэтому, чтобы приведенная вытие программа могла быть скомпилирован;
се последний оператор был закомментиротзгтн.
314 Глава В. Наслецова -
тип наследуемого масса, присваивается ссылка на объект наслсдующего кдассад
Нштример. обратите внимание на следующие версии кчассон TwoDShape и Triangle-
.„
В обеих версиях есть конструкторы, использующие ооъект в качестве параметра,
// Передача ссылочной переменной наследуемого класса ссылки на объект
// наследующего класса.
using System:
class TwoDShape (
double pri~width; //Объявление закрытых переменных.
double pri_height;
// Определение конструктора по умолчанию.
public TwoDShape() (
width = height = 0.0;
// Конструктор с двумя параметрами.
public TwoDShape(double w, double h) (
width = W;
height — h;
// Конструктор, предназначенный для создания объектов, у которых свойствам
// width M height присваивается одно и то же значение.
public TwoDShape(double x) (
width = height = х;
// Конструирование объекта на основе другого объекта.
Public TWODShaPe (TW0DShaPe Ob) ( т Конструирование объекта на основе
width = ob . width; объекта этого же класса
height = ob.height;
//Определение свойств width M height.
public double width (
get { return priiwidth; }
set { pri_width : value; }
1
public double height {
get { return pri_height; }
set { рг1/Не19НЕ 2 value; }
public VO;d showD;m() (
Соп5о1е.Иг1пеЬ1пе("Значения ширины M высоты геометрической фигуры 4 " +
width + " M " + height);
ссылки на объекты наследувмого и наслецующего классов 315
// B этом классе, наследующем класс TwoDShape, определены характеристики,
// свойственные треугольникам.
Class Triangle : TwoDShape (
Strlng style; // Закрытая переменная.
/; определение конструктора по умолчанию.
puolic Triangl€() (
style = "нуль";
// Конструктор с тремя параметрами, вызывающий конструктор наследуемого
// класса.
public Triangle(string s, double w, double h) : base(w, h) (
style S S;
// Конструктор, предназначенный для создания объекта, в котором определены
// характеристики, присущие равнобедренным треугольникам.
public Triangle(double x) : base(x) (
style = "равнобедренный":
f/ Конструирование объекта на основе другого объекта.
public Triangle(Triangle ob) : base(ob) (<———————[kpamuaccunxuHao6umTxnmxa
style _._ ob _ style I. Triangle конструктору класса
TwoDShape
public double area() (
return width * height / 2;
public void showScyle() {
Console.WriceLine("BMn треугольника — " + style);
class Shapes7 (
public static void Main() (
Triangle C1 = new Тг1ап91е("прямоугольный"‚ 8.0, 12.0);
// Копия объекта tl.
Triangle t2 = new Tr1angle(cl);
Соп5о1е.Иг1сеЬ1пе("Информация об объекте С1: ");
tl.showStyle();
tl.showDim();
Console.WriteLine("Hnomanb = " + C1.area());
Console.WriceLine():
Соп5о1е.И:1пеЬ1пе("Информация об объекте — t2: ");
t2.showscy1e();
t2.showDim();
Соп5о1е.Ик1пеЬ1пе("Площадь 2 " + t2.area());
315 Глава В. Наследова
B Hp()I‘p2l‘\I_\1C объект t2 конструируется на основе объекта :1, следовательно, объе
- 1 и гщспгпчньт. Результат вьшогтпепгтя программы приведен ниже:
Иноормаьия со ьоъокте ti:
Ьид треугольника прямоугольный
Зпннедич I ины И высоты геометрической фихурь r S и 12
площадь 48
Информация об объекте t2:
Ниц треугольника прямоугольный
Значения ширины И высоты геометрической фигуры ~ 3 И 13
[Длощидь » 45
Обратите особое внимание на конструктор класса Тгтагиц Le:
/I Конструирование объекта на основе другого объекта.
publ;c Trianqle(Tr;anq;e ob) : од5е(оЬ) {
style е ob.style;
)
' ..
;1е И передает его с помощь
—-nape. Приведем код этого кони-ё
Этот конструктор принимает объект типа Tr;
КЛПОЧСВОГО СЛОВ8 base конструктору КЛЭССН ;НО„
руКТОрШ
V
// Конструирование объекта на основе другого объекта.
риЬ11с TwoDShape(TwoDShape ob) { Е
шьопп ; ob.w1d:h; Ё
height = ob.heigh:; Ё
з ё.
4;
Обратите внимание, что конструктор Twmjshape () ожидает получить в качсс _
аргумента объект класса Twoilshape, HO конструктор Triangle () передает ему объекту
класса Tr;a:1{:, le. Как говорилось ранее, это происходит в силу того, что ссылочной;
переменной, имеющем тип наследуемого шасса, может быть присвоена ссылка наз
объект наследующего шасеа, То есть передача ссылочной переменной -3'0, имеющей
в конструкторе тыоззйаре () тип л-‚ясрзьаре, ссылки на объект OD ютасса Triangle;
является действительной операцией_ Поскольку конструктор Tw0DShape () HHMLLI/xa-._
лизируст‘ только члены класса TwoDShape. его не «интересует» то, что объект может
содержатьдругие члены класса, лобатзленньте при определении наследующих классов.
Минутный практикум
l. МОЖСТ ли наследующий класс использоваться в качестве базового ъстасса
Для другого наслслуюшсго масса‘?
В каком порядке вызываются конструкторы в иерархии классов?
а. Можно ли присВ0'
Км)
3. Преаположихд что ктасс Jet наследует‘ класс Axrpl =
пть ссылочной переменной типа A:.‘:p _== ссылку на объект типа Jet?
1. Да.
Копструкгорьт Ньтзынгквттсн в порядке наследования,
3. Да. Bo всех случая»; ссылочная псрсмстхнал типа паследуеьхого класса может ссылаться на
объект паслслутогттсгсч Класса, по не пмоборот:
VJ
виртуальные методы и переопределение 317
гвиртуальньпе МЕТОДЫ И переопределение
МС год, при определении которого в наследуемом классе было указан по ключевое слово
„д и который был переопредслсн (о переопределении метода: будет рассказано
В Сдшдтуклцсхт абзаце) в одном или более наотедуюхлих классах, называется виртуаль-
"M," ,uemor)o.u. СЛСДОЕЗЗТСЛЬНО. каждый наследующий mace .\10)KCT иметь собственную
„древко виртуального метода. В Св выбор версии виртуального метода. которую
Трдбдстся вызвать, осуьцествлястся в соответствии с типом объекта, на который
ссыласгсгк ссылочная переменная. Этот выбор (Опрсдслсние) осутдсствхвгтетсэп во время
выполнения продрали/ы. Ссылочная переменная может ссылаться на различные типы
объектов. следовательно, могут быть вызваны различные версии виртуальных мето-
дов. ‚хругими словами, именно тип объекта, на который указывает‘ ссылка (а нс тип
ссылочной ггеременнопй). определяет взызываемуво версию виртуального метода. Та-
Km} образом, если mace содержит виртуальный метод и от этого класса были
наследованы другие классы, в которых определены свои версии метода, при ссылке
переменной типа наследуемого класса на различные типы объектов вызываются
различные версии виртуального метода. При огтределенъти виртуального метода в
составе наследуемого ютасса перед типом возвращаемого значения указывается
ктючсвое слово V1 rtual, a при ггереогтределснии виртуального метода в наследую-
щем классе используется модификатор -3verr1de. Процесс определения виртуально-
го метода внутри наследуемого ъстасса, при котором частично или полностью изме-
Нясгся тело метода. а имя, параметры и их типы остаются прежними, называется
nepcm1pe(')e,-venuemMernoda. Виртуальный метод не может быть определен с модифика-
тором static или abstract (ИСПОЛЬЗОВННРПС этих модификаторов рассматривается
далее в этой главе).
Переопределение метода положено в основу концепции (ланолинчес-кого выбора вызы-
ваемого ‚метода. Это механизм, с помощью которого выбор вызываемого переопре-
деленного метода осухцсстпътяется во время взыполнсния программы, а не во время
компиляции.
Ниже приводится программа, в которой демонстрируется использование виртуаль-
ного метода и его переопределенных версий.
ii 3 программе демонстрируется ИСПОЛЬЗОЕЗНИЕ виртуального метода.
или; System;
3-355 Base {
'7 Создание виртуального метода В наследуемом классе.
”ublic virtual void who() ( <——————————~————~—————{O6bflmwHMCBHpTfimbHOH)MmOfl1J
COUSOl€.WritCLLfiC("M€TOfl who(), определенный 3 классе Base.");
Jss Derxvecl : Base ¢
Переопределение метода who() В наследующем классе Derivedl.
3UDlLC Override void who() { 4—————————————————{Пешюпршлшснисвиртшшьнопэмепша
Соп5о1е.И:1ЕеЬ1пе("Метод who(). Переопределенный 5 классе Derived1.");
3\ass Derived2 : Base {
// Еще одно переопределение метода whO() B Наследующем классе Derived2.
318 <„„
publ;c override void who() (
C;dSS Overridebemo \
pub;;c s:at:c мода Ma;n() {
Base baseOb — пси Ва5е();
Юсг1мео1 dOb1 — new Der1ved1();
Derivcd2 dOb2 — new Derived2();
Base baseRef; // Объявление ссылочной переменной типа наследуемого класс$Ё
baseRef = baseob;
baseRer.who();
а В каждом случае вызываемая версия
basegef dobl; мшодаипо()опршшляшсявоъемя
x _ 0 ' :3 с т
baseR&f‘whO(), 4. __ __ _____выпшпшнинпр циммыи тыги‘
от типа объекта. на который ссыла-
ется переменная baseRef.
baseRef = dOb2;
baseRef.who();
}
Результат выполнения программы:
Метод who(), определенный в классе Base.
Метод who(), Переопределенный В классе Derivedl.
Метод who(), переопределенный в классе Derived2.
B этой программе создается наследуемый класс Base, a также два наследуюших кл
De ruedl и Derlx/ed2. B классе вазе объявляется метод who (), который за ;7
переопредсляется в его наследуюших классах. В методе Ма1п () объявляются объе
имеющие тип Base, Derlvt’-:dl и Der1ved2, атакже объявляется ссылочная перем '
ная baseRef типа Base. Затем ссылочной переменной ЬазеРеЁ поочередно прис
ваются ссылки на объекты всех трех типов. Эти ссылки потом используются
вызове метода who r, ) . Как видно из результата выполнения программы выбор верс _
вызываемого метода who () зависит от типа объекта, на который ссылается перемей
ная baseRef. a не от типа самой переменной. ‘
Персопределять виртуальный метод не обязательно. Если наследующий класс
предоставляет собственную версию виртуального метода, то используется метф
наследуемого класса. Например, v_:
/' Если не переопределен виртуальный метод, используется метод наследуемоГ0“
КЛЗССЗ.
*/
us;ng System;
class Base {
// Создание виртуального метода в наследуемом классе.
.
рыо1;с м1гева1 void who() \
Соп5о1е.Иг1сеЬ;пе("Метод who(), определенный в классе Вазе.");
class Derivedl : Base {
_——-
:1
319
Виртуальные МЭТОДЫ И переопределение
/ Переопределение метода who() B наследуюшем классе.
public override void who() {
Console.WriteLine("MeTon who(), переопределенный в классе Derivedl.");
)
lasg Derivedz д Base { 4______{B этом классс не переопределяется мстод who ()
С .
// в этом классе не переопределяется метод who().
)
$355 NoOverrideDemo {
pub;1c static void Main() {
вазе baseob new Base();
Derivedl dObl = new Derivedl();
Derived2 dOb2 = new Derived2();
Base baseRef; // Ссылочная переменная типа наследуемого класса.
baseRef = baseob;
baseRef.who();
baseRef = dObl;
baseRef.who():
Вызов метода who () класса вазе.
который был определен в классе Base.
baseRef dOb2;
baseRef.who(); //
Вызов метода who(),
}
P63)/.-"I bTaT ВЫПОЛ НСНИЯ программы:
Метод who(),
Метод who(),
Метод who().
определенный в классе Base.
переопределенный в классе Derivedl.
определенный в классе Base.
ЗДЕСЬ в классе Derived2 не переопределяется метод who(), a при вызове метода
Wh" () объекта типа Derived2 вызывается метод who() , Который был определен в
массе Base.
для чего нужны переопределенные методы
Псрсопределенньпе методы обеспечивают в Cit поддержку полиморфизма во время
Выполнения программы. Полиморфизм очень важная составляющая объектно-ори-
ентированного программирования, позволяющая определять в наследуемом классе
“Поды, которые будут общими для всех наследующих классов, при этом наследую-
щий класс может определять специфическую реализацию некоторых или всех этих
"Полов. Переопределение методов представляет еще один способ реализации в Ci?
”DuHL1nna полиморфизма «один интерфейс, несколько методов».
ЧЮбы успешно I/lCI10.1b3OBaTb полиморфизм, вы должны понимать, что наследующий
‘1 Наслсдуемый классы формируют иерархию, в которой осуществляется переход от
Чсньшей специализации к большей. В наследуемом классс определены члены класса,
‘Юторые могут непосредственно использоваться наследующим классом, и методы,
‘Юторые в Наследующем классе могут быть либо переопределены, либо оставлены бсз
Изменения. В результате наследующий класс получает определенную свободу при
' °Пределении собственных версий методов, сохраняя при этом совместимый интерфейс
320 Глава 8. Наследован
(то есть возможность обращения к версии метода с использованием одного и то
имени и одной и той же ссылочной переменной). Таким образом применяя наел
дование и переопределение методов в наследуемом шассс, вы можете опрсделя
общие формы (операторы и переменные) методов. которые могут использоват
всеми его наследующими классами.
-.1
д? §:§§??§§t?:§§§£v§ £.§$§$§§$zz$§§§;s§
' ё
ад . .
Вопрос. Могут ли свойства быть виртуальными?
Ответ. Па. Свойства могут быть объявлены с клточевьизт словом тдгпаад
затем нереонрсделеньт с указанием клтоттеттого слова о гпзг Это справедливо
для индекса торов.
змия.
Применение виртуальных МЕТОДОВ
Продолжая изучать виртуальные методы, используем их в классе Тшоэзпаре. B п
дьтдутттих программах для каждого класса. наследуюшего класс Тыоэзпаре, опрел
лялся метод агеаН. Зная тматериач прсдыдутдего раздела мы можем усовершенс
вовать программу, определив в классе Tw«:.DS‘nape виртуальный метод area (). Э
метод можно переопределять в каждом 1-тас.=1сД)*то1.т1См классе, что дает возможное
вычислять площадь в зависимости от типа геометрической фигуры (трсугольних
квадрат и т. д), информация о которой инкапсулирована в данном классе. ( '
наглядности можно добавить в класс 'Pw3DE11ape переменную name типа string, 3
позволит отображать название каждой геометрической фигуры.) Ниже приведен к
этой программы.
sf MCUOnb3OBahM€ ниртуальных методов и полиморфизма.
„д; 2.";-e» „З
using System;
C;dSS TwoDShape [
dcubie pr; width; // ОбЪННЛСНИЁ закрытых переменных.
соио1е рг1„пс19Ьп;
string pr;Wname;
// Определение конструктора но умолчанию.
publ-c Tw3DSnape() [
w-dtn ne;gh: C.C;
name "1:yJ1b' ;
// кот*труктор C параметрами.
TwODShape(dOuble w, double h, s:r1ng n) {
Конструктор, предназначенный для создания объектов, у которых свойствам
{tn И height присваивается одно и то же значение.
рцо-;с ТшоПЗЬаре(осыЬ1е x, sLr;nq n) 1
w;d:h — height — x;
ПЕНИС - - П ,'
321
Виртуальные методы и переопределение
Г
Конструирование объекта на основе другого объекта.
р„д‚1с TwoDShape(TwoDShape ob) {
w;dth = ob.width;
* ne1ght = ob.height;
name L ob.name;
Определение свойств width, height И name.
ри. ьс double width [
де; [ return pr;_w1dth; }
„ас { pri_width — value; }
pablrc double height {
get [ return pri_height; }
set [ рг1_Ье19Ь: = value; }
ubLic strin name
P
get [ return pri_name; }
set [ рг1_пате = value; }
`-1с Void showDim() {
ы° Соп5о1е.Иг1пеЬ1пе("Значения ширины и высоты геометрической фигуры
w;dth + " и " + height);
:u_+_
_, -' Метод area () определен в классе
n‘ „
TwoDS'nape как ниртуальныи.
public virtual double area() {
Console.WriteLine("3ToT метод переопределяется в наследующих классах."):
return 0.0;
7/ B этом классе, наследующем класс TwoDShape, определены характеристики,
/7 присущие треугольникам.
Class Triangle : TwoDShape [
s;r;ng style: // Закрытая переменная.
Определение конструктора по умолчанию.
{чЬ11с Triangle() [
style = "нуль";
Конструктор класса Triangle.
iublic Triangle[string s, double w, double h) :
Ьа5е(И‚ h. "ТРеуГОЛЬНИК") [
style = s;
Ё ’/ Конструктор, предназначенный для создания объекта, B котором определены
Ё // характеристики, присущие равнобедренным треугольникам.
public Triang1e(doub1e х) : base(x, "ТреУГОЛЬНИК") (
style = "равнобедренный ";
322 Глава 8. Наспедов :
}
// Конструирование объекта на основе объекта.
public Triangle(Triangle ob) : base(Ob) [
sLyle ub.style;
// Переомределсние метода area() в классе Triangle.
|1срс0ПрсдсЛснис метода дгсА() u Knaccc'rriang
pubiic Override double area() Е
re:urn widzn ‘ he;gh: / 2;
puolic void showSCyle() {
Console.WriteLine{"BMu треугольника " + style);
// Класс Rectangle, наследующий класс TwoDShape.
class Rectangle : TwoDShape {
// Конструктор, предназначенный для Создания ооъекта, в котором определе
// характеристики, присущие прямоугольникам.
public Rectangle(double w, double h) :
base(w, h, "ПрЯМОУГОЛьНИК")[ }
// Конструктор, предназначенный для создания объекта, B котором определед
// характеристики, присущие квадратам.
public Rectangle(double х) :
base(x, "квадрат") [ }
// Конструирование объекта на основе другого объекта.
public RectangLe(Rec:angle ob) : bdS€(Ob) i }
public bool isSquare() {
if(width == height) return true;
return false;
‘vs. д
р’?! 9» я». '-7
// Переопределение метода агеа() в классе Rectangle.
public override double area() { <————————~————~———-I1q0npMmflCHMCMCKmadIea(r
return width * height; “M3999 ReCta“91e- -”
class Dynshapes [
public static VO;d Mdin() I
TwoDShape[] shapes — new TwoDShapef5j;
shapes[C] — new Тг1ап91е("прямоугольный", 8.0, 12.0);
shapes{l] = new Rectangle(lO);
shapes[2] — new Rectangle(1C, 4);
shapes[3} new Triangle(7.0);
shapcs[4] — new TwoDShape(lO, 20, "базовая");
for(int i—O; i < shdpes.Length; i++) (
Console.writeL1ne("3Ta геометрическая фигура называется — " +
shapes[i].name);
323
@ „
Conso.Le.WriteLim2 ("Площадь " i shapes [i] - d1S€a() ) г
.i‘onso1e.wr;1;eLine() ; \
Для каждой фигуры (каждого объекта)
llbl3blU£1ClC${ COO'l'MC'lC I ВУКПЦНИ МЕТОД
а red () .
P63} ;I.l2lT BLIFIO.-IHCIIMSI программы:
ЦМСЩРИЧЕЁСКЭЯ (ill/11':/pd Ha3b.Bdt".' ‘.'pI.‘,yi":*J1:;l1£/LK
ЭТФ
плд__„__‚ ~ 48
Эта дметрическая фигура называвшая квадрат
пцсцьмь »— 1C0
3..i _u:-Mc':p14uec1<c\H фигура ыазьтваепттся llpHMOy1'OJl:.hlAK
_”1:1:.-.:.uL. — 40
3-yd ‘.‘\‘C'MC'."pMHeCKa$i фигура называется треугольник
Площщь L 24.5
'32: .<*oMe':'pJ/meCz<a;1 фигура называется базовая
ЭТС МЕТОД переоиределяется В H(3CJI(’3,U}.'IC‘Lii/IX KJIdC’C',EiX.
IIm:’._»:.;'.: е 0
Теперь рассмотрим программу подробнее. Как говорилось ранее, сначала в ютассс
' ’ ре объявляется метод area т) с модификатором v;:tua1.. затем он переоп-
рследтяется в классах Triangle И Fevjtarigie. MCTO11 E1f€a() B классе тыорзваре не
содержит‘ оператороц предназначенпых для вычисления площади геометрической
фигуры. B результате его выполнения просто выводится информация о том, что этот
метод должен быть переопределен в наследующих классах. в которых он действитель-
но юлжен будет производить вычисления. Поэтому при каждом переопределении
метода area () в нем определяется оператор, подходящий для вычисления площади
геометрической фигуры, характеристики которой инкапсулированы данным насле-
дутотцим классом. Таким образом. в случае создания класса Ellipse (эллипс), метод
:1 о должен будет вычислять площадь эллипса.
Обратите внимание. что в методе Ma1,n() объявлен массив shapes, содержащий
Обьсктьт класса ‘га "Спаре, но элементам данного массива присваиваются ссылки
“-1 объекты типа ТгьатттётЁс‘. T-h.«1_r;»:. Эта правильно. поскольку
СС-‚ъючные переменные типа наследуемого класса (которыми в данном случае явля-
Югця элементы массива) могут ссылаться на объекты т-таследутотдего класса. Затем
"Р-тграмма обрабатывает в цикле элементы массива. отображая сведения о каждом
“дъекте. Хотя эта программа ‚достаточно нроста„ она демонстрирует возможности
“ь-следотзаниэт и переопрсделениэт методов. Тип объекта, ссылка на который хранится
'* щылочнои переменной. имеющей тип наследуемого класса, определяется во время
выполнения программы, тогда же вызывается соответствующий метод. Если объект
=' следует класс Ттлчэвзгтард, площадь геометрическом фигуры вычисляется С Помо-
ю метода (ее (› . Интертрейс этой операции одинаков шя всех типов используе-
‘J ‘ll фигуры (обьекта).
Ifang Ё е И Т
324 Глава 8. Наспедо V,‘
Минутный практикум
1. Что такое виртуальный метод‘? Как он переопределяется?
2. Какой принцип Сг: поддерживают виртуальные методы?
3. Какая версия виртуального метода вызывается, если сам метод вызва
помощыо ссылочной переменной типа наследуемого ктасса?
Использование абстрактных классов
Иногда требуется создавать наследуемый класс, в котором определены лишь нек с
рые характеристики методов, такие как тип возвращаемого значения, имя и спи
параметров. Тела этих методов пустые, поскольку трудно предвидеть, какие пере
ные и операторы могут понадобиться в объектах наследующих классов. Позто
наследующем классе программист должен реализовать эти методы — опреде"
переменные и операторы, которые будут выполнять действия, необходимые _
объекта этого класса. То есть в таком классе определяются лишь общие предн"
по себе этот класс не реализует один или несколько подобных методов. Напри‘
это происходит при использовании класса тыовзьаре, рассмотренного в предыд И
разделе. Определение метода area () в классе TwoDShape представляет собой то -
«каркас», и при его выполнении ничего не вычисляется.
При Создании библиотек классов вы, наверное, на собственном опыте убедилиА
том, что часто возникает ситуация, когда в наследуемом классе невозможно пре
В базовом классе метод можно определить лишь частично (например, в кл а,
Twobshape для метода area () выводится предупреждающее сообщение о том, ‘ц
его нужно переопределить, чтобы он действительно возвращал значение плоЩ
фигуры). Но иногда, создав достаточно большую библиотеку классов или рабо
встроенными классами, программист вынужден (чтобы вспомнить нужную инд
мацию либо сэкономить время) просматривать код класса или список членов
В этих списках приводятся лишь подписи методов — имена методов, типы
шаемых значений, списки параметров. Если требуется наследовать этот класс,’
такой информации недостаточно для Понимания того, определен метод полное `
или частично. Поэтому для более эффективного использования наследования
базовом классе можно определить абстрактные методы, которые имеют пустые ‘ "A
При объявлении абстрактного метода используется модификатор abstract, ПОЭТО ‘
встретив в списке членов класса метод с этим модификатором. программист зна
что он обязан реализовать (определить) этот метод в Наследующем классе. Абстра “.
НЫЙ метод автоматически становится виртуальным, так что модификатор virtU_
при объявлении такого метода не нужен. Более того, совместное использован
модификаторов virtual И abstract приведет к возникновению ошибки.
1. Виртуальным называется метод, объявленный в наследуемом классе c помощью ключе ' 5
слова virtual и Переопределенный в наследующем классе. Виртуальный метод переоп r
ДСЛЯВТСЯ с указанием модификатора override.
Виртуальные методы являются одним из способов поддержки полиморфизма в C#.
3. вызываемая версия переопределенного метода определяется исходя из типа объекта,
который ссылается переменная во время вызова.
SQ
- ~.=“-< Рит
325
Использование абстрактных классов
PH объявлении абстрактного метода используется следующая форма синтаксиса:
П
abstract
В этом объявлении отсутствует тело метода. Модификатор abstract может исполь-
зоваться только с обычными методами и не может указываться для методов. имеющих
тиП5'дп;с.
кладь содержащий один или более методов, должен быть объявлен как абстрактный
(то сеть при его объявлении указывается модификатор abstract). Поскольку абст-
актный шасс не определен полностью, объекты этого класса создать невозможно,
И попытка создания объекта абстрактного класса с помощью ключевого слова new
npMBC1lC'I‘ к ошибке компиляции.
type name(parameter—list);
когда класс наследует абстрактный класс, он должен реализовать все абстрактные
методы наследуемого класса. Если этого не происходит, то наследующий класс также
должен быть объявлен с модификатором abstract. Таким образом. атрибут ab-
stract наследуется до тех пор, пока методы, а значит и сам mace, не будут полностью
реал и зо ван ы.
Продолжим модификацию класса TwoDShape И объявим его как абстрактный. B этом
ютаесе отсутствует реализация метода area (), предназначенного для Вычисления
площади неопределенной двухмерной геометрической фигуры. Поэтому в версии
программы, которая рассматривалась в предыдущем разделе, метод area т) класса
TwoD3hape определяется как абстрактный, а сам класс TwoDShape объявляется с
модификатором abstract. Таким образом, наследование всеми классами класса
ТшоЭЗНаре означает, что в них должен быть переопределен метод area ( ) .
H Создание абстрактного класса.
П$1П9 System;
abstract class 'I'woDShape { Класс TwoDshape объявляется с модификатором abstracj
double pri_width; // Объявление закрытых переменных.
аоцсде pri height;
sizing pri‘name;
// Определение конструктора по умолчанию.
publi TwoDShape() {
w;,th = height = 0.0;
name 2 "нуль";
/' Конструктор с параметрами.
pu:l;c TwoDShape(double w, double h,
width = w;
height = h;
пате 2 п;
string n)
д‘ "
КОНСТРУКТОР, предназначенный для создания объектов, у которых свойствам
width И height присваивается одно и то же Значение.
Рьоддс TwODShape(doubie х, string n) {
width — height п х;
Паше = n;
//
326 Глава 8. Наспедо : :i’
// Конструирование объекта на основе объекта.
public TwoDShape(TwoDShape ob) [
width < Ob_width;
height I ob.height;
name — ob.name;
// Определение свойств width, height И name.
public double width {
get { return prinwidth; }
set 1 prifiwidth Й value; }
public double height {
get [ return prifiheight; }
set [ рг1*Ье19пЕ › value; }
public string name [
get [ return pri_name; }
set { priiname w value; ё
I
public void showDim() 1
Соп5о1е.Иг1сеЬ1пе("Значения ширины и высоты геометрической фигуры =
width + " И " + height);
+
// Теперь метод area() будет абстрактным.
public abstract double area(); Объяшшнисаапршпнопвмсища
area () B классе '1‘woDShape.
// B этом классе, наследующем класс TwoDShape, определены характеристики,
// присущие
class Triang-e : TwoDShape [
string style; // Закрытая переменная.
реуголвникам.
// Определение конструктора по умолчанию.
public Triangle() i
style "нуль ,
// Конструктор класса Triangle.
public Triangle(string s, double w, double h) :
oasc(w, h, "triangie") 1
style « s;
// Конструктор, предназначенный для создания объекта, В котором определены
// характеристики, присущие равнобедренным треугольникам.
рщЬ11с Triangle(double х) : base(x, "Tp€yPOflbHMK") [
style = "равнобедренный";
._
ё
Ё
ii.
‚‚
// Конструирование объекта на основе объекта.
public TriangZe(Triang1e ob) : base(ob) I
Использование абстрактных классов 327
дсреопределепие методе а1еа() : классе Trlangie.
P PAAQ Override douD;e a:ea() {<—— Псшюпшщыюнисмспшаагеап uxnmxcTr1angle
;gL4rn width * height / 2;
«_1c void ShOwSEyle() {
'3n5DlC.WCiCCLLn€("BMfl треугольника — " f sLy;e);
„ д_д1 Rectangle, Ld' xuac. Гиоюзйаро.
angle : ТиоЮ$Ьаре 1
_-{op [Й ДЛЯ создания CI’Q'b()E('i'd, B L\"O'i’OpOM определены
Х;1Ь‘‹_1К'1'Ер1/1С'1'ИКИ, присущи/хе IZpHMO)'l‘OJ[;>Hl/IKBM.
irc ReC:angle(double w, double h) :
'pyKTOp, предназыачс
>дне(и, h, "прямоугольиик")[ 1
КОНСТРУКТОР, предназначенный ДЛЯ СОЗДЗНИЯ объекта, В КОТОРОМ ОПРЕДЕЛЕНЫ
xapa1<'1-epMc'rm<M, присущие квадратам.
рьг_;С Rectangle(double х) :
9ц5С(Х‚ "квадрат") [ }
Конструирование объекта на основе объекта.
public Rectangle(Rectangle ob) : bdSe(Ob) { }
.s1ic bool isSquare() {
,t(w;dCh »— height) return true;
return false;
Переопределение метода area() н классе Rectangle.
r.0iic overrlde doubLe area() 5 <---————————~—~
return width * he;ght;
НИС МСТОДИ area
tangle () .
i..<s Absshape {
гчэ11с static vold Main() [
TwoDShape[] shapes — new TwoDShape{4];
shapes[O} = new Тг1ап91е("прямоугольныЙ"‚ 8.0, 12.0);
shapes[1] new Rectangle(lO);
shapes[2] new Rectangle(10, 4);
shapes[3] A new Triangle(7.C);
Lor(1n: 1:0; i < shapes.Leng:h; 1w~) 1
Console.WriteLine("3
Console.Wr1teLine("Hnomaub 4 " + shapes_ij.area());
a фигура называется " ‘ shapes[1].name);
ConsO;e.WriteLine();
328 Глава 8. Наслвдов и
Как видите. все наследувоьцие юваееы должны либо переопределять метод area ( ) , л
быть объяшеннывтьт е модификатором abstract. Чтобы проверить это, попыта ;
создать наследующий класс, не персопределяющий метод area (). B результате 6 ‚
выведено сообщение об ошибке компиляции. Конечно. остается возможность со I . .
ссылочной переменной типа TwoDShape, что и реализовано в программе, но объявле Ё а
объектов, имеющих тип TwoDShape, теперь невозможно. Поэтому в данной не
программы объявляется массив из четырех элементов, а не из пяти, как в предь I
И последнее замечание, в класс Twr:-Dshape по-Прсжнсму включен метод showDim'
который нс модифицирован с помощью ключевого слова abstract. B абстрак '
Kmccc МОГУТ присутствовать полноетыо определенные методы. которые могут
пользоваться наследуюшимът классами без переопределения. В наследуюших класф
должны быть переопределень! только абстрактные методы. "
Мину/тыс? практикум
1. Что такое абстрактный метод? Как он создается?
2. Что такое абстрактный класс?
3. Можно ли создавать объекты абстрактного класса?
.LJ
Использование ключевого слова sealed c
целью предотвращения наследования
Каким бы полезным и мощным ни было наследование, иногда возникает H€O6XOII,M.~
МОСТЬ в создании таких классов, которые наследовать невозможно. Например, если
требуется класс, в котором инкапсулирована последовательность инициализации
некоторых специализированных аппаратных средств, то нежелательно, чтобы nonb-
зователи класса имели возможность изменять способ инициализации монитора,
поскольку в противном случае устройство может быть настроено неправильно.
В C# можно легко предотвратить возможность наследования класса с помощью
ключевого слова sealed, которое указывается в начале объявления класса. Невоз-
можно объявить класс, указав одновременно оба ключевых слова, abstract и sealéd.
поскольку абстрактный класс Является пезаверяцснньвхп и для реализации своих абст-
рактных методов и возможности создания обьектов должен быть наследован. '
Пример класса, объявленного С использованием ключевого слова sealed:
Sealed ClaSS А { Ыэтот класс не может бьггь наслсдусмырд I
//
)
// Это объявление класса недействительно.
class B : A {// ОШИБКА! Невозможно наследовать класс А
//
)
Как следует из комментариев, класс В нс может наследовать класс А, если класс A
объявлен как sealed.
{av
1. АбСТраКТНЫМ НЗЗЬЕВЗСТСЯ МЕТОД 663 тела, при СОЗДЗНИИ КОТОРОГО указывается КЛЮЧСВОС СЛОВО
abstract, ТИП возвращаемого ЗНЗЧСНИЯ, ИМЯ МСТОДЭ И СПИСОК параметров. ‚
2. Абстрактным называется класс, который содержит как минимум один абстрактный метод
3. Нет.
329
Класс object
асс object
В (г: определен специальный класс object, который является базовым для всех
других штассов и для всех других типов (включая обычные типы). Другими словами,
Все шпы наследуют класс object, ТО есть ссылочная переменная типа obj ect MO)KCT
ССЬЫНГЬСЯ на объект любого другого типа. Также эта переменная может ссылаться на
любом массив, поскольку массивы реализованы в Cir как классы. Имя Object ~ это
Краткая форма имени объекта System.Qbject, который является составной частью
6,16 шотеки классов .NET Framcwork.
B KJZICCC object определены СЛСДУЮКЦИС ДОСТУПНЫЕ ДЛЯ КЕОКДОГО ОбЪСКТЗ МСТОДЬП
метод Назначение
pub i ‘LC v1 rtual bool Equals (obj ect Определяет, являются ли вызывающий
object) объект и объект, передаваемый в качестве
параметра object, одинаковыми
рыс11с static bool Equals(object 001, Определяет, являются ли одинаковыми
object ob2) объекты 001 и 0b2, переданные методу в
качестве параметров
рк-зпессео Finalyze Выполняет действия, связанные с завер-
шением выполнения программы, до
«сборки мусора». В С# доступ к методу
Finalize осуществляется с помощью де-
структора
рес11с virtual int GetHashCode () Возвращает хеш-код, ассоциированный с
вызывающим объектом
puzlic Туре сестуре () Получает ТИП объекта во время выполне-
ния программы
ргоеессео object Memberwiseclone () Создает «мелкую копию» объекта, В кото—
рую копируются члены класса, но при этом
не копируются объекты, на которые ссыла-
ются эти члены класса
p.;:31 ic static bool Referenceliquals Определяет, ссылаются ли ссылочные пе-
(object 001, Object ob2) ременные 001ио02наодинитотже объект
puulic virtual string ToString () Возвращает строку, описывающую объект
Описание ттазттаченьяя некоторых из указанных методов нуждается в дополнъттельных
почтснениях. Метод Equals (object) Определяет. ссылается ли ссылочная Перемен-
ная типа вызывающего объекта на тот же самый объект, что и ссылочная переменная,
“Среданная в качестве аргумента (при этом определяется, будут ли совпадать две
Ссктлки). При выполнении метода возвращается значение true, если это один и тот
‚Ка‘ объект, и значение false в противном случае. Этот метод может быть переопре-
it ЮН при создании пользовательских классов е указанием критерисв, по которым
бпст оцениваться равенство объектов. Например, можно создать метод Equals (ob-
ее) ‚ который сравнивает содержимое двух объектов. Метод Equals (object,
г ject) для сравнения объектов, передаваемых в качестве параметров, вызываст
“СТОЛ Equals (object).
Метод еееназлсоое () возвращает хеш-код, ассоциированный с вызывающим объ-
Ci\TOM. ЭТОТ КОД МОЖСТ ИСПОЛЬЗОВЗТЬСЯ ПРОИЗВОЛЬНЫМ алгоритмом, применяющим
Хеширование В КЗЧССТВС СРСЛСТВЗ доступа К хранимым объектам.
330 Глава 8. Hacnenoaapj
3
B I‘.'l'dRC 7 уже говортт:тось. что для перетрувктт (итераторы е обычно приходи“-
переоттрсделягт. ктегольт Нфдгдэт) и 'Чб'ЕН:”'.ъЁсъ*!дН, [поскольку в боцтыттиътст.‘
случаев нужно, чтобы это г оттергггор и хтетоды Lupe. „з (3 (l’_\'|lK|1||0Hl4p0H£lJlM один
ково. В случае переопределения метода Есднаю (т пеобхоличо также персопредели"
метод G€tH£-Z1ShC-C‘-'.1r3 () для достижения их совместимости.
Метод Tr.)String() возвращает строку, содержащую описание объекта. из которо ,‹
он вызывается. При указании объекта в качестве параметра метода W‘:lT.E’Line(."’I
метод Tostrmq (т вьтзывастея автоматически. Во многих классах этот метод Mom."
быть переопределена, благодаря чему будет отображаться ртнфорхтацтпя, относящая
к K11‘zK1l0\1_v конкретному объекту Цш-тиоткт класса. Нгтттрихтер,
U.
‘L
// B программе демонстрируется использование метода 2о$Сх_п
using System;
class Myclass I ' '
sta:;c in; counL 1;
I,‘
int id; ’
public MyClass() {
id = count;
count++;
Переопределение метода
public override string Tostringi) i
return "Объект тина MyClass Ш " 4
class Test {
public static void Main() {
MyClass obl 2 new MyClass();
MyClass ob2 2 new MyClass():
Myciass ob3 new MyClass();
C0nsoie.WriCeLine(obl);
Console.WriteLine(ob2);
Console.WriteLine(%b3);
Er‘
fa
‘т
J.
2
)
Результат выполнения программы:
Объект тина МуС1д35 У
Объект тина MyClaSs M
Объект типа Myclass M
о; г._„ ›—‹
Упаковка и распаковка
Ранее уже говорилось о том, что всс типы C11, включая обычные типы, наследуЮТ
Класс object. Поэтому ссылочная переменная типа object может ссылаться Hag
объект любого другого типа, включая обычные типы. При приеваиваттиът ссылочной Ё
перетиенттой типа object зпгтчеиия обычного типа происходит процесс, называемый _
]1!1(1К()!1’К()й‚ B pC3y.:'lbTElTL‘ НЫПОЛПСНИЯ КОГОРОГО ЗПНЧСННС бУЦСТ ХрННИТЬСЯ R ()61>€KTe `
‚ упаковка и распаковка 331
Tm“, obj ect. To CCTb значение обычного типа будет «упаковано» внутри объекта. Затем
этот объект может Использоваться как любой другой объект. Упаковка выполняется
автоматически, для этого нужно просто присвоить значение обьщного типа ссылочной
переменной типа Object, остальную работу выпо.чни'1' компилятор языка C#.
В процессе распаковки происходит ъввлсчснитс значения пз объекта. Для осуществ-
шипя дгого процесса требуется выпотптепие операции приведения типа. При при-
евппвзкъкпа п обычной переменной значения. извлекаемого из объекпа. перед ссылочной
Переменной тина r‘:':.wjeCt B скобках указывается Tun, К которому должно быть
приведено распаковываемое значение.
Ниже приводится простая программа, в которой выполняется упаковка и распаковка
ЗНЫЧСППЯ.
/‚/ в программе демонстрируется использование упаковки и распаковки.
us;::g System;
3 BoxingDemo {
static Vold Main()
При выполнении этою оператора происходит упаковка
значения переменной X B обгьект.
во] а х: // Упаковка значения переменной х в объект.
int y ~' (ir1t)Obj; // Распаковка (извлечение) значения, хранящегося в
// объекте obj, И присваивание его переменной типа int.
Console .Writ:eLine (y);
При выполнении этого оператора
› происходит распаковка значения.
Прп выполнении этой программы будет вьпъсдено значение 10. Обратите внимание,
что упаковка значения переменной х выполняется в результате присваивания этого
значения ссылочной переменной obj, имеющей гип object. Цслочисленное значение
извлекается из переменной obj при помощи выполнения операции приведения типа.
Ниже приводится еще один пример использования упаковки. В программе значение
титла int передается в качестве аргумента методу sqr () , которому должен переда-
Baum: параметр типа object.
п программе демонстрируется выполнение упаковки при передаче методу
-' Значений в качестве аргументов.
L3-nq System:
-J~.::s Boxingbemo {
гндшйс Static void Main() {
Lnt х;
х = 10;
ConSole.WriCeLine("3HaqeHMe переменной x а " + x);
// Значение переменной х автоматически упаковывается при передаче его
1/ мет°ду Sqr H ' Значение переменной x упаковьпвастся при
х Box1nqDemo . sqr (x) ; ВЫЗОВС метода sqr().
Console.WriteLine("3HaueHme переменной х во второй степени — " +- х):
332 Глава 8. Наспедоа
static int sqr(Object о) {
return (int)O * (int)o;
Ё
В рСЗУЛЬТВТС ВЫПОЛНСНИЯ программы буДУГ ВЫ ВСДСНЫ СЛСДУЮЩИС СТРОКИ:
Значение переменной х ~ 10
Значение переменной х во второй степени 2 100
Значение персмсьтной х автоматически упаковывается при персдачс его методу sqr
Упаковка и распаковка обеспсчивают полную унификацию системы типов. Все 4
наследуются из класса obj ect. Ссылка на любой тип может быть присвоена ссылоч I
переменной. Упаковка/распаковка значений обычных типов выполняется автома
ски. Кроме того, поскольку все типы наследуют класс obj ect, OHVI ИМСЮТДОСТУП к M
класса obj ect. B КНЧССТВС примера приведем несколько неожиданную программу.
// обычно вызывают методы экземпляра!
using System;
‚.
е»
// При ИСПСЪПЬЗОВЗНИИ УПЗКОВКИ МОЖНО ВЫЗЫВЗТЬ МЕТОДЫ ЗНЗЧЭНИЯ ТЕК же, как
Ё
I
‘f
Такой оператор является
Class MethOnValue { действительным в C1?!
publzc static vo1d Ma1n() {
Console .WriteLine (10 . ToStr1ng() ) ;
2*
£2
а
T.-
vi
)
B результате выполнения программы отображается строка, состоящая из двух
волов 10. Это происходит в результате того, что метод ToString() B03Bpama7l§
строковое Представление объекта, из которого он вызывается. В данном случае
строковым представлением значения 10, принадлежащего к типу int, будет стро
состоящая из двух символов I0. —
«:9 Минутами лрктакум
х 1. Если mace объявлен с модиоикато ом sealed МОЖЕТ ли он быть наследуй-
5 _‚
3 мым‘? i
K
2. Что представляет собой mac object?
3. Как предотвратить возможность наследования класса?
*__j
1. HCT.
2. Класс object — это базовый класс для всех типов, определенных в Си.
3- ДЛЯ Предотвращения возможности наследования класс должен быть объявлен с модификг‘
тором sealed.
KO
„им. ...‚...-„..=..„..‚. ‹...„.„.-м„„.„‚.мш‚.„„‚„‚„‚....‚ым..‚‹..‚‚‚..‚.‚. . .‚..„‚.„ W
10.
11.
12
‚пропьныевопросы
Кон/загадит амида:
Имеет ли наследуемый класс доступ к члснам наследующего класса?
Имеет ли наследующий класс доступ к ‘шенам наследуемого класса?
Создайте наследующий mace класса TwoDS‘nape И назовите его Сцс1е.
Включите в его определение метод area (). Предназначеннь!й для вы-
числения площади окружности, а также конструктор, использующий
шючевое слово base для инициализации той части объекта, которая
была наследована от шасса TwoDShape.
Каким образом можно предотвратить доступ членов наследующего
класса к членам наследуемого класса?
УКЗЖИТС НЗЗНЗЧСНИС КЛЮЧСВОГО СЛОВЗ base.
Для следующей иерархии наследования укажите порядок, в котором
вызываются конструкторы шассов при создании объекта класса Gamma.
class Alpha {
class Beta :
class Gamma :
Alpha (
Beta { ...
ССЫЛКЗ ННСЛСДУСМОГО КЛЗССВ МОЖСТ УКЗЗЫВЗТЬ H3 ОбЪСКТ НЭСЛСДУЮЩСГО
КЛЗССЗ. ОбЪЯСНИТС‚ ПОЧСМУ ЭТО ИМССТ ЗНЗЧСНИС при переопределении
MCTOII3.
Что такое абстрактный класс?
Каким образом можно предотвратить возможность наследования класса‘?
Объясните, каким образом при использовании наследования, псреоп-
ределения методов и абстрактных классов поддерживается полимор-
физм.
Какой класс является базовым для всех остальных шассов?
Объясните суть процесса упаковки.
Каким образом осуществляется доступ к членам класса, объявленным с
модификатором protected?
333
v...v.q:.m«.-a-v:«::>.. ..
:.. :'<V.Ng.'IIN\./4'»...
. хм: кР-гмч‘
Интерфейсы,
структуры и
перечисления
С] Использование интерфейсов
С! Применение ссылок на интерфейсы
С! Добавление в интерфейсы свойств и
индексаторов
С! Наследование интерфейсов
С! Использование явных реализаций
С] Исследован ие структуры
С] Применение перечислений
фейсы 335
‘W416?
оящей главе рассматривается один из наиболее важных Элементов C13‘: uHme —
еда Этот ЭЛСМСНТ огтредсляет набор методов. которые могут реализовыватьсвт с
помощью класса. Конечно, интерфсттс сам по себе нс может реализовывать методы.
он ‚Шдястсгт чисто ‚тогичсскои конструкниеи, которая описьтвгтст набор гтоддержи-
Васмьп Ьстаесом методов, не диктуя при ‘этом способ реализации.
В д-‚тгтвс также обсуждаются два Новых типа ДНННЫХ, Поддсрживаемых в C«*¥: структуры
I И „дрсчтътсдтеътизт. Структуры нагтоминают классы, отличаясь от‘ последних тем, что
t моГУТ Обрабатывать типы значений, а не ссылочные типы. Перечисления представ—
z „тот собой перечни ииенованных Цкъточислеътньтх констант‘. Благодаря этим новым
Тиши данных значительно расширяются возможности среды программирования C#_
B Hat“!
уж‘
Ё ‘А I интерфейсы
в объектно-ориентированном программировании иногда возникает необходимость
Определения действий, выполняемых гстассом. без указания способа выполнения этих
‚у действий. Ранее уже рассматривался подобный пример: абстрактный метод. Абст-
рактны и тип метода определяет сигнатуру метода, но пе поддерживает реализацию.
-_, B этом случае наследующий класс должен поддерживать свою собственную реализ-
r ацию для каждого абстрактного метода, определенного его наследуемым классом.
Таким образом, абстрактный метод определяет интерфейс метода, но не его реализа-
; цию. Исходя из полезности абстрактных классов и методов, имеет смысл расширить
‘ рассматриваемую концепцию. В Cit можно полностью отделить интерфейс класса от
реализации этого класса. В этом случае используется ключевое слово interface.
* Синтаксис интерфейсов подобен синтаксису абстрактных классов, Однако в случае
g интерфейсов методы нс могут включать тело. Таким образом, интерфейсы ни при
каких условиях не поддерживают реализацию. Этот объект определяет, что нужно
Делать. но не демонстрирует‘, как именно это нужно делать. Сразу же После того, как
интерфейс огтределсн, он может рештизовьттзаться произвольным количеством классов.
Один mace, B свою очередь, может реализовывать любое число интерфейсов.
. Для вьшолнеттття реализации интерфейса класс должен поддерживать тела (реализа-
E ' 11”“) для методов, описываемых с помощью данного интерфейса. Каждый mace
Может свободно определять детали своей реализации. В этом случае два класса могут
реализовывать один и тот же интерфейс различными способами. но каждый отдель-
Ньтй класс по-прежнему поддерживает один и тот же набор методов. Таким образом,
5; Кода Который «знает» о наличии интерфейса, может использовать объекты любого
‘Пасса в случае. если интерфейс для этих объектов будет одинаковым. Путем
" "Одёшржки интерфейса С# позволяет полностью реализовать аспект полуптортфятзъта,
Имщчтхттяй «один интерфейс, несколько методов».
Ооъявясттие интерфейсов вьтполнястся с помощью ктточевого слова interface.
"ЖС приводится упрощенная форма объявления интерфейса:
intgridce name {
rei‘-type method—name1 (param—list) ;
591 ‘Суре meI:hod~name2 (param—list) ;
Inf
\ ‘type method—nameN (param~list) ;
336 Глава 9. Интерфейсы, структуры и перечи
Имя интерфейса указывается с помощью параметра name. Объявление мето`
реализуется с помощью указания их сигнатур и типа возврата. Фактически в дан ,_
случае речь идет об абстрактных методах. Ранее уже упоминалось о том, чю”
интерфейсе, определенном с помощью ключевого слова interface, методы не Mo п
иметь реализацию. Следовательно, каждый класс, включающий interface, до
реализовывать все эти методы. В интерфейсе для методов нея вным образом зад ›
тип public. B ЭТОМ случае также не допускается явный спецификатор доступа. \
Ниже приводится пример объекта interface. ОН определяет интерфейс для кла
который генерирует наборы чисел.
public interface Series {
int ge:Next(); // Возврат следующего числа в наборе. › Ё
void reset(); // Перезагрузка. g 1
void setStart(int x); // Установка начального значения. f
}
При объявлении этого интерфейса используется модификатор public, поэтому . ‘N
может реализовываться с помощью любого класса в любой программе.
„мм. . .
.. . яиц‘ ,«_-
„а, м ..
B дополнение к объявлению сигнатур методов интерфейсы могут объявлять си г:
туры свойств, индексаторов и событий. События будут описываться в главе 10,-’
сейчас вкратце рассмотрим методы, свойства и индексаторы. Интерфейсы не мо ‘I
включать данные-члены. Они также не могуг определять конструкторы, деструкто
или операторные методы. Кроме того, никакой из членов не может определяться
виде static.
Реализация интерфейсов
Как только interface был определен, он может быть реализован одним
несколькими классами. Для реализации интерфейса после имени класса необходи ’
указать название интерфейса (почти так же, как и в случае с определением наследует
Moro класса). Ниже приведена общая форма синтаксиса кода класса, реализующе
интерфейс: `
class class—name: interface—name {
// тело класса
Имя реализуемого интерфейса указывается с помощью параметра interface-name
Если класс реализует интерфейс, он обязан реализовать его в целом. Например.’
невозможно указать и реализовать только часть интерфейса.
Классы могут реализовывать более одного интерфейса. В случае подобной реализа A
интерфейсы должны разделяться запятыми. Класс может наследовать наследуемый
класс, а также реализовывать один или больше интерфейсов. В этом случае в списке,‘
разделенном запятыми, имя наследуемого класса должно быть первым.
Методы, реализующие интерфейс, следует объявлять как public. Это необходимЧ Ё
делать по той причине, что методы неявным образом общедоступны внутри интер‘ - Ц
фейса, поэтому их реализации также Должны быть общедоступны. При этом ТИП г
сигнатуры реализуемого метода должен точно соответствовать типу сигнатуры, ука-
занному в определении interface.
ИнтерфЭЙСЫ 31
Ниже приводится пример, в котором реализуется (с помощью класса ByTwc
Показанный ранее интерфейс Series. Этот класс позволяет генерировать наб
чисед причем каждое из последующих чисел на 2 больше, чем Предыдущее,
// реализация класса Series.
«ass ByTwOs : Series { Реализация интерфейса Series.
С-
in: start;
int val;
public ByTwos() {
start = O;
val = 0:
puoLic int getNext() {
val += 2;
return val;
I
public void reset() {
start = O;
val = 0:
Ъ
public void setStart(int x) {
start = x;
val = x;
}
Нетрудно заметить, что интерфейс ByTwos реализует все три метода, определенн
с помощью интерфейса Series. Это пришлось сделать потому, что класс не MO)!
выполнить частичную реализацию интерфейса.
Ниже приводится код, демонстрирующий возможности класса ByTwos2
// демонстрация возможностей класса ByTwos.
us;ng System;
Class SeriesDemo {
public static void Main() {
ByTwos ob = new ByTwos();
for(int i=0; i < 5; i++)
Console.WriteLine("Cnenywmee значение " +
ob.getNext());
Console.WriteLine("\nBoccTaHoBneHme");
ob.reset();
for(1nt i=0; i < 5; i++)
Console.WriteLine("Cnenymmee значение
оЬ.9есЫехс());
Сопзо1е.Иг1сеЬ1пе("\пНачало со 100");
ob.setStart(lOO);
for(int i=0; i < 5; i++)
Console.WriteLine("Cnenywmee значение " +
ob.getNext());
в лава з. Интерфейсы, структуры и перечислен‘
С целью компиляции класса SeriesDemo в процесс компиляции потребуется вкдю;
читьклассь|$ег1ез‚ВуТшо3!45ег1езОето.В;хшулЬтатскомПИлят0равТ0матически`
скомпилирует все три файла и создаст конечный исполняемый файл. Например, если;
ДанньючайльхименукпсяЗег1ез.с5‚ВуТиоз.сз ИЗег1е5Юето.сз‚длязапусканы
выполнение процесса компиляции используется следующая командная строка:
>csc Series.Cs ByTwos.cs Ser1esDemo.cs
Если используется визуальная среда разработки Visual С ++ IDE, тогда просто;
добавьте угаомянутьте три файла в проект Си. Помсищсние всех трех файлов в один и
тот же файл вполне себя оправдывает.
г
PCJ)/J bT£1T ВЫ ПОЛ HCH ИЯ програм М Ы:
Следующее значение
Следующее значение
Следующее значение
Следующее значение
f-‘(Dd\ubf\J
Следующее значение
Восстановление
Следующее ёначение
Следующее значение
Следующее значение
Следующее значение
Следующее значение
т-АСОШ-ЬЮ
С)
«
Начало со 100 L
Следующее значение 102
Следующее значение 104
Следующее значение 106 ?д
Следующее значение 108
Следующее значение 110
Ютассы. реализующие интерфейсы, могут определять дополнительные члены с целью ..
их Применения в своих собственных целях. Например, в следующей версии интер—
фсйса вуТшоз добавляется метод getPre\/ious п, возвращающий предыдущее зна- `
чсние:
// Реализация интерфейса $ег1е5 и добавление метода getPrevious().
class ByTwos : Series {
int start;
int val;
int prev;
public ByTwos() {
start е O;
val : 0;
prev -2;
public int getNext() £
prev = val;
val += 2;
return val:
интерфейсы
г
nubllC void reseC() i
staxt — O;
val т O;
pzev — ~2;
void sctStart(inL X) 1
X,‘
MG'i’O,I.1,
33
не указанный с ЦОМОЩЫЮ Series.
'.'. getPrevi0us П { <:{,L1o6a1:.v|cHnc метода, нс отцрщтеленнопо с кюмоицью интерфейса Serles.
f:,‘C_‘LlIU PFCV,‘
C:aSS ByThrees : Series {
public ByThrees() {
start I О;
val = O;
qnlic int geCNext() {
3:
return val:
val Р:
ьыЬ11с voio rese:()
влаги I C;
val = О;
:1о11с void setS:drt(1nt x) {
srart L X;
val = х;
Реализация интерфейса 5ег1ез.
Обра [иге внимание на то, что добавление метода getP:’ev1-:\1:s () потребует измено-
нпя реализаций методов. определенных с помощью интерфейса Эешез. Однако,
поскольку интерфейс для этих методов остается неизменным, изменения будут
псшиетны и нс отразятся па предплествуъошегхт коде. Это и является одним из
npcn\1_vu1ccTB, обеспечиваемых интерфейсами.
Как объяснялось ранее, любое количество классов может быть реализовано по-
сттсгкством ключевого слова ЫтсесЕасе. Например, ниже приводится кол класса
1. тгеез, который генерирует наборы чисел, кратных трсм:
Реализация интерфейса Semes другим способомЛ
340 Глава 9. Интерфейсы, структуры и перечисле
т Манутныд практикум
1. Каково общее назначение интерфейса?
2. Какие элементы могут быть членами интерфейса?
3. Каким образом реализуются интерфейсы с помощью класса?
Использование интерфейсных ссылок
Вы, наверное, удивитесь, когда узнаете о том, что можно объявлять переменну ‚д
ссылки, имеющую интерфейсный тип. Другими словами, можно создавать инте V-
фейсную переменную ссылки. Подобная переменная может ссылаться на люб
объект, который реализует ее интерфейс. При вызове метода объекта с помощь „у
интерфейсной ссылки вызывается версия метода, реализуемого данным объекте
Этот процесс напоминает использование ссылки наследуемого класса для доступа
объекту наследуюшего класса, как описано в главе 8.
В следующем примере иллюстрируется применение интерфейсной ссылки. Здес:
задействована одна и та же интерфейсная переменная ссылки для вызова объектов ‘M.
интерфейсах ByTwos И ByThrees.
// H§MoHcTpauMH возможностей интерфейсных ссылок.
using System;
// Определение интерфейса
public interface Series {
int getNext(); // Возврат следующего числа из набора.
void reset(); // Перезапуск.
void setStart(int x); // Установка начального \начения.
// Реализация интерфейса Series ОДНИМ способом.
class ByTwos : Series {
int start;
int val;
public ByTwos() (
start = O;
val = О;
public int getNext() (
val += 2;
return val;
}
public void reset() (
start = O;
val = 0;
1. Интерфейс определяет то, что должен выполнять класс, но не указывает конкретный
алгоритм действий.
2. Членами интерфейса могут быть методы, свойства, индексаторы и события.
3- ДЛя реализации интерфейса после имени класса укажите ключевое слово interface, затем
выполните реализацию для каждого члена интерфейса.
использование интерфейсных ссылок 341
nubllc void setStart(int x) (
start = x:
cal = X7
Ъ
1
/, .ралИЗаЦИЯ интерфейса Series другим способом.
‚ _
Class ByThrees : Series {
lnfi start;
дн: val;
puolic ByThrees() {
public int getNext() (
val т= 3;
return val;
public void reset() {
start = O;
val = 0;
public void setStart(int x) {
szart = x;
val = X;
class Ser1esDemo2 {
public static void Main() {
ByTwos twoob 2 new ByTwos();
ByThrees threeob = new ByThrees();
Series ob;
for(int i>O; i < 5; i++) {
ob — twoob;
Console.WriteLine("Cnenymmee значение ByTwos " +
ob.getNext());
ob = threeob;
Console.WriteLine("Cnenymmee значение ByThrees " +
ob.getNext());
B чстоде Main побъект ob объявлен в качестве ссылки на интерфейс Series. Это
Означает, что он может использоваться с целью хранения ссылок на любой объект,
рсгщизугощий интерфейс Series. B данном случае он используется для ссылки на
Объекты twoob И Threeob, которые являются объектами типа ByTwos И ByThrees
C00TBeTcTBcHHo, И оба они реализуют интерфейс Series. Интерфейс ссылочной
“Сременной знает лишь о методах, объявленных в его разделе и указанных его
Кточевьхм словом interface. B результате объект ob не может использоваться при
дЮСТупе клюбым Другим переменным или методам, которые поддерживаются объектом.
342 Глава 9. Интерфейсы, структуры и перечислений
т .
Проект 9.1. Создание интерфейса Queue
Щ lcharQ.cs, |QDemo.cs
Для того чтобы увидеть интерфейсы в действии, обратимся K практическому примеру_
В предыдущих главах был разработан mace Queue, с помощью которого была
реализована одиночная очередь для символов фиксированного размера. Однако
реализовать очередь можно самыми различными способами. Например, Очередь
может иметь фиксированный размер, либо увеличиваться Очередь может быть
Линейной (в этом случае Элементы находятся в очереди постоянно). либо круговой
(в этом случае Элементы могут бьтть помещены в очередь по мере удаления из нее
старых 3f[CMCHTOB), ОЧСрЁЛЬ ТаКЖС МОЖЕТ ПОМСШНТЬСЯ В МНССИВС, B CBSl3'<lHH()M СПИСКЕ,
в двоичном дереве и других подобных объектах. Каков бы ни был способ реализации
очереди, ее интерфейс остается одним и тем же, а методы put () и get () определяют
интерфейс независимо от деталей реализации. Поскольку интерфейс очереди отделен
от ее реализации, Довольно проето можно задать его, оставив на долю каждой ‚
реализации определение той или иной специфической чертьт.
В ходе осуществления данного проекта мы создадим интерфейс очереди символов, а '
также три его реализации. Во всех трех реализациях для хранения символов исполь` -
зуются массивы. Одна очередь будет линейной с фиксированным размером. В каче»
стве второй очереди выступает круговая очередь (в этом случае по достижении конца 7
базового массива методы get И ее: автоматически возвращаются к началу цикла).
Таким образом, в круговой очереди может‘ сохраняться любое количество элементов
до тех пор, пока возможно удаление из очереди ранее введенных в нее элементов.
В последней реализации создаетея динамическая очередь, которая в случае превы-
шения изначального размера может автоматически увеличиваться.
Пошаговая инструкция
1. Создайте mace ICharQ.Cs и поместите его описание в следующее определение
интерфейса:
// Интерфейс очереди символов.
public interface ICharQ I
// Помещение символа в очередь.
VOld put(char ch);
// Выборка символа из очереди.
char get();
}
Как видите, данный интерфейс являстся достаточно простым и включает лишь
два метода. Каждый класс, реализующий Icha rQ, нуждается в реализации упомя-
нутых двух методов.
[О
Создайте файл тооетс .
3. Начните создавать проект 1 цвета . воспользовавшись указанным ниже кодом
KJaCG1FixedQueue2
/ 1‹
Проект 9.1
Демонстрация возможностей интерфейса ICharQ.
*/
вы... _._. I: ._..
использование интерфейсных ссылок 343
г
usinq System;
Класс очереди фиксированного размера, предназначенный для выполнения
операции с символами.
glass 5;xcdQueue 2 ;CharQ {
ghgr: q; ’; Массив, предназначенный для хранения очереди
дна puLloc, gctloci // Индикаторы put И get
’ Конструирование пустой очереди, имеющей заданный размер.
puolic F1xedQueue(;nL 2е) ;
q 2 new char[s::e~l]; д! Выделение памяти для очереди.
pu:loc — qe:loc — C;
5
Помещение символа в очередь.
gub;ic vold pu:(char ch) (
if(putloc—=q.Lcngth-1) i
C0ns0le.WriteLine(" Очередь 3аполнена.");
return;
pUElUCT+;
q[putloc] = ch;
f/ Получение символа из очереди.
рыЬ11с char ge:() {
if(getloc —= pucLoc) {
Conso;c.WriteLine(" п Очередь пуста.");
return (char) O;
getlocr+;
return q[getloc};
.l21H1-um реализация интерфейса I-'§:harr_: является адаптиротъаъкъсои версией класса
зев рассматриваемого в глгпъс 5. и П0Э'Г0Ё\1у ДОЛЖНЫ быть вам знакомгъ
:еые.ЭТ0Т
ч B файл прет: . ;:s ‚чобавьтс приведенный ниже кол выноса C;,:'.:L1l ,—.r=
mace реализует круговую очередь ДЛЯ символокъ.
’/ Круговая очередь.
glass CircularQueue 2 IChdrQ {
Char[] q; // Очередь содержится 5 ЭТОМ массиве.
int putloc, getloc; // Индикаторы put И get.
// Пустая очередь заданного размера.
pub;;c C;rcula:Queue(;nL 5;;е) 1
;ze+l}; {Ё выделение памяти для очереди.
С:
q = new char’
putioc get;
// Помещение символа в очереди.
puniic void pu:(uhar ch) {
/* Очередь заполнена, либо если putioc меньше чем
getloc, либо если putloc представляет конец массива,
а getloc представляет начало массива. */
. пана н. интерфейсы, структуры и
д
if(putloc+l:=getloC I
((put10c;:q.Length—1) & (geLloc«wO))) ( 1
Console.wr1teLine(" - Очередь заполнена."):
return;
putloc++; ц
1Е(рип1ос==ч.Ьеп9сЬ) putloc 4 O; // Обратный цикл. *
q[putloc] = ch:
)
‘ е
// Выборка символа из очереди. 3 g
public char get() ( 5
if(getloc := putloc) ( . Ё
Console.WriteLine(" е Очередь пуста."); 5 5
return (char) O;
getloc++; " I,
if(getloc==q.Length) getloc F O: // Обратный цикл. ;‚‘
return q[getloc]i ’ '
При использовании круговой очереди может повторно задействоваться простран; . ё
ство массива, освободившееся после выборки элементов. В результате очере.
подобного типа может последовательно включать неограниченное количестве Т
элементов, если происходит удаление из нее ранее помещенных элементовд
Концептуально все достаточно просто —~ нужно просто переустановить значение?
соответствующего индекса в ноль после достижения конца массива ~ HO при это
на первых порах остаются трудности с определением граничных условий, В про
цессе использования круговой очереди момент ее заполнения определяется н; 7 д
тогда, когда достигается конец базового массива, а в том случае, если прйд I
сохранении элемента происходит переписывание невыбранного элемента. Пр}? _
этом метод put () должен проверить несколько условий с целью определения тогод“;
является ли очередь заполненной. Как и предполагается в комментариях, очередь?
считается заполненной, cum putloc будет меньшим getloc либо если putloc; Ё
находится в конце массива, а getloc ~ в начале массива. Таким образом, очередь; `
будет пустой. если putloc И getloc являются эквивалентными. “ if
5. И наконец, в файл IQDemo . cs ПОМССТИТС указанный ниже код класса DynQueue._7'_':
При этом реализуется возрастающая очередь, размер которой увеличивается при; ;
необходи мости. Здд
// Динамическая очередь.
class DynQueue : ICharQ {
char[1 q; // В этом массиве хранится очередь
int putloc, getloc; // the pun and get indices
// Конструирование пустой очереди заданного размера.
public DynQueue(int size) (
q = new char[siZe+1]: // Выделение памяти для очереди.
putloc = getloc = О;
// Помещение символа в очередь, ё
public void put(char ch) (
---——
использование интерфейсных ссылок 345
if(putloc==q.Length—l) {
// Увеличение размера очереди.
char[] t = new char[q.Length * 2];
// Копирование элементов в новую очередь.
for(int i=0; i < q.Length: i++)
t[l] = ЧМ];
q—t:
putloc++;
q[puCloC] = chi
1/ Выборка символа из очереди.
public char get() {
if(getloc == putloc) {
Console.WriteLine(" - Очередь пуста.");
return (char) O;
„а
getloc++;
return q[getloc];
Ё
При использовании этой реализации очереди в случае ее заполнения и попытки
сохранения очередного элемента происходит создание нового базового массива,
размер которого в два раза больше исходного. Содержимое текущей очереди
копируется в массив, а затем ссылка на новый массив присваивается переменной q.
6, Для демонстрации возможностей трех реализаций интерфейса ICharQ B файл
Ь3Юето.с$ ВВСДИТС КОД СЛСДУКЛЦСГО класса.1Чри ЭТОМ ДЛЯ ДОСТУПЕ КО BCCM трем
очередям используется ссылка интерфейса шсьаго.
Х! Демонстрация возможностей очередей.
:Lass IQDemo {
public static void Main() {
FixedQueue ql 2 new FixedQueue(10);
DynQueue q2 = new DynQueue(5);
CircularQueue q3 = new CircularQueue(lO);
ICharQ iQ;
char ch;
int i;
iQ = ql:
// Помещение нескольких символов в фиксированную очередь.
for(i=O: i < 10; i++)
iQ.put((char) ('A' + i));
// Отображение очереди.
Соп3о1е.Иг1се("Содержимое фиксированной очереди: ");
for(i=O; i < 10: i++) {
ch = iQ.get();
Console.write(ch);
346 Глава 9. Интерфейсы, структуры и перечисл
}
Conso1c.Wr;::Linc();
"Q — <22:
.' ’ ПОМСЩСНИЭ HCKL".':)pb'L.‘( СИМВОЛОВ В динамическую очередь.
for(i—C; i < -C; l+*)
;Q.put((char) ('2' — ;));
// Отображение очереди.
Сопзо1е.Шг1Ее("Содержимое динамической очереди: ");
for(;—U; i u 16; ;r+)
Ch — :Q.qet();
COnsOie.Wr;te(Ch);
ё
Cons0;e.Wr;:eLine(); $3
Ш — ЧЗ; ”
// Помещение некоторых символов В круговую очередь.
for(i—U; 1 < 10; 1т+)
iQ.put((Char) ('A' т 1));
// Отображение счереди.
Соп5о1е.Иг1:е("Содержимое круговой очереди: "):
for(;—C; 1 < 10; 1+v) {
Ch — 1Q-gct(); "
C0nsole.Wr;te(Ch); W
Console.WriteL;ne();
// Помещение дополнительных символов в круговую очередь.
for(1a1O; 1 < 20; 1ь+)
iQ-put((char) ('I\' т- i));
// Отображение очереди.
Соп5о;е.И:;:е("Содержимое круговой очереди: ");
Ёог(;—С; 1 х ДП; ;о+) {
ch — 1Q.qet();
COflSOlC.WI;Ct(Ch);
Consoie.Wr;:eL1ne("\nCoxpaHcHme И использование" т
" круговой очереди.");
Х; Сохранение и использование круговой очереди.
tcr(;+C; ; < 20; i++) {
;Q.put((char) ('A' т i));
ch — ;Q.ge:():
ConsJLe.Wri;e(Ch);
}
7. CK0Mrm.m«Ipyi'1Te программу задав включение в нес файлов шпаге.
mo . С- .
7
›_ 347
свойства интерфейса
Z“
д Ниже приводится результат‘ выполнения программы:
Додержимое фиксированной очереди: ABCDEFGHIJ
gjgepmmmoe динамической очереди: ZYXWVUTSRQ
‘одержимое круговой очереди: ABCDEFGHIJ
Додержимое круговой очереди: KLMNOPQRST
хранение и использование круговой очереди.
"ЕСЭЕЕ6Н1дКЬММО1РОНЗТ
9. \ ССЙЧЦС бУДУГ продемонстрированьт некоторые ВСЕЦИ, КОТОрЫС ВЫ МОЖСТС ВЫПОЛ-
пять самостоятельно. B интерфейс {Снега} добавьте метод reset ( ), с помощью
шгорого псреустанатхлътвается очередь. Создайте метод зпаыс, Посредством
ЬОТОрОГО КОПИруСЧСЯ СОДСрЖИМОС очереди ОДНОГО ТИП?! В ОЧСрСДЬ другого ТИПН.
рвойства интерфейса
Как и в случае c методами. при определении свойств в интерфейсе не указывается
тело, Ниже приводится общая форма спецификации свойства:
H Тнойство интерфейса
I
ь
Конечно, get И set представляют свойства только для чтения и свойства для Записи,
соответственно.
Ниже приводится продукт переработки интерфейса Series, а также код класса
B‘_.‘T:. )5. КОТОрЫЙ ИСПОЛЬЗуСТ СВОЙСТВО ДЛЯ ПОЛУЧЕНИЯ И ЗВДИНИЯ СЛСДУЮЦЮГО ЭЛЁМСНТЙ
набора:
// Рзпользование свойства в интерфейсе .
U5ifi1 System:
PubL;c interface Series {
Свойство интерфейса
ii“-. next {% {Объявление свойства в интерфейсе Зехлщ
get; // Возврат следующего числа в наборе.
set; // Установка следующего числа.
лизация интерфейса Series.
ByTwos : Series {
>2; val;
F4DliC ByTwos() {
Val O;
т Получение или установка значения.
3Цо11с шт: next {
Чес I
val +— 2;
return val;
„т, Глава 9. Интерфейсы, структуры и перечисп ‘
// Демонстрация свойства интерфейса.
class SeriesDemo3 (
public static void Main() {
ByTwos ob = new ByTwos();
// Доступ к набору с помощью интерфейса.
for(int i#O: i < 5; i++)
Соп5о1е.Иг1пеЬ1пе("Следующее значение " + ob.next);
COnSOle.WriteLine("\nHaqano с 21");
ob.next е 21;
for(int 1:0; i < 5; i++)
Console.writeLine("Cnenywmee значение " + ob.next);
)
Результат выполнения программы:
Следующее значение 2
Следующее значение 4
Следующее Значение 6
Следующее Значение 8
Следующее Значение 10
Начало с 21
Следующее значение 23
Следующее значение 25
Следующее значение 27
Следующее значение 29
Следующее значение 31
Интерфейсные индексаторы
Обратите внимание на общую форму индексатора, объявленного в интерфейсе:
// Инцексатор интерфейса
element~type this[int index] (
get:
Set;
Как и ранее, лишь get И set представляют собой индексаторы, предназначенные?
только для чтения и только для записи, соответственно.
Ниже приводится еще одна версия интерфейса Series; B ней добавляется индекса‘
тор, предназначенный только для чтения и возвращающий й-и элемент набора.
// Добавление индексатора в интерфейс.
using system;
public interface Series {
// Свойство интерфейса
инюрфейсные индексаторы 349
in; next (
get; // Возврат следующего числа в наборе.
set; // Установка Следующего числа.
/: индексатор интерфейса.
дг‚„ Eh 1 S [ int index] { 4————.rYxa3aHMc индексатора, предназначенного только для чтения.
get; // возврат указанного числа в наборе.
)
// реализация интерфейса Series.
Class ByTwos : Series {
in; val;
public ByTwos() {
val — O;
/; Чтение либо задание свойства.
public int next {
get {
val +~ 2;
return val;
// Получение свойства с помощью индекса.
public int this[int index] { Ръшпшацияиншжсшора
I
Чей x
val = О;
for(int i=0; 1 < index; i++)
val += 2:
return val;
// Цемонстрация индексатора интерфейса.
Class SeriesDemo4 (
Public static void Main() {
8yTwos ob = new ByTwos();
// Доступ к набору с помощью свойства
for(int i=0; i < 5; i++)
Console.WriteLine("Cnenymmee Значение " + ob.next);
Console.WriteLine("\nHauano C 21");
ob.next = 21;
for(int i=0; i < 5; i++)
Console.WriteLine("Cnenymmee значение " +
ob.next);
350 Глава 9. Интерфейсы, структуры и перечиспе
Соп5о1е.Ипщ1еЬ1пе("\пПереуСтановКа 5 O");
\
ob.ncxL U;
// Доступ к набору c LOMomsw индексатора
for(inL LLC: i < 3; ;+Ц
Соп5о1е.Ип1:оЬ1пе("Следующее значение " v oD,L5);
PCB)’.-'lbT2\T ВЫ ПОЛ НС I I I151 программы:
Следующее значение
J‘) I‘-J
'_.Jh"ll§'F.\lLL€€ 3 ЬЭЧОНИС
ладующсе значение
Следующее значение
Следующее значение
Н ткг
о
Начало с 21
Следующее значение 23
Следующее значение 25
Следующее значение 27
Следующее значение 29
Следующее значение 31
Переустдновка в О
Следующее значение
Следующее значение
Следующее значение
Следующее значение
Следующее значение
{'XJO\>JbfJC)
Mufiymflezé драхм/кум
1. Может ли переменная ссылки интерфсгяса указывать на объект, который
регшизует этот интсрфсис?
2. Может ли свойство интерфейса быть npc;lHa3Hz1'IcHHbIu ТОЛЬКО для чтения?
3. Имеют ли тела индсксатор и свойства, определенные внутрь: интерфейса?
Наследование интерфейсов
Возможно наследование интерфейсов. В этом случае используется синтаксис, весьма
напоминатопций синтаксис наследования классов. Если класс реализует интерфейс,
который наслепуст другой интерфейс. должна обеспечиваться рсгшизация для всех
членов. огпрсдслснньпх в составе пспн HzIC_'lc,'10n;IHml интерфснсов. Обраппс впичание
на следупопщпи пример:
// Один интерфейс может наслсдоватэ другой интерфсйз.
using System;
publ;c interface A {
E+’.~.—
:12):
ФР?’
"Я
Наследование интерфейсов 351
„„‚@ meth1();
„ода meth2()i
I интерфейс В включает методы methl() И meth2() Щ он добавляет meth3().
„Ш дс lnterface B : А (
r »3-d mCth3();
jTOT Класс должен реализовывать все методы А и В
dis Myclass : В {
.n;1c vold methl() I
Цоп5о1е.Иг1сеЬ1пе("Реализация meth1().");
JbliC void meth2() {
Console.WriteLine("Peanmaaumx meth2().");
qblic Vold meth3() {
Console.WriteLine("Peanmsaumn meth3().");
dfi5S IFExtend i
rublic static vold Main() {
MyClass ob ~ new MyClass();
ob.meth1():
ob.meth2();
ob.meth3();
§f?§§§fi§§§; gryggfiggcggggggygg .....
J Bonpoc. Если один интерфейс наследует другой интерфейс, можно ли объя-
шггь член наследующего интерфейса скрывающего член, определенный базовым
интерфейсом?
Ответ. Да. Если сигнатура члена наследующего интерфейса совпадает с
Сцгнатурой одного из наследуемых интерфейсов, имя наследуемого интерфейса будет
сьрьтто. Как и в случае е наследованием класса, подобное сокрытие приведет K
Щображению предупреждающего сообщения независимо от того, будет ли указан
Ч ген наследутощсго интерфейса с помощью ключевого слова new.
.~.....-..fi . . .\.. ..\--«H,---W нас, э . .-,I.- », их... а ::--- -«.m-,--.:..~;--~»=- ~~~~~ -'
B качестве эксперимента, можно попытаться удалить реализацию метода methl () B
k 1аеее Myclass. Это приведет к появлению ошибки компиляции. Как упоминалось
Инее, любой класс. реализующий интерфейс. должен рсачизовьттзать все методы,
Которые опрсдсленьт с помощью данного интерфейса, включая те из них, которыС
"аелсдуются из других интерфейсов.
352 Глава 9. Интерфейсы, структуры и перечиспе ’
д.
Ё
‘ь
Явная реализация
При реализации члена интерфейса можно полностью квалифицировать его имя;
качестве имени интерфейса. В результате создается явная реализация члена интерфеё;
со, либо, сокращенно, явная реализация. Например, следующий код ь
Interface IMyIF {
int myMeth(int х);
}
ДОПУСТИМ ПрИ peanmauvm IM}/IF, KEK ПОКВЗВНО НИЖСЁ
с1Ё55 МуС1а55 ° шшугг { Полношъкэонршкшенноеимяъюполышеюя
lnt ImyIF—myMeth(1nt X) { ‘ присошнииявнойреадшшиии
return х / 3;
} н
ъ ’
Как видите, при реализации члена тумесл () класса IMyIF указывается его полное
имя, включая имя его интерфейса. ‘а
Необходимость явной реализации для члена интерфейса может обосновываться двущ
причинами. Во-первых, класс может реализовывать два интерфейса, причем в обохЁх
объявляются методы с помощью одного и того же имени и типа сигнатуры. Благодарй
полному определению имен устраняется двусмысленность подобной ситуации. B6-
вторых, при реализации метода посредством его полностью определенного имена
определяется объем частной реализации, который не доступен для кода, не отнесё-
щегося к классу. А сейчас перейдем к рассмотрению Практических примеров.
Следующая программа содержит интерфейс IE\/en. B котором определяются два
метода: isE1ven () И тзоаа (). Эти методы используются для определения того, яв—
лястся число четным либо нечетным. Затем класс MyClass реализует интерфейс
IEven. После этого явно реализуется метод isOdd().
// Явная реализация члена интерфейса.
using System;
interface IEven {
bool isOdd(int х);
bool isEven(int x):
class Myclass : IEven {
./ / явная pe&.‘“”3a“’T”‘ ° Явно реализован метод isOdd ( ). B результате он будет
cool IEven . lSOdd(ll'1t x) {<%—~—-—neficTB“TenbH0 qaCmhxM_
if((x%2) 1= O) return true;
else return false;
// Обычная реализация.
public bool isEven(int х) {
IEven о = this; // Ссылка на вызывающий объект.
return !o.isOdd(x);
Hanan реализация 353
дддз Demo т
Ир„о;1с statlc Vold Main() {
Myclass ob = new MyClass();
oool result;
result = ob. isEven (4) ;
;f(result) Console.WriteLine("4 четно.");
else Console.Wr1teL1ne("3 нечетно.");
;/ result = ob.isOdd(); // Ошибка, не открыто.
поскольку метод Odd() реализован явно, он недоступен вне класса Myclass. Благо-
даря этому реализация станет по-настояшему эффективной. Метод твоею () внутри
масса MyC lass может стать доступным только после того, как на него будет сделана
ссылка из интерфейса. Причина этого заключается в том, что он вызывается с
помощью реализации о в методе isEven ( ).
Ниже приводится пример, в котором два интерфейса реализуются и объявляются с
помощью метода meth(). C целью преодоления неоднозначности, возникающей в
подобной ситуации, используется явная реализация.
3,-" Использование явной реализации с целью устранения неоднозначности.
жди; System;
mzerface IMyIFAA {
12:: теги (int х) ; ‹———1_Сигнатуры для двух этих методов совпадаютц
mterface IMyIFiB {
in‘; meth(j_nt x); 4»:-*j-.j_—.—__——_
1
‘Б классе МуС1а55 реализуются оба Интерфейса.
s MyClass : IMyIF_A, IMyIF_B (
1F_A а_оЬ:
-:.;1 “_B b_ob;
Явная реализация двух методов meth().
“д? ТМУ I F_A - meth (int X) { u явной реализации устраняется HCO1.1HO3Ha‘lHOCfb;l
return х + х;
1-H‘. IMyIF_B.meth(int X) (<-
return x * x;
вызов метода meth() с помощью ссылки интерфейса.
?ыэ11с int methA(int x){
a/ob = this;
return a*ob.meth(x): // Вызов IMyIF A
Public int methB(int x){
b_ob = this;
return b_pb.meth(x); // Вызов IMyIF B
) _
354 Глава 9. Интерфейсы, структуры и перечисд
class FQIFNames {
public static void Main() {
MyClass ob л new MyClass();
Console.Wr1te("Bm3oB метода 1Му1Ё“А.шеЕЬ(): ");
Console.Wri:eLine(ob.methA(3));
Console.Wr1te("Bu30B метода IMylFWB.meLh(): ");
COnsole.WriEeLine(ob.methB(3));
}
Результат вы полне ния програм м ы:
Вызов метода IMyIF_A.meth(): 6
Вызов метода IMyIFWB.meth(): 9
При просмотре кода программы обратитс внимание на то, что метод meth () nMe_,
одни и те же сигнатуры в 1Му1Р___А и в ТМу1Ёйв. Таким образом, в случае, ее,’
MWC‘-_.iSs реализуст оба этих интерфейса, он должен явно реализовать каждый из н„_
1 '.
no отдельности, полностью квалифицируя их имена в ходе осуществления npouecx.-7')
Поскольку единственным способом, посредством которого может быть вызван яв.
реализованный метод, является ссылка интерфейса, метод MyClaSs создает 1
подобных ссылки, одна из которых предназначена для IMyIF_A, а вторая — г _‘
IMyIF_B. Затем вызываются оба собственных метода класса с помощью метод‘:
интерфейса, в результате чего устраняется неоднозначность.
Минутка/д npa/emzz/gm
I. Если интерфейс А наследуется интерфейсом В, а интерфейс В наследу
классом С, то может ли класс С реализовывать все члены классов А и`
либо только те из них, которые определены классом B?
а
2. Назовите две причины, в силу которых может потребоваться явная peannafig
аЦия интерфейса.
Структуры
Как вы Знаете, классы представляют собой ссылочные типы. Это означает, что ДОСТУ“
к объектам классов производится с помощью ссылок. Это в корне отличается от THUG‘
значений, доступ к которым осуществляется непосредственно. Однако иногда бывай?
полезно обеспечить непосредственный доступ к объекту в силу соображений Эффек?
тивности. Доступ к объектам класса с помощью ссылок влечет дополнительньЁд
нашадные расходы при каждом сеансе доступа. При этом также потребляет“!
дополнительное пространство на жестком диске. При работе с очень маленькими
объектами величина дополнительного потребляемого пространства может ИМСТЁ
большое значение. С Целью решения подобных проблем в С# предлагаются стр)”
туры. Структура подобна классу, но она имеет скорее тип значения, но никак Не “д:
ссылки.
1. Реализующий класс должен включать все члены, указанные в иерархии интерфейсов.
2. Явная реализация может использоваться для устранения неоднозначности, а также
препятствия отображению члена интерфейса.
Отруктурья 3
ОбъЯРЩСНИС структур ВЫПОЛНЯСТСЯ С ПОМОЩЬЮ КЛЮЧСВОГО СЛОВЗ struct. ТВК
дуСТ ОТЧСТИТЬ, ЧТО структуры СИНТНКСИЧССКИ ПОДОбНЫ KJl'<\CC3M. ИЖС ПрИГ5ОДИ'
one
Обобщенная форма объекта struct:
strut: name : interfaces {
/'1." Q:5'bHBJl€HI/SH ЧЛЕНОВ
i
Имя структуры указывается с помощью параметра name.
Структуры не могут наследовать другие структуры либо ютассы. а также нс мог
приподняться в качестве основы для других структур либо классов. Однако кажд
структура может реализовывать один или больше интерфейсов. Реализуемые инте
фспеы указываются после имени структуры в виде списка, разделенного запятыми
как п в случае с ючассамгд члены структуры включают методьд поля‚ индексатор
свойства, операторные методы, а также события. Кроме того, структуры мог
определять конструкторы, но не деструкторы. Однако для структуры не может бьг
определен конструктор, заданный по умолчанию (без параметров). Причина этот
зактгочается в том, что конструктор, заданный по умолчанию, автоматически опрт
дслястсзт для всех структур и не может быть изменен.
Объект структуры может быть создан с помощью ключевого слова new точно так Жч
как объект ютасса (но не обязательно). Как только будет использовано ключевое слов
new, вызывается указанный конструктор. Если названное ключевое слово не пру
меняется, объект по-прсжнему создастся, но не ининиализируетсгт. Благодаря этом
не требуется выполнять инициализацию вручную.
Ниже приводится пример использования структуры:
// Демонстрация возможностей структуры.
using System;
// шнределение структуры.
punlic strlng name;
public double balance;
Public account(string n, double b) {
name п п;
balance < b;
/,‘
Демонстрация возможностей структуры account.
Cl
dSs StructDemo {
Public static void Main() {
account accl — new account("ToM", 1232.22); // Явный конструктор.
account acc2 = new account(); // Конструктор, заданный по умолчанию.
account acc3; // Конструктор отсутствует.
Console.WriteL1ne(acc1.name + " имеет на счету " 4 accl.balance);
Console.Wr1teL1ne();
if(acc2.name null) ConSole.WritcL1ne("acc2.name равно null.");
C0nsOle.WriteLine("acc2.balance равен " + acc2.balance);
ConsOle.WriteLine();
356 Глава 9. Интерфейсы, структуры и перечи
// Д Ц И а С 3 О О с ол Зован'я
олжен mum иал зи он Tb a с ц er И н ь л .
асс3.пате г "Мэри";
acc3.balance = 99.33;
Console.WriteL1ne(acc3.name + " имеет баланс " + acc3.balance);
т
Результат выполнения программы:
Том имеет на счету 1232.22
acc2.name равно null.
aCc2.balance равен О
Мэри имеет на счету 99.33
I т
д Вопрос. Известно, что в С++ также имеются структуры и использ
тстючевое слово struct. Подобны ли структуры языков программирования у
C++?
OTBeT. Нет. В C++ ключевое слово struct определяет тип класса. Т:
образом, в С++ ключстзьте слова struct и class практически эквивалентны. (Р б
заключается в способе реализации доступа к членам ктасса, заданного по Д
нию.) В Ci? s:ruct определяет тип значения, а class определяет тип ссылки. -
.‚<—„„..-‚х..„«„„.‚.‚.‚:...‚‚ „ш. ;.. . -. .‚ .‚ „
В примере программы структура инициализируется либо посредством ключе
слова new, используемого для вызова конструктора, либо посредством
объявления объекта. Если используется ключевое слово new, выполняется ин '
‚лизания полей структуры. Процесс инициализации происходит либо с помо_
конструктора. заданного по умолчанию. который инициализирует все поля с п
щью стандартных значений, либо путем использования конструктора, опредСЛбнЁ
пользователем. Если ключевое слово new HC применяется, объект не инициализ
а его полям Должны быть назначены Значения до использования объекта.
д...-›ъ.._ч‚‚ы „л. ..~ „в.
Перечисления 3
Г|од перечислением понимается набор именованных целочисленных констаНТ.
деляюших все допустимые значения переменной для данного типа. Подобный’!
объектов весьма распространен. Ниже приводится перечисление, включающее _
id
сание чонст США: з
i
d
penny, nickel, dlme, quarter, half—dollar, dollar
Ключевое слово enum определяет перечислимый тип. Рассмотрим общую ф
ИСПОЛЬЗУСМУЮ при определении ПСрСЧИСЛСНИЯЁ
ш..=-. .....,~., д.
enum name { enumeration list };
Имя типа, используемого в перечислении, указывается с помощью параметра д
Список enumeration llst представляет еобой список идентификаторов. разд ‚
пых запятыми.
I nepe|«MC
‘ °DeM
f.\
пения
В C1cI'1\.'l>OU.1CM фрагменте КОДИ ОПРСДСЛЯСТСЯ I'lCpC'll/IC_I'l'CHl/IC COlf1I
J ›.
Ш Coin { penny, nickel, dime, quarter, half~dollar, dollar};
en”
Важны“ B ПОНЯТИИ «ПСрСЧИСЛСНИС» ЯВЛЯЕТСЯ ТОТ ФИКТ, ЧТО KH)K.[J.OMy ИЗ СИМВО
ООТВСТСТ|ЗУСТ ЦСЛОЧИСЛСННОС ЗНВЧСНИС. В результате область ПРИМСНСНИЯ СИМВО
С ' ‚ ‚ . ‚ ‚ ~ , ‚
СОВПаДНСТ С ООЁШСТЫО ИСПОЛЬЗОВдНИЯ ЦСЛОЧИЬГТСННЫХ ЗНдЧСНИИ. ЦСЛОЧИЬЁТСННОС 3
чениС ДЛЯ КНЖДОГО СИМВОЛВ бОЛЬШС ЗНВЧСНИЯ предшествующего СИМВОЛВ. ПО уМ
анию }НЦЧСНИС ПСрВОГО СИМВОЛ?! ПСрСЧГАСЕЛСНИЯ равно НУЛЮ.
Ч
Доступ к элементам пере-тиелениэг осуществляется посредством имен типов, с
П0дь3овнпиех1 точечного оператора. Обратите внимание на код
+ coin.penny Ф
am5O1e_NriLeLine("penny is
" nickel is + coin.nickel);
который отображает следующее сообщение:
рш„Ш ;s О nickel is 1
Ниже приводится пример программы, демонстрирующей возможности перечислен
coin:
N демонстрация возможностей перечисления
using System;
class EnuwDemo (
enum coin ( penny, nickel, dime, quarter,
half_dollar, dollar };
public static void Main() {
str;ng[] names = (
"penny",
"nickel",
"dire",
"quarter",
”half(dollar",
"aoilar"
Т:
CO1“ 1; // Объявление переменной для обработки перечисления
// Использование переменной i для цикла
‹
// 05Pd5O'l'Kl/I перечисления Переменная coin MO)K(
fOr(; _ COln_penny,. i <: COin_dOllar; i++)4?i_KonTpo;1npoBa'r1, цикл f
Console.WriteLine(names[(int)i] + " имеет значение " + i);
r имеет значение 3
d‘\
Чщ_\ O~+ar имеет значение 4
fir Имеет значение 5
51>
aT"”C внимание на то. что управление ЦИКЛОМ for осуществляется с помош
СШЮЙ. имеющий тип coin. Поскольку перечисление имеет Цедочиеленн
т
358 Глава 9. Интерфейсы, структуры и Перечис ›
тип, значение перечисления может присваиваться там же, где могут назнача Я
целочисленные значения. Однако при использовании значения псречисле '
ЦСЛЬЮ индексации массива names требуется провести преобразование типа. С др Ё‘,
стороны, как только значения персчислимых значении в Coin начинаются с
эти значения могут использоваться при индексировании массива names с дед‘-
получения названий монет. Техника. заключающаяся в сопоставлении осмыслен“?
строк и значений перечисления, является весьма полезной.
е:
Инициализация перечисления
С помощью иницишизатора можно указать значения одного или большего количдд.
ства символов. Для этого после каждого символа должен следовать знак равенства?“
целочисленное значение. Символы, отображаемые после инициализаторов, предс-рай;
ляют собой назначенные значсния, которые должны быть больше. чем предыдущё
значение инициализации. Например, в следующем коде значение 100 присваиваерёд
символу quarter‘.
enum coin { penny, nickel, dime, quarter—lOO, Ё
па1Е_с1о11ак, dollar};
Теперь СИМВОЛЫ MMCIOT TEIKHC ЗНЭЧСНИЯЁ
реппу О
nickel 1
dime 2
quarter 100
halfwdollar 101
dollar 102
Указание базового типа ПЕРЕЧИСЛЕНИЯ
По умолчанию перечисления основываются на типе int, НО можно соЗДаВаТЬ
перечисления любого целочисленного типа, за исключением char. Для указания
типа, отличного от int, поместите наименование базового типа после имени nepej
ЧИСЛСНИЯ, разделив их между собои двоеточием. Например, после применения
следующего оператора соты становится перечислением, основанным на типе byte-
спит Coin : byte i penny, nlckel, dime, quarter,
half_dollar, dollar};
Теперь, например, значение, присваиваемое члену Coin .permy, имеет тип byte.
H Минутные? практикум
I _ 1. Нужно ли при создании структуры пользоваться ключевым словом new?
2. Может ли для структуры определяться деструктор?
3. Что такое перечисление‘?
J
1. Нет, объект struct может создаваться подобно объекту любого другою типа без исполню‘
вания ключевого слова new. Однако при этом объект не будет инициализирован.
2. Нет, структуры не могут обладать деструкторами.
3. Перечисление представляет собой перечень именованных целочисленных констант.
K0
и
„ТрОП ьные ВОП рОСЫ
1.
Ключевой афоризм для Сд звучит так: «Один интерфейс, много мето-
дов». Какие свойства лучше всего йЛл10сТриру1о‘г ‘уго высказывание?
Сколько ъетассов может рсгыизовать интерфейс? Сколько интерфейсов
может рсгшизовать класс’?
Могут ли наследоваться пнтерфейеьл?
Должен ли класс рсгьлизовьдваггь все ‘клены интерфейса‘?
Может ли интерфейс объявлять конструктор?
Создайте интерфейс для класса Чсп1с1е (глава 7). Назначьте ему имя
IVen1Cle.
Создайте интерфейс для безопасных массивов. добейтесь этого путем
адаптации массива, устойчивого к отказам, из последнего примера
главы 7.
Каковы различия между st,rux:t И class?
Покажите, каким образом можно создать перечисление для планет
солнечной системы. Назовите его удапегз.
1539
-‘ ./'.’v’Vvf?r':-
-z-:-<-<m>'«\~' тет мг»
С!
С!
С!
С!С!С!С!С!С!
Использование блоков try И catch
Если исключение не перехвачено... ё
-s-
Использование нескольких операторов
catch
Вложенные блоки try
Генерирование исключений
Объект Exception
Использование ключевого слова finally Ед
Исследование встроенных исключений
С#
Создание собственных классов исклю-
чений
Использование ключевых слов checked И Ё
unchecked
Обработка
исключений
- г‘ :...-
‘Waco system.Exception 361
mags рассматривается обработка исключений. Искл/очение представляет собой
бку, происходящую во время выполнения программы. С помощью подсистемы
шибоёгки исключений для Cit можно обрабатывать такие ошибки, не вызывая краха
(‚бра „мы. Решение этой задачи в Cir основано на усовершенствованных методиках.
“розёнэяемьпх в языках программирования C++ И Java. Поэтому для читателей,
прздодцэях с основами одного из этих языков, эти методики уже известны. Однако
Ёёработка исючючений в Ctr отличается четкостью и простотой реализации.
Наличие механизма обработки исючючсний позволяет упростить процесс написания
кода Ранее код обработки исключений полностью создавался програмвмист‘ом.пНа—
пример. если язык программирования не имеет средств обработки исключении. то
Необходимо прсдусмотреть возврат методами кодов ошибок. возникающих в резуль-
тате сбоев программы. И Обработку этих кодов. Подобный подход неудобен и чреват
возникновением новых ошибок. Обработка исшючений ускоряет процесс обработки
ошибок, поскольку программа может содержать блок кода, именуемый обработчиком
исключений. Этот блок кода будет выполняться автоматически в случае возникновения
каких-либо ошибок. Необязательно вручную проверять правильность выполнения
каждой отдельной операции или вызова метода. При появлении ошибки происходит
ее обработка с помощью обработчика ошибок.
Другая причина. по которой важна обработка исключений. состоит в том. что в Св
определены стандартные исключения rum наиболее часто встречагощихся ошибок,
таких как деление на ноль или выход значения индекса за пределы диапазона.
Поэтому для устранения подобных ошибок в программе нужно только следить за
возникновением соответствующих исключений и обрабатывать их.
Таким образом, для успешного овладения языком программирования С# необходимо
всовершенстве освоить методику работы с подсистемой C#, выполняющей обработку
исключений.
Класс System.Exception
ВЯЗЫКС Ci? иск;почения представлены ктассами. Все ютассы исключений могут быть
Унаследовапы от настроенного класса исключений Exception, являющегося частью
П ‚ .. - ,.
р°сТрань гва имсн System. Поэтому ВСС исключения представляют Сооои подклассы
класса Exception.
I::t'(i:“n‘:Ca ériceptlon наследуются шассы исютючений System.Exception И Арр11—
ченигг Ёйзерсъоп. Таким образом, в С# определяются две общие категории исклю-
програшоразованньпе средои исполнения C# (ТО. есть CLR) И сгенерированные
едобёпёгпьпзтът приложениями. Ни Зузпетехсерпдогт, ни Appl:cat;onEx::ept1on
ерхнис .15llO‘T ничего нового в класс Ехеерые-п. Этипклассы фактически определяют
Очки двух различных иерархии исшючениът.
В
дейфхчйвсдслены несколько встроенных исключений, наследуемых из класса Sys—
;,cm}0-I-K:-l« цеп‘. Папример, если возникает ситуация с делением на ноль, генерируется
OCH Be Кчие ‘Эмцоевдшееовхсерсщотт. Как будет отмечено далее в этои главе, на
а касса исключении Appl:LCat1onExceptJ_on пользователь может сформиро-
с - „
B0“ собственные классы исключении.
352 гдава 1o_ обработка исключе
5
Основы обработки исключений д;
Обработка исключений в Сд выполняется с применением четырех ключевых слой?
try, catch, throw И flnally. Эти ключевые слова образуют взаигиосвязаннуГ t
подсистему, в которой использование одного из ключевых слов_ влечет за coed,“
использование других. Все упомянутые ключевые слова будут подробнее рассмотрещз в
ниже. Однако сначала полезно получить общее представление о той роли, котораЁ
отведена при обработке исключений каждому из этих ключевых слов, Рассмотриф
вкратце функции каждого из них. ъ
В блок try помещаются онсраторы программы, за выполнением которых необходим‘) д
следить на предмет возникновения исютточеньтй. Возникающее исключение перехвад, 5.
тывается и обрабатывается блоком catch. Для генерирования исключения применяёё]
‘н: „‚
ется ключевое слово throw. B блок finally помещается код, который всегда; _
выполняется при выходе из блока try.
Использование блоков try И catch “ё
Обработка исключений основана на использовании блоков try И catch. Эти блокидё
работают совместно: нельзя задать блок try И не задать catch, И наоборот. НижеЁ
приводится общий синтаксис блоков try,-‘catch. выполняющих обработку исклю-Ё:
чений:
try (
// Блок кода, для которого выполняется мониторинг ошибок.
catch (Exceplypel exob) {
// Обработчик исключений Excepiypel.
}
catch (ExcepType2 exob) (
// Обработчик исключений ЕхсерТуре2.
I д
Параметры ЕхсерТуре задают типы обрабатываемых исключений. После генерировани
исключения происходит его перехват соответствующим оператором catch, выпол
няпощим обработку этого исключения. Как показано в общей форме синтаксиса, О
блоком try может ассоциироваться более одного оператора Catch. Тип исютючения д,
определяет вьппогтняемьпй оператор catch. Если возникшее исключение соответствует’
типу иеютючения, указанному в операторе cat ch, то выполняется блок команд этою _
оператора (а все остальные блоки Catch игнорируются). При псрехвате исключения
опсратором catch соответствующий объект исключения exOb получает его значение.
Фактически указание объекта ехов не является обязательным. Если обработчик
исключений не нуждается в доступе к объекту исключения (это часто бывает На
практике), нет необходимости в указании exOb. Учитывая сказанное, для многих
примеров в главе объекты исктючсньтя задавать не надо.
Важно отметить, что если исключение не генерируется, работа операторов блока try g
завершается в обычном порядке, а все соответствующие ему операторы catch
пропускаются. Выполнение программы возобновляется е первого оператора, следую’
щего за последним опсратором catch. Таким образом, операторы catch Bb|3blB'c1}OTCfl_ _
ТОЛЬКО В ТОМ cnyuae, ССЛИ генерируется ИСКЛЮЧСНИС.
основы обработки исключений 363
простой пример исключения
Ниже приводится простой пример, демонстрирующий методику отслеживания и
перехвата исютючений. Вы, наверное, знаете, что при обращении к индексу, выхо—
дяшсму за пределы массива, возникает ошибка. В этом случае система выполнения
Cg генерирует исключение IndexOutOfRangeException, представляющее собой
Стшщиртное исключение. определенное в C11. Назначение следующей программы
здкдцючас’гся в генерировании и перехвате подобного исключения:
/: демонстрация обработки исключений.
‘юны; System;
class ExcDe-mol {
public static void Main() {
;ut[] nums — new int[4];
try f
Console.Writ:eLine("3'1‘a строка выводится до генерирования исключенияНЧ;
Генерирование ИСКЛЮЧЕНИЯ ВСЛЕДСТВИЭ выхода ИНДЕЁКСЗ 3.3 пределы диапазона.
I1UmS[71 т 10; <— Попытка использования
Cons0le.Wr11:eL1ne ("ЭТОТ текст не будет отображен . ") ; Индекс?! массива “Urns,
находящегося за преде-
лаипадиапазона.
|
!
catch (Index0utOfRangeException) {
// Перехват исключения.
Соп$о1е.Иг1сеЬ1пе("Индекс за птределами диапазона!");
1
Console.WriI:eLine ("После оператора catch.") ;
}
P€J_V.'IbT'clT ВЫПОЛНСНИЯ ПрОГрЭММЬГС
Эта строка выводится до генерирования исключения.
Индекс за пределами диапазона!
После оператора catch.
B данной программе кратко рассматриваются некоторые основные моменты, связан-
ные е обработкой исютючений. Во-первых, код, который требуется отслеживать на
"рсдгиет выявления ошибок, заключается в блок try. Во-вторых, в случае возникно-
Всния исключения (в данном случае ы по причине попытки использования значения
Индекса массива nums. выходящего за пределы диапазона) таковое перехватывается
епсратором Catch. Начиная с этого момента контроль передается блоку catch, a
шок try завершает свою работу. При этом блок catch фактически не вызывается.
Скорее, этому блоку передается управление. В результате оператор WriteLine () ,
С-Тсдующий за оператором использования индекса. значение которого выходит 33
"Рсделы диапазона, выполнен не будет. После выполнения оператора catch ynpaB-
JICHI/Ic передается операторам, следующим за блоком Catch. РСШСНИС Проблем,
Ньпываюцъих возникновение исключения, — удел обработчика исключений. При
‘вши программа продолжает выполняться и не завершается аварийно.
Обратите внимание на то, что в конструкции Catch НС указан ни один параметр.
Как упоминалось рансе, параметр требуется только тогда, когда необходим доступ К
Объектам исключений. В некоторых случаях значение объекта исключения может быть
Использовано обработчиком исключений с целью получения дополнительных сведений
354 глава 1о_ обработка исключ
ь
об ошибке, но часто достаточно самого факта возникновения ИСКЛЮЧСНИЯ. Та
образом, отсутствие параметра Catch B обработчике исключении (как в прецыдуще‘
примере программы) не является чем—либо необычным. 3
‘T’ ..
Ранее уже упоминалось о том, что если в блоке try исключение не возника-‘Т ‚_
операторы catch нс вызываются и управление программой передается оператор‘,
следующему после оператора Catch. C целью проверки Этого факта в предыдуще б
примере Программы замените строку `
ь
nums[7] : 10;
строкой
nums[C] : 10;
B результате исключение не возникнет. н блок catch HC будет вызван.
Второй пример исключения
Важно понимать, что весь код, входящий в состав блока try, контролируется
предмет наличия исключений. При этом указываются исключения, которые мо
генерироваться методом, вызываемым внутри блока try. Исключения подобно г
типа могут перехватываться этим же блоком try (предполагается, что метод сам _
себе не выполняет перехват исключений). Ниже приводится пример корректно
программы: °
/* Исключение может генерироваться с помощью одного
метода, a затем перехватываться другим методом. */
using System;
class EXcTest {
// Генерирование исключения. Ё
public statlc void genExcept1on() {
int[] nums 4 new int[4];
Cons0le.WriteLine("3Ta строка выводится до генерирования исключения.")?
// Генерирование исключения в случае выхода индекса за пределы диапазона
ПШПЗ [7] + 10; 4 Здесь хенерирустся исключение.
Соп5о1е.Иг1сеЬ1пе("Этот текст не будет отображен."):
class ExcDemo2 {
public static void Main() {
try {
ExcTest.genException();
1
catch (IndexOutOfRange-Exception) [
// catch the exception
Соп5о1е.Иг1сеЬ1пе("Индекс за пределами лиапазона!");
}
Console.WriteLine("HoCne оператора саЕсп.");
п
365
Если исключение не перехвачено
и/”
рдзхътьтат выполнения этой программы аналогичен результату выполнения предыдущей:
Эта строка ВЫВОЦИТСЯ ДО генерирования ИСКЛЮЧЕНИЯ.
Индекс за пределами диапазона!
После оператора catch.
Как только метод genExcept1on() вызывается внутри блока try‘, генерируемое в
редультате исютючение (если оно до сих пор не перехвачено) перехватывается с
Помощью оператора Catch, входящего в состав метода Main (). Понятно, что если
метод genException () перехватит исключение, оно уже не будет возвращено методу
мд*п().
Минутные? драхма/сим
Что такое исключение?
F) __
Частью какого оператора должен быть код, отслеживасмый исключениями?
3. Каковы функции оператора са tch? Что происходит после выполнения этого
оператора?
Если исключение не перехвачено
Перехват и обработка одного из стандартных исключений C# (выполняемые в
предыдущем примере) предотвращают аварийное завершение программы. После
возникновения исключения оно должно быть перехвачено и обработано неким
фрагментом кода. Если программа не перехватывает исключение, последнее будет
перехвачено системой выполнения C#. Проблема. появляющаяся в этом случае,
заключается в том, что система выполнения отображает сообщение об ошибке и
завершает выполнение программы. Например, в следующей версии предыдущего
примера исключение, возникающее в результате выхода за пределы диапазона, не
перехватывается программой:
// Позволяет системе выполнения C# обрабатывать ошибки.
дзьпо System;
Class Notfiandled {
public static void Main() {
1nt[] nums = new 1nt[4];
Console.WriteLine("3Ta CTpOKa выводится до генерирования исключения.");
// Генерирование исключения при выходе значения индекса за пределы
// диапазона.
nums[7] = 10;
1- В роли исключения выступает ошибка времени выполнения.
2- Для отслеживания исключений потребуется включить код в состав блока try.
3- Оператор Catch перехватывает исключения. Так как оператор catch не вызывается из
программы, то после выполнения блока catch управление не передается обратно оператору
программы, при выполнении которого возникло исключение. Выполнение программы
продолжается с операторов, находящихся после блока Catch.
366 Глава 10. Обработка исключен
Ы
Е:
В ЭТОМ СЛУЧЗС ВЫПОЛНСПИС программы прерывается И отображается СЛСДующе_
сообщение об ошибке: д
Unhandled Ехсерзщоп: System.IndexOutOfRangeException:
Exception of type System.lndexOutOfRangeException
was thrown.
at NotHandLed.Ma1n()
_ "ti.
Хотя сообщения подобного типа являются весьма полезными в случае отладки;
весьма нежелательно, чтобы их видели пользователи данной программы! По Этой? .
причине важно, чтобы программа еама обрабатывала исключения.
„Щ,
«m
Ранее уже упомингшось о том, что тип исключения должен соответствовать типуё I
указанному с помощью опсратора catch. Если это не так, исключение может бы _ ;
не перехвачено. Например, в следующей программе предпринимается попыпсЁё
обработки ошибки, возникающей вследствие выхода за границы массива, путей
перехвата оператором catch исключения DivideByZeroException (еще одно вс
ь
В результате происходит аварийное завершение программы.
// Эта программа неверно обрабатывает исключение!
using System;
class ExcTypeMismatch (
public static void Main() {
int[] nums = new int[4};
try {
Console.WriteLine("3Ta строка выводится до генерирования исключения.");
// Генерирование исключения выхода значения индекса массива за пределы
// диапазона. ___
nums {7} = 10; b генерируется исшючение 1пеехоипогкапценхеерс1оп._ъ
ConSole.WriteLine("3wow текст не отобразится."); Z
/* Невозможно перехватить ошибку выхода за границы массива
с помощью исключения DiVideEyZeroException. ’/
catch (DivideByZeroException) { ‹___________[Пяшпкатюршша ощибкисткмюишю
// перехват Исключения исключенияО1ч1аеву2егоЕхсерс1ош
Console.WriteLine("mnnexc за п еделами диапазона!");
P
}
Console.WriteLine("H0cne оператора caLch.");
}
Ре 3 ул ьтат вы п ол не н и я п рогра м м ы: ‘
g
3T6 строка ВЫВОДИТСЯ ДО генерирования ИСКЛЮЧЕНИЯ.
Unhandled Exception: System.IndexOutOfRangeException:
Exception of type System.Index0utOfRangeException
was thrown.
at ExCTypeMiSmatch.Main()
изящная Обработка ошибок c помощью ИСКЛЮЧЭНИЙ 367
Резуттьтаты выполнения программы показывают, что оператор catch, рассчитанный
На исключение DivideByZeroException, НС перехватывает исютючение Index~
fi”.;:PanqeExcept1on
___‚ ‚
изящная обработка ошибок с помощью
исключений
Одним из основных преимуществ обработки исюпочений является то, что в этом
шуте программа может реагировать на ошибки, а затсм продолжать выполнение.
Обратите внимание на следующии примср, в котором элементы одного массива
дшятш на элементы другого. Если произошло ДСЛСНИС на ноль, генерируется
исключение Div1deByZeroExCeption. B программе выполняется обработка исклю-
чения с отображением сообщения об ошибке. а затем программа продолжает выпол-
няться. Таким образом ситуация деления на ноль не приводит к появлению ошибки
выполнения с последующим аварийным завершением работы программы. Вместо
этого происходит изящная обработка ошибки, в результате которой возможно про-
должение выполнения программы.
// изящная обработка ошибок и продолжение выполнения программы.
using System;
class ExcDemo3 {
puoLic static void Main() {
;nt[] numer = { 4, 8, 16, 32, 64, 128 1;
int[] denom ( 2, O, 4, 4, 0, 8 }:
и
{or(int i—O; i<numer.Length; i++) {
try {
Console.WriteLine(numer[i] г " / " +
denOm[i] + " будет " +
numer{i]/denOm[i]);
}
catch (DivideByZeroException) {
// Перехват исключения
Соп5о1е.Иг1СеЬ1пе("Нельзя делить на ноль!"):
ч
ь
›
ВОТ рСЗУЛ ЬТИТ ВЫ ПОЛ НС Н ИЯ П рО rpa М МЫ Ё
4 / 2 будет 2
Нельзя делить на ноль!
I 4 будет 4
fix 4 будет 8
Ёдазя делить на ноль!
/ 8 будет 16
ЭТОТ пример демонстрирует ещс один важный факт: как только исключение обраба-
Щвается, оно устраняется из системы. Таким образом, в программе, в которой блок
ti y заключен в тело пита, будут обрабатываться все исключения. Благодаря этому
Возможна обработка повторяющихся ошибок.
368 Глава 10. Обработка исключен б
Минутный практикум
1. Каким должен быть тип нсктючсния в операторс Catch?
2. Что произойдет в случае, если исютючение не будет перехвачено?
3. Что должна выполнять программа при возникновении истючения?
Использование нескольких операторов catch
C одним блоком try может быть связано более одного блока catch. Подобная
практика является общепринятой. Однако каждый блок catch должен перехватываф v
исключения различного типа. Приведенная ниже программа перехватывает ошибка,
связанные с выходом индекса за границы массива и с дегжснием на ноль.
1
// Использование нескольких операторов catch.
using System;
class ExcDemo4 {
public static void Main() {
// Массив numer содержит больше элементов, чем denom.
int[j numer ь { 4, 8, 16, 32, 64, 128, 256, 512 };
int[] denom = { 2, 0, 4, 4, O, 8 };
for(int i=0; i<numer.Length; i++) {
try {
Conso1e.WriteLine(numer[i] + " / " +
denom[i] + " будет " +
numer[i}/denom[i]);
}
catch (DivideByZeroException) {«<
// Перехват исключения
Соп5о1е.Иг1пеЬ1пе("Нельзя делить на ноль!");
}
catch (IndexOutOfRangeException) (4
// Перехват исключения
Console.WriteL1ne("He найдены совпадающие элементы.");
}1есколько операторов
catch.
}
Результат ВЫ ПОЛ НСН ИЯ программ ЫЁ
4 / 2 будет 2
Нельзя делить на ноль!
16 / 4 будет 4
32 / 4 будет 8
Нельзя делить на ноль!
___—
1. Тип исключения в операторе catch должен соответствовать типу перехватываемого исклЮ’
чения.
2. Неперехваченное исключение непременно приводит к досрочному прекращению выполнт
ния программы.
3. Программа должна осуществлять изящную и рациональную обработку исключений. При этом
нельзя оставлять необработанные исключения и следует избегать нестандартных заверщений-
Перехват всех исключений 369
;Z8 / 8 будет 16
не найдены совпадающие элементы.
не найдены совпадающие элементы.
113 результата выполнения программы видно, что Каждый оператор Catch COOTBCT—
ствует своему собственному типу исключения.
Как правило, выражения Catch проверяются в порядке их записи в программе.
в итоге выполняется только тот блок, исключение которого совпадает с возникшим.
Остальные блоки Catch будут в этом случае проигнорированы.
перехват всех исключений
Иногда требуется перехватывать все исключения, не обращая внимания на их тип.
Для выполнения этой задачи воспользуйтесь оператором catch, для которого нс
указываются параметры. Такой оператор перехватывает все исключения. В приведен-
ном ниже примере возникающие исключения IndexOutOfRangeException И Di-
:-;deByzeroException перехватываются и обрабатываются одним и тем же блоком
:atCh:
Я Использование.оператора catch, "перехватывающего все".
ь51ПЧ System;
class ExcDemo5 {
public static void Main() {
// Массив numer содержит больше элементов, чем denom.
int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };
int{] denom = { 2, 0, 4, 4, 0, 8 1;
for(int i=0; i<numer.Length; i++) {
try {
Console.Wr1teLine(numer[i] т " / " +
denom[i] + " будет " +
numer[i]/denom[i]);
Catch { 2% n6pCXB3TbIB8lOTCH ВСЁ ИСКЛЮЧСНШ
Соп$о1е.Иг1пеЬ1пе("Возникло исключение.");
Результат выполнения программы:
4 / 2 будет 2
Возникло исключение.
16 / 4 будет 4
32 / 4 будет 8
Возникло исключение.
128 / 8 будет 16
Возникло исключение.
Возникло исключение.
370 Глаза 10. Обработка исключений
Вложенные блоки try
БЛОКИ try иогут вкладываться друг в друга. Исключение, генерируемое во внутрсн-
нем блоке try И не перехваченнос блоком Catch, соответствующим данному внут-
реннему блоку try, распространяется на внешний блок try. Ниже показан пример
возникновения исключения IndexOutOfRangeException, которое не перехватыва-
ется внутренним блоком try, НО зато перехватывается внешним блоком try:
// Использование вложенного блока try.
uslng System;
Class NestTrys {
public static void Ma1n() {
// Массив numer содержит больше элементов, чем denom.
in:[] numer Z [ 4, 8, 16, 32, 64, 128, 256, 512 };
int[] denom = { 2, O, 4, 4, O, 8 );
try ‹ // внешний блок try4—————-—:————B,,(,m,,,,,c
for(int iLO; i<numer.Length; i++) { mwxntry
try { // Вложенный блок try<(——————-*-———
Console.WriteLine(numer[i] + " / " +
denom[i] + " будет " +
numer[i]/denom[i]);
}
catch (DivideByZeroException) {
// Перехват исключения.
Соп5о1е.И:1пеЬ1пе("Нельзя делить на ноль!");
}
catch (IndexOutOfRangeException) {
// Перехват исключения.
Conso1e.WriteLine("He найдены совпадающие элементы.");
Соп5о1е.Иг1ЕеЬ1пе("Критическая ошибка — выполнение програмы прервано.");
)
РСЗУЛ ЬТЗТ ВЫПОЛ НСНИЯ программы:
4 / 2 будет 2
Нельзя делить на ноль!
16 / 4 будет 4
32 / 4 будет 8
Нельзя делить на ноль!
128 / 8 будет 16
Не найдены совпадающие элементы.
Критическая ошибка — выполнение программы прервано.
В этом примере исключение, возникающее при попытке деления на ноль, обрабаты-
вается внутренним блоком try. При этом обеспечивается продолжение выполнения
программы. Ошибка, связанная е выходом за границы маееива‚ перехватывается
внешним блоком try, который завершает выполнение программы.
Хотя, конечно, использование вложенных операторов try обуславливается не един-
ственной причиной, предыдущая программа еоздает важный прецедент, который
Генерирование исключений 371
ЦОЖСТ быть обобщен. Часто вложенные блоки try используются для обработки
ошибок разных видов различными способами. Некоторые ошибки являют ся фаталь-
пыми и нс могут быть исправлены. Другие же являются не столь значительными и
могут быть обработаны немедленно. Многие программисты используют внешний
(шок try с Целью перехвата большинства серьезных ошибок, позволяя внутреннему
блоку try обрабатывать менее серьезные. Можно также воспользоваться внешним
блоком try с оператором Catch без параметров для перехвата тех ошибок, которые
пе обрабатываются внутренним блоком.
Маг/умильно’ драхма/кум
1. Каким образом могут персхватыватьсзт все исключения?
2. Может ли один блок try использоваться для перехвата двух или большего
количества исключений различных типов?
3. ЧТО ПрОИСХОДИТ В СЛУЧЭС, ЕСЛИ ИСКЛЮЧСНИС не ПСрСХВЗТЫВНСТСЯ внутренним
блоком при использовании вложенных блоков try?
Генерирование исключений
B предыдущих примерах производился перехват исключений. автоматически сгене-
рированных С#. Однако исключение может быть сгенерировано и посредством
оператора throw. Ниже показан синтаксис этого оператора:
:hrow exceptob;
Здесь exCeptOb является объектом класса исключений. наследуемым из класса
_XCeptiCm
‚Еалсе приводится пример, показывающий, каким образом оператор throw o6ecne-
чивает генерирование исключения DivideByZerOExCeptiOr12
‘Х Генерирование исключения оператором throw.
.sing System;
‘lass ThrowDemo {
public static void Ma1n() {
try {
Console.WriteLine("Uepen использованием оператора throw.");
throw new DivicleByZeroE1xcept;1_on():
Ё
Catch (Div1deByZeroException) {
// Перехват исключения.
Соп5о1е.Иг1пеЬ1пе("Исключение перехвачено.");
)
Console.WriteLine("Uocne блока try/catch.");
)
1_ Для перехвата всех исключений воспользуйтесь оператором catch без указания параметра
исключения.
3, Да, воспользуйтесь оператором catch для каждою перехватываемого исключения.
3. Исключение, не перехваченное внугренним блоком try/catch, перемещается вовне по
направлению K внешнему блоку try.
372 Глава 10. Обработка исключений I‘:
Резул ьтат вы пол не н И я п ро грамм ы:
Перед использованием оператора throw.
Исключение перехвачено.
После блока try/catch.
Обратите внимание на то, что исключение D:Lv:LdeByZeroExCeption было создано
с помощью модификатора new оператора throw. Помните о том, что throw оперирует
с объектами. Поэтому, прежде чем использовать этот оператор, Требуется Подготовить
объект. В рассматриваемом случае для создания объекта D1videByZeroExCeption
используется конструктор, заданный по умолчанию, однако для создания исключе-
ний доступны и другие конструкторы (соответствующий пример приведен далее).
„дтветы лрофессг/эяала и
Э
Вопрос. Почему возникает потребность в обеспечении генерирования исклю-
чений самим программистом?
Ответ. Такая потребность возникает при использовании исключений из
созданных программистом классов исключений. Далее в этой главе будет показано,
что создание собственных классов исключений позволяет обрабатывать ошибки в
коде в рамках общей стратегии обработки исключений.
. ...ае‹и‚.„.< .>›.:‚-›-»<.‹;ы.„хм-мщш
Повторное генерирование ИСКЛЮЧЕНИЯ
Исключение, перехваченное одним оператором catch. может генерироваться по-
вторно, благодаря чему оно может перехватываться внешним оператором catch.
Наиболее частой причиной повторного генерирования исключений является обеспе-
чение доступа к нему со стороны нескольких обработчиков. Например, один обра-
ботчик исключений может обрабатывать один аспект исключения, второй — другой
аспект. Для повторного генерирования исключения нужно просто указать ключевое
слово throw без имени исключения. То есть необходимо воспользоваться следующей
формой оператора throw:
throw;
Помните о том, что повторно сгенерированное исключение не может быть повторно
перехвачено тем же оператором Catch. Для повторного перехвата используется
следующий оператор catch.
Приведенный ниже код иллюстрирует механизм повторного генерирования исюпю-
чений:
// Повторное генерирование исключения.
using System;
class Rethrow {
public static void genException() {
// Массив numer содержит больше элементов, чем denom.
int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 );
int[] denom = { 2, O, 4, 4, O, 8 );
for(int i=0; i<numer.Lenqth; i++) {
использование блока finally
D
1
}
catch(IndexOutOfRangeException)
try {
Console.WriteLine(numer[i] + " X " +
denom[i] + " будет " +
numer[1]/denom[i]);
1
catch (DivideByZeroException) {
// Перехват исключения.
Соп5о1е.И:1пеЬ1пе(“Нельзя делить на ноль!“);
}
catch IndexOutOfRangeException) {
// Перехват исключения.
Console.Wr1teLine("He найдены совпадающие элементы.");
throw; // Повторное генерирование исключения.
1
Повторное генерирование исключенияч
;lass RethrowDemo {
public static void Main() {
try {
Rethrow.genException();
Перехват повторно стенерированною
4——;——
{ ИСКЛЮЧСНИЯ.
// Повторный перехват исключения.
Соп5о1е.И:1ЕеЬ1пе("Критическая ошибка
+
"выполнение программы прекращено.");
В данной программе ошибка деления на ноль обрабатывается локально, с помощью
метода ger1EXception() , a ошибка превышения границ массива будет сгенерирована
повторно. Исключение перехватывается с помощью метода Main () .
Мену/пяди? практикум
Что выполняет оператор throw?
По отношению к чему применяется throw: K ТИПНМ или объектам?
Может ли исключение генерироваться повторно после того, как оно было
перехвачено? Если да, то какая форма оператора throw используется в этом
случае?
Использование блока finally
Иногда требуется определить блок кода, который будет вызываться в то время, когда
СШС ВЫПОЛНЯСТСЯ бЛОК try/catch. Исключение может спровоцировать ошибку,
Прерывающую выполнение текущего метода, вызывая его досрочный возврат. Но этот
1.
2.
Оператор throw генерирует исключения.
Оператор throw применяется по отношению к объектам. Эти объекты должны быть
экземплярами корректных классов исключений.
Да, укажите оператор throw без исключения
374 Глава 10. Обработка исключений
метод может открыть файл или сетевое соединение, которые должны быть закрыты_
Подобные ситуации довольно часто возникают в программировании, и в С# поддедъ
живается удобный способ их обработки с помощью блока finally.
C целью указания блока кода, который вызывается после выхода из блока try/Catch,
поместите блок finally B конец последовательности с ry/Catch. Общая форма
конструкции try/catch, включающей блок finally, показана нижс:
try {
// Блок кода, выполняющий Мониторинг ошибок
catch (ExcepType1 ex0b1) {
// Обработка исключения ExcepTypel.
catch (ЕхсерТуре2 ехОЬ2) {
// Обработка исключения ЕхсерТуре2_
finally {
// Код блока finally.
)
Блок finally будет вызываться независимо от того, появится исключение или нет,
и независимо от причин возникновения такового. Таким образом, не важно, завер-
шается блок try обычным образом либо возникает исключение, — B любом случае
выполняется последний блок кода, определенный ключевым словом finally.
Нижс приведен пример использования блока finally.
// Использование блока finally.
using System;
class UseFinally {
public static void genException(int what) {
int t;
int[} nums 2 new int[2];
Console.WriteLine("nonyuenme " + what);
try {
switch(what) {
case 0:
t = 10 / what; // Ошибка деления на ноль.
break;
case 1:
nums[4] ; 4; // Ошибка выхода индекса за границы массива.
break;
case 2:
return; // Возврат из блока try.
}
catch (DivideByZerOException) {
// Перехват исключения.
Соп5о1е.И:1ЕеЬ1пе("Нельзя целить на ноль!");
return; // Возврат из блока catch.
Более близкое знакомство с исключениями 375
catch (IndexOutOfRanqeException) {
//' Перехват исключения.
ConsoLe.W:;LeL1nc(”He найдены совпадающие злементы.");
;
finally { Bb13bxuacTcx независимо от блоков crylcatch.
COnso1e.WrLteL1ne("Bmxou ИЗ блока try.");
_Agss FinallyDemo {
public static void Main() {
for(in: i«O; 1 < 3; 1++) {
U5eF1naliy.qenExCeptiOn(i);
Console.Wr1teLine();
Результат выполнения программы:
Получение O
Нельзя делить на ноль!
Выход из блока try.
‚учение 1
Не найдены совпадающие элементы.
Выход из блока try.
Получение 2
Выход из блока try.
Как показывает результат выполнения программы, независимо от того, как был
осуьцеетвлен выход из блока try, вызывается блок finally.
БОЛЭЭ бЛИЗКОЭ ЗНЭКОМСТВО С ИСКЛЮЧЭНИЯМИ
.10 настоящего времени производился перехват исктючений, но с самими объектами
псшпочений не выполнялись никакие действия. Ранее уже говорилось, что конструк-
mm Catch позволяет указывать тип исключения и объект исшючения. Все исклю-
чения наследуются из класса Exception.
B шассе Except lOl'1 определяется множество свойств. Три наиболее интересных из
ппх Называются Message, StackTrace И Targecsxte. BCC ОНИ предназначены ТОЛЬКО
jun чтения. Свойство Message СОДЕРЖИТ строку, описывающую природу ОШИбКИ-
Свойство Згасктгасе содержит строку. включающую стек вызовов, предшествую-
щих исключению. Свойство Targetiilte возвращает объект. который указывает
метод. генерирующий исключение.
B классе Exception также определяется несколько методов. Наиболее часто исполь-
зуемым является метод Tos-* ring О ‚ который возвращает строку, описывающую
исключение. В частности, метод ToStr1ng() Вызывается автоматически в случа6‚
когда исключение отображается с помощью метода Writ€Lir1e () .
376 Глава 10. Обработка искпючениф
A T
Следующая программа демонстрирует применение описанных свойств и метода:
// Использование членов класса Exception. д
using System;
class ExcTest ( F
public static void genException() {
.~5»%~’.1l.;
;nt[] nums = new 1nt[4];
fi§fi
Console.WriteLine("Uepeu тем как исключение будет сгенерировано.");
"едал:
// Генерирование исключения выхода индекса за пределы диапазона. H
nums[7] = 10;
Console.Wr1teLine("3Tow ТЕКСТ не отображается"): 4
class UseExcept {
public static void Main() {
try {
ExcTest.genException();
’ . д???
catch (IndexOutOfRangeException exc) {
// catch the exception
Соп5о1е.Иг1пеЬ1пе("Стандартное сообщение: ");
Console.WriteLine(exc); // Вызов метода ToString()
Console.WriteLine("Tpaccnpoaxa стека: " + exc.StackTrace);
Соп5о1е.Иг1сеЬ1пе("Сообщение: " + exc.Message);
Console.WriteLine("TargetSite: " + exc.TargetSi:e);
}
Console.WriteLine("Uocne оператора catch.");
Э
В ютассе EXCept_‘LOl'1 определяются четыре конструктора. Два из них будут рассмот-
рены в дальнейшем:
Exception()
Exception(string str)
Первый конструктор является конструктором, заданным по умолчанию. Второй
указывает свойство Message, связанное C ИСКЛЮЧСНИСМ. При создании собственных
классов исключений требуется реализовывать оба конструктора.
Часто ИСПОЛЬЗУЕМЫЕ ИСКЛЮЧЕНИЯ
В пространстве имен system определяются многие стандартные встроенные исключе-
ния. Все они наследуются из класса Systemlixception. а также генерируются CLR B
случае возникновения ошибок в процессе выполнения. Многие из наиболее часТ0
используемых стандартных исключений, определенных в C11, представлены в табл. 10.}.
Наследование КЛЗССОВ ИСКЛЮЧЭНИЙ з
таблица 10.1. Наиболее часто используемые исключения, определенные в пространств;
имен System
Исключение Значение
ArrayTypeMismatchException Тип сохраненного значения несовместим с типом массив
DivideByZeroException Предпринята попытка деления на ноль.
lndexOutOfRangeException Индекс массива выходит за пределы диапазона.
|nvalidCastException Некорректное преобразование в процессе выполнения.
ou10fMemoryException Вызов new был неудачным из-за недостатка памяти.
Overf|owException Перепол Нение при выполнении арифметической операциь
StackOverfIowException Переполнение стека.
Мену/тульи‘? тактику/и
I. Что содержит свойство Message?
2. Когда вызывается код из блока finally?
3. Каким образом отображается трассировка событий, предшествовавших воз‘
никновению исключения?
Наследование классов исключений
Несмотря на то что встроенные исключения C# обрабатывают большинство распро-
страненных ошибок, механизм обработки исключений C# He ограничивается этими
ошибками. На самом деле мощь подхода Cit K обработке исключений проявляется в
способности к обработке исключений, заданных программистом. Можно создавать
заказные исключения, выполняющие обработку ошибок в пользовательском коде.
Генерирование исключений не представляет особых сложностей. Просто определите
mace, наследуемый из класса Exception. B качестве общего правила руководствуй-
тесь тем, что определенные пользователем исключения наследуются из класса Ap—
licationException, так как они представляют собой иерархию зарезервированных
исключений, связанных с приложениями. Наследусмыс классы нс нуждаются в
Цщктической реализации в каком-либо виде, поскольку само их СуЩССТВОВЗНИС В
системе типов данных позволяет воспользоваться ими в качестве исключений.
СОЗДЭВНСМЫС ПОЛ ЬЗОВНТСЛСМ КЛНССЫ ИСКЛ1ОЧСНИЙ автоматически ПОЛУЧНЮТ ДОСТуПНЫе
ДЛЯ НИХ СВОЙСТВН И МЕТОДЫ, ОПРЁДСЛСНЪ1ЫС В КЛНССС Exception. KOH€‘IHO, МОЖНО
переопределять ОДИН ИЛИ ООЛЬШС ЧЛСНОВ В создаваемых ВНМИ КЛЗССНХ ИСКЛЮЧСНИЙ.
Далее будет рассмотрен пример, В КОТОрОМ СОЗДЗСТСЯ исключение NOHI r1tReSUl'(,EX‘
ieption, определяющее два стандартных конструктора, а ТНКЖС переопределяющее
чстод Тозт ring ().
-/ 4’, Использование заказного ИСКЛЮЧЕНИЯ .
using System;
L Свойство Message содержит текст, описывающий исключение.
Блок finally является последней конструкцией, вызываемой после выхода из блока try.
3. Для вывода на экран трассировки стека Отобразите значение свойства StackTrace, опреде-
ляемого В классе Exception.
378 глава 1о_ обработка исключений
// Создание Исключения.
class Non1n'..Resul:Excep1:ion : Applicacionfixceptlon {
// Реализация стандартных конструкторов.
pUbl;C Ngn:ntResuLtException() : base() 1 }
pUbl;¢ :gn:n:ResultException(strlng str) . basetstr) { }
// псддкнределение Tostring для NonIntResul:ExcepLion.
DUb_‘3 gverride String ToString() f<——~——————+HCWmHwmGmHMCMCflflBTbSCr1ng(L
дс:„:п Message;
:us:omExcep:Demo {
‚с static vo;d Ма1п() (
Здесь numer содержит некоторые нечетные значения.
ди:[} numer { 4, 8, 15, 32, 64, 127, 256, 512 );
.nL{] dcnom { 2, О, 4, 4, C, 8 };
for(int i:O; i<numer.Length; i++) {
try i _ Г енсрирование заказного исключения. J
1f((numer[1]%2) E: O)
throw new
NonIntResultException("Pe3ynbTaT " +
numer[i] + " / " + denom[1} + " Нечетный-");
Console.Wr;teLine(numer[i] + " / " +
denom[i] + " будет " т
numer[i]/aenom[;});
ь
д
catch (DivideByZeroException) {
// Перехват Исключения.
Соп5о1е.И:1пеЬ1пе("Нельзя делить на ноль!");
}
catch (IndexOutOfRangeException) {
// Перехват исключения.
Console.WriteLine("He найдены совпадающие злементы.");
)
catch (NOnIn:ResUltExcepEiOn exc) {
Console.WriteL;ne(exc);
Ъ
РС3_’Л ЬТНТ ВЫПОЛ НСНИЯ ПрОГрНМ М bl:
4 / 2 будет 2
Нельзя делить на ноль!
Результат 15 / 4 нечетный.
32 / 4 будет 8
Нельзя делить на ноль!
Результат 127 / 8 Нечетный.
Не найдены совпадающие элементы.
Не найдены совпадающие элементы.
Перехват исключений производного класса 379" ш
Перед тем как перейти к дальнейшему изложению, немного поэкспериментируем С
программой. Например, попытайтесь Превратить в комментарий переопределение
чстода ToString() И понаблюдайте за результатами. Либо попытайтесь создать
исключение. воспользовавшись заданным по умолчанию конструктором’ а Затем
„осмотрите, как C11 генсрирует это Исктючение в качестве сообщения, заданного по
х woman И ю.
перехват ИСКЛЮЧЭНИЙ производного класса
При попытках перехвата типов исключений, относящихся к наследуемому и name-
дующему классу, необходимо проявлять осторожность в случае использования one-
p;~1T0p0B catch. Это связано с тем, что конструкция catch ДЛЯ наследуемого класса
совпадает с такой же конструкцией для любого наследующего класса. К примеру.
поскольку наследуемым классом для всех исключении является класс Exception‘
следовательно, конструкции catch из класса Exception будут перехватывать все
возможные исключения. Конечно, использование конструкции Catch 663 аргументов
обеспечивает более «прозрачный» способ для перехвата всех исключений, чем опи-
сьпвалось ранее. Однако проблема перехвата исключений наследуюших классов
является весьма важной в другом контексте, особенно когда пользователь создает
свои собственные исключения.
Если требуется перехватывать исключения в наследуемом и Наследующем классах
одновременно, в последовательности catch наследующий класс будст первым. Если
жс этого не сделать, конструкция catch из наследуемого класса будет также выпол-
нять перехват во всех Наследующих классах. Этого правила следует придерживаться,
пбо помещение наследуемого класса первым приведет к созданию недостижимого
кода, когда Конструкция catch ИЗ наследующего класса никогда не будет вызываться.
В С# недостижимая конструкция catch приведет к появлению ошибки.
В следующей программе создаются два класса исключений, ExceptA И ExCeptB.
Класс ExceptA наследуется из класса Applicationlixception. Класс ExceptB
наследуется из класса ExCeptA. Затем эта программа генерирует исключения обоих
типов.
// Исключения наследующего класса должны следовать перед исключениями
наследуемого класса.
using System;
// Создание исключения.
class Excepw = Applicationmeption I
public ExceptA() : base() 1 I
public ExceptA(string str) : base(str) { }
public override string ToString() {
return Message;
// Создание исключения, наследующего исключение ExceptA
class ExceptB : ExceptA {«————————————————————{Нашшдшшниеисюшгюнияизьхсеёёё]
public ExceptB() : base() { )
public ExceptB(string str) : base(str) { )
380
Глава 10. Обработка исключении
public override string ToString() (
return Message;
ciass OrderMatters {
public static void Main() {
for(1nt х = 0; х < 3; x++) {
try {
if(x==0) throw new ExceptA("HepeXBaqeHo исключение ExceptA");
else if(x~—l) throw new ExceptB("HepexBaueHo исключение ExceptB");
else throw new Exception():
}
catch (ExceptB exc)
// Перехват Исключения.
Console.WriteLine(exc);
}
catch (ExceptA exc) { <—~—~——————————————
// Перехват исключения.
Console.writeLine(exc);
}
catch (Exception exc)
Console.writeLine(exc);
{<-
Порядок вызова этих операторов
Catch имеет значение.
{Aw
-.
Bonpoc. Поскольку исключения обычно проявляются в виде какой—либо
СЁЁ
ошибки, почему так важен перехват исключений наследуемого класса?
Ответ. Конструкция catch, перехватывающая исключения наследуемого клас-
са, Позволяет организовать перехват целой категории исключений, обеспечить их
обработку с помощью одного оператора catch. а также избежать Дублирования кода.
Например, можно создать набор исключений, описывающих аппаратные ошибки
некоторого рода. Если обработчик исключений просто сообщает пользователю о том,
что произошли аппаратные ошибки, можно воспользоваться обшей конструкцией
catch с Целью перехвата всех исключений данного типа. Обработчик может просто
отображать строку Message. Поскольку используемый в данном случае код будет
Оди наковым для всех исключений, одна конструкция catch может обрабатывать все
аппаратные исключения.
..=-:..- -.v.».'; \{(\\
PC3)/JIbTaT выполнения программы будет таким:
Перехвачено исключение ExcepEA
Перехвачено исключение ExceptB
System.Exception: Exception of type System.Excep:ion was thrown
at OrderMatters.Main()
Обратите внимание на порядок следования операторов catch. Этот порядок является
суцюственньцм Поскольку Исключение ExceptB наслсдшпея из исключения Ex-
CeptA, оператор catch для исключения Ехсергв должен следовать прежде оператора
M мим! и
перехват исключений производного класса 331
galich для исключения ЕхсерЬА. Аналогично, оператор catch для класса Exception
(Который является наследуемым классом для всех исключений) должен отображаться
последним. Если вы хотите проверить это утверждение на практике, попытайтесь
переставить местами операторы catch. B результате произойдет отдибка компиляции.
проект 10-1. Добавление исключений в класс
оиеие
И OExcDemo.cs
B ХОДС осуществления этого проекта будут созданы два класса исключений, которые
могут использоваться классами очередей, разработанными в проекте 9-1. Эти классы
отображают ошибки состояний, свидетельствующие о том, что очередь пуста или
заполнена. Данные исключения вызываются методами put () и get () B случас воз-
никновения соответствующих ошибок. C Целью упрощения эти исключения в раз-
рабатываемом проекте включены лишь в один класс FixedQueue, HO Пользователь
может легко включить исключения в другие классы очередей из проекта 9-1.
Пошаговая инструкция
1. Создайте файл QExcDemo . cs.
2. B файле QExcDemo . cs определите следующие исключения:
/*
Проект 10—1
Добавление обработки исключений в классы очередей.
«к
I
using System;
// Исключение для ошибок заполнения очереди.
class QueueFullExCeption : ApplicationExcep:ion (
public QueueFullExCeption() : base() ( ]
public QueueFullException(string str) : base(str) { }
public override string ToString() {
return "\n" + Message;
}
// Исключение для ошибок пустой очереди.
class QueueEmptyException : ApplicationExcept1on (
public QueueEmptyException() : base() { }
public QueueEmptyException(string str) base(str) ( }
public override string ToString() {
return "\n" + Message;
}
2
Исключение QueueFullExcept1On генерируется в случае, если предпринимается
попытка сохранения элемента в уже заполненной очереди. Исключение Queue~
EmptyExCeption генерируется в случае, ссли предпринимается попытка удаления
элемента из пустой очереди.
382 Глава 10. Обработка исключении?
xx‘
3. I/IJMCHI/ITC класс гшеооиеие таким образом, чтобы он генерировал исключение
в случае возникновения ошибки, как показано ниже. Добавьте сго в класс
QExcDemo.Ca
// Класс символов фиксированного размера, использующий исключения.
class FixedQueue : ICharQ (
char[] q; // Этот массив содержит очередь.
xnt putloc, getloc; f/ Индикаторы put И get
// Конструирование пустой очереди заданного размера.
public F1xcQQueue(;nt slze) [
q 2 new char[s12e+l]; // Выделение памяти для очереди.
putloc getloc — 0;
// Помещение символа в очередь.
public void put(char ch) {
if(putloc—=q.Length-1)
throw new ОщеиеЁи11ЕхсерЕ1оп("Максимальная длина " +
(q.Length—l));
putloc++;
q[putloc] 2 ch;
// Получение символа из очереди.
public char get() {
if(getlo :4 putloc)
throw new QueueEmptyExCeption();
getloC++;
return q[getloc];
}
Благодаря добавлению искчючений в класс E1.;<ec‘=Queue можно обрабатывать
ошибки очереди наиболее рациональным способом. Как вы помните, предыдущая
версия FixedQueue просто отображала сообщение об ошибке. Генерирование
исключений более эффективно, поскольку в этом случае код, использующий класс
FixedQueue, может еще и обрабатывать ошибки.
4. Для проверки возможностей обновленного класса FixedQueue просто добавьте
указанный ниже код шкасса QExcDemo в файл овхевето . cs.
// Демонстрация возможностей исключений, относящихся к очереди.
class QExcDemo {
public static Void Main() {
FixedQueue q = new FixedQueue(lO);
char ch;
lnt i;
try {
// Переполнение очереди.
Еог(1—0; i < ll; 1++) {
Console.Write("HoumTKa сохранения: " +
(char) ('A' + i));
q.put((char) ('A‘ + i));
Console.WriteLine(" OK");
использование ключевых слов checked и unchecked 383
}
Console.Wr1teL1ne();
1
catch (QueueFullException exc) (
Console.WriteLine(exc);
}
Console.WriteLine();
try {
// Попытка чтения из пустой очереди.
Еог(1—0; i < ll; L++) [
Console.Write("UouyqeHme следующего символа: ");
ch — q.get();
Console.Wri:eLine(Ch);
}
catch (QueueEmptyException exc) (
Console.WriteLine(exc);
Ъ
S. Для создания программы потребуется скомпилировать файл оЕхсоето . cs COBM6—
стно с файлом IQChar.cs. Файл IQChar.cs содержит код интерфейса очереди.
При запуске на выполнение программы овхсоешо получается следующий результат:
Попытка сохранения: А — OK
попытка сохранения: В — OK
Попытка сохранения: С — OK
Попытка сохранения: D Щ OK
HOUHTKB сохранения: E Ш OK
Попытка сохранения: F е OK
Попытка сохранения: G — OK
Попытка сохранения: Н — OK
Попытка сохранения: 1 — OK
Попытка сохранения: J ~ OK
Попытка сохранения: K
Максимальная длина 10
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
Получение следующего символа:
An exception of type QueueEmptyException was thrown
<‘.4r—4:IJC')“’1D'JU(‘)G13>'
Использование ключевых слов checked И
Unchecked
B коде выполнения арифметических вычислений могут возникать ошибки перепол-
нсния. Обратите внимание на представленный ниже пример.
334 Глава 10. Обработка исключений;
е;
byte a, b, result: f
a — 127; i
b — 127;
result = (byte) (a * b) ‹ .
B ЭТОМ примере произведение множителей а и b превышает допустимый диапазон 1„ „д
значения byte. B результате произошла ошибка переполнения. f
B C# можно задавать вызов исключений н случае переполнения. воспользовавшись I
ключевыми словами checked и unchecked. Для указания выражения, проверяемого ._
в случае переполнения, используется ключевое слово checked. Для игнорирования ‚д
переполнения используется ключевое слово unchecked. B последнем случае происдёд
ходит уссчение результата с тем, чтобы тип результата совпадал C типом Целевого} "
ВЫрНЖСНИЯ.
.. .- вы, ‚щ .
Y.
Оператор checked MMCCT две основных формы. Одна форма используется mm’:
проверки указанного выражения. Вторая форма используется для проверки блока
операторов.
cheCked(eXpr)
checkeu {
// Проверяемые операторы.
I
7
} .
B ЭТОМ фрагменте кода параметр expr определяет проверяемое выражение. Если при
вычислении выражения возникает переполнение, генерируется исключение ОЧег-“Ъ
flowException. '
Оператор unchecked имеет три основных формы. При использовании псрвой формы ,
игнорируется переполнение для указанного исключения. Вторая форма игнорирует Ё :
переполнение для блока операторов.
unchecked (expr)
unchecked (
// Операторы, для которых будет проигнорировано переполнение.
д
з г
В этом фрагменте кода параметр ехрк определяет выражение, которое не будет
проверяться на предмет возникновения ситуации переполнения. Если при вычисле-
НИИ Подобного выражения возникает ситуация переполнения, происходит усечение
результата.
Приведенная ниже Программа демонстрирует механизм использования ютючевых
слов checked и unchecked.
// Механизм использования ключевых слов checked И unchecked.
usLng System;
class CheckedDemo {
public static void Ma1n() { Ё
byte a, b;
byte result; 5_
a 1
b = 127;
Использование ключевых слов спескеЁ [unchecked 385
г
г u
_, »1l’cx_\-:n.1:11', npnucmmm K I1cpcn0_1HL‘Hm0. усскдшся J
AC};
.‘l_'.'-1'.'I Y LI, "
_.__.____ .‘
ПСНПС IIDHHOJIII k H.'HCp|IpO|5dHHI(7 ИСК РОЧСННЯ J
!x_‘Sd,;
_'v::sL „с . wr-:eL;11e(
‘ ch (L"»v»:rff.owE2:<:e:}‘-: ; on -.:x»:>
ilcpcxua-; ‚а: мшчеднн.
‚Де. Nr-L»:..;:1~:-to.
Рсъ‘. ibféll Bl>IIl().'IHCHll}I I1p0rpzi\1\n.1I
Hk,_ :«~:§»;;c:.m1f: p13?‘/J1b'l‘a‘L‘Z
5‘ ~ .-gvcri1owE:-:ccp;1on;
В \l)1'.' ВЫПОЛНСННЯ П[)О1"р21\1\11›( DC iyilblill llhl‘{HC.lClIH51 HCllp0BC]3$IC\10l() Bl)lpEl)KCHI15l
_\'CL“\.IClC5l. YICPCYIOIIIICIIIIC. B'L>Ii'L>llii\C\1()C Tlp()liCp5IC\11:I\I !3blD2l‘/kC}llrlC\1. ‘.l])1lB(),'l1lT К ГСПС-
pll}‘=I2’.dH1‘|l0HC)\_H0‘lClH‘l§1.
f:Z>L(/‘ H „Г“
В Г‚;‘С‚11›1‚1у1[1СЁ1 ПрОГрЦИЁЧС Г1()Ъ\‹15&П!О 11С11О.Н‚»3ОНННПС K.'IIO‘lCI5bl,\ СЛОН -.1
. . B СОСТНВС СДНПСЁГВСПНОГО l}h1pd)KC1lH$I. B C.l'L‘,‘l_VIOLIJ.Cl1 IlDOFpfl\l\1C ;lC\[()HCl’[)l«l—
p_\i:‘2(.‘7[ проверка И ()TC_\/TCTBHC ПРОВСРКИ 1.15! 6510101 опсрачоров:
шльзозание ключевых слвд СЙССгЗЗО и 1,'nchc»:‘~;eQ
2-‘ 4|] Кацап
— L;r1chec‘»:».:d( (Юные) (а * с-ъ ) ;
Consc-lc.WL;:eL;ne("}{.3;:p-.wacp;aeMb117: pc3;.J1L.-2a-_': r покинь;
-:.‘aec.~:ed «Q
386 Глава 10. Обработка исключе-
b : 7;
result — checked((oyte)(a * b)); // Все в порядке-
Соп3о1е.Иг;сеЬ1пе("Проверяемый результат: " + result);
a — 127;
b и 127;
resu;; — checkea((byLe)(a * b)); Н! генерируется исключение.
Соп5о;с.Иг;:еь:пе("Проверяемый результат: " - it); ’/ Не может
// выполняться_
1
catch (OverflowExcep::on exc) Ё
’/ Перехват исключения.
ConsQLe.WriteLine(exc);
I
PC'3_VJ] ьтат вы пол 1 гения програм мы:
Непровернемый результат: 1
Непроверяемый результат: 113
Проверяемый результат: 14
Sustem.OverflowException: Excep;;on of type
SysCem.OverflowException was Lhrown.
at ChackedBlocks.Main()
Как видите, в случае возникновения переполнения в пепроверяемых блоках про-
изводится усечение результата. Если же переполнение происходит в проверяемом
блоке, генерируется исключение. ‘
Единственная причинам в силу которой требуется использовать ключевые слова
снесет и цпсиесказ, Заключается в том, что статус переполнения проверяемый/не-
проверяемый определяется путем установки соответствующей опции компилятора, а
также с помощью самой среды выполнения. Поэтому в некоторых программах лучше
явно указать статус проверки переполнения.
ёгаеевга: ааафееееааааа --~» к
(3 Bonpoc. Когда следует использовать обработку исключений в программах?
Когда нужно создавать пользовательские, заказные классы исключений?
Ответ. Поскольку в C# повсеместно применяются исключения е целью
сообщения о возгтикаюгдих ошибках, практически все прикладные программы ИС-
пользуют механизм обработки исключений. Обработка исключений, основанная На
использовании встроенных исключений Cir, ЯВЛЯСТСЯ наиболее простой для освоения
г-гачингггощими программистами. Труднее принять решение относительно того, когда
и как следует использовать пользовательские, настраиваемые исключения. СУЩССТ‘
вуют два обших способа создания отчетов о возникающих ошибках: возвращаемые
значения и исключения. Какой же подход лучше? Несомненно, при программировг‘
нии на языке Ctr использование механизма обработки исключений должно бЫТЬ
нормой. Хотя возврат кода ошибки и может стать альтернативой в некоторых случат“
нсклпочения обеспечивают более совершенный и более наглядный способ обработки
ошибок. Именно этот сгтособ должны выбирать профессиональные программисты На
Cit, выполняющие обработку ошибок в евоих программах.
Контрольные вопросы
ё
ул
`!
387
КНКОИ КЛАСС ПНХОД!’ГГСЯ [Iii BCDIHIIHC HCDilDXI1l1 HCK.|lO‘lCHlll1'.)
Kp'dTK() OHHILWITC .VlCTO_'U1Ky MCIl()Jll')_’;()B'clHl'1}l OHCPZITODOB {Ty И СЕАССП.
Найдите ошибку в слсдупоцтехт фрагменте кола:
// ...
vais§18] 10;
catch (IndexoutOfRangeException exc) (
// Обработка ошибки.
1
Что произойдет если исключсъпис пе будет персхначстю?
Найдите ошибку п следутопнем фрагменте кона:
class A : Exceptlon ( ...
class B : А
ll ll
try {
//
1
catch (A exc) х
Catch (B exc) { ... Э
Может ли исключение, персхвачсннос внутренним блоком по—
вторно генерироваться в качестве исключения для внснтнсго блока
cat»:'r..’
Является ли блок Ежа} 1; последним фрагментом кода, вьтполняемьтм
перед завершением программы‘? Обоснуйтс свой ответ.
B упражнении 3 раздела «Контрольные вопросы» планы 6 был создан
класс Stack. Добавьте в этот класс заказные исшюченптя. которые
сообщают о состояниях пустого и заполненного стека.
Расскажите о назначении ключевых слов chec:~:ed и un-:hec}<e=:.
Каким образом могут быть персхкзачепы псе исключения‘?
Ввод! вывод
„д-.. M, . .-
.‚ 5,. ____.,. .
С! Поток
С! Классы потоков
:3 Консольный ввод/вывод
С! Ввод/вывод в файлы
С! Чтение и запись двоичных данных
С] Произвольный доступ к содержимому
файла
=3
Преобразование числовых строк
потоки ввода/вывода С# V у 389
J j7"“J‘J-I'.’7»
С g;1\101() начала этои книги в прот рамных Нсполшустси подсистема ввопа/тъьтпота C3
д 1-: _ . e.-L;:~.>_—(‘=. Олпако пока она пе бьтла четко оттпсатта, Поскольку при
Ьволс ‘выволе в Сд ътсттользуется иерархия классов. Нсв03\1о‚к1т() претсппштт. теорито
д тегалтт этои тюпсътстехтьт без прелвартттельиого описания классов. утеканиша
Н;уд‚тС‚!ОВ2\НЪ1$| и исктточениьт. Теперь мы готовьт к изучению лог! теуты. Как отмеча-
тост, за тлаве I, в C‘-1: ътсподтьзуткттея подсистема ввода/вьпзолзт и классы. опредеиеттттьте
gs L~[)L-,1c ,-NET }<1'amcw01'k_ Таким образом, оттисытзая подсистему‘ ввода/вьтвола в Сд,
„Ш Цмктиттескгт огптсьтвасьт подсистему! ввода/вьтттодат платформы ,1'\7ET.
B ; ;.:1sc расечатривьттотсэт утеханизхтьт консольного вводгт-"вьтвола и ввОДа/вьтвода в
q;.v.:1.:1.1, Следует у‘чес‘ть‚ -1'1 о попспстсутп ввода;вывола в Ст?’ является вссьуш развитой/т.
Вт мыс p11cc\121'1‘p111;;11o гся паиоолее важные и широко псптътьзуч-хтьтс IKUSUO/‘§\I|()C¥H этом
по ‘цпстехты. однако некоторые аспекты иеполт.ъоттлнттът \1c_x(11111s\121 пволпуттьтвола
‘етея изучать сауюстояте_'н‚тто‚ К тчгтстыо, полспстеута вво.тдтдчъьттъолдт C3 ловольтто
-.:=111:1. Как только вы пойутеге основы „поп подснст’ехтьт‚ освоить ;1c тали станет
3Н;1Ч:НС.ЛЬНО проще.
Потоки ввода/вьтвода С#
Up-'1"p£m.\1b1 C3 реализуют ввод/вьптотт е помощью потоков, Пол потоком понимается
выпи; либо получение информации. Поток связан с физическим устройствои гто-
ерт гном системы ввода/вьпзктдта Сд. Все потоки велут себя одинаково. в то вреиэт
как реальньтс физические устройства, которые с ниии связаны, су-тпеетвепно отли—
чазогея друг от друга. Поэтому классы и методы ввола/вьтводтгт ттогут применяться
совттссгно с устройствами многих типов. Наприттер. хтстолы. используемые ‚тля
ВЬт-тла на консоль. иогут также пспользоватьсэт для записи лапных в 1[):'111;1 па лискс.
БЗЙТОВЫЭ ПОТОКИ И ПОТОКИ СИМВОЛОВ
На сапом низком уровне все операции вволгтчзьтвода в C33 оперируют баптахттт.
Пи тобныи подход имеет смысл, поскольку большинство у=еттэ‹›ттс‘гтз. прслназттаттеъттт ых
‚и, выполнения операций ввода/вьттъоцат, являются баттгиорттетттьтрованттьтмтт. Однако
J1-‘.‘.L>i\1 при общении с коутпьютероут удобнее исттользовать сиивольт. Напоутттттхт, что
П ".13 тип данных char имеет разрялттостт, 16 битов, а run ,lE1HHL>1\ 2:-gt: является
57 итовым. Если применяется набор сиупшлотз ASCII. i10B().'H>l~lO прое го вьптолнттть
Ц*‹›бра3‹›тзагттте между‘ типами >; при )10.\1:L:1s1 величины типа ' « H_\-'>1<11o
' "прировать старший байт. Латп-тьттт приехт пе 110:1\0,111'1 при работе с оегильттьтмтт
Сихитодтахтът Lnicodc, которые l1Cl'I()flb}} тот оба банта. Поэтохту‘ баитовые потоки не
1:. дтпс удобно 11cr1o;11,3m1;1 гь при BblI1().lHCHH1l c1z1\11so_'11)1—1o1o виола, вывода. ‚Ътя реше-
'5»:'.' пои задачи в C3? определены несколько классов. преобразу ЮШЪН баитотзьтй поток
1 .uF()K сиившотз. то есть автохтгггичеектт преобразуютиттх ланные т ппа C3119 в данные
ж: ЁГЁАХ и наоборот.
Предопределенные потоки
I
I :1 ттрслопутелелеттттьтч потока прелс- ..2.1c.=1:.1 сиопсгвакттт т а - - е
‘т т я 31111101<>1<11:1':c""1:' ^ '_1'1 11cm 11[70:p.’1\1‘.1. использутопттп простран-
H» - м Поток ; m c«.m11:1'1r;111\'c1 L'F.l||,'L'lD£'Il()\l_\ 1'm1'o1<y111.11:o.'1z1.
Глава 11. BB011/Bb|3°;_
,\,o;maHmo ЭТО консоль. Так. при вызове метода 'I,‘onso1e.'w‘r1~eL1,ne() инфорд;
пя автоматически направляется в поток C-:.-nsol е; сип. Поток C-3-nsole . In связан?
3 „шдартнькм устройством ввода„ которым по умолчанию является тсчавиатурад _
mm эти потоки могут быть перенаправленьт на любое соответствующее устройство
щд/вьттзода. Стандартные потоки являются символьными потоками. Поэтому они
тьпштот и запнсьтвают символы. .
flvfzxflymfle/:7 практикум
1, Что в C17 понимается под потоком?
2, Назовите типы потоков C11.
3 Какие потоки являются предопределенпьтми?
[ССЫ ПОТОКОВ
Cr: определяются классы байтовых потоков и классы потоков символов. Однако? '
mum потоков символов в действительности представляют собой оболочку, котор .-° д‘
собразует передаваемый на более низком уровне поток байтов в поток ситиволов "
тичсм выполнение этого преобразования происходит автоматически. Таким o6pa-if
M, символьные потоки построены на основе байтовых потоков и являются ‘
гическим дополнением. в `
ге классы потоков определены в пространстве имен System.Io. Для применений]
их классов в начале программы обычно помещают следующий оператор: " '
ада
inr; Syste:n.IO,- Е
к
an.
CT HCO6XO£1l/IMOCTI/I B yKEl3aHl/H4 КЛЭССВ System.IO ДЛЯ КОНСОЛЬНОГО ВВОЦЗ/ВЫВОД
ЛИСС C3.'.'S3le Ol'lpC£l€flCH B пространстве ИМЁН System.
пасс Stream
ОТОКИ в Ci? создаются с помощью Ютасса System. IO. Stream. Класс Stream представ‘
«ст байтовые потоки и является базовым классом для всех других классов потоков. Он
доке является абстрактным. поэтому не существует реализации объекта Stream, Класс‘
Ere ш определяет набор стандартных операций с потоками. В табл. I 1.1 описанб {
CCKOIIDKO рЭСПрОСТрЭНСННЫХ МЕТОДОВ. ОПрСДСЛЁННЬЖХ В КЛЗССС 5t‘C€E:.IT‘\.
т;
‚вяжете; з».
ё:
>3:
абпица 11.1. Некоторые методы, определенные в классе Stream
V‘eTon. Описание
Jcld C1ose() Закрывает поток
“Old F1ush() Записывает содержимое потока в физическое устройство
Ш‘ ReadB:xte() BO3Bp8L1J.aeT целочисленное представление следующею
доступного байта в потоке ввода. По достижении конца ‘Ё.
файла возвращает »1
„к ——-—:———"" .
[ под UOTOKOM B C3 понимается вывод либо получение данных.
2- В С“ СЕТЬ два типа потоков — байтовые и ПОТОКИ символов.
3 Предопределенными потоками являются потоки Console . In, Console . out Н Console . Error-
классы потоков 391
“’__'_"
amp, Описание
„д Read(byte[] buf, Осуществляет попытку чтения numBy2‘es байтов из потока
А '_:1t offset, int numBytes) ввода и помещает их в массив buf, начиная с элемента
buf[offset]. При этом возвращается количество успешно
считанных байтов
‘Hg Seek(long Oflsefl
—_ekOrig1n Ofighn
Задает позицию текущего байта в потоке, используя значе-
ние смещения ог/зеготносительно позиции orig/n
ч нгцевупе (byte b) Осуществляет запись одного байта в поток вывода
_.,._; 'AIr1te (byte[j бит‘, Осуществляет запись numBy!es байтов в поток вывода из
г offset, int: numBy!es) массива buf, начиная с элемента bufl offset]. При этом
возвращается количество записанных байтов
Как правило, если появляется ошибка ввода/вызова, зтетоды, приведенные в табл. I 1.1,
генерируют исключение товхсерыоп. Если предпринимается попытка выполнения
недопустимой операции (например, попытка записи информации в поток, предна-
значенный только ДЛЯ чтения), генерируется исключение NotsupportedE:-:::eptLcn.
Обратите внимание на то, что в классе stream определены методы. Предназначенные
для записи и чтения данных. Однако не все потоки поддерживают н запись, и чтснис,
поскольку можно открывать потоки, преднвзнвченньте только для чтения или только
для записи. Также не все потоки поддерживают позиционирование методом Seek и .
С цел ью определения возможностей конкретного потока используется одно или
несколько свойств класса Stream. Эти свойства приведены в табл. 1 1.2.
Таблица 11.2. Свойства, определенные в классе Stream
Свойство Описание
b0-IL CanRead СВОЙСТВО ИМЭЭТ ЗНЗЧЭНИЭ true, GCHVI ВОЗМОЖНО чтение ИЗ ПОТОКЭ.
Свойство предназначено только для чтения
ЁСПЪ Canseek СВОЙСТВО имеет ЗНЗЧЭНИЭ true, ЭСПИ ПОТОК ПОЗВОЛЯЕТ задавать ТЕКУ-
ЩУЮ ПОЗИЦИЮ ЭПЭМЭНТЗ. СВОЙСТВО ПОЭДНЭЗНЗЧЭНО ТОЛЬКО ДЛЯ ЧТЭНИЯ
под Canwrite СВОЙСТВО ИМЕЕТ ЗНЗЧЭНИЭ true, ЕСЛИ ВОЗМОЖНЭ ЗАПИСЬ В ПОТОК. CHOW-
CTBO ППЭДНЗЗНЗЧЭНО ТОЛЬКО ДЛЯ ЧТЕНИЯ
lcng Length Свойство указывает длину потока. Свойство предназначено только для
ЧТЕНИЯ
lcng Position Свойство представляет позицию текущего элемента потока. Свойство
ПОВДНЗЗНЗЧЭНО как ДЛЯ ЧТЕНИЯ. так И ДЛЯ ЗЗПИСИ
Классы байтовых потоков
Из класса Stream наследуются три ктасса байтовых потоков. перечисленные ниже.
Класс Описание
Ёц t fe redstre am Обеспечивает буферизацию байтового потока. В большинстве случаев
буферизация позволяет увеличить производительность
Байтовый поток, предназначенный для осуществления операций вво-
да/вывода в файл
Байтовый поток, использующий память в качестве хранилища
Ё!Ъе$сгеаш
Метогузггеат
-„.вц„_;_;„.г
‘Н
392 Глава 11. Ввод/выв.„
j*“"“ ’ jg
Программист может определять и собственные классы потоков Однако для n011aB_~,._‘
ляюшего большинства приложений вполне достаточно встроенных потоков. -
Классы потоков символов
В верхнеи части иерархии символьных потоков находятся абстрактные массы
йидхсьеаае; и Техьнщсег. Методы, опрсдслснныс в этих K.'IdCCdX, обеспечиваю;
хншпмгпьътьтвт набор функций ввода/вывода для всех символьных потоков.
mm
B табл. 11.3 приведены методы ввода класса TextRea-der. Обычно в случае возник.
новснпя ошибки эти методы генерируют исключение IOE:~:c—3pt;!:-:1. (Иногда возмож. ‚д
но возникновение других исключений.) Особый интерес представляет метод Read-‘
..J_AV‘.K'_E т) , которыи полностью считывает строку текста, возвращая ее в виде строки Ст:
(‚шинных типа st:‘;.'1g). Этот метод удобно использовать при чтении данных из потока
ввода, содержащих пробелы. -
4
.’sc’,“"g
Таблица 11.3. Методы ввода, определенные в классе ТехтНеасег
Метод Описание
‘.'Old Close () Закрывает поток ввода _
im: Peek () Получает следующий символ из потока ввода, но не удали-еду д
ет его. Возвращает -1‚ если нет доступного символа яд f
mt Read() Возвращает целочисленное представление следующегоъ?! 3
доступного символа из потока ввода. Возвращает -1 в '
случае достижения конца потока д?‘ д у
in‘: Read (char (1 buf, Осуществляет попытку чтения питС/тагз символов из no-3: 5'
int offsef, тока ввода и помещает их в массив buf, начиная с элементёд ` -
дпй numchars) bL/f[offset]. При этом возвращается количество успешна
считанных символов I
int ReadBlock (char [] buf, Осуществляет попытку чтения питспагз символов из под
int offset, TOKa ввода и помещает их в массив buf, начиная с элемента '5 I
in t numchars) buf [ offset]. При этом возвращается количество успешно д
считанных символов -
5 с к ing ReadL1ne () Считывает строку текста и возвращает ее в виде строки C#.:_
Нулевое значение возвращается в том случае, если быпддё g
считан символ конца файла 7. ‘
Strlng ReadToEnc1() Считывает все оставшиеся символы потока и возвращает “
их в виде строки С#
В классе Техгиыгег определения различные версии методов ’m:1te() и шипе-д
дне () . которые позволяют выводить данные встроенных типов. Ниже приводитсяр.
лишь несколько перегруженных версии ЭТИХ методов.
МЕТОД Описание `
тог-д Wr1te(i:1t Val) Запись значения типа lnt
‘ac: d Wr1te(C10uble val) Запись значения типа double _
var; te(b0o1 val) 3al'1VlCb значения типа boo]. {Ё
:3‘ w:-i1:e;j пе (st:-mg val) 3aI'1|/1Cb строки, завершаемой символами перехода на H0‘ '7 j.
вую строку =
1'01-j Wt: :e.'-;ne (щи: val) Запись значения типа ьцпт: И символов перехода на H0‘ Ё
вую строку .
‘ест d Wm I:eLLne (char val) 3aI'1VlCb символа, за которым следуют символы перехода
на новую строку
Консольный ввод/вывод 393
и
В дополнение к методам 'w‘rite() И нынешнем В классе Техсигтсег также
опрсдсцтеттьт МЕТОДЫ C1ose() И F‘luSh()?
„д: ;ua1 vuld Close П
._д;‘ на; void Flush()
\]gr0,'1 Шва: т) используется run вывода на физический носитель данных„ которые
остались в буфере вывода. Метод С1т-5с (2 закрывает поток.
Классы Tc:-Lt ‘г -"Le: и Te:v.LL‘5L;:e: рсалттзутотся с помошьто псрсчнслснттьп ниже
K.lilL_L'0|i СИМВОЛЬНЫХ ПОТОКОВ. H03'ro\1y ДНННЫС ПОТОКИ llO,'UlCp')KHB2[l()T .\lCTO[1bI ll
(_-n()llC'lBEl. OIIDCHCIICHHDIB B КЛЕТССИХ ’['eXtRe.a-jer И Te)-2CWriEF:-E.
Класс потока Описание
5; .'.3a“.1Reader Считывает символы из байтового потока. Этот класс является оболоч-
кой байтового потока ввода
5: aznwriter Записывает символы в байтовый поток. Этот класс является оболочкой
байтового потока вывода
5;› ngReader Считывает символы из строки
s: т ingwrlter Записывает символы в строку
двоичные потоки
В дополнение к байтовым и символьным потокам в Ci? определяются два двоичттьтх
класса потоков, которые могут использоваться для непосредственного считьттзгтнттхт и
записи двоичных данных. Эти потоки называются втпагуваагге: и зщагдчсгцег.
Они подробно раеемгттриватотсгт далее в этой главе при оттисании механизмах вво-
да ‘вьпзода в двоичные файлы.
Тсттсрь, когда вы получили обшес представление о структуре подсистемы ввода/вви-
вода в Cd, приступим к детальному изучению компонентов этои структуры. Сначала
рас мотрт-т м подсистему консольного ввода/вьпзода.
Минутный тракте/кум
` 1. Какой класс находится в верхней части иерархии потоков?
Ix)
Назовите свойства класса 51геьт.
Lu
КНКИС КЛНССЫ НИХОДЯТСЯ вверху иерархии КЛЭССОВ CHMBO.-'ll>HblX ПОТОКОВ?
КОНСОЛЬНЫЙ ВВОД/ВЫВОД
.‚
Мптеольньти ввод/вьшод реализуется с помощью стандартных потоков -T-; 23:: 1 . 1:1,
' " И C-c.r.sole.Error. Консольный ВВОД/ВЬПЮП рассматривался в главе 1.
полому вы е ним уже знакомы. Как будет показано далее. этот ввод/вьптодт обладает
HQ-K0 ГОРЫ V1“ ДО П OJ H ИТСЛ ЬН Ы M H ВОЗ МОЖ НОСТЯ \1 и .
1' B ВСрХНСЙ ЧЕСТИ иерархии ПОТОКОВ НЭХОДРГГСЯ КЛЭСС Stream.
2‘ ЭТО СЛСДУЮЩИС свойства: Canseek, CanRead, Canwrlte. Length и Pos;L;-on.
3 ВНСрХУ иерархии КЛЭССОВ СИМВОЛЬНЫХ ПОТОКОВ НЭХОДЯТСЯ КЛЭССЫ TCxtReaC1er И TeXtWr;Ler.
394 Глава 11_ Ввод/выа
Однако, прежде чем приступить к изложению дальнейшего материала, следует еще;
раз подчеркнуть следующий момент: большинство реальных приложений C1; H..‘
ЯВЛЯЮТСЯ КОНСОЛЬНЫЧИ ||р11.`1())КСНПЯ\|!/1„ ОРИСП!ИрОВЦНПЫЧН Hd ОбрНбОТКу Тскстай
В данном случае речь идет скорее о трафичсских программах либо компонентах;
использующих оконный интерфейс для организации взаимодействия с пользовать ‘ч:
лем. Поэтому в Сгг-программах не столь [широко применяется консольный ввод/вы. =_
вод. Хотя программы, имеющие интерфейс в виде текстовой строки, представляют
собой прекрасные учебные примеры и могут пригодиться в качестве компактныхд
утилит, они не подходят в качестве регьтьньтх коммерческих программ. ё ‹
Чтение ДЗННЫХ С КОНСОЛИ
Поток CQnso1e.In ЯВЛЯСТСЯ экземпляром класса Текггдэдег и для получения:
доступа к нему прсдътазътачсътьт методы и свойства. определенные в массе Tex
tReader. Однако обычно используются методы класса Ссчп5-31е‚ с помощью которых
выполняется автоматическое считывание ‚данных из потока C-:1ns3le.In. B кдасееЁ
Console определяются два метода ввода: F<»:a~.:i() И веаёьдпе ( ).
Для чтения одного Символа применяется метод Него? () .
static Ln: Read()
считанный с консоли. Возвращаемый символ имеет тип int. И требуется его,
преобразование в тип char. При возникновении ошибки возвращается значение —1 `
Если возникает сбой, метод генерирует исключение :0-Ex-.:
Для считывания строки символов используется мстод F¢ea*iL1'n»3 () :
static str;ng ReadL1ne()
Этот метод считывает введенные символ ы до тех пор, пока не будет нажата ктавиша
[Етег]. Считанные символьт возвращаются в виде объекта типа stri. При сбое
также генерируется исключение гдгхсертдсъп.
Ниже приводится ятрогргтмма, демонстрирующая процесс чтения массива символовдс:
из потока Console . In.
// Ввод с консоли посредством метода ReadLine().
using System;
class ReadChars { 4
pub;ic static void Ма1п() { 3
str;ng sir;
ConsOie.WriteL;ne("Bseumwe несколько символов.");
str 2 Console.ReadL1ne();
ConsoLe.WriLeLine("Bm ВВЕЛИ: " + s:r);
}
РСЗУЛЬТНТ ВЫПОЛНЁНИЯ ПрОГрИММЫЗ
Введите несколько символов.
Это гест.
Вы ввели: Это тест.
Консольный ввод/вывод 395
Несмотря на то что методы ктасса шелесте рсаализупот наиболее простой способ
Чтения потока Console. In. можно вызывать мстопьд находящиеся на более низком
урдшю и в классе . ехцнеаоех. H иже приведен текст прсдыдуьщеи программы,
„Срсписштятоьт с использованием методощ определенных в классе Te:-:t'.~Tea:ier.
/„ непосредственное использование потока Console.In для чтения массива
ддйтов C клавиатуры. */
;g System;
,5 ReadChars2 {
дч11с static void Main() (
sLring зак;
Сопзо1е.Иг1сеЬ;пе("Введите несколько символов.");
HCIIOCDL‘,-ZIC'I‘l5CHHOC ‘ПСНИС И.‘ ПОЦОКЦ
„и х Cons0le.1n.ReadLine(); 4-—~—————-——~Cwq0l€ Ш
Console.WriteL;ne("Bm ввели: + sir);
Х
J
ОбрЦТИТС ВНИМЗНИЁ на ТО, KE1KBbI3bIB8BTC$I МСТОЦ ReadLine () ДЛЯ ПОТОК?! COUSOl€ . In.
Вывод данных на консоль
Потоки -:’j.3nso3_e.Out И C-:>ns:.ie.Error являются объектами типа Te:~::wr:,- tier.
Вывод Данных на консоль Проще всего реализовать посредством уже знакомых вам
методов имеем и игцеыпеи. Различные версии этих методов Предназначены
ДЛЯ вывода данных каждого из встроенных типов. В классе Cc-r.s«:'.-le определены
собственные версии методов Wr1te() и иысеыпе (). поэтому их можно вызвать
Непосредственно через ЭТОТ класс. Однако Эти (и другие) методы можно вызвать и
через класс Textwriter. который находится на более низком уровне.
НПА«3ПрИВ0ДИТСЯ программа,демонстрируклиая ВЫПОЛНСНИСЗЗПИСИПЗПОТОКИ Con-
вши: И Conso1e.Error.
Запись данных в потоки Console.Out И Consolc.Err0r.
354%} System;
5,~ss Errout {
Dubllc static void Main() (
1n: a:10, b=O;
;nt result;
Conso1e.Out.WriteLine("fenepupoaahme исключсния."\:
try i
result — а / b; // Генерирование исключения.
Catch(DiV;deByZeroExcept1on схс) 1
. . . Записьвтнпоки :onso1e.0ut
Conso;e.Error.Wr1teLLne(exc.Message);<————
MConsole.Hrron
Г*"
Класс Fi|eStream И байт-ориентированньтй
ввод/вьтвод в файлы
396 паза; 11. ВВОД/вьт :. `
Гоэаерирдвд т т ‚то ис ' '
X’Iu1__..Ag<a деления на н
Пногдтгт ттользоттачелтт. нчетоттттте небольнтон оттыт‘ в нроттэахтхтттроватттттт. не мо д
рсттнпь. когда следует нрнхтсттэгть ноток гг л- Err:-z ”0CK().Il,K'\' потоки CO5’
.2: AK: ад И и ‚ г д, т; г‘ тто ухтолчаттттто осутттсстттлятот вывод на коттсоль, заче
нужны дтва разлнчттьтх потока’? При ответе на этот вопрос елслует учесть тот факт”
что стандартные нотокн хтожтто нсрсттаттравтттт. на лруттте устройства ввола/ттьтво ‚‹
Напрттхтер. еолержтптос нотока V
. т \то:›кно нанравтттт, вместо экрана
файл на ДПСКС, 1отла сообщения об отннбках ттьтволтт. нанрнхтер. 6_\:1_\'r таттиеаны д
журнштьттьттт фанл. а обычные сообщения прот рахтхты отобра ъятся на )краттс_ Кстати‘
если вы вол на коттеоль IlcpclI:lI'IpuH,lcH, а ттоток отттнбок нст, то сообщения об онтибка
иожно уНПЦС-ГГ: на коттсолтт во ире \тя работ ы нрот рахтхтьт. 1!роттедтура нсрснаттратзлени
ттотокотз бутет расехтотрстш ластец ноеле оттнсаттття операции вволат/ттьтвола в файлы
В СЙ подитержтнъатотея классы. обеспечнтттттонтис опсраттитт чтеттттгт/заттттстт дитя файле
Конечтто‚ наиболее расттроетраттснттьтхт тттнохт файла является лнсковытт файл. ’~
уровне операттттоттттотт систехты все фанлы расехнттрттватотся как лвоттчттые (Ьаттльт. Ка
тт елштовгьтк) ожидать. в CI: IlO,L'h‘|)>Kl1B£lK)lC$l хтегольт чтения и запнен бантов при?
работе е фаицтгт шт. Поэтому достаточно широко распроетраттетты операннтт чтения/за
обеспечивается Заключение Q>?1i71.1(m0I‘() ll()l()K2l, 0pnCH'rHp0uaHlIor0 на использовани
байтов в енмттольттьттт поток. Операции с символьными файлами удобны, eon‘
ванне баитотъ.
Для еотлаттття батповото ттотока_ евятанттоттт с файло\т_ используется класс Р11е
згтемт. ‘Этот класс наелсттуетсьт на класса - мг: и поэтому нчеет все свойства,
присущие классу :.' т. ‘
Почните. что классы потоков (в то\т числе и класс э г‘) оттрсдтелсттьт В\`_
п -
густа . Почпохту обычно в начало лтоботт ттрот‘рьт\т\тьт‚ прнхтеттятоцтстт классы .-_
потоков, включасчся коттструкттття, `
Ц5-П
Щ
S»s;er.ZG:
Открьттие и закрьттие файла ц
При соутатттттт батттотъетто потока„ сттяъгтнттотсэ с файлом. требуется сфорхтироваТЪЁ
объект „ ‚ед: г»
ф
_~:. Из зтпто обьекта наслслуется несколько коттсттэук торов. Наибо"? .
„ТСС ‘ШСТО 1|С1|()_П-$)`С`1С5| C.lC.'1_\'}()|Ill1il K()lICI‘p}l\I()pI
v
4
E‘; L<:S:rca:::t_:=:1,
IJIL‘ arr \'K;1u,1n:1c1 mm открыштс\1‹›тотрлнла. которое хтожет mu ОЧЦТЬ ттолттосГЬЮ
оттрсгтслеттттьттт путь к нсхтх: Нарахтетр оттрецтелътст. какнхт образохт огкрьтттастсЯ’
Класс Filestream И байт-ориентированный ввод/ВЫВОД В файлы 39'.
фдпл. Здесь необходимо указать одно из значений, Определенных в персчислснит
. (Ее. Эти значения описаны в табл. I 1.4.
Ecjtn попытка открытия файла была неудачной, генерируется ьтсклпочеттис. Если фей.
Нс цОЖСТ быть открыт, поскольку он не существует, генерируется неключени‹
‚. “е:ЁоипСЕхсер:1ощ
таблица 11.4. Значения перечисления FiIeMode
Едёнение Описание
емоое .Append выводимые данные добавляются поспе данных, находящихся
в файле
г; je:V.cde . Create Создается новый выходной файл. Любой ранее созданный
файл с аналогичным именем уничтожается
г; .eMode .CreateNew Создается новый выходной файл
г; _еМос1е . Ореп Открывается существующий файл
51, „мосте _openorCreate B случае наличия файла происходит его открытие, если файл
не существует, он создается
э‘; :;=..\’.ode . Truncate Открывается существующий файл, но его длина усекается до
ноля
Если из-за ошибки ввода/вывода файл не открывается. генерируется исключеник
IC“7:-/_::epL;C»I‘.. Другие возможные исключения: ArgumentNullE:xcept ion (имя (райл:
вьтргтжлется нулевым значением), ArgL:mer.tException (параметр, задающий рсЖИА
открьттия файла, некорректен), Secumtylzxception (пользователь не имсст прат
доступа) и D1 reCtoryNotFoundExCept1on (указанный каталог не существует).
Ниже демонстрируется способ открытия файла test. е: пл я ввода данных:
P_-ciLream fin;
_н new FileStream("Lest.dat", FileMode.Open);
ca:ch(FileNotFoundException exc)
‘“us0le.Wr1teL;ne(exc.Message):
.c:urn;
mi д {
Lsole.WriLeLine("fieuoamoxuo открыть файл."):
2oLurn;
Здесь первый оператор catch перехватывает ошибку. связанную с иевозможътостьк
обнаружения файла. Второй оттератор catch обеспечивает‘ перехват‘ всех остаЛЬНЬП
исключений, связанных с выполнением файловых операций. Часто имсст Смысл орга
Пшовать вьщачу сведений о каждой ошибке, что позволит располагать более точными
свсленизпхти о характере возникшего затруднения. Для простоты изложения в расемат
Гдщлсхгнях примерах перехватывается лишь исключение FileNoLF‘oundExcept1on.
КАК уже упоминалось, только что описанный конструктор Еддезпгеат открывас’
фаил mu чтения и записи. Если доступ необходимо ограничить только чтением или
“Ыько записью, укажите строку следующего вида:
FiieS:ream(sLr1ng filename, FileMode mode, F1leAccess how)
398 Глава 11_ Ввод/вьтдод;
“"—‘**'“‘—“”“‘”“‘"—““‘*—“‘*"——-.;
Ka1< и прежде, параметр fj lename указывает имя открываемого файла. а параметр
теще определяет режим открытия файла. Значснищ передаваемое параметром how Ё‘
задает способ доступа к фаилу. Здесь может фигурировать одна из указанных ниж; =_
величин, заданных псречислснисм Г11еАСсе5$2 `
Е11еАссе5з.Неао Fi1eAccess.Wri:e F;;eAcc0ss.ReadWriLe
Например следующая строка открывает файл только для чтения:
F;:eStream fin — new FLleStream("te3L.dat", F1leMode.Up€n. F;leA:cess.Read)
Если работа с файлом была завершена. необходимо закрыть его„ используя метод.
Ciose () : - ‘д
void Clo5e()
При закрытии файла происходит освобождение системных ресурсов, вьтделеттньтх для
файла. С этого момента они становятся доступными при обработке другого (райлад
Метод Close () может генерировать исключение говхсерыоп.
О
Класс Filestream определяет два метода, которые могут считывать байты из файлай
Чтение байтов c помощью класса Fi|eStream _
т?
Неаовупеп и F<ead()_ для считывания одного байта из файла воспользуйтесвг '5
методом Ёеаёвупе () ‚ общая форма которого приводится ниже: ‚у:
int ReadEyte()
Каждый раз при вызове этого метода из файла считывается один байт‚ который
возвращается в виде целочисленного значения. Если при этом встречается признак ..
конца файла. возвращается значение -1. В случае возникновения ошибки генерируд Ё‘
ется исключение 1ОЕхСерп1оп.
Для считывания блока байтов воспользуйтесь метолом ага 1 ‘э :
int Read(oy:e[] buf} in: offset, inL numBytes)
Метод Reaii () предпринимает попытку считать байты. количество которых оттределяется
. ‚ .. хм
параметром пытвугез. из файла в буфер 13211. начиная с позиции. заданной параметром г’ ;
off set, При этом возвращается количество успешно считанных байтов. В случае в д
возникновения ошибки ввода/вьтводя1 генерируется исключение Юдхсершоп. Возможп ’
ны и другие истечючения, в том чище исключение NOt.SL1ppOrt,edExCeptiOn, генерирУ-д
емое в случае. когда операция чтения не поддерживается потоком.
д;-
В следующей программе используется метод ReadByt,e() для ввода и отображения; д
содержимого гскстового файла. Имя которого указано в качестве аргумента команда; '
Ной строки. Обратите внимание на ‘го. что блоки пгу-‘сдг “*1 обрабатывают 111353‘
оцтибки. происходящие при нервом запуске программы (указанный файл не найден; _
либо имя файла не указано). Аналогичный подход может применяться всякий рЭЗд}
когда используются аргументы командной строки. ` `
:’
93
/* Отображение файла. Д
Для использования программы укажите имя файла, -
содержимое которого будет отображено.
Например, для просмотра содержимого файла TEST.CS Q
ч \_4 щ ы '
воспользуитесь следующеи командном строкои:
ShOwF;le TEST.CS j
класс Filestream И байт-ориентированный ввод/вывод в файлы 399
цдпё ЗУЗЁЕШЁ
дддд System.IO;
ShowFile {
puoiic static void Main(stringi] args) {
А ‚пе 1;
Ellcstream fin;
Lry t
еды — new FileStream(arqs[R], F;LeMoce.Opcn);
catch(FileNotF0undExCepL1on cxu) f
Console.WriteL1ne(exc.Message);
return;
catch(IndexOutOfRangeExcept1on ехс) {
Console.WriteLine(exC.Message + "\nUsage: ShowF1ie File");
return;
f/ Чтение байтов, пока не встретится символ EOF
do [
try {
i = fin.ReadBy1:e () I
} catch(:OExcept1on ехс) {
Console.WriteLine(exc.Message);
return;
if(i Es —1) Console.Write((char) i);
Ё While (1 Е‘ '1) 7 flEc3|n 1 равно —l. достигнут конец файла.
fin.C1ose();
Запись в файл
Для записи байтов в файл воспользуйтесь методом Шгфтевугё- () :
Уе_д WriteByte(byte val)
Эпп метод записывает байт, указанный параметром val. B (райл. Если при шппси
ВОшпкла ошибка, генерируется исключение 1C‘:;>:r;ep:;::.r. Если поток вывода не
Шкрьтт. генерируется исключение NOtSuppDttedE:(Cept.1-3:1.
Вантовый массив может быть записан в файл с помощью метода игц -():
Ln: wr;te(byte[] buf, int offset, int numBytes)
§§??8§§??é! i.§@&$€€€£!@§:‘§fi$
® Bonpoc. Уже отмечалось, что метод Pea зги ) возвраьцает значение -I по
1--
ДОСТИЖСНИИ конца файла, но при этом отсутствует спсциачьное вознргнцасхтос
дпачение в случае возникновения ошибки. Почему’?
Ответ. В CL‘ ошибки обрабатываются е помощью иеютючений. Поэтому, если
МЩОД ReadByte () ЛИбО другой метод ввода/пьшода возвращает значение, это озна-
‘MOT, что ошибка не произошла. При этом обеспечивается более «прозрачный» путь
Обработки Ошибок ВВОЦЗ/ВЬШОЦН, чем в случае возврата специальных кодов ошибок.
___,_5,_ :__:__ ‚дм, . _ › › _ ,„.м„‹мт=-...‚.-=....‚ ‚‚‚‚ ‚‚ ,.‚ .. .;. . -.-- т . . ¢.- -- „Ч г- - ‚п .
400
Метод 'wIr;t:? (т записьттъает‘ в файл количество байтов. указанное параметром num- _
начиная с нозитттттт. которая задана е помощью тшраметдъ 9'
3::-Les. из массива д: 25.
ь. При этом возвращается количество записанных байтов Если в процесс,
записи ттроттехолтгт отиибка. геттерпрустся ттст<:тт‹)че!—тттС '
вьтттоцтт не открыт, теттерирдетстт исклточетттте
возникновение и лрутих истетточстттттят.
‚ -. [Возможно
д '.;::-i.- ~ 9:’
Навсрттое. вы знаете о Том. что ттри вьтттолттетттти вывода в файл часто даттнтэтс не
заттисыттатотся сразу же на физичес кое устройство. Вместо этот о вы ВОЛ буферизуется
операционной системой до тех пор‚ пока тте накопится Достаточно больтттотт фрагмент
Данных, которы и сразу же заттисывгтстстт На ‚тиск. В результате возрастает производи-
тельность системы. Натпричер. запись дтаттттых в тратта Ha диске оеутттесттъляется по
секторам. размеры которых варьирутотстт от I28 байт и болытте. Результаты вывода
ттрогтэамхтьт обычно буферы тутотся до тех Hop, пока на писк не может быть записан
ттельттт сектор Однако если требуется, чтобы данные заттттсьтттгтдтттсь на (ризическое
устройство независимо от стеттетти загтоцтттеттття буферам мо/кно воеттоцтьзотзгггься ме-
“тодом FL; ‘ '
пои: L-‘lush ()
Если ттри ттьтттолттетттттт этом оттертттттттт возникает отттттбкгт, генерируется иеклточет-тие
1.,‘-E;‘xCr::r;.t 13:.
По завсртттетттттт оттератттттт вывода траттлтт ттсобихтимо его ЗПКРЫТГд восттоцтьзоттгтвтттись
методом -C:,_ з г т, При дтохт таратттщтуетсзт. что лтобые выводимые ‚тттнттьтс, находя-
тттиеся в буфере. 53,1?! заттттсаттьт и файл. Поэтому‘ тте стоит вьтзьтттгттт, метод г} ()
ттсрсл закрытием тратты.
Bc;1C;1yt()u1c.\1 примере вы ттолттяс тся коттироттатттте (раттлгт. Имена исмътттого тт ЦСЛСВОГО
трайлотт задатотсзт в коматтттттотт строке.
/‘ Копирование файла.
При ИСПОПыЗОНдЫИЪ/д очей ттрот раммтдт укажтгггк: имена исходном;
и Целевого файлов.
Ейоттримср, для конировдттмя r:di/'1
;?EZCL!I\‘i J‘
команштой кттрокой:
Iv; г ‚ ‘т х?
мети/каши
‚А файл
Сору? ‚т д‘ 55,5
us ing syszem;
щит; SysLc.'~'..10;
class Cv:,pyF;1c
p-_m‘.,;c str1;-c год) M.::_:1[s::.L::1u.3( ;iIL_'§-}
in: _:
F;;eS:rcar 110:
Ъ : '.CSLu‘r::' .\
‚” O'_':<pL.:';'./.0 l.1_?X()J..{liOiL2 ¢‘:lI;‘.J[d
Lry
[шт —
Ъ сыт со (EL '.<:I\"-3:
E‘.Li<:S: r-:an'(a1-:x,s;-":, ‚ Е‘- _ r31V.odr3 .L‘Ipen) ;
i<";L.x1<x$$:<cs:y.:-*ivJ:'. схс) т
Глава 11. Ввод/вывод?"
Если нотой _
401
ввод/вьквод в символьные файлы
Console_writeL1ne(exc.Message + "\пИсходный файл не найден");
return;
// Открытие файла—копии
zry {
fout — new FileStream(args[1j, FileMode.Create);
} catch(FileNot?oundException exc) {
Console.Wri:eLine(exc.Message + "\пОшибка создания файла—копии");
ГССЦГПЁ
}
catch(IndexOutOfRangeException exc) i
Console.WriteLine(exc.Message + "\nUsage: CopyFi1e From To");
return;
// Копирование файла
try {
do { Считывание байтов из одною файла с
i : fin _ReadByte Н д ‹____._________ ПОСЛСЛУЮЩСЙ ИХ Записью В ДРУГОЙ Фат!-
if(i != -1) fout.writeBy:e( (byte)i); 4————————-——l
} while(i != -1);
} catch(IOException exc) {
Console.WriteLine(exc.Message + "Файловая ошибка");
f1n.ClOse();
fout.Close();
Манутныд практикум
|. Что возвращает метод ReadByte () no достижении конца файлах?
2. Для чего предназначен метод Flush п?
3. Какой метод следует вызвать для записи блока байтов‘?
Ввод/вывод в символьные файлы
\nTsI существуют общие методы обработки байтовых файлов, для этой цели можно
-i1K>KC воспользоваться символьными потоками. Преимущество символьных потоков
‘гроявляется в том, что они оперируют непосредственно с символами Unicode.
Поэтому, если возникает потребность в хранении текста Unicode, наилучшим выбо-
MM B этом случае будут еимвольн ые потоки. Как правило, при выполнении файловых
операций, основанных на использовании символов, класс Filestream включается
И состав класса Зсгеатвеаоег или Streamwriter. Эти классы обеспечивают авто-
чцтичеекое преобразование байтовых потоков в символьные и наоборот.
По достижении конца файла метод Reacnsyte () возвращает значение -1.
Вызов метода Flush () приводит к тому, что данные, находяитнеея в буферах вывода,
ФИЗИЧЕСКИ ЗЗПИСЫВЭЮТСЯ на устройство.
3, Для записи блока байтов вьтзываетея метод Write () .
„д _-
Глава11_Бводшьшод
T
i
I
ё
Помните о том. что на уровне операционной системы файл состоит из набора байтовд
ЭТО ОСТИСТСЯ СПРЗВЁПЛИВЫМ И ДЛЯ КЛЭССОВ StreamReader ИЛИ Streamwriter.
Класс Streamwriter ъшследустся из класса Textwriter. Класс StreamReade
HawwflyflnxlM3KflaCuiTextReader.B рсшльппелцшссы StreamWr1terl13Lream
Reader получают Доступ к методам и свойствам, определенным наслсцуемьячи им
массами.
Использование класса Streamwriter 3’
Для создания символьного потока вывода включите объект Stream (например;
FileStream) в Состав класса Streamwriter. Кчасс StreamWr;ter определяет не;
СКОЛЬКО конструкторов. ОДИН ИЗ НаИбОЛСС популярных конструкторов ПОКЦЗаН Нижеч
StreamWriter(Stream stream)
Здесь параметр stream указывает имя открытого потока. Если указанный поток буди?
пуст, конструктор вызывает исключение ArqumentExcept1-rm. a если stream равен;
ншпо‚вызываскшялжлкщсниеАг9ытепсши11Ехсерп1оп.Сршц/жеткжлссощшнит
класс Streamwriter автоматически вьтполняет преобразование символов в байты.
Ниже приводится код простой утилиты, вьхполняющей запись информации на диск;
которая считывает строки текста, введенного с клавиатуры, а затем записывает его в`
файл rest. txt. Считывание текста продолжается до тех пор, пока пользователь не;
введет слово «stop». Утилита использует класс FileStream, включенный в СОСТЁШ
класса streamwriter, с целью вывода информации в файл.
/1‹
ПРОСТЗЯ УТИЛИТЗ, ВЫПОЛНЯЮЩЗЯ ЗЗПИСЬ на ДИСК,
которая демонстрирует возможности класса Screamwriter. */
using System;
using System.IO; '
class KtoD { '
public statlc void Main() { I
string str;
Filestream fout;
try {
fout = new FileStream("test.txt",
Ъ
catch(IOException exc) {
FileMode.Create);
Console.writeL1ne(exc.Message + “Невозможно открыть фаЙл.")
I
recur“ ’ Солшниеклшшазпгеашигъсед
}
treamwriter fstrwout 2 new StreamWr1:er(fout);
Console.WriteL1ne("BBenMTe текст stop для выхода‚");
do (
Console.Write(":
n);
str = Console.ReadLine();
if(str Э: “stop“) {
Str = str г "\r\n"; // Добавление символов образования новой строки.
try {
д Зашись строк в файл.
fstr_out.Wr1te(str);
гита .‘ '
‹ =„ «*5 .. ‘;',‘.'~“"'v¢JI;£\4‘a(
¥:v- Wfififlwv
403
Вдоль/вывод в символьные файлы
} cdLch(IOException exc) (
Console.WriteL1ne(exc.Messaqe * "Файловая ошибка");
return ;
wh1le(sCr Е; "sLop");
5з;г_оыь.С1озе():
э. .
у
5’ в пскоторьтх случаях можно открыть файл напрямую, используя клаее ":::v:2am~
х При этом используется один из указанных ниже констрьукторьотз:
I/'-.
5;1gamWriter(s:rinq filename)
5;:c:mWriter(s:ring f1lename, bool appendflag)
Здесь параметр fi 1е name указывает имя открываемого файла, причем это может быть
_ полностью определенное имя. При использовании конструктора второго вида данные
‘— будут добавляться в конец существующего файла, сели флагу appendTrue присвоено
' значение еще. Если же упомянутому флагу присвоено значение false, [l‘dHHblC будут
замещать содержимое указанного файла. При отсутствии файла в обоих случаях
происходит его создание, Также в обоих случаях генерируется исключение IoExcep~
в случае наличия ошибки. Могут генерироваться и другие исключения.
ъ
„..-,„.- _ ,_
Ниже приводится код утилиты, вьтполняющей запись информации на диск. Код
переписан таким образом, что включает класс Streamwmter, выполняющий откры-
me выходного файла.
/* Открытие файла с помощью класса Streamwriter. */
‘г nq Sys tern;
U:.nq System.IO;
c;:5s KtoD {
г.о;1с Static void Ma1n() {
string str;
Streamwrlter fstrkout;
try {
f5tr_out — new StreamWriter("test.txt");
}
catch(IOException exc) {
Console.WriteL1ne(exc.Message + "Невозможно открыть файл.");
return ;
Соп5о1е.Шг1пеЬ1пе("Введите текст ‘stop’ ДЛЯ выхода."):
do {
ConsOle.Wr1te(": ");
эк: — Console.ReadLine();
if(str != "stop") (
str = str + "\r\n"; // Добавление символов образования новой строки.
try {
fstr\out.write(str);
) catch(IOExCeption exc) {
404 Глава 11, Ввод/вывод
Console.Wr;teL1ne{exc.Messaqe + "Файловая ошибка");
return ;
3 wh;Le(s:: lz "stop");
fstr_oJ:.CAose():
Использование класса StreamReader
Для создания символьного потока ввода въклхочите байтовый поток в класс stream— д
Reader. Эгот класс определяет несколько конструкторов. Наиболее часто использу- I‘
ется следующий: 2
/
StreamReader(Stream stream)
ЗДЕСЬ параметр stream определяет имя открытого потока. Если значение параметраЁ
stream равно нулю. генерируется исключение Arg'JmentNL‘.llExCept1or.. Сразу же
после создания класс Зъгеаптйтаайег будет автоматически выполнять преобразований
байтов в симпольх.
Следующая программа прсдстгпзляет собой простую утилиту; она считывает тсксто- `_
вый файл test .txt И отображает его содержимое на экране. Таким образом, данная:
утилита является комплементарной по отношению к утилите записи на диск, при-д
поденной в претьыдутпхем разделе. 1‘,
/* Простая утилита, которая демонстрирует возможности класса F;leReader. */
using SysLem;
using System.10;
class DtOS (
public static Void Main() {
- 1 1 ' . „ _
F'L‘eStrea'“ fl"! (.-m1hmaHMc строк из файла с последующим
5;г1п9 s; ,//’mxnnfipaxcHmu4Ha3nmHo
.CS:1eaA("‘es‘ Ix: ‚ Р1.еМоёс.Ореп);
сапсп(1ОЕхсер:1оп ехс) I
Conso1Q.Wr:teLine(cxc.Mcssaqe ч "Невозможно открыть фдйл.");
д.
*1
*<
- м. -a..._.cm..u.. . шрифт „жида;
Leturn I
..-....'..._...s; .. „ж . v .
StreamReader fstrmin r new StreamRcader(fin);
while((s fstr_in.ReadL1ne()) 1: nu;l) {
Console.WriLeL1ne(s);
fstrmin.Close();
‚а
fl&u-.au.e:ac.aon.—~.a~..s......:- - .~ ‘д
Перенаправление стандартных потоков 405
ттпрагите внимание на то. каким образом определяется конец файла. Как тольк
д;:‚т.‘тка‚ возвращаемая методом ReadL1ne(), получит дзначеньте‘ равное нулю, эт
\n\.l'/KC-I на достижение конца файла.
к. к и в случае с классом St;f€-dmW’f1t~3L”. в НСКОТОрЫХ случаях можно открыть фай
‚тдтоередсттзенно. воспользовавидись возможностями класса 3-t,re:.mReader. Для вы
„г. чтения этой операции служит следтутоттттттт коттсгрукгор:
:d."1Reader (str.:ng filename)
Здесь параметр Е; lename указывает имя открываемого файла, причем может задаватье
Iih тттый пугь. Сутисствоваттие файла является обязательным. Если файл не существуе’
тд-тзртттэуется исклточение ::;~Exc -Li.-;r. Если параметр f1le'.':am»: представляет собо
H5._‘:_\-'I() строку, генерируется исключение Ах-‘дытттегтгЕхсёрёгдтг.
„ -1n—(V,-‘uv»W")'/ ‚ы - _. „ту у
Минутные)’ практикум
1. Какой класс используется для считывания символов из файла?
2. Какой класс применяется для записи символов в файл‘?
3. Зачем нужны отдельные символьн ьте классы вводта/ттьттзодгт?
Перенаправление стандартных потоков
l\.11\ упоминалось ранее„ стандартные потоки, такис как C-)nsole.In, могут быт
тигреттаттравленьт. Конечно, наиболее часто потоки перенаправлятотся в файл. Ка
только стандартный поток перенаправлен, вводимые и вьттзодимьте данные перенг
нравлятотся автоматически. Путем псрснаправления стандартных потоков программ
может считывать команды из файла на диске. создавать журнальные файлы либо да›т‹
но тутаТь данные из сети.
ёёагаегаа; арафаееаааааа
П
:_;~. Bonpoc. Я слышал, что можно указать «символьная колттровкгт» путем открь
татя класса ЗЬгеаПРеаЗЭГ и.ти Зсггэаттъчггытг. В чем сугь стптводтт‚н()т‘() кодировант
И как его можно использовать?
Ответ. Классы 3‘.:reamReader И Streamwriter преобразуют байты в символ
“ ПгЮборот. При этом используется силтвольттагт кодировка. указывающая СП0С(
"Рсобразоваттигт. По умолчанию в C# используется кодировка UTF-8. KOTOP?
5H:.‘.f-I\:IC5l совместимой с кодировкой Unicode. Поскольку ASCII является подмножн
С’; этом Unicode. свойство кодировки, заданное по ухтолчанито‚ обрабатывает символ
=\S(‘ll тт Ljnicodc. Для указания другой кодировки можно воспользоваться перегр‘
‘Кгчттьтмтт версиями конструкторов StreamReader пли Strearrwriter, которь
“Ъдтоттгттот параметр кодирования. Иначе говоря„ символьная кодировка 'грСбУ°""
дшёпцтьтто редко.
. ‘4 2‘#М?Н5‘5"93м„
315151 считывания СИМВОЛОВ ИСПОЛЬЗУЕТСЯ КЛЗСС SCr9dfi1R€a«1er_
IE “Я ЗЭПИСИ СИМВОЛОВ ПРИМСНЯСТСЯ КЛЭСС StreamWri1_;(jr_
Luna'-
Гимвольные классы ввода/вьттзода автоматизируют прямое и обратное преобразован
DyLe—chaL.
Перснаправление стандартных потоков выполняется двумя путями. Во-ттсрпзых, npn‘
Bbl3OB€ программы в командной строке можно воспользоваться символами > и <
I
выполнякядими псренапранление1зСоп5о1е.1п иъъСоп5о1е.Ооп‚Соотвептвеннод
Например, обратите внимание на такую программу: з!
using System;
class Test (
publ;c static vo;d Main() {
Console.WriteLine("3To текст.");
)
При вызове программы следующим образом:
Test > log
строка "Это текст . " записывается в файл 1':-g. Ввод может перенаправляться таким
же образом. Единственное, что нужно помнить при направлении ввода. ~ убедиться
В TOM, ЧТО ИСТОЧНИК ВХОПНОЙ игнрормации ПСРСДЭЁТ КОЛИЧССТВ0 ВХОДНЫХ ДЭННЬШ,
достаточное для удовлетворения требований программы. Если же это требование неё
выполняется,програмзн1«3ависастж
Операторы перенаправления командной строки (< и >) не являются частыо Cit. HQ
ОНИ поддерживаются операционной системой. Таким образом, если в среде поддерд‘
живается перснаправление операций ввода/вьпвода (как в случае C Windows), можно
перенаправлять стандартный ввод и вывод без выполнения каких-либо изменений в
программе. Однако существует и второй способ перснаправления стандартных пото-
ков, при использовании которого применяется программный контроль. В этом случае
необходимо воспользоваться методами setln () , SetOut () И Setflrror () , которые
ЯВЛЯКЛЮЯ членамъакласса Console:
static Void SetIn(TextReader input)
static voio SetOut(Textwriter output)
static void SetError(TextWriter output)
Таким образом, для перенаправления вывода вызовите метод Setln (Ъ Указав тре-
буемый поток. Можно использовать любой поток ввода в том сущ/чае, если он
наследуется из класса TestReader. Для перенаправления вывода в файл укажите
файл, который входит в состав класса Textwmter. Следующая программа демонст-
рирует соответствующий пример.
// Перенаправление COnsole.Out.
using System;
using SysLem.IO;
class Redirect {
public static void Main() {
Streamwriter 1og_out;
try {
log_out = new Streamwriter("logfile.txt"):
)
catch(IOExccpt;on exc) f
"”'- к : :
,..-eta-a_ I 2:.
fnr(1nt
_L\g OUC
ПерЭНЗПрЭВПЭНИЭ стандартных потоков
;лп5о1е.
Conso;e.
Jonsole.
.Close();
407
COnsole.Wr1teLJ_ne(exc.Messaqe + "Невозможно открыть Файл"):
return
I
Setout (ioqiout) ; ‘___;__.«E1cpcHaIIpaIueHncuoroka C-rnsole .'..‘-uL.‘|
Wr1teL;ne("HaqaHO журнального файла.");
;—O; ;<lU;
1тт) COnsole_Wr1teL1ne(1);
WriteL1ne("KoHeu журнального файла.");
После запуска програмиь! на выполнение на Экране ничето нс отображается Весь
вывод направляется в файл 1-::gf1le.txtI
ндчдцо журнального файла.
юстг-цслшцьшюно
К1:СЦ журнального файла.
ТСПСрЬ ВЫ МОЖСТС ПОЭКСПСрИМСНТИрОННТЬ С другими НСТрПСННЫМИ ПОТОКНМИ.
Проект 11-1. Утилита сравнения файлов
Исовтргпеасэ
В лом проекте разрабатывается простая. но весьма полезная утилита. используевиая
При сравнении файлов. Эта утилита открывает оба сравниваемых (Ьаила, а затем
считывает и
сравнивает каждый соответствующий набор байтов. Если при этом
Обнаруживается несоответствие, файлы считаются раздтьпчньцаи. Если одновременно
достигнуты концы обоих файлов и несоответствие не найдено, файлы считаются
Одинаковыми.
Пошаговая инструкция
I. СОЗДНЙГС файл COmpFiles.Cs.
ч . п
к В фаил Com}:-E‘1Les . cs введите слелуюнхии код:
т
Проект
1 1
1-
Сравнение двух файлов.
Укажите имена двух сравниваемых файлов в командной строке.
т/
Например:
CompFile FIRST.TXT SECOND.TXT
J
class es 1
pub1;c sLa:1; void Ma:n(s:r:nq:i arqs)
in: L~C, ] O,
:- › r‘.
. -А‚
:- r,\.
. .„‚
‚/ Открытие легат}; файла.
LIY Ё
fl — new FiieStream(a;qs[Q),
} catch(F1leNotFoundExCepL;on exc) 1
Cons0le.WriteL:ne(exc.Messaqe);
F;LeMod9.0pen);
return;
'/ трытие вторш1о файла.
Lry
f2
1 caLch(F;1eN0"»
Conso1e_W:i
M new F еМосс.Ореп›;
return;
3
} catch(:ndexOu:OfPangeExcepr1on exc) \
CQnsole.Wr;teLine(exc.Messaqe + "\nUsage: CQnpFile fl £2");
xeturn;
I
’/ Срав е е файлов.
fry I <
do 1 Ч
i f1.ReddBy:e();
} f.:’..RF.3:1\"lByt€;‘();
;L(; ! д) bzeak;
3 wh;le(; E» “Д && J l— 1);
C3:ch(IOExcep:ion ехс) 1
Соп5о1е.Иг;геЬ3ие(ехс.Ме‹
}
;£(1
‘-*1’)
Соп5п1е.Иг;сеЬ1пе("Файцы разлцчны_"):
else
"):
COnsoLe.Wri:eLLne("¢aQnm ggauauamw.
С ЦСЛЬЮ тестирования nporpzlxmm 3-m1.F . Ъ c1<()rmp_vi'n‘cLbaih Соаг- -
фцпд E-:'.’"I['. Згпсч nbinojnurrc c:|c:1y1()1u_w() команду:
CompFi cs СогрЁ:1е5 Петр
. (25
Пров рпмиы щшжъпи сообщим, о mu, -rm файлы соппацапог.
чтение и запись двоичных данных __ й _ 409
4 ‘Затем сравните файлы Сотпр-Р‘; I пе . и In ;,,-A/51 Ё г д (пока ШН ранее). НОСПОЛЪ‘
ъовавшись слсцутотттетт командой:
i'.es CompFiles.cs C0pyF1;e.r:s
Программу Сьзтргд1ез можно оботаттггь различиыитт оттиияхтът. Например. мож-
но Добавить опцию. позволяюшутто программе игнорировать регистр символов.
‚[ругос расширение ее возможностей заключается в том. что программа >_'I‘p—
' -Ales MOFJIEK бы отображать позиции огличат()илтхся ‚чанньтх в фаилах.
V 1
ЧТЭНИЭ И ЗЭПИСЬ ДВОИЧНЫХ данных
До сих пор мы рассматривании механизмьт чтения и записи бантов и символов. по
сдтслуст учитьтвать, что можно Производить «хгеиие и запись данных других типов,
например значений типа дик, c«:,~u.t~;e или sn-:.:"r. Для чтения и записи ‚твоичттьтх
значений, имеющих встроенные типы данных Сд, применяются потоки Bmac-;—
R: fie: И Blnarywrlter. При использовании этих потоков важно понимать, что
данные считываются и записываются во внутреннем двоичном формате, а не в
текстовой форме. воспринимаемой человеком.
Поток Binarywriter
Поток Binarywrite‘: представляет собой оболочку для баитоттого потока. которая
управляет записью двоичных данных. Ниже показан наиболее часто исподтьзуехтьттнт
KOHCTpyKTOpI
E--sarywriter (Stream outputstream)
Злссь параметр out-putstream определяет поток. в который производится запись
данных. Для записи вывода в файл в качестве этого параметра указывается объект,
Сщланньтй конструктором Filestream. Если значение параметра с.т_;:рцхъ$::е.а:г„
Равно нулю, генерируется исключение Arg1;me:«r:tNul1E.<-cap:1,-::n. Если параметр
С riutstream задаст поток, не открьггьтй для записи, генерируется исключение
'.::.'nentExCep:1on.
”U:'()K Blnarfi-vr;:er определяет метоць], которые обесисчътватот заииеьштттттьтх всех
Вкпроенньтх тттпов C3. Многие из этих методов притвсленьт в табл. 11.5. Поток
Ё. zrywriter TEUOKC определяет стандартные методы I.“;(-:59 (_) И Fin Ц). работа
‘Шюрьтх описьизгьтась ранее.
Таблица 11.5. Наиболее часто используемые методы, определенные потоком Bmarywriter
NETOD. Описание
а Wr1te(sDyte val) ЗЭПИСЬ байта со знаком
о Write (oyce val) Запись байта без знака
.а wr: пе (byte и buf) Запись массива байтов
во wr1te(shO1’t УЗИ Запись короткого целого
т Wrl ‘ее (ushort val) 3al'W|Cb короткого ЦеПоГО без знака
; Write (lnt val) Запись целого числа
31d Wr1,te(uint val) Запись целого без знака
41 О Глава 11. Ввод/выв
метод Описание
voi d Write (long val) Запись длинного целого
то id Wr1t:e(u1ong val) Запись длинного целого без знака
vs 1 d Wr i Се (t ica t val) Запись значения типа float
‘.'.:‘lO ‘write (double val) Запись значения типа douse Le
Vcld Write (char val) Запись символа
щ- id Wr1_te(char[] val) 3anVlCb массива символов
vcld Write (string val) Запись строки
Поток BinaryReader
HOTOK BinaryReader является оболочкой, которая включает байтовый поток, вы-
полгтякштттгт чтение двоичных данных. Ниже показан наиболее часто используемый
конструктор для ‘этого потока.
в; naryRea<:‘er (Scream inputs tream)
Здесь параметр inputscream обозначает поток, из которого происходит считывание
данных. Для выполнения считывания данных из файла в качестве этого параметщ '
можно указать объект, созданный с помощью конструктора Filestream.
значение параметра inputstream равен нулю, генерируется исключение Argumem; " д
NullException. Если параметр inputstream не открыт для чтения. Генерируете?‘
искпючение ArgumentException. 5
Поток BinaryReader поддерживает методы, обеспечивающие считывание всех прод? ,_
СТЫХ типов данных в С#. Наиболее часто используемые из них приведены в табл. 11. .
Поток B1naryReader также определяет три версии метода Read () , приведенные:
ниже. - `
шт; Read() Возвращает целочисленное представдд '
пение следующего доступного символа:
из потока ввода. При достижении концдд
файла возвращает значение -1
1пЕ Read(byte[] buf, int offset, int num) Осуществляетпопыткусчитать питбай-
тов в буфер buf, начиная с элемента; е?
находящегося в позиции offset, aoaapa-‘ '»
щая при этом количество успешно CHM-
ТЗННЫХ байтов
шк Read (char [] buf, mt offset, int num) Осуществляет попытку считать пит CHM‘
волов в буфер buf, начиная с элемента. .„
находящегося в позиции offset, возврг‘ '
щая при этом количество успешно СЧИ‘
танных символов г
В РСЗУЛЬТНТС сбоя при выполнении этих методов генерируется исключение товхсер‘
{:1-31"..
Также определен стандартный метод Съэзе ( ). б
чтение И ЗЭПИСЬ двоичных данных 41 1
г
Таблица 11.6. Наиболее часто используемые методы, определенные потоком BinaryReader
Петзд Описание
' ReadBoo Lean () Чтение значения типа bool
D.) дд ReadByte () Чтение значения типа byte
‚за ReadSByte () Чтение значения типа sbyte
ш ; С [J ReadBytes(1nt num) Чтение пит байтов и возврат их в виде массива
д if ReadChar () Чтение значения типа char
дд [J ReadChars (int, num) Чтение пит символов и возврат их в виде массива
Г:
див Le ReadDouble () Чтение значения типа double
f_. at Readsingle () Чтение значения типа float
sh; r t ReadInt16 () Чтение значения типа short
11.: ReadInt32 т) Чтение значения типа int
1:: щ Readlnt 64 () Чтение значения типа long
usr-..i)rt ReadUintl 6 () Чтение значения типа ushort
u1:1t FieadUint32 () Чтение значения типа uint
сдало ReadUint64() Чтениезначениятипац1оп9
string ReadString() Чтение строки
Демонстрация двоичного ввода/вывода
Нпжс Приводится пример программы, которая демонстрирует возможности потоков
E; ryReader И Binarywriter. Эта Программа сначала записывает в файл. а затем
считывает из него значения различных типов.
/1 Запись и считывание двоичных Данных.
us:ng System;
us;ng System.IO;
Cfiass кираса {
рцЬ;1с static void Main() {
Binarywriter dacaouti
BinaryReader datain:
Lnt 1 = 10;
double d т 1023.56;
cool b : true;
try {
dataout = new
BinaryWriter(new Fi1eStream("testdata", F1leMode.Create));
catch(IOException exc) {
Conso1e.wr1:eLine(exc.Message * "\пНевозможно открыть файл."):
return;
try i
Console.WriteLine("3anmcb " + 1);
412 Глава 11. Ввод/вывод
da:aOut.Write(1); <-
CQflSOl€.WIiCGLin6("3dhMCb " + 0);
daLdOUt.Write(d); 4—--~——~—--——~A—-—-—*—{3mnmbuuownmmgmnnmx
п
Соп5о1е.Их;пеЬ1пе("Запись 1 b);
dataOut.Wr1:e(b); 4—
Console.WriteLine(“3aumcb " ~ 12.4 * 7.4);
da':aOut.Writ.e(12.2 * 7.4) г
ca:ch(IOExceptien cxc) (
ConSole.wr1teL1ne(exc.Messcgc +
н
\п0шибка 3аписи."):
dataOu:.Close(); ‘
Console.WriteLine();
// Теперь снова чтение.
Cry {
ddtaln = new
BinaryReader(new FileStream("testdata", F1LeMode.Open));
}
catch(IOException exc) {
Console.writeLine(exC.Message + "\пНевозможно открыть файл."); -X
return;
}
try ( Ё
1 = dataIn.Read1nt32(); <~—-——w——~——~————»——-—v——~—«~—-—- 2'
Cons0ie.WriteLine("Hwenme “ r 1); jg
d ‹ daEaIn.ReadDoubie();<r—-—-— 1
Соп5о1е.Иг1сеЬ1пе(“Чтение “ т d); Ё
Чтению двоичных данных.
b = dataIn.ReadBoolean();
Сопзо1е.Ик1сеЬЬпе("Чтение “ т b); {З
d = data1n.ReadDouble();<~——————~*
Consoie.WriteL1ne("flwenme “ т d):
i
catch(IOExccption cxc) {
C0ns0Le.WriteL;ne(exc.Mc5sage * "Ошибка чтенин.");
dataIn.Cl0se();
}
P63)/J] ЬТНТ ВЫ ПОЛ НС Н Н Я ПРОПЭНМ \l HI
Запись 10
Запись 1023.56
Запись True
‚Минутная? лрактаг/гум
1. Какой поток используется при записи в двоичные фаильт‘?
7. Какой метод вьтзывается для записи значения титта "Li-'J1;k:le в ш;
формате?
3. Какои метод вызывается :т.тя чтения значения тина ЗЬ-цгп в НЮ
формате?
произвольный доступ к содержимому файл
До настоящего момента времени нами неттользотзацтсэт так называемый I106.-’1edo<
нын досптуп к содержимому фаилгт, байт за батттом. Однако в Ci: B03M(
11/;n1z;1;0.-1o/zbziz ()0cmyn к содержимому фатмов. Для реализации подобного ,1
используется метод Seek () , определенны и в классе E11331 :’1":a:11. Этот метод
‚тяет задавать индикатор позиции текущего Элемента данных в файле (который
называется файловый указатель).
Стттттаксис метода Боек т 1 таков:
1=Ё'‚Ц S<~:ek(long newPos, See}:Or1g1n or;g1.r;)
Здесь параметр r1ewP::-5 указывает на новуто 110311111110(1:611111;1x),11;1 фанлоттого ука
начиная от местоположения, указанното параметром Поелслтттттт па
“(ЧАСТ 1l'\lCTb ОДНО ИЗ 5НЕ1ЧСНИЙ‚ ОПрСДСЛСНПЬЦ П ПСРСЧИ(.`ЛСНИЪК Её "511-31”
Значение Описание
. n Отсчет от начала файла
С ': „не Отсчет от текущего местоположения
Отсчет от конца файла
После вьтзова метода Seek 1) сгтедуюнтие оттерантти чтения ‚тибо записи нроизш
В попой позишттт (райлоного указателя. Если во время позициоттированття ука
“i‘u11s0i'1,:1eT ошибка. генерируется исключение
Жттттттег операттито позиционирования. генерируется ттекцтточеттттс тчпгзергогв
ь ЛЁ.
Ехсердддп. Если поток не ттт
H.110 приводится пример, лсмонстрнругтоттттттт способ вьтгтогтттсътття оттераттш
iii: .z1,11;o;1z1 с проттзтзодтьттьтхт ‚тоступом к содержимому фанта. В ЭТОМ ПРПМСРС ‘5
’“"~"c11,1:110'1‘cs1 прописные буквы алфавита. которые затем ечтт т ыттатотстт в промыто,
”"i‘5!.11<c.
I При ‘записи в двоичные файлы используется ттоток B1r1aLyWL._L1_~:.
.1151 записи значения Типа оощде вызывается метод W1;Le () .
uaru
Для чтения значения тина short, вызывается метод геемыптдб () .
414 Глава 11. Ввод/вывод,
// Демонстрация механизма произвольного доступа К содержимому файла.
us;ng System; 3 F
using System.IO;
class RandomAccessDemo (
publlc static void Main() {
F;leSLream f;
char ch;
Lry [
f new F1lcStream("random.daL", F-lCMQdC.CrCdL€)i
Ъ
catch(F1leNo:FounaExcept1on exc) i
Console.wr1teLine(exc.Message);
return ;
// 3aUMCb букв В алфавитном порядке.
for(int 1-0; i < 26; i++) {
try {
f.WriCeByte((byte)('A'+i));
}
catch(IOExcept;on exc) {
Console.WriLeL1ne(exC.Mcssage);
return ;
’ MCHOJKBSOBHHHC МСТОДН Seek ( ) для
перемещения qmfmouoro указателщ
try {
// Считывание букв.
f.Seek(O, SeekOrigin.Beg1n); // Поиск первого байта.
ch — (char) f.ReadBy:e():
Console.WriLeL1ne("nepsoe значение " + ch);
f.Seek(1, SeekOrig1n_begin): // Поиск второго байта.
ch (char) f.ReadByte();
Console.WriteL1ne("Bwopoe значение " т ch);
f.Seek(4, SeekOrigin.Begin); // Поиск пятого байта.
ch (char) f.ReadByte();
Console.WriteLine(HHToe значение " + ch);
COn501e.Wr1teL1ne();
// Теперь чтение четных букв.
Соп$о1е.Иг1СеЬ1пе("Четные буквы: ");
for(int 1:0; i < 26; i f» 2) {
f.Seek(i, SeekOrigin.Begin); // Поиск 1—го байта
ch (char) f.ReadByLe();
Consd;e.wriCe(ch 4 " ");
}
catch(IOException exc) {
Console.WriteL1ne(exC.Message);
f.Close();
преобразование числовых строк в их внутренние представления 415
г
PL‘ JVJ bTllT B Ы П О]! НС Н И Я П р() гра M М Ы I
r:‘\:/.506 значение A
u- . ace значение В
п,„де значение Е.
каждое другое значение:
‘E.GIKMOQ$UWY
Минутный драк/никуда
1. Каким образом позиционируется файловый указатель’?
Км)
Какое значение параметра огщш задает отсчет от текущего значения
файлового указателя?
Какое исюп ючение генерируется. если метод Seek () вызывается для потока,
который не поддерживает позиционирование?
Ь.)
преобразование числовых строк в их
внутренние представления
Перед завершением рассмотрения механизмов ввода/вывода обратимся к технике,
которая используется при чтении числовых строк. Как вы помните, метод Wr1te—
L1.'.»?() B C# обеспечивает удобный способ вывода данных различных типов на
консоль. Сюда также включаются числовые значения встроенных типов данных,
таких как int И double. B результате метод икцеьше () автоматически преобразует
числовые значения в их представления, более удобные для восприятия человеком.
Однако в С# не поддерживается метод ввода, который мог бы считывать и преобра-
зовывггть строки, содержащие числовые значения, в их внутреннее ‚двоичное пред-
етьтвтенис. Например, невозможно ввести с клавиатуры такую строку, как «1О0» и
автоматически преобразовать ее в соответствующее целочисленное значение. которое
может быть сохранено в переменной типа int. ДЛЯ выполнения этой задачи необхо-
димо воспользоваться методом, который определен для всех встроенных числовых
типов: Parse (),
Перед тем как перейти кдальнейшему рассмотрению вопроса, необходимо вспомнить
Oman важный факт: все встроенные типы C#, такие как int И double, Ha самом дСПС
явпиотся псевдонимами (другими именами) для структур, определенных в библио-
TQM‘ NET Framework. Фактически Microsoft утверждает, что тип языка Сд и тип
C’1l>\ К туры .NET практически совпгщают. Просто используются различные наимено-
Bdmm. Поскольку типы значений Сд поддерживаются структурами. то эти типы
Яшнготея членами соответствующих структур.
чище показаны типы числовых значений C# И соответствующие им имена струКТУР
.; ЪТ
для позиционирования файлового указателя используется метод Seek ( ) .
Значение SeekOrigin . Current.
Если метод Seek () не поддерживается потоком, генерируется исключение NotSupported-
Exception.
41 6 глава 11. Ввод/вые
.›
Имя структуры .NET Тип данных C#
Double double 5
S ingle float Е,
тише short Ё
I nt:32 int
int 6 4 long
U I n :1 6 ushori
U int 32 шт
Ulnt 6 4 ulong
Byte byte
Sbyte sbyte
Эти структуры определены в нроеграатсгве имен System. Таким образом, полностью .-a
определенное имя структуры 32 будет зузсегхт. Int 32. Эти структуры предлагают а 7;‘
широкий набор методов, позволяющих полностью интегрировать типы значений вЁ
иерархию объектов C#. Также числовые структуры определяют статические методЫЦ ‘б
преобразующие ‘тисловую строку в соответствующий ей двоичный эквивалент, Эти}, "-
методы приведены ниже. Каждыи на них возвращает ‚двоичное ъначснис, соответетч
вующее строке. -
Структура Метод для преобразования д
Double static double Parse(string str) V
Single static float Parse(string str) ';
Int:64 static tong Parse(string str) 3.
1nt32 static int Parse(string Str) д т
Intl 6 static short Parse(string sir) п
отпив/х static ulong Parse(string str)
U1nt32 static шт Parse(string str)
UInt16 static ushort Parse(string str} _
Byte static byte Parse(string str)
Звуке static sbyte Parse(string str)
Методы Parse () генерируют исключение Ротик- эрёьэп, если параметр sir Не)?"
содержит корректного значения. еоотвстствуюпшего нужному Тину дат-хных. Исклю-
ЧСНИС АггдшгпэттпгшЫЕхсерпщсвп генерируется, если параметр st v: ИМССТ значение
nul i, а если строка sf I после преобразования не может быть помещена в ПсрсИеННУРб д- у.
соответствующего Типа, генерируется Исключение СпегЕЬ-ё ' `
Методы Parse () обеспечивают удобный способ преобразования в числовое значениЩ 3
строки, считываемой с клавиатуры или из текстового файла. в подходящий внутрен‘ .
ний формат. Например, в слснуюънсгй программе подсчитывается среднее ‘знамение .'
чисел. введенных пользователем. Сначала программа просит указать количеств0 g
значений, для которых рассчитывается среднее значение. Затем она счигьнгаст‘ это
число с помощью метода Reéa-jI,1r:c— и посредством метода 12:32 . г. ") HpCO6pa‘I~:' 1
зовывгтет строку в целочисленное значение. Далее осуишествдтяется ввод значений,
причем метод Double . Parse () HCI10J'ib3yL‘TC$i для преобразования строк в их числое’;
вые эквиваленты. имеющие тип double.
Преобразование числовых строк в их внутренние представления
/,‚ Эта npoI'paMMa ПОДСЧИТЫЕЗЭТ
us;Hg
using
C15
pug;
Р
PC;
УК1
Cg)‘.
J>La)l\)I-‘
среднее значение ВВЕДЕННЫХ ЧИСЕЛ */
System;
System.IO;
AvgNumS {
ic static vold Main()
str;
ss
(
string
2n: n;
O.
C;
sum O;
avg,
gouoie
gcuble
Сэп$о1е.Иг1пе("Укажите количество вводимых чисел: ");
_Lr > Console.ReadLine();
try {
П
1nt32 _ Parse (str) ; <——————-—{l'Ipeo6pa3ouaHuc строки в число типа юс.
catch(FormatException exc) {
Console.WriteLine(exc.Message);
n А O;
ca:ch(OverflowExcept1on ехс) i
Console.WriteLine(exc.Message);
n 0;
{
Console.WriteLine("Baenmwe + n + " значений.");
for(int i=0; i < n ; i++)
Console.Write(": ");
str = Console.ReadLine();
try {
t ; Double _ parse (str) I-4_______! Hpcoépznoszmnc строки в число типа doubltfl
catch(FormatException exc) {
Console.WriteLine(exc.Message);
t 0.0;
в
I
}
catch(OverflowException exc) {
Conso1e.writeLine(exc.Message);
t О;
в
I
sum += t;
avg sum / n;
Zonsole.WriteLine("cpennee значение " + avg);
}.’1bT8T ВЫПОЛНЕНИЯ программы:
Ките количество вводимых чисел:
ците 5 значений.
.5
инее значение 3.3
418 Глава11.Ввщшв„
по ссуде.
/9‹
Улучшенный проект 2-3
Вычисление регулярных платежей по ссуде.
*/
using System;
Class RegPay {
public static void Main() (
decimal Principal; // Величина ссуды
decimal IntRate; // Процентная ставка в виде десятичной дроби
// (например, 0.075)
decimal PayPerYear; // Количество платежей в год
decimal NumYears; // Количество лет
decimal Payment; %/ Размер платежа
decimal numer, denom; // Временные переменные
double b, e; // Параметры вызова метода Pow()
string str;
Console.Write(“BBenmTe величину ссуды: ");
str = Console.ReadLine();
try {
Principal = Decimal.Parse(str);
) catch(FormatException exc) {
C0nsole.WriteLine(exc.Message);
return;
Console.write("BBenMTe процентную ставку (в виде 0.085): ");
str = Console.ReadLine();
try (
IntRate 4 Decimal.Parse(str);
} catch(FormatException exc) (
Console.WriteLine(exc.Message);
return;
„д
Соп5О1е.Иг1пе("Введите количество лет: ");
str = Console.ReadLine();
try {
NumYears = Decimal.Parse(str);
} catch(FormatException exc) {
Console.WriteLine(exc.Message);
return;
Console.Write("BBeuMTe количество платежей в год: ");
` ` Преобразование числовых строк в их внутренние ПредСТаВПеНИЯ 419
gr = Console.ReadLine();
PayPerYear — Decimal.Parse(str);
g catch(F0rmatException exc) {
Console.WriteLine(exc.Message);
return;
numer = IntRate * Principal / PayPerYear;
e (double) —(PayPerYear * NumYears);
п (double) (In:Rate / PayPerYear) + 1;
шепот = 1 — (decimal) Mach.Pow(D, e);
Payment 2 numer / denom;
Console.WriteLine("flnawex {O:C}", Payment);
Редут ьтат В ЫПОЛ НСНИЯ программы:
Введиве величину ссуды: 10000
Введите процентную ставку (в виде 0.085): 0.075
введиге количество лет: 6
Введите количество платежей В год: 12
Платеж $200.38
бегаете; лрафаееаааааа »-
Bonpoc. Для чего еще пригодны структуры значенис-тип, такие как Int32
или Double?
Ответ. Структуры значение-тип поддерживают несколько методов, облегчаю-
щих интеграцию встроенных типов С# в иерархию объектов. Например, все струк-
туры обладают методами CompareTo () , которые используются для сравнения значе-
нии. методами Equals (), которые проверяют два значения на равенство; а также
“Сюдами, которые возвращают значение объекта в различных формах. Числовые
СТРУктуры также включают поля Minvalue и Maxvalue, содержащие максимальное
И минимальное Значения, которые могут быть сохранены посредством объекта
данного типа.
а›‚.»._<‚яъ\„„„„`‹к‚:\
.- ---,; ;~-2. _- I. x „ ,. › .. нггх
Проект 11-2. Создание справочной системы
EFiieHelp.cs
B Проекте 4.1 был создан класс Help, выдающий сведения об управляющих опера-
“Шах С#. В подобной реализации справочная информация хранится в классе, а
Пшьзователь может выбирать ее в меню, воспользовавшись нумерованными опция-
"H. Хотя этот подход является вполне функциональньпи, вообше говоря, он Не
Щсгшен при создании справочной системы. Например, если понадобится изменить
“Чпавочную информацию, придется вносить изменения в исходный код программы.
‚Роне того, выбор тем справки по номерам является более утомител ьным, чем поиск
420
:3?‘
no H.V1CHI/I, И неприемлем в случае обширных списков тем. Поэтому этот ячсдостатодёг
был устранен в следующей версии справочной системы.
Справочная система хранит информацию в файле справки. Этот файл представляет-
собой стандартный текстовый файл, который можно изменять и расширять no‘
желанию пользователя, не прибегая при этом к изменению самого кода программы”
Пользователь может получать нужную ему информацию путем ввода имени желаемой‘
темы. После этого справочная система производит поиск согласно введенному‘
ключевому слову. Если соответствующая информация найдена, она отображается на}:
Экране. i
Пошаговая инструкция
1. Сначала потребуется создать файл, который будет использоваться справочной;
системой. Этот файл является стандартным текстовым файлом, который может
быть организован следующим образом:
#имя—темы1
справка но теме 1
#имя—темы2
справка по теме 2
#имя—темыЫ
справка ПО теме N
J
Имя каждой темы должно находиться в отдельной строке и предваряться симё
волом #. Благодаря использованию знака # программа может быстро находищё
начало каждой темы. После имени темы может указываться произвольное коли-д
чество строк справочной информациьд посвященной этой теме. При этом нужно
позаботитьея о пустой строке между последним предложением справки по одной
теме и началом следующей. После конца каждой строки не требуются завершаю-
щие пробелы.
Ниже приводится простой пример справочного файла, который может иепользо—
ваться ‘для тестирования справочной еистемы. Этот файл хранит сведения 06
управляющих операторах C11.
#1f
if(condiLi0n) statement;
else statement;
fiswitch
SWitCh(expression) {
case constant:
statement sequence
break;
// ...
#for
421
преобразованив числовых строк в их внутренние представления
/
for(init; condition; iteration) statement;
fiwhile
while(condition) statement;
fido
до {
statement;
while (condition);
éoreak
break; or break label;
ficontinue
continue; or continue label;
Назовите этот файл helpfile . txt.
2. Создайте файл Fi lel-Ielp . cs.
3. НЗЧНИТС СОЗДЗВЗТЬ НОВЫЙ класс Help, BOCfl0IIb30l321B[I_lI/lCb СЛСДУЮЩИМИ СТрОКЗМИ
КОЦ81
class Help {
string helpfile; // Имя справочного файла.
public Help(string fname) {
helpfile = fname;
}
Имя справочного файла передается конструктору Help И хранится в экземпляре
переменной helpfile. Поскольку каждый Экземпляр класса Help может содер-
жать свою собственную копию файла helpfile, каждый экземпляр может пре—
доставлять справку по отдельной теме.
4» Добавьте метод helpon () , КОД которого приведен ниже, в класс Help. Этот метод
используется для выборки справки по указанной теме.
’ Отображение справки по теме.
public bool helpon(string what) {
Streamfleader helpRdr;
int ch;
string topic, info;
try (
helpRdr = new StreamReader(helpfile);
}
catch(FileNotFoundException exc) {
Console.WriteLine(exc.Message);
return false:
try {
do (
// Считывание символов, пока не будет найден символ #
ch = helpRdr.Read();
// Проверка найденной темы на Соответствие искомой
if(Ch == '#') {
topic = helpRdr.ReadLine();
лава
422 г 11. в од/ 5
if(what =— topic) i // Найдены темы.
do {
info — heipRdr.ReadL1ne();
if(info !— null) Console.writeLine(info);
} while((info Ее null) && (info Ее ""));
helpRdr.Close();
return true;
}
} while(Ch !— -1);
}
catch(IOExCept1on exc) {
Console.WriteLine(exc.Message);
}
helpRdr.Close();
return false; // Тема не найдена
}
Класс Help открывается с помощью метода StreamReader. Поскольку справоч.
ный файл содержит текст, за счет использования символьного потока справочная
система обеспечиват поддержку различных языков.
Метод helpon () работает следующим образом. Строка, содержащая имя темы,
передается в параметре what. Затем открывается справочный файл. После этого
метод производит поиск в файле, сравнивая значение параметра what И наиме-
нование темы, находящейся B файле. Помните, что каждая тема начинается
знаком #, поэтому ЦИКЛ поиска просматривает файл на предмет наличия симво-
лов #. Как только очередная тема будет найдена, производится проверка на
предмет того, соответствует ли тема, обозначенная знаком #‚ теме, соответствую-
щей ключевому слову what. Если соответствие установлено, отображается инфор-
мация, связанная с этой темой. В случае обнаружения соответствия метод help-
on к) возвращает значение true. Если жс соответствие нс установлено, он
возвращает значение false.
5. Класс Help также поддерживает метод getselection () :
// Получение справки по указанной теме.
public string getSeleCtion() {
string topic : "";
Console.Write("BBenmTe тему: ");
try {
topic 2 Console.ReadLine();
1
Catch(IOException exc) {
Console.Wr1teLine(exc.Message);
return "";
}
return topic;
}
6. Ниже приводится полный код справочной системы.
/›\‹
Проект 11-2
Справочная система использует файл на диске
для хранения текстов справки.
*/
flp€0Opa3OB8H|/68 ЧИСЛОВЫХ строк E ИХ ВНУТрЭННИЭ представления
using System;
using System.IO;
/* Класс Help открывает справочный фаил‚
ищет тему, затем отображает информацию
но этой теме. */
class Help {
string helpfile; // ИМЯ справочного файла.
public Help(string fname) {
helpfile = fname;
}
// Отображение справки по теме.
public bool helpon(string what) {
StreamReader helpRdr;
int ch;
string topic, info;
try (
helpRdr = new StreamReader(helpfile);
}
catch(F1leNotFoundExcept1on exc) {
Console.WriteLine(exc.Message);
return false;
}
try {
do i
// Считывание символов, пока не будет найден символ
ch 4 helpRdr.Read();
// Сравнение тем.
if(ch == '#') {
topic = helpRdr.ReadLine();
if(what == topic) { // Тема найдена
do (
info — helpRdr.ReadLine();
if(info Е; null) Console.WriteLine(1nfo);
Е while((info E3 null) && (info !— ""));
helpRdr.Close();
return true;
}
}
} wh1le(ch != —1);
}
catch(IOException exc) {
Console.WriteLine(exc.Message);
}
helpRdr.Close();
return false; // Тема не найдена.
}
// Получение справки по указанной пользователем теме.
public string getSelection() {
string topic = "";
Console.Write("BBenMTe тему: ");
try {
topic = Console.ReadLine();
423
424 Глава 11. Ввод/вы
i
catch(IOExcept1on exc) {
C0nsole.wr1teLine(exc.Message);
return "";
ч
return toplc;
// Демонстрация возможностей файловой справочной системы.
class Fllefielp {
public static void Ma1n() {
Help hipobj — new He1p("heLpf;;e.tx:"):
string Eoplc;
Console.WriteLine("flposepxa справочной системы. " т
"Введите stop ДЛЯ выхОда."):
do {
topic = hlpobj.getSelecti0n(};
if(!hlp0bj.helpon(topic))
Console.WriteLine("TeMa не найдена.\п");
} while(topiC != "stop");
~-$£?§§§:;%i f?§@@$§i;’£’¢';’$§§§:§£
.- ВОП ОС. Ранее в ЭТОЙ главе же поминался класс потоков Memor Streané
э У У ‚
используемый mm организации хранилища в памяти компьютера. Каким образов}
может использоваться этот класс? 5
Ответ. Класс MemoryStream является реализацией ютасса Stream, которая
использует байтовый массив для организации ввода/вывода. Ниже приводится вызов
одного из конструкторов этого класса. ‘
MemorySyteam(byte{] buf)
3JlCCb параметр buf представляет собой байтовый массив. используемый в качествб
источника или цели для запросов ввода/вывода. Поток, создаваемый с помощью
конструктора, можно записывать или считывать. Он поддерживает метод Seek()-
Необходимо помнить о том, что Значение параметра buf должно быть достаточно
большим, чтобы mace MemoryStream MOI‘ принять весь адресуемый ему объем
данных.
Потоки, использующие хранилища в памяти, достаточно широко используются В
программировании. Например, можно организовывать поэтапный комплексный
вывод, сохраняя промежуточные фрагменты информации в массиве. Эта техника
является особенно полезной при программировании в графической среде. такой как
Windows. Можно также перенаправлять стандартный поток для чтения информации
из массива. Например это может оказаться полезным при тестировании программы.
И последнее замечание. Для создания символьного потока, основанного на исполь-
зовании памяти, воспользуйтесь потоками St‘:ingReader или Strlngwriter.
*‘“' м- ‹- -- и». ›. _ '\—-L же ..„:‚‚.„.-‹. ~-:~ ‚ 1 ‚- „_-.-‹е‚.. . же
43
Контрольные вопросы
Г
10.
425
Почему в C# определяются байтовые и символьные потоки?
Какой класс находится на вершине иерархии потоков?
Покажите, как следует открывать файл Для считывания байтов?
Покажите, как следует открывать файл для считывания символов?
Для чего служит метод Seek ( ) ?
Какие классы поддерживают двоичный ввод/вывод для встроенных
типов данных?
Какие методы используются для перенаправленьягт стандартных потоков
под управлением программы?
Каким образом можно преобразовать числовую строку, такую как
«123.23». в ее двоичный эквивалент?
Создайте программу, копируюшую текстовый файл. В результате вы-
Полнения программы все пробелы должны заменяться символами дефи-
сов. Используйте классы файлов байтового потока.
Перепишите программу из пункта 9 таким образом, чтобы она исполь-
зовала шассы символьных потоков. "
Делегаты, событиядё‘
Е
пространства имен
И ДОПОЛНИТЕЛЬНЫЕ
элементы языка С#5ЁЁ
CICICICICICICICICI
Делегаты
События
Пространства имен
Создание операторов преобразования
препроцессор
Атрибуты
Указатели и незащищенный контекст
Идентификация типа во время работы
программы
Дополнительные ключевые слова в С#
ё?
йузётй
я.
vi‘
делегаты 427
Итак. эта глава является последней в данной книге. В ней мы рассмотрим важные
„еиенты языка C# —~ делегаты, события и пространства имен, а также расскажем
об операторах преобразования, атрибутах, директивах препроцессора и некоторых
передовых технологиях, которые применяются главным образом в особых ситуациях.
делегаты
термин «делегат» звучит необычно для новичков, изучающих Cit. На самом деле
дедсгдггьт в понимании и использовании нс сложнее любых других Элементов этого
язьткгк. По своей структуре делегат — это объект, который может ссылаться на метод.
то есть при создании делегата создается объект, который может хранить ссылку на
Метол. Кроме того, эта ссылка может быть использована для вызова метода. Следо-
вательно, делегат может вызывать метод, на который он ссылается.
Хотя метод не является объектом, для него выделяется некоторая область памяти.
Адрес этой области памяти — это точка входа метода, и именно адрес инициируется
при вызове метода. Значение адреса метода (области памяти) может быть присвоено
делегату. Если делегат ссылается на метод. то данный метод можно вызывать е
помощью этого делегата. Кроме того, один и тот же делегат может использоваться
при вызове различных методов, для этого ему присваивается ссылка на требуемый
метод. Важным свойством делегата является то, что он позволяет указать в коде
программы вызов метода, но фактически вызываемый метод определяется во время
работы программы, а не во время компилирования.
Идимечанае у а у _ г п _
Если вы знакомы с языками С/С++, вам будет интересно узнать, что делегаты в С
сходны е указателями на функции в С/С++
к ли «же-атаман; г извел: „атаки:
„ьъсдхх .212. ‚г:
Для объявления делегата необходимо использовать ключевое слово delegate. Общая
Форма синтаксиса объявления делегата показана ниже.
delegate ret—I:ype name (parame2:er—lis2:);
3i1CCb словосочетание тее-суре - тип значения, возвращаемого методами, которые
бУдуг вызваны делегатом. Слово name — имя делегата. Параметры, необходимые
Методам при их вызове с помощью делегата, указываются в списке параметров
Pa: —..'neter-list. Делегат может вызывать только те методы, типы возвращаемых
Значений и списки параметров которых совпадают с типами и списками, указанными
“DH объявлении делегата.
Как уже говорилось, характерной особенностью делегата является возможность его
Использования для вызова любого метода, который соответствует подписи делегата. Это
{Щеп ВОЗМОЖНОСТЬ определить во время выполнения программы, какой из методов должен
°ЁПЬ вызван. Вызываемый метод может быть методом экземпляра, ассоциированным с
‘юъсктом, либо статическим методом, ассоциированным с классом. Метод можно
Вызвать только тогда, когда его подпись соответствует подписи делегата.
раССМОТрИМ простую программу, В КОТОРОЙ ИСПОЛЬЗУСТСЯ ДСЛСГЗТ.
/' — _
‘ Простая программа, В КОТОРОМ демонстрируется ИСПОЛЬЗОВАНИЕ делегата.
“Еще System;
428 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка о
// Объявление делегата.
delegate string strMod(string str);
class DelegateTest {
// Метод заменяет пробелы дефисами в строке, передаваемой ему в качестве
// параметра.
static string replaceSpaces(string a) {
Cons0le.WriteLine("3aMeHa пробелов дефисами.");
return a.Replace(' ', '-');
// Метод, предназначенный для удаления пробелов В передаваемой ему строкечё
static string removeSpaces(string a) {
П" I
‚
string temp д
int i;
Conso1e.WriteLine("ynaneume пробелов.");
for(i=O; i < a.Length; i++)
if(a[i] != ' ') temp +2 a[i];
return temp;
// Метод выводит передаваемую ему строку в обратном порядке.
static string reverse(string a) {
string temp е "";
int i, j;
Console.WriteLine("BuBon строки в обратном порядке.")г
for(j=0, i=a.Length-1; i >= 0; i~—, j++)
temp +2 a[i];
return temp;
public static void Main() {
// Создание делегата.
strMod strop а new strM0d(replaceSpaces);
string str;
// Вызов метода с помощью делегата. вьщо„мспЩдс,„„0дЫю„ш„шм
str = 5СгОр("Проверка работы методов.");‘<*—————*-дШЮГШ3-
Соп5о1е.Иг1ЕеЬ1пе("Преобразованная строка: " + str);
Console.WriteLine();
вихор = new strMod(removeSpaces);
str = strOp("UpoBepKa работы методов.");
Соп5о1е.Иг1сеЬ1пе("Преобразованная строка: " + str);
Console.WriteLine();
strOp 2 new strMod(reverse);
str = 5схОр("Проверка работы методов."):
Соп5о1е.Иг1СеЪ1пе("Преобразованная строка: " + str);
делегаты
X
рсзул ьтат ВЫПОЛ нения програм мы:
с „мена пробелов дефисами.
дрдсбразованнзя строка: Проверка-работьгметодов.
у _ —. :ex~;1/Le пробелов.
F: _ xgpaaosaaaasa строка: Проверкаработыметодов.
строки в обратном порядке.
„доразованная строка: дэодотем ытобар акреворг]
П;
Рассмотрим эту программу более подробно. В ней объявляется делегат strMod,
который принимает один параметр типа string и возвращает значение типа string.
B КЛНССС DeJ_egateTest объявляются три метода С модификатором statlc C соот-
ветствующей делегату подписью. Эти методы предназначены для преобразования
строки, передаваемой им в качестве параметра, Обратите внимание, что метод
_ acespases () для Замены пробелов дефиеами использует один из методов класса
„2-3, который называется Replace ().
B методе Ма1п() создается объект strop типа strMod, которому присваивается
ссылка на метод replaceSpaces(). B операторе
strmod strop 2 new st:rMod(replaceSpaces) ;
метод replaCeSpaCes() передается конструктору делегата в качестве параметра.
Используется только имя метода без указания параметра (то есть при создании
’)1<3cMn;mpa ДСЛЁГЗТЗ УКЗЗЫВЗСТСЯ ТОЛЬКО ИМЯ МСТОДЗ, На КОТОРЫЙ ДОЛЖЕН ССЫЛЗТЬСЯ
делегат). Подпись метода должна соответствовать подписи, определенной в объявле-
mm делегата. Если это условие не выполнено, при компилировании программы
произойдет ошибка компиляции.
В СЛСДУЮЩСЙ СТрОКС кода экземпляр делегата strop I/lCn0JIb3yCTC$l ДЛЯ вызова метода
‚ддасезрасез ().
ь;г = strOp("1'1pO25ep1~<a работы методов. ");
Поскольку объект strop ссылается на метод :ер1асе5ра5е5()‚ го вызывается
именно этот метод.
‚Ъщее в программе экземпляру делегата зсгор присваивается ссылка на метод
ломезрасез () . после чего экземпляр делегата зсгор вызывается вновь. На этот
раз инициируется метод removespaces () .
B saxsepujenne программы экземпляру делегата strop присваивается ссылка на метод
‘-Iers () , после чего вновь выполняется оператор
Lr = StrOp("UpOBepKa работы методов.");
На этот раз вызывается метод revers (). В данной программе при каждом вызове
их земпляра делегата st гор будет вызываться тот метод, на который ссылается делегат
пор во время вызова. Таким образом, вызывасмьяй метод определяется во время
ыполнения программы, а не во время компилирования.
3 этом примере использовались методы с модификатором зеаыс, но делегатам
может присваиваться также ссылка на методы экземпляра. Например, ниже приве-
Цена новая версия предыдущей программы, в которой операции со строкой инкап-
чулированы внутри класса StringOps.
._.‚ . „и“... .-. ныли ‘длин, ьиивпип, приырапьп по. имен И ДОПОЛНИТЕЛЬНЫЕ ЭЛЕМЕНТЫ ЯЗЫКЕ
// Новая версия предыдущей программы, в которой делегаты ссылаются на
// методы зкземпляра.
using System;
// Объявление делегата.
delegate Strlng strMod(str1ng str);
c;ass Stringops {
// Метод заменяет пробелы дёфисами в строке, передаваемой ему В качестве
// параметра.
рцЮ11с string rep1aCeSpaces(string a) {
Con5ole.WriteLine("3aMeHa пробелов дефисами.");
return a.Rep1ace(' ', '—');
// Метод, предназначенный для удаления пробелов E передаваемой ему строкед„
public string removeSpaces(string a) { *
string temp = "";
int ii
Console.WriteLine("Ynaneune пробелов.");
for(i:O; 1 < a.Length; i++)
if(a[i] != ' ') сет +— a[i1;
return temp;
// Метод выводит передаваемую ему строку в обратном порядке.
рцЬ11с string reverse(str1ng a) {
string temp = "";
int i, j;
Console.WriteLine("BmBon строки в обратном парядке.");
for(j=O, i=a.Length—l; i >= О; ;~-. j**)
temp +— а[1];
return temp;
class DelegateTest i
public static void Main() [ _
Stringops so = new Str1ngOps(); *
// Создание экземпляра делегата.
strMod strop = new strMod(so.replacespaces);4~————
string str;
Создание делегата е использо-
ванием методаэкзсмпляра
// Вызов метода с помощью делегата.
str : 5сгОр("Проверка работы методов.");
Соп5о1е.Иг1СеЬ1пе("Преобразованная строка: " + str)
Console.Wr1teLine();
strop = new strMod(so.removeSpaCeS);
str з 5пгОр("Проверка работы методов."):
ддлегаты
соп5о1е.Иг1сеЬ1пе("Преобразованная строка: " + str);
conso1e.WriteLine();
дпдор 2 new strMod(so.reverse);
51: x strOp("UpoBepKa работы методов.");
Соп$о1е.Иг1сеЬ;пе("Преобразованная строка: " + str):
i
В результате работы этой программы на экран выводятся те же етроки, что и в
Прдцьядуъцей. Но в данном случае делегат ссылается на методы, используя экземпляр
шасси St r1ngOps.
MHOl'0aD,peCHOCTb делегатов
одним из замечательных свойств делегата является его способность хранить несколь-
K0 адресов области памяти. Последовательно инициируя эти адреса, Делегат может
один за другим вызывать соответствующие методы. Эта характеристика делегатов
называется многоадресностыо. Такая последовательность вызываемых методов, или
цепочка ‚пе/подав, конструируется достаточно просто — сначала необходимо создать
Экземпляр делегата, а затем использовать оператор += для добавления методов K
цепочке. Хотя фактически при этом создается не цепочка методов, а Цепочка ссылок
на методы, в результате чего этот дслегат становится многоадресным. Для удгшения
метода из цепочки используется оператор —=. (Конечно, для добавления и удаления
методов из Цепочки можно по отдельности использовать операторы +‚ ы и г, но
применение составных операторов присваивания += и —= более удобно.) Единствен-
ное ограничение —— делегаты, хранящие несколько ссылок, должны иметь тип воз-
вращаемого значения void.
Ниже приведена Программа, дсмонстрирутоъцая использование многоадресного деле-
гата. Это новая версия предыдущей программы, в которой в качестве типа возвра-
щаемого значения методов, преобразующих строку, используется тип void, а для
возвращения вызывающей подпрограмме измененной строки применяется модифи-
катор параметра re f.
/I B программе демонстрируется использование многоадресности делегата.
us;ng System;
f Объявление делегата.
dczegate void strMod(ref string str);
F.ass Strinqops {
'/ Метод заменяет пробелы дефисами E строке, передаваемой ему E качестве
“/ параметра.
bzatic void replaceSpaCes(ref string a) {
Console.WriteLine("3aMeHa пробелов дефисами.");
а = a.ReplaCe(' ', '—');
’/ Метод, предназначенный для удаления пробелов в передаваемой ему строке.
static void removeSpaces(ref string a) [
string temp z "";
int i;
Соп5о1е.Иг1сеЬ1пе("Удаление пробелов.");
for(i=0; i < a.Length; 17+)
432
\
1
Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка;
if(a[i] != ' ') temp += a[i];
a = temp;
// Метод выводит передаваемую ему строку В обратном порядке.
static void reverse(ref string a) [
пп
string temp = ;
int i, j;
Conso1e.WriteLine("BHBOn строки В обратном порядке.");
for(j=O, i=a.Length—l; i >: O; i--, j++)
temp += a[i);
a temp,‘
public static void Main() {
// Создание экземпляра делегата.
strMod strOp;
strMod replacesp = new strMod(replaCeSpaCes);
strMod removesp = new strMod(removeSpaces);
strMod reversestr = new strMod(reverse):
string str = "Проверка работы методОв.";
// Делегату присваиваются две ссьшки.
strop = гер1асе5р;
a
Co’ a ис с о! иссьи ок. "
strop += reversestr;
// Вызов многоадресного делегата.
strOp(ref str);
Соп5о1е.Иг1ЕеЬ1пе("Преобразованная строка:
Console.WriteLine();
" т str);
// Удаление из цепочки ссылок ссылки на метод replacespaces И добавление
// ссылки на метод removespaces
strop —= replacesp; М
strep removesp:
Проверка работы методов."; // Присвоение строковой Переменной str
// строки "Проверка работы методов" в первоначальном виде.
str 2 "
// Вызов многоадресного делегата.
strOp(ref str);
Console.WriteLine("Hpeo6pa3oEaHHaH строка:
Console.WriteLine();
" + str);
Ниже приведен результат выполнения этой программы.
Замена пробелов дефисами.
Вывод строки в обратном порядке.
Преобразованная строка:
.водотем—Ытобар—акреворП
Вывод строки в обратном порядке.
Удаление пробелов.
Преобразованная строка:
.водОтемытобаракреворП
В методе Main () создаются четыре экземпляра делегата. Экземпляр strOp имеет
значение null, поскольку при создании он не был инициирован. Остальные трИ
433
Делегаты
‚делегата ссылаются на методы, предназначенные для Преобразования строки. Далее
в программе создается многоадресный делегат, который затем вызывает методы
:emoveSpaces () и reverse () . Это осуществляется с помощью трех операторов:
„скор replacesp;
5:rOp +: reversestr;
:;rOp(ref str);
B nepBoM операторе экземпляру strop делегата strMod присваивается ссылка на
цетод replacesp. Затем с помощью оператора += к цепочке присоединяется ссылка
на метод reversestr. При вызове объекта зскор вызываются оба метода. Первый
чстод Заменяет пробелы дефисами, а второй изменяет порядок символов в строке.
В операторе
›5СгОр -: replacesp;
сначала из цепочки удаляется ссылка объекта replacesp, а затем к цепочке прибав-
: ляется ссылка объекта removeSp:
ztrop + removesp;
Janee B программе вновь вызывается объект strOp с еще не преобразованной строкой
в качестве аргумента. Теперь из строки удаляются пробелы, и она выводится в
обратном порядке.
Преимущества ИСПОЛЬЗОВЗНИЯ делегатов
В предыдущем разделе описывалось использование делегатов, но не объяснялось, для
чего был создан этот элемент языка. Делегаты в C# применяются по двум причинам,
во-первых, они поддерживают события (которые мы рассмотрим в следующем разделе).
во-вторых, они предоставляют возможность выбора вызываемого метода во время
выполнения Программы, а не во время компилирования. Эта способность весьма
полезна, когда необходимо создание базовой конструкции (программы), в которую потом
чожно добавлять компоненты. Представьте себе программу для создания рисунков
«подобную стандартной программе Windows Paint). B нее можно добавлять CI1C1J.MEL'IbHbl€
световые фильтры или анализаторы изображений. Кроме того, программист может
создать последовательность таких фильтров или анализаторов. При использовании
делегатов эта схема модификации программы легко реализуется.
Минутные? практикум
1 .
2. Как объявляется делегат?
Что такое делегат? Какие преимущества дает использование делегатов’?
3. Что такое многоадресность?
Делегат — это объект, имеющий ссылку на метод. Делегат позволяет выбрать вызываемый
метод во время выполнения программы.
2. Делегат объявляется с помощью ключевого слова delegate, за которым указывается тип
возвращаемого значения, имя делегата и список параметров вызываемых методов.
3. Многоадрссность — это способность делегата хранить несколько ссылок на различные
МЕТОДЫ, ЧТО ПОЗВОЛЯЕТ при ВЫЗОВВ делегата инициировать 3'ry LI6l'I0‘IKy МЕТОДОВ.
434 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка с“)
События
Еще одним важным элементом языка C#, построенным на основе использованияб
делегатов, является событие. Фактически событие — это автоматическое ьтзвсщение;
о каком-либо произошедшем действии. Функционируют оно следующим образом; _
LU}! 06LCKTa, который в соответствии с его определением (кодом) должен реагироваты
на какое-то событие. регистрируется обработчик этого события. Когда событие.
происходит, вызываются все зарегистрированные обработчики. Обработчики собы-
тии создаются на основе делегатов. '
K,“
События являются членами класса и объявляются с использованием ключевого словаЁ
-'—2'-sent. Общий синтаксис объявления события представлен ниже. `
event even !:—delega се object~name;
A"
Здесь словосочетание event-delegate — 3T0 имя делсгата‚ используемого дляг‘›
поддержки события, а словосочетание object;~name — имя создаваемого объекта;
события.
Рассмотрим следующую программу: `*
' I
Очень простой пример использования события. ’__
using System;
,'/ Объявление делегата, на основе которого будет определено Событие. "г?
091993116 V0id MYEV€m’—H8nd1€1"() 7 4————1[9бъявлсние делегата для события.
// Объявление класса, в котором инициируется событие.
class MyEvent 1
\
public event MyEventHandler activate;
// B ЭТОМ методе ини ии ется событие.
Ц FY
public void fire о 4
if (activate !~ null) 4__—_:‘Onpc11e:xcHnc условия для инициализации события. ,',-
actlvate () ;
class EventDemo { '3
static VO1d handler() [
Console .WriteLine ("Произошло событие . ") ;
publlc statlc void Main() {
“YEW-nt М = new MvEvent<>-‘ ’
// Добавление метода handler() К цепочке обработчиков события.
evt . activate +: new MyEventI-landler (handler) ; Добавление метода к (возможной)
цспочкс обработчиков события.
// Инициирование События.
GVC - fire () ; Инициирование события.
1
события 435
Результат выполнения этой программы следующий:
ддэытие ПРОИЗОШЛО.
хотя данная программа достаточно проста, в ней содержатся вее основные Элементы,
„еобходгдмьте для правильной обработки события. Давайте рассмотрим ее подробнее.
программа начинается с объявления делегата для хранения ссылок на обработчики
события.
д„;е9 ate void MyEventHand1er();
Все обработчики события активизируются с помощью делегата. Следовательно,
делегат события определяет подпись события. В данном примере событие не имеет
параметров, но их наличие допускается. Поскольку, как правило, событие является
хшогоадресным, при его объявлении должен указываться тип возвращаемого значе—
Hm! void.
{МИСС в Программе создается класс МуЕчепе, в котором объявляется событие acti-
:e и определяются условия его инициирования. Событие объявляется в следующей
строке кода:
p;o;ic event MyEventHandler activate;
Обратите внимание на используемый синтаксис. Так объявляются все типы событий.
Внутри класса MyEvem: объявляется также метод fire () , который будет вызываться
программой для инициализации события. Условие, при котором выполняется ини-
циализация, определено с помощью оператора if.
ifi(aCtivate != null)
activate();
Обратите внимание, что обработчик вызывается только в случае, если делегат,
ассоциированный с событием activate, имеет значение, отличное от null.
Внутри класса Even tDemO создается обработчик события handler () . B этом примере
обработчик события просто выводит сообщение. Но можно определить обработчик
так, чтобы его операторы выполняли более содержательные действия. В методе
.~~.:n() создается объект типа MyEvent, а метод handler() регистрируется как
обработчик для события, объявленного в этом классе.
Hyfivent evt = new MyEvent();
Добавление метода handler() К цепочке обработчиков события.
1L.activate +: new MyEventHandler(handler);
()r3paTuTe внимание, что обработчик добавляется с использованием оператора +=.
C обытия поддерживают только применение операторов += и —=.
Наконец, происходит инициирование события.
Инициирование событие.
?С.Е1ге();
Вызов метода f ire () приводит к вызову всех зарегистрированных обработчиков
события. В данном случае вызывается только один зарегистрированный обработчик,
По их может быть и несколько.
436
Глава 12. Делегаты, события, Пространства имен и дополнительные эпеМВНТЫ ЯЗЫКЗ C#
широковещательное событие
Как уже говорилось ранес, события создаются на основе делегатов. А поскольку
ДСЛСГНТЫ МОГУТ бЫТЬ .\IHOl‘OaD.pCCHblMH, ТО СОбЫТИЯ МОГУТ НКТИВИЗИРОВЗТЬ НССКОЛЬКО
обработчиков, даже то. которые были определены в других объектах. Такис события
называются широковещателы:Ькми (что является синонимом слова многоадресные)
11x использование позволяст вптожюству Объектов «реагировать» На и3вСЫ1сние 0
событии. Ниже приведена программа, в которой используется широковещательное
событие.
// B программе демонстрируется использование широковещательного события.
us;ng System;
// Объявление
делегата,
на основе которого будет определена Событие.
celegate void MyEventHandler();
// Объявление
class MyEvent {
класса,
В котором инициируется событие.
public event MyEventHandler activate;
// B этом методе инициируется событие.
public void fire()
if(aCtivate
activate();
class X {
{
null)
public void Xhandler() {
Console.WriteLine("Coéumme получено объектом класса Х.
class Y {
п);
public void Yhandler() {
Console.WriteLine("Coéumme получено объектом класса
class EventDemo {
Y.");
static void handler() {
Console.WriteLine("Co6uTMe получено объектом класса
public static
MyEvent evt =
Х xOb = new X();
Y yOb = new Y();
// Добавление методов handler(),Xhandler()
// обработчиков
evt.activate +=
evt.activate +=
evt.activate +:
EventDemo.");
void Main() {
new MyEvent():
И ¥hand1er() Э Цепочку
события.
new MyEventHandler(handler);
new MyEventHandler(xOb.Xhandler); <————»———
new MyEventHandler(yOb.Yhandler);
Создание Цепочки
обработчиковдля
события
События 437
// Инициирование события.
evt.fire();
Console.writeLine();
// Удаление одного обработчика из цепочку.
evt.activate —= new MyEventHandler(xOb.Xhandler):
evt.fire();
Результат выполнения этой программы следующий:
Добытие получено объектом класса EventDem0.
Событие получено объектом класса Х.
событие получено объектом класса Y.
ьобытие получено объектом класса Y.
Зобытие получено объектом класса EventDemo.
B Этой программе создаются два дополнительных класса Х и Y, B которых также
определены обработчики событий, совместимые по своей подписи с типом MyEven-
tfiiandler. Следовательно, эти обработчики могут стать частью Цепочки. Обратите
внимание, что обработчики. определенные в классах Х и Y, не имсют модификатора
:atic. Это означает, что сначала в программе создаются объекты каждого класса,
а уже затем добавляется относящийся к объекту обработчик.
Необходимо понимать, что сгенерированньте события передаются отдельно каждому
экземпляру объекта, а не классу в целом. Следовательно, для получения извещения
о событии должен быть зарегистрирован (определен) обработчик каждого объскта
класса. Например, в приведенной ниже программе происшедшее событие (вызов
метода fire) инициирует обработчики трех объектов класса X.
/ На происшедшее событие реагируют обработчики каждого из объектов класса.
‚зтпо System;
‹/ Объявление делегата, на основе которого будет определено событие.
éelegate void MyEventHandler();
/ Объявление класса, В котором инициируется событие.
:1а$$ MyEvent {
public event MyEventHandler activate;
// B этом методе инициируется событие.
public void flref) {
if(activate E: null)
activate();
Class X {
lnt id;
Public X(int x) { id = x; )
publlc void Xhandler() {
Соп5о1е.Их1сеЬ1пе("Событие получено объектом М" + 1d):
1
438 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка Cm‘
class EventDemo { f
public static void Main() ( да
MyEvent evt = new MyEvent();
X ol = new X(l):
X 02 new X(2)
X 03 = new X(3)
Н
ч. ц.
evt.activate +— new MyEventHanuier(o1.Xhandler);
evt.activate ++ new MyEventHand;er(o2.Xhandler);
evC.activate +4 new MyEventHandler(o3.Xhandler);
// Инициирование события.
evt.fire();
}
B результате выполнения этой программы будет выведена следующая информация:
Событие получено объектом Ml
Событие получено объектом Ш2
Событие получено объектом ЮЗ
Как видите, для каждого объекта отдельно регистрируется обработчик события, и
можно сказать, что каждый объект получает отдельное извещение о происшедшем
событии. '
Примечание у
.... \-.:$ ;->‘-- „он V
.___1-...».,_»~.::.~\.(,_-.<»...- „ф, _.. .- ...,-,... .\-.- .,- x -— .:°-::›.—:-\‹ат‚›$‹с1
В C# разрешается со дание любог типа события. Однако для совместимости
компонентов со средой .NET Framework необходимо придерживаться традиций
программирования, свойственных компании Microsoft. "
„мзду
Ф Минутная? практикум 5
1. Что такое событие в C#, И какое ключевое слово используется для его
объявления‘?
Чем для события является делегат‘?
3. Могут ли события быть широковешатсльными?
Пространства имен
Еще в главе I мы говорили, что одним из базовых понятий C# является пространство
имен. Фактически, так или иначе, пространство имен используется в каждой C#—
программе. До этой главы у нас не было необходимости в детгшях рассматривать
пространства имен, поскольку его применение обеспечивается в Сшпрограммах по
умолчанию.
1. Событие — это извещение о каком-либо изменении в программе. Для объявления события
используется ключевое слово event.
2. Делегат служит основой для создания события.
3. Да.
rlpocrpancraa ИМЭН .
Сначала вспомним, что нам уже известно о пространстве имен. Пространство ш
Определяет область объявления, что позволяет хранить каждый набор имен (класс
интерфейсов) отдельно от других наборов. В C# имена, объявленные в одном п]
странстве имен, не конфликтуют с такими же именами, объявленными в друц
пространстве имен. Библиотекой .NET Framework (библиотекой C#) используе
пространство имен system, поэтому каждая С#-программа начинается строкой
„5;п9 System;
Как вам ИЗВЕСТНО изглавь111,классьд предна3наченньк:для ВЫПОЛНЕНИЯ операь
„подха/вывода, определяются В пределах пространства ИМЕН, ПОДЧИНЕННОГО (НЗХО,
ШСГОСЯ внутри) пространству ИМЕН System, которое НЗЗЬКВЗЕТСЯ System. IO. Kp(
1ого,СуЦЮСТВуЕТ МНОЖЕСТВО других пространств ИМЕН, ПОДЧИНЕННЬШ пространс
имен System, в которых хранятся другие части библиотеки Cit.
Пространство имен является очень важной составляющей языка C#, поскольку
последние годы было создано огромное количество переменных, методов, свойст
классов. В пространство имен могут быть включены библиотечные программы, к(
других программистов и организаций, а также ваши собственные коды. Без при
нсния пространства имен попытки использования классов с одинаковыми имен:
приводили бы к возникновению конфликтов при компоновке программы (наприн
если вы определите в своей программе класс с именем Finder, то возник
конфликт с другим классом Finder, входящим в состав библиотеки, созданп
другим программистом и применяемой в вашей программе). К счастью, в C#
проблема решена — использование пространства имен позволяет ограничит
стбласть видимости имен, объявленных в его пределах.
Объявление пространства имен
Пространство имен объявляется с помощью ключевого слова namespace. Обт
синтаксис объявления пространства имен показан ниже.
namespace name {
// Члены класса
Здесь name — название пространства имен. Все элементы, которые определ
внутри пространства имен (это могут быть классы, структуры, делегаты, nape-
иптерфейсья или другие пространства имен) находятся в пределах области видим
этого пространства имен.
Ниже приведен пример создания пространства имен Counter. B нем локализу
лил — класс CountDown, используемый для реализации простого счетчика обратт
огсчета.
Объявление пространства имен, локализующего классы, B которых реализов
различные способы подсчета чего—либо.
1-éiftlespace Counter 1’ 6LgmeHne пространства имен соипсед)
// Класс, предназначенный для обратного отсчета.
class CountDown (
int val;
public CountDown(int n) { val = п; }
public void reset(int n) {
440 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка с,
Ё‘
val е n;
public int Count() {
if(val > O) return val——:
‘V4’.-1.»;-
else return O;
I
B приведенном выше коде класс =3:-untnviwn Объявляется в пределах области видит
MOUTH, 0l1pC11c.-‘mc.\1m?'1 прост ранетвоч ииен Г‘ :-:'.nL»:zr.
Теперь рассмотрим программу, в котором ‚1е\4онетрируегс›| исгподпьзованнс простран- '
ства имен Counter.
// B программе демонстрируется использование пространства имен. ‚
using System;
// Объявление пространства имен, локализующего классы,
// в которых реализованы различные способы _;
// подсчета чего—либо. '~
namespace Counter { `
// Класс, предназначенный для обратного отсчета. 5%”
Class Countbown ( ;§£
‘ jun
Lnt val; ‚,„
public CountDown(int n) { val n; } j
public void reset(int n) {
val = П;
public int count() { J4
if(val > O) return val——; 3;
else return 0; _Д
1 Здесь пространство имен Counter используется
1 для полного определения местонахождения класса
Countuown. .:§
class NSDemo { §.
public statlc void Main() { '§
Coun:er.CountDown cdl = new Counter.CountDown(1C); Ё
int i; A
а
do { Ё
1 = cdl.count(); Ё
Console.Wr1te(i + " "); ё
} while(; > 0); Ё
Console.Wr1teLine(); *
Counter.CountDown cd2 L new Counter.CounLDown(20):
т
do {
i — cd2.counL();
ConsOle.Write(1 + " ");
} while(1 > C);
пространства имен 44
canso;e.Wr1teL1ne();
ca2.reset(4);
со 1
L — ca2.coun:();
Consc;e.write(i + " ");
; while(i > O):
Console.WriteL1ne();
РСЦПЬТНГВЫПОЛНСНИЯ программьпвьнлядителсдукпдизпобразом:
E З 7 6 5 4 3 Ё 1 C
с L5 18 L7 16 15 14 13 12 ll LO 9 Ё 7 6
1 O
U‘
‚Ь
ш
ГЦ
‚‚_.
C)
‚
ф.) г)
Подробно проанализируем некоторые важные моменты этой программы. Поекольк:
класс Coum:Down объявляется в пределах пространства имен Counter, при созданиъ
объекта имя ютаеса CountDown должно указываться втяесте C пространством имеъ
-: шеек, как показано ниже:
С:дп:ег.СощпсВошп cdl е new Counter.CountDown(lO);
Однако После Создания объекта нет никакой необходимости указывать проетранетвс
имен перед Именем объекта или его членов. Сдедовател ьно, метод cdl . count ()
МОЖНО вызывать непосредственно:
; cal.count();
Директива using
Как уже говорилось в главе 1, очень утомительно каждый раз указывать пространство
имен. когда в программе имепот место частые обращения к членам этого пространства
имен. Использование директивы using облегчит вам эту задачу. Для всех программ
данной книги пространство имен System“ делается видимым путем его указания
nu-.__rc директивы us;nq. СЛСДОВЗТСЛЬНО. директива using может также использо-
виться для того, чтобы сделать видимыми создаваемые вами пространства имен.
C3 шествуют две формы синтаксиса директивы using. Первая форма выглядит так:
ц„„п9 name;
Здесь слово name указывает имя пространства имен, к которому необходимо полу-
‘ПНЬ доступ. Эту форму синтаксиса директивы using вы уже встречали. Все членьь
"”DL‘JcncHHb|e B пределах указанного пространства имен, становятся доступными (то
ест, становятся частью текущего пространства имен) и могут использоваться без
Полного определения местонахождения. Директива using должна быть указана В
Начале каждого файла. перед всеми другими объявлениями.
Ниже показана новая версия предыдущей программы„ в которой директива uslng
используется для того, чтобы сделать создаваемое пространство имен видимым.
B программе демонстрируется использование пространства имен.
-sLng System;
C помощью директивы using пространство Имен Counter становится видимым
" для кода этой программы.
442 Глава 12. Делегаты, собьпия. пространства имен и дополнительные элементы языка
using Counter; ‘____________.__ Использование директивы using делает
видимым пространство имсн Counter. ’
// Объявление пространства имен, локализующего классы, в которых реализованы:
// различные способы подсчета чего—либо. `
namespace Counter { `
.
// Класс, предназначенный для обратного отсчета.
class CountDown {
int val;
м...‘ . .
public CountDown(int n) ( val = n; }
public void reset(int n) {
val = п;
‘- .' m...r.n.:mma
public int count() { V
if(val > О) return va1—-; `
else return 0;
class NSDemo {
public static void Main() (
// Теперь класс CountDown может использоваться непосредственно без
// указания его местонахождения.
CountDown cdl :'new CountDown(l0);
Теперь имя (класс) CoLmtDown
используется непосредственно.
int i; a ›
do { -
i = Cd1.count(); fie;
Console.Write(i + " "); :*
} while(i > O): __‚
Console.WriteLine(); 1”
CountDown cd2 = new CountDown(20);
do {
i = cd2.count():
Console.Write(i +
} while(i > О);
Console.WriteLine();
„ ");
cd2.reset(4);
do {
i = cd2.count();
Console.Write(i + " ");
} while(i > O); f‘
Console.WriteLine(); i
}
Обрати“? ВНИМЭНИБ На ВЩВ ОДИН важный момент. В Этой программе одно простран‘
ство имен нс скрывает ДРУГОГО пространства имен. При объявлении пространства _
Пространства имен 443
имен видимым оно ‚чобавляст свои имена к другим Доступным в настоящий момент
‚птснам. Таким образом, оба пространства имен, System и Counter, становятся
видимыми для кода этой программы.
вторая форма синтаксиса директивы using
Директива us-mg имеет и вторую форму. показанную ниже.
u5_nq alias — name;
Здесь слово alias: (псевдоним) становится другим именем для класса или простран-
стш имен, указывасмого вместо слова name. Далее представлена еще одна версия
прсдшыдушсй программы, в которой создастся псевдоним Count класса Coun-
_»V.i- -.“.ountDown.
/; B программе Демонстрируется использование псевдонима.
using System;
/j создание псевдонима класса Counter.CountDown.
u5_ng Count 2 Counter.CountDown; ‹——————{ЁощшниепсшщонимаклшхаСоцпсег.Соыппвоыц
/‚ Объявление пространства имен, локализующего классы, в которых реализованы
// различные способы подсчета чего—либо.
namespace Counter {
if Класс, предназначенный для обратного отсчета.
class CountDown {
int val;
public CountDown(int n) { val = n: }
public void reSet(int n) {
val = п;
public int count() {
if(val > O) return val~-;
else return O;
2-155 NSDemo {
public static void Main() (
Count cdl = new Count(l0)i
int L;
do {
i : cdl.count();
C0ns0le.Write(1 + " ");
} while(i > 0);
Console.writeLine():
Count cd2 = new Count(20);
do {
i = cd2.count();
444 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка ch]
fi‘__>‘__
г»
„ п);
Console.Write(i +
ъ while(i > с); д,
Console.WriteL1ne(); Д;
cd2.reset(4);
do {
i е cd2.count(); д
Console.Write(i + " ");
} wh;le(1 > O);
Console.WriteLine();
1
После создания псевдоним Count класса Counter . CcuntDown можно использовать
для объявления объектов без указания пространства имен. Например, строка кода
Count cdl = new Count(l0);
создает объект типа CountDown.
Аддитивность пространств имен
В C# CyLL1€CTByCT возможность использования одного имени для объявления более
одного пространства имен. Это позволяет в одной программе делать видимыми
несколько пространств имен, имеющих одинаковое имя. Например, в следующей‘
программе определены два пространства имен Counter. B одном содержится класс
CountDown, BO втором содержится класс CountUp. При компилировании члены обоих и.
пространств имен Counter становятся видимыми. ‚_
// B n ог амме демонст И ется аддитивность п ост анств имен.
P
using System;
// С помощью директивы us;ng пространства имен Counter становятся видимыми
// для кода этой программы.
using Counter;
// Объявление Одного пространства имен Counter.
namespace Counter { 4’
// Класс, предназначенный для обратного отсчета.
class CountDown {
int val;
public CountDown(1nt n) { val — n; }
B этой прокраммс объявляются два
пространства имен Counter.
public void reset(int n) {
val = n;
public int count() {
if(val > О) return val—~;
else return О;
// Объявление еще одного пространства имен Counter.
namespace Counter { 4
// КЛаСС‚ Предназначтчный для отсчета ы порядке возрастания.
445
пространства ИМЕН
glass CountUp {
1nL val;
;n: target;
publ;c 1n: Target { geL{ return target;
public CountUp(1n: n) { target — n: val — O; }
public void reset(1nz n) {
target — n;
val ; O;
public int coun:() {
if(val < target) return val++;
else return target;
C.aSS NSDemo {
,,bl1c static мода Main() {
C0untDown Cd = new CountDown(lO);
COuntUp Cu 2 new C0unCUp(8);
LUC 1;
do (
i = cd.count();
Conso1e.wr1te(; + " ");
] wh1le(i > O);
Console.WriteLine();
do {
1 = cu.count();
C0nsole.Write(i + " ");
] while(i < cu.Target);
При выполнении программы на жран будут выведены числа:
L9876543210
C_2345678
0братите внимание, что оператор
J .ng Counter;
дкгшет видимым все содержимое пространства имсн Саше-эк. Следовательно, доступ
К Нбоим массам Countaown и Ccunt-Up МОЖСТ осуществляться непосредственно без
Уёцзания пространства имен_ То, что пространство имен Стпцёек было разлеглено на
-1'-c части. не имеет значения.
Вложенные пространства имен
Пространство имен может быть вложенным. Рассмотрим следующую программу:
" В программе демонстрируется использование вложенного пространства имен.
”‘ing System;
446 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка щ
namespace NS1 ( ,_
class ClassA ( '
public ClassA() ( ,
Console.WriteLine("OnepaTop конструктора класса ClassA.");
Ъ Пространство имен NS2 является вло-
} жснным в пространство имен NS1.
namespace NS2 ( // Объявление вложенного пространства имен_
class ClassB (
public ClassB() Ё
Console.WriteLine("OnepaTop конструктора класса С1а55о.");
Class NestedNSDemo (
public static void Main() { д
NS1.ClassA а= new NS1.ClassA(): ;§‘
и м. . ,.....«.- ‘ч.
// NS2.ClassB b = new NS2.ClassB(); // Ошибка!!! Местоположение пространствд
// имен NS2 не указано полностью,
// ПОЭТОМУ ОНО Не будет видимо для
// кода программы.
б.
NS1.NS2.ClassB b = new NS1.NS2.ClassB(); // ЭТО правильное полное
// определение местонахождения класса ClassB.
} “I
Результат выполнения программы будет следующим:
Оператор конструктора класса ClassA
Оператор конструктора класса ClassB. ›ЁЁ
‘ .
‘J
B ЭТОИ программе пространство ИМСН NS2 ЯВЛЯЕТСЯ ВЛОЖСННЫМ В пространство [AME _ ц
N51. Поэтому для получения доступа к классу ClassB необходимо полностью указатВЁ
д
его местоположение, то есть, используя операторы точка (.), указать оба пространства?
ИМСН —-пространство MMCH верхнего уровня NS1 И ВЛОЖСННОС NS2,
Вложенное пространство имен можно объявить в одной строке кода, используя одно '5
ключевое слова namespace и разделяя пространства имен оператором точка. Напри‘.
мер, фрагмент кода
namespace OuterNS (
namespace InnerNS (
//
Ъ
МОЖНО переп ИСЭТЬ ТЗКП
namespace OuterNS.InnerNS (
//
['IpOCTpaHCTBa VIMBH
пространство ИМЕН, ИСПОЛЬЗУЕМОЕ ПО УМОЛЧЗНИЮ
Если в программе не объявляется пространство имен„ то используется пространство
„мы. определенное по умолчанию. Поэтому в предыдущих программах вам не нужно
бьшо использовать ключевое слово namespace. Пространство имен по умолчанию
\'_'1U6H0 применять для коротких простых программ, но код больших прикладных
, Ьрограмм должен содержаться в каком-либо пространстве имен. Код необходимо
иикапсулировать в пространство имен для предотвращения конфликтов имен.
пространство имен — это один из Элементов Языка, помогающий в организации
прокрамм И делающий их жизнеспособными в современном сложном сетевом окру-
женин.
Минутные драхма/кум
Что определяет пространство имен?
Напишите две формы синтаксиса использования директивы using?
Могут ли два пространства имен с одинаковыми именами использоваться в
одной программе?
Ь)?м>-—
Проект 12-1. Помещение класса Set B
пространство имен
И Setcs, SetDemo.cs
B проекте 7-1 был создан класс Set, B котором имитировалось множество. Теперь
необходимо поместить этот класс в собственное пространство имен, поскольку он
может конфликтовать с другими классами, имеющими такое же имя.
д Пошаговая инструкция
1. Используя файл SetDemo.cs ИЗ проекта 7-1, перенесите весь код класса set в
файл Set.cs. При этом поместите этот код в пространство имен MyTypes . set.
K'dK n0Ka3aH0 ниже:
[Ж
Проект 12—1
Помещение класса Set B пространство имен.
*/
using System;
namespace MyTypes.Set (
class Set (
char[] members; // Массив, содержащий символы (имитирующий множество).
in: len; // Количество элементов множества.
Пространство имен определяет область видимости.
using namel{using alias г name.
Да.
L-Jl~J-'
448
Глава 12. Делегаты, события, пространства имен и дополнительные элементы нзьпка С
// Создание пустого множества.
public Set() {
len = O;
// Создание пустого множества заданного размера. Ё
public Set(int size) ( E
members = new char[size]; // Выделение памяти для множества.
len = 0; // Элементы не были сконструированы.
I
// Конструирование множества на базе другого множества.
public Set(Set s) {
members = new char[s.len]; // Выделение памяти для множества.
for(int i~O; i < s.len; i++) members[i] = s[i]; _
len = s.len; // Количество элементов множества. j
} .
// Определение свойства Length, предназначенного только для чтения. ›
public int Length ( f
geti
return len;
}
u
} ё
// Определение индексатора‚ предназначенного только для чтения. L
public char this[int idx]{
get {
if(idx >= O & так < 1еп) return members[idx];
else return (char)O;
/* Если элемент входит в состав множества, метод find возвращает индекс
элемента, если не входит — возвращается значение ~1.
*/
int find(char ch) {
int 1;
Еог(1=0; i < len: i++)
1f(members[i] ch) return 1;
return -1;
// Добавление уникального элемента в множество.
public static Set operator +(Set ob, char ch) {
Set newset — new SeE(Ob-len + 1); // Увеличение числа элементов
// нового множества на единицу.
// Копирование элементов.
for(int i:O; 1 < ob.len; i++)
newset.members[i] = ob.members[i];
// Переменной 1еп возвращаемого объекта присваивается значение
// переменной len копируемого объекта, то есть передается размер
npocrpahcnaa ИМеН
// множества.
newset.len н ob.len;
// Выполняется проверка — имеется ли добавляемый символ в множестве.
if(ob.find(ch) —— —1) { // Если элемент не найден,
// он добавляется в новое множество.
newset.members{newset.len] — ch;
newset.len++;
Б
return newset; // Возвраь нового множества (объекта класса зсь)
// Удаление элемента из множества.
public static Set operator —(Set ob, char ch)
Set newset — new Se:();
ifit i — Ob.fiUd(Ch); // Если элемент не найден,
// переменной 1 присваивается значение 1.
// Копирование оставшихся элементов с использованием перегруженного
// оператора т.
for(int j=O; j < ob.len; jv+)
if(j != i) newset = newset 4 ob.members[jj;
return newset;
// Объединение множеств.
public static Set operator +(Set obl, Se: ob2)
Set newset = new Set(ob1); // Копирование первого множества.
// Добавление уникальных ЭЛЕМЕНТОВ ИЗ ВТОРОГО множества.
for(int i;O; 1 < oD2.len; l++)
newset ; newset + ob2{i];
return newset; // Возврат новошо множества.
// Разность множеств.
public static Set operator -(Set obl, Set ob2) {
Set newset — new Set(Obl): // Копирование первого множества.
// вычитание из первого множества элементов второго множества.
for(int i=0; i < ob2_len; 1+‘)
newset — newset — ob2{i];
return newset; // Возврат нового множества.
}
Ix.)
После помещения macca Set B пространство имен M'_,'Types.Set для использо-
вания этого класса необходимо включить пространство имен :~IyTypes.5e: B
программу, как это показано ниже:
using MyTypes.Set;
чаи I лава м. делегаты, События. пространства ИМЭН И дополнительные ЭЛеМеНТЫ fi3b|Ka
3. Если этого не сделать, в программе необходимо будет каждый раз при обращении,
к классу Set полностью указывать его местоположение, как показано в следую„
щей строке кода:
MyTypeS.S€C.SeE sl 2 new MyTypes.Set.Set();
Операторы преобразования
ИНОГДЗ ВОЗНИКЭСТ НСОбХОДИМОСТЬ ИСПОЛЬЗОВННИЯ ОбЪСКТОВ КЛЗССЭ В выражениях,
содержащих другие типы данных. Для решения этой задачи можно использовать
перегрузку одного или нескольких операторов. Но чаще всего необходимо простое
преобразование из одного типа класса в другой. Для этого в С# Предусмотрена
возможность создания (mepamopoe преобразования, с помощью которых объект соз-
данного вами класса можно преобразовать в другой тип.
Существуют две формы синтаксиса операторов преобразования ~ неявная и явная.
Они показаны ниже:
public static operator implicit target—type(source-type v) {return value;)
public static operator explicit target~type(source—type V) (return valuei}
Здесь словосочетание target‘-type ~ это тип, в который преобразуется объект
(конечный тип), словосочетанис source-type — исходный тип, элемент V — ecu-
лочная переменная. которая ссылается на объект. а слово value ~ значение объекта
после преобразования. Операторы преобразования возвращают данные только типа
target-type.
Если при определении оператора указано неявное преобразование (implicit), то
преобразование инициируется автоматически, например, при использовании объекта
в выражении с переменной, имеющей тип target—type (то есть тип, в который и
преобразовывался объект при определении оператора преобразования). Если при
определении оператора указано явное преобразование (explicit), то преобразова-
ние инициируется при использовании приведения типов. Одной паре (объекту
исходного типа и типа, в который преобразуется объект) нельзя задавать два опера-
тора преобразования, для одного из которых было бы указано явное преобразование,
а для другого неявное.
Чтобы продемонстрировать работу оператора преобразования, используем класс
ThreeD, созданный в главе 7. Вспомним, что класс Threeb хранит координаты
некоторой точки в трехмерном пространстве. Предположим, что необходимо преоб‘
разовать объект типа ThreeD B целочисленный тип таким образом, чтобы при CFO
использовании в целочисленном выражении содержалось значение, равное произве-
дению значений всех трсх координат. Для этого используем оператор неявН0Г0
преобразования:
public static implicit operator ;n:(ThreeD opl)
i
return ор1.х ' opl.y * ор1.2;
1
Дгшсе приведена программа, в которой используется оператор преобразования.
// В программе Демонстрируется использование оператора преобразования.
using System;
операторы преобразования 45 1
// КлдСС‚ Содержащий Координаты объекта В трехмерном пространстве.
д,а$$ ThreeD (
;nt х. y, 2; // Координаты объекта.
public ThreeD() ( x 2 y > 2 » O; }
Public ThreeD(int i, int j, int k) 1 x = i; y = 3; 2 z k; }
Перегрузка бинарного оператора 4 для пары операндов,
имеющих тип ThreeD.
publlc static ThreeD operator +(ThreeD opl, ThreeD op2)
ThreeD result — new ThreeD();
result.x = ор1.х + ор2.х;
:esu1t.y — op1.y + op2_y;
result.z 1 opl.z 4 op2.2;
return result;
£/ Определение оператора, для которого указано неявное преобразование из
/ типа ThreeD B ТИП int.
public static implicit operator int(ThreeD opl)
д Оператор, выполняющий преобра-
зованис из типа ThreeD B ‘ГНП 1nt.
return opl.x * ор1.у * ор1.2;
:/ Отображение координат Х, Y, Z.
publlc void show()
i
Console.WriteLine(x + ", " + у т ", + 2);
class ThreeDDemo (
public static void Main() (
ThreeD a = new ThreeD(1, 2, 3);
ThreeD b new ThreeD(10, 10, 10);
ThreeD с = new ThreeD();
lnt 1;
I!
Console.Write("Koopnmuawu объекта а: ");
a.show();
Console.WriteL1ne();
Соп$о1е.Иг1пе("Координаты объекта b: ");
b.show();
Console.WriteLine();
C = а + b; // Сложение объектов а и b.
Console.write(“Pe3ynbTaT выполнения операции сложения “ +
"объектов а и b: ");
c.show(); И 6
; , _ нициирустся прсо pa30B2lH нс
Console.WrrteL1ne(), BTMHIDL
1 И а; // При выполнении этого оператора присваивания инициируется
// неявное преобразование объекта а в тип Ant.
452 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка
Соп5о1е.Иг1пеЬ1пе("Результат выполнения выражения 1 а: + 1);
Conso;e.Wr;;eL1nc(); иН„Ц„„рУшС„,„ко5д„0„д„„с
В ТИП Int.
д _ a * 2 b; 1/ Выполняется неявное преобразование двух объектов а И b B
/./ ШИН :_n:.
Соп5о1е.Иг;пеЬ1пе("Результат выполнения выражения а * 2 т b: " т 1);
Э
При выполнении программы будут получены следующие резульгатья:
Координаты объекта а: L, 2, 3
Координаты объекта D: 1C, LO, 1C
Результат выполнения операции сложения объектов a И b: ll, 12, 13
Результат выполнения выражения 1 ~ а: 6
Результат выполнения выражения а * 2 — b: -988
Как показано в программе. При использовании объекта типа Three!) B целочисленном
вьяражении, таком как : а, инициируется оператор нсявного преобразования.
В данном конкретном случае при выполнении оператора преобразования возвраща-
ется значение 6, которое является произведением значений координат, хранящихся
в объекте а. Однако если выражение не требует преобразования в тип int, оператор
преобразования нс вызывается. Поэтому при выполнении выражения C я а + ь
вызов оператора преобразования орегайог шк () НС требуется.
Помните, что для решения различных задач можно создавать различные операторы
преобразования, Например, можно определить оператор для преобразования в тип
double или long. Каждый такой оператор применяется автоматически и независимо
от остальньях.
Оператор неявного преобразования применяется автоматически в следующих слу-
чаях: когда преобразование требуется в выражении, объект передается методу, вы-
полняется оператор присваивания, используется явное приведен ис типов. В качестве
альтернативного можно создать оператор явного преобразования, который применя-
ется только при использовании явного приведения типов и нс вызывается автомати-
чсски. Например, ниже приведена переработанная предыдущая программа, в которой
используется явное приведение к типу int
// B программе демонстрируется использование оператора явного
// преобразования.
us;nq System;
/I Класс, Содержащий координаты объекта в трехмерном пространстве.
c;as5 ThreeD {
105 X, Y: Z; // Кэординаты объекта.
Public ThreeD() { x — y = z : 0; }
public ThreeD(in: i, int j, in: k) { х v 1; y : j; z : k; i
5/ UCPCUPy3Ka бинарного оператора + для пары операндов,
4' I
.4 имеющих тип ThreeD_
publ;c SCdLic ThreeD operator +(Th;eeD Opl, Th[eeD Opg)
операторы преобразования
453
ThreeD result — new ThreeD();
result.x 2 ор1.х + ор2.х;
result.y е ор1.у 4 op2.y:
result.z — op1.z + op2.z;
return result;
:, Теперь определяется оператор явного преобразования.
public static explicit operator inL(ThreeD ор1)-‹————н————Т”шр”"шЮбдвшЩ“"°
явное
return opl.x * opl.y ‘ ор1.2;
'/ Отображение координат Х, Y, Z.
public void show()
I
Console.WriteL1ne(x + ", " + y + "‚ " 4 z);
mass ThreeDDemo 4
public static void Main() {
ThreeD a = new ThreeD(l, 2, 3);
ThreeD b L new ThreeD(lO, 10, 10);
ThreeD с = new ThreeD();
int i;
Сопзо1е.Иг1пе("Координаты объекта а: ");
a.show();
Console.WriteLine();
Соп$о1е.Иг1пе("Координаты объекта b: ");
b.show();
Console.WriteLine():
c 2 a + D; // Сложение объектов а и b.
Соп5о1е.Иг1пе("Результат выполнения операции сложения " +
"объектов а и b: ");
c.show();
Conso1e.WriteLine();
i = (int) a; // Для выполнения этого оператора присваивания требуется
// выполнение операции приведения типа, после чего будет вызван
// оператор явного Преобразования объекта a В тип ;n:.
Console.WriteL;ne("Pesynbwaw ВЫПОЛНСНИЯ выражения 1 — a: т i);
C0“501e-WriCeLln€(); 'finmph1mfiymcHnmumummm
OllCD'dl1HVJ ПрППСдСНИЯ ТИПЫ
i L (int)a * 2 —
СоП$о1е.Иг1ЕеЬ1пе("Результат выполнения выражения а * 2 — b:
(int)b; // Требуется выполнение операции приведения шипа.
" + i);
454 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка Cg ‘
y
Поскольку теперь оператор преобразования указан с ключевым словом explicit‘ }
для его инициации и преобразования объекта класса ThreeD B тип int требуется”
выполнение операции приведения типа. Например, если в операторе
1 = (1пс) а;
удалить операцию приведения ТИПЭ, програзамгх HC бУДСТ скомпилирована.
«Ответы дрдфессдвуаид
д; Вопрос. Если оператор неявного преобразования вызывается автоматически
без использования операции приведения типа, зачем нужно создавать оператор -__'5
явного преобразования?
Ответ. Хотя неявное преобразование удобно в использовании, вы должны
применять его только тогда, когда уверены, что преобразование не приведет к
возникновению ошибки. Оператор неявного преобразования можно создавать только ._._
B случае выполнения двух условий. Во—первых, при преобразовании не должна быть ‘
потеряна информация, что происходит при уссчении, переполнении или потере
Знака. Во-вторых, преобразование не должно приводить к возникновению исключи-
тельной ситуации. Если не соблюдается хотя бы одно из этих условий, нужно
использовать явное преобразование.
ДЛЯ ИСПОЛЬЗОВЭНИЯ операторов ПрСОбРЭЗОВЭНИЯ СУЩЕСТВУЮТ НСКОТОрЫС ОГрННИЧСНИЯ.
O Нельзя создавать оператор, который выполнял бы преобразование из встроенного
типа в какой-либо другой. Например, нельзя переопределить операцию приведе—
ния типа double K типу int.
B операторе преобразования нельзя использовать тип object.
O Л
O Нельзя определять неявное и явное преобразование для одной пары типов, Ё-
состояшеи из исходного и конечного типов.
O
O
Нельзя определять преобразование из наследуемого класса в наследующий.
В операторе преобразования нельзя использовать интерфейс.
Минутные? практикум
l Для чего предназначен оператор преобразования? с
2. Чем отличается оператор явного преобразования от оператора неявного
преобразования?
3. Можно ли использовать оператор преобразования для преобразования ИЗ
встроенного типа?
1. Оператор преобразования предназначен для преобразования в тип класса или из типа класса-
Оператор неявного преобразования вызывается автоматически. Оператор явного преобрг‘ j‘ I’
зования должен вызываться е использованием операции приведения типа. :
3. НЕТ. 3
yo
препроцессор 455
препроцессор
В С# определены несколько директив препроцессора, которые влияют на способ
интерпретации компилятором исходного файла программы. Эти директивы изменя-
ют гскст исходного файла, в котором они встречаются, перед трансляцией программы
в объектный код. Директивы препроцессора в основном пришли в C1? ИЗ С++_
срлктитяески препроцсссоры в этих языках очень похожи. Данные инструкции полу-
чъыи название директив препроцессора. поскольку они традиционно обрабатывались
в отдельной фазе компилирования. которая называлась препроиессоро/и. Современные
технологии компилирования больше не требуют отдельной фазы для обработки
директив, но имя этих инструкций осталось прежним.
в Cpl определены следующие директивы препроцессора.
#аеЕ1пе #e11f #else #end1f
fiendregion #error #if #line
firegion #undef #warning
Все директивы препроцессора начинаются со знака #. Кроме того, каждая из них
должна находиться на отдельной строке программы.
Современная объектно-ориентированная архитектура C# не так нуждается в дирек-
тнвах препроцессора, как архитектура более старых языков, но иногда директивы
препроцессора могут быть полезны, особенно для условной компиляции.
Далее по порядку будут рассмотрены все директивы препроцессора.
Директива препроцессора #define
C помощью директивы #define определяется символьная последовательность. K0-
торая называется символьной константой (symbol). Наличие или отсутствие конкрет-
пой символьной константы определяется с помощью директивы #if ИЛИ #el1f И
может использоваться для управления компилированием. Ниже приведен общий
синтаксис директивы #define.
#define symbol
Обратите внимание, что в этом операторе отсутствует точка с запятой. Между
директивой #define И символьной константой может быть произвольное количество
пробелов, а признаком окончания символьной константы является символ новой
Строки. Например, mm определения символьной константы EXPERIMENTAL Hc06x0-
Juno использовать директиву
#.:.—:f1ne EXPERIMENTAL
г.
Вопрос. Я знаю, что в С++ директиву #define можно использовать для
Выполнения текстовых подстановок, например, определения имени для значения или
.1тя создания макросов, аналогичных функциям. Поддерживаются ли в C# Такие
свойства директивы #define?
OTBeT. Нет. В С# директива #define используется только пля определения
символьной константы.
‘Ч
456 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка Cg
Директивы препроцессора #if И #endif
Использование директив препроцессора м: и ¥.5er1Ij1f nU3ls0JmCT условно компили_
ровать ттое.'1с,г1оватсльткость кода. Их вьптолттепис зависит от рсзуьчьтата Оценки
выражения. вюпочаютпсго одну или более символьпьтх констант. Выражение может
принимать ЗНЗЧСНИС ЁЕЫе ИЛИ Ёсзщ CH.\Il30.IbllEl$l K()H(.'I2lH'IEl ПрПНИМНСГ ЗНИЧСНиС
г где, если опа оттрслслсна Директивой #~:‘r;:f1:~.v:3.
Общий синтаксис Директивы #15 следующий:
#if symbol~express10n
statement sequence
#endif
Если вьтражснис, следующее за директивой #:f, принимает значение true, то код,
находящийся между директивами мг и #епс11Е‚ будет скомпилирован. В противном
случае этот код пропускается компилятором. Наличие директивы #end1f указывает
на окончание блока операторов директивы #;f.
Символьное выражение может быть простым и состоять из одной символьной
константы. В символьном выражении могут использоваться операторы I, =2, !=, &&
И | . Также разрешается применение круглых скобок.
Рассмотри м небольшую программу.
// B программе демонстрируется использование директив препроцессора.
// #if, #end1f И #define.
#def1ne EXPERIMENTAL
using System;
Этот оператор будет скомпилирован, поскольку
Class Test т символьная константа вхввшмвп-гнь опрсдслш
на дирсктивои 4ШеЕ1пе.
public static мода Main() [
#if EXPERIMENTAL
Console.WriteLine("3ToT оператор будет скомпилирован только при" +
"\пналичии в программе определенной символьной константы EXPERlMENTAL.")i
#endif
Соп5о1с.Иг1СеЬ;пс("Этот оператор будет скомпилирован независимо от " +
"определения символьной константы.");
B результате выполнения программы будут выведены следующие строки:
Этот оператор будет скомпилирован только при
наличии в программе определенной символьной константы EXPERIMENTAL.
Этот оператор будет скомпилирован независимо от определения символьной
константы .
B этой программе опрслелсна символьная константа E?(PERIME.NTAL. CJIC,'1OBE1TC.TIbH0a
когда в коле программы встречается директива #15, символьное выражение прини‘
маст значение шве и компьтльярустся первый оператор WriteL1ne () ;. Если удали“
определение ситивольт-тойт константы EXPERIMENTAL И повторно скомпилировать
Программу, то первый оператор Writ,eLine() ; скомпилирован нс будет, посколЬКУ
выражение после директивы {zif примет значение false. Второй oncpaTOP
Препроцессор 457
.iiteL1n€(),‘ будет скомпилирован п любом случае, поскольку он нс янляется
частью блока мг.
Г
КАК уЖС ГОВОРИЛОСЬ, Н бЛОКС #1:; МОЖНО ПАСПОЛЬЗОПЗНТЬ Cl-l\lB().7lbllOC Bblp2l)KCHl1C.
ПНПрГ/ЯМСР, В СЛСЦУЮЩЁЙ ПрОГРЭММС УСЛОВИСМ ДЛЯ КОМП ИЛЯЦПН ОДНОГО И} ОПСРНГОРОН
ЯВЛЯСТСЯ ННЛИЧИС ДВУХ ОПРСДСЛСННЫХ СИМВОЛЬНЫХ КОПСТННТ.
В программе демонстрируется использование символьного выражения.
:г zcfine EXPERIMENTAL
::cfine TRIAL
;ng SysLem;
‚авз Test a
public static void Main() [
#if EXPERIMENTAL
COnsole.WriteLine("onepawop будет скомпилирован В той версии
"программы, в которой будет определена символьная константа EXPERIMENTAL.");
#endif
п
#if EXPERIMENTAL && TRIAL ‘Симвшпмосвышишнис]
COnsole.Error.WriteLine("OnepaTop будет скомпилирован в той версии " 1
"программы, в которой будут определены две символьные константы " +
"EXPERIMENTAL И TRIAL.");
#endif
Console.WriteLine("3ToT оператор будет скомпилирован независимо от " +
"определения символьных констант.");
Результат выполнения программы следующий
Ё.ератор будет скомпилирован в той версии программы, а которой будет
Снределена символьная константа EXPERIMENTAL.
ратор будет скомпилирован в той версии программы, в которой будут
педелены две символьные константы EXPERIMENTAL и TRIAL.
3.зт оператор будет скомпилирован независимо от определения символьных
F нстант.
B програмью ОПр6ДСЛСНЫ EEC СИМВОЛЬНЫС КОНСТЗПТЫ - EXPERIMENTAL И TRIAL.
Вгорой оператор WriteLine () ; будет скомпилирован только в случае определения
Обеих символьных констант.
директивы препроцессора #е|$е и #e|if
B C-if директива #eIse работает так же, как оператор else. C се помощью указывается
альтернативный вариант блока операторов. который будет скомпилирован. если
DC 3y.='lbTE1TO.'\1 оценки выражения директивы ёдв будет значение Гпэельтлуппая
“Рограмма может быть переписана следующим образом:
В ПрОГрЗММЭ демонстрируется ИСПОЛЬЗОВЭЦИЭ дирек-кивы препроцессора #0150.
*Cefine EXPERIMENTAL
Using System;
458 Глава 12. Делегаты, события, пространства имен и дополнительные элементы язьтка Cg Ё
class Test {
public static void Main() [
#if EXPERIMENTAL
Соп$о1е.Иг1сеЬ1пе("Оператор будет скомпилирован в той версии " +
"программы, в которой будет определена символьная константа EXPERIMENTAL_");
#8138 Использование директивы #e1se1
Соп5о1е.Иг1ееЬ1пе("Альтернативный вариант оператора."):
#end1f
#1f EXPERIMENTAL && TRIAL
Console.Error.WriteLine("onepawop будет скомпилирован в той версии " +
"программы, в которой будут определены две символьные константы " +
"EXPERIMENTAL И TRIAL.");
Ё 9139 Использование директивы денет
Console.Error.Wf1teL;ne( Ьще один альтернативный вариант оператора.");
#endif
Console.WriteLine("3ToT оператор будет скомпилирован независимо от " +
"определения символьных констант.");
}
}
В этой программе из пяти операторов Сопзо1е.ик1сеыпе(): компилируются
только три. В результате выполнения этих операторов на экран выводятся следующие
строки:
Оператор будет скомпилирован в той версии программы, В которой будет
определена символьная константа EXPERIMENTAL.
Еще один альтернативный вариант оператора.
Этот оператор будет скомпилирован независимо от определения символьных констант.
Поскольку в этой версии программы символьная константа TRIAL He определена,
компилятор будет использовать код, указанный для второй Директивы #else.
Обратите внимание, что наличие в коде программы директивы #else означает как
конец блока операторов, относящихся к директиве #if, так и начало блока операто-
ров, относящихся к директиве #е15е. Это необходимо, поскольку с директивой #if
может ассоциироваться только одна директива #end1 f.
Предназначение директивы #elif аналогично предназначению оператора else if-
OHa используется Для создания Цепочки 1Е—е15е—1Е, с помощью которой можН0
определить несколько вариантов компиляции программы. За директивой #elif
следует символьное выражение. Если выражение принимает значение true, ТО
компилируется блок кода, идущий за символьным выражением, а выражения, г
указанные за другими директивами #e1if, не проверяются. Если выражение прини- "3
маст значение false, то проверяется следующее условное выражение в цепочке-
Общая форма синтаксиса директивы #elif выглядит так:
#if symbol—expression
statement sequence
#elif symbol-expression
statement sequence
#elif symbol-expression
statement sequence
#elif symbol~expression
statement sequence
u А_. ..
где
1
45
ДИРЭКТИВЗ препроцессора #undef
gc;if symbol-expression
statement sequence
ycndlf
Например,
_ В программе демонстрируется использование директивы препроцессора #е11Е.
»ао11пе RELEASE
us;ng System;
clgss Test {
public static void Main() [
#if EXPERIMENTAL
Console.WriteLine("onepamop будет скомпилирован в той версии " +
"программы, в которой будет определена символьная константа EXPERIMENTAL.");
#8131 f RELEASE Использование директивы #e1if
Соп5о1е.Иг1СеЬ1пе("Оператор будет скомпилирован в той версии " +
"программы,
#е1$е
Console.writeLine("onepawop будет скомпилирован в той версии " +
"программы, " +
в которой будет определена символьная константа RELEASE.");
B которой не будет определена ни одна из перечисленных
"выше констант. ");
#endif
#if TRIAL && !RELEASE
Console.WriteLine("Onepawop будет скомпилирован в той ферсии " +
"программы,
#endif
B которой будет определена символьная константа TRIAL.");
Console.WriteLine("3ToT оператор будет скомпилирован независимо от " +
"определения символьных констант.");
Программа выводит на экран следующие строки:
Сдвратор будет скомпилирован в той версии программы, в которой будет
.ределена символьная константа RELEASE.
{д С,
‘ют оператор будет скомпилирован независимо от определения символьных констант.
директива препроцессора #undef
С Помощью Директивы препроцессора #undef удаляется данное ранее определение
символьной константы, которая указывается за директивой. То есть далее В TTPOFWM‘
Мс указанная символьная константа не будет считаться определенной. Общий син-
Таксис директивы #undef следующий:
fiundef symbol
460 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка од
Напригиср, в таком фрагменте кода:
#def;ne SMALL
fiif SNALL
‚я /' _ _ _ .
{fun-def SMALL
// С этого места нрохраммы символьная константа SMALL не считается
// определенной.
После указания в программе директивы Юлией символьная константа SMALL неЁ
считается определенной. -
Директива #3:’.-jef HCnO.'Ib’$yCTCSI B основном для локализации символьной константы д
в тех частях кода. где она необходима.
Директива препроцессора #еггог
С помощью директивы препроцессора #еггог компилятор получает команду оета- ‘
НОВИТЬ компилирование. Эта директива используется для отладки программы. Еёд
общий синтаксис выглядит так:
#еггог error—message
Если в коде программы встречается директива #еггог, на экран выводится сообще-
ние об ошибке. Например. если компилятор обнаружит в коде строку Ё
#еггог Далее B программе возникает ошибка! ;
КОМПРШИрОВаНИС ОСТЗНИВЛИВЗСТСЯ, И на Экран ВЫВОДИТСЯ СООбЩСНИС Далее В ПрОд
грамме возникает ошибкад
Директива препроцессора #warning
Директива гъыакш ng аналогична директиве #еггог за исключением того, что при ее ж
HCHO.-'Ib3OBE1HH И предупреждающее сообщение выводится, но компилирование НЕ 5
(ъстанатзливдетсэп. Общий синтаксис директивы smarnlng следующий:
#warn1ng warning~mes5age
Директива препроцессора #Ппе
Директива препроцессора # line используется для указания номера строки, с которой-да
будут нумероваться строки следующего за директивой кода, и имени файла, в который
будут направляться все сообщения, возникающие при компиляции данного кода-
Общий синтаксис директивы ёыпе следующий:
#11пе number "filename”
Здесь Элемент num‘:-er — .'HO6OC ЦСЛОС число, которое становится номером следующей
за директивой строки. а необязательная строка f 1 iename - это любой действитель‘
Ный гддентьтаъикатор файла, в который будут направляться еообщгндля. возникающие ;`
Атрибуты 41
при КОМПИЛЯЦИИ. Директива #1 1-ne B ОСНОВНОМ ИСПОЛЬЗУСТСЯ ДЛЯ ОТЛЭДКИ ПрОГрамм
И Р. СПСЦПАЕЪЧЬНЫХ ПрИЛОЖСНИЯХ.
у директивы препроцессора #region и #endregion
_‚ используя директивы iiregicn и iiendre-gzicn, можно определить фрагмент код;
который будет свернут или вновь развернут е помощью средств интегрированно
" среды разработки Visual C++ IDE. Общий синтаксис использования этих директи
представлен ниже:
.- ударов
,5,’ code sequence
Г ficr.-jregion
Минутные драхма/кум
д 1. Какая директива препроцессора используется для определения символьной
' константы?
2. Для чего применяется директива препроцессора hf‘?
3. Какие операторы разрешено использовать в символьных выражениях, ассо-
циированных с директивой #if или #elif?
Атрибуты
В С# есть возможность добавлять в программу описательную информацию в форме
атрибута. С помощью атрибута определяется дополнительная информация, которая
ассоциируется с классом, структурой, методом и так далее. Например. можно создать
атрибут, в котором указывается тип кнопки, отображаемой классом.
Атрибуты определяются в квадратных скобках перед Элементом. для которого они
Создаются. Вы можете определить собственные атрибуты либо использовать атрибу-
ты. определенные в Св. Создание собственных атрибутов в этой книгс не рассмат-
Впвастся, а встроенные в C# атрибуты Conditlonal И x'Jbsc:let.e вы можете успсшно
использовать.
Атрибут Conditional
' АГРИбут Conditional является, возможно, одним из наиболее часто используемых
В C5. Он позволяет создавать условные методы. Условный метод выполняется ТОЛЬКО
П том случае, если указанная в атрибуте символьная константа определена с помощью
‘Шгйсктивы темпе. В противном случае метод выполняться Не будет. ТО ССТЬ
Использование условного метода является альтернативой условному компилировашпо
L Для определения символьной константы используется директива препроцессора тепле.
3- Директива препроцессора мг применяется для определения блок кода, который будет
скомпилирован, если символьное выражение, ассоциированное с этой директивой, прини-
мает значение true. B Завершения этого блока кода (операторов) указывается директива
#endif.
3. B символьных выражениях, ассоциированных с директивой #1: или #elif, разрешено
использование следующих операторов: 14, =;‚ 5,5,, | 1 и 1
462 Глава 12. Делегаты. события, пространства имен и дополнительные элементы языка cg
C помощью директивы #if. Для использования атрибута Conditional Необходимо
вктючить в программу пространство ИМСН System. Diacmc-stics.
Рассмотрим следующий пример:
// В программе демонстрируется использование условного атрибута C0nditiona1_
fidefine TRIAL
using System;
uslng System.Diagnostics;
class Test {
Указание в условном атрибуте символьной
{Conditional ("TRIAL") ] 4—.————»
константы TRIAL.
мота t.r1al() [
Console .WriteLine ("Выполнение этого метода зависит от определения символьной" +
" константы TRIAL. ") ;
[Conditional ("RELEASE") ) .yK83aHKC В УСЛОВНОМ атрибуте СИМВОЛЬНОЙ
void release П { константы RELEASE. .
Console.WriteLine("Bwxom-xe1—me этого метода зависит от определения сшшольной" +
" константы RELEASE. ") ;
public static void Main() {
Test с = new Test(); д“;
C-Cridl(); // Метод будет выполнен только в том случае, если в
// программе была определена символьная константа TRIAL.
t.release(); // Метод будет выполнен только в том случае, если в
// программе была определена символьная константа RELEASE.
При выполнении программы будет вызван метод, выводящий следующую строку:
Вызов этого метода зависит от определения символьной константы TRIAL.
Внимательно рассмотрим эту программу, чтобы понять. почему в результате выво-
дится именно эта строка. Прежде всего, отметим, что в программе определена
символьная константа TRIAL. Далее обратим внимание на то, как кодируются методы '-
tnal () И release () . Для обоих методов указан атрибут Conditional, имеющий
такой синтаксис:
[Conditional symbol]
Здесь элемент symbol ~ это символьная константа, от определения которой зависит,
будет ли выполняться данный метод. Этот атрибут может использоваться только с
Методами. Если символьная константа определена, то при вызове метода его код будбТ
ВЫПОЛНЯТЬСЯ. ЕСЛИ СИМВОЛЬНаЯ константа НС определена, ТО МЕТОД ВЫПОЛНЯТЬСЯ Не
будет.
ВНУТРИ метода Main () вызываются оба метода trial () И release (). Но в програм-
ме определена только символьная константа TRIAL, следовательно, выполняется
только метод crial(). Вызов метода release() игнорируется. Если опредеЛИТЬ
символьную константу RELEASE, ТО метод release () тоже будет выполняться. Если
Атрибуты 463
определение символьной константы TRIAL будет удалено, метод trial () ВЫПОЛНЯТЬ-
„н не будет.
условные методы имеют некоторые ограничения.
о Для них должен быть указан тип возвращаемого значения мота.
о Они должны быть членами класса, а не интерфейса.
с) Перед ними не может стоять ключевое слово override.
Атрибут Obsolete
AIpM6yTSystem.Obsolete n03B0nfleTOTMCuaTb3neMeHTruxflpammblKaK)wTapCBLuMfi.
Это атрибут имеет следующий синтаксис:
Gosolete "message"]
Здесь словом message обозначается сообщение, которое будет выведено на Экран
при компилировании данного элемента программы. Ниже приведена небольшая
программа, в которой используется атрибут System.Obsolete.
1.’
B программе демонстрируется использование атрибута Obsolete.
using System;
B атрибуте указывается строковый литерал, который
C_ass Test { будет выведен при компилировании метода myMeth ( ) .
{0bsolete("BMecTo этого метода следует использовать метод myMeth2.")l
static int myMeth(int a, int b) {
return a / b;
// Усовершенствованная версия метода myMeth.
static int myMeth2(int a, int b) {
return b 2: 0 ? 0 : а /Ь;
public static void Main() {
// При компилировании этого оператора будет выведено сообщение.
Console.writeLine("4 / 3 2 " + Test.myMeth(4, 3));
// При компилировании этого оператора сообщение, указанное в атрибуте,
// выведено не будет.
Console.WriteLine("4 / 3 = " + Test.myMeth2(4, 3));
Если при компилировании программы в методе Ma1n() встретится вызов метода
" }‘I'Ieth () , будет выведено сообщение, рекомендующее программисту использовать
Чстод туМесЪ12 () .
Ь
464 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка Cg J»
Минутный лрактакум А;
I. Что такос атрибут?
2. Что такое условный метод?
3. Какие функции выполняет атрибут Obsolete?
Небезопасный код
Со‘: позволяет писать так называемый небезопасный код. Небезопасным является не `
тот код, который плохо написан, а тот, выполнение которого не управляется CLR_
Как уже говорилось в главе I, язык C# обычно используется [Um создания управляе-
мого кода. Однако существует возможность написать код, выполнение которого не
контролируется CLR. Поскольку неуправляемый код не подвергается такому контро-
лю и ограничениям, как управляемый код, он называется небезопасным. При его
использовании нельзя быть уверенным, что он не произведет какие-либо вредные
действия. Следовательно, термин небезопасный не означает, что код некорректный по _
существу. имеется в виду лишь то, что этот код потенциально может выполнять
действия, не контролируемые CLR.
Конечно, применение управляемого кода предпочтительнее, но он не позволяет
использовать указатели. Если вы знакомы с языком С или С++, то знаете, что-
указатсли — это переменные, хранящие адреса других объектов. Следовательно,
указатели имеют некоторое сходство со ссылками в Cit. Основное различие между‘
ними заключается в том, что указатель может содержать юбой адрес памяти, а ссылка
всегда содержит адрес памяти, выделенной для объекта ее типа. Поскольку указатель
может ссылаться на любой адрес памяти, существует возможность его некорректного
использования. Кроме того, при использовании указателей можно допустить ошибку
в коде. Поэтому в С# не поддерживается использование указателей при создании
управляемого кода, Однако они полезны и даже необходимы для некоторых типов
программ (например. для утилит системного уровня). Все операции с указателями I
должны быть отмечены как небезопасные, поскольку они выполняются за пределами ‘
управляемой среды.
В общем, если вам необходимо создать код, который выполняется за пределами CLR.
лучше Использовать язык С++_ В Cit указатели объявляются и применяются так же,
как и в языках С/С++_ Но основная цель C# — создание управляемого кода. а
способность этого языка поддерживать неуправляемый код используется для решения
особых задач. Чтобы скомпилировать неуправляемый код, Необходимо применить
опцию компилятора /unsafe.
Работа с небезопасным кодом — это отдельная сложная тема, которая в данной книГб
рассматриваться не будет. Но все же мы коротко расскажем об использовании
указателей и двух ключевых слов, unsafe И fixed, применяющихся для поддержки
неуправляемого кода.
1. Атрибут — это дополнительная информация, ассоциированная с каким-либо элементом
(классом, структурой, методом).
2. УСЛОВНЫМ НЭЗЬПВаеТСЯ Метод. для которого определен атрибут Condit ional. Условный метод
выполняется только в том случае, если была задана указанная в атрибуте символьная
константа.
3. Атрибут Obsolete инициирует при компиляции вывод предупреждающего сообщения об
использовании устаревшего элемента (чаше всего это касается метода).
к
Небезопасный код 465
краткая информация об указателях
указатели — это переменные, которые хранят адреса других объектов. Например,
если переменная х содержит адрес объекта у, то говорят. что переменная х «указы-
Racy» на объект у. Общий синтаксис объявления указателя следующий:
,.pe* var~nazr.e
Здесь слово type — это тип указателя. который не должен быть типом класса,
Словосочетание х/аг-пате — это имя указателя, Например, для объявления пере-
хтенной р как указателя на объект типа int используется оператор
;nt‘ 1p;
ДЛЯ указателя С ТИПОМ float ИСПОЛЬЗУСТСЯ оператор
f;oat* Ер:
В пслом, если в операторе объявления перед именем переменной стоит знак *, такая
переменная становится указателем.
Тип данных, на который ссылается указатель, определяется его типом. Следователь-
но, в предыдущих примерах указатель ip может использоваться для ссылки на объект
типа int, а указатель Ер - ДЛЯ ссылки на объект типа float. Именно потому, что
указатели могут содержать адрес любой области памяти, они являются потенциально
небезопасными.
Существуют два оператора для работы с указателя ми — * И е. Оператор а является
унарным оператором, который возвращает адрес области памяти, выделенной для его
операнда. (Вспомним, что для унарного оператора требуется только один операнд.)
Например. в результате выполнения фрагмента кода
шт ip;
in: num = 10;
;р = #
переменной ьр будет присвоен адрес области памяти, выделенной для псремснной
пат. Этот адрес указывает местоположение переменной в оперативной памяти
компьютера. но он не имеет ничего общего со значением переменной num. Следова-
тельно, указатель 1р не хранит значение 10 (начальное значение переменной num),
он только содержит адрес, по которому хранится эта переменная. Можно сказать,
что оператор & возвращает адрес переменной, перед именем которой он использу-
Сгся. Таким образом, оператор 1р = # можно прочитать так: «Переменная ip
получает адрес переменной mum».
Оператор * является дополнением к оператору &. Это унарный оператор, возвращаю-
щий значение переменной, адрес которой содержится в его операнде. Например, если
Переменная 1р хранит адрес переменной num, то во фрагменте кода
дм‘. val;
val = а1р;
Переменной val будет присвоено значение 10, то есть значение переменной num, на
Которую ссылается указатель 1p. Можно сказать, что оператор * возвращает значе-
Hue, хранящееся по адресу, содержащемуся в операнде. В этом случае оператор val
е *1p; можно прочесть следующим образом: «Переменная val принимает значе-
Hue, хранящееся по адресу, который содержится в переменной 1p».
466 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка С#
Указатели могут также использоваться со структурами. При обращении к члену
структуры с помощью указателя нужно использовать оператор —>- BMCCTO оператора
точка (.). Нагтримср, определим простую структуру:
struct Mystruct {
public int x;
public int y;
public int sum() { return x + y; };
i
Доступ к членам структуры с использованием указателей должен выгю‚тняться
следующим образом:
Mystruct O = new MyStruCt();
Mystruct‘ p; // Объявление указателя.
p 2 &о;
р—>х ~ 10;
р—>у = 20;
Console.WriteLine("CyMMa равна: " + p->sum());
-fimeemw дрдфессшлала
<3 Bonpoc. При объявлении указателя в C++ действие огтератора * не распро-
страняется на весь список переменных в операторе объявления. Следовательно, в
операторе
int* p, q;
объявляется указатель р с типом int и целочисленная переменная q. То же самое
происходит и в следующих двух операторах, в которых объявляются переменные р и q:
int* p; Ж
int q; Ё
Справедливо ли данное правило для C#'.-’
*
Ответ. Нет. В С# действие оператора
переменных, а оператор
распространяется на ВССЬ СПИСОК
int* p; q;
CO3flEi€T две переменные, которые СТЗНОВЯТСЯ указателями. СГЮДОВЗТСЛЬНО, ЭТОТ one-
ратор равносилен ДВУМ операторам
int* p;
int* q;
Это важное отличие, которое необходимо учитывать при переносе кода С++ в C#. Е‘
Ключевое слово unsafe
Любой код, В котором используются указатели, должен быть отмечен как небезопас- Q '
ньтй с помощью ключевого слова unsafe. Отмечать можно как отдельный оператор,
так и весь метод. Например, ниже приведена програмтиа. в которой используется
указатель внутри метода Main () . полностью объявлсньаого небезопасным. (ДЛЯ
Небезопасный код 467
компилировании программы с небезопасным кодом нужно в командной строке
‚шести следующую команду: csc /unsafe filename, где filename — имя компи-
дпруемого грайла.)
В программе демонстрируется использование указателя и ключевого слова
unsafe.
.;_ng System;
‚азз Unsafecode {
/; Объявление метода Main как нсбезопасного.
Unsafe Fmbi-LC Static Void Main U { 4———jMeTo1I Ma1n() отмечен как небезопасный
1_nt count : 99; поскольку в нем используется указатель.
int *р: // Создание указателя, имеющего тип int.
p acount; //Указателю р присваивается адрес переменной count.
Сопзо1е.И:1пеЬ1пе("Первоначальное значение переменной count = " + *p);
‘p : 10; // Переменной count присваивается значение с помощью указателя р.
COnsole.WriteLine("HOBoe значение переменной count 2 " + *р);
Рсзул ьтат ыполнения программы будет следующим:
Первоначальное значение переменной count = 99
Новое значение переменной count = 10
Ключевое слово fixed
НСПОЛЬЗОВЗНИС модификатора f 1хес1 предотвращает возможность перемещения
управляемого объекта при «сборке мусора» (естественно, сам объект не перемещается,
просто при оптимизации памяти для него выделяется другая область, имеющая другой
адтрсс). Этот модификатор необходимо указывать, если на такой объект ссылается
указатель. Поскольку указателю ничего не известно о действиях системы «сборки
\‘.\C0pa», B случае перемещения объекта он будет ссылаться уже на другой ОбЪСКТ-
Ниже приведен общий синтаксис использования тетючевого слова fixed.
i~x:d (суре*р ~ afixcdobj) {
// Использование фиксированного объекта, для которого
// указан модификатор fixed.
Здесь переменная р является указателем, которому присваивается адрес ОбЪСКТЗ-
Объект будет сохраняться в области памяти, выделенной для него на время выпол-
нения блока кода. Модификатор fixed можно указывать и для одиночного операто-
l)a_ HO B любом случае он может использоваться только в небезопасном коде.
Рассмотрим пример использования модификатора fixed.
B программе демонстрируется использование небезопасного кода.
lS;nq System;
CLass Test {
public 1nt пот:
public Test(int i) { “Um Д i; }
468 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка
class UnsafeCode {
// Объявление метода Main как небезопасного.
unsafe public static void Main() {
Test о 5 new Test(l9);
Использование модификатора E1 xed для фиксиро-
нания местоположения объекта о в памяти.
ё
3:
fixed (int *р = &o.num { // MCUOHb3OBaHM€ MOflM§flKaTOpa fixed для Ii
// присвоения указателю р адреса переменной O.num_ ‚р
‘Ё
Соп$о1е.Иг1пеЬ1пе("Первоначальное значение переменной o.num : " + *p); Ё
5
*р = 10; // Переменной О.ПЦШ присваивается значение с помощью Ё
\
// указателя p. 3
Console.WriteLine("Hosoe значение переменной o.num = " + *p); ‘Ё
) H
} Ё
› г
. .. Ч
Резул ьтат ВЫПОЛНЕНИЯ этои программы следующии:
Первоначальное значение переменной д.пит é 19 З
Новое значение переменной o.num = 10 g
.4
B программе с помощью модификатора flxed предотвращается перемещение н
памяти объекта о. Поскольку указатель р ссылается на переменную o.mun, npuég
перемещении объекта о адрес в указателе будет неверным. Ё
Мы кратко описали создание и использование небезопасного кода. Если в своихд
программах вы собираетесь Создавать небезопасный код, то должны изучить все am?
вопросы более подробно. °
Минутная? практикум
1 1. Что такое указатель?
2. Когда используется ключевое слово unsafe?
3. Какие функции выполняет модификатор flxed?
Идентификация типа во время работы
программы
В С# существует возможность определить тип объекта во время выполнения про-
граммы. Фактически в С# есть три ключевых слова для поддержки идентификации
типа во время Выполнения программы, is, as И typeof. Хотя эти ключевые слова
используются только при создании высокопрофессиональных программ, важно иметь
о них общее представление.
__‚.
1. Указатель —— это переменная, в которой хранится адрес области памяти, выделенной для
другого объекта.
2. Любой код, в котором используются указатели, должен быть отмечен как небезопасный С
ПОМОЩЬЮ ключевого слова unsafe.
3. Использование модификатора fixed предотвращает перемещение объекта во время «сборки
мусора».
ъъъттчгчдш '
д
‘S1
к ~ «~13-1.-,..~.m'.=‘;179~,~.'~=.s.1:'-s.-‘vs’
Ё‘.
Идентификация типа во время работы программы 469
проверка типа С ПОМОЩЬЮ КЛЮЧЭВОГО слова iS
принадлежность объекта к определенному типу можно проверить с помощью опе-
рдгора is. синтаксис Которого выглядит следующим образом:
exp; 15 type
ЗЦССЬ ЭЛСМСНТ expr — ЭТО выражение, ТИП которого сравнивается С ТИПОМ, УКНЗЗННЫМ
‘1 0 EC ЭТИ Ь СО падают 1 ‘I со CT TO е
справа ОТ ОПС (T pa ls. ЛИ ТИП 1 B ( 1.11 BMC ИМЫ. З ‘ЛЬТЗТ
Операции принимает значение true. B противном случае — ЗНЗЧСНИС false. Ниже
приведена программа, в которой используется оператор 13.
/.v в программе демонстрируется использование оператора ls.
„дли; System;
ОбЪСКТ к! ПС ЯВЛЯСТСЯ ОбЪСКТОМ типа В,
CJI€i_[OB2lTCJlhHO, DCJy)lh'l‘21'I' ВЫНПЛНСНИЯ
ЦНННОПО оператора ls принимает
‚дань A H
B значение false.
АН
class UseIs { ‚ _ _
_ _ _ _ _ Результат anon операции
PUDl1" Static Void Маш“) { принимает значение true,
A a I new A(); ПОСКОЛЬКУ объекта имеет
Bb=new ВО: “WA-
if(a is A) Console.Writ:eLine("O6*be1<:1~ a имеет тип A.");
if(b is A)
Console.WriteLine("O6'beKT b имеет тип A,\n" +
" поскольку класс В является наследующим по отношению к классу A."):
;,f(a is B) 4
Console.Writ:eLine("STOT оператор не будет выполнен, поскольку класс А" +
..
не наследует класс В.");
if(b is B) Console.WriteLine("O6'be1<:1~ в имеет тип В.");
1‚Е(а is object) Console.WriteLine("O61se1<:1~ a имеет тип Object:.");
Э
B результате работы программы будет выведена следующая информация:
Ой ьскт а имеет тип А.
С-с-ьекч- b имеет тип A,
I-‘JCKOIIbKy класс В является наследующим по отношению к классу A.
Соьект в имеет тип В.
Ссьект а имеет тип Object.
БОЛЬШИНСТВО ВЫрЗЖСНИЙ С КЛЮЧЕВЫМ СЛОНОМ is НС Hy)Ki1H}OTC${ B ПОЯСНСНИИ, HO два
ш них рассмотрим подробнее. Во-первых, обратите внимание на оператор
«i (D is A)
COnsole.Writ:eLine("O6"beK:1‘ b имеет тип А‚\п" +
поскольку класс В является наследующим по отношению к классу A.")i
Выражение (b 1S A) принимает значение true, поскольку объект b является объектом
типа В, который наследует класс А. Следовательно, тип объекта Ь совместимт с типом А.
OJHZIKO КЛЗСС А НС НЗСЛСДУСТ КЛЗСС В, СЛСДОНЭТСЛЬНО, при ВЫПОЛНСНИИ оператора
5 (а is B)
Console.WriteLine("STOT оператор не будет выполнен, поскольку класс A" +
" не наследует класс В. ") ;
Зсловное выражение оператора if принимает значение false, поскольку объеКТ д
Пчеет тип A, который не наследует класс В. Следовательно, эти типы несовместимы.
470 Глава 12. Делегаты, события, пространства имен и дополнительные элементы нзьака cg й
Ключевое слово as д ,'
В C1? существует возможность выполнить операцию приведения Типа BO время
выполнения программы, причем, если преобразование невозможно, это не приведет
к возникновению исключительной ситуации. Для ВЬШОЛНСНИЯ МОЖНО ИСПОЛЬЗОВаТь
оператор as, применение которого имеет следующий общий синтаксис:
expr as type
3I1CCb элспиент expr —- ЭТО выражение, которое приводится К типу, указанному справа
ОТ оператора as, ЕСЛИ операция ПрИВ6ДСНИЯ ТИПЭ Пр0ЦПП1УСПСП1НО,ВОЗВРНЦНЮТСЯ
ССЫЛКИ на ТИП. В ПрОТИВНОМ СЛУЧЗС ВОЗВРНЩЗСТСЯ пустая CCbL-'IKil.
Ключевое слово typeof
Указав оператору typeof в качестве параметра тип некоторого объекта (или обычный
тип), можно получить объект класса System.Type. Этот объект будет содержать
информацию, ассоциированную с указываемым типом. Оператор имеет следующую
общую форму синтаксиса:
typeof(type);
Здесь элемент type —~ это тип, о котором нужно получить полную информацию.
Класс System . Type предназначен для получения полной информации о каком-либо
типе, Например, при выполнении приведенной ниже программы выводится полное
имя класса StreamReader:
// B программе демонстрируется использование оператора typeof.
using System;
using System.IO:
class UseTypeof (
public static void Ma1n() {
Type t = typeof(StreamReader);
Conso1e.WriteLine(t.FullName);
}
P63)/.7lbT'(1T ВЫПОЛНСНИЯ программы ПОКЗЗЗН НИЖЕ.
System.IO.StreamReader
ер Минутка/а практикум
При помощи какого оператора можно определить, совместим ли тип объсКТа
с указываемым типом‘?
2. В чем различие между оператором as И операцией приведения типов?
3. Что оператор typeof получает в качестве параметра?
1. Совместимость типа объекта с указываемым типом можно определить при помощи oneP3'
тора is.
2. Неудачное выполнение операции приведения типа приводит к возникновению исключи
тельной ситуации. При неудачном выполнении оператора as возвращается значение null-
3. Оператор typeof fIOIIy'~[a6T.B Ka'-{CCTBC параметра тип объекта или обычной переменной, 0
котором нужно получить полную информацию.
другие ключевые слова
другие ключевые слова
Завершая книгу, мы дадим краткое описание некоторых ключевых слов, о Kore
до лого не рассказывали.
модификатор доступа internal
в дшпполнение к модификаторам рыЬ11с, рычаге и protected, которые исполь
вались в программах данной книги и с помощью которых указывалась возможно
доступа к кгленам класса, в C11 также определен модификатор internal. Э
\1‹›‚1Ь1(])ИКаТ0р объявляет, что член класса известен всем файлам в пределах сбор
но неизвестен за пределами сборки. Сборка —— это файл (или файлы), в котор
содержатся компоненты программы и информация о се версии. Следователь:
можно сказать, что член класса, указанный как internal, становится известным BC
программе, но нигде больше.
Ключевое слово sizeof
Иногда может потребоваться информация о размере в байтах (количестве байте
выгделенных для представления данных) одного из обычных типов Cit. Для получен:
такой информации используется оператор sizeof. OH имеет слеггующий синтакси
sizeof (type)
3.—1CCb элемент type ~— это тип, размер которого определяется. Оператор sizec
.\IO>KCT использоваться только в небезопасном коде. Следовательно, он предназначе
для применения в особых ситуациях, в частности при одновременной работе
управляемым и неуправляемым кодом.
Ключевое слово lock
Ключевое слово lock используется при работе с множеством попгоков. B Ci
"Рограмма может иметь два и более потоков выполнения. Когда это происходит, част:
Программы работают в многозадачном режиме. Следовательно, они выполняютсэ
Независимо И одновременно. При этом может возникнуть ситуация, когда два ПОТОК?
попытаются одновременно использовать ресурс, которым в конкретный момент
может пользоваться только один поток. [Ьтя решения этой проблемы можно создат!
0.10/C крипшческого кода, который будет выполняться в конкретный момент только
Шним потоком. Этот блок создается е помощью ключевого слова loc k. Общая (])opMa
его синтаксиса выглядит так:
ask (obj) (
/’ Блок критического кода.
Здесь элемент obj — это объект, который стремится получить доступ К коду, ОПРЁЛС‘
ленному в блоке критического кода. Если один поток уже Вы пол няст данный блок кода:
второй поток будет ожидать выполнения первого потока. Только после того, как первый
поток закончит выполнение этого блока, доступ к нему получит второй поток.
Поле readonly
B классе можно еоздать поле, предназначенное только для чтения, объявив его КЗК
readonly. Поле, ОбЪЯВЛСННОС с указанием ключевого слова readonly, может бьгть
н
472 Глава 12. Делегаты, события, пространства имен и дополнительные элементы языка од?
инициализировано, но после этого его невозможно изменить. Следовательно, обы›р'.
явление поля как readonly — это хороший способ создания констант (напримергд _
для значений размерностей массива, используемых во всей программе). Поля re- ‚ _,’
adonly могут быть созданы как с модификатором static, TEIK И без него. "
// В программе демонстрируется использование ключевого слова readonly.
using System;
class MyClass [
public scam readonly int S1213 . 10;
class DemoReadonly {
public static void Main() {
int[]nums 2 new int[MyClass.SIZE];
for(int i=0; i < MyClass.SIZE; i++)
nums[i] 2 i;
foreach(int i in nums)
Console.Write(i + “ “);
// MyClass.SIZE = 100: // ошибка!!!
// Значение КОНСТЗНТЫ не МОЖЕТ бЫТЬ ИЗМЭНЕНО.
}
B начале программы при объявлении константы MyClass.SIZE ей присваивается
значение 10. После этого ее можно использовать, но нельзя изменять. Вы можете
убедиться в этом, если удалите символ комментария перед началом последнего
оператора и попробуете компилировать программу. Компилятор сообщит об ощибкег
Ключевое слово stackalloc
Выделить память из стековой гпамяти можно с помощью ключевого слова stackal-
loc. Его можно указывать только во время инициализации локальных переменных.
Общая форма синтаксиса ключевого слова stackalloc представлена ниже.
type *р = stackalloc type[size]
Здесь элемент р — это указатель, который получает адрес области памяти, достаточно —
большой для хранения указанного количества объектов типа type (КОЛИЧЕСТВО
объектов указывается вместо слова size). Ключевое слово stackalloc используется
в небезопасном коде.
Обычно память для объектов выделяется из кучи (динамически распределяемой
области памяти), то ссть из области свободной памяти. Выделение памяти из стековой
памяти является исключением. Переменные, помещенные в стсковую память, Не
перемещаются при «сборке мусора», но существуют только во время выполнения
блока, в котором они объявлены. Единственным преимуществом использования
ключевого слова stackalloc является то, что после выделения памяти подобным
способом программисту не нужно беспокоиться о перемещении переменных во время
«сборки мусора».
ч го делать дальше 473
оператор Ц$|П9
K_H0‘ICBOC СЛОВО us;ng МОЖЕТ ИСПОЛЬЗОВЗТЬСЯ НС ТОЛЬКО В KEi‘ICCTBC 0!/IPEKHII/16b1, HO И
В КаЧССТВС onepamopa. B ЭТОМ СЛУЧЗС CFO СИНТЗКСИС ВЫГЛЯДИТ ТИК:
u5;ng (obj) {
1/ MCHOflb3OBaHM€ объекта.
Здесь элемент obj — это объект, который используется внутри блока usmg. После
выполнения операторов блока вызывается метод шзрозе (). Оператор using при-
меняется только для объектов, которые реализуют интерфейс System . IDisposable.
модификаторы const И volatile
Модификатор const используется для объявления переменных, значение которых
нельзя изменить. Этим переменным необходимо присваивать начальные значения при
их объявлении. Переменная с модификатором const ЯЦЛЯСТСЯ константой. Оператор
cons: int i ; 10;
создает переменную 1 с модификатором const, имеющую значение IO‘ C помощью
модификатора volatile компилятору сообщается, что значение переменной может
изменяться способами, не определенными в программе явно. Например, значение
переменной, в которой хранится информация о текущем системном времени, может
обновляться автоматически операционной еиетемой. В таком случае содержимое
переменной изменяется без явного выполнения оператора присваивания. Причиной,
по которой следует учитывать возможность внешнего изменения переменной, яв-
ляется то, что С#—компилятору разрешено оптимизировать некоторые выражения
автоматически, предполагая, что содержимое переменной не изменяется, если она
не встречается в коде слева от оператора присваивания. Но если процессы за
пределами выполняемого кода (например. второй поток выполнения программы)
могут изменить значение переменной, то такое предположение, на основе которого
компилятором выполняется оптимизация, станет ошибочным. С помощью модифи-
катора Volatile компилятору сообщается, что он должен получать (считывать)
значение этой переменной при каждом обращении к ней.
ЧТО делать дальше
Примите наши поздравления! Если ны прочитали и проработали вее предыдущие
13 глав, то смело можете называть себя программистом на Cit. Безусловно, многое
Оше предстоит изучить ~ библиотеки, подсистемы, но вы имеете базу, ИСПОЛЬЗУЯ
'\'0TOpy10 можете расширять свои знания и опыт. Ниже мы перечислим некоторые
Темы, которые рекомендуем вам изучить в будущем.
9 Использование С# для создания Щйпоошв-приложений, в частности GUI (графи-
ческие пользовательские интерфейсы), в которых используются различные эле-
менты GUI, такие как кнопки, меню, епиеки и полосы прокрутки.
Создание многопотоковых приложений.
Встроенные компоненты.
Использование коллекций классов Св, поддерживающих стеки. очереди и еписки.
Использование классов, предназначенных для работы в сети.
Управление версиями ваших приложений.
ООООО
474 Глава 12. Делегаты, события, Пространства имен и дополнительные элементы языка С#
я мытые ат
Напишите, как объявить делегат с именсм Ё111ег, которыи возвращает
значение типа double И получает один аргумент типа ц nt.
Как создастся многоадресный делегат? Какие существуют ограничения‘!
Какая взаимосвязь существует между делегатами и событиями?
Может ли событие быть широковещатсльным? Экзсмгитяру или классу
передается событие?
Назовите основное преимущество использования пространства имен.
Напишите синтаксис создания псевдонима с помощью опсратора us-
lng.
Какие два вида операторов преобразования существуют в Cii? Напишите
общую форму их синтаксиса.
Назовите директивы препроцессора, которые используются для услов-
ного компилирования.
9. Напишите синтаксис использования атрибута.
10. Что такос небезопасный код?
11. Как во время выполнения программы определить тип объекта?
Продолжайте самостоятельно экспериментировать C элсментами языка С#.
„ж.
""£'.;
-.w~>v;;»\."-
Z:.l**.'A..<»».<*<‘h.)<:)fi.)h<‘1fl5i..)~...E..)I~«4»v ».» .. е: . .... .‚: .-.._ .—::‹ *.*::.:4:›:6д!6
.;.э:‚..:‹....-‚- ..: ч: .. ..-,/.- 5/*.-2 .-;‘. /,v<":}.:‘I‘-'l" ‚..- -;.‚-„ -:› M»-...-V. .«:.,~».~.....».-...\«..
Г ерберт Шилдт
С#: учебный курс
Перевод с английского под редакцией А. Падалки
Редактор Е. Нисьцижа
Художник Н. Барды-оков
Корректор Е. Юрбапншп
Технический редактор 3. Лобач
ББК 32.973-018я7
УДК 681 .3.06(075)
Шилдт Г.
U57 С#: учебный курс. — СПб.: Питер; К.: Издательская группа ВН\/‚ 2003. ——
512 с.: ил.
ISBN 966—552—121—7
ISBN 5—94723-167-О
Эта книга послужит прекрасным пособием как для специалистов, желающих изучить
язык C# ~— новый язык фирмы Microsoft, так и для начинающих пользователей, нс владеющих
{и одним из языков программирования. Книга написана простым и доступным языком. Ее aa-
тор, Герберт Шилдт. получил мировую известность благодаря изданию серии книг по про-
‘раммированию.
Original English language Edition Copyright © McGraw—Hill, 2001
Гс) Издательская группа ВН\/‚ Киев, 2003
I9) ЗАО Издательский дом «Питер», 2003
Права на издание получены по соглашению с McGraw-Hill.
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было фор-
ме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников. рассматриваемых издательством как на-
дежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может
гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возмож-
ные ошибки. связанные с использованием книги.
ISBN 966-552-121-7
ISBN 5—94723-167-0
ISBN 0-O7-213329-5 (англ)
ООО «Питер Принт». 196105. Санкт-Петербург, ул. Благодатная. д. 67.
Лицензия MD, Не 05784 от 07.09.01.
ООО «Издательская группа BHV»
Свидетельство о занесении в Государственный реестр серия ДК N9 I75 от I10‘? 3000
Налоговая льгота общероссийский классификатор продукции ОК 005-924, том 2; 953005 литература учебная.
Подписано в печать 31.01.03. Формат 70х1ОО'/„_. Усл. п. л. 41,28. Доп. тираж 4000 экз. Заказ N2 2281.
Отпечатано с фотоформ в ФП/П «Печатный двор» им. А. М. Горького у
Министерства РФ по делам печати, телерадиовещания и средств массовых коммуникации-
197110, CaHKT-]'I€rcp6ypr, Чкштовский пр.. 15.