Лекция 7.pptx.pptx
- Количество слайдов: 59
Лекция 7
Исключения
Что такое исключения Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда и вовсе невозможно. Такие ситуации называются исключениями.
Виды ошибок Штатные ошибки, обработка которых является частью бизнес логики. Пример: пользователь ввел неверный пароль – отобразить соответствующее сообщение и попросить ввести снова.
Виды ошибок Непредвиденные ошибки, это ошибки, появление которых мы не ожидаем. В случае таких ошибко мы говорим пользователю «у нас что-то сломалось» , В этом случае речь идет о непроверяемых исключениях.
Проверяемые и непроверяемые исключения В шарпе нет проверяемых. “Если реализовать этот механизм так, как он был реализован в Java, то, я думаю, вы просто размениваете один набор проблем на другой. ”
Как обрабатывать исключения if-else try-catch
Что-когда использовать? Если событие происходит регулярно в рамках нормальной работы программы, используйте программный метод проверки на наличие ошибок. В случае программной проверки на наличие ошибок при возникновении исключения выполняется больше кода.
Что-когда использовать? Если событие происходит редко, то есть событие носит действительно исключительный характер и указывает на ошибку, то используйте обработку исключений. При использовании обработки исключений в обычных условиях выполняется меньше кода.
Try-catch-finally В блок try помещается код, выполнение которого мы считаем потенциально опасным. Блоки catch обрабатывают возможные виды ошибок. Блок finally используется для очистки ресурсов, если это необходимо.
Примеры try { x = x / y; } catch(Divide. By. Zero. Exception ex) { Console. Write. Line("Делить на 0 нельзя!"); }
Пример try { x = x / y; } finally { Console. Write. Line("x = " + x); }
Пример try { x = x / y; } catch(Divide. By. Zero. Exception ex) { Console. Write. Line("Делить на 0 нельзя!"); } finally { Console. Write. Line("x = " + x); }
Блоки catch Несколько блоков catch с различными фильтрами исключений могут быть указаны последовательно. Блоки catch проверяются сверху вниз в коде, однако для каждого вызванного исключения выполняется только один блок catch. Выполняется первый блок catch, указывающий точный тип или базовый класс созданного исключения.
Блоки catch Если нет блока catch, который определяет соответствующий фильтр исключений, выбирается блок catch, в котором не выбран фильтр, если таковой имеется в операторе. Очень важно, чтобы первыми были размещены блоки catch с самыми конкретными (т. е. , самыми производными) типами исключений.
Пример try { x = x / y; } catch (Divide. By. Zero. Exception ex) { Console. Write. Line("Делить на 0 нельзя!"); } catch (Exception ex) { Console. Write. Line("Случилась какая-то ошибка"); }
Рекомендации В блоках catch следует всегда упорядочивать исключения от более конкретных к более общим.
Рекомендации Разрабатывайте классы таким образом, чтобы исключение никогда не создавалось при нормальном использовании. В большинстве случаев следует использовать предопределенные типы исключений. NET Framework.
Рекомендации Создавайте новый класс исключений, только если предопределенное исключение не подходит. Вызывайте исключение Invalid. Operation. Exception, если значение свойства или вызов метода не соответствуют текущему состоянию объекта.
Рекомендации Вызывайте исключение Argument. Exception или класс, производный от Argument. Exception, если передаются недопустимые параметры.
Фильтры исключений В C# 6. 0 (Visual Studio 2015) была добавлена такая функциональность, как фильтры исключений. Они позволяют обрабатывать исключения в зависимости от определенных условий.
Пример try { int result = x / y; } catch(Exception ex) when (y==0) { Console. Write. Line("y не должен быть равен 0"); } catch(Exception ex) { Console. Write. Line(ex. Message); }
Иерархия исключений Exception является базовым классом для исключений. Некоторые классы исключений наследуют непосредственно от Exception, в том числе классы Application. Exception и System. Exception. Эти два класса образуют основу для почти всех исключений среды выполнения.
System. Exception Среда выполнения создает соответствующий производный класс от класса System. Exception при возникновении ошибок. Эти ошибки возникают из завершившихся неудачно проверок во время выполнения (например, массив ошибок "вне диапазона") и могут возникать при выполнении любого метода.
System. Exception Наиболее тяжелые исключения, которые создаются средой выполнения или при наличии неустранимых условий, включают Execution. Engine. Exception, Stack. Overflow. Exception и Out. Of. Memory. Exception.
Проброс исключений “Чаще всего код не должен заниматься обработкой исключений - это не его дело. Он должен выполнить некие действия, если получит исключения, пробросит его вызывающему коду, которому можно доверить эту самую обработку (согласно Single responsibility (SOLID), класс не должен брать на себя более одной обязанности). ”
Стек вызовов Это стек, содержащий упорядоченный, по очередности вызова, список функций. Функция 3 Функция 2 Функция 1 Главная функция
Проброс исключений Во многих случаях исключение может инициироваться не методом, вызванным непосредственно кодом, а другим методом, расположенным ниже в стеке вызовов.
Проброс исключений “В хорошо написанном приложении отношение конструкций tryfinally к trycatch примерно 10 к 1. ” “вы защищаете себя от исключений, а не обрабатываете их. Обработку исключений вы реализуете где-то в другом месте. ”
Проброс исключений “Я думаю, что вся концепция «обработки» исключений слегка напоминает игру для дураков. Я, наверное, могу посчитать на пальцах одной руки количество случаев, когда я был действительно в состоянии обработать специфический тип исключения и сделать в обработчике что-то интеллектуальное. В 99% случаев ты должен ловить или всё или ничего. Когда выбрасывается исключение любого типа, восстановите стабильное состояние и затем либо продолжайте, либо прерывайте исполнение программы. ”
Как получить стек вызовов? У класса Exception есть свойство Stack. Trace, которое получает строковое представление непосредственных кадров в стеке вызова. Оно перечисляет вызовы методов в обратном хронологическом порядке, то есть, самый последний вызов метода описан первым.
Свой класс ошибок Создаем свой класс и наследуемся от класса Exception Переопределяем все, что нам нужно Определить конструкторы класса исключения. Как правило классы исключений имеют один или несколько конструкторов
Виды конструкторов Exception(), который использует значения по умолчанию для инициализации свойства объекта исключения. Exception(String), который инициализирует новый объект исключения с заданным сообщением об ошибке. Exception(String, Exception), который инициализирует новый объект исключения с указанной ошибкой сообщением и внутренним исключением.
Пример Наш класс
Пример Встроили в код обработки
Пример Через оператор throw мы сами можем генерировать ошибки
Логирование
Логи, зачем они нужны? Основные цели, ради которых существуют логи: сказать, что же делает система прямо сейчас, не прибегая к помощи отладчика, т. к. это иногда не оправдано; провести «расследование» обстоятельств, которые привели к определенному состоянию системы (например, падению или багу); проанализировать, на что тратится больше времени/ресурсов, т. е. профилирование.
Простые решения Создать метод, который бы дозаписывал в файл события. public static void Log(string message) { File. Append. All. Text("log. txt", message); }
Чуть сложнее Создать класса, со свойствами (для управления уровнем логирования). Класс инициализируется при старте программы и вызывается из основного модуля функция записи в лог. Сам класс решает куда писать полученное сообщение, в файл, в БД или вообще проигнорировать его.
А что на практике? На практике, оказывается, что всё не так просто: одного лог-файла становится уже недостаточно, возникают проблемы с многопоточностью, форматом логов, записью времени, производительность и т. д.
Что делать? Существуют готовые решения в виде логгеров для сохранения информации о состояниях системы и т. п. Какой логгер считать «хорошим» ?
Уровни логгирования любое сообщение несёт в себе информацию определённой критичности, и время реакции на сообщения отличаются.
Пример Debug. Отправлен запрос в базу на сохранение Debug. Завершён запрос в базу на сохранение Debug. Запрос в базу занял 0. 02 секунды, извлечено 1000 записей Info. Проведена транзакция по счёту 400000 (John Doe), получено $2000. Warn. Отклонена транзакция с суммой платежа 0. Error. Ошибка при сохранении транзакции 123: …. . Fatal. Не могу запустить модуль отправки исходящих сообщений MSMQ, из-за ошибки конфигурации модуля (…). Транзакции не будут обрабатываться.
Уровни логгирования Все сообщения системы можно разделить на несколько типов и для каждого можно сделать отдельную обработку.
Уровни логгирования Debug: сообщения отладки, профилирования. В production системе обычно сообщения этого уровня включаются при первоначальном запуске системы или для поиска узких мест (bottleneck-ов).
Уровни логгирования Info: обычные сообщения, информирующие о действиях системы. Реагировать на такие сообщения вообще не надо, но они могут помочь, например, при поиске багов, расследовании интересных ситуаций итд.
Уровни логгирования Warn: записывая такое сообщение, система пытается привлечь внимание обслуживающего персонала. Произошло что-то странное. Возможно, это новый тип ситуации, ещё не известный системе. Следует разобраться в том, что произошло, что это означает, и отнести ситуацию либо к инфо-сообщению, либо к ошибке. Соответственно, придётся доработать код обработки таких ситуаций.
Уровни логгирования Error: ошибка в работе системы, требующая вмешательства. Необходимо принимать меры довольно быстро! Ошибки этого уровня и выше требуют немедленной записи в лог, чтобы ускорить реакцию на них. Нужно понимать, что ошибка пользователя – это не ошибка системы.
Уровни логгирования Fatal: это особый класс ошибок. Такие ошибки приводят к неработоспособности системы в целом, или неработоспособности одной из подсистем. Чаще всего случаются фатальные ошибки из-за неверной конфигурации или отказов оборудования. Требуют срочной, немедленной реакции.
Ротация лог-файлов Логи со временем разрастаются, старые становятся неактуальными. Хороший логгер должен уметь подменять активный файл при наступлении определённых условий. Есть два режима: по дате по размеру файла.
Куда записывать лог Хороший логгер должен поддерживать отправку сообщений по протоколу UDP, запись в базу, взаимодействие с очередями сообщений, такими, как MSMQ или JMS. Кроме того, отлично, когда логгер предоставляет возможность реализации собственного потребителя сообщений.
Потокобезопасность Плохой логгер может: пропустить часть сообщений; выбросить исключение отрицательно повлиять на производительность Грамотная реализация потокобезопасности в логгере – один из ключевых моментов.
Асинхронное логгирование Типичная практика логгирования – асинхронная запись. При этом важно, чтобы размер буфера был гибко настраиваемый, например, debugсообщения можно писать по 100 штук, а error – немедленно после возникновения.
Формат и конфигурация логов Формат должен быть настраиваемый, с возможностью указать то, что писать и куда писать. Например, можно писать в файл, хранящийся по пути из переменной окружения. Кроме того, полезная фича – динамическая конфигурация логгера, слежение за файлом конфигурации. Надо включить debug-режим – поменяли конфиг и наслаждаемся вкусными логами без перезапуска приложения.
Правила записи исключений выберите уровень (подсистему), где Вы будете логгировать исключения.
Правила записи исключений если вы обработали исключение, возможны три случая: исключение считается обработанным и не пробрасывается выше по стеку. В этом случае запишите исключение с подробным стеком в лог;
Правила записи исключений исключение пробрасывается выше по стеку в той же подсистеме. Не логгируйте такое исключение. Однако убедитесь, что выше по стеку его запишут; исключение пробрасывается выше по стеку в другую подсистему. Например, на другую машину или в другой процесс. Залоггируйте такое исключение, или запишите диагностическое сообщение об исключении;
Правила записи исключений если исключение не обрабатывается – не логгируйте его. Однако убедитесь, что выше это исключение будет обязательно залоггировано


