.NET和C#异常。抓住什么是合理的

时间:2009-08-19 07:23:22

标签: c# exception-handling

免责声明,我来自Java背景。我没有做太多的C#。这两个世界之间存在大量的转移,但当然存在差异,其中一个是异常倾向于被考虑的。

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

 try {
   some work
 } catch (Exeption e) {
       commonExceptionHandler();
 }

(原因不重要)。我收到的答复我不太明白:

  

直到.NET 4.0,抓住它是非常糟糕的   例外。这意味着你抓到各种各样   低级致命错误等伪装   错误。这也意味着在事件中   某种腐败   触发这样的例外,任何开放   最后堆栈上的块将是   执行,所以即使   callExceptionReporter函数尝试   记录和退出,甚至可能没有   到那一点(最后的块可能会   再扔一次,或造成更多腐败,   或删除重要的东西   磁盘或数据库)。

我可能比我意识到的更困惑,但我不同意其中一些。请其他人评论。

  1. 据我所知,我们不想接受许多低级别的例外情况。我的commonExceptionHandler()函数可以合理地重新抛出那些。这似乎与this answer相关的问题一致。这说“根据你的上下文,使用catch(...)是可以接受的,只要重新抛出异常即可。”所以我总结使用catch(Exception)并不总是邪恶的,默默地吞下某些例外情况。

  2. 短语“在.NET 4之前,Catch异常非常糟糕”.NET 4中有哪些变化?这是对AggregateException的引用,它可能会给我们一些与我们捕获的异常有关的新事物,但我不认为更改是基本的“不要吞下”规则。

  3. 下一句真的很烦人。这可能是对的吗?

  4.   

    这也意味着在事件中   某种腐败   触发这样的例外,任何开放   最后堆栈上的块将是   执行(最后块可能   再扔一次,或造成更多腐败,   或删除重要的东西   磁盘或数据库)

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

    lowLevelMethod() {
        try {
            lowestLevelMethod();
        } finally {
             some really important stuff
        }
    }
    

    在我的代码中我调用了lowLevel();

     try {
          lowLevel()
     } catch (Exception e) {
           exception handling and maybe rethrowing
     }
    

    无论我是否捕获异常,这对于finally块的执行都没有任何影响。当我们离开lowLevelMethod()时,finally已经运行了。如果最终要做任何坏事,比如我的磁盘损坏,那么它会这样做。我捕捉异常没有任何区别。如果它到达我的异常块我需要做正确的事情,但我不能成为dmis执行最终的原因

5 个答案:

答案 0 :(得分:11)

对于问题#2: 作者的意思是“腐败的国家例外”。它们将在.NET 4.0中引入(CLR团队在2008年的this谈话中宣布了这一点)。

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

可能想要捕获这些例外:

  1. 在main()中 - 写入日志,退出,关闭addin
  2. 非常罕见的情况,当你知道代码抛出这样的异常时(例如某些情况下与本机代码互操作)
  3. 为此,您应该将属性[HandleProcessCorruptedStateException]放在要捕获CorruptedStateException的方法上。

    要详细了解这些例外情况,请参阅this MSDN文章。

答案 1 :(得分:9)

作为一般规则,除非:

,否则不应捕获异常
  1. 您有一个特殊的例外,您可以处理并执行某些操作。但是,在这种情况下,您应该始终检查您是否应该首先考虑并避免例外。

  2. 您位于应用程序的顶层(例如UI),并且不希望将默认行为呈现给用户。例如,您可能需要一个带有“请发送给我们您的日志”样式消息的错误对话框。

  3. 在以某种方式处理异常之后重新抛出异常,例如,如果您回滚数据库事务。

  4. 您的finally块始终执行。您的代码示例是正确的 - 低级方法应该只有tryfinally。例如,非托管调用可能知道它需要处理非托管资源,但这不会暴露给调用它的.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
    }
    

答案 2 :(得分:5)

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

你是正确的,在退出方法之前总是调用finally块(即使在try部分引发异常的情况下)。所以你是否想要在out方法上捕获异常完全取决于你......这不应该影响被调用的finally块。

答案 3 :(得分:1)

关于你的第三个问题:

如果你有

nastyLowLevel() {
  doSomethingWhichMayCorruptSomethingAndThrowsException();
}

MyEvilCatcher() {
  try {
    nastyLowLevel();
  } catch (Exception e) {
    MyExceptionHandler(e);
  }
}

WiseCatcher() {
  try {
    MyEvilCatcher();
  } catch (LowLevelException e) {
    DoSomethingWiseSoFinnalyDontRuinAnything();
  } finally {
    DoSomethingWhichAssumesLowLevelWentOk();
  }
}

我认为你所提出的回答只是意味着一些低级方法可以使用异常来告知某些外部方法必须特别小心。如果你在catch-all-handler中忘记了这个异常并且不重新抛出它们,那么可能会出现问题。

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

答案 4 :(得分:0)

我认为引用的回答是错误的(或者可能是2.0而不是4.0)?这对我来说听起来有点虚假。