Исключения и обработка исключений
Функции обработки исключений в языке C# помогают вам справиться с непредвиденными или исключительными проблемами, которые возникают при выполнении программы. При обработке исключений используются ключевые слова try , catch и finally для действий, которые могут оказаться неудачными. Это позволяет обрабатывать ошибки так, как кажется разумным, а также правильно высвобождать ресурсы. Исключения могут создаваться средой выполнения (CLR), платформой .NET , библиотеками сторонних поставщиков или кодом самого приложения. Чтобы создать исключение, используйте ключевое слово throw .
Во многих случаях исключение может создаваться не тем методом, который вызывается в вашем коде, а одним из последующих методов в стеке вызовов. Если создается такое исключение, среда CLR разворачивает стек, находит метод с блоком catch для исключений соответствующего типа и выполняет первый такой обнаруженный блок catch . Если подходящий блок catch не будет обнаружен во всем стеке вызовов, среда CLR завершает процесс и выводит сообщение для пользователя.
В этом примере метод выполняет проверку деления на нуль и перехватывает ошибку. Если не использовать обработку исключений, такая программа завершит работу с ошибкой DivideByZeroException was unhandled (Исключение DivideByZero не обработано).
public class ExceptionTest < static double SafeDivision(double x, double y) < if (y == 0) throw new DivideByZeroException(); return x / y; >public static void Main() < // Input for test purposes. Change the values to see // exception handling behavior. double a = 98, b = 0; double result; try < result = SafeDivision(a, b); Console.WriteLine("divided by = ", a, b, result); > catch (DivideByZeroException) < Console.WriteLine("Attempted divide by zero."); >> >
Общие сведения об исключениях
Исключения имеют следующие свойства.
- Исключения представляют собой типы, производные в конечном счете от System.Exception .
- Используйте блок try для выполнения таких инструкций, которые могут создавать исключения.
- Когда внутри такого блока try возникает исключение, поток управления переходит к первому подходящему обработчику исключений в стеке вызовов. В C# ключевое слово catch обозначает обработчик исключений.
- Если для созданного исключения не существует обработчиков, выполнение программы прекращается с сообщением об ошибке.
- Не перехватывайте исключение, если вы не намерены его обрабатывать с сохранением известного состояния приложения. Если вы перехватываете System.Exception , создайте его заново в конце блока catch , используя ключевое слово throw .
- Если блок catch определяет переменную исключения, ее можно использовать для получения дополнительных сведений о типе созданного исключения.
- Программа может явным образом создавать исключения с помощью ключевого слова throw .
- Объекты исключения содержат подробные сведения об ошибке, например состояние стека вызовов и текстовое описание ошибки.
- Код в блоке finally выполняется, даже если создано исключение. Используйте блок finally , чтобы высвободить ресурсы, например закрыть потоки и файлы, которые были открыты внутри блока try .
- Управляемые исключения реализованы в платформе .NET на основе структурированного механизма обработки исключений Win32. Дополнительные сведения см. в статьях Structured Exception Handling (C/C++) (Структурированная обработка исключений в C и C++) и A Crash Course on the Depths of Win32 Structured Exception Handling (Интенсивное погружение в структурированную обработку исключений на платформе Win32).
Спецификация языка C#
Дополнительные сведения см. в разделе Исключения в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
Когда нужны исключения
Тема Исключений (за и против) не нова, и уже не раз обсуждалась. Но всё же, я надеюсь, что каждый из прочитавших данную статью почерпнёт что-то новое и полезное для себя.
Причиной появления этой публикации стало нежелание больше молчать и просто смотреть, как интернет заполняют «всезнающие» программисты, которые учат новичков в нашей сфере забивать гвозди микроскопом, находя десятки аргументов в защиту своих методов! Потому, эта публикация направлена, скорее, на новичка постигающего программирование и задающего вопросы. Она является моим «правильным ответом»
Итак, что же такое Исключение (Exception)?

