Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > .NET для новичков > Хотелось бы разобраться с исключениями


Автор: Primordial 18.11.2013, 02:39
Доброго времени суток! Перехожу от теории к практике и хотелось бы в первом приближении определиться с использованием исключений. Как я понимаю, разработка грамотного кода подразумевает следующее:
1. Если уже существует класс исключения, уместного в данной ситуации, необходимо использовать его. Например, ArgumentException, в случае, если передаваемые в метод аргументы по какой-либо причине не подходят и т.д.
2. Определять свои пользовательские классы исключений следует в случае, если подходящих встроенных не нашлось. (Как вообще определить когда и для чего вводить свои типы исключений? Как, например, отреагировать на то, что класс читающий конфиг не нашел или не смог открыть файл: каким-либо из встроенных или пользовательским?)
3. В случае если я ввожу свои типы исключений, для каждой возможной ошибки мне следует создать свой тип (Или же можно ограничиться одним, общим для какой-либо задачи типом? Например, все ошибки, которые могут возникнуть при чтении конфига, выбрасывать как исключения одного типа с соответствующими описаниями). 

Верны ли приведенные выше суждения?

Автор: dzaraev 19.11.2013, 11:48
1. разумеется
2. наследуете от класса Exception или от одного из его наследников

Цитата(Primordial @  18.11.2013,  02:39 Найти цитируемый пост)
 (Как вообще определить когда и для чего вводить свои типы исключений? Как, например, отреагировать на то, что класс читающий конфиг не нашел или не смог открыть файл: каким-либо из встроенных или пользовательским?)

Ну вы же сами выше написали: когда не хватает базовых - вводите свои.

3. Исключение это такой же класс, который должен иметь какую-то одну "обязанность". "Обязанностью" в данном случая является описание ошибки. Какие именно классы плодить - зависит от того, какие ошибки вы хотите различать и обрабатывать в коде (например ловить в try/catch). Если обработчик ошибки в catch блоке "понимает" что надо делать при InvalidOperationException - значит хватит и этого эксепшена. Если, нет - пусть ловит более конкретный эксепшн, например MyEpicFailException.
Обработка в блоке catch базового класса Exception - плохая практика, т.к. вы вряд ли гарантированно можете восстановить согласованное состояние программы во всех возможных случаях. Поэтому обрабатывают более конкретные исключения, которые ожидаемо могут быть выброшены в блоке try. Т.е. те ошибки, которые вы знаете как "починить". 
Допустим есть код 
Код

try
{
     DoMyCriticalTask();
}
catch (InvalidOperationException ex)
{
    //весьма сложно предусмотреть все причины падений и выполнить соответствующую "починку"
}

(Читайте коммент в коде.) Дело в том, что вы не знаете как восстановиться из всех возможных причин вылета такого исключения, но вы, например знаете, что есть вероятное место ошибки в вашей операции, которое вы также знаете как починить. Пишете в этом случае своё исключение:
Код

try
{
     DoMyCriticalTask();
}
catch (MyEpicFailException ex)
{
    //семантика исключения более точна, а значит можно выполнить более адекватное восстановление нормальной работы программы.
}

В дальнейшем например вы можете еще более уточнить ошибку и выполнить еще более "умный2 код восстановления, например более оптимальный или надежный, при этом вы можете наследовать более точный эксепшн от более обощенного: MySpecificFailReasonException : MyEpicFailException. При этом, вы можете продолжать генерировать старое исключение в случаях, когда не моете генерировать более точное, и в блоке catch обрабатывать оба:
Код

try
{
     DoMyCriticalTask();
}
catch (MySpecificFailReasonException ex)
{
    //более точная ошибка, более конкретный код восстановления.
}
catch (MyEpicFailException ex)
{
    //когда это предусмотренная нами ошибка, но точная причина может быть неизвестна, код восстановления может быть другим
}



Автор: Primordial 24.11.2013, 15:26
Цитата

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

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

В свое время, когда я активно писал на Delphi, я заводил класс-предок исключений, скажем для бизнес-логики, в котором у меня было поле типа "строка" с уникальным идентификатором ошибки (использовался GUID). Т.о. я всегда мог отследить в каком месте кода возникло исключение посредством поиска по коду проекта. Разумеется, при наличии UI, пользователю показывалось осмысленное сообщение. Есть ли смысл делать под .net что-то похожее? Насколько такой подход распространен?

Автор: jonie 24.11.2013, 21:50
Цитата(Primordial @  24.11.2013,  16:26 Найти цитируемый пост)

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

короче читать это: http://msdn.microsoft.com/en-us/library/ms229014%28v=vs.100%29.aspx
(ранее было доступно в виде pdf)

Автор: dzaraev 25.11.2013, 06:54
Цитата(Primordial @  24.11.2013,  15:26 Найти цитируемый пост)
В свое время, когда я активно писал на Delphi, я заводил класс-предок исключений, скажем для бизнес-логики, в котором у меня было поле типа "строка" с уникальным идентификатором ошибки (использовался GUID). Т.о. я всегда мог отследить в каком месте кода возникло исключение посредством поиска по коду проекта. Разумеется, при наличии UI, пользователю показывалось осмысленное сообщение. Есть ли смысл делать под .net что-то похожее? Насколько такой подход распространен? 

Не скажу за всех, но я предпочитаю просто сохранять стек (свойство Exception.StackTrace), это даёт возможность не просто найти место выброса исключения, но и увидеть "историю" его появления.
Код

internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Debug.WriteLine("Попытка - не пытка...");
                ProblemMethod();
            }
            catch (InvalidOperationException ex)
            {
                Debug.WriteLine("-------------------У нас проблемы:-------------------");
                Debug.WriteLine(ex.StackTrace);
                Debug.WriteLine("-----------------------------------------------------");
            }
        }

        static void ProblemMethod()
        {
            throw new InvalidOperationException("Всё пропало!");
        }
    }


Output:
Код

Попытка - не пытка...
A first chance exception of type 'System.InvalidOperationException' occurred in ConsoleApplication6.exe
-------------------У нас проблемы:-------------------
   в ConsoleApplication6.Program.ProblemMethod() в D:\Work\sandbox\ConsoleApplication6\ConsoleApplication6\Program.cs:строка 29
   в ConsoleApplication6.Program.Main(String[] args) в D:\Work\sandbox\ConsoleApplication6\ConsoleApplication6\Program.cs:строка 17
-----------------------------------------------------

Автор: jonie 25.11.2013, 11:35
Цитата(dzaraev @  25.11.2013,  07:54 Найти цитируемый пост)

Не скажу за всех, но я предпочитаю просто сохранять стек (свойство Exception.StackTrace), 

Уже давно люди придумали готовые логгер либы, вроде nlog-а, где формат вывода можно настроить не в коде 8-)
Ну а при наличии реально проблем как я уже говорил - делайте Debugger.Break (например) - это позволит снять дамп, ну или Enviroment.FailFast после снятия дампа и записи в лог...

Автор: dzaraev 25.11.2013, 12:17
Цитата(jonie @  25.11.2013,  11:35 Найти цитируемый пост)
Уже давно люди придумали готовые логгер либы, вроде nlog-а, где формат вывода можно настроить не в коде 8-)

Спасибо, я в курсе. Если вы про черточки  - это просто для примера )) Суть самого пример в том, что не нужен какой-то гуид, чтобы понять по логу - где случился фейл.


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)