Программирование на языке высокого уровня C#

Простейший ввод-вывод. Управляющие операторы

Показывать лекцию целиком

Консольный ввод-вывод

Презентацию к данной лекции Вы можете скачать здесь.

Любая программа при вводе исходных данных и выводе результатов взаимодействует с внешними устройствами. Совокупность стандартных устройств ввода и вывода, то есть клавиатуры и экрана, называется консолью. В языке C# нет операторов ввода и вывода. Вместо них для обмена с внешними устройствами применяются стандартные объекты. Для работы с консолью в C# применяется класс Console, определенный в пространстве имен System. Методы этого класса Write и WriteLine уже использовались в листинге 3.1. Поговорим о них подробнее, для чего внесем некоторые изменения в этот листинг. Результаты этих изменений представлены в листинге 4.1.

using System;
namespace ConsoleApplication1
{    class Class1
    {    static void Main()
        {
            int     i = 3;
            double  y = 4.12;
            decimal d = 600m;
            string  s = "Вася";

            Console.WriteLine( "i = " + i );                   // 1
            Console.WriteLine( "s = " + s );                   // 2
            Console.WriteLine( "y = {0} \nd = {1}", y, d );    // 3
       }
    }
}

Результат работы программы:

i = 3
s = Вася
y = 4,12
d = 600

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

Методы с одинаковыми именами, но разными параметрами называются перегруженными. Компилятор определяет, какой из методов вызван, по типу передаваемых в него величин. Методы вывода в классе Console перегружены для всех встроенных типов данных, кроме того, предусмотрены варианты форматного вывода.

Листинг 4.1 содержит два наиболее употребляемых варианта вызова методов вывода. Если метод WriteLine вызван с одним параметром, он может быть любого встроенного типа. В строке, помеченной комментариями "1" и "2", нам требуется вывести в каждой строке не одну, а две величины, поэтому прежде чем передавать их для вывода, их требуется "склеить" в одну строку с помощью операции +.

Перед объединением строки с числом надо преобразовать число из его внутренней формы представления в последовательность символов, то есть в строку. Преобразование в строку определено во всех стандартных классах C# — для этого служит метод ToString(). В данном случае он выполняется неявно.

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

