.NET和C#例外。什么是合理的捕捉

.NET和C#例外。什么是合理的捕捉,c#,exception-handling,C#,Exception Handling,免责声明,我来自Java背景。我做的不多。这两个世界之间存在着大量的转移,但当然也存在着差异,其中之一就是人们对例外的看法 我最近回答了一个C#问题,建议在某些情况下这样做是合理的: try { some work } catch (Exeption e) { commonExceptionHandler(); } (原因并不重要)。我得到了一个我不太明白的回答: 在.NET4.0之前,很难赶上 例外。这意味着你能抓住各种各样的机会 低级致命错误等伪装 漏洞。这也意味

免责声明,我来自Java背景。我做的不多。这两个世界之间存在着大量的转移,但当然也存在着差异,其中之一就是人们对例外的看法

我最近回答了一个C#问题,建议在某些情况下这样做是合理的:

 try {
   some work
 } catch (Exeption e) {
       commonExceptionHandler();
 }
(原因并不重要)。我得到了一个我不太明白的回答:

在.NET4.0之前,很难赶上 例外。这意味着你能抓住各种各样的机会 低级致命错误等伪装 漏洞。这也意味着 某种腐败 触发这样一个异常,任何打开的 最后,堆栈上的块将被删除 执行,所以即使 callExceptionReporter函数尝试 要登录并退出,它甚至可能无法 到那一点(最后的块可能 再扔一次,或者造成更多的腐败, 或者从列表中删除一些重要的内容 磁盘或数据库)

也许我比我意识到的更困惑,但我不同意其中的一些观点。请其他人发表评论

  • 我知道有很多低级别的异常我们不想接受。我的commonExceptionHandler()函数可以合理地重新引用这些。这似乎与一个相关的问题相一致。它说“根据您的上下文,可以接受使用catch(…),前提是重新抛出异常。”因此我得出结论,使用catch(异常)并不总是邪恶的,默默地吞咽某些异常才是邪恶的

  • 短语“直到.NET4捕获异常是非常糟糕的”在.NET4中有什么变化?这是对AggregateException的引用,它可能会给我们提供一些新的东西来处理我们捕获的异常,但我不认为会改变基本的“不要吞咽”规则

  • 下一个短语真的很麻烦。这是对的吗

  • 这也意味着 某种腐败 触发这样一个异常,任何打开的 最后,堆栈上的块将被删除 已执行(最后一个块可能 再扔一次,或者造成更多的腐败, 或者从列表中删除一些重要的内容 磁盘或数据库)

    我的理解是,如果一些低级代码

    lowLevelMethod() {
        try {
            lowestLevelMethod();
        } finally {
             some really important stuff
        }
    }
    
    在我的代码中,我调用lowLevel()


    无论我是否捕获异常,这对finally块的执行都没有任何影响。当我们离开lowLevelMethod()时,finally已经运行了。如果finally要做任何不好的事情,比如损坏我的磁盘,那么它就会这样做。我抓住了这个例外没有什么区别。如果它到达我的异常块,我需要做正确的事情,但我不能成为dmis最终执行的原因,我认为引用的响应是错误的(或者可能是指2.0而不是4.0)?对我来说这听起来有点假。

    关于你的第三个问题:

    如果你有

    nastyLowLevel() {
      doSomethingWhichMayCorruptSomethingAndThrowsException();
    }
    
    MyEvilCatcher() {
      try {
        nastyLowLevel();
      } catch (Exception e) {
        MyExceptionHandler(e);
      }
    }
    
    WiseCatcher() {
      try {
        MyEvilCatcher();
      } catch (LowLevelException e) {
        DoSomethingWiseSoFinnalyDontRuinAnything();
      } finally {
        DoSomethingWhichAssumesLowLevelWentOk();
      }
    }
    
    我认为您所问的回答只是意味着一些低级方法可能会使用异常来通知某些外部方法必须特别小心。如果您在catch-all处理程序中忘记了这些异常,并且不重新引用它们,则可能会出现问题


    通常,我更喜欢仔细考虑哪些异常可能被抛出,并明确地捕获它们。我仅在生产环境的最外层使用“通用处理程序”,以便记录意外异常,并以格式良好的方式将其呈现给客户(包括向我们发送日志文件的提示)。

    作为一般规则,您不应捕获异常,除非:

  • 你有一个特殊的例外,你可以处理和做一些事情。然而,在这种情况下,您应该始终检查您是否应该首先尝试解释和避免异常

  • 您处于应用程序的顶层(例如UI),不希望向用户显示默认行为。例如,您可能需要一个带有“请向我们发送您的日志”样式消息的错误对话框

  • 在以某种方式处理异常后重新抛出异常,例如,如果回滚一个DB事务

  • 您的
    最终
    块始终执行。您的代码示例是正确的-低级方法应该只有
    try
    finally
    。例如,非托管调用可能知道它需要处理非托管资源,但这不会暴露给调用它的.Net方法。该调用应该在其
    finally
    块中除去非托管资源,调用托管方法可以处理异常,也可以将异常传递给其他方法

    如果异常中存在需要处理的内容,则应重新抛出该异常,例如:

    try {
        conn.BeginTransaction();
        //do stuff
        conn.CommitTransaction();
    }
    catch (Exception) {
        conn.RollbackTransaction(); //required action on any exception
        throw; //re-throw, don't wrap a new ex to keep the stack trace
    }
    finally {
        conn.Dispose(); //always dispose of the resource
    }
    

    我的座右铭是处理您可以(或需要)的事情,让任何其他异常冒泡并在未处理的异常事件中捕获它们

    您是正确的,在退出该方法之前,始终会调用finally块(即使在try部分引发异常的情况下也是如此)。因此,您是否希望捕获out方法上的异常完全取决于您……这不应影响正在调用的finally块。

    对于问题#2: 作者的意思是“腐败状态例外”。它们将在.NET4.0中引入(CLR团队在PDC2008的谈话中宣布了这一点)

    正常捕获块无法捕获损坏的状态异常。示例:访问冲突、内存无效。

    但您可能希望捕获以下异常:

  • 在main()中–写入日志、退出、关闭加载项
  • 非常罕见的情况是,当您知道代码抛出这样的异常时(例如,某些情况下与本机代码互操作)
  • 为此,应将属性[HandleProcessCorruptedStateException]放在要捕获CorruptedStateException的方法处

    要了解更多有关这些例外情况的信息,请参阅MSDN文章。

    我同意您关于#3的看法,但我无法给出确切的答案
    try {
        conn.BeginTransaction();
        //do stuff
        conn.CommitTransaction();
    }
    catch (Exception) {
        conn.RollbackTransaction(); //required action on any exception
        throw; //re-throw, don't wrap a new ex to keep the stack trace
    }
    finally {
        conn.Dispose(); //always dispose of the resource
    }