Класс обработки исключений

Как лучше всего обрабатывать исключения, не помещая блоки try/catch везде?

У меня была идея создать класс, предназначенный для получения и обработки исключений, но мне интересно, хорошая ли это идея дизайна. Такой класс получит исключение, а затем решит, что с ним делать, в зависимости от его типа или кода ошибки, может даже проанализировать трассировку стека для конкретной информации и т. Д.

Вот основная идея и реализация:

public class ExceptionHandler
{
    public static void Handle(Exception e)
    {
        if (e.GetBaseException().GetType() == typeof(ArgumentException))
        {
            Console.WriteLine("You caught an ArgumentException.");
        }
        else
        {
            Console.WriteLine("You did not catch an exception."); 
            throw e;   // re-throwing is the default behavior
        }
    }
}

public static class ExceptionThrower
{
    public static void TriggerException(bool isTrigger)
    {
        if (isTrigger)
            throw new ArgumentException("You threw an exception.");
        else
            Console.WriteLine("You did not throw an exception."); 
    }
}

class Program
{
    static void Main(string[] args)
    {
        try
        {
            ExceptionThrower.TriggerException(true); 
        }
        catch(Exception e)
        {
            ExceptionHandler.Handle(e);  
        }
        Console.ReadLine(); 
    }
}

Я подумал, что это будет интересное начинание, потому что теоретически вам понадобится только один или очень мало блоков try/catch для вызовов вашего метода main(), и пусть класс исключения обрабатывает все остальное, включая перебрасывание, обработку, ведение журнала и все, что угодно.

Мысли?

4 ответа

Решение

На самом деле есть веская причина, почему вы не видите подобных проектов в рабочем коде.

Прежде всего, такой дизайн не может помочь вам уменьшить количество try/catch пары в вашем коде (это должно быть очевидно). Это может помочь вам уменьшить количество catch заявления для данного try, так как вы могли просто поймать System.Exception и вперед к ExceptionHandler...

Но что дальше?

Каждое исключение должно обрабатываться по-разному. Как бы ExceptionHandler точно знаешь что делать? Вы можете попытаться решить эту проблему несколькими способами, например:

  1. Вытекают из ExceptionHandler и поместите код для обработки исключений в виртуальных методах
  2. Передать номер Action<Exception> экземпляры к обработчику и заставить его вызывать правильный

Решение (1) будет хуже, чем было раньше: теперь вам нужно создать целый новый класс для каждого try заблокируйте и переопределите кучу методов, чтобы в итоге получилось что-то худшее, чем было раньше (не сразу понятно, как код конкретного класса вписывается в поток вашей программы). Это также оставило бы еще один важный вопрос без ответа: вам может понадобиться контекст (доступ к переменным в текущей области видимости) для правильной обработки исключения. Как вы предоставите доступ к этому контексту?

Решение (2) на самом деле очень похоже на написание catch блоки, которые мы хотели избежать (каждый Action будет эффективно содержание catch блок). В итоге мы делаем то же самое, только более сложным и многословным образом.

Есть и другие вопросы:

  • Что должно ExceptionHandler делать, если он не может обработать исключение? Бросив его снова, вы потеряете исходную трассировку стека, фактически уничтожив всю полезную информацию.
  • Что делать, если есть ошибка в ExceptionHandler? Вы можете переубедить try/catch, Можете ли вы доверять коду, который вы написали себе в той же степени?

Для ExceptionThrower... какую пользу это может предложить throw new Exception();?

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

ОК, это, вероятно, не тот ответ, который вы хотите, но...

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

  1. быть защитником любого ввода, чтобы избежать исключений в первую очередь
  2. положил try..catch блокирует, где имеет смысл перехватывать и обрабатывать исключение (обратите внимание, что это означает, что вы не должны иметь try..catch блоки во всех методах)

Итак, где имеет смысл поймать и обработать исключение? Короче говоря, если ваш код обладает знаниями, позволяющими обрабатывать исключения. Если это не так, пусть исключение всплывает к вызывающей стороне. Единственное место, где, я думаю, вы должны перехватывать все исключения и иметь общее поведение по умолчанию, касающееся того, что делать, это на верхнем уровне вашего приложения. Обычно это пользовательский интерфейс.

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

Почему в вашем коде так много блоков try-catch? Можете привести несколько примеров? Исключения по самой своей природе "Исключительные", и это не так часто! Вы не только не должны часто отлавливать исключения, но и каждое исключение, и тот же стандартный код, который работает в одной ситуации, вероятно, не подходит во многих других ситуациях.

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

Извините, это не очень хорошая идея. Когда вы ловите исключение в своем коде с помощью обычного блока try/catch, окружающего соответствующий раздел, вы получаете возможность использовать две критические части информации для решения проблемы: тип исключения, а также место возникновения исключения.

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

Более того, блоки try/catch часто также включают в себя блок finally, в котором вы можете быть уверены, что все произойдет, даже если выдается исключение (например, закрытие потоков и т. Д.). У вас нет никакого способа договориться с этим.

Правильная обработка исключений может быть сложной, и нет волшебной палочки, которая сделает это простым и понятным. Если бы это было так, .Net уже включил бы это.

Другие вопросы по тегам