Работа с платформой.NET, у меня есть сервис с рядом методов, которые могут, генерирует несколько типов исключений: MyException2, MyExc1, Исключение... Для обеспечения надлежащей работы для всех методов каждый из них содержит следующие разделы:
[WebMethod]
void Method1(...)
{
try
{
... required functionality
}
catch(MyException2 exc)
{
... process exception of MyException2 type
}
catch(MyExc1 exc)
{
... process exception of MyExc1 type
}
catch(Exception exc)
{
... process exception of Exception type
}
... process and return result if necessary
}
Это очень скучно, чтобы иметь точно тот же материал в КАЖДОМ сервисном методе (каждый метод имеет различный набор параметров) за точно теми же исключениями, обрабатывающими функциональность...
Там возможность состоит в том, чтобы "сгруппировать" эти разделы выгоды и использовать только одну строку (что-то подобное макросам C++)? Вероятно, что-то новое в.NET 4.0 связано с этой темой?
Спасибо.
P.S. Любые мысли приветствуются.
Если обработка исключений точно такая же во всех ваших методах, вы можете сделать что-то вроде:
void CallService(Action method)
{
try
{
// Execute method
method();
}
catch(MyException2 exc)
{
... process exception of MyException2 type
}
catch(MyExc1 exc)
{
... process exception of MyExc1 type
}
catch(Exception exc)
{
... process exception of Exception type
}
}
Затем вы можете просто переписать свой клиентский код, чтобы он выполнял :
int i = 3;
string arg = "Foo";
this.CallService( () => this.Method1(i) );
this.CallService( () => this.Method2(arg, 5) );
Это позволяет использовать ваши методы Method1 и Method2 просто:
void Method1(int arg)
{
// Leave out exception handling here...
... required functionality
... process and return result if necessary
}
void Method2(string stringArg, int intArg)
{
// Leave out exception handling here...
... required functionality
... process and return result if necessary
}
Я бы внимательно посмотрел, что вы делаете для "обработки" этих исключений. Есть большая вероятность, что блоки catch вообще не нужны, и что вам следует разрешить исключениям распространяться.
Внезапная вспышка гиковости (и чтобы показать, что вы могли бы, но, скорее всего, не должны и не хотите делать): вы можете сделать все это более компонуемым и многоразовым, создавая функции на муха, содержащая логику обработки исключений:
static class ExceptionHandlerExtensionMethods
{
// extend to Func<T> as desired
public static Action Catching<T>(this Action what, Action<T> handler)
where T : Exception
{
return () =>
{
try
{
what();
}
catch (T ex)
{
handler(ex);
}
};
}
}
Теперь вы можете где-то реализовать и повторно использовать функции-обработчики, относящиеся к конкретным типам исключений, и объединить их в обработку исключений каким-либо другим методом.
Чтобы удалить избыточность, вы можете написать вспомогательную функцию, которая добавляет «типичные» функции обработчика исключений к тому, что вы хотите вызвать, и вместо этого вызывать этот декорированный метод.
Почему бы просто не преобразовать код во вспомогательный метод, чтобы сделать это за вас (вы также можете добавить любые новые исключения, которые вам понадобятся в будущем, в HandleException
, что делает его достаточно масштабируемым)?
try
{
... required functionality
}
catch (Exception e)
{
HandleException(e);
throw; // only if you need the exception to propagate to caller
}
private void HandleException(Exception e)
{
if (e is MyException2)
{
... process exception of MyException2 type
}
else if (e is MyExc1)
{
... process exception of MyExc1 type
}
else
{
... process exception of Exception type
}
}
Использование метода CallService от Рида Копси:
void DoSomething(object param1, int param2)
{
this.CallService(() =>
{
// work with param1 and param2 here
}
}
В случаях, когда вам нужно вернуть значение, вам может потребоваться продублировать CallService, чтобы вернуть параметр типа.
T CallService<T>(Func<T> callback) { /* ... */ }
Я знаю, что это плохая практика, но если у вас точно такая же обработка ошибок в каждом операторе catch, почему бы просто не использовать последний оператор catch как catch all?
Это, конечно, предполагает, что все ваши исключения наследуются от Exception.
В похожей ситуации я сделал следующее. Я покажу эту технику в два этапа ...
Шаг 1. Создайте метод, который обеспечивает конкретный контекст выполнения для другого кода:
// this static method is responsible for setting up a context for other code
// to run in; specifically, it provides the exception handling "plumbing":
public static void guarded(Action action)
{ // ^^^^^^^^^^^^^
try // actual code to be executed
{
action();
}
catch (SomeExceptionA ea)
{
// handle exception...
}
catch (SomeExceptionB eb)
{
// handle exception...
}
// etc.
}
Шаг 2. Примените этот контекст к любому фрагменту кода:
Далее , вы просто "оборачиваете" эту обработку исключений вокруг фактического кода в ваших методах:
public void MethodA()
{
guarded(() => // <-- wrap the execution handlers around the following code:
{
// do something which might throw an exception...
});
}
public void MethodB()
{
guarded(() =>
{
// do something which might throw an exception...
});
}
Резюме:
Общая идея здесь состоит в том, чтобы написать функцию ( guarded
в приведенном выше примере), которая устанавливает конкретный контекст выполнения для выполнения другого кода. (Контекст в этом примере обеспечивает обработку исключений.) Код, который должен выполняться в этом контексте, предоставляется как лямбда-функция. Вы даже можете адаптировать функцию создания контекста, чтобы код лямбда-функции мог возвращать значение.