Использование, наконец, вместо улова

Я видел этот шаблон несколько раз:

        bool success = false;
        try
        {
            DoSomething();
            success = true;
        }
        finally
        {
            if (!success)
                Rollback();
        }

И мне было интересно: почему это лучше, чем использование catch для откатов?

        try
        {
            DoSomething();
        }
        catch
        {
            Rollback();
            throw;
        }

В чем разница между двумя способами проверки отката изменений при сбое?

Ответ 1

Ясная цель здесь - вызвать Rollback для вызова в случае какой-либо ошибки. Оба фрагмента кода достигают этой цели. Первый использует окончательно, который всегда выполняется, который проверяет, что последняя строка блока try была успешно достигнута. Второй ловит любые ошибки, откатывает транзакцию и затем перебрасывает исключение, которое было обнаружено. Результат любого фрагмента заключается в том, что любые выброшенные исключения будут приводить к откату, пока все еще пузырится до следующего уровня.

Вы упомянули, что проект был перенесен с Java. В Java вы можете перебросить исключение аналогично тому, как вы можете использовать С# с помощью throw;. Вы также можете создать новое исключение, которое все равно будет поддерживать стек вызовов (et al.). Во-вторых, это немного яснее/проще в С# (хотя и не так много), и первое имеет преимущество фактически работающего в Java как написано.

Ответ 2

Я размещаю здесь некоторый код, даже если это не связано с вопросом (удаляем позже).

С помощью этой программы:

using System;

namespace testcs
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                try
                {
                    foo();
                    foo();
                    foo();
                }
                catch
                {
                    throw;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void foo()
        {
            throw new Exception("oops");
        }
    }
}

Сохраняется трассировка стека (смотри номера строк!), но внутри функции main вы увидите строку "19", строка, где throw вместо этого - истинная строка, в которой была вызвана foo() ( строка 13).

Ответ 3

Оператор finally обычно используется для очистки ресурсов. Метод Rollback() может быть хорошо использован там, если исключения не являются единственными причинами откат транзакции. Close() или Dispose() - это простые кандидаты, которые попадают в блок finally.

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

Ответ 4

Я не уверен, что это не просто анекдотические данные, но я лично использовал этот шаблон по очень практической причине: Когда DoSomething генерирует исключение, отладчик Visual Studio будет разбит на DoSomething, где исключение возникает в первой версии, а во второй версии оно будет разбито на throw;. Это позволяет проверить состояние приложения до того, как Rollback очистит все.

Screenshot

Ответ 5

Если вам не нравится этот код, какой тип исключения вы используете:

try
{
   DoSomething();
   ok = true;
}
finally
{
    if(!ok)Rollback();
}

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

try
{
   DoSomething();
   ok = true;
}
catch(FirstExcetionType e1)
{
    //do something
}
catch(SecondExcetionType e2)
{
    //do something
}
catch(Exception e3)
{
    //do something
}
finally
{
    if(!ok)Rollback();
}

используя, наконец, в конце, может сделать ваш код более удобочитаемым, чем вызов отката от каждого отдельного оператора catch.

Ответ 6

finally всегда выполняется не только для обнаружения исключений.

Конечно, в этом конкретном случае откат необходим только тогда, когда произошла ошибка, но в качестве общего шаблона try-finally может быть более полезна для управления ресурсами (где часто вам необходимо убедиться, что вы всегда Close() или Dispose() вашего ресурса). Особенно, если автор кода исходит из фона Java, где эта идиома более распространена.