C # Как обрабатывать несколько исключений, которые делают все то же самое? - PullRequest
6 голосов
/ 10 ноября 2009

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

public void LoadControl(ControlDestination controlDestination, string filename, object parameter)
{
    try
    {
        // Get filename with extension
        string file = GetControlFileName(filename);

        // Check file exists
        if (!File.Exists(file))
            throw new FileNotFoundException();

        // Load control from file
        Control control = LoadControl(filename);

        // Check control extends BaseForm
        if (control is BaseForm)
        {
            // Set current application on user control
            ((BaseForm)control).CurrentApplication = this;
            ((BaseForm)control).Parameter = parameter;

            // Set web user control id
            control.ID = filename;

            Panel currentPanel = null;

            switch (controlDestination)
            {
                case ControlDestination.Base:
                    // Set current panel to Base Content
                    currentPanel = pnlBaseContent;
                    // Set control in viewstate
                    this.BaseControl = filename;
                    break;
                case ControlDestination.Menu:
                    // Set current panel to Menu Content
                    currentPanel = pnlMenuContent;
                    // Set control in ViewState
                    this.MenuBaseControl = filename;
                    break;
            }

            currentPanel.Controls.Clear();
            currentPanel.Controls.Add(control);
            UpdateMenuBasePanel();
            UpdateBasePanel();

        }
        else
        {
            throw new IncorrectInheritanceException();
        }
    }
    catch (FileNotFoundException e)
    {
        HandleException(e);
    }
    catch (ArgumentNullException e)
    {
        HandleException(e);
    }
    catch (HttpException e)
    {
        HandleException(e);
    }
    catch (IncorrectInheritanceException e)
    {
        HandleException(e);
    }

}

Вот как выглядит HandleException:

private void HandleException(Exception exception)
{
    // Load error control which shows big red cross
    LoadControl(ControlDestination.Menu, "~/Controls/Error.ascx", null);

    // Store error in database
    DHS.Core.DhsLogDatabase.WriteError(exception.ToString());

    // Show error in errorbox on master
    Master.ShowAjaxError(this, new CommandEventArgs("ajaxError", exception.ToString()));
}

Ответы [ 6 ]

12 голосов
/ 10 ноября 2009

Вы делаете все правильно (вы должны перехватывать только те исключения, которые собираетесь обработать, и нет способа отловить более одного типа исключения в одном блоке catch), но в качестве альтернативы вы можете просто catch(Exception ex), проверьте тип исключения, и если это не то, что вы ожидаете, просто throw снова, что-то вроде этого:

var exceptionTypes=new Type[] {
    typeof(FileNotFoundException),
    typeof(ArgumentNullException),
    //...add other types here
};

