Использование наконец вместо улова
Я видел этот шаблон несколько раз сейчас:
bool success = false;
try
{
DoSomething();
success = true;
}
finally
{
if (!success)
Rollback();
}
И мне было интересно: почему это лучше, чем использовать catch для отката?
try
{
DoSomething();
}
catch
{
Rollback();
throw;
}
Каковы различия между двумя способами обеспечения того, чтобы изменения откатывались при сбое?
6 ответов
Четкая цель здесь состоит в том, чтобы вызвать Rollback
быть вызванным в случае любой ошибки. Оба фрагмента кода достигают этой цели. Первый использует finally, который всегда выполняется, который проверяет, что последняя строка try
блок был успешно достигнут. Вторая перехватывает любые ошибки, откатывает транзакцию и затем повторно генерирует исключение, которое было перехвачено. Результатом любого фрагмента является то, что любые сгенерированные исключения приведут к откату, в то же время поднимаясь до следующего уровня.
Вы упомянули, что проект был портирован с Java. В Java вы можете повторно генерировать исключение, как в C#, используя throw;
, Вы также можете выдать новое исключение, которое все еще будет поддерживать стек вызовов (и др.). Второе немного яснее / проще в C# (хотя и немного), а первое имеет преимущество в том, что фактически работает в Java как написано.
Я публикую здесь некоторый код, даже если он на самом деле не имеет отношения к вопросу (удаляю позже).
С помощью этой программы:
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).
finally
Выписка обычно используется для очистки ресурсов. Rollback()
метод можно использовать там, если исключения не являются единственными причинами для отката транзакции. Close()
или же Dispose()
методы являются первыми кандидатами для попадания в блок finally.
Однако вы не хотите выполнять там что-либо, что может генерировать исключения.
Я не уверен, что это не просто случайное свидетельство, но я лично использовал эту модель по очень практической причине: когда DoSomething
выдает исключение, отладчик Visual Studio сломается DoSomething
где исключение происходит в первой версии, в то время как оно сломается на throw;
во второй версии. Это позволяет проверять состояние приложения перед Rollback
все почистил.
Если вам неважно в этом конкретном коде, какой тип исключения вы используете:
try
{
DoSomething();
ok = true;
}
finally
{
if(!ok)Rollback();
}
Это сохранит Call Stack в его первоначальном виде на 100%. Также, если вы используете обработку исключений следующим образом:
try
{
DoSomething();
ok = true;
}
catch(FirstExcetionType e1)
{
//do something
}
catch(SecondExcetionType e2)
{
//do something
}
catch(Exception e3)
{
//do something
}
finally
{
if(!ok)Rollback();
}
использование finally в конце может сделать ваш код более читабельным, чем вызов отката из каждого оператора catch.
finally
всегда выполняется, а не только при отлове исключений.
Конечно, в данном конкретном случае откат необходим только при возникновении ошибки, но в общем случае try-finally
может быть более полезным для управления ресурсами (где часто вам нужно убедиться, что вы всегда Close()
или же Dispose()
вашего ресурса правильно). Особенно, если автор кода исходит из Java-фона, где эта идиома более распространена.