Прочитав эту главу, вы научитесь:
• объявлять булевы переменные;
• использовать булевы операторы для создания выражений, вычисляемых в true или в false;
• записывать инструкции if для принятия решений на основе результата вычисления булева выражения;
• записывать инструкции switch для принятия более сложных решений.
Из главы 3 «Создание методов и применение областей видимости» вы узнали, как группировать взаимосвязанные инструкции в методы. Кроме этого, вы научились использовать параметры для передачи информации методам, а инструкции return — для передачи информации из методов. Разделение программы на набор отдельных методов, каждый из которых разработан для выполнения конкретных задач или вычислений, является необходимой стратегией разработки. Многие программы нуждаются в решении больших и сложных задач. Разбиение программ на методы помогает разобраться с этими задачами и сконцентрироваться на их поэтапном решении.
Методы, рассмотренные в главе 3, имели весьма простую структуру, в которой каждая инструкция выполнялась последовательно после завершения выполнения предшествующей инструкции. Но для решения многих реальных задач нужна также возможность написания кода для выборочного выполнения различных действий и направления хода выполнения программы, в зависимости от обстоятельств, через тот или иной метод. Узнать о том, как выполнить эту задачу, вам позволит данная глава.
В мире C#-программирования, в отличие от реального мира, все либо белое, либо черное, либо правильное, либо неправильное, либо истина (true), либо ложь (false). Например, если создать целочисленную переменную по имени x, присвоить ей значение 99, а затем спросить, содержит ли x значение 99, ответ определенно будет положительным (true). Если же спросить, правда ли, что x меньше 10, ответ вполне очевидно будет отрицательным (false). Все это примеры булевых выражений, которые всегда вычисляются в true или false.
ПРИМЕЧАНИЕ Ответы на эти вопросы не обязательно точно такие же для всех других языков программирования. У неинициализированной переменной значение не определено, и вы не можете, к примеру, со всей определенностью сказать, что она меньше 10. В программах, написанных на C и C++, подобные проблемы зачастую являются источником ошибок. Компилятор Microsoft Visual C# решает эту проблему за счет гарантированного присвоения вами значения переменной перед тем, как проверять ее на имеющееся значение. При попытке проверки содержимого неинициализированной переменной программа просто не пройдет компиляцию.
В Visual C# имеется тип данных под названием bool. В переменной типа bool может храниться одно из двух значений, true или false. Например, следующие три инструкции объявляют переменную типа bool по имени areYouReady, присваивают этой переменной значение true, а затем выводят ее значение на консоль:
bool areYouReady;
areYouReady = true;
Console.WriteLine(areYouReady); // запись True на консоль
Булевым называется оператор, выполняющий вычисление, результатом которого будет либо true, либо false. В C# есть несколько очень полезных булевых операторов, простейшим из которых является оператор NOT, представленный восклицательным знаком (!). Оператор ! инвертирует булево значение, выдавая его противоположность. В предыдущем примере, если значение переменной areYouReady равно true, значение выражения !areYouReady будет false.
Двумя булевыми операторами, которыми вам придется пользоваться довольно часто, являются равенство (==) и неравенство (!=). Это бинарные операторы, с помощью которых можно определить, является ли значение таким же, как и другое значение того же типа, получая при этом булев результат. Обобщенное представление о работе этих операторов при использовании в качестве примера int-переменной по имени age дано в табл. 4.1.
Таблица 4.1
Оператор | Значение | Пример | Результат, если значение age равно 42 |
== | Равно | age == 100 | false |
!= | Не равно | age != 0 | true |
Оператор равенства == не следует путать с оператором присваивания =. Выражение x==y сравнивает x с y и имеет значение true, если значения одинаковы. А выражение x=y присваивает значение переменной y переменной x и возвращает в качестве результата значение переменной y.
Ближайшими родственниками операторов == и != являются операторы отношений. Эти операторы применяются для выяснения того, верно ли, что значение меньше или больше другого значения того же типа. Порядок использования этих операторов показан в табл. 4.2.
Таблица 4.2
Оператор | Значение | Пример | Результат, если значение age равно 42 |
< | Меньше чем | age < 21 | false |
<= | Меньше или равно | age <= 18 | false |
> | Больше чем | age > 16 | true |
>= | Больше или равно | age >= 30 | true |
C# предоставляет также два других бинарных булевых оператора: оператор логического И, представленный группой символов &&, и оператор логического ИЛИ, представленный группой символов ||. Обобщенно они известны как условные логические операторы. Их целью является объединение двух булевых выражений или значений в один булев результат. Эти операторы похожи на операторы равенства и отношения тем, что значения выражений, в которых они появляются, бывают либо истинными, либо ложными, но отличаются тем, что значения, к которым они применяются, также должны быть либо истинными, либо ложными.
Результат использования оператора && равен true только в том случае, если оба булева выражения вычисляются в true. Например, следующая инструкция присваивает значение true переменной validPercentage только в том случае, если значение переменной percent больше или равно 0 или меньше или равно 100:
bool validPercentage;
validPercentage = (percent >= 0) && (percent <= 100);
СОВЕТ Типичной ошибкой новичков является попытка объединить две проверки, указывая переменную percent лишь единожды:
percent >= 0 && <= 100 // эта инструкция не пройдет компиляцию
Избежать подобной ошибки, а также конкретизировать цель использования выражения помогают круглые скобки. Например, сравнение
validPercentage = percent >= 0 && percent <= 100
и сравнение
validPercentage = (percent >= 0) && (percent <= 100)
возвращают одно и то же значение, поскольку приоритет оператора && ниже, чем операторов >= и <=. Но второе выражение сообщает о своей цели в более понятной форме.
Результат использования оператора || равен true, если любое из булевых выражений вычисляется в true. Оператор || используется для выяснения того, вычисляется ли любое из объединяемых им булевых выражений в true. Например, следующая инструкция присваивает значение true переменной invalidPercentage в том случае, если значение переменной percent меньше 0 или больше 100:
bool invalidPercentage;
invalidPercentage = (percent < 0) || (percent > 100);
Оба оператора, как &&, так и ||, проявляют свойство под названием короткое замыкание. Иногда при выяснении результата условного логического выражения вычислять оба операнда нет необходимости. Например, если левый операнд оператора && вычисляется в false, результат всего выражения должен быть false независимо от значения правого операнда. Аналогично этому, если значение левого операнда оператора || вычисляется в true, результат всего выражения должен быть true независимо от значения правого операнда. В этих случаях операторы && и || пренебрегают вычислением правого операнда. Рассмотрим несколько примеров.
Если в выражении
(percent >= 0) && (percent <= 100)
значение переменной percent меньше нуля, булево выражение слева от оператора && вычисляется в false. Это значение служит признаком того, что результат всего выражения должен быть false, и булево выражение справа от оператора && не вычисляется.
Если в выражении
(percent < 0) || (percent > 100)
значение переменной percent меньше нуля, булево выражение слева от оператора || вычисляется в true. Это значение служит признаком того, что результат всего выражения должен быть true, и булево выражение справа от оператора || не вычисляется.
При осмотрительном составлении выражений, использующих условные логические операторы, можно ускорить выполнение кода, избегая ненужной работы. Помещать простые, легко вычисляемые булевы выражения следует слева от условного логического оператора, а более сложные — справа от него. Во многих случаях окажется, что вычислять более сложные выражения программе уже не нужно.
В табл. 4.3 помещена сводная информация о приоритетности и ассоциативности всех уже знакомых вам операторов. Операторы в одной и той же категории имеют одинаковый уровень приоритета. Операторы тех категорий, которые расположены в таблице выше остальных, имеют приоритет над операторами тех категорий, которые расположены ниже.
Обратите внимание на то, что у операторов && и || разные приоритеты: у && приоритет выше, чем у ||.
Когда в методе нужно выбрать между выполнением двух различных инструкций в зависимости от результата вычисления булева выражения, можно воспользоваться инструкцией if.
Таблица 4.3
Категория | Оператор | Описание | Ассоциативность |
Основные | () | Переопределение приоритета | Левая |
++ | Постинкремент | ||
-- | Постдекремент | ||
Унарные | ! | Логическое НЕ | Левая |
+ | Возвращение значения операнда в неизменном виде | ||
| - | Возвращение значения операнда с отрицательным знаком |
|
++ | Преинкремент | ||
-- | Предекремент | ||
Мультипликативные | * | Умножение | Левая |
/ | Деление | ||
% | Остаток целочисленного деления | ||
Аддитивные | + | Сложение | Левая |
- | Вычитание | ||
Отношений | < | Меньше чем | Левая |
<= | Меньше или равно | ||
> | Больше чем | ||
>= | Больше или равно | ||
Равенства | == | Равно | Левая |
!= | Не равно | ||
Логического И | && | Условное И | Левая |
Логического ИЛИ | || | Условное ИЛИ | Левая |
Присваивания | = | Присваивание значения правостороннего операнда левостороннему и возвращение присвоенного значения | Правая |
Для инструкции if используется следующий синтаксис (if и else являются ключевыми словами C#):
if ( booleanExpression )
statement-1;
else
statement-2;
Если булево выражение booleanExpression вычисляется в true, выполняется инструкция statement-1, в противном случае выполняется инструкция statement-2. Ключевое слово else и следующая за ним инструкция statement-2 являются необязательными. Если условие else отсутствует и выражение booleanExpression вычисляется в false, выполнение продолжается, начиная с кода, следующего за инструкцией if. Обратите также внимание на то, что булево выражение должно быть заключено в круглые скобки, иначе код не пройдет компиляцию.
Рассмотрим, к примеру, инструкцию if, повышающую значение переменной, представляющей секундную стрелку секундомера, на единицу (минуты пока игнорируются). Если значение переменной seconds равно 59, оно переустанавливается на 0, в противном случае увеличивается на единицу путем использования оператора ++:
int seconds;
...
if (seconds == 59)
seconds = 0;
else
seconds++;
Пожалуйста, пользуйтесь только булевыми выражениями!
Выражение в инструкции if должно быть заключено в круглые скобки. Кроме того, выражение должно быть булевым. В некоторых других языках, в частности C и C++, можно написать целочисленное выражение, и компилятор молча конвертирует целочисленное значение в true (если оно будет ненулевым) или в false (если оно будет нулевым). В C# данное поведение не поддерживается и при записи такого выражения компилятор выдает ошибку.
Если в инструкции if случайно указать вместо оператора проверки равенства (==) оператор присваивания (=), компилятор C# обнаружит вашу ошибку и, как показано в следующем примере, откажется от компиляции кода:
int seconds;
...
if (seconds = 59) // ошибка в ходе компиляции
...
if (seconds == 59) // все в порядке
Случайные присваивания были еще одним весьма распространенным источником ошибок в программах на C и C++, в которых присваиваемое значение (59) молча конвертировалось в булево выражение (при этом любое неотрицательное значение рассматривалось как true), в результате чего код, непосредственно следовавший за инструкцией if, неизменно выполнялся.
Кстати, для инструкции if вы можете в качестве выражения воспользоваться булевой переменной, но она, как показано в следующем примере, все равно должна быть заключена в круглые скобки:
bool inWord;
...
if (inWord == true) // допустимо, но используется крайне редко
...
if (inWord) // используется чаще и считается более подходящей
разновидностью
Обратите внимание на то, что в синтаксисе ранее показанной инструкции if после if (booleanExpression) и после ключевого слова else указано только по одной инструкции. Временами, когда булево выражение вычисляется в true, требуется выполнить более одной инструкции. Можно, конечно, сгруппировать инструкции внутри нового метода, а затем вызвать этот метод, но проще все же сгруппировать инструкции внутри блока, представляющего собой простую последовательность инструкций, указанных между открывающей и закрывающей фигурными скобками.
В следующем примере две инструкции, одна из которых переустанавливает переменную seconds в нуль, а вторая увеличивает значение переменной minutes на единицу, сгруппированы внутри блока, и если значение seconds равно 59, выполняется весь блок:
int seconds = 0;
int minutes = 0;
...
if (seconds == 59)
{
seconds = 0;
minutes++;
}
else
{
seconds++;
}
ВНИМАНИЕ Если не поставить фигурные скобки, компилятор C# свяжет с инструкцией if только первую инструкцию (seconds = 0;). Следующая за ней инструкция (minutes++;) при компиляции программы не будет распознана компилятором в качестве части инструкции if. Более того, когда компилятор дойдет до ключевого слова else, он не свяжет его с предыдущей инструкцией if, а выдаст вместо этого синтаксическую ошибку. Поэтому лучше всего всегда определять инструкции для каждой исполняемой ветви инструкции if внутри блока, даже если блок состоит только из одной инструкции. Если в дальнейшем потребуется добавить дополнительный код, это убережет вас от неприятностей.
В блоке также запускается новая область видимости. Внутри блока можно определить переменные, исчезающие сразу же после того, как будут выполнены содержащиеся в нем инструкции. Это положение иллюстрируется следующим фрагментом кода:
if (...)
{
int myVar = 0;
... // здесь myVar может использоваться
} // а здесь myVar исчезает
else
{
// здесь myVar уже не может использоваться
...
}
// и здесь myVar не может использоваться
Инструкции if могут находиться внутри других инструкций if. Таким образом можно собирать в цепочку последовательность булевых выражений, проверяемых одно за другим до тех пор, пока одно из них не будет вычислено в true. В следующем примере, если значение переменной day равно нулю, то первая проверка вычисляется в true и переменной dayName присваивается строковое значение "Sunday". Если значение day не равно нулю, первая проверка дает отрицательный результат и управление передается условию else, которое запускает вторую инструкцию if и сравнивает значение переменной day с единицей. Вторая инструкция if выполняется только в том случае, если результат первой проверки не является положительным. Аналогично этому третья инструкция if выполняется только в том случае, если с положительным результатом не проходит вторая проверка:
if (day == 0)
{
dayName = "Sunday";
}
else if (day == 1)
{
dayName = "Monday";
}
else if (day == 2)
{
dayName = "Tuesday";
}
else if (day == 3)
{
dayName = "Wednesday";
}
else if (day == 4)
{
dayName = "Thursday";
}
else if (day == 5)
{
dayName = "Friday";
}
else if (day == 6)
{
dayName = "Saturday";
}
else
{
dayName = "unknown";
}
В следующем упражнении вам предстоит создать метод, использующий каскад, созданный из инструкций if, для сравнения двух дат.
Откройте в Microsoft Visual Studio 2015 проект Selection, который находится в папке \Microsoft Press\VCSBS\Chapter 4\Selection вашей папки документов.
Щелкните в меню Отладка на пункте Начать отладку. Среда Visual Studio 2015 выполнит сборку и запуск приложения. В форме будут выведены два элемента управления выбором даты под названиями firstDate и secondDate. В обоих элементах будет показана текущая дата. Щелкните на кнопке Compare (Сравнить).
В текстовом поле, находящемся в нижней части окна, появится следующий текст:
firstDate == secondDate : False
firstDate != secondDate : True
firstDate < secondDate : False
firstDate <= secondDate : False
firstDate > secondDate : True
firstDate >= secondDate : True
Булево выражение firstDate == secondDate будет вычислено в true, поскольку и в firstDate, и в secondDate установлена текущая дата. Похоже, что на самом деле правильно сработали только оператор «меньше чем» и оператор «больше или равно». Работающее приложение показано на рис. 4.1.
Рис. 4.1
Вернитесь в Visual Studio 2015. Щелкните в меню Отладка на пункте Остановить отладку. Выведите в окно редактора код файла MainPage.xaml.cs. Найдите метод compareClick, который должен выглядеть следующим образом:
private void compareClick(object sender, RoutedEventArgs e)
{
int diff = dateCompare(firstDate.Date.LocalDateTime,
secondDate.Date.LocalDateTime);
info.Text = "";
show("firstDate == secondDate", diff == 0);
show("firstDate != secondDate", diff != 0);
show("firstDate < secondDate", diff < 0);
show("firstDate <= secondDate", diff <= 0);
show("firstDate > secondDate", diff > 0);
show("firstDate >= secondDate", diff >= 0);
}
Этот метод запускается, когда пользователь щелкает в форме на кнопке Compare. В выражениях firstDate.Date.LocalDateTime и secondDate.Date.LocalDateTime содержатся значения типа DateTime, представляющие даты, отображаемые в форме в элементах управления firstDate и secondDate в другом месте приложения. Тип данных DateTime относится к еще одному типу, как и int или float, кроме того что он содержит субэлементы, позволяющие получить доступ к отдельным частям даты, таким как год, месяц или день.
Метод compareClick передает методу dateCompare два значения типа DateTime. Целями этого метода являются сравнение дат и возвращение int-значения 0, если они одинаковые, –1 — если первая дата меньше второй и +1 — если первая дата больше второй. Дата считается больше другой даты, если хронологически она наступает позже. Метод dateCompare мы изучим на следующем этапе.
Метод show выводит результаты сравнения в текстовую область info, которая находится в нижней половине формы.
Найдите метод dateCompare, который должен выглядеть следующим образом:
private int dateCompare(DateTime leftHandSide, DateTime rightHandSide)
{
// TO DO
return 42;
}
На данный момент этот метод при вызове, независимо от значения своих параметров, вместо 0, –1 или +1 возвращает одно и то же значение. Этим и объясняется, почему приложение работает не как, как от него ожидалось. Чтобы правильно сравнить две даты, необходимо реализовать в методе соответствующую логику.
Удалите из метода dateCompare комментарий // TO DO и инструкцию return. Добавьте к телу метода dateCompare следующие инструкции, выделенные жирным шрифтом:
private int dateCompare(DateTime leftHandSide, DateTime rightHandSide)
{
int result = 0;
if (leftHandSide.Year < rightHandSide.Year)
{
result = -1;
}
else if (leftHandSide.Year > rightHandSide.Year)
{
result = 1;
}
}
ПРИМЕЧАНИЕ Не пытайтесь именно сейчас выполнять сборку приложения. Метод dateCompare еще не имеет законченного вида, и сборка не состоится.
Если выражение leftHandSide.Year < rightHandSide.Year вычисляется в true, дата в переменной leftHandSide должна по хронологии наступить раньше даты в переменной rightHandSide и программа устанавливает значение переменной result в –1. В противном случае, если выражение leftHandSide.Year > rightHandSide.Year вычисляется в true, дата в переменной leftHandSide должна наступить позже даты в переменной rightHandSide и программа устанавливает значение переменной result в 1.
Если выражение leftHandSide.Year < rightHandSide.Year вычисляется в false и выражение leftHandSide.Year > rightHandSide.Year также вычисляется в false, то свойство Year обеих дат должно иметь одинаковое значение и программе следует сравнить месяцы каждой из дат.
Добавьте к телу метода dateCompare следующие инструкции, выделенные жирным шрифтом. Наберите их после кода, введенного на предыдущем этапе:
private int dateCompare(DateTime leftHandSide, DateTime rightHandSide)
{
...
else if (leftHandSide.Month < rightHandSide.Month)
{
result = -1;
}
else if (leftHandSide.Month > rightHandSide.Month)
{
result = 1;
}
}
Эти инструкции сравнивают месяцы, следуя той же логике, которая использовалась при сравнении годов на предыдущем этапе.
Если выражение leftHandSide.Month < rightHandSide.Month вычисляется в false и выражение leftHandSide.Month > rightHandSide.Month также вычисляется в false, то свойства Month обеих дат должны иметь одинаковые значения, следовательно, в завершение программе нужно сравнить дни в каждой из дат.
После кода, введенного на двух предыдущих этапах, добавьте к телу метода dateCompare следующие инструкции, выделенные жирным шрифтом, :
private int dateCompare(DateTime leftHandSide, DateTime rightHandSide)
{
...
else if (leftHandSide.Day < rightHandSide.Day)
{
result = -1;
}
else if (leftHandSide.Day > rightHandSide.Day)
{
result = 1;
}
else
{
result = 0;
}
return result;
}
Теперь вы уже должны разбираться в построении этой логики. Если оба выражения, leftHandSide.Day < rightHandSide.Day и leftHandSide.Day > rightHandSide.Day, вычисляются в false, значения свойств Day обеих переменных должны быть одинаковыми. Чтобы логика программы дошла до этого места, значения Month и значения Year также должны быть идентичны, следовательно, обе даты должны быть одинаковыми и программа устанавливает значение переменной result в нуль.
Завершающая инструкция возвращает значение, сохраненное в переменой result.
Щелкните в меню Отладка на пункте Начать отладку. Приложение будет собрано заново и запущено на выполнение. Щелкните на кнопке Compare. В текстовой области появится следующий текст:
firstDate == secondDate : True
firstDate != secondDate : False
firstDate < secondDate: False
firstDate <= secondDate: True
firstDate > secondDate: False
firstDate >= secondDate: True
Для одинаковых дат будут выведены абсолютно правильные результаты.
Воспользуйтесь элементом управления типа DatePicker и выберите для второй даты большее значение, после чего щелкните на кнопке Compare. В текстовой области появится следующий текст:
firstDate == secondDate: False
firstDate != secondDate: True
firstDate < secondDate: True
firstDate <= secondDate: True
firstDate > secondDate: False
firstDate >= secondDate: False
И снова первая дата по хронологии наступает раньше второй — это абсолютно правильные результаты.
Протестируйте работу приложения для каких-нибудь других дат и убедитесь в том, что результаты соответствуют вашим ожиданиям. Когда закончите, вернитесь в Visual Studio 2015 и остановите отладку.
Сравнение дат в реальных приложениях
Теперь, когда вы увидели, как используются довольно длинные и сложные серии инструкций if и else, должен заметить, что в реальных приложениях для сравнения дат такой прием использоваться не будет. Если взглянуть на метод dateCompare из предыдущего упражнения, то в нем можно увидеть два параметра, leftHandSide и rightHandSide, значения которых относятся к типу данных DateTime. Логика, которую вы записывали, сравнивает только ту часть параметров, которая относится к дате, но в них содержится и элемент времени, который вами не рассматривался (и не показывался). Чтобы два значения типа DateTime считались одинаковыми, у них должны быть не только одинаковые даты, но и одинаковое время. Сравнение дат и времени является настолько распространенной операцией, что в типе DateTime имеется встроенный метод под названием Compare, который занимается следующим: берет два аргумента типа DateTime и сравнивает их, возвращая значение, показывающее, меньше ли первый аргумент, чем второй, в случае чего результатом будет отрицательное значение; больше ли первый аргумент, чем второй, в случае чего результатом будет положительное значение; или оба аргумента представляют одни и те же дату и время, в случае чего результатом будет нуль.
Иногда при написании каскада из if-инструкций выясняется, что все эти инструкции похожи друг на друга, поскольку вычисляют сходные выражения. Разница между ними лишь в том, что каждая из if-инструкций сравнивает результат выражения с другим значением. Рассмотрим, к примеру, следующий блок кода, использующий инструкцию if для проверки значения переменной day и определения того, к какому дню недели оно относится:
if (day == 0)
{
dayName = "Sunday";
}
else if (day == 1)
{
dayName = "Monday";
}
else if (day == 2)
{
dayName = "Tuesday";
}
else if (day == 3)
{
...
}
else
{
dayName = "Unknown";
}
Зачастую в подобных ситуациях можно вместо каскада if-инструкций воспользоваться инструкцией switch, повысив эффективность и удобочитаемость программы.
Для инструкции switch используется следующий синтаксис (switch, case и default являются ключевыми словами):
switch ( controllingExpression )
{
case constantExpression :
statements
break;
case constantExpression :
statements
break;
...
default :
statements
break;
}
Выражение controllingExpression, которое должно быть заключено в круглые скобки, вычисляется только один раз. Затем управление переходит к блоку кода, определяемому выражением constantExpression, чье значение равно результату вычисления выражения controllingExpression. (Идентификатор constantExpression также называют меткой альтернативы.) Код выполняется вплоть до инструкции break, на которой инструкция switch завершает свою работу, и программа продолжает работу, выполняя инструкцию, которая стоит сразу же за закрывающей фигурной скобкой инструкции switch. Если ни одно из значений constantExpression не равно значению controllingExpression, выполняются инструкции, находящиеся ниже необязательной метки default.
ПРИМЕЧАНИЕ Каждое значение constantExpression должно быть уникальным, чтобы controllingExpression могло соответствовать только одному из них. Если значение controllingExpression не соответствует какому-либо значению constantExpression, а метка default отсутствует, выполнение программы продолжается с первой инструкции, следующей за закрывающей фигурной скобкой инструкции switch.
Предыдущий каскад из if-инструкций можно переписать, воспользовавшись следующей инструкцией switch:
switch (day)
{
case 0 :
dayName = "Sunday";
break;
case 1 :
dayName = "Monday";
break;
case 2 :
dayName = "Tuesday";
break;
...
default :
dayName = "Unknown";
break;
}
При всем несомненном удобстве инструкции switch, к сожалению, ее нельзя применять везде, где заблагорассудится. Любая применяемая switch-инструкция должна отвечать следующим правилам.
• Инструкцию switch можно применять только к конкретным типам данных, а именно к int, char или string. С любыми другими типами (включая float и double) следует использовать инструкцию if.
• Метки альтернатив должны быть выражениями, представленными константами, например, 42, если типом данных инструкции switch является int, '4', если типом данных является char, или "42", если типом данных является string. Если метку альтернативы нужно вычислить во время выполнения программы, следует воспользоваться инструкцией if.
• Метки альтернатив должны быть уникальными выражениями. Иными словами, две метки альтернатив не могут иметь одно и то же значение.
• Вы можете указать, что хотите выполнить одинаковые инструкции для более чем одного значения, предоставив список меток альтернатив, не прерываемый инструкциями, в случае чего код для завершающей метки списка выполняется для всех указанных в списке меток. Но если у метки имеется одна и больше связанных с ней инструкций, выполнение не может быть передано дальше вниз следующим меткам, и в таком случае компилятор выдает ошибку. Все эти положения проиллюстрированы в следующем фрагменте кода:
switch (trumps)
{
case Hearts :
case Diamonds : // Передача управления вниз разрешена, поскольку
// код между метками отсутствует
color = "Red"; // код выполняется для меток Hearts и Diamonds
break;
case Clubs :
color = "Black";
case Spades : // Ошибка – код между метками
color = "Black";
break;
}
ПРИМЕЧАНИЕ Наиболее распространенным способом остановки передачи управления следующим меткам альтернатив является применение инструкции break, но для выхода из метода, содержащего инструкцию switch, можно также воспользоваться инструкцией return или throw, выдающей исключение и прекращающей выполнение инструкции switch. Инструкция throw рассматривается в главе 6 «Обработка ошибок и исключений».
Правила передачи управления внутри инструкции switch
Поскольку случайная передача управления от одной метки альтернатив следующей такой метке при наличии какого-либо кода в промежутке между ними невозможна, вы можете вполне свободно переставлять секции инструкции switch, что не оказывает на ее работу никакого влияния (включая и метку default, которая по соглашению обычно ставится в качестве последней метки, но именно на этом месте стоять не обязана).
Программисты, работающие на C и C++, должны заметить, что инструкция break является обязательной для каждой метки в инструкции switch (даже для метки default). Это требование имеет свою положительную сторону, так как в программах на C или C++ программисты часто забывают ставить инструкцию break, позволяя программе продолжать выполнение с передачей управления на следующую метку, что приводит к трудно обнаруживаемым ошибкам.
При желании вы можете имитировать такую же передачу управления другой метке, как в C и C++, и в программе на C#, для чего нужно воспользоваться инструкцией goto для перехода к следующей метке альтернатив или к метке default. Но вообще-то использовать goto не рекомендуется, и в этой книге такой прием не демонстрируется.
В следующем упражнении вам предстоит завершить программу, считывающую символы из строки и отображающую каждый символ на его XML-представление. Например, символ левой угловой скобки (<) имеет в XML специальное значение (используется для формирования элементов). Если в данных содержится этот символ, он должен быть переведен в свое текстовое представление <, чтобы XML-процессор знал, что это данные, а не часть XML-инструкции. Похожие правила применяются к символам правой угловой скобки (>), амперсанда (&), одинарной кавычки (') и двойной кавычки ("). Вами будет создана инструкция switch, проверяющая значение символа и перехватывающая специальные символы XML, фигурирующие в качестве меток альтернатив.
Откройте в Microsoft Visual Studio 2015 проект SwitchStatement, который находится в папке \Microsoft Press\VCSBS\Chapter 4\SwitchStatement вашей папки документов.
Щелкните в меню Отладка на пункте Начать отладку. Среда Visual Studio 2015 выполнит сборку и запуск приложения, которое выведет на экран форму, содержащую две текстовые области, разделенные кнопкой Copy (Копировать).
Наберите в верхней текстовой области следующий образец текста:
inRange = (lo <= number) && (hi >= number);
Щелкните на кнопке Copy.
Инструкция, как показано на рис. 4.2, будет скопирована в нижнюю текстовую область в неизменном виде, без перевода символов <, & и > в их представления.
Рис. 4.2
Вернитесь в Visual Studio 2015 и остановите отладку.
Выведите в окно редактора код файла MainPage.xaml.cs и найдите в нем метод copyOne, который копирует символ, указанный в качестве его входного параметра, в конец текста, выводимого в нижнюю текстовую область. На данный момент в copyOne содержится инструкция switch с единственной меткой альтернативы default. На следующих нескольких этапах вы внесете в эту инструкцию switch изменения, позволяющие переводить символы, имеющие для XML особое значение, в их отображение в языке XML. Например, символ < будет преобразован в строку <.
Добавьте к инструкции switch после ее открывающей фигурной скобки и непосредственно перед меткой default следующие инструкции, выделенные жирным шрифтом:
switch (current)
{
case '<' :
target.Text += "<";
break;
default:
target.Text += current;
break;
}
Если текущим копируемым символом будет левая угловая скобка (<), предыдущий код добавит к тексту, выводимому на свое место, строку "<".
Добавьте после только что добавленной инструкции break и выше метки default следующие инструкции:
case '>' :
target.Text += ">";
break;
case '&' :
target.Text += "&";
break;
case '\"' :
target.Text += """;
break;
case '\'' :
target.Text += "'";
break;
ПРИМЕЧАНИЕ Одинарная кавычка (') и двойная кавычка (") имеют в C# специальное значение — они используются в качестве разделителей символов и строковых констант. Обратный слеш (\) в последних двух метках альтернатив является символом отмены специального действия, заставляющим компилятор C# рассматривать эти символы в качестве литералов, а не разделителей.
Щелкните в меню Отладка на пункте Начать отладку. Наберите в верхней текстовой области:
inRange = (lo <= number) && (hi >= number);
Щелкните на кнопке Copy.
Инструкция будет скопирована в нижнюю текстовую область. На этот раз в XML-отображение, реализованное в инструкции switch, переводится каждый символ. В целевом текстовом поле отобразится следующее:
inRange = (lo <= number) && (hi >= number);
Поэкспериментируйте с другими строками и убедитесь в том, что все специальные символы (<, >, &, " и ') обрабатываются должным образом.
Вернитесь в Visual Studio и остановите отладку.
В данной главе вы узнали, что такое булевы выражения и переменные, увидели, как используются булевы выражения с инструкциями if и switch для принятия решений в ваших программах, и объединили булевы выражения с помощью булевых операторов.
Если хотите продолжить работу и изучить следующую главу, оставьте открытой среду Visual Studio 2015 и перейдите к главе 5 «Использование инструкций составного присваивания и итераций».
Если сейчас вы хотите выйти из среды Visual Studio 2015, то в меню Файл щелкните на пункте Выход. Если увидите диалоговое окно с предложением сохранить изменения, щелкните на кнопке Да и сохраните проект.
Чтобы | Сделайте следующее | Пример |
Определить равенство двух значений | Воспользуйтесь оператором == или оператором != | answer == 42 |
Сравнить значения двух выражений | Воспользуйтесь оператором <, <=, > или >= | age >= 21 |
Объявить булеву переменную | Воспользуйтесь в качестве типа переменной ключевым словом bool | bool inRange; |
Создать булево выражение, вычисляемое в true только в случае, когда оба условия вычисляются в true | Воспользуйтесь оператором && | inRange = (lo <= number) && (number <= hi); |
Создать булево выражение, вычисляемое в true, если любое из двух условий вычисляется в true | Воспользуйтесь оператором || | outOfRange = (number < lo) || (hi < number); |
Выполнить инструкцию, если условие вычисляется в true | Воспользуйтесь инструкцией if | if (inRange) process(); |
Выполнить более одной инструкции, если условие вычисляется в true | Воспользуйтесь инструкцией if и блоком | if (seconds == 59) { seconds = 0; minutes++; } |
Связать различные инструкции с различными значениями управляющего выражения | Воспользуйтесь инструкцией switch | switch (current) { case 0: ... break; case 1: ... break; default : ... break; } |