Главная      Учебники - Разные     Лекции (разные) - часть 35

 

Поиск            

 

Основы алгоритмизации и программирования 2

 

             

Основы алгоритмизации и программирования 2

ОСНОВЫ АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ

КОНСПЕКТ ЛЕКЦИЙ

Тамбов 2009

ББК

УДК

Х

Утверждена на заседании кафедры прикладной информатики Тамбовского филиала ФГОУ ВПО МГУКИ «__» августа 2009 г., протокол № __

Составители: канд. техн. наук, доцент В.Н. Точка,

Основы алгоритмизации и программирования:конспект лекций для студентов, обучающихся по специальности 080801 «Прикладная информатика (в менеджменте) (дневной формы обучения)»:/ сост. В.Н. Точка. - Тамбов, 2009.- 100 с.

© Тамбовский филиал ФГОУ ВПО МГУКИ, 2009

СОДЕРЖАНИЕ

1. ПЕРЕМЕННЫЕ И ВЫРАЖЕНИЯ .................................................................8

1.1. Базовый синтаксис С# .....................................................................................8

1.2. Переменные ...................................................................................................9

1.2.1. Простые типы переменных ......................................................................10

1.2.2. Именование переменных .........................................................................11

1.2.3. Соглашения, используемые при именовании .............................................11

1.2.4. Литеральные значения ............................................................................11

1.2.5. Литеральные строки................................................................................12

1.2.6. Объявление переменных и присваивание им значений ...............................12

1.3. Выражения ....................................................................................................12

1.3.1. Математические операторы .....................................................................13

1.3.2. Операторы присваивания ........................................................................14

1.3.3. Старшинство операторов .........................................................................14

1.4. Пространства имен .........................................................................................14

1.5. Вопросы для повторения ............................................................................16

2. УПРАВЛЕНИЕ ПОРЯДКОМ ВЫПОЛНЕНИЯ ПРОГРАММЫ ................17

2.1. Булева логика .................................................................................................17

2.2. Операторы работы с битами ............................................................................18

2.2.1. Логические операторы присваивания .......................................................19

2.2.2. Старшинство операторов с дополнениями ................................................20

2.2.3. Оператор goto. ........................................................................................20

2.3. Ветвление ......................................................................................................21

2.3.1. Тринарный оператор ...............................................................................21

2.3.2. Оператор if. ............................................................................................21

2.3.3. Проверка большего количества условий с помощью оператора if ................22

2.3.4. Оператор switch ......................................................................................23

2.4. Организация циклов .......................................................................................23

2.4.1. Цикл do. .................................................................................................24

2.4.2. Цикл while..............................................................................................24

2.4.3. Цикл for .................................................................................................24

2.4.4. Прекращение выполнения цикла ..............................................................25

2.4.5. Бесконечные циклы ................................................................................25

2.5. Вопросы для повторения ............................................................................25

3. ДОПОЛНИТЕЛЬНЫЕ СВЕДЕНИЯ О ПЕРЕМЕННЫХ ............................26

3.1. Преобразованиепеременных из одного типа в другой ........................................26

3.1.1. Неявные преобразования .........................................................................26

3.1.2. Явные преобразования ............................................................................27

3.1.3. Выполнение явных преобразований с помощью команд преобразования .....28

3.2. Сложные типы переменных ............................................................................28

3.2.1. Перечислимый тип .................................................................................28 3.2.2. Определение перечислимых типов ...........................................................28

3.2.3. Структуры .............................................................................................29

3.2.4. Массивы ................................................................................................29 3.2.5. Циклы foreach. ........................................................................................31

3.2.6. Многомерные массивы ...........................................................................31 3.2.7. Массивы массивов ..................................................................................31

3.2.8. Действия над строками ............................................................................32

3.3. Вопросы для повторения ............................................................................33

4. ФУНКЦИИ ........................................................................................................34

4.1. Описание и использование функций ................................................................34

4.2. Возвращаемые значения..................................................................................35

4.3. Параметры .....................................................................................................35

4.3.1. Соответствие параметров ........................................................................36

4.3.2. Массивы параметров ...............................................................................36 4.3.3. Передача параметров по ссылке и по значению .........................................36

4.3.4. Выходные параметры .............................................................................37

4.4. Область действия переменных .........................................................................38 4.5. Функция Main() ..............................................................................................39

4.6. Функции структур ..........................................................................................40 4.7. Перегрузка функций .......................................................................................40

4.8. Вопросы для повторения ............................................................................41

5. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ...............42

5.1. Объектно-ориентированное программирование ................................................42

5.2. Объект ...........................................................................................................42

5.2.1. Свойства и поля ......................................................................................43

5.2.2. Методы..................................................................................................43

5.2.3. Конструкторы ........................................................................................44

5.2.4. Деструкторы ..........................................................................................44

5.2.5. Статические члены класса и члены класса экземпляра ................................44

5.3. Ссылочные и значимые типы данных ...............................................................45

5.3.1. Структуры .............................................................................................45

5.3.2. ООП в приложениях Windows .................................................................45

5.4. Вопросы для повторения ............................................................................46

6. ОПРЕДЕЛЕНИЕ КЛАССОВ ...........................................................................47

6.1. Определение классов в С# ...............................................................................47

6.2. Конструкторы и деструкторы ..........................................................................49

6.3. Типы структур................................................................................................50

6.4. Неглубокое и глубокое копирование ................................................................51

6.5. Вопросы для повторения ............................................................................52

7. ЧЛЕНЫ КЛАССОВ И ИХ СВОЙСТВА ........................................................52

7.1. Члены классов ................................................................................................52

7.1.1. Определение членов................................................................................52

7.1.2. Определение полей .................................................................................52

7.1.3. Определение методов ..............................................................................52

7.1.4. Определение свойств ..............................................................................53

7.2. Свойства членов .............................................................................................55

7.2.1. Дополнительные действия с членами класса .............................................55

7.3. Вопросы для повторения ............................................................................56

8. РАБОТА С ФАЙЛАМИ ...................................................................................57

8.1. Потоки ..........................................................................................................57

8.1.1. Классы для ввода и вывода ......................................................................57

8.1.2. Классы File и Directory.............................................................................57

8.1.3. Класс FileInfo..........................................................................................58

8.1.4. Класс DirectoryInfo ..................................................................................58

8.1.5. Имена пути и относительные пути ...........................................................58

8.1.6. Объект FileStream ...................................................................................58

8.1.7. Позиция внутри файла ............................................................................59

8.1.8. Чтение данных .......................................................................................59

8.1.9. Запись данных ........................................................................................60

8.2. Объект StreamWriter........................................................................................61 8.3. Форматирование данных .................................................................................62

8.4. Объект StreamReader. ......................................................................................62

8.4.1. Чтение данных .......................................................................................63

8.4.2. Файлы с ограничителем ..........................................................................63

8.5. Вопросы для повторения ............................................................................64

ЗАДАНИЯ ДЛЯ САМОСТОЯТЕЛЬНОЙ РАБОТЫ ........................................65 СПИСОК ЛИТЕРАТУРЫ ...................................................................................66

1. ПЕРЕМЕННЫЕ И ВЫРАЖЕНИЯ

Компьютерная программа это последовательность операций над данными. Это утверждение оказывается справедливым даже для наиболее сложных случаев, таких, например, как развернутые, многофункциональные Windowsприложения типа Microsoft Office Suite. И хотя пользователи зачастую не задумываются над структурой приложений, внутренняя сущность последних заключается именно в этом.

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

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

Рассмотрим базовый синтаксис, используемый при программировании на С#.

1.1.Базовый синтаксис С#

Внешне код на С# очень напоминает код на C++ или на Java. Компиляторы С# не обращает внимание на лишнее пустое пространство, которое может состоять из пробелов, возвратов каретки или символов табуляции (эти символы известны под общим названием символов пустого пространства). Отсюда можно сделать вывод, что мы обладаем большой свободой в выборе способа форматирования нашей программы, хотя следование определенным правилам помогает создавать более удобные для чтения программы.

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

С# – язык, обладающий блочной структурой; другими словами, каждый оператор является частью некоторого блока кода. Эти блоки, для обозначения начала и конца которых используются фигурные скобки, соответственно, { и }, могут содержать произвольное количество операторов или не содержать их вовсе. Фигурные скобки не сопровождаются точками с запятой.

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

{

<строка кода 1, оператор 1>;

<строка кода 2, оператор 2>

<строка кода 3, оператор 2>

;

}

Здесь выражения <строка кода х, оператор у> не являются действительными составляющими кода на С#: этот текст используется для обозначения места, куда впоследствии будут помещены операторы С#. В данном примере вторая и третья строки кода являются частями одного и того же оператора, поэтому после второй строки точка с запятой отсутствует.

В этом простом примере кода использованы отступы, чтобы сделать запись на С# более наглядной. Такой подход является обычной практикой; в действительности VS автоматически делает такие отступы по умолчанию. Каждый блок кода имеет свою величину отступов, определяющую, насколько далеко вправо он сдвинут. Блоки могут быть вложены друг в друга (т. е. блоки могут содержать в себе другие блоки), и в таком случае величина отступа у вложенных блоков должна быть больше:

