Я имею многое из этого в различных методах в моем коде:
try
{
runABunchOfMethods();
}
catch (Exception ex)
{
logger.Log(ex);
}
Что относительно того, чтобы создать это:
public static class Executor
{
private static ILogger logger;
public delegate void ExecuteThis();
static Executor()
{
// logger = ...GetLoggerFromIoC();
}
public static void Execute<T>(ExecuteThis executeThis)
where T : Exception
{
try
{
executeThis();
}
catch (T ex)
{
// Some kind of Exception Handling Strategy...
logger.Log(ex);
// throw;
}
}
}
И просто с помощью него как это:
private void RunSomething()
{
Method1(someClassVar);
Method2(someOtherClassVar);
}
...
Executor.Execute<ApplicationException>(RunSomething);
Там какие-либо оборотные стороны к этому подходу? (Вы могли добавить методы Исполнителя и делегатов, когда Вы хотите наконец и используете дженерики для типа Exeception, Вы хотите поймать...),
Править: Извините за то, чтобы быть неясным - то, чем я был действительно после, было некоторым входом на общем представлении попытаться переместить выполнение кода от рассматриваемого класса до более обобщенного класса, который делает это. Я просто сделал быстрый макет решения, но в реальной жизни Вы будете естественно использовать вещи, такие как стратегии обработки исключений, абстрактные базовые классы выполнения с более специализированными классами выполнения для определенного слоя/части системы. Я обычно создаю один метод с попыткой.../runABunchOfMethods-part (это делает обработку исключений за специализированными исключениями), который называет runABunchOfMethods, которые в свою очередь выполняют ограниченный набор других методов "чистый код" стиль.
Я куплю аргумент путаницы на некоторых уровнях, но если целое решение/архитектура проявляет этот предложенный подход, новый программист должен смочь понять шаблон.
Я отредактировал Исполнителя для включения универсального T, чтобы позволить коду вызова указывать exeception только, чтобы показать, как обработать специализированный exeception. В других случаях у Вас мог бы быть набор catch:es в зависимости от, что Вы хотите сделать, но это - особые случаи в конкретных подклассах, о которых я говорил.
Если вы хотите, чтобы ваши объекты были чистыми, вы можете рассмотреть возможность использования AOP-фреймворка, например PostSharp. Тогда протоколирование исключений (например) можно будет обрабатывать в одном месте, если вы того пожелаете.
EDIT:
Можно убрать блоки try / catch с помощью PostSharp - вот пример общего обработчика исключений, который может быть создан в PostSharp:
[Serializable]
public class CommonExceptionHandling : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
// can do some logging here
// ...
// throw the exception (out of postsharp) to where it occurred:
eventArgs.FlowBehavior = FlowBehavior.RethrowException;
// If you want to ignore the exception, you can do this:
//eventArgs.FlowBehavior = FlowBehavior.Return;
base.OnException(eventArgs);
}
}
Если вы примените этот атрибут к классу, любые исключения, которые бросают любые методы в этом классе, будут направлены через вышеприведенный код.
Недостатком является ваш подход к общей обработке исключений. Вы не должны ловить базовый класс Exception
без веской причины. Это может скрыть различные проблемы; хорошо, вы их регистрируете, но ваш код не знает, что только что закончилась память, или файл не существует, и продолжает работать так, как будто проблемы вообще не было.
Метод, который вы здесь описали, будет способствовать общей обработке исключений.
Связанные вопросы:
Действительно ли так плохо ловить общее исключение?
Является ли плохой практикой ловить неспецифическое исключение, такое как System.Exception? Почему?
EDIT (ответ на редактирование и уточнение OP):
Я не уверен, чего вы хотите добиться. В основном вы скрываете исключение (общее или специфицированное - оно просто проглатывается). Вызовите свой метод Executor.Hide
и понятно, что здесь происходит. Но есть ли преимущество в проглатывании исключений? Я так не думаю. Опять же, могут быть места, где это необходимо - но они редки и должны быть выбраны намеренно. Метод, который вы приводите, побуждает проглатывать исключение, не задумываясь об этом.
Вы закомментировали строку rethrow (throw ex
или лучше просто throw
, что сохраняет стек). Что вы получите, включив эту строку? По сути, только логирование. Вы ловите исключение, записываете его в журнал и отбрасываете, чтобы ... поймать его снова? Почему вы не можете поместить логирование в это последнее место?
Старайтесь ловить исключение только тогда, когда вы в состоянии его обработать. Там же вы можете вести журнал. Любой хороший логгер сможет показать вам трассировку стека, так что вы не потеряете информацию.
Связанное:
Где ставить try catch?
Вы получаете еще один уровень непрямолинейности, который может сделать ваш код более трудным для чтения и понимания (особенно для того, кто смотрит на ваш код впервые).
На самом деле вы ничего не выиграете, но кое-что потеряете:
В самом начале....
Вы НЕ должны иметь код, подобный нижеприведенному, разбросанным по вашей кодовой базе.
try
{
runABunchOfMethods();
}
catch (Exception ex)
{
logger.Log(ex);
}
Я бы предложил вам сначала посмотреть на реальную необходимость такого кода. Вы должны ловить очень специфическое исключение во всех случаях (в отличие от общего catch all).
После этого, если у вас все еще остается несколько методов, отлавливающих одно и то же исключение, вы можете подумать о создании нескольких методов в вашем исполнителе, которые обрабатывают определенный тип исключения.
Обычно лучший шаблон для регистрации исключений в вашем приложении состоит в том, чтобы только перехватывали исключения в методах верхнего уровня в вашем коде.
Main
. ThreadProcs
ваших рабочих потоков. Практически во всех других точках вашего приложения вы должны только перехватывать определенные исключения, а затем повторно генерировать новое исключение, обертывающее исходное исключение. Если вам не нужно оборачивать исходное исключение, вам не нужно в первую очередь перехватывать исходное исключение.
Подобная структура вашего приложения приведет лишь к нескольким местам, где вы перехватываете, регистрируете и "проглатываете" исключения.
Я сразу перейду на тезис о том, что "это заставляет вас злоупотреблять исключениями, вылавливая только на общем уровне". Однако в более специализированных случаях вы можете извлечь выгоду из своего рода «обертки делегата обработки исключений». Например, если вы много работали с классами, связанными с БД, которые всегда генерируют одни и те же исключения, у вас может быть выделенная оболочка-исключение БД.
Однако в конечном итоге тот факт, что исключения «загромождают» ваш код, является обратной стороной к тому, чтобы сделать ваш код безопасным.
Если вы видите этот код во многих классах, значит, вы делаете что-то очень-очень неправильное в моей книге :-) В крайнем случае вы должны перебросить исключение (просто "throw", а не "throw(ex)"), но вся идея постоянно ловить базовое исключение только для того, чтобы записать его в лог (или вообще в любое время, за исключением нескольких крайних случаев), на мой взгляд, не является правильным подходом.
Я понятия не имею, что за приложение вы пишете, но не можете ли вы зафиксировать такие события, как AppDomain.UnhandledException, которое срабатывает всякий раз, когда в любом потоке возникает необработанное исключение (то, которое вы не поймали), и записать его в журнал? Возможно, вы захотите объединить это с более конкретными операторами catch, которые также ведут журнал, если вы хотите регистрировать и восстанавливаемые исключения.
Обратной стороной будет отсутствие гибкости. Что делать, если вы хотите восстановиться после определенного исключения в одном из вызываемых методов - например, при неудачном подключении к SMTP-серверу вы можете захотеть сохранить содержимое исходящей почты в своей базе данных, чтобы повторить попытку позже? Это причина, по которой вам следует по возможности избегать перехвата общего типа исключения.
Кроме того, вы теряете часть ясности своего кода (по-моему, в любом случае), поскольку трудно увидеть, где происходит обработка исключений.