Как лучше всего обрабатывать исключения на c. Отладка и обработка исключительных ситуаций. Определение собственных классов исключений

Скачать на Телефон 13.04.2019
Скачать на Телефон

Последнее обновление: 23.10.2018

Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Например, при передачи файла по сети может неожиданно оборваться сетевое подключение. такие ситуации называются исключениями . Язык C# предоставляет разработчикам возможности для обработки таких ситуаций. Для этого в C# предназначена конструкция try...catch...finally .

Try { } catch { } finally { }

При использовании блока try...catch..finally вначале выполняются все инструкции в блоке try . Если в этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally . И затем конструкция try..catch..finally завершает свою работу.

Если же в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается, и среда CLR начинает искать блок catch , который может обработать данное исключение. Если нужный блок catch найден, то он выполняется, и после его завершения выполняется блок finally.

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

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

Class Program { static void Main(string args) { int x = 5; int y = x / 0; Console.WriteLine($"Результат: {y}"); Console.WriteLine("Конец программы"); Console.Read(); } }

В данном случае происходит деление числа на 0, что приведет к генерации исключения. И при запуске приложения в режиме отладки мы увидим в Visual Studio окошко, которое информирует об исключении:

В этом окошке мы видим, что возникло исключение, которое представляет тип System.DivideByZeroException , то есть попытка деления на ноль. С помощью пункта View Details можно посомтреть более детальную информацию об исключении.

И в этом случае единственное, что нам остается, это завершить выполнение программы.

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

Class Program { static void Main(string args) { try { int x = 5; int y = x / 0; Console.WriteLine($"Результат: {y}"); } catch { Console.WriteLine("Возникло исключение!"); } finally { Console.WriteLine("Блок finally"); } Console.WriteLine("Конец программы"); Console.Read(); } }

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

Int y = x / 0;

выполнение программы остановится. CLR найдет блок catch и передаст управление этому блоку.

После блока catch будет выполняться блок finally.

Возникло исключение! Блок finally Конец программы

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

Следует отметить, что в этой конструкции обязателен блок try . При наличии блока catch мы можем опустить блок finally:

Try { int x = 5; int y = x / 0; Console.WriteLine($"Результат: {y}"); } catch { Console.WriteLine("Возникло исключение!"); }

И, наоборот, при наличии блока finally мы можем опустить блок catch и не обрабатывать исключение:

Try { int x = 5; int y = x / 0; Console.WriteLine($"Результат: {y}"); } finally { Console.WriteLine("Блок finally"); }

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

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

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

Static void Main(string args) { Console.WriteLine("Введите число"); int x = Int32.Parse(Console.ReadLine()); x *= x; Console.WriteLine("Квадрат числа: " + x); Console.Read(); }

Если пользователь введет не число, а строку, какие-то другие символы, то программа выпадет в ошибку. С одной стороны, здесь как раз та ситуация, когда можно применить блок try..catch , чтобы обработать возможную ошибку. Однако гораздо оптимальнее было бы проверить допустимость преобразования:

Static void Main(string args) { Console.WriteLine("Введите число"); int x; string input = Console.ReadLine(); if (Int32.TryParse(input, out x)) { x *= x; Console.WriteLine("Квадрат числа: " + x); } else { Console.WriteLine("Некорректный ввод"); } Console.Read(); }

Метод Int32.TryParse() возвращает true , если преобразование можно осуществить, и false - если нельзя. При допустимости преобразования переменная x будет содержать введенное число. Так, не используя try...catch можно обработать возможную исключительную ситуацию.

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

Исключение является экземпляром класса, который был явно и неявно унаследован от базового класса System.Exception.

Переменные, объявленные в блоке try выходят из видимости, когда управление предается в блок catch или finally. По окончании блока try, даже если ничего не произошло (ошибка не возникла), управление автоматически передается в блок finally, который должен содержать инструкции для освобождения ресурсов.

При обнаружении ошибки код осуществляет генерацию исключения (создание экземпляра класса исключения) и выдает его следующим образом:

Throw new IndexOutOfRangeException(“Вы ввели: ” + userInput);

Как только компилятор встречает оператор throw внутри блока try, он немедленно ищет соответствующий блок catch.

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

