Используя общий класс для выполнения с попыткой/выгодой/наконец?

Я имею многое из этого в различных методах в моем коде:

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 в зависимости от, что Вы хотите сделать, но это - особые случаи в конкретных подклассах, о которых я говорил.

5
задан 11 June 2010 в 13:40
поделиться

9 ответов

Если вы хотите, чтобы ваши объекты были чистыми, вы можете рассмотреть возможность использования 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);
    }
}

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

1
ответ дан 18 December 2019 в 07:53
поделиться

Недостатком является ваш подход к общей обработке исключений. Вы не должны ловить базовый класс Exception без веской причины. Это может скрыть различные проблемы; хорошо, вы их регистрируете, но ваш код не знает, что только что закончилась память, или файл не существует, и продолжает работать так, как будто проблемы вообще не было.
Метод, который вы здесь описали, будет способствовать общей обработке исключений.

Связанные вопросы:
Действительно ли так плохо ловить общее исключение?
Является ли плохой практикой ловить неспецифическое исключение, такое как System.Exception? Почему?

EDIT (ответ на редактирование и уточнение OP):
Я не уверен, чего вы хотите добиться. В основном вы скрываете исключение (общее или специфицированное - оно просто проглатывается). Вызовите свой метод Executor.Hide( RunSomething ); и понятно, что здесь происходит. Но есть ли преимущество в проглатывании исключений? Я так не думаю. Опять же, могут быть места, где это необходимо - но они редки и должны быть выбраны намеренно. Метод, который вы приводите, побуждает проглатывать исключение, не задумываясь об этом.
Вы закомментировали строку rethrow (throw ex или лучше просто throw, что сохраняет стек). Что вы получите, включив эту строку? По сути, только логирование. Вы ловите исключение, записываете его в журнал и отбрасываете, чтобы ... поймать его снова? Почему вы не можете поместить логирование в это последнее место?
Старайтесь ловить исключение только тогда, когда вы в состоянии его обработать. Там же вы можете вести журнал. Любой хороший логгер сможет показать вам трассировку стека, так что вы не потеряете информацию.

Связанное:
Где ставить try catch?

12
ответ дан 18 December 2019 в 07:53
поделиться

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

3
ответ дан 18 December 2019 в 07:53
поделиться

На самом деле вы ничего не выиграете, но кое-что потеряете:

  • Удобочитаемость
  • Простота (вы вводите другой класс, еще один делегат, который должен понять читатель)
  • Вы не можете просто перешагнуть через Executor.Execute, чтобы перейти к Method1 (...). Если вы перейдете, вы пропустите весь блок кода в своем делегате.
  • ...
1
ответ дан 18 December 2019 в 07:53
поделиться

В самом начале....

Вы НЕ должны иметь код, подобный нижеприведенному, разбросанным по вашей кодовой базе.

try 
{ 
  runABunchOfMethods(); 
} 
catch (Exception ex) 
{ 
  logger.Log(ex); 
} 

Я бы предложил вам сначала посмотреть на реальную необходимость такого кода. Вы должны ловить очень специфическое исключение во всех случаях (в отличие от общего catch all).

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

0
ответ дан 18 December 2019 в 07:53
поделиться

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

  • В консольном приложении вы должны перехватывать и регистрировать исключения в Main .
  • В приложении WinForms вы должны перехватывать и регистрировать исключения в ваших обработчиках событий.
  • В службе вы должны перехватывать и регистрировать исключения, а затем ThreadProcs ваших рабочих потоков.
  • И т. Д.

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

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

1
ответ дан 18 December 2019 в 07:53
поделиться

Я сразу перейду на тезис о том, что "это заставляет вас злоупотреблять исключениями, вылавливая только на общем уровне". Однако в более специализированных случаях вы можете извлечь выгоду из своего рода «обертки делегата обработки исключений». Например, если вы много работали с классами, связанными с БД, которые всегда генерируют одни и те же исключения, у вас может быть выделенная оболочка-исключение БД.

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

0
ответ дан 18 December 2019 в 07:53
поделиться

Если вы видите этот код во многих классах, значит, вы делаете что-то очень-очень неправильное в моей книге :-) В крайнем случае вы должны перебросить исключение (просто "throw", а не "throw(ex)"), но вся идея постоянно ловить базовое исключение только для того, чтобы записать его в лог (или вообще в любое время, за исключением нескольких крайних случаев), на мой взгляд, не является правильным подходом.

Я понятия не имею, что за приложение вы пишете, но не можете ли вы зафиксировать такие события, как AppDomain.UnhandledException, которое срабатывает всякий раз, когда в любом потоке возникает необработанное исключение (то, которое вы не поймали), и записать его в журнал? Возможно, вы захотите объединить это с более конкретными операторами catch, которые также ведут журнал, если вы хотите регистрировать и восстанавливаемые исключения.

1
ответ дан 18 December 2019 в 07:53
поделиться

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

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

1
ответ дан 18 December 2019 в 07:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: