Обработка исключений, это хороший способ? - PullRequest
28 голосов
/ 18 марта 2010

Мы боремся с политикой правильной обработки исключений в нашем приложении. Вот наши цели (кратко):

  • Обрабатывать только определенные исключения.
  • Обрабатывать только те исключения, которые можно исправить
  • Вход только один раз.

Мы предложили решение, которое включает в себя общее исключение для конкретного приложения и работает следующим образом:

try {
  // Do whatever
}
catch(ArgumentNullException ane)
{
  // Handle, optinally log and continue
}
catch(AppSpecificException)
{
  // Rethrow, don't log, don't do anything else
  throw;
}
catch(Exception e)
{
  // Log, encapsulate (so that it won't be logged again) and throw
  Logger.Log("Really bad thing", e.Message, e);
  throw new AppSpecificException(e)
}

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

У меня не так много опыта работы с шаблонами обработки исключений ... Это хороший способ решения наших задач? Есть ли у него серьезные недостатки или большие красные предупреждения?

Примечание: Одним из недостатков этого является то, что после первого улова вы теряете способность обрабатывать определенное исключение (если вы вызываете метод, который вызывает другой метод, а второй выдает исключение, которое вы не в состоянии справиться с этим) но я обнаружил, что никогда не делал этого никоим образом ... Я обрабатываю только исключения с одним уровнем глубины ...

Ответы [ 7 ]

60 голосов
/ 18 марта 2010

Если вы регистрируете исключение слишком близко ко времени его первого выброса, вы не будете регистрировать полную трассировку стека.

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

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

6 голосов
/ 18 марта 2010

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

Поймать исключение только для его обработки (например, для регистрации) или добавить контекстную информацию.

0 голосов
/ 18 марта 2010

Я бы порекомендовал больше думать о том, какие шаблоны вы хотите использовать для «вручения»

Если ваши шаблоны обработки сводятся к записи или повторному выбрасыванию, то повторно отправленная ошибка в конечном итоге будет записана. Итак, в конце концов, это просто регистрация ошибок. Если вы используете ASP.NET, используйте elmah, так что, по крайней мере, ваш код не покрыт пробной таблицей try / catch-and-log.

Есть только несколько способов "обработать" ошибки, которые не заканчиваются простой регистрацией.

Re-попробовать. (Остерегайтесь бесконечных петель)

Подождите и повторите попытку.

Попробуйте другой, но эквивалентный метод (Не удается подключиться по http? Попробуйте подключиться по https).

Установить пропущенные условия (создать папку, которая вызвала исключение FolderNotFoundException)

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

0 голосов
/ 18 марта 2010

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

0 голосов
/ 18 марта 2010

Первый вариант решения проблемы трассировки стека:

class AppSpecificException : ApplicationException
{
    public string SpecificTrace { get; private set; }
    public string SpecificMessage { get; private set; }

    public AppSpecificException(string message, Exception innerException)
    {
        SpecificMessage = message;
        SpecificTrace = innerException.StackTrace;
    }

}

Мне пришлось написать пример, чтобы понять вопрос и проверить проблему с трассировкой стека, это код для меня, обратите внимание на метод button2_click, наконец, в моем текстовом поле отображается строка сбоя и трассировка стека:

    private String internalValue;

    private void Operation1(String pField)
    {
        if (pField == null) throw new ArgumentNullException("pField");
        internalValue = pField;
    }

    private void Operation2(Object pField)
    {
        if (pField == null) throw new ArgumentNullException("pField");
        internalValue = Convert.ToInt32(pField).ToString();
    }

    private void Operation3(String pField)
    {
        if (pField == null) throw new ArgumentNullException("pField");
        internalValue = pField;
        Operation2(-1);
    }


    /// <exception cref="AppSpecificException"><c>AppSpecificException</c>.</exception>
    private void button1_Click(object sender, EventArgs e)
    {
        try
        {
            Operation1("One");
            Operation2("Two");
            Operation3("Three");
            MessageBox.Show(internalValue);
        }
        catch (ArgumentNullException ex)
        {
            textBoxException.Text = ex.Message + (char) 13 + (char) 10 + ex.StackTrace;
        }
        catch (AppSpecificException ex)
        {
            //textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;
            throw;
        }
        catch (Exception ex)
        {
            textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;                    
            throw new AppSpecificException("crash", ex);
        }

    }

    private void button2_Click(object sender, EventArgs e)
    {
        try
        {
            button1_Click(sender, e);
        }
        catch (AppSpecificException ex)
        {
            textBoxException.Text = ex.SpecificMessage + (char) 13 + (char) 10 + ex.SpecificTrace;
        }
    }
0 голосов
/ 18 марта 2010

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

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

Обратите внимание, что вы можете добавить try catch в методы для управления определенным исключением, которое не управляется вашими перехватчиками.

Перехват единства: http://msdn.microsoft.com/en-us/library/dd140045.aspx

0 голосов
/ 18 марта 2010

Это довольно распространенный подход для решения проблемы обработки исключений (от более специфического к менее конкретному).

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

Повторное исключение - это хорошо, лучше объявить метод, чтобы он вызывал определенные исключения и позволял вызывающим их обрабатывать.Таким образом, вам придется создавать меньше кода и централизовать некоторые элементы управления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...