Класс обработки исключений
Как лучше всего обрабатывать исключения, не помещая блоки 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
точно знаешь что делать? Вы можете попытаться решить эту проблему несколькими способами, например:
- Вытекают из
ExceptionHandler
и поместите код для обработки исключений в виртуальных методах - Передать номер
Action<Exception>
экземпляры к обработчику и заставить его вызывать правильный
Решение (1) будет хуже, чем было раньше: теперь вам нужно создать целый новый класс для каждого try
заблокируйте и переопределите кучу методов, чтобы в итоге получилось что-то худшее, чем было раньше (не сразу понятно, как код конкретного класса вписывается в поток вашей программы). Это также оставило бы еще один важный вопрос без ответа: вам может понадобиться контекст (доступ к переменным в текущей области видимости) для правильной обработки исключения. Как вы предоставите доступ к этому контексту?
Решение (2) на самом деле очень похоже на написание catch
блоки, которые мы хотели избежать (каждый Action
будет эффективно содержание catch
блок). В итоге мы делаем то же самое, только более сложным и многословным образом.
Есть и другие вопросы:
- Что должно
ExceptionHandler
делать, если он не может обработать исключение? Бросив его снова, вы потеряете исходную трассировку стека, фактически уничтожив всю полезную информацию. - Что делать, если есть ошибка в
ExceptionHandler
? Вы можете переубедитьtry
/catch
, Можете ли вы доверять коду, который вы написали себе в той же степени?
Для ExceptionThrower
... какую пользу это может предложить throw new Exception();
?
Обработка исключений - это уже сложный вопрос, и достаточно сложно сделать это правильно, не добавляя дополнительные механизмы в машину. Особенно, если они не купят тебе ничего нового. Не делай этого.
ОК, это, вероятно, не тот ответ, который вы хотите, но...
У меня, как правило, аллергия на идею общего класса обработки исключений. Вы можете почти услышать, как это противоречие само по себе. Исключением является исключительное событие. Исключительные события не могут быть обработаны в общем виде, но требуют специальной обработки, где бы они ни появлялись, что, по сути, означает, что ваш код должен выполнять две вещи:
- быть защитником любого ввода, чтобы избежать исключений в первую очередь
- положил
try..catch
блокирует, где имеет смысл перехватывать и обрабатывать исключение (обратите внимание, что это означает, что вы не должны иметьtry..catch
блоки во всех методах)
Итак, где имеет смысл поймать и обработать исключение? Короче говоря, если ваш код обладает знаниями, позволяющими обрабатывать исключения. Если это не так, пусть исключение всплывает к вызывающей стороне. Единственное место, где, я думаю, вы должны перехватывать все исключения и иметь общее поведение по умолчанию, касающееся того, что делать, это на верхнем уровне вашего приложения. Обычно это пользовательский интерфейс.
У нас есть класс в нашей кодовой базе, который имеет подпись, довольно похожую на ту, которую вы предложили, и теперь я могу вам сказать, что в ней только страдания и страдания!
Почему в вашем коде так много блоков try-catch? Можете привести несколько примеров? Исключения по самой своей природе "Исключительные", и это не так часто! Вы не только не должны часто отлавливать исключения, но и каждое исключение, и тот же стандартный код, который работает в одной ситуации, вероятно, не подходит во многих других ситуациях.
Никто не говорил, что обработка исключений была простой (или создавал компактный код) - вы должны тщательно продумать каждую ситуацию, в которой вы хотите поймать исключение, и обработать его соответствующим образом - избегайте ловли исключений, которые вам не нужно обрабатывать.
Извините, это не очень хорошая идея. Когда вы ловите исключение в своем коде с помощью обычного блока try/catch, окружающего соответствующий раздел, вы получаете возможность использовать две критические части информации для решения проблемы: тип исключения, а также место возникновения исключения.
С вашей договоренностью вы должны иметь дело со всеми исключениями, зная только, что это за исключения. Вы больше не знаете, где на самом деле произошло исключение, поэтому вы не можете ничего с этим поделать, кроме как зарегистрировать его или показать сообщение пользователю.
Более того, блоки try/catch часто также включают в себя блок finally, в котором вы можете быть уверены, что все произойдет, даже если выдается исключение (например, закрытие потоков и т. Д.). У вас нет никакого способа договориться с этим.
Правильная обработка исключений может быть сложной, и нет волшебной палочки, которая сделает это простым и понятным. Если бы это было так, .Net уже включил бы это.