Васильев А.Н. Основы программирования на C#. Васильев А. Н. Программирование
Скачать 5.54 Mb.
|
223 и в нее же записаны значения переменных x, y и z. Например, значение переменной x можно узнать по имени этой переменной с помощью инструкции или с помощью выражения *(int*)symbs[0]. В последнем случае следует учесть, что symbs[0] есть указатель напер- вые два байта из области памяти, выделенной под переменную x. Выражение с явным приведением типа есть указатель на все четыре байта области, выделенной под переменную x. А выражение это значение в области памяти, выделенной под переменную x. Что касается значения переменной x, тов первые два байта этой переменной записывался код символа ƍAƍ (это число 65 ), а в два других байта записан код символа ƍBƍ (это число 66). Если бинарный код в четырех байтах интерпретировать как значение типа int , то с учетом того, что смещение на два однобайтовых блока означает умножение на 2 16 = 65 536, то получаем 65 + 66 × 65 536 = 4 325 441. Ну а значение выражения *symbs[0] — это символ ƍAƍ, записанный впервые два байта области памяти, выделенной под переменную x. { i НАЗ А МЕТКУ Читателю предлагается самостоятельно объяснить результат вычисления прочих подобных выражений, использованных в программе. Резюме Какой дурак на Плюке правду думает Абсурд! из к/ф «Кин-дза-дза» • Указатель представляет собой переменную, значением которой является адрес другой переменной. При объявлении указателя указывается тип переменной, на которую может ссылаться указатель, и звездочка * . Указатели объявляют для значений базовых типов Получить адрес переменной можно, указав перед именем переменной символ & . Если перед именем указателя поставить звездочку * , то получим доступ к значению, записанному по адресу, хранящемуся в указателе Правила адресной арифметики определяют, какие операции и как могут выполняться с указателями. В частности, указатели можно сравнивать, указатели можно вычитать, индексировать, а также можно прибавлять к указателю целое число и вычитать из указателя целое число Глава 4 224 • Если структура не содержит членов ссылочного типа, то для экземпляра структуры можно объявить указатель Блок fixed используется в случае, если необходимо обеспечить безопасное использование области памяти, на которую ссылается указатель Имя массива является указателем на начальный элемент массива. Элементы массива в памяти расположены подряд, один за другим С помощью указателей можно получить доступ к символам текста в текстовом объекте. Методы использования указателей при работе с текстом фактически означают работу с символьным массивом Можно объявить указатель, который ссылается на указатель. Это называется многоуровневой адресацией Блок программного кода, в котором используются указатели, помечается ключевым словом unsafe . Соответствующее приложение также необходимо компилировать в режиме использования небезопасного кода. Задания для самостоятельной работы Капу, капу жми Нажал уже... из к/ф «Кин-дза-дза» 1. Напишите программу, в которой объявляется переменная типа int. В первый и последний байты области памяти, выделенной под эту переменную, запишите число 1, а в два внутренних байта запишите символ ƍAƍ. 2. Напишите программу, в которой объявляется переменная типа double . В область памяти, выделенную под эту переменную, запишите такие значения в первый байт запишите значение 1, в следующие два байта запишите символ ƍAƍ, в следующие четыре байта запишите значение ив оставшийся восьмой байт запишите значение 3. 3. Напишите программу, в которой объявляются три переменные типа int . Первые две переменные получают случайные значения. Область памяти, выделенная под третью переменную, заполняется следующим образом первые два байта копируются из первой переменной, а следующие два байта копируются из второй переменной. Предложите способ проверки корректности вычислений Указатели Напишите программу, в которой объявляется переменная типа int. Переменной в качестве значения присваивается случайное число. Затем с использованием указателей) выполняется циклический сдвиг байтов первый байт становится вторым, второй байт становится третьим, третий байт становится четвертым, а четвертый байт становится первым. Предложите способ проверки правильности вычислений Напишите программу, содержащую структуру с символьными целочисленным полями, а также метод, который при вызове отображает значения этих полей. Создайте указатель на экземпляр структуры и с помощью этого указателя присвойте значения полям экземпляра структуры и вызовите метод из экземпляра Напишите программу, в которой есть класс с целочисленным полем. На основе класса создайте объект. С помощью указателей запишите впервые два байта области памяти, выделенной подполе объекта, символа в два следующие байта — символ ƍBƍ. Проверьте значение поля и объясните результат Напишите программу, в которой создается массив из двух элементов типа int. Заполните этот массив по байтам последовательностью натуральных чисел. Проверьте значения байтов, значения элементов целочисленного массива и объясните результат Напишите программу, в которой объявляется переменная типа double и одномерный массив, элементы которого являются указателями назначение типа byte. В этот массив записываются адреса однобай- товых блоков из области памяти, выделенной под переменную, но через один адрес первого блока, третьего, пятого итак далее. Используя этот массив указателей путем их индексирования, следует заполнить все байты в области памяти, выделенной под переменную, последовательностью натуральных чисел и проверить результат Напишите программу, в которой создается текстовый объекта потом с помощью указателей непосредственно в этом объекте выполняется инверсия текста порядок символов заменяется на противоположный Напишите программу, в которой объявляется переменная типа int, а также указатель на указатель назначение типа char. С помощью этого указателя нужно записать впервые два байта в области памяти, выделенной под переменную типа int, символа в следующие два байта символ ƍBƍ. Проверьте значение целочисленной переменной и объясните результат Глава ОБРАБОТКА ИСКЛЮЧЕНИЙ Товарищ эцилопп! Они у нас вещи украли Поймайте их! из к/ф «Кин-дза-дза» Эта глава посвящена обработке исключительных ситуаций. Мы узнаем, как создавать программные коды, устойчивые к возникновению непредвиденных ситуаций. Среди наших ближайших задач есть такие знакомство с основными принципами обработки исключений применение конструкции try-catch для перехвата исключений знакомство с основными классами исключений использование нескольких блоков для перехвата исключений разных типов использование вложенных блоков и блока finally; • искусственное генерирование исключений создание классов пользовательских исключений применение инструкций checked и Как обычно, все рассматриваемые темы иллюстрируются специально подобранными примерами. Принципы обработки исключений Здравствуйте! Мы — наши туристы, отстали от группы. Подбросьте нас до города, а там мы как-нибудь уже сами Переводи. из к/ф «Кин-дза-дза» Программы создаются для того, чтобы они работали эффективно, быстро и надежно. К сожалению, очень сложно обезопасить программный Обработка исключений 227 код от возможных ошибок. Здесь имеются ввиду не ошибки, связанные с некорректным синтаксисом или неправильно реализованным алгоритмом. Имеются ввиду ошибки, которые возникают в процессе выполнения программы и появление которых потенциально невозможно или сложно предвидеть. Пример подобной ситуации представить легко. Допустим, при выполнении программы пользователь должен ввести целое число. Значение, которое вводит пользователь (в консольном окне или поле ввода, программа считывает и пытается интерпретировать как целое число. Если же пользователь ввел нецелое число, а что-либо другое, возникает ошибка. Причем эта ошибка не связана с некорректным программным кодом. Она обусловлена действиями пользователя. Предусмотреть в программе, что именно введет пользователь, не получится. Поэтому каждый раз, когда такая программа запускается на выполнение, потенциально может возникнуть ошибка. Проблема в том, что если ошибка возникает, то программа завершает выполнение (и появляется сообщение об ошибке. Это очень неудобно. Подобных ситуаций пытаются избегать. И возможности для этого имеются НАЗ А МЕТКУ Напомним, что очень краткое и поверхностное знакомство с обработкой исключений у нас состоялось ранее, впервой части книги, в главе Чтобы понять, как программа может реагировать на возникновение ошибки, имеет смысл хотя бы кратко узнать, что происходит, если случается ошибка. А происходит следующее. Если при выполнении некоторой команды возникает ошибка, то выполнение этой команды прекращается. Ноне все так просто. Еще автоматически создается объект, который содержит информацию о возникшей ошибке. Саму ошибку называют исключением или исключительной ситуацией, а объект называется объектом исключения или объектом ошибки. Этот объект передается в программу для обработки. Иногда говорят, что объект исключения вбрасывается в программу. Далее возможны два варианта. Вариант первый в программе предусмотрена обработка исключений. Если так, то исключение обрабатывается, и программа продолжает работу. Вариант второй в программе не предусмотрена обработка исключительных ситуаций. В таком случае используется механизм обработки по умолчанию выполнение программного кода прекращается, и выводится сообщение об ошибке Глава 5 228 { i НАЗ А МЕТКУ Как отмечалось выше, при возникновении ошибки создается объект. Объекты, как мы знаем, создаются на основе классов. В языке C# имеется иерархия классов, каждый из которых соответствует ошибке определенного типа. При возникновении ошибки объект исключения создается на основе класса, который соответствует типу возникшей ошибки. Итак, мы можем научить программу реагировать на возникновение ошибки. Главное преимущество в наличии системы перехвата и обработки исключений в том, что программа не завершает работу в аварийном режиме. Хотя, откровенно говоря, есть и другие позитивные моменты. Даже больше — иногда они настолько положительны, что приходится генерировать исключения специально. Такой способ программирования мы также обсудим. Использование конструкции try-catch — Они будут на карачках ползать, а мы на них плевать А зачем Как зачем Удовольствие получать. из к/ф «Кин-дза-дза» Мы уже сталкивались с конструкцией try-catch. В самом простом варианте эта конструкция используется следующим образом. Программный код, при выполнении которого может возникнуть ошибка, заключается в отдельный блок (выделяется фигурными скобками, помеченный ключевым словом try. Данный блок будем называть блоком, а содержащийся в нем код — контролируемым кодом. После блока следует блок. В этом блоке размещается программный код, предназначенный для обработки исключений. Общий шаблон использования конструкции try-catch такой (жирным шрифтом выделены ключевые элементы шаблона // Ʉɨɧɬɪɨɥɢɪɭɟɦɵɣ ɤɨɞ } Обработка исключений // Ʉɨɞ ɞɥɹ ɨɛɪɚɛɨɬɤɢ Если при выполнении контролируемого программного кода в блоке ошибок не было, то блок игнорируется. По завершении выполнения кода из блока начинают выполняться команды после конструкции try-catch. Если же при выполнении команд в блоке возникла ошибка, то выполнение команд в блоке прекращается и начинают выполняться команды в блоке. По завершении выполнения блока начинают выполняться команды после конструкции. Напомним, что это наиболее простая схема перехвата и обработки исключений. Перехватываются исключения всех типов (все виды ошибок, которые, в принципе, можно перехватить и обработать, а объект исключения, который создается при возникновении ошибки, никак не используется. Если мы хотим использовать при обработке ошибки объект исключения, то необходимо немного видоизменить способ описания блока. Правда изменения минимальны после ключевого слова catch вкруг- лых скобках указывается имя класса, определяющего тип перехватываемых ошибок, а также формальное обозначение для объекта исключения. То есть используется следующий шаблон описания блока ɨɛɴɟɤɬ){ // Ʉɨɞ ɞɥɹ ɨɛɪɚɛɨɬɤɢ Если в качестве типа для объекта исключения указать класс Exception, тов блоке будут перехватываться исключения всех типов, и при этому нас будет возможность использовать объект исключения ПОДРОБНОСТИ bЕсли в блоке указать класс перехватываемых ошибок, то этот блок будет обрабатывать ошибки данного класса и всех его подклассов. Класс Exception является базовым для прочих классов исключений. Поэтому если указать тип исключения как Exception , тов соответствующем блоке будут перехватываться исключения всех типов Глава Небольшой пример, в котором при обработке исключительной ситуации используется объект ошибки, представлен в листинге Листинг 5.1. Использование конструкции try-catch using System; // Ʉɥɚɫɫ ɫ ɝɥɚɜɧɵɦ ɦɟɬɨɞɨɦ: class UsingTryCatchDemo{ // Ƚɥɚɜɧɵɣ ɦɟɬɨɞ: static void Main(){ // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( Ǝɇɚɱɢɧɚɟɬɫɹ ɜɵɩɨɥɧɟɧɢɟ ɩɪɨɝɪɚɦɦɵƎ); // Ɉɛɴɹɜɥɟɧɢɟ ɩɟɪɟɦɟɧɧɵɯ: int A=10,B=0; // Ɉɬɨɛɪɚɠɟɧɢɟ ɡɧɚɱɟɧɢɣ ɩɟɪɟɦɟɧɧɵɯ: Console.WriteLine( Ǝɉɟɪɟɦɟɧɧɚɹ A=Ǝ+A); Console.WriteLine( Ǝɉɟɪɟɦɟɧɧɚɹ B=Ǝ+B); // Ʉɨɧɬɪɨɥɢɪɭɟɦɵɣ ɤɨɞ: try{ // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( Ǝȼɵɱɢɫɥɹɟɬɫɹ ɜɵɪɚɠɟɧɢɟ A/BƎ); // ȼɵɱɢɫɥɟɧɢɟ ɜɵɪɚɠɟɧɢɹ (ɩɨɩɵɬɤɚ ɞɟɥɟɧɢɹ ɧɚ ɧɨɥɶ): Console.WriteLine( ƎɊɟɡɭɥɶɬɚɬ: Ǝ+A/B); // Ɉɬɨɛɪɚɠɟɧɢɟ ɜɵɪɚɠɟɧɢɹ (ɤɨɦɚɧɞɚ ɧɟ ɜɵɩɨɥɧɹɟɬɫɹ): Console.WriteLine( Ǝȼɵɱɢɫɥɟɧɢɹ ɡɚɤɨɧɱɟɧɵƎ); } // Ȼɥɨɤ ɨɛɪɚɛɨɬɤɢ ɢɫɤɥɸɱɟɧɢɹ: catch(Exception e){ // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( Ǝȼɨɡɧɢɤɥɚ ɨɲɢɛɤɚ!Ǝ); // Ɍɢɩ ɨɲɢɛɤɢ: Console.WriteLine( ƎɌɢɩ ɨɲɢɛɤɢ: Ǝ+e.GetType().Name); // Ɉɩɢɫɚɧɢɟ ɨɲɢɛɤɢ: Обработка исключений Console.WriteLine( ƎɈɩɢɫɚɧɢɟ: Ǝ+e.Message); // ɂɫɬɨɱɧɢɤ ɨɲɢɛɤɢ (ɩɪɨɝɪɚɦɦɚ): Console.WriteLine( Ǝɉɪɨɝɪɚɦɦɚ: Ǝ+e.Source); // Ɇɟɬɨɞ, ɜ ɤɨɬɨɪɨɦ ɩɪɨɢɡɨɲɥɚ ɨɲɢɛɤɚ: Console.WriteLine( ƎɆɟɬɨɞ: Ǝ+e.TargetSite.Name); // Ɇɟɫɬɨ (ɫɬɪɨɤɚ) ɫ ɨɲɢɛɤɨɣ: Console.WriteLine( ƎɆɟɫɬɨ ɨɲɢɛɤɢ: Ǝ+e.StackTrace); } // Ɉɬɨɛɪɚɠɟɧɢɟ ɫɨɨɛɳɟɧɢɹ: Console.WriteLine( Ǝɉɪɨɝɪɚɦɦɚ ɡɚɜɟɪɲɢɥɚ ɜɵɩɨɥɧɟɧɢɟƎ); Результат выполнения программы представлен ниже: Результат выполнения программы (из листинга 5.1) ɇɚɱɢɧɚɟɬɫɹ ɜɵɩɨɥɧɟɧɢɟ ɩɪɨɝɪɚɦɦɵ ɉɟɪɟɦɟɧɧɚɹ A=10 ɉɟɪɟɦɟɧɧɚɹ B=0 ȼɵɱɢɫɥɹɟɬɫɹ ɜɵɪɚɠɟɧɢɟ A/B ȼɨɡɧɢɤɥɚ ɨɲɢɛɤɚ! Ɍɢɩ ɨɲɢɛɤɢ: DivideByZeroException Ɉɩɢɫɚɧɢɟ: ɉɨɩɵɬɤɚ ɞɟɥɟɧɢɹ ɧɚ ɧɭɥɶ. ɉɪɨɝɪɚɦɦɚ: Listing05_01 Ɇɟɬɨɞ: Main Ɇɟɫɬɨ ɨɲɢɛɤɢ: ɜ UsingTryCatchDemo.Main() ɜ D:\Book\Listing05_01\ Program.cs: ɫɬɪɨɤɚ 18 ɉɪɨɝɪɚɦɦɚ ɡɚɜɟɪɲɢɥɚ Программа достаточно простая. Вся интрига закручивается в главном методе. Вначале выполнения программы в консольном окне появляется сообщение соответствующего содержания. Также объявляются две целочисленные переменные переменная A со значением 10 и переменная B Глава со значением 0. Значения переменных отображаются в консольном окне. После этого начинает выполняться блок с контролируемым кодом. Сначала командой Console.WriteLine( Ǝȼɵɱɢɫɥɹɟɬɫɹ ɜɵɪɚ- ɠɟɧɢɟ A/BƎ) выводится сообщение о вычислении частного двух целочисленных значений. При выполнении команды Console. WriteLine( ƎɊɟɡɭɥɶɬɚɬ: Ǝ+A/B) предпринимается попытка вычислить значение выражения A/B. Но при нулевом значении переменной возникает ошибка, связанная с делением на ноль. Поэтому дальше команды в блоке не выполняются (то есть до выполнения команды Console.WriteLine( Ǝȼɵɱɢɫɥɟɧɢɹ ɡɚɤɨɧɱɟɧɵƎ) в блоке дело не доходит. Вместо этого создается объект исключения, и этот объект передается для обработки в блок ПОДРОБНОСТИ bВыполнение команды начинается с вычисления значения аргумента, переданного методу. При вычислении значения аргумента вычисляется выражение A/B . Как отмечалось выше, в этом месте возникает ошибка. Поэтому выполнение команды Console. WriteLine(" Ɋɟɡɭɥɶɬɚɬ: "+A/B) прекращается, и никакого сообщения в консольном окне не будет. В блоке объект (точнее, ссылка на объект) исключения обозначен как e. Именно через переменную e в теле блока мы обращаемся к объекту исключения. Там командой Console.WriteLine( Ǝȼɨɡɧɢɤ- ɥɚ ɨɲɢɛɤɚ!Ǝ) отображается сообщение о возникновении ошибки, ада- лее с использованием объекта исключения e в консольном окне отображаются некоторые дополнительные сведения о том, что произошло. У объекта исключения e есть метод GetType(). Метод результатом возвращает объект класса Type, содержащий информацию о типе объекта e. Этот объект класса Type имеет текстовое свойство Name, значение которого название класса, к которому относится объект исключения e. Поэтому значение выражения e.GetType().Name — это название класса ошибки, которая произошла. Как следует из результата выполнения программы, произошла ошибка класса DivideByZeroException из пространства имен Текстовое свойство Message объекта e содержит описание ошибки. В данном случае значением свойства является текст Ǝɉɨɩɵɬɤɚ Обработка исключений ɧɚ ɧɭɥɶ.Ǝ. Далее, значением свойства Source является текст с названием приложения или объекта, вызвавшего исключение. Значением свойства TargetSite является объект класса MethodBase. Объект содержит информацию о методе, вызвавшем исключение. Название метода можно получить с помощью свойства Name. Таким образом, значением выражения e.TargetSite.Name является текст ƎMainƎ с названием главного метода, в котором произошла ошибка. Значением свойства StackTrace является текст с указанием места (вплоть до номера строки кода, в котором возникла ошибка. На этом выполнение блока завершается. Работа программы завершается выполнением команды Console.WriteLine( Ǝɉɪɨɝɪɚɦɦɚ ɡɚɜɟɪɲɢɥɚ ɜɵɩɨɥɧɟɧɢɟƎ). q ПОДРОБНОСТИ bПри делении на ноль произошла ошибка класса DivideByZeroException , который является производным от класса. Класс ArithmeticException является производным от класса SystemException , который, в свою очередь, является производным от класса Exception . Вопи- сании блока объект ошибки e указан как относящийся к классу Exception . Свойства и методы объекта e , использованные в программе, — это свойства и методы объекта класса Exception . На самом деле объект ошибки относится к другому классу (имеется ввиду класс DivideByZeroException ). Но поскольку класс DivideByZeroException является производным от класса Exception , то, во-первых, ошибки класса DivideByZero Exception перехватываются в блоке, и, во-вто- рых, свойства и методы, которые есть у объекта класса Exception , также есть и у объекта класса Стоит также заметить, что если мы в программном коде изменим значение переменной B сна (вместо инструкции B=0 следует использовать инструкцию B=2), то результат выполнения программы будет таким, как показано ниже: |