使用finally而不是catch

时间:2012-05-23 14:15:08

标签: c# transactions try-catch try-finally

我现在已经看过几次了这个模式:

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

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

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

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

6 个答案:

答案 0 :(得分: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() true 行(第13行)。

答案 1 :(得分:2)

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

但是,您不希望在那里执行任何可以抛出异常的内容。

答案 2 :(得分:2)

我不确定这不仅仅是anecdotal evidence,但我个人使用这种模式是出于一个非常实际的原因:当DoSomething抛出异常时,Visual Studio调试器将会进入{ {1}}在第一个版本中发生异常,而在第二个版本中它将在DoSomething处中断。这允许在throw;清除所有内容之前检查应用程序状态。

Screenshot

答案 3 :(得分: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语句调用回滚更具可读性。

答案 4 :(得分:1)

始终执行

finally,而不仅仅是捕获异常。

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

答案 5 :(得分:0)

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

您提到该项目是从Java移植的。在Java中,您可以 re-throw an exception类似于使用throw;在C#中的方式。您还可以throw a new exception继续维护调用堆栈(等)。第二个在C#中更清晰/更简单(虽然不是很多),第一个具有实际使用Java编写的优势。