Из управляющих последовательностей чаще всего используются символы перевода строки ( \n ) и горизонтальной табуляции ( \t ).

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

  • Символы, представляющие собой число, вводятся с клавиатуры в строковую переменную.
  • Выполняется преобразование из строки в переменную соответствующего типа.
  • Преобразование можно выполнить либо с помощью специального класса Convert, определенного в пространстве имен System, либо с помощью метода Parse, имеющегося в каждом стандартном арифметическом классе. В листинге 4.2 используются оба способа.

    using System;
    namespace ConsoleApplication1
    {    class Class1
        {    static void Main()
            {
                Console.WriteLine( "Введите строку" );
                string s = Console.ReadLine();                  // 1
                Console.WriteLine( "s = " + s );
                
                Console.WriteLine( "Введите символ" );
                char c = (char)Console.Read();                  // 2
                Console.ReadLine();                             // 3
                Console.WriteLine( "c = " + c );
            
                string buf;        // строка – буфер для ввода чисел
    
                Console.WriteLine( "Введите целое число" );
                buf = Console.ReadLine();
                int i = Convert.ToInt32( buf );                 // 4
                Console.WriteLine( i );
    
                Console.WriteLine( "Введите вещественное число" );
                buf = Console.ReadLine();
                double x = Convert.ToDouble( buf );             // 5
                Console.WriteLine( x );
    
                Console.WriteLine( "Введите вещественное число" );
                buf = Console.ReadLine();
                double y = double.Parse( buf );                 // 6
                Console.WriteLine( y );
    
                Console.WriteLine( "Введите вещественное число" );
                buf = Console.ReadLine();
                decimal z = decimal.Parse( buf );               // 7
                Console.WriteLine( z );
            }
        }
    }

    Ввод строки выполняется в операторе 1. Длина строки не ограничена, ввод выполняется до символа перевода строки.

    Ввод символа выполняется с помощью метода Read, который считывает один символ из входного потока (оператор 2). Метод возвращает значение типа int, представляющее собой код символа, или –1, если символов во входном потоке нет (например, пользователь нажал клавишу Enter ). Поскольку нам требуется не int, а char, а неявного преобразования от int к char не существует, приходится применить операцию явного преобразования типа.

    Оператор 3 считывает остаток строки и никуда его не передает. Это необходимо потому, что ввод данных выполняется через буфер — специальную область оперативной памяти. Фактически данные сначала заносятся в буфер, а затем считываются оттуда процедурами ввода. Занесение в буфер выполняется по нажатию клавиши Enter вместе с ее кодом. Метод Read, в отличие от ReadLine, не очищает буфер, поэтому следующий после него ввод будет выполняться с того места, на котором закончился предыдущий.

    В операторах 4 и 5 используются методы класса Convert, в операторах 6 и 7 — методы Parse классов Double и Decimal библиотеки .NET, которые используются здесь через имена типов С# double и decimal.

    Внимание

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

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

    Ввод-вывод в файлы

    Часто бывает удобно заранее подготовить исходные данные в текстовом файле и считывать их в программе. Вывод из программы тоже бывает полезно выполнить не на экран, а в текстовый файл. Работа с файлами подробно рассматривается позже, а сейчас приведем лишь образцы для использования в программах. В листинге 4.3 приведена версия программы из листинга 4.1, выполняющая вывод не на экран, а в текстовый файл с именем output.txt. Файл создается в том же каталоге, что и исполняемый файл программы, по умолчанию — ...\ConsoleApplication1\bin\Debug.

    using System;
    using System.IO;                                                     // 1
    namespace ConsoleApplication1
    {    class Class1
        {    static void Main()
            {
                StreamWriter f = new StreamWriter( "output.txt" );       // 2
                int     i = 3;
                double  y = 4.12;
                decimal d = 600m;
                string  s = "Вася";
    
                f.WriteLine( "i = " + i );                               // 3
                f.WriteLine( "s = " + s );                               // 4
                f.WriteLine( "y = {0} \nd = {1}", y, d );                // 5
    
                f.Close();                                               // 6
            }
        }
    }

    Для того чтобы использовать в программе файлы, необходимо:

  • Подключить пространство имен, в котором описываются стандартные классы для работы с файлами (оператор 1).
  • Объявить файловую переменную и связать ее с файлом на диске (оператор 2).
  • Выполнить операции ввода-вывода (операторы 3–5).
  • Закрыть файл (оператор 6).
  • Ввод данных из файла выполняется аналогично. В листинге 4.4 приведена программа, аналогичная листингу 4.2, но ввод выполняется из файла с именем input.txt, расположенного в каталоге D:\C#. Текстовый файл можно создать с помощью любого текстового редактора, удобно использовать Visual Studio.NET.

    using System;
    using System.IO;
    namespace ConsoleApplication1
    {    class Class1
        {    static void Main()
            {
                StreamReader f = new StreamReader( "d:\\C#\\input.txt" );
                string s = f.ReadLine();
                Console.WriteLine( "s = " + s );
                
                char  c = (char)f.Read();
                f.ReadLine();
                Console.WriteLine( "c = " + c );
            
                string buf;
                buf = f.ReadLine();
                int i = Convert.ToInt32( buf );
                Console.WriteLine( i );
    
                buf = f.ReadLine();
                double x = Convert.ToDouble( buf );
                Console.WriteLine( x );
    
                buf = f.ReadLine();
                double y = double.Parse( buf );
                Console.WriteLine( y );
    
                buf = f.ReadLine();
                decimal z = decimal.Parse( buf );
                Console.WriteLine( z );
                f.Close();
            }
        }
    }

    Математические функции — класс Math

    Самая простая программа, которую можно себе представить (не считая "Hello, World!"), состоит из ввода исходных данных, вычислений по каким-то формулам и вывода результата. В выражениях, из которых состоят формулы, часто используются математические функции, например, синус или возведение в степень. Они реализованы в классе Math, определенном в пространстве имен System. Описание методов и полей класса приведено в таблице 4.1.

    Основные поля и статические методы класса Math
    Имя Описание Результат Пояснения
    Abs Модуль Перегружен | x | записывается как Abs(x)
    Acos Арккосинус double Acos(double x)
    Asin Арксинус double Asin(double x)
    Atan Арктангенс double Atan2(double x, double y) — угол, тангенс которого есть результат деления y на x
    BigMul Произведение long BigMul(int x, int y)
    Ceiling Округление до большего целого double Ceiling(double х)
    Cos Косинус double Сos(double x)
    Cosh Гиперболический косинус double Cosh(double x)
    DivRem Деление и остаток Перегружен DivRem(x, y, rem)
    E База натурального логарифма (число е) double 2,71828182845905
    Exp Экспонента double e x записывается как Exp(x)
    Floor Округление до меньшего целого double Floor(double х)
    IEEERemainder Остаток от деления double IEEERemainder(double x, double y)
    Log Натуральный логарифм double loge x записывается как Log(x)
    Log10 Десятичный логарифм double log10 x записывается как Log10(x)
    Max Максимум из двух чисел Перегружен Max(x, y)
    Min Минимум из двух чисел Перегружен Min(x, y)
    PI Значение числа $$\pi$$ double 3,14159265358979
    Pow Возведение в степень double xy записывается как Pow(x, y)
    Round Округление Перегружен Round(3.1) даст в результате 3 Round (3.8) даст в результате 4
    Sign Знак числа int Аргументы перегружены
    Sin Синус double Sin(double x)
    Sinh Гиперболический синус double Sinh(double x)
    Sqrt Квадратный корень double $$\surd$$ x записывается как Sqrt(x)
    Tan Тангенс double Tan(double x)
    Tanh Гиперболический тангенс double Tanh(double x)

    В листинге 4.5 приведен пример применения методов класса Math.

    using System;
    namespace ConsoleApplication1
    {    class Class1
        {    static void Main()
            {
                Console.Write( "Введите х: " );
                double x = double.Parse( Console.ReadLine() );
                Console.Write( "Введите y: " );
                double y = double.Parse( Console.ReadLine() );
                Console.WriteLine( "Максимум из х и y : " + Math.Max(x, y) );
                double z = Math.Pow(Math.Sin(x), 2) +  Math.Pow(Math.Sin(y), 2);
                Console.WriteLine( "Сумма квадратов синусов х и y : " + z ); 
            }
        }
    }

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

    Выражения, блоки и пустые операторы

    Любое выражение, завершающееся точкой с запятой, рассматривается как оператор, выполнение которого заключается в вычислении выражения. Частным случаем выражения является пустой оператор ; (он используется, когда по синтаксису оператор требуется, а по смыслу — нет). Примеры:

    i++;               // выполняется операция инкремента
    a *= b + c;        // выполняется умножение с присваиванием
    fun( i, k );       // выполняется вызов функции
    while( true );     // цикл из пустого оператора (бесконечный)

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

    Условный оператор if

    Условный оператор .

    (рис 4.1) Структурная схема условного оператора

    Формат оператора:

    if ( логическое_выражение ) оператор_1; [ else оператор_2; ]

    Сначала вычисляется логическое выражение. Если оно имеет значение true, выполняется первый оператор, иначе — второй. После этого управление передается на оператор, следующий за условным. Ветвь else может отсутствовать.

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

    Примеры условных операторов:

    if ( a < 0 ) b = 1;                                                  // 1
    if ( a < b  ( a > d || a == 0 ) ) b++; else { b *= a; a = 0; }     // 2
    if ( a < b ) if ( a < c ) m = a; else m = c; 
    else         if ( b < c ) m = b; else m = c;                         // 3
    if ( b > a ) max = b; else max = a;                                  // 4

    Если требуется проверить несколько условий, их объединяют знаками логических условных операций. Например, выражение в примере 2 будет истинно в том случае, если выполнится одновременно условие a < b и одно из условий в скобках. Оператор в примере 3 вычисляет наименьшее значение из трех переменных. Обратите внимание, что компилятор относит часть else к ближайшему ключевому слову if.

    В качестве примера подсчитаем количество очков после выстрела по мишени, изображенной на рис 4.2.

    (рис 4.2) Мишень

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

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {            
                Console.WriteLine( "Введите координату x" );
                double x = Convert.ToDouble(Console.ReadLine() );
    
                Console.WriteLine( "Введите координату у" );
                double y = double.Parse( Console.ReadLine() );
    
                int kol = 0;
                if      ( x * x + y * y < 1 ) kol = 2;
                else if ( x * x + y * y < 4 ) kol = 1;
                Console.WriteLine( "Результат = {0} очков", kol );
            }
        }
    }

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

    Оператор .

    (рис 4.3) Структурная схема оператора switch

    Формат оператора:

    switch ( выражение ){
        case константное_выражение_1: [ список_операторов_1 ]
        case константное_выражение_2: [ список_операторов_2 ]
        ...
        case константное_выражение_n: [ список_операторов_n ]
        [ default: операторы ]
    }

    Выполнение оператора начинается с вычисления выражения. Тип выражения чаще всего целочисленный (включая char ) или строковый. Затем управление передается первому оператору из списка, помеченному константным выражением, значение которого совпало с вычисленным. Если совпадения не произошло, выполняются операторы, расположенные после слова default, а при его отсутствии управление передается следующему за switch оператору.

    Каждая ветвь переключателя должна заканчиваться явным оператором перехода, а именно одним из операторов break, goto или return:

  • оператор break выполняет выход из самого внутреннего из объемлющих его операторов switch, for, while и do ;
  • оператор goto выполняет переход на указанную после него метку, обычно это метка case одной из нижележащих ветвей оператора switch ;
  • оператор return выполняет выход из функции, в теле которой он записан.
  • В листинге 4.7 приведен пример программы, реализующей простейший калькулятор на четыре действия.

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {
                string buf;
                double a, b, res;
    
                Console.WriteLine( "Введите первый операнд:" );
                a = double.Parse( Console.ReadLine() );
                 
                Console.WriteLine( "Введите знак операции" );
                char op = (char)Console.Read();
                Console.ReadLine();
                 
                Console.WriteLine( "Введите второй операнд:" );
                b = double.Parse( Console.ReadLine() );
    
                bool ok = true;
                switch (op)
                {
                    case '+' : res = a + b; break;
                    case '-' : res = a - b; break;
                    case '*' : res = a * b; break;
                    case '/' : res = a / b; break;
                    default  : res = double.NaN; ok = false; break;
                }
                if (ok) Console.WriteLine( "Результат: " + res );
                else    Console.WriteLine( "Недопустимая операция" );
            }
        }
    }

    Операторы цикла и передачи управления

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

    (рис 4.4) Структурные схемы операторов цикла

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

    Проверка условия продолжения цикла выполняется на каждой итерации либо до тела цикла (тогда говорят о цикле с предусловием ), либо после тела цикла (цикл с постусловием ).

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

    Цикл завершается, если условие его продолжения не выполняется. Возможно принудительное завершение как текущей итерации, так и цикла в целом. Для этого служат операторы break, continue, return и goto. Передавать управление извне внутрь цикла запрещается.

    Цикл с предусловием while

    Формат оператора:

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

    Выражение должно быть логического типа. Например, это может быть операция отношения. Если результат вычисления выражения равен true, выполняется простой или составной оператор. Эти действия повторяются до того момента, пока результатом выражения не станет значение false. После окончания цикла управление передается на следующий за ним оператор.

    Выражение вычисляется перед каждой итерацией цикла. Если при первой проверке выражение равно false, цикл не выполнится ни разу.

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

    $$\mathbf{X} = \left( \begin{array}{l} x,x<0 \\ tx,o\leqslant x <10 \\ 2t,x \geqslant 10 \end{array} \right)$$

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

    Текст программы приведен в листинге 4.8.

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {
                double Xn = -2, Xk = 12, dX = 2, t = 2, y;
                Console.WriteLine( "|   x    |    y   |" ); // заголовок таблицы
    
                double x = Xn;                                  
                while ( x <= Xk )                                
                {
                    y = t;                                          
                    if ( x >= 0  x < 10 ) y = t * x;              
                    if ( x >= 10 )          y = 2 * t;              
                    Console.WriteLine( "| {0,6} | {1,6} |", x, y ); 
                    x += dX;                                        
                }
            }
        }
    }

    Цикл с постусловием do

    Цикл с постусловием реализует структурную схему, приведенную на рис. 4.4, б, и имеет вид:

    do оператор while ( выражение );

    Сначала выполняется простой или составной оператор, образующий тело цикла, а затем вычисляется выражение (оно должно иметь тип bool ). Если выражение истинно, тело цикла выполняется еще раз, и проверка повторяется. Цикл завершается, когда выражение станет равным false или в теле цикла будет выполнен какой-либо оператор передачи управления.

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

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {
                char answer;
                do
                {
                    Console.WriteLine( "Купи слоника, а?" );
                    answer = (char) Console.Read();
                    Console.ReadLine();
                } while ( answer != 'y' );
            }
        }
    }

    Цикл с параметром for

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

    for ( инициализация; выражение; модификации ) оператор;

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

    for ( int i = 0, j = 20; ...
    int k, m;
    for ( k = 1, m = 0; ...

    Областью действия переменных, объявленных в части инициализации цикла, является цикл. Инициализация выполняется один раз в начале исполнения цикла.

    Выражение типа bool определяет условие выполнения цикла: если его результат равен true, цикл выполняется.

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

    for ( int i = 0, j = 20; i < 5  j > 10; i++, j-- ) ...

    Простой или составной оператор представляет собой тело цикла. Любая из частей оператора for может быть опущена (но точки с запятой надо оставить на своих местах!).

    Для примера вычислим сумму чисел от 1 до 100:

    int s = 0;
    for ( int i = 1; i <= 100; i++ ) s += i;

    В листинге 4.10 приведена программа, выводящая таблицу значений функции из листинга 4.8.

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {
                double Xn = -2, Xk = 12, dX = 2, t = 2, y;
                Console.WriteLine( "|   x    |    y   |");   // заголовок таблицы
    
                for ( double x = Xn; x <= Xk; x += dX )          
                {
                    y = t;                                       
                    if ( x >= 0  x < 10 ) y = t * x;           
                    if ( x >= 10 )          y = 2 * t;           
                    Console.WriteLine( "| {0,6} | {1,6} |", x, y );
                }
            }
        }
    }

    Любой цикл while может быть приведен к эквивалентному ему циклу for и наоборот. Например, два следующих цикла эквивалентны:

    Цикл for:

    for ( b1; b2; b3 ) оператор;

    Цикл while:

    b1;
    while ( b2 )
    {
        оператор;
        b3
    }

    Цикл перебора foreach

    Цикл foreach используется для просмотра всех объектов из некоторой группы данных, например, массива, списка или другого контейнера. Он будет рассмотрен в разделе "Оператор foreach".

    Операторы передачи управления

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

  • оператор безусловного перехода goto ;
  • оператор выхода из цикла break ;
  • оператор перехода к следующей итерации цикла continue ;
  • оператор возврата из функции return ;
  • оператор генерации исключения throw.
  • Эти операторы могут передать управление в пределах блока, в котором они использованы, и за его пределы. Передавать управление внутрь другого блока запрещается.

    Оператор goto

    Оператор безусловного перехода goto используется в одной из трех форм:

    goto метка;
    goto case константное_выражение;
    goto default;

    В теле той же функции должна присутствовать ровно одна конструкция вида:

    метка: оператор;

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

    Вторая и третья формы оператора goto используются в теле оператора выбора switch. Оператор goto case константное_выражение передает управление на соответствующую константному выражению ветвь, а оператор goto default — на ветвь default.

    Оператор break

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

    Для примера рассмотрим программу вычисления значения функции Sin x (синус) с точностью $$\varepsilon$$ = 10-6 с помощью бесконечного ряда Тейлора по формуле:

    y = x - x3/3! + x5/5! - x7/7! +…

    Этот ряд сходится при | x | < $$\infty$$. Точность достигается при |Rn| < ε, где Rn —остаточный член ряда, который для данного ряда можно заменить величиной Cn очередного члена ряда, прибавляемого к сумме.

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

    Для уменьшения количества выполняемых действий следует воспользоваться рекуррентной формулой получения последующего члена ряда через предыдущий: Cn+1 = Cn x T, где T — некоторый множитель. Подставив в эту формулу Cn и Cn+1, получим выражение для вычисления Т:

    $$T=1+\frac {c_{n+1}} {c_n} +\frac {2n!\cdot {x^{2(n+1)}}} {x^{2n}\cdot(2(n+1))!}=\frac {x^2}{(2n+1)(2n+2)}$$

    В листинге 4.11 приведен текст программы с комментариями.

    using System;
    namespace ConsoleApplication1
    {   class Class1
        {   static void Main()
            {
                double e = 10^-6;
                const int MaxIter = 500;     // ограничитель количества итераций
                Console.WriteLine( "Введите аргумент:" );
                double x = Convert.ToDouble( Console.ReadLine() );
    
                bool done = true;                 // признак достижения точности
                double ch = x, y = ch; 
                for ( int n = 0; Math.Abs(ch) > e; n++ )
                {
                    ch *= -x * x / (2 * n + 2 ) / ( 2 * n + 3); // очередной член ряда 
                    y += ch;                    // добавление члена ряда к сумме
                    if ( n > MaxIter ) { done = false; break; }
                }
                if ( done ) Console.WriteLine( "Сумма ряда - " + y );
                else        Console.WriteLine( "Ряд расходится" );
            }
        }
    }

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

    Оператор continue

    Оператор перехода к следующей итерации текущего цикла continue пропускает все операторы, оставшиеся до конца тела цикла, и передает управление на начало следующей итерации.

    Перепишем основной цикл листинга 4.11 с применением оператора continue:

    for ( int n = 0; Math.Abs(ch) > e; n++ )
    {
        ch *= x * x / ( 2 * n + 1 ) / ( 2 * n + 2 );
        y += ch;
        if ( n <= MaxIter ) continue;
        done = false; break;

    Оператор return

    Оператор возврата из функции return завершает выполнение функции и передает управление в точку ее вызова. Синтаксис оператора:

    return [ выражение ];

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

    Базовые конструкции структурного программирования

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

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

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

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

    .

    (рис 4.5) Базовые конструкции структурного программирования

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

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

    Обработка исключительных ситуаций

    В языке C# есть операторы, позволяющие обнаруживать и обрабатывать ошибки (исключительные ситуации), возникающие в процессе выполнения программы.

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

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

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

    Исключения генерирует либо среда выполнения, либо программист с помощью оператора throw. В таблице 4.2 приведены наиболее часто используемые стандартные исключения, генерируемые средой. Они определены в пространстве имен System. Все они являются потомками класса Exception, а точнее, потомками его потомка SystemException.

    Часто используемые стандартные исключения
    Имя Описание
    DivideByZeroException Попытка деления на ноль
    FormatException Попытка передать в метод аргумент неверного формата
    IndexOutOfRangeException Индекс массива выходит за границы диапазона
    InvalidCastException Ошибка преобразования типа
    OutOfMemoryException Недостаточно памяти для создания нового объекта
    OverflowException Переполнение при выполнении арифметических операций
    StackOverFlowException Переполнение стека

    Исключения обнаруживаются и обрабатываются в операторе try.

    Оператор try

    Оператор try содержит три части:

  • контролируемый блок — составной оператор, предваряемый ключевым словом try. В контролируемый блок включаются потенциально опасные операторы программы. Все функции, прямо или косвенно вызываемые из блока, также считаются ему принадлежащими;
  • один или несколько обработчиков исключений — блоков catch, в которых описывается, как обрабатываются ошибки различных типов;
  • блок завершения finally выполняется независимо от того, возникла ошибка в контролируемом блоке или нет.
  • Синтаксис оператора try:

    try блок [ блоки catch ] [ блок finally ]

    Отсутствовать могут либо блоки catch, либо блок finally, но не оба одновременно.

    Рассмотрим, каким образом реализуется обработка исключительных ситуаций.

  • Обработка исключения начинается с появления ошибки. Функция или операция, в которой возникла ошибка, генерирует исключение.
  • Выполнение текущего блока прекращается, отыскивается соответствующий обработчик исключения, и ему передается управление.
  • Выполняется блок finally, если он присутствует.
  • Если обработчик не найден, вызывается стандартный обработчик исключения. Обычно он выводит на экран окно с информацией об исключении и завершает текущий процесс.
  • Обработчики исключений должны располагаться непосредственно за блоком try. Они начинаются с ключевого слова catch, за которым в скобках следует тип обрабатываемого исключения. Можно записать один или несколько обработчиков в соответствии с типами обрабатываемых исключений. Блоки catch просматриваются в том порядке, в котором они записаны, пока не будет найден соответствующий типу выброшенного исключения.

    Существуют три формы записи обработчиков:

    catch( тип имя ) { ... /* тело обработчика */ }
    catch( тип )     { ... /* тело обработчика */ }
    catch            { ... /* тело обработчика */ }

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

    Вторая форма не предполагает использования информации об исключении, играет роль только его тип.

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

    Пример:

    try {
       ...  // Контролируемый блок 
    }
    catch ( OverflowException e ) { 
        ...    // Обработка исключений класса OverflowException (переполнение)
    }
    catch ( DivideByZeroException ) { 
        ... // Обработка исключений класса DivideByZeroException (деление на 0)
    }
    catch { 
        ...    // Обработка всех остальных исключений 
    }

    Если исключение в контролируемом блоке не возникло, все обработчики пропускаются.

    В любом случае, произошло исключение или нет, управление передается в блок завершения finally (если он существует), а затем — первому оператору, находящемуся непосредственно за оператором try.

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

    Оператор throw

    До сих пор мы рассматривали исключения, которые генерирует среда выполнения C#, но это может сделать и сам программист. Для генерации исключения используется оператор throw с параметром, определяющим вид исключения. Параметр должен быть объектом, порожденным от стандартного класса System.Exception. Этот объект используется для передачи информации об исключении его обработчику.

    Синтаксис оператора throw:

    throw [ выражение ];

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

    throw new DivideByZeroException();

    Здесь после слова throw записано выражение, создающее объект стандартного класса "ошибка при делении на 0" с помощью операции new.

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

    Вопросы и задания для самостоятельной работы студента

  • Какой класс используется для консольного ввода-вывода? Приведите примеры ввода и вывода переменных различных типов.
  • Какие средства языка используются для преобразования величин из символьной формы представления во внутреннюю?
  • В каких случаях оператор switch предпочтительнее оператора if?
  • Какой вид цикла лучше использовать при вводе данных?
  • Поясните смысл использования базовых конструкций структурного программирования.
  • Изучите по справочной системе состав пространства имен System.
  • Изучите свойства и методы стандартного класса Console по справочной системе.
  • Лабораторные работы

    Лабораторная работа 1. Линейные программы

    Напишите программу для расчета по двум формулам. Предварительно подготовьте тестовые примеры с помощью калькулятора.

    $$z_1=2sin^2(3\pi-2a)cos^2(5\pi+2a)$$ $$z_2=\frac 1 4 -\frac 1 4 sin(\frac 5 2 \pi-8a)$$

    Лабораторная работа 2. Разветвляющиеся вычислительные процессы

    Задание 1. Вычисление значения функции

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

    Задание 2. Попадание точки в заштрихованную область

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

    Лабораторная работа 3. Организация циклов

    Теоретический материал: глава 4, разделы "Операторы цикла", "Базовые конструкции структурного программирования".

    Задание 1. Таблица значений функции

    Вычислить и вывести на экран в виде таблицы значения функции, заданной графически (см. задание 1 лабораторной работы 2), на интервале от xнач до xкон с шагом d x. Интервал и шаг задать таким образом, чтобы проверить все ветви программы. Таблицу снабдить заголовком и шапкой.

    Задание 2. Серия выстрелов по мишени

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

    Задание 3. Ряды Тейлора

    Вычислить и вывести на экран в виде таблицы значения функции, заданной с помощью ряда Тейлора, на интервале от xнач до xкон с шагом d x с точностью $$\varepsilon$$. Таблицу снабдить заголовком и шапкой. Каждая строка таблицы должна содержать значение аргумента, значение функции и количество просуммированных членов ряда.

    $$ln\frac {x+1}{x-1}=2\sum^{\infty}_{n=0}\frac 1 {(2n+1)x^{2n+1}}=2(\frac 1 {x} +\frac 1 {3x^3}+\frac 1 {5x^5}+\cdots )\left| x \right| >1$$
    Вернуться к учебному плану