C #: Эквивалент блока python try / catch / else - PullRequest
7 голосов
/ 04 марта 2010

В Python есть такой полезный код обработки исключений:

try:
    # Code that could raise an exception
except Exception:
    # Exception handling
else:
    # Code to execute if the try block DID NOT fail

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

Предполагая, что эта функция или что-то подобное не существует, это стандартная практика, чтобы поместить нормальный код вtry блок или после блока catch?

Причина, по которой я спрашиваю, состоит в том, что у меня есть следующий код:

if (!IsReadOnly)
{
    T newobj;
    try
    {
        newobj = DataPortal.Update<T>(this);

        List<string> keys = new List<string>(BasicProperties.Keys);
        foreach (string key in keys)
        {
            BasicProperties[key] = newobj.BasicProperties[key];
        }
    }
    catch (DataPortalException)
    {
        // TODO: Implement DataPortal.Update<T>() recovery mechanism
    }
}

Для чего требуется, чтобы нормальный код был в блоке tryпотому что в противном случае, если исключение было вызвано и впоследствии обработано, newobj было бы не назначено, но кажется неестественным иметь столько кода в блоке try, который не связан с DataPortalException.Что делать?

Спасибо

Ответы [ 10 ]

9 голосов
/ 04 марта 2010

Я бы предпочел видеть остальную часть кода вне try / catch, чтобы было ясно, откуда исходит исключение, которое вы пытаетесь перехватить, и что вы случайно не поймаете исключение, которое не пытались поймать.

Я думаю, что наиболее близким эквивалентом Python try / catch / else является использование локальной логической переменной, чтобы запомнить, было ли выброшено исключение.

bool success;

try
{
    foo();
    success = true;
}
catch (MyException)
{
    recover();
    success = false;
}

if (success)
{
    bar();
}

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

7 голосов
/ 04 марта 2010

Варварское решение: создайте класс Else, полученный из Exception, бросьте его экземпляр в конце блока try и используйте catch (Else) {...} для обработки других вещей.

Я чувствую себя таким грязным.

3 голосов
/ 04 марта 2010

Это может быть понижено, но у c # нет goto (заметьте, у меня почти нет знания c #, поэтому я не знаю, работает ли это)

как насчет

try 
{ 
...
} 
catch(Exception ex) 
{ 
...
goto Jump_past_tryelse
} 
...//Code to execute if the try block DID NOT fail

Jump_past_tryelse:
...
2 голосов
/ 04 марта 2010

Позвольте мне повторить идею из подобного вопроса StackOverflow . Вы не можете сделать это напрямую, но вы можете написать метод, который инкапсулирует необходимое вам поведение. Посмотрите на оригинальный вопрос, чтобы узнать, как реализовать метод (если вы не знакомы с лямбда-выражениями и Func делегатами) Использование может выглядеть так:

TryExceptRaise(() => { 
    // code that can throw exception
  }, (Exception e) => { 
    // code to run in case of an exception
    return (...); 
  }, () => {
    // code to run if there is no exception
    return (...);
  });
2 голосов
/ 04 марта 2010

Просто поместите свой блок "else" перед уловом. Тогда он будет выполняться только в том случае, если выполнение кода достигнет этой точки:

try
{
    fee();
    fi();
    foe();
    fum();

    /// put your "else" stuff here. 
    /// It will only be executed if fee-fi-foe-fum did not fail.
}
catch(Exception e)
{
    // handle exception
}

Учитывая это, я не вижу использования try..catch ... иначе, если в описании ОП отсутствует что-то важное.

2 голосов
/ 04 марта 2010

C # не имеет такой концепции, поэтому у вас есть только три варианта,

  • поместите код else внутри попытки.
  • поместите код else вне блока try catch, используйте локальную переменную, чтобы указать успех или неудачу, и блок if вокруг вашего кода else.
  • поместите код else в блок finally, используйте локальную переменную, чтобы указать успех или неудачу, и блок if перед вашим кодом else.
1 голос
/ 10 мая 2017

С C # версия 7, вы можете использовать локальные функции для эмуляции этого поведения:

Пример 1: (начиная с версии C # 7)

void Main()
{
    void checkedCode()
    {
        try 
        {
            foo();
        }
        catch (Exception ex)
        {
            recover();
            return;
        }
        // ElseCode here
    }
    checkedCode();
}

Если вы предпочитаете лямбда-синтаксис, , вы также можете объявить метод запуска

void Run(Action r) { r(); }

, который должен присутствовать только один раз в вашем коде, а затем использовать шаблон для анонимных методов следующим образом

Пример 2: (более старые версии C # и версия C # 7)

Run(() => {
    try
    {
        foo();
    }
    catch (Exception)
    {
        recover();
        return;
    }
    // ElseCode here
});

везде, где вам нужно заключить код в безопасный контекст.

Попробуйте в DotNetFiddle


Примечания:

  • В обоих примерах создается контекст функции, так что мы можем использовать return; для выхода при ошибке.
  • Вы можете найти шаблон, подобный тому, который используется в Пример 2 в JavaScript: Самовывоз анонимных функций (например, JQuery использует их). Поскольку в C # вы не можете вызывать себя самостоятельно, используется вспомогательный метод Run.
  • Поскольку Run не обязательно должна быть локальной функцией, Пример 2 также работает со старыми версиями C #
0 голосов
/ 10 мая 2017
if (!IsReadOnly)
        {
            T newobj;
            bool Done;
            try
            {
                newobj = DataPortal.Update<T>(this);
                List<string> keys = new List<string>(BasicProperties.Keys);
                foreach (string key in keys)
                {
                    BasicProperties[key] = newobj.BasicProperties[key];
                }
                Done = true;
            }
            catch (DataPortalException)
            {
                // TODO: Implement DataPortal.Update<T>() recovery mechanism
                Done = false;
            }
            finally
            {
                if (newobj != null && Done == false)
                {
                    List<string> keys = new List<string>(BasicProperties.Keys);
                    foreach (string key in keys)
                    {
                        BasicProperties[key] = newobj.BasicProperties[key];
                    }
                }
            }
        }
0 голосов
/ 04 марта 2010

это было бы пустое утверждение, как хиты

try 
{ 
    somethingThatCanThrow(); 
} 
catch(Exception ex) 
{ 
    LogException(ex); 
    return;
} 
ContinueFlow();
0 голосов
/ 04 марта 2010

Вы можете сделать что-то вроде этого:

if (!IsReadOnly)
{
    T newobj = null;
    try
    {
        newobj = DataPortal.Update<T>(this);    
    }
    catch (DataPortalException)
    {
        // TODO: Implement DataPortal.Update<T>() recovery mechanism
    }
    if (newobj != null)
    {
        List<string> keys = new List<string>(BasicProperties.Keys);
        foreach (string key in keys)
        {
            BasicProperties[key] = newobj.BasicProperties[key];
        }
    }
}
...