Суббота, 18.05.2024, 17:52
Приветствую Вас Гость | RSS
Форма входа
Поиск
Календарь
«  Май 2024  »
ПнВтСрЧтПтСбВс
  12345
6789101112
13141516171819
20212223242526
2728293031
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Charles Petzold ".NET Book Zero" Глава 8. Выбор и циклы

C# поддерживает те же операторы выбора, повторения, и управления потоком, которые используются в Си и C++, но с некоторыми ограничениями. Эти ограничения не являются серьезными и, как правило, предназначены для того, чтобы помочь вам избежать распространенных ошибок программирования. В этой главе я буду обсуждать выражения, строящиеся вокруг ключевых слов – if, else, switch, case, default, do, while, for, foreach, in, break, continue и goto.

Операторы выбора

Основные выражения выбора включают в себя ключевые слова if и else. Ключевое слово if должно предшествовать логическому выражению, заключённому в скобки. Выражение, которое следует после этого – выполняется, если логическое выражение принимает значение истинно (true):

if (a < 5)
    b += 27;

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

if (a = 5)

Компилятор С# на это выражение выкинет флаг об ошибке, и вы, скорее всего, будете благодарны ему за это.

Конечно, никакой компилятор не может предложить полную защиту от невнимательности программиста. В одной ранней C# программе я писал, я определил булеву переменную с именем trigger, но вместо того, чтобы написать:

if (trigger)

по какой-то причине я хотел быть немного более точным и собирался набрать следующее:

if (trigger == true)

Вместо этого, я набрал следующее:

if (trigger = true)

Если триггер определен как bool это вполне допустимое утверждение в C#, но оно, явно, не делает того, что я хотел. Если должен быть выполнен более чем один оператор, вы можете сгруппировать их в качестве блока операторов в фигурных скобках:

if (a < 5)
{
    b += 27;
    c = 0;
}

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

Выражение if может включать в себя пункт else:

if (a < 5) 
    b += 27; 
else 
    b -= 7;

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

if (a < 5)
{
    b += 27;
}
Else
{
    b -= 7;
}

Вы можете вкладывать друг в друга if выражения, и такая практика более распространена, не пустой пункт else, а другое if выражение:

if (a < 5)
{
    …
}
Else
{
    If (a > 5)
    {
        …
    }
    Else
    {
        …
    }
} 

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

if (a < 5)
{
    …
}
else if (a > 5)
{
    …
}
else
{
    …
}

Фигурные скобки и операторы внутри фигурных скобок называется блоком. (См "Спецификация языка C #", § 8.2) Вы можете объявить новые переменные в пределах блока, но они видны только внутри блока и во всех вложенных блоках.

Вы не можете объявить переменную с тем же именем, которое уже объявлено в родительском блоке, например:

int A;
…
{ 
int A; // Не разрешается! 
…
}

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

Тем не менее, объявление переменных с тем же именем в блоках от одного родителя – допускается:

{ 
    int A; 
    …
} 
…
{ 
    int A; // Не вызовет проблем! 
    …
} 

Конструкция switch и case в C# имеет ограничение, которого нет в C. В C и C ++ вы можете сделать так:

switch(a)
{
    case 3:
    b = 7; // Fall through isn’t allowed in C#
    case 4:
        c = 3;
    break;
    default:
        b = 2;
        c = 4;
    break;
}

В C или C++, в случае, когда a равно 3, один оператор выполняется, а затем выполнение переходит к метке, когда a равно 4. Это может быть тем, что вы хотели, или же вы, скорее всего, забыли набрать выражение break. Чтобы помочь вам избежать ошибок, подобных этому, C# компилятор сообщит об ошибке. C# позволяет case, перейти к следующей метке case только тогда, когда case не содержит никаких выражений. C# допускает следующее:

switch (a)
{
    case 3:
    case 4:
        b = 7;
        c = 3;
    break;
    default:
        b = 2;
        c = 4;
    break;
}

Чтобы компенсировать ограничения в отношении выхода из цикла, C# позволяет использовать выражение goto в конце case для перехода в другую метку case. Вот – разрешенная в C# реализация, только-что описанного запрещённого переключения блоков:

switch(a)
{
    case 3:
        b = 7;
    goto case 4;
    case 4:
        c = 3;
    break;
    default:
        b = 2;
        c = 4;
    break;
}

Вам не нужен финальный break в конце case, если вместо этого там находится goto. Вы также можете разветвить case используемое по умолчанию :

