Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Books / 3_C#_2005_для_чайников_(Дэвис-2008)

.pdf
Скачиваний:
86
Добавлен:
24.03.2015
Размер:
15.46 Mб
Скачать

Вложенные циклы

Один цикл может находиться в теле другого цикла: f o r ( . . . н е к о т о р о е у с л о в и е . . . )

{

f o r ( . . .

н е к о т о р о е д р у г о е у с л о в и е . . . )

{

 

/ /

. . . к а к и е - т о д е й с т в и я . . .

Внутренний цикл выполняется полностью при каждом выполнении тела внешней цикла. Переменная-счетчик цикла, используемая во внутреннем цикле, является неопре деленной вне области видимости внутреннего цикла.

Цикл, содержащийся внутри другого цикла, называется вложенным (nested) Вложенные циклы не могут "пересекаться", т.е. приведенный далее исходны текст некорректен:

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

Оператор b r e a k внутри вложенного цикла прекращает выполнение только этап вложенного цикла. В приведенном далее фрагменте исходного текста оператор brea) завершает работу цикла Б и возвращает управление циклу А:

// Цикл

f o r

А

 

 

 

f o r ( . . .

н е к о т о р о е у с л о в и е

. . . )

 

{

 

 

 

 

 

//

Цикл

f o r Б

 

 

 

f o r ( . . .

н е к о т о р о е д р у г о е у с л о в и е

. . . )

{

 

 

 

 

 

 

/ / . . . н е к о т о р ы е д е й с т в и я . . .

 

i f

(Истинное

у с л о в и е )

 

 

{

 

 

 

 

 

 

b r e a k ; //

Выход

из ц и к л а

Б, но не из ц и к л а А

}

}

}

В С# нет команды, которая бы обеспечивала одновременный выход из обоих циклов. | Это не настолько уж большое ограничение, как могло бы показаться. На прак­ тике зачастую сложную логику, содержащуюся внутри таких вложенных цик­ лов, лучше инкапсулировать в виде функций. Выполнение оператора r e t u r n в любом месте обеспечивает выход из функции, т.е. из всех циклов, какой бы ни была глубина вложенности. Функции и оператор r e t u r n будут рассматри­ ваться в главе 7, "Функции функций".

106

Часть II. Основы программирования в С#

В приведенной далее программе DisplayXWithNestedLoops , которую

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

II DisplayXWithNestedLoops

 

 

 

 

// Использует пару вложенных циклов для

вывода на

экран

// большой

буквы X

 

 

 

 

 

 

using System;

 

 

 

 

 

 

 

 

namespace DisplayXWithNestedLoop s

 

 

 

{

 

 

 

 

 

 

 

 

 

 

public

c l a s s Program

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

public

s t a t i c v o i d

M a i n ( s t r i n g [ ]

a r g s )

 

{

 

 

 

 

 

 

 

 

 

 

in t

nConsoleWidth = 4 0 ;

 

 

 

//

Итерация

по

строкам

 

 

 

f o r ( i n t

nRowNum

= 0;

 

 

 

 

 

nRowNum < nConsoleWidth ;

 

 

 

 

nRowNum + = 2)

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

//

Итерация по столбцам

 

 

 

fo r

( i n t

nColumnNum

= 0 ;

 

i

 

 

 

 

nColumnNum < nConsoleWidth ; nColumnNum++)

{

 

 

 

 

 

 

 

 

 

 

/ / П о

умолчанию используетс я пробел

 

 

cha r с

=

1

1;

 

 

 

 

 

//

Если номер строки и столбца

совпадают . . .

 

if

(nColumnNum ==

nRowNum)

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

//

. . .

заменим

пробел обратной косой

чертой

 

 

с =

' \\ ' ;

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

// Если столбец на противоположной

 

 

//

стороне строки . . .

 

 

 

 

i n t

nMirrorColumn

= nConsoleWidt h - nRowNum;

 

if

(nColumnNum ==

nMirrorColumn)

 

 

{

 

 

 

 

 

 

 

 

 

 

/ /

. . .

заменим

пробел косой

чертой

 

 

 

с =

' / 1 ;

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

//

Вывод

символа

в текущей

позиции

 

 

C o n s o l e . W r i t e ( с ) ;

 

 

 

 

}

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( ) ;

 

 

 

}

 

 

 

 

 

 

 

 

 

 

//

Ожидаем подтверждения пользователя

 

Глава 5. Управление потоком выполнения

 

 

107

Si

Сначала вычисляется выражение в круглых скобках после оператора s w i t c h . В дан ном случае это просто значение переменной n M a r i t a l S t a t u s . Затем вычисленщ значение сравнивается со значениями у каждого из операторов c a s e . Если нужное зна чение не найдено, управление передается операторам, следующим за меткой default.

Аргументом оператора s w i t c h может быть также строка s t r i n g : s t r i n g s = " D a v i s " ;

s w i t c h ( s )

{

c a s e " M a l l o r y " :

// Некоторые действия break ;

c a s e " W e l l s " :

// Некоторые действия break ;

c a s e "Arturo" :

// Некоторые действия break ;

c a s e "Brown":

// Некоторые действия break ;

d e f a u l t :

//Действия, если такой

//фамилии нет в списке

}

При применении конструкции s w i t c h действует ряд ограничений.

S Аргумент оператора s w i t c h () должен иметь перечислимый тип или тип s t r i n g .

S Нельзя использовать числа с плавающей точкой.

^ Значения c a s e должны иметь тот же тип, что аргумент оператора s w i t c h .

SЗначения c a s e должны быть константами в том смысле, что их значения должны быть известны во время компиляции.

S Каждая конструкция c a s e должна завершаться оператором break (или какой-то иной командой выхода, например, return) . Оператор break передает управле­ ние за пределы конструкции s w i t c h .

Допускается наличие нескольких c a s e для одного блока кода, как в следующем примере: s w i t c h ( n M a r i t a l S t a t u s )

{

c a s e

0 .-

 

 

 

c a s e

2 :

 

 

 

 

//

Действия

для

холостяков/незамужних и для

 

// разведенных одни и те же

 

break ;

 

 

c a s e

1 :

 

 

 

 

//

Действия

для

семейных

break ;

110

Часть II. Основы программирования в С#

case

3 :

 

 

 

//

 

Действия для вдов(цов)

 

break;

 

case

4

:

 

 

 

// Действия для неизвестног о семейного состояния

 

break;

 

d e f a u l t :

 

 

 

//

 

Действия,

когда переменная принимает значение,

 

//

 

отличающееся от все х перечисленных выше - по все й

 

//

 

видимости,

эт о означает , что произошла какая - то

 

//

 

ошибка

 

break;

}

Управление может быть передано в другую точку при помощи оператора безусловно­ го перехода g o t o . За этим оператором может следовать:

метка;

cas e в конструкции s w i t c h ;

ключевое слово d e f a u l t , обозначающее блок d e f a u l t в конструкции s w i t c h .

Два последних случая предназначены для перехода от одного блока c a s e к другому в конструкции s w i t c h .

 

Вот пример применения оператора g o t o :

//

Если

условие и с т и н н о . . .

if

(а >

Ь)

I

 

 

 

//

Управление оператором g o t o передаетс я коду,

 

//

расположенному за меткой e x i t L a b e l

I'goto e x i t L a b e l ;

//Некоторый программный код exitLabel:

//Управление передаетс я в э т у точку

Оператор g o t o крайне непопулярен

по той

же причине, по которой он является

очень мощным средством — в силу его

полной

неструктурированности. Отслеживание

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

Вокруг применения g o t o ведутся почти "религиозные войны". Доходит до критики С# просто за то, что в нем есть этот оператор. Но на самом деле в нем нет ничего ужасного или демонического. Другое дело, что его применения сле­ дует избегать, если в этом нет крайней нужды.

Глава 5. Управление потоком выполнения

111

Одно дело — объявить переменные там и сям, и складывать их, и вы­ читать. И совсем другое — писать реальные программы, которыми может пользоваться еще кто-то. В этой части речь пойдет о том, как группировать данные и работать с ними. Вы научитесь думать о про­ граммах как о наборе сотрудничающих объектов и приступите к созданию собственных объектов. Это заложит основу для всей вашей будущей деятельности в качестве программистов.

Глава 6

Объединение данных - классы и массивы

>Введение в классы С#

>Хранение данных в объектах

>Ссылки на объекты

>Создание массивов объектов

ы можете

свободно объявлять и использовать все встроенные типы данных —

такие как

i n t , d o u b l e или b o o l — для хранения информации, необходимой

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

большинству программ требуется средство для объединения связанных данных в акку­

ратные пакеты.

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

и т.п. Логически рассуждая,

имя

студента

может

иметь тип s t r i n g , год рождения —

int или s h o r t , средний б а л л —

d o u b l e .

Такой

программе необходима возможность

объединить эти разнотипные

переменные

в единую структуру под именем S t u d e n t .

К счастью, в С# имеется структура, известная как класс, которая предназначена для об­ легчения группирования таких разнотипных переменных.

В других случаях программам требуются наборы однотипных объектов. Возьмем для примера программу, которая должна усреднять успеваемость. Тип d o u b l e — естест­ венный кандидат для представления индивидуальной успеваемости студента, но для того чтобы представлять успеваемость группы студентов, необходим тип, который является набором d o u b l e . Для таких целей в С# существуют массивы.

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

Классы

Класс представляет собой объединение разнотипных данных и функций, логически организованных в единое целое. С# предоставляет полную свободу при создании клас­ сов, но хорошо спроектированные классы призваны представлять концепции.

Аналитик скорее всего скажет, что "класс отображает концепцию из предметной об ласти задачи в программу". Предположим, например, что ваша задача— построения имитатора дорожного движения, который должен смоделировать улицы, перекрестка шоссе и т.п.

Любое описание такой задачи должно включать термин транспортное средство Транспортные средства обладают определенной максимальной скоростью движении имеют вес, и некоторые из них оснащены прицепами. Таким образом, имитатор дорож ного движения должен включать класс V e h i c l e , у которого должны иметься свойств наподобие dTopSpeed, nWeight и b T r a i l e r .

Поскольку классы — центральная концепция в программировании на С#, они буи гораздо детальнее рассмотрены в главах части IV, "Объектно-ориентированное программирование"; здесь же описаны только азы.

О п р е д е л е н и е к л а с с а

Пример класса V e h i c l e может выглядеть следующим образом:

p u b l i c c l a s s V e h i c l e

 

 

 

{

 

 

 

 

 

p u b l i c

s t r i n g sModel;

/ /

Название модели

p u b l i c

s t r i n g sManufacturer ;

/ /

Производитель

p u b l i c

i n t

nNumOfDoors;

/ /

Количество

дверей

p u b l i c

i n t

nNumOfWheels;

//

Количество

колес

}

 

 

 

 

 

Определение класса начинается словами

p u b l i c c l a s s ,

за которыми идет имя

класса (в данном случае — V e h i c l e ) .

 

 

 

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

За именем класса следует пара фигурных скобок, внутри которых могут быть не сколько членов (либо ни одного). Члены класса представляют собой переменные, образующие часть класса. В данном примере класс V e h i c l e начинается с члена string sModel, который содержит название модели транспортного средства. Второй член в этом примере — s t r i n g sManuf a c t u r e r , а последние два члена содержат количество дверей и колес в транспортном средстве.

Как и в случае обычных переменных, делайте имена членов максимально информативными. Хотя я и добавил к именам членов комментарии, они не являются обязательными с точки зрения С#. Обо всем должны говорить имена переменных.

Модификатор p u b l i c перед именем класса делает класс доступным для всей про-! граммы. Аналогично, модификаторы p u b l i c перед именами членов также делают их доступными для всей программы. Возможны и другие модификаторы, но более подроб­ но о доступности речь пойдет в главе 11, "Классы".

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

116

Часть III. Объектно-основанное программирование

Глав,

 

Что такое о б ъ е к т

Объект класса объявляется аналогично встроенным объектам С#, но не идентично им.

Термин объект используется для обозначения "вещей". Да, это не слишком полезное определение, так что приведем несколько примеров. Переменная типа i n t является объектом i n t . Автомобиль является объектом V e h i c l e .

Вот фрагмент кода, создающий автомобиль, являющийся объектом V e h i c l e :

Vehicle myCar;

myCar = new V e h i c l e () ;

В первой строке объявлена переменная myCar типа V e h i c l e , так же как вы можете

объявить переменную nSomething класса

i n t (да, класс является типом, и все объекты

С# определяются как классы). Команда

new V e h i c l e () создает конкретный объект

типа Vehicle и сохраняет его местоположение в переменной myCar. Оператор new ("новый") не имеет ничего общего с возрастом автомобиля — он просто выделяет новый блок памяти, в котором ваша программа может хранить свойства автомобиля myCar.

В терминах С# myCar — это объект класса V e h i c l e . Можно также сказать, что myCar— экземпляр класса V e h i c l e . В данном контексте экземпляр

(instance) означает "пример" или "один из". Можно использовать этот термин и как глагол, и говорить об инстащировании V e h i c l e — это именно то, что

делает оператор new.

Сравните объявление myCar с объявлением переменной num типа i n t :

int num; num = 1;

В первой строке объявлена переменная num типа i n t , а во второй созданной пере­ менной присваивается значение типа i n t , которое вносится в память по месту располо­ жения переменной num.

Переменная встроенного типа num и объект my Саг хранятся в памяти поразному. Константа 1 не занимает память, поскольку и процессор, и С# знают, что такое 1. Но процессор понятия не имеет о такой концепции, как V e h i c l e . Выражение new V e h i c l e выделяет память, необходимую для описания транспортного средства, понятного процессору, С#, и вообще — всему миру.

Доступ к ч л е н а м о б ъ е к т а

Каждый объект класса V e h i c l e имеет собственный набор членов.

Приведенное да­

лее выражение сохраняет число 1 в члене nNumberOf Doors объекта,

на который ссы­

лается myCar:

 

 

 

 

myCar. nNumberOf Door s = 1;

 

 

 

Каждый оператор С# имеет не только значение, но и тип. Объект myCar явля­

ется объектом типа V e h i c l e . Переменная V e h i c l e .nNumberOf Doors

име­

ет тип i n t

(вернитесь к определению

класса V e h i c l e ) . Константа 1

также

имеет тип

i n t , так что типы константы

с правой стороны от оператора при­

сваивания и переменной с левой стороны соответствуют друг другу.

Глава 6. Объединение данных - классы и массивы

117

Аналогично, в следующем фрагменте кода сохраняются ссылки на строки strinJ

описывающие модель и производителя myCar:

myCar . sManufacturer = "BMW"; myCar.sModel = " I s e t t a " ;

(Isetta— небольшой автомобиль, который производился в 1950-х годах и имел одя

дверь впереди.)

Пример объектно-основанной программы

Программа V e h i c l e D a t a O n l y очень проста и делает следующее:

определяет класс V e h i c l e ; создает объект myCar; указывает свойства myCar;

получает значения свойств myCar и выводит их на экран.

Вот код программы V e h i c l e D a t a O n l y :

/ / V e h i c l e D a t a O n l y

 

 

 

//

Создает

объект V e h i c l e ,

заполняет

ег о

члены информацией,

//

вводимой

с клавиатуры,

и выводит

ее на

экран

u s i n g System ;

namespace V e h i c l e D a t a O n l y

{

p u b l i c c l a s s V e h i c l e

{

p u b l i c

s t r i n g sModel;

p u b l i c

s t r i n g sManufacturer ;

p u b l i c

i n t nNumOfDoors,-

p u b l i c

i n t nNumOf Wheels,-

//Модель

//Производитель

//Количество дверей

//Количество колес

}

p u b l i c c l a s s Program

{

//Начало программы

s t a t i c v o i d M a i n ( s t r i n g [ ] a r g s )

{

//Приглашение пользователю

C o n s o l e . W r i t e L i n e ( " В в е д и т е информацию о машине");

// Создание экземпляра V e h i c l e V e h i c l e myCar = new V e h i c l e (),-

//Ввод информации для членов класса

Console . Write("Модел ь = " ) ;

s t r i n g s = C o n s o l e . R e a d L i n e ( ) ; myCar.sModel = s;

118

Часть III. Объектно-основанное программировав