Добавить в цитаты Настройки чтения

Страница 275 из 371



         if (x > limit)

            {x — = step; у += step;}

         else

            {x += step; у — = step;}

         sum1 += x; sum2 +=y;

     }

    //limit = step; //переменная step перестала существовать

    //limit = i; // переменная i перестала существовать

    Console.WriteLine("x= {0}, y= {1}, sum1 ={2}, sum2 = {3}",

         x, у, sum1,sum2);

}

Заметьте, здесь в тело основного блока вложен блок, задающий тело цикла, в котором объявлены две локальные переменные — i и step.

В свою очередь, в тело цикла вложены блоки, связанные с ветвями then и else оператора if. Закомментированные операторы, стоящие сразу за окончанием цикла, напоминают, что соответствующие локальные переменные, определенные в блоке, перестают существовать по его завершении.

Приведенная процедура Block является методом класса Testing, который входит в проект statements, созданный для работы с примерами этой лекции. Вот описание полей и конструктора класса Testing:

/// <summary>

/// Класс Testing — тестирующий класс. Представляет набор

/// скалярных переменных и методов, тестирующих работу

/// с операторами, процедурами и функциями С#.

/// </summary>

public class Testing

{

    public Testing(string name, int age)

    {

        this.age = age;

        this.name = name;

     }

     //поля класса

     public string name;

     public int age;

     private int period;

     private string status;

}

Пустой оператор





Пустой оператор — это "пусто", завершаемое точкой с запятой. Иногда полезно рассматривать отсутствие операторов как существующий пустой оператор. Синтаксически допустимо ставить лишние точки с запятой, полагая, что вставляются пустые операторы. Например, синтаксически допустима следующая конструкция:

for (int j=1; j<5; j++)

           {;;;};

Она может рассматриваться как задержка по времени, работа на холостом ходе.

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

Как в C++ и других языках программирования, в языке C# для выбора одной из нескольких возможностей используются две конструкции — if и switch. Первую из них обычно называют альтернативным выбором, вторую — разбором случаев.

Оператор if

Начнем с синтаксиса оператора if:

if(выражение_1) оператор_1

else if(выражение_2) оператор_2

else if(выражение_К) оператор_К

else оператор_N

Какие особенности синтаксиса следует отметить? Выражения if должны заключаться в круглые скобки и быть булевого типа. Точнее, выражения должны давать значения true или false. Напомню, арифметический тип не имеет явных или неявных преобразований к булевому типу. По правилам синтаксиса языка C++, then-ветвь оператора следует сразу за круглой скобкой без ключевого слова then, типичного для большинства языков программирования. Каждый из операторов может быть блоком — в частности, if-оператором. Поэтому возможна и такая конструкция:

if(выражение 1) if(выражение2) if(выражение 3)…

Ветви else и if, позволяющие организовать выбор из многих возможностей, могут отсутствовать. Может быть опущена и заключительная else-ветвь. В этом случае краткая форма оператора if задает альтернативный выбор — делать или не делать — выполнять или не выполнять then-оператор.

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

Оператор switch

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

switch(выражение)

{

    case константное_выражение_1: [операторы_1 оператор_перехода_1]

    …

    case константное_выражение_К: [операторы_К оператор_перехода_К]

    [default: операторы_N оператор_пepexoда_N]

}

Ветвь default может отсутствовать. Заметьте, по синтаксису допустимо, чтобы после двоеточия следовала пустая последовательность операторов, а не последовательность, заканчивающаяся оператором перехода. Константные выражения в case должны иметь тот же тип, что и switch-выражение.

Семантика оператора switch чуть запутана. Вначале вычисляется значение switch-выражения. Затем оно поочередно в порядке следования case сравнивается на совпадение с константными выражениями. Как только достигнуто совпадение, выполняется соответствующая последовательность операторов case-ветви. Поскольку последний оператор этой последовательности является оператором перехода (чаще всего это оператор break), то обычно он завершает выполнение оператора switch. Использование операторов перехода — это плохая идея. Таким оператором может быть оператор goto, передающий управление другой case-ветви, которая, в свою очередь, может передать управление еще куда-нибудь, получая блюдо "спагетти" вместо хорошо структурированной последовательности операторов.

Семантика осложняется еще и тем, что case-ветвь может быть пустой последовательностью операторов. Тогда в случае совпадения константного выражения этой ветви со значением switch-выражения будет выполняться первая непустая последовательность очередной case-ветви. Если значение switch-выражения не совпадает ни с одним константным выражением, то выполняется последовательность операторов ветви default, если же таковой ветви нет, то оператор switch эквивалентен пустому оператору.

Полагаю, что оператор switch — это самый неудачный оператор языка C# как с точки зрения синтаксиса, так и семантики. Неудачный синтаксис порождает запутанную семантику, являющуюся источником плохого стиля программирования. Понять, почему авторов постигла неудача, можно, оправдать — нет. Дело в том, что оператор унаследован от C++, где его семантика и синтаксис еще хуже. В языке C# синтаксически каждая case-ветвь должна заканчиваться оператором перехода (забудем на минуту о пустой последовательности), иначе возникнет ошибка периода компиляции. В языке C++ это правило не является синтаксически обязательным, хотя на практике применяется та же конструкция с конечным оператором break. При его отсутствии управление "проваливается" в следующую case-ветвь. Конечно, профессионал может с успехом использовать этот трюк, но в целом ни к чему хорошему это не приводит. Борясь с этим, в C# потребовали обязательного включения оператора перехода, завершающего ветвь. Гораздо лучше было бы, если бы последним оператором мог быть только оператор break, писать его было бы не нужно и семантика стала бы прозрачной — при совпадении значений двух выражений выполняются операторы соответствующей case-ветви, при завершении которой завершается и оператор switch.

Еще одна неудача в синтаксической конструкции switch связана с существенным ограничением, накладываемым на case-выражения, которые могут быть только константным выражением. Уж если изменять оператор, то гораздо лучше было бы использовать синтаксис и семантику Visual Basic, где в case-выражениях допускается список, каждое из выражений которого может задавать диапазон значений.