Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Основные элементы языка.](./t3l1.md) | [Содержание](../readme.md#тема-4-программирование-на-языке-c-основы) | [Массивы как структурированный тип данных.](./t3l1_3.md) # Основы языка `C#` **Содержание:** * [Операторы отношения и логические операторы](#Операторы_отношения_и_логические_операторы) * [Условные операторы](#Условные_операторы) * [Циклы](#Циклы) * [Операторы перехода](#Операторы_перехода) ## Операторы отношения и логические операторы В обозначениях **оператор отношения** и **логический оператор** термин **отношения** означает взаимосвязь, которая может существовать между двумя значениями, а термин **логический** — взаимосвязь между логическими значениями "истина" и "ложь". И поскольку операторы отношения дают истинные или ложные результаты, то они нередко применяются вместе с логическими операторами. Именно по этой причине они и рассматриваются совместно. Ниже перечислены операторы отношения: Оператор | Значение :-------:|-------- `==` | Равно `!=` | Не равно `>` | Больше `<` | Меньше `>=` | Больше или равно `<=` | Меньше или равно К числу логических относятся операторы, приведенные ниже: Оператор | Значение :--------:|------- `&` | И `\|` | ИЛИ `^` | Исключающее ИЛИ `&&` | Укороченное И `\|\|` | Укороченное ИЛИ `!` | НЕ Результатом выполнения оператора отношения или логического оператора является логическое значение типа **bool**. В целом, объекты можно сравнивать на равенство или неравенство, используя операторы отношения `==` и `!=`. А операторы сравнения `<`, `>`, `<=` или `>=` могут применяться только к тем типам данных, которые поддерживают отношение порядка. Следовательно, операторы отношения можно применять ко всем числовым типам данных. Но значения типа **bool** могут сравниваться только на равенство или неравенство, поскольку истинные (`true`) и ложные (`false`) значения не упорядочиваются. Например, сравнение `true > false` в `C#` не имеет смысла. Рассмотрим пример программы, демонстрирующий применение операторов отношения и логических операторов: ```cs short d = 10, f = 12; bool var1 = true, var2 = false; if (d < f) Console.WriteLine("d < f"); if (d <= f) Console.WriteLine("d <= f"); if (d != f) Console.WriteLine("d != f"); // Следующее условие не выполнится if (d > f) Console.WriteLine("d > f"); // Сравниванием переменные var1 и var2 if (var1 & var2) Console.WriteLine("Данный текст не выведется"); if (!(var1 & var2)) Console.WriteLine("!(var1 & var2) = true"); if (var1 | var2) Console.WriteLine("var1 | var2 = true"); if (var1 ^ var2) Console.WriteLine("var1 ^ var2 = true"); Console.ReadLine(); ``` ![](../img/03007.png) ### Укороченные логические операторы В **C#** предусмотрены также специальные, *укороченные*, варианты логических операторов `И` и `ИЛИ`, предназначенные для получения более эффективного кода. Поясним это на следующих примерах логических операций. Если первый операнд логической операции `И` имеет ложное значение (`false`), то ее результат будет иметь ложное значение независимо от значения второго операнда. Если же первый операнд логической операции `ИЛИ` имеет истинное значение (`true`), то ее результат будет иметь истинное значение независимо от значения второго операнда. Благодаря тому что значение второго операнда в этих операциях вычислять не нужно, *экономится время и повышается эффективность кода*. Укороченная логическая операция `И` выполняется с помощью **оператора &&**, а укороченная логическая операция `ИЛИ` — с помощью **оператора ||**. Этим укороченным логическим операторам соответствуют обычные логические операторы `&` и `|`. Единственное отличие укороченного логического оператора от обычного заключается в том, что второй его операнд вычисляется только по мере необходимости. Укороченные логические операторы иногда оказываются более эффективными, чем их обычные аналоги. Так зачем же нужны обычные логические операторы `И` и `ИЛИ`? Дело в том, что в некоторых случаях требуется вычислять оба операнда логической операции `И` либо `ИЛИ` из-за возникающих побочных эффектов. Пример: В данном случае используется укороченный оператор и операции сравнения выполнится нормально ```cs if (f != 0 && (d % f) == 0) Console.WriteLine("{0} делится нацело на {1}",d,f); ``` В этом случае так же используется укороченный оператор но при этом возникнет исключительная ситуация т.к. первый оператор сравнения содержит деление на `0` ```cs if ((d % f) == 0 && f != 0) Console.WriteLine("{0} делится нацело на {1}", d, f); ``` При использовании целостного оператора `&` в любом случае возникнет исключительная ситуация, т.к. второе выражение будет вычислено несмотря на то, что первое выражение вернуло `false` ```cs if (f != 0 & (d % f) == 0) Console.WriteLine("{0} делится нацело на {1}", d, f); ``` Другой пример, когда в выражении изменяется переменная: При использовании обычного оператора `|`, в данной конструкции переменная `i` будет инкреминироваться ```cs if (b | (++i < 10)) Console.WriteLine("i равно {0}", i); // i = 1 ``` При использовании укороченного оператора `||` значение `i` останется прежним ```cs if (b || (++i < 10)) Console.WriteLine("i равно {0}", i); // i = 0 ``` Стоит отметить, что при возникновении исключительной ситуации во время отладки кода, IDE выводит сообщение следующего характера: ![](../img/03008.png) ## Условные операторы Условные операторы позволяют управлять *потоком выполнения* программы, чтобы не выполнялась каждая строка кода, как она следует в программе. Давайте рассмотрим все условные операторы языка `C#`: ### Оператор if Для организации условного ветвления язык `C#` унаследовал от `С` и `С++` конструкцию `if...else`. Ее синтаксис должен быть интуитивно понятен для любого, кто программировал на процедурных языках: ```cs if (условие) оператор (операторы) else оператор (операторы) ``` Если по каждому из условий нужно выполнить более одного оператора, эти операторы должны быть объединены в блок с помощью фигурных скобок `{...}`. (Это также касается других конструкций `C#`, в которых операторы могут быть объединены в блок — таких как циклы `for` и `while`.) Стоит обратить внимание, что в отличие от языков `С` и `С++`, в `C#` условный оператор `if` может работать только с булевскими выражениями, но не с произвольными значениями вроде `-1` и `0`. В операторе `if` могут применяться сложные выражения, и он может содержать операторы `else`, обеспечивая выполнение более сложных проверок. Синтаксис похож на применяемый в аналогичных ситуациях в языках `С` (`С++`) и `Java`. При построении сложных выражений в `C#` используется вполне ожидаемый набор логических операторов. Давайте рассмотрим следующий пример: ```cs string myStr; Console.WriteLine("Введите строку: "); myStr = Console.ReadLine(); if (myStr.Length < 5) Console.WriteLine( "\nВ данной строке меньше 5 символов"); else if ((myStr.Length >= 5) && (myStr.Length <= 12)) Console.WriteLine( "\nВ данной строке {0} символов",myStr.Length); else Console.WriteLine( "\nВ данной строке больше 12 символов"); Console.ReadLine(); ``` ![](../img/03013.png) Как видите количество `else if`, добавляемых к единственному `if`, не ограничено. Один момент, который следует отметить касательно `if`: фигурные скобки применять не обязательно, если в условной ветви присутствует только один оператор, как показано в исходном примере. ### Оператор switch Вторым оператором выбора в `C#` является оператор **switch**, который обеспечивает многонаправленное ветвление программы. Следовательно, этот оператор позволяет сделать выбор среди нескольких альтернативных вариантов дальнейшего выполнения программы. Несмотря на то что многонаправленная проверка может быть организована с помощью последовательного ряда вложенных операторов `if`, во многих случаях более эффективным оказывается применение оператора **switch**. Этот оператор действует следующим образом. Значение выражения последовательно сравнивается с константами выбора из заданного списка. Как только будет обнаружено совпадение с одним из условий выбора, выполняется последовательность операторов, связанных с этим условием. Ниже приведена общая форма оператора **switch**: ```cs switch(выражение) { case константа1: последовательность операторов break; case константа2: последовательность операторов break; case константаЗ: последовательность операторов break; ... default: последовательность операторов break; } ``` Хотя оператор `switch...case` должен быть знаком программистам на `С` и `С++`, в `C#` он немного безопаснее, чем его эквивалент `С++`. В частности, он запрещает "сквозные" условия почти во всех случаях. Это значит, что если часть **case** вызывается в начале блока, то фрагменты кода за последующими частями **case** не могут быть выполнены, если только не используется явно оператор **goto** для перехода к ним. Компилятор обеспечивает это ограничение за счет того, что требует, чтобы за каждой частью **case** следовал оператор **break**, в противном случае он выдает ошибку. Важно отметить, что заданное выражение в операторе **switch** должно быть **целочисленного типа** (**char**, **byte**, **short** или **int**), **перечислимого** или же **строкового**. А выражения других типов, например с плавающей точкой, в операторе **switch** не допускаются. Зачастую выражение, управляющее оператором **switch**, просто сводится к одной переменной. Кроме того, константы выбора должны иметь тип, совместимый с типом выражения. В одном операторе **switch** не допускается наличие двух одинаковых по значению констант выбора. Давайте на примере рассмотрим использование оператора **switch**: ```cs Console.WriteLine("Введите язык (C#, VB или C++)"); string myLanguage = Console.ReadLine(); sw1(myLanguage); Console.ReadLine(); // Данный метод выводит выбор пользователя static void sw1(string s) { switch (s) { case "C#": Console.WriteLine("Вы выбрали язык C#"); break; case "VB": Console.WriteLine("Вы выбрали язык Visual Basic"); break; case "C++": Console.WriteLine("Вы выбрали язык С++"); break; default: Console.WriteLine("Такой язык я не знаю"); break; } } ``` ![](../img/03014.png) ### Тернарный оператор Тернарный оператор (`?`) относится к числу самых примечательных в `C#`. Он представляет собой условный оператор и часто используется вместо определенных видов конструкций `if-else`. Ниже приведена общая форма этого оператора (в квадратных скобках не обязательная часть): ``` [var Result =] Условие ? Выражение_если_условие_истинно : Выражение_если_условие_ложно; ``` Здесь **Условие** должно относиться к типу **bool** а *Выражение_если_условие_истинно* и *Выражение_если_условие_ложно* — к одному и тому же типу. Обратите внимание на применение двоеточия и его местоположение в операторе `?`. Значение выражения `?` определяется следующим образом. Сначала вычисляется *Условие*. Если оно истинно, то вычисляется *Выражение_если_условие_истинно*, а полученный результат определяет значение всего выражения `?` в целом. Если же *Условие* оказывается ложным, то вычисляется *Выражение_если_условие_ложно*, и его значение становится общим для всего выражения `?`: ```cs int b, c; c = -4; b = (c >= 0) ? c : c*c; ``` Присваивать переменной результат выполнения оператора `?` совсем не обязательно. Например, значение, которое дает оператор `?`, можно использовать в качестве аргумента при вызове метода. А если все выражения в операторе `?` относятся к типу **bool**, то такой оператор может заменить собой условное выражение в цикле или операторе `if`. Давайте рассмотрим пример использования тернарного оператора: ```cs int result; // Реализуем функцию модуля числа for (int i = 5; i >= -5; i--) { result = i >= 0 ? i : -i; Console.Write("{0}\t", result); } Console.WriteLine("\n\n"); // Выбор четных чисел for (int i = 0; i < 10; i++) { if (i % 2 == 0 ? true : false) Console.Write("{0}\t",i); } Console.ReadLine(); ``` ![](../img/03012.png) ## Циклы В **C#** имеются четыре различных вида циклов (`for`, `while`, `do...while` и `foreach`), позволяющие выполнять блок кода повторно до тех пор, пока удовлетворяется определенное условие. В этой лекции мы познакомимся с циклами **for** и **while**. ### Цикл for Цикл for в `C#` предоставляет механизм итерации, в котором определенное условие проверяется перед выполнением каждой итерации. Синтаксис этого оператора показан ниже: ```cs for (инициализатор; условие; итератор) оператор (операторы) ``` Здесь: **инициализатор** это выражение, вычисляемое перед первым выполнением тела цикла (обычно инициализация локальной переменной в качестве счетчика цикла). Инициализация, как правило, представлена оператором присваивания, задающим первоначальное значение переменной, которая выполняет роль счетчика и управляет циклом; **условие** это выражение, проверяемое перед каждой новой итерацией цикла (должно возвращать true, чтобы была выполнена следующая итерация); **итератор** - выражение, вычисляемое после каждой итерации (обычно приращение значения счетчика цикла). Обратите внимание на то, что эти три основные части оператора цикла **for** должны быть разделены точкой с запятой. Выполнение цикла **for** будет продолжаться до тех пор, пока проверка условия дает истинный результат. Как только эта проверка даст ложный результат, цикл завершится, а выполнение программы будет продолжено с оператора, следующего после цикла **for**. Стоит отметить, что цикл **for** отлично подходит для повторного выполнения оператора или блока операторов заранее известное количество раз. Давайте рассмотрим практическое применение цикла **for** на следующем примере: ```cs // Данный метод выводит таблицу умножения // размерностью b x b static void tab(byte b) { Console.WriteLine("Таблица умножения {0} x {0}\n", b); // Этот цикл проходит по строкам for (int i = 1; i <= b; i++) { // Этот цикл проходит по столбцам for (int j = 1; j <= b; j++) Console.Write("{0}\t", j * i); Console.WriteLine(); } Console.WriteLine(); } tab(8); // Давайте разберем нестандартные возможности цикла for // ************************************************* // // Применение нескольких переменных управления циклом for (byte i = 0, j = 20; i <= j; i += 5, j -= 5) Console.WriteLine("i = {0}, j = {1}",i,j); Console.WriteLine(); // Использование условного выражения в цикле bool b = false; for (byte i = 1, j = 100; !b; i++, j--) if (i < Math.Sqrt(j)) Console.WriteLine("Число {0} меньше квадратного корня из {1}", i, j); else b = true; // Отсутствие части цикла int k = 0; for (; k < 10; ) { k++; Console.Write(k); } Console.WriteLine("\n"); // Цикл без тела int sum = 0; for (int i = 1; i <= 10; sum += ++i); Console.WriteLine("Значение суммы: {0}",sum); Console.ReadLine(); ``` ![](../img/03015.png) ### Цикл while Подобно **for**, **while** также является циклом с предварительной проверкой. Синтаксис его аналогичен, но циклы **while** включают только одно выражение: ```cs while(условие) оператор (операторы); ``` где **оператор** — это единственный оператор или же блок операторов, а **условие** означает конкретное условие управления циклом и может быть любым логическим выражением. В этом цикле оператор выполняется до тех пор, пока условие истинно. Как только условие становится ложным, управление программой передается строке кода, следующей непосредственно после цикла. Как и в цикле **for**, в цикле **while** проверяется условное выражение, указываемое в самом начале цикла. Это означает, что код в теле цикла может вообще не выполняться, а также избавляет от необходимости выполнять отдельную проверку перед самим циклом. Пример: ```cs // Пример возведения числа в несколько степеней byte l = 2, i = 0; int result = 1; while (i < 10) { i++; result *= l; Console.WriteLine("{0} в степени {1} равно {2}",l,i,result); } Console.ReadLine(); ``` ![](../img/03016.png) ### Цикл `do ... while` Цикл `do...while` в **C#** — это версия **while** с постпроверкой условия. Это значит, что условие цикла проверяется после выполнения тела цикла. Следовательно, циклы `do...while` удобны в тех ситуациях, когда блок операторов должен быть выполнен как минимум однажды. Ниже приведена общая форма оператора цикла `do...while`: ```cs do { операторы; } while (условие); ``` При наличии лишь одного **оператора** фигурные скобки в данной форме записи необязательны. Тем не менее они зачастую используются для того, чтобы сделать конструкцию `do...while` более удобочитаемой и не путать ее с конструкцией цикла `while`. Цикл `do...while` выполняется до тех пор, пока условное выражение истинно. В качестве примера использования цикла `do...while` можно привести следующую программу, расчитывающую факториал числа: ```cs try { // Вычисляем факториал числа int i, result = 1, num = 1; Console.WriteLine("Введите число:"); i = int.Parse(Console.ReadLine()); Console.Write("\n\nФакториал {0} = ", i); do { result *= num; num++; } while (num <= i); Console.Write(result); } catch (FormatException ex) { Console.WriteLine("Вы ввели не число. {0}",ex.Message); } finally { Console.ReadLine(); } ``` ### Цикл foreach Цикл **foreach** служит для циклического обращения к элементам **коллекции**, представляющей собой группу объектов. В **C#** определено несколько видов коллекций, каждая из которых является **массивом**. Ниже приведена общая форма оператора цикла foreach: ```cs foreach (тип имя_переменной_цикла in коллекция) оператор; ``` Здесь **тип имя_переменной_цикла** обозначает **тип** и **имя переменной** управления циклом, которая получает значение следующего элемента коллекции на каждом шаге выполнения цикла **foreach**. А **коллекция** обозначает циклически опрашиваемую коллекцию, которая здесь и далее представляет собой массив. Следовательно, тип переменной цикла должен соответствовать типу элемента массива. Кроме того, тип может обозначаться ключевым словом **var**. В этом случае компилятор определяет тип переменной цикла, исходя из типа элемента массива. Это может оказаться полезным для работы с определенного рода запросами. Но, как правило, тип указывается явным образом. Оператор цикла **foreach** действует следующим образом. Когда цикл начинается, первый элемент массива выбирается и присваивается переменной цикла. На каждом последующем шаге итерации выбирается следующий элемент массива, который сохраняется в переменной цикла. Цикл завершается, когда все элементы массива окажутся выбранными. Цикл **foreach** позволяет проходить по каждому элементу коллекции (объект, представляющий список других объектов). Формально для того, чтобы нечто можно было рассматривать как коллекцию, это нечто должно поддерживать интерфейс **IEnumerable**. Примерами коллекций могут служить **массивы**, классы коллекций из пространства имен **System.Collection**, а также пользовательские классы коллекций. Пример использования цикла **foreach**: ```cs // Объявляем два массива int[] myArr = new int[5]; int[,] myTwoArr = new int[5, 6]; int sum = 0; Random ran = new Random(); // Инициализируем массивы for (int i = 1; i <= 5; i++) { myArr[i-1] = ran.Next(1, 20); for (int j = 1; j <= 6; j++) myTwoArr[i - 1, j - 1] = ran.Next(1, 30); } // Вычисляем квадрат каждого элемента одномерного массива foreach (int fVar in myArr) Console.WriteLine("{0} в квадрате равно {1}",fVar,fVar*fVar); Console.WriteLine(); // Вычислим сумму элементов многомерного массива foreach (int fTwoVar in myTwoArr) sum += fTwoVar; Console.WriteLine("Сумма элементов многомерного массива: {0}",sum); Console.ReadLine(); ``` ## Операторы перехода Язык **C#** предлагает несколько операторов, позволяющих немедленно перейти на другую строку программы. Давайте их рассмотрим. ### Оператор goto Имеющийся в **C#** оператор **goto** представляет собой оператор безусловного перехода. Когда в программе встречается оператор **goto**, ее выполнение переходит непосредственно к тому месту, на которое указывает этот оператор. Он уже давно "вышел из употребления" в программировании, поскольку способствует созданию "макаронного" кода. Хотя в некоторых случаях он оказывается удобным и дает определенные преимущества, если используется благоразумно. Главный недостаток оператора **goto** с точки зрения программирования заключается в том, что он вносит в программу беспорядок и делает ее практически неудобочитаемой. Но иногда применение оператора **goto** может, скорее, прояснить, чем запутать ход выполнения программы. Для выполнения оператора **goto** требуется метка — действительный в **C#** идентификатор с двоеточием. Метка должна находиться в том же методе, где и оператор **goto**, а также в пределах той же самой области действия. Пример использования оператора **goto**: ```cs // Обычный цикл for выводящий числа от 1 до 5 Console.WriteLine("Обычный цикл for:"); for (int i = 1; i <= 5; i++) Console.Write("\t{0}",i); // Реализуем то же самое с помощью оператора goto Console.WriteLine("\n\nА теперь используем goto:"); int j = 1; link1: Console.Write("\t{0}",j); j++; if (j <= 5) goto link1; Console.ReadLine(); ``` Репутация оператора **goto** такова, что в большинстве случаев его применение категорически осуждается. Вообще говоря, он, конечно, не вписывается в рамки хорошей практики объектно-ориентированного программирования. ### Оператор break С помощью оператора **break** можно организовать немедленный выход из цикла в обход любого кода, оставшегося в теле цикла, а также минуя проверку условия цикла. Когда в теле цикла встречается оператор **break**, цикл завершается, а выполнение программы возобновляется с оператора, следующего после этого цикла. Оператор **break** можно применять в любом цикле, предусмотренном в **C#**. ```cs // В данном цикле выведутся числа от 1 до 5 вместо 100 for (int i = 1; i < 100; i++) if (i <= 5) Console.WriteLine(i); else break; Console.ReadLine(); ``` Обратите внимание если оператор **break** применяется в целом ряде вложенных циклов, то он прерывает выполнение только самого внутреннего цикла. В отношении оператора **break** необходимо также иметь в виду следующее. Во-первых, в теле цикла может присутствовать несколько операторов **break**, но применять их следует очень аккуратно, поскольку чрезмерное количество операторов **break** обычно приводит к нарушению нормальной структуры кода. И во-вторых, оператор **break**, выполняющий выход из оператора **switch**, оказывает воздействие только на этот оператор, но не на объемлющие его циклы. ### Оператор continue С помощью оператора **continue** можно организовать преждевременное завершение шага итерации цикла в обход обычной структуры управления циклом. Оператор **continue** осуществляет принудительный переход к следующему шагу цикла, пропуская любой код, оставшийся невыполненным. Таким образом, оператор **continue** служит своего рода дополнением оператора **break**. В циклах `while` и `do...while` оператор **continue** вызывает передачу управления непосредственно условному выражению, после чего продолжается процесс выполнения цикла. А в цикле **for** сначала вычисляется итерационное выражение, затем условное выражение, после чего цикл продолжается: ```cs // Выводим числа кратные 5 for (byte i = 1; i <= 100; i++) { if (i % 5 != 0) continue; Console.Write("\t{0}", i); } Console.ReadLine(); ``` Оператор **continue** редко находит удачное применение, в частности, потому, что в **C#** предоставляется богатый набор операторов цикла, удовлетворяющих большую часть прикладных потребностей. Но в тех особых случаях, когда требуется преждевременное прерывание шага итерации цикла, оператор **continue** предоставляет структурированный способ осуществления такого прерывания. ### Оператор return Оператор **return** организует возврат из метода. Его можно также использовать для возврата значения. Имеются две формы оператора **return**: одна — для методов типа **void**, т.е. тех методов, которые не возвращают значения, а другая — для методов, возвращающих конкретные значения. Для немедленного завершения метода типа **void** достаточно воспользоваться следующей формой оператора **return**: ```cs return; ``` Когда выполняется этот оператор, управление возвращается вызывающей части программы, а оставшийся в методе код пропускается. Для возврата значения из метода в вызывающую часть программы служит следующая форма оператора **return**: ```cs return значение; ``` Давайте рассмотрим применение оператора **return** на конкретном примере: ```cs int result = Sum(230); Console.WriteLine("Сумма четных чисел от 1 до 230 равна: " + result); Console.ReadLine(); // Метод, возращающий сумму всех четных чисел // от 1 до s static int Sum(int s) { int mySum = 0; for (int i = 1; i <= s; i++) if (i % 2 == 0) mySum += i; return mySum; } ``` --- ## Контрольные вопросы 1. Операторы отношения 1. Логические операторы (обычные и укороченные) 1. Условные операторы 1. Тернарный оператор 1. Циклы 1. Оператор break 1. Оператор continue 1. Оператор return --- Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Основные элементы языка.](./t3l1.md) | [Содержание](../readme.md#тема-4-программирование-на-языке-c-основы) | [Массивы как структурированный тип данных.](./t3l1_3.md)