Оператор throw может находиться в любом методе, вызванном во время выполнения блока try, — оно не обязано располагаться в том же самом методе, в котором определен блок try

Обработчики исключений для производного класса (IndexOutOfRangeException) должны идти раньше, чем для базового (Exception) (!)

Если обработчик catch записан как: catch { … } то это значит, что он отвечает за любой код (за любое возникшее исключение), в том числе написанным не на C# или не управляемым.

То, что в качестве исключений могут быть переданы только экземпляры класса, производного от System.Exception является требованием C#.

While(true) // бесконечный цикл { Console.Write("Выберите пункт меню от 0 до 5 или для выхода: "); string userInput = Console.ReadLine(); if (userInput == "") {Console.WriteLine("Вы нажали значт выходим..."); break; } try { int x = int.Parse(userInput); switch (x) { case 0: Console.WriteLine("Выбран пункт 0"); break; case 1: Console.WriteLine("Выбран пункт 1"); break; case 2: Console.WriteLine("Выбран пункт 2"); break; case 3: Console.WriteLine("Выбран пункт 3"); break; case 4: Console.WriteLine("Выбран пункт 4"); break; case 5: Console.WriteLine("Выбран пункт 5"); break; default: throw new IndexOutOfRangeException(); break; } } catch (IndexOutOfRangeException e) { Console.WriteLine("Неверное значение. Выберити цифру от "); } catch (FormatException e) { Console.WriteLine("Ошибка преобразования, возможно вы ввели строку..."); } catch (Exception e) { Console.WriteLine("Еще какая-то ошибка"); } catch {} // обработчик неуправляемого кода и кода на другом языке }

Свойства и методы System.Exception

if (ErrorCondition == true) { Exception myException = new ClassMyException("Help!"); myException.Source = "Название приложения"; myException.HelpLink = "myHelpFile.txt"; throw myException; }

В коде не нужно генерировать исключения от общего класса System.Exception – он не дает представления о природе ошибочного состояния (!). В иерархии существует два важных класса:

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

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

IOException (пространство имен System.IO) связаны с чтением и записью данных в файл.

StackOverflowException возникает тогда, когда участок памяти отведенный под стек, заполняется до отказа. Переполнение стека может возникнуть например в том случае, когда метод начинает рекурсивно вызвать самого себя. Обычной причиной появления EndOfStreamException является попытка чтения за границами файла. Переполнение OverflowException возникает, например при попытке привести int, содержащий -40 к типу uint в контексте checked.

Вложенные боли try используются по двум причинам:

— с целью изменения типа сгенерированного исключения (использование свойства InnerException, которое содержит ссылку на любое сгенерированное исключение);

— обработка разных исключений в разных участках кода.

Определение собственных классов исключений

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

Class LandLineSpyFoundException: ApplicationException { public LandLineSpyFoundException(string spyName) : base("шпийон"+spyName) { } public LandLineSpyFoundException(string spyName, Exception innerException) : base(spyName, innerException) { } }

Основы обработки исключений

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

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

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

Программные ошибки (bugs)

Так обычно называются ошибки, которые допускает программист. Например, предположим, что приложение создается с помощью неуправляемого языка С++. Если динамически выделяемая память не освобождается, что чревато утечкой памяти, появляется программная ошибка.

Пользовательские ошибки (user errors)

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

Исключения (exceptions)

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

По приведенным выше описаниям должно стать понятно, что структурированная обработка исключений в.NET представляет собой методику, предназначенную для работы с исключениями, которые могут возникать на этапе выполнения. Даже в случае программных и пользовательских ошибок, которые ускользнули от глаз программиста, однако, CLR будет часто автоматически генерировать соответствующее исключение с описанием текущей проблемы. В библиотеках базовых классов.NET определено множество различных исключений, таких как FormatException, IndexOutOfRangeException, FileNotFoundException, ArgumentOutOfRangeException и т.д.

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

Роль обработки исключений в.NET

До появления.NET обработка ошибок в среде операционной системы Windows представляла собой весьма запутанную смесь технологий. Многие программисты включали собственную логику обработки ошибок в контекст интересующего приложения. Например, команда разработчиков могла определять набор числовых констант для представления известных сбойных ситуаций и затем применять эти константы в качестве возвращаемых значений методов.