Исключение — это то, вероятность (возможность) чего исключается системой… это то что в условиях программы произойти не может.
Посмотрите в свой код. Можете ли вы к каждому исключению дописать «но ведь это невозможно» или «но это же исключено»? Думаю, мало кто сможет честно ответить «да». Если ваш ответ «нет» — значит часть исключений на самом деле не являются таковыми, просто вы использовали этот механизм, потому как вам показалось это более удобным. То самое «удобство» такого подхода будет рассмотрено далее.
Кто же должен определять, какие ситуации программа исключает, а какие нет?
Все исключения могут быть продиктованы только заданием или здравым смыслом, и ничем или никем иным. Исключение не определяется ни типом данных, ни их источником, хотя нередко можно услышать или прочитать в «умных» статьях: «Неправильный клиентский ввод не может быть исключением»… не верьте! Вот, просто, не надо в такое верить.
Вы можете в задании получить вполне разумное исключение неверного клиентского ввода: «С системой будут работать специально обученные менеджеры нашей компании, потому ввод неправильных данных исключён» или «Менеджер будет загружать в систему письма, которые уже прошли проверку правильности формата, поэтому формат всегда будет именно такой». Вот теперь заказчик сам вам сказал, где следует породить исключение в написанной для него программе (и ничего страшного, что это клиентский ввод). Разумеется, если речь всё же идёт о неком продукте, а не библиотеке, программу не надо просто ронять. Пускай на верхнем уровне висит обработчик исключений и превращает все необработанные исключения в ошибки, сохраняет данные и деликатно завершает выполнение (или передаёт управление основному модулю, если ошибка не в нём).
Но, как и обычно, не забываем читать между строк и уточнять задание. Если вас просят сохранить файл, уточните, возможна ли ситуация, что файл не сохранится. А если решили не уточнять, тогда не исключайте такую вероятность, а реализуйте реакцию на это событие. Ваш код не должен ни о чём умалчивать.
Или предусмотрите, или исключите.
Почему любое лишнее Исключение является вредным для кода?
Не раз сталкивался с ситуацией, когда отрицательный результат выполнения функции возвращали в виде исключения, хотя такой результат был предусмотрен самой задачей. На этот счёт слышал разные пояснения:

