Собственный класс. Выбрасывайте исключения на основе собственного класса, унаследованного от Exception
, а не напрямую — на основе Exception
, потому что это дает возможность определить свой собственный обработчик и отделить отслеживание и обработку исключений, выбрасываемых вашим кодом и кодом фреймворка .NET.
Отдельные поля. Создавайте отдельные поля в собственном классе для передачи существенной информации, вместо сериализации и десериализации данных в поле Message
. Несмотря на то, что идея упаковки в Message
сложных данных в виде строки типа JSON выглядит соблазнительно, это редко является удачной идеей, поскольку добавляет дополнительный расход ресурсов на кодирование, локализацию, декодирование.
Сообщения в лог. Записывайте в лог сообщение всякий раз, когда обработчик отлавливает Exception
, с помощью вызова Exception.ToString();
это упростит отслеживание при отладке исключительных ситуаций.
Точный класс. Используйте наименее общий класс для отлавливания исключений, иначе это может приводить к труднообнаруживаемым ошибкам. Рассмотрим следующий код:
public class SimpleClass { public static string DoSomething() { try { return SimpleLibrary.ReportStatus(); } catch (Exception) { return "Failure 1"; } } } public class SimpleLibrary { public static string ReportStatus() { return String.Format("Success {0}.", 0); } }
Если предположить, что код классов SimpleClass
и SimpleLibrary
находится в отдельных сборках, то в случае, когда обе сборки установлены правильно, код выполняется правильно, выводя сообщение «Success 0», а если сборка с классом SimpleLibrary
не будет установлена, тогда код выполняется неправильно, выводя сообщение «Failure 1», несмотря на то, что никакой ошибки при исполнении функции ReportStatus
не происходит. Проблема неочевидна из-за слишком обобщенной обработки ислючений. Код, форматирующий строку, выбрасывает исключения ArgumentNullException
и FormatException
, поэтому именно эти исключения и должны перехватываться в блоке catch, тогда станет очевидной причина ошибки — это исключение FileNotFoundException
из-за отсутствия или неправильной установки сборки, содержащей класс SimpleLibrary
.
Содержательная обработка. Всегда обрабатывайте исключения содержательно. Код вида
try { DoSomething(); } catch (SomeException) { // TODO: ... }
скрывает проблемы, не позволяя обнаружить их при отладке или выполнении.
Очистка в блоке finally
. Удаляйте временные объекты в блоках finally
. Рассмотрим операцию записи временного файла.
void WorkWithFile(string filename) { try { using (StreamWriter sw = new StreamWriter(filename)) { // TODO: Do something with temporary file } File.Delete(filename); } catch (Exception) { File.Delete(filename); throw; } }
Как видно, код удаляющий файл, дублируется. Чтобы избежать повтора, нужно удалять временный файл из блока finally
.
void WorkWithFile(string filename) { try { using (StreamWriter sw = new StreamWriter(filename)) { // TODO: Do something with temporary file } } finally { File.Delete(filename); } }
Оператор using
. Используйте оператор using
. Он гарантирует вызов метода Dispose
, даже если при вызове методов в объекте происходит исключение.
using (Font f = new Font("Times New Roman", 12.0f)) { byte charset = f.GdiCharSet; }
Использование оператора using
равноценно блоку try/finally
, но более компактно и лаконично.
Font f = new Font("Times New Roman", 12.0f); try { byte charset = f.GdiCharSet; } finally { if (f != null) ((IDisposable)f).Dispose(); }
Результат функции. Не используйте исключения для возврата результата работы функции и не используйте специальные коды возврата для обработки ошибок. Каждому — свое. Результат функции нужно возращать, а ошибки, которые нельзя пропускать, — обрабатывать с помощью исключений.
Отсутствие ресурса. Возвращайте null
при отсутствии ресурса. Согласно соглашению, общепринятому для API .NET, функции не должны выкидывать исключения при отсутствии ресурса, они должны возвращать null
. Так GetManifestResourceStream
возращает null
, если ресурсы не были указаны при компиляции или не видны для вызывающего кода.
Исходное место. Сохраняйте информацию об исходном месте возникновения исключения. Например,
try { // Do something to throw exception } catch (Exception e) { // Do something to handle exception throw e; // Wrong way! throw; // Right way }
В случае вызова throw e;
информация о месте генерации исключения будет подменена новой строкой, поскольку создание нового исключения очистит стэк вызовов. Вместо этого нужно сделать вызов throw;
который просто перевыбросит исходное исключение.
Добавление смысла. Меняйте исходное исключение, только чтобы добавить смысл, необходимый для пользователя. Например, в случае подключения к базе данных, пользователю может быть безразлична причина сбоя подключения (ошибка сокета), достаточно информации о самом сбое.
Сериализация. Делайте исключения, унаследованные от Exception
, сериализуемыми с помощью [Serializable]
. Это полезно, так как никогда заранее неизвестно, где будет получено исключение, например, в веб службе.
[Serializable] public class SampleSerializableException : Exception { public SampleSerializableException() { } public SampleSerializableException(string message) : base(message) { } public SampleSerializableException(string message, Exception innerException) : base(message, innerException) { } protected SampleSerializableException(SerializationInfo info, StreamingContext context) : base(info, context) { } }
Стандартные конструкторы. Реализуйте стандартные конструкторы, иначе правильная обработка исключений может быть затруднена. Так для исключения NewException
эти конструкторы таковы:
public NewException();
public NewException(string);
public NewException(string, Exception);
protected or private NewException(SerializationInfo, StreamingContext);
По материалам MSDN, CodeProject, StackOverflow.
ссылка на оригинал статьи http://habrahabr.ru/post/178805/
Добавить комментарий