【问题标题】:Using finally instead of catch使用 finally 而不是 catch
【发布时间】:2012-05-30 02:40:24
【问题描述】:

这种模式我已经见过几次了:

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 这是有道理的。我没有意识到 Java 不允许您正确地重新抛出异常。在这里,我认为这将与关键部分等有关......
    • @configurator:既然你提到了,我想可能确实与Constrained Execution Regions有联系。
    • -1 表示“在 Java 中无法重新抛出异常” - apart from throw e, that is
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      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】:

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

        有了这个程序:

        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 所在的行是 true 行,其中foo() 已被调用(第 13 行)。

        【讨论】:

        • +1,点了,感谢您花时间创建示例。我认为您不一定需要删除它,因为它说明了问题中两种方法之间的区别(而且评论肯定太长了)。
        【解决方案5】:

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

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

        【讨论】:

        • 但是在没有异常的情况下success会为假,所以finally子句是空操作。
        • 鉴于我看到的项目实际上是从 Java 移植过来的,Java 背景评论确实有意义。
        • @configurator finally 子句不会是空操作,因为总会有比较
        • @Richard:我的意思是它不会有任何可见的效果,并不是说它不会进行任何计算。
        【解决方案6】:

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

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

        【讨论】:

        • 正如我所提到的,在 finally 语句中进行资源清理是很常见的。这样您就不必重新抛出。
        • @configurator 很有趣,它是迄今为止评分最高的,还有其他类似的,也许编辑问题以引发更符合您的愿望的响应?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-09
        • 2018-10-24
        • 1970-01-01
        • 2012-06-19
        • 2013-05-18
        • 2018-03-23
        相关资源
        最近更新 更多