catch(Exception ex) {
    if(exceptionTypes.Contains(ex.GetType()) {
        HandleException(ex);
    } else {
        throw;
    }
}

ОБНОВЛЕНИЕ: С C # 6 (поставляется вместе с Visual Studio 2015) вы можете сделать следующее:

catch(Exception ex) when (exceptionTypes.Contains(ex.GetType()) {
    HandleException(ex);
}
3 голосов
/ 10 ноября 2009

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

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

    public static void Handle<T>(Action action, Action<T> handler)
        where T : Exception
    {
        try
        {
            action();
        }
        catch (T exception)
        {
            handler(exception);
        }
    }

    public static void Handle<T1, T2>(Action action, Action<Exception> handler)
        where T1 : Exception
        where T2 : Exception
    {
        try
        {
            action();
        }
        catch (T1 exception)
        {
            handler(exception);
        }
        catch (T2 exception)
        {
            handler(exception);
        }
    }

    public static void Handle<T1, T2, T3>(Action action, Action<Exception> handler)
        where T1 : Exception
        where T2 : Exception
        where T3 : Exception
    {
        try
        {
            action();
        }
        catch (T1 exception)
        {
            handler(exception);
        }
        catch (T2 exception)
        {
            handler(exception);
        }
        catch (T3 exception)
        {
            handler(exception);
        }
    }

    public static void Handle<T1, T2, T3, T4>(Action action, Action<Exception> handler)
        where T1 : Exception
        where T2 : Exception
        where T3 : Exception
        where T4 : Exception
    {
        try
        {
            action();
        }
        catch (T1 exception)
        {
            handler(exception);
        }
        catch (T2 exception)
        {
            handler(exception);
        }
        catch (T3 exception)
        {
            handler(exception);
        }
        catch (T4 exception)
        {
            handler(exception);
        }
    }
}

public class Example
{
    public void LoadControl()
    {
        Exceptions.Handle<FileNotFoundException, ArgumentNullException, NullReferenceException>(() => LoadControlCore(10), GenericExceptionHandler);   
    }

    private void LoadControlCore(int myArguments)
    {
        //execute method as normal
    }

    public void GenericExceptionHandler(Exception e)
    {
        //do something
        Debug.WriteLine(e.Message);
    }        
}
3 голосов
/ 10 ноября 2009

Я бы изменил рефакторинг следующим образом: -

public class Sample
{
    public void LoadControl( ControlDestination controlDestination, string filename, object parameter )
    {
        HandleExceptions( HandleException, () =>
        {
            //.... your code
        } );
    }

    private void HandleExceptions( Action<Exception> handler, Action code )
    {
        try
        {
            code();
        }
        catch ( FileNotFoundException e )
        {
            handler( e );
        }
        catch ( ArgumentNullException e )
        {
            handler( e );
        }
        catch ( HttpException e )
        {
            handler( e );
        }
        catch ( IncorrectInheritanceException e )
        {
            handler( e );
        }
    }

    private void HandleException( Exception exception )
    {
        // ....
    }
}

Если бы я использовал VB.NET, я бы использовал фильтры исключений для выполнения серии перехватов. Но поскольку мы используем C #, у вас есть наиболее эффективный подход, а не

private void HandleExceptions( Action<Exception> handler, Action code )
    {
        try
        {
            code();
        }
        catch ( Exception e )
        {
            if ( e is FileNotFoundException
                || e is ArgumentNullException
                || e is HttpException
                || e is IncorrectInheritanceException )
                handler( e );
            else
                throw;
        }
    }
0 голосов
/ 10 ноября 2009

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

public void LoadControl(ControlDestination controlDestination, string filename, object parameter)
{
    try
    {
        // Get filename with extension
        string file = GetControlFileName(filename);

        // Check file exists
        if (!File.Exists(file))
            throw new FileNotFoundException();

        // Load control from file
        Control control = LoadControl(filename);

        // Check control extends BaseForm
        if (control is BaseForm)
        {
            // Set current application on user control
            ((BaseForm)control).CurrentApplication = this;
            ((BaseForm)control).Parameter = parameter;

            // Set web user control id
            control.ID = filename;

            Panel currentPanel = null;

            switch (controlDestination)
            {
                case ControlDestination.Base:
                    // Set current panel to Base Content
                    currentPanel = pnlBaseContent;
                    // Set control in viewstate
                    this.BaseControl = filename;
                    break;
                case ControlDestination.Menu:
                    // Set current panel to Menu Content
                    currentPanel = pnlMenuContent;
                    // Set control in ViewState
                    this.MenuBaseControl = filename;
                    break;
            }

            currentPanel.Controls.Clear();
            currentPanel.Controls.Add(control);
            UpdateMenuBasePanel();
            UpdateBasePanel();

        }
        else
        {
            throw new IncorrectInheritanceException();
        }
    }
    catch (Exception e)
    {
        HandleException(e);
    }
}


public void HandleException(Exception e)
{
    if (e is FileNotFoundException
            || e is ArgumentNullException
            || e is HttpException
            || e is IncorrectInheritanceException)
    {
        // Load error control which shows big red cross
        LoadControl(ControlDestination.Menu, "~/Controls/Error.ascx", null);

        // Store error in database
        DHS.Core.DhsLogDatabase.WriteError(exception.ToString());

        // Show error in errorbox on master
        Master.ShowAjaxError(this, new CommandEventArgs("ajaxError", exception.ToString()));
    }
}
0 голосов
/ 10 ноября 2009

Я собираюсь ответить на это не зависящим от языка образом:

1. То, что вы сделали сейчас, правильно. В этом нет ничего плохого, кроме того, что это может стать утомительным, если вы делаете это много раз.

2. Поймай самую общую форму исключения, которое есть. Просто

catch(Exception e)
{
    ...
}

3. Может быть, вы хотите поймать только некоторые исключения, без перехвата всех исключений , что вы и сделали бы, если бы только что сделали # 2.

Сделайте то, что вы сделали в # 2, плюс измените HandleException, чтобы обрабатывать только определенные типы исключений. Таким образом, вам когда-либо придется вводить команду tem out только один раз, и она все еще более компактна, чем выше.

private void HandleException(Exception e) throws Excpetion
{
    // Reject some types of exceptions
    if (!((e is FileNotFoundException) ||
        (e is ArgumentNullException) ||
        (e is HttpException ) ||
        (e is IncorrectInheritanceException )))
    {
        throw;
    }

    //Rest of code
    ...
}

Edit:

Я вижу, что Konamiman имеет улучшенную версию этого третьего варианта. Я говорю пойти на это.

0 голосов
/ 10 ноября 2009

Напишите так:

try
{
  // code that throws all sorts of exceptions
}
catch(Exception e)
{
  HandleException(e);
}

edit: обратите внимание, что это прямой ответ на ваш вопрос, а не комментарий о том, является ли это рекомендуемой практикой.

edit2: однако вы можете проверить в своей функции, является ли тип e конкретным списком исключений, и если это не так, вы можете сбросить его. Производительность обработки исключений не является проблемой, поскольку она должна быть ... в первую очередь исключительной.

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