Как избежать исключений, ловит copy-paste в .NET - PullRequest
7 голосов
/ 17 июня 2010

Работа с .NET Framework У меня есть служба с набором методов, которые могут генерировать несколько типов исключений: MyException2, MyExc1, Exception ... Чтобы обеспечить правильную работу для всех методов, каждый из них содержит следующие разделы: *

[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
}

Очень скучно иметь один и тот же материал в КАЖДОМ методе обслуживания (каждый метод имеет свой набор параметров) с точно такими же функциями обработки исключений ...

Есть ли возможность "группировать" эти catch-секции и использовать только одну строку (что-то похожее на макросы C ++)? Возможно, что-то новое в .NET 4.0 связано с этой темой?

Спасибо.

P.S. Любые мысли приветствуются.

Ответы [ 7 ]

6 голосов
/ 17 июня 2010

Если обработка исключений абсолютно одинакова во всех ваших методах, вы можете сделать что-то вроде:

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
}
5 голосов
/ 17 июня 2010

Почему бы просто не преобразовать код в вспомогательный метод, чтобы сделать это для вас (вы также можете добавить любые новые исключения, которые вам понадобятся в будущем, к 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
    }
}
2 голосов
/ 17 июня 2010

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

1 голос
/ 17 июня 2010

Я сделал следующее в аналогичной ситуации. Я покажу технику в два этапа ...


Шаг 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 в вышеприведенном примере), которая устанавливает конкретный контекст выполнения для запуска другого кода. (Контекст в этом примере обеспечивает обработку исключений.) должен быть выполнен в этом контексте, предоставляется как лямбда-функция. Вы даже можете адаптировать функцию создания контекста, чтобы код в лямбда-функции мог возвращать значение.

1 голос
/ 17 июня 2010

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

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);
             }
         };
    }
}

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

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

0 голосов
/ 17 июня 2010

Использование метода CallService от Рида Копси:

void DoSomething(object param1, int param2)
{
    this.CallService(() =>
    {
         // work with param1 and param2 here
    }
}

В случаях, когда вам нужно вернуть значение, вам может потребоваться продублировать CallService для возврата параметра типа.

T CallService<T>(Func<T> callback) { /* ... */ }
0 голосов
/ 17 июня 2010

Я знаю, что это плохая практика, но если в каждом операторе catch используется одинаковая обработка ошибок, то почему бы просто не использовать последний оператор catch в качестве catch all?

Это, конечно, предполагает, что все ваши исключения наследуются от Exception.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...