Предложения по созданию многоразового блока try/catch в С#?
У меня есть класс, который содержит около 20-некоторых методов. Каждый из них выполняет обработку сообщений веб-сервисов. Я просто должен был внести в него изменения и понял, что каждый из этих методов имеет ту же самую попытку/уловку вокруг него:
try
{
/* *** actual processing specific to each method goes here *** */
}
catch (FaultException<CustomException> cfex)
{
// common stuff
}
catch (CustomException cfex)
{
// common stuff
}
catch (Exception ex)
{
// common stuff
}
finally
{
FinalizeServiceCall(wsBus, wsMessage, response, logProps);
}
Мой вопрос: вместо того, чтобы иметь такой же блок try/catch в каждом методе, есть ли способ сделать его общим? Мои мысли состояли в том, что .NET имеет такие вещи, как TransactionScope
, который каким-то образом обнаруживает, возникает ли исключение при выходе из этого блока. Есть ли я, я могу использовать что-то подобное, чтобы создать общий блок try/catch? Любые другие идеи?
Ответы
Ответ 1
Я бы сделал это следующим образом:
Создайте метод, содержащий try/catch, и передайте Action
в него и выполните это действие внутри части try:
public void Method1()
{
Action action = () =>
{
// actual processing of Method 1
};
SafeExecutor(action);
}
public void Method1b()
{
SafeExecutor(() =>
{
// actual processing of Method 1
});
}
public void Method2(int someParameter)
{
Action action = () =>
{
// actual processing of Method 2 with supplied parameter
if(someParameter == 1)
...
};
SafeExecutor(action);
}
public int Method3(int someParameter)
{
Func<int> action = () =>
{
// actual processing of Method 3 with supplied parameter
if(someParameter == 1)
return 10;
return 0;
};
return SafeExecutor(action);
}
private void SafeExecutor(Action action)
{
SafeExecutor(() => { action(); return 0; });
}
private T SafeExecutor<T>(Func<T> action)
{
try
{
return action();
}
catch (FaultException<CustomException> cfex)
{
// common stuff
}
catch (CustomException cfex)
{
// common stuff
}
catch (Exception ex)
{
// common stuff
}
finally
{
FinalizeServiceCall(wsBus, wsMessage, response, logProps);
}
return default(T);
}
Две версии SafeExecutor
дают вам возможность обрабатывать методы с и без типов возврата.
Method1b
показывает, что вам не нужна переменная Action
в ваших методах, вы можете включить ее, если считаете, что это более читаемый.
Ответ 2
Есть способы, которыми вы можете сделать это легко - во-первых, для меня я начал использовать АОП, чтобы поймать мои исключения
это эффективно превратит ваш код
try
{
/* *** actual processing specific to each method goes here *** */
}
catch (FaultException<CustomException> cfex)
{
// common stuff
}
catch (CustomException cfex)
{
// common stuff
}
catch (Exception ex)
{
// common stuff
}
finally
{
FinalizeServiceCall(wsBus, wsMessage, response, logProps);
}
во что-то вроде
[HandleException( Exception , FaultException<CustomException>,
"Error Getting Details" )]
public MYType GetDetails( string parameter )
{
//.... call to service
}
используя Postsharp - подробности здесь
альтернативно есть сообщение в блоге Mark Rendle на как поймать исключения в способе функционального программирования - я не пробовал это, хотя
Ответ 3
Вы определили сквозную проблему. Вы можете использовать подход с аспектно-ориентированным программированием (AOP) к этой проблеме. Это можно либо выполнить во время выполнения, используя прокси-сервер, который находится перед вашим классом, либо во время компиляции с помощью инструмента AOP, который изменяет скомпилированный код.
В прошлом я использовал Castle Dynamic Proxy для этого (во время выполнения). В качестве альтернативы вы можете использовать одну из других инфраструктур AOP, таких как PostSharp.
Ответ 4
Если параметры одинаковые или близкие к одному, вы всегда можете передать делегат. Если они не могут, вы можете вызвать код путем отражения и принять параметр "object []", чтобы перейти к вызову.
Ответ 5
Что вы можете сделать, это написать выше код в методе, который принимает действие или Func в качестве параметра, который определяет метод, который должен быть вызван в блоке throw, вместе с его параметрами.
Итак, если вы вызовете M(1, "string")
в своем блоке броска, он станет DoStuff(M, 1, "string")
DoStuff будет выглядеть как
void DoStuff<T1, T2, TResult>(Func<T1, T2, TResult> myMethod, T1 arg1, T2 arg2)
{
try
{
myMethod(arg1, arg2)
}
catch (FaultException<CustomException> cfex)
{
// common stuff
}
catch (CustomException cfex)
{
// common stuff
}
catch (Exception ex)
{
// common stuff
}
finally
{
FinalizeServiceCall(wsBus, wsMessage, response, logProps);
}
}