11

我现在已经多次看到这种模式:

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

我一直在想:为什么这比使用 catch 进行回滚更好?

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

确保更改在失败时回滚的两种方法之间有什么区别?

4

6 回答 6

4

我在这里发布了一些代码,即使它与问题并不真正相关(稍后将删除)。

有了这个程序:

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 行)。

于 2012-05-23T14:29:13.297 回答
2

finally语句通常用于清理资源。Rollback()如果异常不是回滚事务的唯一原因,则该方法可能可以在那里使用。Close()Dispose()方法是最终出现在 finally 块中的主要候选者。

但是,您不想在那里执行任何可能引发异常的事情。

于 2012-05-23T14:19:11.457 回答
2

我不确定这是否不仅仅是轶事证据,但我个人出于一个非常实际的原因使用了这种模式:当DoSomething抛出异常时,Visual Studio 调试器将DoSomething在第一个版本中发生异常的地方中断,而它会throw;在第二个版本中打破。Rollback这允许在清理所有内容之前检查应用程序状态。

截屏

于 2012-05-23T14:30:08.187 回答
2

如果您不关心此特定代码中使用的是什么类型的异常:

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

最后使用 finally 可以使您的代码比从每个单独的 catch 语句调用回滚更具可读性。

于 2012-05-23T14:31:12.533 回答
1

finally总是被执行,不仅在捕获异常时。

当然,在这种特定情况下,只有在出现错误时才需要回滚,但作为一般模式,这try-finally可能对资源管理更有用(在这种情况下,您通常需要确保您始终Close()Dispose()正确地使用您的资源)。特别是如果代码的作者来自 Java 背景,这个习语更普遍。

于 2012-05-23T14:16:58.293 回答
0

这里的明确目标是Rollback在发生任何错误时调用。两个代码片段都实现了这一目标。第一个使用 finally,它总是运行,它验证try块的最后一行是否已成功到达。第二个捕获任何错误,回滚事务,然后重新抛出捕获的异常。任何一个片段的结果都是抛出的任何异常都将导致回滚,同时仍然冒泡到下一个级别。

您提到该项目是从 Java 移植的。在 Java 中,您可以 重新抛出异常,类似于在 C# 中使用throw;. 您还可以抛出一个仍将维护调用堆栈的新异常(等)。第二个在 C# 中更清晰/更简单(虽然不是很多),第一个具有实际使用 Java 编写的优点。

于 2012-05-23T14:29:34.990 回答