switch(a)
{
    case 1:
        b = 2;
    goto case 3;
    case 2:
        c = 7;
    goto default;
    case 3:
        c = 5;
    break;
    default:
        b = 2;
    goto case 1;
}

Выражение в switch должно принимать любой целый тип, символ, строку, или перечисление, и должно соответствовать типу указанному в метках case.

Вы даже можете использовать строковую переменную в выражении switch и сравнивать его с текстовой строкой в метке case:

switch (strCity)
{
    case "Boston":
        …
    break;
    case "New York":
        …
    break;
    case "San Francisco":
        …
    break;
    default:
        …
    break;
}

Конечно, это именно тот тип, который вызывает у одержимых производительностью C и C++ программистов чувство отвращения. Все эти сравнения строк просто не могут быть очень эффективными. На самом деле, из-за технологии, известной как – "Пул строк" (string interning) (которая включает в себя таблицу всех уникальных строк, используемых в программе), сравнение происходит намного быстрее, чем вы могли бы подумать.

Операторы циклов

C# также поддерживает выражение while для повторения группы операторов, пока условие истинно. Вы можете использовать условие в верхней части блока:

while (a < 5) 
{
    … 
}

или в нижней части блока:

do 
{
    … 
} 
while (a < 5);

Как и в операторе if, выражение в скобках должно принимать булевое значение. Во втором примере, блок выполнится, по крайней мере один раз, независимо от значения переменной a.

Блок while или do может содержать оператор break, при исполнении которого произойдёт прерывание цикла и выполнение программы продолжится со следующего оператора после блока while или do. Блок также может содержать оператор continue, который пропускает оставшуюся часть выражения и возвращается в начало цикла.

Оператор for выглядит так же, как в C и C++:

for (i = 0; i < 100; i++)
{
    … 
}

В скобках, первая часть это – инициализация, которая выполняется перед циклом. Вторая часть – логическое выражение. Содержимое цикла выполняется только тогда, когда это выражение истинно. Последняя часть – выполняется в конце блока. Если бы A, B, и C являлись выражениями объявления for:

for (A; B; C) 
{
    … 
}

это было бы почти эквивалентно следующему:

A; 
while (B) 
{
    … 
    C;
}

Я говорю "почти" потому что цикл for может содержать выражение continue, для пропуска оставшейся части цикла и для начала со следующей итерации. Тем не менее, в данном случае, выражение C будет выполнено, в отличие от конструкции while. Цикл for может содержать объявление break, для выхода из цикла.

Как и в C++, очень типично для C# программистов объявлять переменную для итераций в операторе for :

for (float a = 0; a < 10.5f; a += 0.1f) 
{
    … 
}

Переменная действительна только в пределах цикла for.

Удобным дополнением к операторам циклов, которые C# унаследовал от C и C++, является оператор foreach, взятый из Visual Basic. Я покажу вам некоторые примеры foreach в главе 10, при обсуждении массивов. Выражение foreach также работает с другими типами коллекций, и со строками. Предположим, вы хотите, отобразить все символы из строки, с именем str, каждый на отдельной строке. Код цикла for выглядит следующим образом:

for (int i = 0; i < str.Length; i++) 
Console.WriteLine(str[i]);

Выражение foreach значительно проще:

foreach (char ch in str) 
Console.WriteLine(ch);

Скобки содержат определение переменной с именем ch типа char; Эта переменная должна соответствовать типу элементов в массиве или коллекции. Далее следует ключевое слово in сопровождаемое переменной, содержащей элементы. Вы можете использовать break и continue в пределах блока foreach.

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

Выражение foreach требует коллекцию, которая поддерживает конкретный метод, что подробно описано в "Спецификации языка C#", §8.8.4. В практическом смысле, можно сказать, что выражение foreach работает с коллекциями, которые реализуют интерфейс IEnumerable.

Операторы перехода

Пункт §8.9 "Спецификации языка C#" описывает категорию операторов перехода включающих: break, continue и return. Я более подробно опишу return в главе 11. Эта категория также включает в себя throw (который я опишу в главе 12) и goto. Вы уже видели, как использовать goto в выражении switch. Вы также можете использовать goto, чтобы перейти к метке. Метки определяются с помощью идентификатора сопровождаемого двоеточием:

NowhereElseToGo:

Вы можете перейти к этой метке при помощи выражения:

goto NowhereElseToGo;

Метки имеют область видимости подобно переменным, и метка должна быть в том же блоке или родительском блоке, что и вызов goto. Другими словами, вы не можете перейти в середину блока. Вы можете выпрыгнуть из блока, а не в блок.