- Не передавать же по всей цепочке вызовов флаг завершения
- Мне надо передать данные, собранные до получения отрицательного ответа
- Функция может вернуть отрицательный результат по трём причинам. Так проще передать эту причину
- множество других доводов.
Почему же так не надо делать? Да потому, что:
- Вы не сможете смело использовать уже написанные методы в других местах, ведь они могут порождать неожиданные исключения;
Заключение
Старался не вдаваться лишние подробности и в каждое предложение вкладывать максимум смысла. Надеюсь у меня это получилось, и читая эти строки, вы думаете: «пять минут, а столько нового!» Ну что же, надеюсь это так.
Напоследок хотелось бы сказать, что не следует изворачиваться под давлением факторов, сроков и «так же проще», и писать неправильный код, зато быстро.
Совершенство и отказоустойчивость нашего кода и определяет наш профессионализм.
- совершенный код
- исключения
Зачем нужна обработка исключений?
Всю программистскую жизнь (примерно 1 год) меня мучает вопрос: зачем нужна обработка исключений? Разве трудно просто использовать условие? Частый пример для использования исключений в учебниках (в моем случае по С++) — деление на нуль. Когда пользователь вводит 2 числа и, если второе (делитель) равен нулю, то выбрасывается исключение. Но зачем нужно это делать? Почему нельзя сразу вывести текст ошибки и закончить прогармму ( или передать вызывающему модулю)?
Отслеживать
задан 26 авг 2012 в 11:09
cheremushkin cheremushkin
601 1 1 золотой знак 7 7 серебряных знаков 22 22 бронзовых знака6 ответов 6
Сортировка: Сброс на вариант по умолчанию
По своему опыту могу предложить следующий взгляд на выбор варианта с использованием исключений:
Везде, где это не обусловлено крайней необходимостью, НЕ стоит использовать исключения.
Наиболее веские на то причины:
- Исключения не предназначены для организации нормальных путей выполнения программы. Как пример, это усложняет задачу (или делает ее вообще практические невозможной) по предугадыванию пути выполнения программы (результат: branch miss predication). Сопровождать такой код становится также нетривиальной задачей.
- Снижает быстродействие. Поддержка механизма исключения обходится не бесплатно, т.к. компиляторам приходится создавать доп. проверки и перехватчики исключений, поддерживать сами объекты исключений и пр. Это может особенно неожиданно «выстрелить» на какой-нибудь экзотической платформе в неподходящий момент.
- Заранее возлагает на пользователя требование, чтобы он использовал механизм исключений для работы с вашим кодом.
- Меж-модульное (т.е. за пределы бинарного модуля, например, DLL) прохождение исключений — плохая практика. Дело в том, что пользователь вместо перехвата по ссылке может использовать перехват объекта исключения по значению, что приведет к тому, что будет создаваться копия оригинального объекта исключения у клиента в совсем другом окружении. Кроме неэффективности, это еще и небезопасно.
- Отсутствие понимания того, как этот механизм работает. Банально, но весьма жизненно.
«Ну а когда же тогда их стоит использовать?»
Исключения стоит использовать в следующих случаях:
- Заранее известно, что ни в этом месте кода, ни в непосредственном клиенте невозможно корректно обработать серьезную ошибку. Тогда остается послать исключение «куда-повыше», в надежде, что «там» знают, что делать.
- При необходимости уведомить клиента о невозможности корректно создать (инициализировать) объект — путем генерировании исключения в конструкторе. С другой стороны, можно инициализации объект с помощью, например, функции Init() , возвращающей код ошибки, и предоставить клиенту возможность самостоятельно решить, использовать ли ему исключения.
Отслеживать
ответ дан 26 авг 2012 в 16:18
vladimir_ki vladimir_ki
1,923 10 10 серебряных знаков 12 12 бронзовых знаков
На пункты 4 и 5 нужно обратить особое внимание.
26 авг 2012 в 17:03плюс к ответу. пример — работа с аппаратной частью, особенно в драйверах, когда бывают случаи «отказа» оборудования.
27 авг 2012 в 3:24
Исключения являются одним из наиболее привлекательных средств языка. Нужно только научиться правильно ими пользоваться. Вопреки расхожему мнению, применение исключений намного шире, чем банальная обработка ошибок. Особенно удобно использовать их для передачи управления (а, возможно, и данных) сразу через несколько уровней в иерархии вызовов. Или даже не вызовов, а просто вложенных друг в друга блоков кода. Само название как бы намекает нам на то, что исключения предназначены для работы с исключительными ситуациями, которые вовсе не обязаны быть ошибочными. Они лишь должны быть исключительными по сравнению с обычным ходом выполнения программы. Настолько исключительными, чтобы не имело смысла каждый раз делать упреждающую проверку.
Отслеживать
ответ дан 26 авг 2012 в 11:46
2,949 1 1 золотой знак 12 12 серебряных знаков 23 23 бронзовых знака@Shamov, «Особенно удобно использовать их для передачи управления. «, а longjmp() не балуетесь? Вот с «Они лишь должны быть исключительными по сравнению. » согласен.
26 авг 2012 в 12:39
Да, отличное средство, если надо написать код, в котором никто никогда не разберется.
26 авг 2012 в 13:01я когда-то писал парсер для самодельного языка. В модели с исключениями получилось очень красиво — парсер себе парсит рекурсивно. Если что то не так — бросает исключение. И оно по всей цепочке подымается вверх. А это десятки вложенных вызовов могут быть. Как бы делать этот код с кодами возвратов — смутно представляю. Но вот делать код, который бросает исключения на каждом шаге — это уже не то. В некоторых компиляторах генерится такой код для исключений, что они могут убить всю производительность.
26 авг 2012 в 15:07
В одной конторе, где я работал, использовалась очень сложная самодельная библиотека, полностью построенная на кодах возврата. Множество этих кодов росло в геометрической прогрессии. Очень часто случалось такое, что системщики выпускали очередной релиз, и в changelog’е красовался один единственный пункт: добавлены новые ошибки. Имелись в виду новые коды возврата. Мы всё время радостно улыбались этой двусмысленной фразе. А однажды им пришлось добавить специальную функцию — erroneous_error(), которая возвращала true, если предыдущий код ошибки не следует считать ошибкой.
Что такое исключения в программировании
Рассказ об ошибках, которые можно предусмотреть заранее.
Большинство наших проектов устроены так: когда во время работы программы возникает какая-то ошибка, то программа аварийно завершается. Иногда при этом она выдаёт сообщение об ошибке. Кажется, что это нормальная ситуация, но на самом деле большинство ошибок можно предусмотреть и научить программу правильно с ними работать. Для этого нам нужны обработчики ошибок.
Что такое обработчик ошибок
Чтобы программа знала, что делать, если возникла какая-то ошибка, используют обработчики исключительных ситуаций, или, проще говоря, обработчики исключений. Смысл такой:
- Мы заранее прикидываем, в каком месте и почему может возникнуть ошибка.
- Пишем в этом месте специальный код, который предупредит компьютер, что это плановая ошибка и что у нас уже есть решение, мол, всё под контролем.
- Компьютер применяет наше решение и переходит к следующей команде.
- Программа не падает, не завершается с ошибкой, а продолжает работать.
Такие обработчики есть не в каждом языке программирования, но большинство современных языков это умеют делать.
Пример программы без обработчика исключений
Допустим, у нас в программе на Python предусмотрено чтение данных из файла и есть такой код:
Но если на диске этого файла не будет, то компьютер, когда дойдёт до этой строчки, выдаст ошибку:

