Предложения по созданию многоразового блока try / catch в C #? - PullRequest
18 голосов
/ 18 мая 2011

У меня есть класс, в котором есть около 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?Есть другие идеи?

Ответы [ 5 ]

47 голосов
/ 18 мая 2011

Я бы сделал это так:

Создайте метод, содержащий 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 в ваших методах, вы можете встроить ее, если выдумаю, что это более читабельно.

7 голосов
/ 18 мая 2011

есть способы, с помощью которых вы можете сделать это легко - во-первых, для меня я начал использовать AOP, чтобы ловить мои исключения

, это фактически превратит ваш код

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 - подробности здесь

в качестве альтернативы есть сообщение в блоге Марк Рендл на как пойматьисключения в функциональном программировании - я не пробовал этот, хотя

5 голосов
/ 18 мая 2011

Вы идентифицировали сквозную проблему .Вы можете использовать подход аспектно-ориентированного программирования (AOP) к этой проблеме.Это можно выполнить во время выполнения с помощью прокси-сервера, который находится перед вашим классом, или во время компиляции с помощью инструмента AOP, который изменяет скомпилированный код.

В прошлом я использовал Castle Dynamic Proxy для этого (во время выполнения).В качестве альтернативы вы можете использовать одну из других AOP-сред, таких как PostSharp .

1 голос
/ 18 мая 2011

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

0 голосов
/ 18 мая 2011

Что вы можете сделать, это написать приведенный выше код в методе, который принимает Action или 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);
        }
}
...