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

1. Не допускается использование конструкций

try
{
…
}
catch
{
…
}

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

2. Следует четко разделять логику и представление приложения. Все исключения сгенерированные в логическом слое должны передаваться наружу в исходном виде, либо обернуты в другое исключение, при этом исходное исключение обязательно должно передаваться в конструктор вновь генерируемого исключения.
Т.е. все методы, относящиеся к логике приложения не должны скрывать исключения. За исключением случаев, когда исключение ожидаемо и может быть корректно обработано без нарушения работы приложения.

Примечание: фильтр исключений вида catch(Exception ex) или catch не является обработкой ожидаемого исключения!

3. При реализации собственного типа исключений обязательно реализовывать ВСЕ конструкторы родительского исключения.

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

5. Думать, когда использовать исключения, а когда нет. Например, при делении на ноль и выходе за границу массива. Т.е. если при выполнении операции, где возможно деление на 0 нужно смотреть какова вероятность того, что аргумент может быть 0 — если часто, то лучше проверить аргумент на равенство 0, иначе лучше использовать проверку на DivideByZeroException.

6. Запрещается использовать код следующего вида:


int SomeMethod(SomeData data)
{
try
{
…
    return 0;
}
catch
{
…
return -1;// or return false; or etc.
}
}

Данная конструкция приводит к многочисленным ошибкам в бедующем, когда не проверяется код возврата функции (что бывает очень часто). При этом исходное исключение скрывается, из-за этого могут появляться самые различные исключения в неожиданных местах .

7. Там где это возможно, вместо блоков try{}finally{} использовать using(){}
Так, например, следующий код может не освобождать ресурсы, как это ожидалось:

FileStream stream1 = new FileStream(…);
FileStream stream2 = new FileStream(…); // 1
try
{
…
}
finally
{
stream1.Dispose(); // 2
stream2.Dispose();
}


В данном коде две ошибки:
а) При генерации исключения при создании stream2 – stream1 не будет закрыт, до сборки мусора.
б) При генерации исключения при закрытии stream1 – stream2 не будет закрыт, до сборки мусора.
Вместо этого было правильнее написать:

using(FileStream stream1 = new FileStream(…))
{
using(FileStream stream2 = new FileStream(…))
{
…
}
}


8. Все пользовательский исключения должны быть унаследованы от ApplicationException а не от Exception (Просто хороший стиль). Так же должен обязательно быть определен атрибут [Serializable].

9. Конструкторы типов-исключений не должны генерировать исключений.

10. При реализации фильтров исключений более «специализированные» исключения должны обрабатываться раньше, т.е. например, если есть исключение

class MyException1: Exception 
{
}

class MyException2: MyException1
{
}

То фильтр для них должен выглядеть следующим образом:

try
{
}catch(MyException2)
{
}
catch(MyException1)
{
}
catch(Exception)
{
}


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

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

13. Никогда не генерируйте исключения в методах Dispose и Close, даже если объект уже закрыт или очищен.

14. Если вы обрабатываете исключение, то верните приложение в согласованное состояние (т.е. восстановить значение переменных, смещение в потоках и.т.п)

15. Не перехватывайте специальные исключения OutOfMemoryException, StackOverflowException, ThreadAbortException и ExecutionEngineException или перехватывайте их только для регистрации. Если перехватываете какое-либо из этих исключений его нужно сгенерировать повторно.
Автор: Александер Малафеев    Оценить