Подтвердить что ты не робот

Предложения по созданию многоразового блока 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? Любые другие идеи?

4b9b3361

Ответ 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);
        }
}