Помимо приемов, изобретаемых самими разработчиками, в API-интерфейсе Windows определены сотни кодов ошибок с помощью #define и HRESULT, а также множество вариаций простых булевских значений (bool, BOOL, VARIANT BOOL и т.д.). Более того, многие разработчики СОМ-приложений на языке С++ (а также VB 6) явно или неявно применяют небольшой набор стандартных СОМ-интерфейсов (наподобие ISupportErrorlnfo. IErrorlnfo или ICreateErrorlnfо) для возврата СОМ-клиенту понятной информации об ошибках.

Очевидная проблема со всеми этими более старыми методиками - отсутствие симметрии. Каждая из них более-менее вписывается в рамки какой-то одной технологии, одного языка и, пожалуй, даже одного проекта. В.NET поддерживается стандартная методика для генерации и выявления ошибок в исполняющей среде, называемая структурированной обработкой исключений (SEH - structured exception handling) .

Прелесть этой методики состоит в том, что она позволяет разработчикам использовать в области обработки ошибок унифицированный подход, который является общим для всех языков, ориентированных на платформу.NET. Благодаря этому, программист на C# может обрабатывать ошибки почти таким же с синтаксической точки зрения образом, как и программист на VB и программист на С++, использующий C++/CLI.

Дополнительное преимущество состоит в том, что синтаксис, который требуется применять для генерации и перехвата исключений за пределами сборок и машин, тоже выглядит идентично. Например, при написании на C# службы Windows Communication Foundation (WCF) генерировать исключение SOAP для удаленного вызывающего кода можно с использованием тех же ключевых слов, которые применяются для генерации исключения внутри методов в одном и том же приложении.

Доброго времени суток! Сегодня мы поговорим о том, как обрабатывать ошибки в программах, написанных на языке C#.

Сразу хочу отметить, что ошибки в программах, можно условно разделить на две группы: ошибки времени сборки программы (на этапе компиляции) и ошибки времени выполнения программы. В этом уроке разговор будет об ошибках времени выполнения!

Давайте вспомним пример из предыдущего урока, где пользователь должен был ввести с клавиатуры два целых числа. Тогда я еще просил вводить именно числа и именно целые. Почему? До потому, что если ввести в той программе не число, а просто текст, или не целое число, а дробное, то будет ошибка времени исполнения! Именно по этой причине, я сегодня рассказываю Вам про механизмы и принципы обработки ошибок в программах.

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

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

Try { //Потенциально опасный блок кода }catch(тип_ошибок) { //Обработка ошибок } //Дальнейшие операторы программы

А работает это так, если в блоке try , не произошла ошибка, то блок catch не выполняется, а управление передается дальше, т.е. выполняется следующий за блоком catch оператор. А вот если внутри блока try произошла ошибка, то выполнение этого блока прерывается, и управление передается в блок catch , а уже потом выполняются следующие за этим блоком операторы.

На практике, после одного блока try , может быть несколько блоков catch , для отдельной обработки ошибок разных типов.

Давайте модифицируем код программы из предыдущего урока, добавим контроль и обработку ошибок, которые могут возникнуть при вводе пользователем некорректном данных.

//Потенциально опасный блок try { //Приглашение пользователю ввести первое число Console.Write("Введите первое число и нажмите клавишу Enter: "); //Получение первой строки string firstString = Console.ReadLine(); //Преобразование первой строки в число int firstArg = Convert.ToInt32(firstString); //Приглашение пользователю ввести второе число Console.Write("Введите первое число и нажмите клавишу Enter: "); //Получение второй строки string secondString = Console.ReadLine(); //Преобразование второй строки в число int secondArg = Convert.ToInt32(secondString); //Сложение двух переменных int result = firstArg + secondArg; //Вывод результата Console.WriteLine("Результат сложения введенных чисел: " + result.ToString()); } //Блок обработки ошибок, SystemException - самый общий тип ошибок catch (SystemException) { Console.WriteLine("Во время выполнения программы произошла ошибка, вероятно, были введены некорректные данные!"); }

Теперь, если пользователь введет некорректные данные вместо целого числа, то произошедшая ошибка будет обработана. Если вставить этот код в метод «Main» , собрать и запустить программу, то можно будет увидеть следующее поведение:

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



Рекомендуем почитать

Наверх