{

<строка кода 1>;

{

<строка кода 2>;

<строка кода 3>;

}

<строка кода 4>; }

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

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

Еще одна вещь, которая может часто встречаться в коде на С#, это комментарии. Комментарии не являются частью кода на С#, однако они сосуществуют с ним. Предназначение комментариев в точности соответствует их названию: они позволяют включать в программу описательный текст на обычном английском (французском, немецком, монгольском и т. д.) языке, который игнорируется компилятором. Когда приступают к созданию достаточно длинных сегментов кода, то бывает полезно включить в программу напоминание о том, что именно разработчики собираются сделать. В С# предусмотрены два способа включения комментариев. Можно использовать либо специальные маркеры, обозначающие начало и завершение комментария, либо маркер, который означает, что "все, что находится за ним до конца строки, является комментарием".

Для того чтобы обозначить комментарии по первому способу, используются сочетания символов /* в начале комментария и */ в конце комментария. Они могут располагаться как на одной строке, так и на разных строках; в последнем случае все строки, находящиеся между ними, также являются частью комментария. Единственное, что не может входить в состав комментария,– это сочетание символов */, поскольку оно будет интерпретировано как конец комментария. Допустимы следующие комментарии:

/* Это комментарий */

/* Так же, как . . .

и это! */

А вот приведенный ниже пример ошибочен:

/* Сочетание символов “*/” часто используется для завершения комментария */

В этом случае последняя часть комментария (символы, расположенные после */) будет интерпретирована как код С#, что приведет к возникновению ошибки.

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

// Это еще один вид комментария.

А вот попытка воспользоваться следующим комментарием приведет к неудаче, поскольку вторая строка будет интерпретироваться как код на С#

// Это тоже комментарий,

а вот это уже нет.

Такой вид комментариев оказывается полезным для документирования отдельных операторов, поскольку весь комментарий целиком умещается на одной строке:

<Некоторый оператор>; // Объяснение данного оператора

В С# существует еще и третий тип комментариев, который позволяет создавать документацию для программы. Этот комментарий также расположен на одной строке, но в отличие от последнего случая начинается с трех символов /, а не с двух:

/// Это особый вид комментария

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

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

Базовая структура консольного приложения на С#

Простейший код консольного приложения (Console Application) имеет следующий вид:

using System;