Давайте нарисуем это в виде простой схемы:

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

try: file = open("myfile2.txt") except FileNotFoundError: print("Файл не найден, создаю новый") file = open("myfile2.txt","a")Команда try — это начало нашего обработчика исключений. Она говорит компьютеру: «Попробуй выполнить вот эту команду, а мы посмотрим, что произойдёт».
Except — это какую ошибку мы ожидаем здесь увидеть. В нашем случае мы хотим предусмотреть случай, что такого файла нет, поэтому пишем стандартную ошибку для такой ситуации.
Сравните текст этой ошибки с тем, что нам выдал компьютер в предыдущем разделе.
В других языках конструкция обработчика исключений может выглядеть по-другому, но смысл тот же: говорим компьютеру, какую команду нужно выполнить и что делать, если появилась конкретная ошибка.
Когда что-то не предусмотрено — будет ошибка
Если программе в этом блоке встретится другая ошибка, не та, которую мы предусмотрели, то программа остановится и всё перестанет работать. Например, вот какие ошибки могут возникнуть с файлом:
- файл есть на диске, но к нему нет прав доступа;
- файл занят другой программой;
- сам диск повреждён и данные не читаются.
Во всех этих случаях программа сломается, потому что мы не предусмотрели эти ситуации:

Получается, всё нужно делать с обработкой исключений?
Нет, и вот почему:
- Обработка исключений занимает лишнее время, поэтому программа с ними работает медленнее, чем без них.
- Не всё можно предусмотреть. Если разработчик не знает, что здесь может быть ошибка, то и предусмотреть он это тоже не сможет.
- Конструкции с обработчиками делают код менее читаемым и понятным для человека. Ему нужно будет держать в голове точку начала обработки, понять, как обработка влияет на программу в целом, и выяснить, что будет с программой после работы обработчика ошибок.
Конечно, есть места в коде, которые лучше делать с обработкой ошибок: работа с файлами, сетевые запросы или получение внешних данных. Но запихивать исключения на каждую команду в программе точно не стоит.