namespace ConsoleApplication {

l

/// <summary>

/// Summary description for Class

/// </summary>

class Classl

{

static void Main (string[] args) {

//

// ТОDО: Add code to start application here //

static void Main(string[] args)

Console.WriteLine(“The first application"); } } }

Рис. 1.1 – Простейшая программа вида Console Application

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

Наиболее важной частью кода является следующее: static void Main(string[] args)

{

// TODO: Add code to start application here

// static void Main(string[] args)

Console.WriteLine(“The first app in Beginning C# Programming!");

}

Это тот заключенный в фигурные скобки блок кода, который и выполняется при запуске. Единственной строкой, выполняющей какие-либо действия, является строка, которую добавили к автоматически сгенерированному коду и которая – одна из всех – не является комментарием. Этот код выводит некоторый текст в консольном окне, а понимание того, каким образом выполнение приложения доходит до точки вызова Console.WriteLine(), а также механизм его работы сейчас не важен.

1.2.Переменные

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

Хотя все данные в компьютере фактически представляют собой одно и то же (последовательность, состоящую из нулей и единиц), переменные могут отличаться друг от друга, образуя так называемые типы. Возвращаясь к аналогии с коробочками, можно представить коробочки различных размеров и форм, причем разные объекты могут помещаться только в специально предназначенные для них коробочки. Обоснование системы типов заключается в том, что для манипуляций над данными различных типов могут требоваться различные методы, и, приписав переменным конкретные типы, можно избежать серьезной путаницы. Ведь вряд ли получится что-то осмысленное, если рассматривать последовательность нулей и единиц, представляющую собой оцифрованное изображение, в качестве звукового файла.

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

Синтаксис объявления переменных в С# состоит из указания типа переменной и ее имени:

<ТИП> <ИМЯ>;

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

Существует бесконечное множество типов, которые можно использовать, так как имеется возможность описывать

свои собственные типы, предназначенные для хранения любых даже самых сложных данных.

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

1.2.1.Простые типы переменных

Простые типы – это такие типы значений, как численные или логические (true (истина) или false (ложь)), которые являются составными частями любых приложений, а также других, более сложных типов. Большинство из имеющихся типов являются численными, что на первый взгляд выглядит несколько странно: разве для хранения чисел не достаточно одного типа?

Причина такого изобилия численных типов кроется в способе хранения чисел в памяти компьютера как последовательности нулей и единиц. В случае целых значений мы используем несколько бит (отдельных цифр, которые могут принимать значение нуля или единицы) и просто представляем число в двоичном формате. Переменная, в которой может храниться N бит, позволяет представлять любые числа в диапазоне от 0 до ( N 12 - ). Числа, превышающие последнее значение, оказываются слишком большими и не могут быть размещены в переменной данного типа.

В качестве примера рассмотрим переменную, в которой может храниться 2 бита. Соответствие между целыми числами и их двоичным представлением в этом случае будет следующим:

0 = 00 1 = 01 2 = 10 3 = 11

Если необходимо хранить большие числа, то для этого потребуется большее количество бит (3 бита, например, позволят хранить числа, лежащие в диапазоне от 0 до 7).

Отсюда неизбежно следует вывод о том, что хранение произвольного числа требует бесконечного количества бит, что, безусловно, неприемлемо для персонального компьютера. Но даже если бы необходимое количество бит и имелось, использовать такой объем памяти для числа, изменяющегося, скажем, от 0 до 10, было бы крайне неэффективно (из-за неиспользуемой памяти). В данном случае вполне достаточно 4 бит, которые позволяют разместить в одной и той же области памяти все числа указанного диапазона.

Именно для этого существует несколько целых типов, которые могут быть использованы для хранения чисел из различных диапазонов и которые занимают различный объем памяти (вплоть до 64 битов). Эти типы целых чисел сведены в таблицу, приводимую ниже.

Каждый тип использует один из стандартных типов, определенных в .NET Framework. Применение стандартных типов – это как раз то, что делает возможным совместное использование различных языков программирования. Имена, которые используются для этих типов в С#, обозначают определенные типы, описанные в .NET Framework. В таблице 1.1 приводятся имена типов и указывается, какому типу в библиотеке .NET Framework они соответствуют.

Буква "u", встречающаяся перед некоторыми из имен, является сокращением от слова "unsigned" ("беззнаковый"). Это говорит о том, что в переменных соответствующего типа не могут храниться отрицательные значения. Этот факт отражен в столбце "Допустимые значения".

Таблица 1.1 – Типы данных C# и соответствующие им типы .NET Framework

Тип

Тип .NET Framework

Допустимые значения

sbyte

System. Byte

Целое в диапазоне от -128 до 127

byte

System. Byte

Целое в диапазоне от 0 до 255

short

System.Intl6

Целое в диапазоне от -32 768 до 32 767

ushort

System.UIntl6

Целое в диапазоне от 0 до 65 535

int

System.Int32

Целое в диапазоне от -231 до 231 -1

uint

System.UInt32

Целое в диапазоне от 0 до 232 -1

long

System.Int64

Целое в диапазоне от -263 до 263 -1

ulong

System.UInt64

Целое в диапазоне от 0 до 264 -1

Кроме целых чисел часто требуется хранить дробные значения (или значения с плавающей запятой), которые представляют нецелые числа. Существует три типа переменных с плавающей запятой, которые можно использовать: float, double и decimal. Первые два из них хранят числа с плавающей запятой в виде ± m 2× e , причем диапазон допустимых значений m и е для каждого типа свой. Тип decimal использует альтернативную форму представления чисел ± m 10× e . Эти три типа приводятся ниже вместе с допустимыми значениями m и е и диапазонами действительных чисел

(таб. 1.2).

Таблица 1.2 – Дробные типы данных C# и соответствующие им типы .NET Framework

float

System.Single

0

224

-149

104

1.5 10× - 45

3.4 10× 38

double

System.Double

0

253

-1075

970

5 10× -324

1.7 10× 307

decimal

System.Decimal

0

296

-26

0

1 10× -28

7.9 10× 28

Кроме численных типов, существует еще три простых типа (таб. 1.3).

Таблица 1.3 – Символьные и логические типы данных C# и соответствующие им типы данных .NET Framework

Тип

Тип .NET Framework

Диапазон допустимых значений

char

System.Char

Отдельный символ в кодировке Unicode, хранящийся в виде целого числа в диапазоне от 0 до 65 535

bool

System.Boolean

Логическое значение (Boolean), которое может принимать значения true (истина) илиfalse(ложь)

string

System.String

Последовательность символов

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

Логический тип bool – один из наиболее часто используемых в С# типов переменной; аналогичные типы часто встречаются в кодах на других языках программирования. Использование переменной, которая может принимать только два значения – либо true, либо false,– создает очень важную возможность ветвления логики приложения. Одним из применений такого типа данных - сравнение значений переменных и проверка допустимости введенной информации.

1.2.2.Именование переменных

Нельзя использовать в качестве имени переменной произвольную последовательность символов. Однако в распоряжении разработчика имеется весьма гибкая система именования.

Основные правила при именовании переменных следующие:

- первый символ в имени переменной должен быть либо буквой, либо символом подчеркивания _, либо @; - последующими символами могут быть буквы, символ подчеркивания или цифры.

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

Если по ошибке используется одно из них, компилятор сгенерирует соответствующую ошибку при компиляции. Следующие имена переменных являются правильными: myBigVar

VAR1

_test

А вот эти – нет: 99BottlesOfBeer namespace

It's-All-Over

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

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

myVariable MyVariable MYVARIABLE

1.2.3.Соглашения, используемые при именовании

Имена переменных – это нечто такое, что приходится использовать очень часто. Поэтому стоит посвятить немного времени тому, какие типы имен следует применять. Однако прежде чем перейти к обсуждению этого вопроса, следует отметить, что данная тема полна противоречий. На протяжении многих лет одни системы именования сменялись другими, а некоторые разработчики готовы насмерть стоять за признание только собственных систем.

До недавнего времени наиболее популярной была система, известная под именем венгерской системы записи. Она предполагает использование перед именами всех переменных префикса, набираемого в нижнем регистре и обозначающего тип переменной. Например, если переменная имеет тип int, то перед ней следует поместить символ i, например, iAge. При использовании такой системы оказывается очень просто определять тип переменных даже при беглом взгляде на программу.

Однако в более современных языках, вроде С#, реализовать венгерскую систему оказывается совсем не просто. Эта система вполне может использоваться для простых типов, описанных выше (таб. 1.1-1.3), поскольку для обозначения каждого типа вполне достаточно префиксов, состоящих из одной или двух букв. Но поскольку имеется возможность создавать свои собственные типы и, кроме того, в базовой .NET Framework таких сложных типов присутствует несколько сотен, то система быстро перестает работать. Когда над проектом трудятся несколько человек, существенно возрастает вероятность того, что с использованием различных префиксов будет возникать путаница, которая может иметь самые разрушительные последствия.

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

В настоящее время в пространствах имен .NET Framework используются два соглашения об именовании, известные как PascalCase и camelCase. Отличить одну систему от другой можно по использованию регистра. Обе системы применяются к именам, составленным из нескольких слов; при этом каждое слово в составном имени набирается в нижнем регистре, за исключением первой буквы слова, которая должна набираться в верхнем регистре. В системе camelCase существует еще одно дополнительное правило, которое гласит, что первое слово должно начинаться с буквы в нижнем регистре.

Следующие имена переменных соответствуют правилам camelCase:

age

firstName

timeOfDeath

А вот так имена должны записываться в соответствии с правилами PascalCase:

Age

LastName

WinterОfDisconten

t

Следует отметить, что система PascalCase соответствует рекомендациям компании Microsoft.

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

Тип(ы)

Значение

Суффикс

Пример/Допустимые значения

bool

Логическое

Отсутствует

trueилиfalse

int, uint, long, ulong

Целое

Отсутствует

100

uint, ulong

Целое

u или U

100U

long, ulong

Целое

1 илиL

100L

ulong

Целое

ul, uL, Ul, UL, lu, lU, Lu или LU

100UL

float

Вещественное

f или F

1,5F

double

Вещественное

Отсутствует, d или D

1,5

decimal

Вещественное

m или М

1,5M

char

Символьное

Отсутствует

'а' или escapeпоследовательность

string

Строковое

Отсутствует

“a. . .а", может включать escape-последовательности

Таблица 1.4 – Типы переменных и соответствующие им литеральные значения разделителя слов в имени переменной: например, my_first_variable. Однако, такой подход в настоящее время не рекомендуется.

1.2.4.Литеральны е значения

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

1.2.5.Литеральные строки

Приведем таблицу всех возможных escape-последовательностей для справочных целей.

В таблице 1.5 последний столбец содержит шестнадцатеричные значения кодов символов в кодировке Unicode.

Кроме приведенных выше символов, с помощью escape-последовательности можно задать любой символ в кодировке Unicode. Такая последовательность должна состоять из стандартного символа \, за которым следует символ u и четырехзначное шестнадцатеричное значение (например, те четыре цифры, которые расположены после символа х в последнем столбце вышеприведенной таблицы).

Отсюда следует, что, например, следующие две строки эквивалентны

“Karli\’s string.”

“Karli\u0027s string.”

Очевидно, что возможность использовать escape-последовательности в кодировке Unicode дает дополнительную гибкость.

Также можно применять "дословные" строки (verbatim). Это означает, что в строку включаются все символы, находящиеся между двумя двойными кавычками, в том числе символы конца строки и символы, для которых в противном случае потребовалось бы использование escape-последовательностей. Единственным исключением из этого правила является escape-последовательность для символа двойной кавычки, которая должна использоваться во избежание преждевременного завершения обработки строки. Чтобы получить дословную строку, необходимо поместить в ее начало символ @:

@”Verbatim string literal.”

Эта строка может быть задана и обычным способом, а вот для следующего примера такой способ оказывается единственно возможным: @"A short list item 1 item 2"

Escapeпоследовательность

Выводимый символ

Код символа в кодировке Unicode

\’

Одиночная кавычка

0x0027

\”

Двойная кавычка

0x0022

\\

Обратный слэш

0х005С

\0

Null

0x0000

\a

Тревога (выдает звуковой сигнал)

0x0007

\b

Удаление предыдущего символа(backspace)

0x0008

\f

Формирование строки (form feed)

0х000С

\n

Новая строка

0х000А

\r

Возврат каретки

0x000D

\t

Символ горизонтальной табуляции

0x0009

\v

Символ вертикальной табуляции

0x000B

Дословные строки оказываются полезными, в частности, для имен файлов, поскольку в них обычно присутствует большое количество обратных слэшей. Использование обычных строк привело бы к необходимости использования Таблица 1.5 – Escape-последовательности, используемые в литеральных строках двойных обратных слэшей во всей строке. Например:

“C:\\Temp\\MyDir\\M yFile.doc”

Используя

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

@"С:\Temp\MyDir\M

yFile.doc”

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

1.2.6.Объявление переменных и присваивание им значений

Для объявления переменных требуется просто указать их тип и имя, например: int age;

Далее ей присваивается значение с помощью оператора присваивания: age = 25;

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

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

int xSize, ySize;

В данном случае обе переменные – xSize и ySize – описаны как целые числа.

Второй способ заключается в одновременном объявлении переменной и присваивании ей значения, что фактически означает объединение двух строк программы

int age = 25;

Существует возможность совместного использования этих двух способов: int xSize = 4, ySize = 5;

В данном случае соответствующие значения присваиваются и переменной xSize, и переменной ySize.

1.3.Выражения

Для выполнения действий над переменными язык С# имеет целый набор операторов, в частности, оператор присваивания =. Записывая операторы в определенном сочетании с переменными и литеральными значениями (при использовании совместно с операторами они называются операндами), можно создавать выражения, которые являются основными строительными блоками компьютерных программ.

Существуют различные операторы, начиная от самых простых и до чрезвычайно сложных, которые редко встречаются где-либо, кроме как в математических приложениях. Простые операторы включают в себя все основные математические действия (например, оператор + производит сложение двух операндов), а сложные операторы предназначены для выполнения манипуляций над двоичным представлением содержимого переменной. Существуют также логические операторы, работающие с логическими значениями, и операторы присваивания типа =.

Все математические операторы могут быть условно разделены на три категории:

- унарные операторы, которые выполняют действие над единственным операндом;

- бинарные операторы, которые выполняют действие над двумя операндами;

- тринарные операторы, которые выполняют действие над тремя операндами.

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

1.3.1.Математические операторы

Существует пять простых математических операторов, причем два из них имеют как бинарную, так и унарную форму. В приведенной ниже таблице 6 перечислены все математические операторы, даны элементарные примеры их использования и показаны результаты их работы с простыми численными типами (целыми числами и числами с плавающей запятой).

Здесь приведены примеры с использованием простых численных типов, так как результаты, получаемые при действиях с другими простыми типами, могут оказаться неясными. Действительно, что можно получить при сложении двух логических переменных? Ничего, поскольку компилятор не позволит использовать оператор + (а также любые другие

Таблица 1.6.1 – Математический оператор сложения строк

Оператор

Значение

Пример использования в выражении

Результат

+

Бинарное

varl = var2 + var3;

Переменной varl присваивается значение, которое представляет собой результат объединения двух строк, хранящихся в переменных var2 и var3

Таблица 1.7 – Операторы инкремента и декремента в C#

Оператор

Значение

Пример

использования в выражении

Результат

++

Унарное

varl = ++var2;

Переменной varl присваивается значение переменной var2+1. Переменная var2 увеличивается на единицу

--

Унарное

varl = --var2;

Переменной varl присваивается значение переменной var2-l. Переменнаяvar2 уменьшается на единицу

++

Унарное

varl = var2++;

Переменной varl присваивается значение переменной var2. Переменная var2 увеличивается на единицу

--

Унарное

varl = var2--;

Переменной varl присваивается значение переменной var2. Переменнаяvar2 уменьшается на единицу

Таблица 1.6 – Математические операторы C#

Оператор

Значение

Пример использования в выражении

Результат

+

Бинарное

varl = var2 + var3;

Переменной varl присваивается значение, которое представляет собой сумму значений переменных var2 и var3

-

Бинарное

varl = var2 - var3;

Переменной varl присваивается значение, получающееся в результате вычитания значения переменнойvar3 из значения переменнойvar2

*

Бинарное

varl = var2 * var3;

Переменной varl присваивается значение, которое представляет собой результат перемножения

переменныхvar2 иvar3

/

Бинарное

varl = var2 / var3;

Переменной varl присваивается значение, которое представляет собой результат деления переменной var2 на переменную var3

%

Бинарное

varl = var2 % var3;

Переменной varl присваивается значение, которое представляет собой остаток от деления переменной var2 на переменнуюvar3

+

Унарное

varl = +var2;

Переменной varl присваивается значение переменнойvar2

Унарное

varl = -var2;

Переменной varl присваивается значение переменной var2, умноженное на -1

математические операторы) с переменными типа bool. Сложение переменных типа char также выглядит несколько

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

Необходимо отметить, что бинарный оператор + имеет смысл и тогда, когда он используется с переменными типа string. В этом случае предыдущая таблица должна быть дополнена следующим (табл. 6.1).

Никакие другие математические операторы к строкам применяться не могут.

Два других оператора, это операторы увеличения (инкремент) и уменьшения (декремент), каждый из которых является унарным оператором и может использоваться одним из двух способов: либо непосредственно перед операндом, либо сразу после него (табл. 1.7).

Эти операторы всегда приводят к изменению значения, хранящегося в соответствующем операнде. Оператор ++ всегда увеличивает значение своего операнда на 1, а оператор – уменьшает его на 1. Отличие заключается в том результате, который записывается в переменную varl, поскольку местоположение оператора определяет порядок выполнения действий. Когда любой из этих двух операторов помещен перед операндом, он выполняется прежде какихлибо других вычислений. Размещение любого из этих двух операторов после операнда приводит к тому, что оператор выполняется после завершения всех остальных вычислений для данного выражения.

Рассмотрим следующий код:

int varl, var2 = 5, var3 = 6; varl = var2++ * --var3;

Вопрос: какое значение будет присвоено переменной varl? Перед тем как выражение начнет вычисляться, будет выполнен оператор --, расположенный перед переменной var3, что приведет к изменению ее значения с 6 на 5. Можно проигнорировать оператор ++, расположенный за переменной var2, поскольку он не будет оказывать никакого влияния до тех пор, пока вычисление этого выражения не будет завершено, поэтому в varl будет записан результат умножения 5 на 5, т. е. 25.

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

varl = varl + 1;

Такие выражения имеют очень широкое распространение, в частности, при организации циклов.

1.3.2.Операторы присваивания

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

Все другие операторы присваивания работают схожим с оператором = образом. Так же, как и у оператора =, результат их выполнения – присваивание расположенной слева переменной значения, вычисленного посредством операторов и операндов, расположенных в правой части.

Результатом использования дополнительных операторов присваивания является вовлечение переменной varl в вычисления, т. е. код вроде.

varl += var2;

приводит к получению точно такого же результата, что и код:

varl = varl + var2;

Оператор += может использоваться и со строками, так же, как и оператор +.

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

1.3.3.Старшинство операторов

При вычислении выражения все операторы выполняются в определенной последовательности. Это не означает, что они всегда выполняются слева направо. В качестве самого простого примера рассмотрим следующий оператор varl = var2 + var3;

Здесь оператор + выполняется перед оператором =.

Оператор

Значение

Пример

использования в выражении

Результат

Бинарное

varl = var2;

Переменной varl присваивается значение переменной var2

+=

Бинарное

varl += var2;

Переменной varl присваивается значение, которое представляет собой сумму переменных varl и var2

-=

Бинарное

varl -= var2;

Переменной varl присваивается значение, которое представляет собой разность, полученную в результате вычитания переменной var2 из переменной varl

*=

Бинарное

varl *= var2;

Переменной varl присваивается значение, которое представляет собой произведение переменных varl и var2

/=

Бинарное

varl /= var2;

Переменной varl присваивается значение, полученное в результате деления переменной varl на переменную var2

%=

Бинарное

varl %= var2;

Переменной varl присваивается значение, являющееся остатком от деления переменной varl на переменную var2

Таблица 1.8 – Операторы присваивания Существуют и другие ситуации, когда старшинство операторов не совсем очевидно, например:

varl = var2 + var3 *

var4;

В этом выражении сначала будет выполнен оператор *, затем оператор + и последним – оператор =. Это принятый в математике стандартный порядок выполнения операций, он приведет к получению того же самого результата, который можно ожидать при выполнении аналогичных арифметических вычислений на бумаге. Можно также управлять порядком выполнения операторов с помощью скобок, например: varl = (var2 + var3) * var4;

В данном случае в первую очередь будет выполняться то, что содержится в скобках, то есть оператор + будет выполнен перед оператором *.

Для всех рассматриваемых операторов порядок выполнения показан ниже; при этом операторы с одинаковым старшинством (такие как * и /) выполняются в порядке своего следования слева направо.

Таблица 1.9 – Старшинство операторов

Старшинство

Операторы

Высшее

++,--(используемые в качестве префиксов) , +, -(унарные) *, /, %, =, *=, /=,

%=, +=,-=

Низшее

++,--(используемые в качестве суффиксов)

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

1.4.Пространства имен

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

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

Код С# по умолчанию содержится в глобальном пространстве имен. Это означает, что к объектам в коде С# можно обратиться из любого другого кода в глобальном пространстве имен просто по их имени. Можно воспользоваться ключевым словом namespace для того, чтобы явно задать пространство имен для любого блока кода, заключенного в фигурные скобки. Имена, находящиеся в таком пространстве имен, если к ним обращаются не из данного пространства имен, должны квалифицироваться.

Квалифицированным именем называется имя, в котором содержится вся информация касательно его иерархии. Это означает, что если имеется код, находящийся в одном пространстве имен, и необходимо воспользоваться именем, определенным в другом пространстве имен, то следует использовать ссылку на это пространство имен. В квалифицированных именах для разделения уровней пространств имен используется символ точки. Например:

namespace LevelOne {

// программа, находящаяся в пространстве имен LevelOne

//в ней описывается имя "NameOne"

}

// программа, находящаяся в глобальном пространстве имен

В этой программе описывается единственное пространство имен – LevelOne. В данном случае в программе не содержится никакого исполняемого кода. Это сделано для того, чтобы придать обсуждению максимально общий характер; напротив, в код помещен комментарий, в котором содержится это описание. Код, содержащийся внутри пространства имен LevelOne, может просто ссылаться на имя NameOne, и никакой классификации не требуется. Однако в коде, находящемся в глобальном пространстве имен, необходимо использовать классифицированное имя LevelOne. NameOne для того, чтобы на него сослаться.

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

namespace LevelOne {

// программа, находящаяся в пространстве имен LevelOne namespace LevelTwo {

// программа, находящаяся в пространстве имен LevelOne.LevelTwo

//в ней описывается имя "NameTwo" }

}

// программа, находящаяся в глобальном пространстве имен

В данном случае обращение к имени NameTwo из глобального пространства имен должно иметь вид LevelOne.LevelTwo.NameTwo, из пространства имен LevelOne – LevelTwo.NameTwo, а из пространства имен LevelOne.LevelTwo – NameTwo.

Имена идентифицируются пространствами имен уникальным образом. Можно описать одно и то же имя NameThree как в пространстве имен LevelOne, так и в пространстве имен LevelTwo:

namespace LevelOne {

// здесь описывается имя "NameThree" namespace LevelTwo {

// здесь описывается имя "NameThree" }

}

В данном случае описаны два разных имени, которые могут использоваться независимо друг от друга LevelOne.NameThree и LevelOne.LevelTwo.NameThree.

После того, как пространство имен определено, появляется возможность использовать оператор using для упрощения доступа к содержащимся в нем именам. Оператор using как бы говорит: "Да, нам действительно понадобится доступ к именам из данного пространства имен, поэтому не заставляйте меня классифицировать их каждый раз". Например, в следующей программе предполагается, что код, находящийся в пространстве имен LevelOne, должен иметь доступ к именам пространства имен LevelOne.LevelTwo без какой бы то ни было классификации:

namespace LevelOne

{ using LevelTwo; namespace LevelTwo {

// здесь описывается имя "NameTwo"

}

}

Код, находящийся в пространстве имен LevelOne, теперь может обращаться к LevelTwo.NameTwo просто как к NameTwo.

Бывают случаи, как в примере с NameThree, когда такой подход может приводить к конфликту между именами, находящимися в различных пространствах имен (при этом код, скорее всего, компилироваться не будет). В подобных случаях можно задать для пространства имен подставное имя (alias) в операторе using:

namespace LevelOne {

using LT = LevelTwo;

// здесь описывается имя "NameThree"

namespace LevelTwo {

// здесь описывается имя "NameThree" } }

Тогда в пространстве имен LevelOne мы сможем обращаться к LevelOne.NameThree просто как к NameThree, а к LevelOne.LevelTwo.NameThree как к LT.NameThree.

Операторы using оказывают влияние на то пространство имен, в котором они находятся, а также на все вложенные пространства имен, которые могут содержаться в данном пространстве имен. В вышеприведенном коде в глобальном пространстве имен использовать LT.NameThree нельзя. Однако если оператор using расположить следующим образом:

using LT = LevelOne.LevelTwo; namespace LevelOne {

// здесь описывается имя "NameThree namespace LevelTwo

{

// здесь описывается имя 'NameThree'

}

} то использовать имя LT.NameThree можно будет и из глобального пространства имен, и из пространства имен LevelOne.

Оператор using сам по себе не обеспечивает доступа к именам, находящимся в других пространствах имен. До тех пор, пока код из пространства имен не будет каким-либо способом привязан к проекту (например, описан в исходном файле проекта или описан в каком-либо коде), привязанному к этому проекту, получить доступ к содержащимся в нем именам будет невозможно. Более того, если код, в котором содержится некое пространство имен, привязан к проекту, то имеется доступ к содержащимся в нем именам независимо от использования оператора using. Оператор using всего лишь упрощает обращение к этим именам и позволяет сократить сильно удлиняющийся в противном случае код, делая его более понятным.

Вернувшись к программе Console Application (рис.1) можно обнаружить там следующие строки кода, относящиеся к пространствам имен:

using System;

namespace ConsoleApplication { }

В первой строке располагается оператор using, в котором объявляется, что пространство имен System будет использоваться во всей программе С# и доступ к нему из любых пространств имен данного файла должен осуществляться без использования классификации. Пространство имен System является корневым пространством имен в .NET Framework и включает все основные функциональные возможности, для консольных приложений.

1.5. Вопросы для повторения

1. Что такое атомарные переменные?

2. Перечислите основные типы переменных, используемых в C#.

3. Приведите общий вид программы консольного приложения.

4. Какие правила используются при именовании переменных?

5. Что такое пространства имен?

6. Приведите математические операторы, использемые в языке C#.

7. Что такое старшинство операторов?

2. УПРАВЛЕНИЕ ПОРЯДКОМ ВЫПОЛНЕНИЯ ПРОГРАММЫ

У всех программ на С#, которые рассматривались до сих пор, имеется одна общая черта. Во всех случаях выполнение кода происходило последовательно сверху вниз, строка за строкой, без каких-либо пропусков. Однако если бы приложения всегда выполнялись таким образом, то возможности разработчиков были бы сильно ограничены.

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

Ветвление – выполнение кода обусловлено результатами предшествующих вычислений , например: "выполнить этот код только в том случае, если значение переменной myVal меньше 10".

Использование циклов – повторяющееся выполнение одних и тех же операторов (определенное количество раз или до тех пор, пока не выполнится контрольное условие).

Оба способа предполагают использование булевой логики.

2.1.Булева логика

Тип bool, может принимать одно из двух значений: true (истина) или false (ложь). Этот тип очень часто применяется для записи результата выполнения какой-либо операции, с тем, чтобы мы могли выполнить какие-либо определяемые им действия. В частности, тип bool используется для хранения результатов сравнения.

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

Логическое сравнение требует применения логических операторов сравнения (также известных под именем операторов отношения), которые приведены в таблице 2.1. В этой таблице переменная var1 во всех случаях имеет тип bool, а типы переменных var2 и var3 меняются.

Таблица 2.1 - Логические операторы сравнения

Оператор

Тип

Пример выражения

Результат

= =

Бинарный

varl = var2 = = var3;

Переменной varl присваивается значение true в том случае, если переменная var2 равна переменной var3, и значение false в противном случае.

! =

Бинарный

varl = var2 != var3;

Переменной varl присваивается значение true в том случае, если переменная var2 не равна переменной var3, и значение false в противном случае.

<

Бинарный

varl = var2 < var3;

Переменной varl присваивается значение true в том случае, если переменная var2 меньше переменной var3, и значение false в противном случае.

>

Бинарный

varl = var2 > var3;

Переменной varl присваивается значение true в том случае, если переменная var2 больше переменной var3, и значение false в противном случае.

<=

Бинарный

varl = var2 <= var3;

Переменной varl присваивается значение true в том случае, если переменная var2 меньше или равна переменной var3, и значение false в противном случае.

>=

Бинарный

varl = var2 >= var3;

Переменной varl присваивается значение true в том случае, если переменная var2 больше или равна переменной var3, и значение false в противном случае.

В программе можно использовать эти операторы для численных значений следующим образом:

bool isLessThan10;

isLessThan10 = myVal <10

;

В результате выполнения этого кода переменной isLessThan10 будет присвоено значение true в том случае, если значение переменной myVal меньше 10, а в противном случае – значение false.

Операторы сравнения могут использоваться также и для переменных других типов, например, для строк:

bool isKarli;

isKarli = myString = = “Karli”

В данном случае переменной isKarli будет присвоено значение true только при условии, что строка, содержащаяся в переменной myString, имеет вид "Karli"

Аналогичным образом можно поступать и с логическими переменными:

bool isTrue;

isTrue = myBool= = true

;

Однако в этом случае допускается использование только операторов = = и !=.

Распространенной ошибкой является безосновательное предположение, что если val1 < val2 есть ложь, то val1 > val2 – истина. Если val1 = = val2, оба предыдущих выражения будут ложными.

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

(см. таб. 2.2).

Теперь можно записать последний пример следующим образом:

bool isTrue;

isTrue = myBool & true

;

Для операторов & и | существуют аналоги (табл. 2.3).

Таблица 2.2 – Операторы для работы с логическими значениями

Оператор

Тип

Пример выражения

Результат

!

Унарный

varl = ! var2;

Переменной varl присваивается значение true в том случае, если переменная var2 имеет значение false, и значение false, если значение var2–true (операция "логическое не" (NOT)).

&

Бинарный

varl = var2 & var3;

Переменной varl присваивается значение true в том случае, если обе переменные var2 и var3 имеют значение true, и значение false в противном случае (операция "логическое и" (AND)).

|

Бинарный

varl = var2 | var3;

Переменной varl присваивается значение true в том случае, если хотя бы одна из переменных – var2 или var3 (или обе) – имеет значение true, и значение false в противном случае (операция "логическое или" (OR)).

^

Бинарный

varl = var2 ^ var3;

Переменной varl присваивается значение true в том случае, если одна из переменных – var2 или var3, но не обе одновременно – имеет значение true, и значение false в противном случае (операция "исключающее или" (XOR)).

Таблица 2.3 - Аналоги логических операторов & и |

Оператор

Тип

Пример выражения

Результат

&&

Бинарный

varl = var2 && var3;

Переменной varl присваивается значение true в том случае, если обе переменные var2 и var3 имеют значение true, и значение false в противном случае (операция "логическое и" (AND)).

||

Бинарный

varl = var2 || var3;

Переменной varl присваивается значение true в том случае, если одна из переменных, var2 или var3 (или обе), имеет значение true, и значение false в противном случае (операция "логическое или" (OR)).

Результат выполнения этих операций в точности соответствует результату выполнения операций & и |, однако существует одно важное отличие; оно заключается в способе достижения результата, позволяющем в некоторых случаях ускорить операцию. Оба эти оператора рассматривают значение своего первого операнда (в приведенной выше таблице это var2) и в определенных случаях могут вообще обходиться без обработки второго операнда (в нашем примере var3).

Если значение первого операнда оператора && false, то нет необходимости рассматривать значение второго операнда, поскольку результатом все равно будет значение false. Аналогично оператор || вернет значение true, если его первый операнд имеет значение true, независимо от значения второго операнда. При выполнении операторов & и | всегда происходит вычисление обоих операндов.

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

2.2.Операторы работы с битами

По прочтении предыдущего раздела может возникнуть вопрос, для чего вообще существуют операторы & и |. Ответ заключается в том, что эти операторы могут использоваться для выполнения операций над численными значениями. Фактически они имеют дело с хранящейся в переменной последовательностью битов, а не со значением переменной как таковым.

Бит операндаБит операнда 1 1

Бит операндаБит операнда 2 2

Бит результатаБит результата выполнения операциивыполнения операции |

1

1

&1

11

01

11

01

10

10

00

01

00

0

0

0

ТаблицаТаблица 2.4 2.5 - -БитовБитовая операция ая операция & | Рассмотрим работу этих операторов, начав с оператора &. Он действует следующим образом: каждый бит первого операнда сравнивается с находящимся в такой же позиции битом второго операнда, а бит, записывающийся в аналогичной позиции результата, определяется следующим образом (см. табл. 2.4).

Аналогичные действия выполняются и в случае применения оператора |, только результат вычисляется несколько иначе (см. табл. 2.5).

В качестве примера рассмотрим операцию, выполняемую

следующим кодом:

int result, op1, ор2; op1 = 4; ор2 = 5; result = opl & op2;

Здесь необходимо принять во внимание двоичное представление операндов op1 и ор2, соответственно 100 и 101.

Результат этой операции получается путем сравнения двоичных

Бит операнда 1

Бит операнда 2

Бит результата выполнения операции

1

1

0

1

0

1

0

1

1

0

0

0

Таблица 2.6-Битовая операция & битов, находящихся в одинаковых позициях в каждом из этих логических представлений:

- первому слева биту результата присваивается значение 1, если оба крайних левых бита op1 и ор2 равны 1, и значение 0 в противном случае;

- следующему биту результата присваивается значение 1, если оба следующих бита op1 и ор2 равны 1, и значение

0 в противном случае;

Таблица 2.7–Логический оператор ~

Бит операнда

Бит результата выполнения операции

1

0

0

1

- так продолжается для всех остальных битов.

В данном примере крайние биты слева у обоих операндов op1 и ор2 равны 1, поэтому крайний левый бит результата также будет равен 1. В следующей позиции оба бита равны 0, а в третьей позиции биты равны

Биты

Десятичное представление

Значение

000

0

Черный

100

4

Красный

010

2

Зеленый

001

1

Синий

101

5

Пурпурный

по

6

Желтый

011

3

Голубой

111

7

Белый

Таблица 2.8–Трехбитовое представление цвета соответственно 1 и 0; таким образом, второй и третий биты результата будут равны 0. В качестве окончательного результата в двоичном представлении будет получено число 100, т. е. результат выполнения этой операции будет равен 4.

Аналогичный процесс происходит при использовании оператора |, с тем лишь отличием, что получающийся в результате бит равняется 1 в том случае, если хотя бы один из соответствующих битов операндов равен 1.

Точно таким же способом можно использовать и оператор ^. В этом случае в результате появляется 1, если только у одного из операндов, но не у обоих одновременно,

в соответствующей позиции находится единица.

В языке С# допускается использование унарного оператора работы с битами ~, воздействие которого на операнд состоит в инвертировании (изменении) всех его битов таким образом, что все биты, которые были равны 0, получают значение 1, и наоборот (см. табл. 2.7).

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

Предположим, что мы храним эти значения в переменной типа int. Начиная с черного цвета или, что то же самое, со значения переменной типа int, равного 0, можно выполнить различные операции. Например:

int myColor = 0; bool containsRed; myColor = myColor | 2;

// Добавлен бит зеленого цвета, значение переменной myColor стало 010 myColor = myColor | 4;

// Добавлен бит красного цвета, значение переменной myColor стало 110 containsRed = (myColor & 4) = = 4;

// Выполняется проверка на наличие бита, отвечающего за красный цвет

В результате выполнения последней строки кода значение переменной containsRed будет true, поскольку "красный" бит в переменной myColor равен 1.

Этот способ может оказаться весьма полезным в том случае, если выполняемые операции используются для одновременной проверки нескольких битов (например, для 32 в случае значений типа int). Однако существуют более эффективные способы хранения дополнительной информации в отдельных переменных, для чего используются переменные сложных типов.

Помимо описанных выше четырех операторов для работы с битами следует рассмотреть еще два оператора (табл.

2.9).

Таблица 2.9 – Операторы побитового сдвига

Оператор

Тип

Пример выражения

Результат

>>

Бинарный

varl = var2 >> var3;

Переменной varl присваивается значение, которое получается при сдвиге двоичного содержимого переменной var2 вправо на число битов, равное значению переменной var3.

<<

Бинарный

varl = var2 << var3;

Переменной varl присваивается значение, которое получается при сдвиге двоичного содержимого переменной var2 влево на число битов, равное значению переменной var3.

Работу этих операторов, обычно называемых операторами побитового сдвига, лучше всего проиллюстрировать с помощью небольшого примера:

int varl, var2 =10, var3 = 2;

var1 = var2 << var3;

В этом примере переменной varl будет присвоено значение 40. Действительно, двоичное представление числа 10 – "1010"; после сдвига на две позиции влево оно превратится в "101000" – двоичное представление числа 40. То, что мы сделали, можно изобразить как операцию умножения. Каждый бит при сдвиге на одну позицию влево увеличивает свое значение в два раза, соответственно сдвиг на две позиции эквивалентен умножению на 4. И наоборот, при сдвиге всех битов вправо возникает эффект деления операнда на два, при этом любой целый остаток теряется:

int varl, var2 =10; varl = var2 >> 1;

В данном случае varl будет содержать значение 5, а в результате выполнения следующего примера ее значение станет равным 2:

int varl, var2 = 10; varl = var2 >>2;

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

2.2.1.Логические операторы присваивания

Последние операторы - это комбинация рассмотренных выше операторов с оператором присваивания. Они во многом сходны с математическими операторами присваивания, рассматривавшимися в предыдущей лекции ( + = , *= и т.д.). Логические операторы приведены в таблице 2.10.

Таблица 2.10 – Логические операторы присваивания

Оператор

Тип

Пример выражения

Результат

&=

Бинарный

varl &= var2;

Переменной varl присваивается значение, являющееся результатом выполнения операции varl & var2.

|=

Бинарный

var1 |= var2;

Переменной varl присваивается значение, являющееся результатом выполнения операции varl I var2.

^=

Бинарный

varl ^= var2;

Переменной varl присваивается значение, являющееся результатом выполнения операции varl * var2.

Эти операторы используются как с логическими, так и с численными значениями, точно так же, как и операторы &, | и ^.

При выполнении операторов &= и |= применяются операторы & и |, а не && и ||, что приводит к издержкам, характерным для этих более простых операторов.

Для операторов побитового сдвига также существуют соответствующие операторы присваивания (табл. 2.11).

Таблица 2.11 – Операторы побитового присваивания

Оператор

Тип

Пример выражения

Результат

>>=

Унарный

varl >> var2;

Переменной varl присваивается значение, которое получается в результате сдвига двоичного содержимого переменной varl вправо на число битов, равное значению переменной var2.

<<=

Унарный

varl << var2;

Переменной varl присваивается значение, которое получается в результате сдвига двоичного содержимого переменной varl влево на число битов, равное значению переменной var2.

2.2.2.Старшинство операторов с дополнениями Старшинство операторов приведено в таблице 2.12.

Таблица 2.21 – Старшинство операторов

Старшинство

Операторы

Высшее

++, - - (используемые в качестве префиксов), (), + , - (унарные), !, ~ *, /, % +, -, <<, >> <, >, <=, >=, = =, !=, &, ^, |, &&, ||, =, *=, /=, %=, +=, - =, << =, >>=, &=, ^=, | =

Низшее

++,--(используемые в качестве суффиксов)

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

varl = var2 <= 4 && var2 >= 2;

в котором оператор && выполняется после операторов <= и >=.

Здесь необходимо отметить тот факт, что совершенно не возбраняется использовать скобки с целью сделать выражения более понятными. Компилятор в любом случае разберется, в каком порядке следует выполнять операторы, а вот людям свойственно забывать о таких вещах. Записав вышеприведенное выражение следующим образом:

varl = (var2 <= 4) && (var2 >= 2);

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

2.2.3.Оператор goto

С# позволяет помечать строки кода, а затем непосредственно переходить к их выполнению с помощью оператора goto. У этого оператора имеются свои преимущества и недостатки Основное преимущество заключается в том, что это очень простой способ управлять очередностью выполнения кодов. Однако чрезмерное его использование может привести к трудному для понимания "лоскутному" коду, что является его главным недостатком.

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

goto <имяМетки>;

Метки задаются таким образом:

<имяМетки>:

В качестве примера разберем следующий код:

int myInteger = 5;

goto myLabel;

mylnteger += 10;

myLabel:

Console.WriteLine(“myInteger = {0}", myInteger);

Его выполнение осуществляется следующим образом:

- переменная myInteger объявляется как имеющая тип int и ей присваивается значение 5;

- оператор goto прерывает нормальный ход выполнения кода и передает управление на строку с меткой myLabel:; - значение переменной myInteger выводится на консоль.

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

Если попытаться использовать этот код в приложении, то при попытке его откомпилировать появится предупреждение "Unreachable code detected" ("Обнаружен невыполняемый код") с номером соответствующей строки.

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

В качестве примера "лоскутного" кода, получающегося при использовании оператора goto, рассмотрим следующую программу start:

int myInteger = 5; goto addVal;

writeResult: Console.WriteLine(“myInteger = {0}”, myInteger); goto start;

addVal: myInteger +=10; goto writeResult;

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

Оператор goto предназначен для совместного использования с некоторыми другими конструкциями, рассматриваемыми в настоящей лекции.

2.3.Ветвление

Ветвление – это способ управлять тем, какая строка кода должна выполняться следующей. Строка, на которую следует передать управление, определяется с помощью некоторого условного оператора. Работа такого условного оператора основана на выполнении сравнения контрольного значения и одного или нескольких возможных значений с использованием булевой логики.

Существует три способа ветвления, имеющихся в С#:

- тринарный оператор; - оператор if; - оператор switch.

2.3.1.Тринарный оператор

Наиболее простой способ выполнения проверки – использование тринарного (или условного) оператора. Синтаксис тринарного оператора имеет следующий вид:

<проверка> ? <результатЕслиИстина> : <результатЕслиЛожь>

В данном случае условие <проверка> вычисляется для получения логического значения, и в зависимости от него результатом выполнения этого оператора будет <результатЕслиИстина> или <результатЕслиЛожь>. Можно использовать этот оператор, как показано на рис. 2.1.

string resultString = (myInteger < 10) ? “Меньше 10”: “Больше или равно 10”;

оператора

Рис. 2.1 – Пример использования тринарного

Результатом выполнения тринарного оператора будет одна из двух строк, каждая из которых может быть присвоена переменной resultString. Выбор того, какую именно из этих двух строк следует присвоить, производится путем сравнения значения переменной myInteger с 10, причем если значение переменной меньше 10, присваивается первая строка, а если больше или равно 10 – вторая. Например, в том случае, если значение переменной myInteger равняется 4, переменной resultString будет присвоена строка “Меньше 10”.

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

2.3.2.Оператор if

Оператор if является гораздо более гибким способом принятия решений. В отличие от операторов ?:, операторы if не имеют результата (поэтому их нельзя использовать в операторах присваивания), зато появляется возможность применять их для выполнения каких-либо других операторов при соблюдении определенного условия. Простейшей формой оператора if является следующая:

if (<условие>)

<код, который выполняется, если вычисление <условия> дает результат true>;

Когда происходит вычисление <условие> (а в результате этих вычислений должно получаться логическое значение, иначе код не пройдет компиляцию), строка кода, расположенная под оператором, будет выполнятся только в случае значения true. После того как эта строка кода будет выполнена, а также если эта строка не будет выполняться (из-за того, что получено значение false), работа продолжится со следующей за условным оператором строки кода.

Можно задать дополнительный код, воспользовавшись оператором else в сочетании с оператором if. Этот оператор будет выполняться только в том случае, если в результате вычисления <условия> будет получено значение false.

if (<условие>)

<код, который выполняется, если вычисление <условия> дает результат true>; else

<код, который выполняется, если вычисление <условия> дает результат false>;

Каждый раздел кода может занимать несколько строк, для чего используются фигурные скобки:

if (<условие>)

{

<код, который выполняется, если вычисление <условия> дает результат true>;

}

else

{

<код, который выполняется, если вычисление <условия> дает результат false>;

}

В качестве простого примера перепишем код (рис.2), в котором использовался тринарный оператор, с помощью оператора if:

string resultString = (myInteger < 10) ? “Меньше 10” : “Больше или равно 10”;.

Поскольку результат выполнения оператора if нельзя присваивать переменным, то потребуется отдельный шаг string resultString; if (myInteger < 10) resultString = “Меньше 10”;

else resultString = “Больше или равно 10”;

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

Давайте обратимся к примеру.

1. Создайте новое консольное приложение.

2. Добавьте следующий код в Class1.cs:

static void Main(string[] args) { string comparison;

Console.WriteLine("Enter a number:");

double varl = Convert.ToDouble(Console.ReadLine()) ; Console.WriteLine(“Enter another number:”) ; double var2 = Convert.ToDouble(Console.ReadLine()); if (varl < var2)

comparison = "less than";

else { if (varl == var2) comparison = "equal to";

else comparison = "greater than";

}

Console.WriteLine("The first number is {0} the second number.”, comparison); }

3. Запустите код и введите два числа в ответ на приглашения.

Как это работает

Первая часть кода вам уже знакома здесь происходит получение двух значений типа double из строк, введенных пользователем.

string comparison;

Console.WriteLine("Enter a number:");

double varl = Convert.ToDouble (Console. ReadLine())

Console.WriteLine("Enter another number:");

double var2 = Convert.ToDouble(Console.ReadLine());

;

Далее переменной comparison типа string, в зависимости от значений, полученных для varl и var2, присваивается строка:

if (varl < var2) comparison = "less than";

Если данное условие не выполняется, то это значит, что varl либо больше, либо равно var2. Тогда в разделе else первого сравнения делается еще одно, вложенное сравнение:

else { if (varl == var2) comparison = "equal to";

Раздел else второго сравнения будет достигнут только в том случае, если var1 больше, чем var2:

else comparison = "greater than";

В заключение происходит вывод значения переменной comparison на консоль:

Console.WriteLine("The first number is {0} the second number.", comparison);

Использование вложенных операторов представляет собой лишь один из возможных путей решения этой задачи. С тем же успехом можно было бы написать следующее:

if (varl < var2) comparison = "less than";

if (varl = = var2) comparison = "equal to";

if (varl > var2) comparison = "greater than";

Недостатком такого способа является необходимость выполнять все три сравнения независимо от значений переменных varl и var2. В первом же случае выполняется только одно сравнение, если varl < var2 есть true, и два сравнения в противном случае (мы также выполняем сравнение varl = = var2), т. е. выполняется меньшее число строк кода. Разница в быстродействии несущественна, однако она может иметь значение для тех приложений, для которых скорость выполнения является критичной.

2.3.3.Проверка большего количества условий с помощью оператора if

В предыдущем примере проверялись три условия для значения переменной var1, которые охватывали все возможные для нее значения. Однако в некоторых случаях может потребоваться выполнять проверку на какие-то конкретные значения, например, 1,2, 3, 4 и т. д. Используя тот же подход, получим неудобоваримый вложенный код вроде:

if (varl = = 1) {

// выполнение каких-либо действий } else { if (varl = = 2) {

// выполнение каких-либо других действий > else {

if (varl = =31 || varl = = 4) {

// выполнение каких-либо других действий > else {

// выполнение каких-либо других действий }

}

}

Существует очень распространенная ошибка, когда условие в третьем сравнении записывается как if (var1 = = 3 || 4). В таком случае в полном соответствии со старшинством операторов сначала будет выполнен оператор = =, в результате чего оператор || будет выполняться с операндами логического и численного типов. Это приведет к ошибке.

В такой ситуации разумнее применить несколько иную систему отступов и объединения элементов кода для блоков else (т. е. использовать после блока else одну строку кода, а не целый блок кода). Если поступить таким образом, то получим следующую структуру, содержащую операторы else if:

if (varl = = 1) {

// выполнение каких-либо действий }

else if (varl = = 2) {

// выполнение каких-либо других действий }

else if (varl = = 3 || varl = = 4) {

// выполнение каких-либо других действий } else {

// выполнение каких-либо других действий }

На самом деле операторы else if представляют собой два отдельных оператора, и вышеприведенный код идентичен первоначальному варианту. Однако такой код является более удобочитаемым.

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

2.3.4.Оператор switch

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

Принципиальная структура оператора switch имеет следующий вид. switch (<контрольнаяПеременная>)

{ case <значение1>:

<код, который должен выполняться в том случае, если <контрольнаяПеременная> = = <значение1> > break;

case <значение2>:

<код, который должен выполняться в том случае, если <контрольнаяПеременная> = = <значение2> > break;

case <значениеN>:

<код, который должен выполняться в том случае, если <контрольнаяПеременная> = = <значениеN> > break;

default:

<код, который должен выполняться в том случае, если <контрольнаяПеременная> != ни одному из значений>> break; }

Значение, которым обладает <контрольнаяПеременная>, сравнивается со всеми значениями <значениеХ>, каждое из которых имеет свой собственный оператор case; в случае совпадения значений выполняется код, предусмотренный для данного условия. Если ни одно из значений не совпало, будет выполняться код из раздела default (при условии, что этот блок не пуст). После завершения выполнения кода в некотором разделе выполняется еще одна команда - break. В случае выполнения кода в одном из блоков case переход к выполнению другого оператора case не происходит.

Такая логика выполнения программы является одним из отличий языка С# от языка C++, в котором при обработке операторов case разрешается переходить от выполнения одного оператора к выполнению другого.

Оператор break в данном случае просто прерывает выполнение оператора switch, и работа программы продолжается со следующего оператора

В программе на С# имеются и другие способы избежать передачи управления от одного оператора case другому. Мы можем использовать либо оператор return, который приведет к прерыванию выполнения текущей функции, а не просто оператора switch, либо оператор goto. Оператор goto (он описан выше) в данном случае будет работать, поскольку операторы case в программе на С# фактически выполняют роль меток.

Имеется единственное исключение из общего правила, гласящего, что после выполнения одного оператора case не может выполняться другой оператор case. Если разместить несколько операторов case подряд (объединим их) перед каким-либо блоком кода, фактически организуем одновременное выполнение проверки нескольких условий. Если хотя бы одно из этих условий окажется выполненным, то управление будет передано на этот блок кода.

Эти условия также относятся и к оператору default. Не существует никакого правила, требующего, чтобы этот оператор обязательно находился в конце всего перечня сравнений, и при желании мы можем объединять его с операторами case. Включение оператора, прерывающего нормальное выполнение программы - break, goto или return,гарантирует правильную последовательность выполняемых действий в любом случае.

Все переменные <значениех>, использующиеся для сравнений, должны иметь постоянное значение. Один из способом добиться этого - применение литеральных значений:

switch (myInteger) { case 1:

<код, который должен выполняться в том случае, если myInteger = = 1 > break; case -1:

<код, который должен выполняться в том случае, если myInteger = = -1 > break; default:

<код, который должен выполняться в том случае, если myInteger != ни одному из значений, использовавшихся для сравнения> break; }

Другой способ получить постоянные значения переменных – это использование переменных-констант. Переменные-константы являются точно такими же переменными, как и все остальные, за исключением одного ключевого момента: значение, которое в них содержится, не может быть изменено ни при каких условиях. После того как присвоить переменной-константе какое-либо значение, оно будет оставаться неизменным на протяжении выполнения всей программы. Переменные-константы могут оказаться удобными в данном случае, поскольку зачастую код легче понимать, когда используемые для сравнения значения не видны в момент выполнения сравнения.

Можно объявить переменную-константу, добавив ключевое слово const к описанию типа переменной; при этом требуется сразу определить ее значение. Например:

const int intTwo =2; '

Этот код является вполне допустимым, однако если попытаться выполнить следующий код:

const int intTwo;

intTwo =2;

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

2.4.Организация циклов

Цикл – это участок кода, в котором операторы выполняются по несколько раз. Такой способ в ряде случаев бывает чрезвычайно удобным, поскольку позволяет повторять необходимые действия столько раз, сколько требуется (тысячи и даже миллионы), без необходимости выписывать соответствующий код такое же количество раз.

В качестве простого примера рассмотрим программу, вычисляющую количество денег, которое окажется на счете в банке по истечении десятилетнего срока, исходя из предположения, что проценты начисляются ежегодно и никакие суммы не снимаются и не добавляются на счет:

double balance = 1000;

double interestRate =1.05; //5% годовых

balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate; balance *= interestRate;

Выписывать один и тот же код 10 раз весьма расточительно, и потом, как быть, если потребуется изменить срок с 10 лет на другое значение? Придется вручную копировать строку с кодом необходимое число раз, что добавит немало головной боли!

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

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

2.4.1.Цикл do

Цикл do работает следующим образом. Код, который помечен как выполняющийся в цикле, выполняется первый раз, затем проверяется некоторое логическое условие, и если результат равен true, то этот код выполняется еще раз, и т. д.

Цикл заканчивается, когда условие перестает выполняться.

Цикл do имеет следующую структуру: do {

<код, выполняющийся в цикле> } while (<условие>);

где в результате вычисления <условия> получается логическое значение.

Точка с запятой после оператора while является обязательной; весьма распространена ошибка, когда ее забывают. Мы можем использовать этот цикл, например, для того, чтобы вывести в столбец числа от 1 до 10:

int i = 1;

o

d

{

Console.WriteLine(“{0}”, i++); } while (i <= 10);

В данном случае мы использовали оператор ++ в виде суффикса, который увеличивает значение i на единицу после его вывода на экран; поэтому необходимо выполнять проверку i <= 10, чтобы число 10 попало в набор выводимых чисел.

2.4.2.Цикл while

Цикл while очень похож на цикл do, однако у него имеется одно важное отличие: вычисление логического условия в цикле while происходит в начале цикла, а не в конце. Если в результате проверки условия будет получено значение false, то цикл while вообще не будет выполняться и управление передается коду, расположенному после этого цикла. Цикл while оформляется следующим образом:

while (<условие>) {

<код, выполняющийся в цикле> }

Он может использоваться практически так же, как и цикл do. Например:

int i = 1; while (i <= 10) {

Console.WriteLine(“{0}”, i++); }

Выполнение этого кода приведет к тому же результату: числа от 1 до 10 будут выведены в столбец.

2.4.3.Цикл for

Последний тип цикла - это цикл for. Он относится к тому типу циклов, которые выполняются заранее заданное количество раз и сами отвечают за организацию счетчика цикла. Для организации цикла for требуется следующая информация:

- начальное значение для инициализации переменной цикла;

- условие для продолжения выполнения цикла, зависящее от переменной цикла;

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

Например, если необходимо организовать цикл с переменной цикла, изменяющейся от 1 до 10 с шагом, равным единице, то в этом случае начальное значение равно 1, условием для продолжения цикла будет "переменная цикла меньше или равна 10", а операцией, выполняющейся по окончание каждого прохода цикла, будет прибавление значению переменной цикла единицы. Эта информация должна быть размещена в структуре цикла for следующим образом:

for (<инициализация>; <условие>; <операция>) <код, выполняющийся в цикле>

Этот код работает так же, как и следующий цикл while

<инициализация>; while (<условие>)

<код, выполняющийся в цикле> <операция>

Однако формат цикла for оказывается более понятным, поскольку все необходимые параметры, определяющие цикл, собраны воедино, а не распределены по разным операторам

При рассмотрении циклов do и while приводился пример с выводом чисел от 1 до 10. Приведем код, позволяющий сделать то же самое с помощью цикла for

int i;

for (i = 1; i <= 10; ++ i)

Console. WriteLine (“{0}”, 1)

;

Целой переменной цикла с именем i присваивается начальное значение 1, после чего она увеличивается на 1 в конце каждого прохода. В теле цикла происходит вывод значения i на консоль.

Обратите внимание, что когда начинает выполняться код, расположенный за циклом, переменная i обладает значением, равным 11. Дело в том, что в конце прохода цикла, в котором переменная i равняется 10, ее значение увеличивается на единицу до проверки условия i <= 10. Очевидно, что после проверки выполнение цикла прекращается.

Как и циклы while, циклы for выполняются только в том случае, если проверка условия дает true перед первым проходом цикла, т. е. код, находящийся внутри цикла, может вообще ни разу не выполняться

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

for (int i = 1; i <= 10; ++i)

Console.WriteLine("{0}", i) ; }

В этом случае переменная i будет недоступна коду, находящемуся вне цикла.

2.4.4.Прекращение выполнения цикла

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

- break – приводит к немедленному завершению цикла.

- continue – приводит к немедленному завершению текущего прохода цикла (вычисления продолжаются со следующего прохода).

- goto – позволяет выйти из цикла в помеченную точку кода (этот метод не рекомендуется использовать, если вы хотите получить легко читаемый и понимаемый код).

- return – выводит из цикла и из функции, в которой он находится.

Команда break позволяет просто выйти из цикла, при этом управление передается на первую строку кода после цикла. Например:

int i = 1; while (i <= 10) { if (i = = 6) break;

Console.WriteLine(“{0}”, i++) ; }

Этот код выведет числа от 1 до 5, поскольку когда значение переменной i достигнет 6, команда break приведет к выходу из цикла.

Команда continue прекращает выполнение текущего прохода цикла, а не всего цикла. Например: int i;

for (i = 1; i <= 10; i++)

{

if ((i % 2) = = 0) continue;

Console.WriteLine(i);}

Третий способ предполагает использование оператора goto. Например: int i = 1;

while (i <= 10)

{

if (i = = 6) goto exitPoint;

Console.WriteLine(“{0}”, i++) ; }

Console.WriteLine("Этот код никогда не будет выполняться!”); exitPoint:

Console.WriteLine('Этот код будет выполнен после того, как произойдет выход из цикла посредством goto.");

Выход из цикла посредством команды goto допустим (разве что несколько неряшлив), а вот использование goto для входа в цикл извне запрещено.

2.4.5.Бесконечные циклы

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

while (true) {

// код, выполняющийся в цикле }

Иногда такой цикл бывает полезен, тем более что из него всегда можно выйти, например, с помощью оператора break.

Однако в тех случаях, когда создание бесконечного цикла происходит по недосмотру, последствия могут оказаться очень неприятными. Рассмотрим следующий цикл, который очень напоминает цикл for из предыдущего раздела:

int i = 1;

while (i <= 10)

{

if ((i % 2) = = 0)

continue;

Console.WriteLine(“{0}”, i++); }

Здесь увеличение переменной цикла i происходит в последней строке цикла, т. е. после оператора continue. Поэтому если управление передается на оператор continue (а это произойдет, когда переменная i станет равной 2), следующая итерация будет исполняться с тем же значением переменной i, опять пройдет проверка текущего значения 1, начнет выполняться итерация и т. д. Это приведет к зависанию приложения. Обратите внимание, что в этом случае существует возможность выйти из зависшего приложения обычным способом, поэтому не придется перезагружать компьютер. 2.5. Вопросы для повторения

1. Что такое ветвление?

2. Что такое булева логика?

3. Логические операторы сравнения.

4. Опереторы, используемые для работы с логическими переменными.

5. Операторы работы с битами.

6. Старшинство операторов.

7. Оператор if-else.

8. Оператор switch.

9. Организация циклов.

10. Цикл while, while-do, do-while.

11. Цикл for.

3. ДОПОЛНИТЕЛЬНЫЕ СВЕДЕНИЯ О ПЕРЕМЕННЫХ

В языке C# существуют еще несколько типов переменных:

-перечислимый тип – тип переменных, которые принимают дискретные значения из множества, определяемого пользователем, и которые могут нести дополнительную смысловую нагрузку.

-структуры – сложные типы переменных, которые создаются