.Net处理exception时常见的编程错误?

在处理exception时,你所看到的最常见的错误是什么?

看起来exception处理可能是学习如何在.Net中“正确”的最难的事情之一。 特别是考虑到.NET开发人员为了避免常见编程错误而排名第一的答案? 与exception处理有关。

希望通过列举一些最常见的错误,我们都可以学会更好地处理exception。

在处理exception时,你所看到的最常见的错误是什么?

我可以想很多。

首先阅读我关于exception分类的文章,将其归类为令人烦恼的骨头的致命的外生的

http://ericlippert.com/2008/09/10/vexing-exceptions/

一些常见的错误:

  • 未能处理外部例外。
  • 未能处理令人头痛的exception。
  • 抛出令人烦恼的例外的方法的构build。
  • 处理实际上无法处理的exception,如致命exception。
  • 处理隐藏代码中的错误的exception; 不要处理一个头骨的exception,修复这个bug,使其不被抛出

  • 安全错误: 失败到不安全模式

    try { result = CheckPassword(); if (result == BadPassword) throw BadPasswordException(); } catch(BadPasswordException ex) { ReportError(ex); return; } catch(Exception ex) { LogException(ex); } AccessUserData(); 

    看看发生了什么? 我们没有采取不安全的模式。 如果CheckPassword抛出NetworkDriverIsAllMessedUpException,那么我们抓住它,logging它并访问用户的数据,而不pipe密码是否正确 。 没有安全模式; 当你得到任何exception,承担最坏的。

  • 安全错误:直接或间接产生泄露敏感信息的exception。

    这不完全是关于处理代码中的exception,而是关于生成由恶意代码处理的exception。

    好笑的故事。 在.NET 1.0发布给客户之前,我们发现了一个可能调用引发exception的方法的错误:“调用此方法的程序集没有权限确定文件C:\ foo.txt的名称”。 大。 谢谢你让我知道。 什么是阻止说汇编捕捉exception和询问其消息来获取文件名? 没有。 我们在发货之前解决了这个问题

    这是一个直接的问题。 一个间接的问题将是我在LoadPicture ,VBScript中实现的一个问题。 它给出了一个不同的错误消息,这取决于错误的参数是一个目录,一个不是图片的文件,还是一个不存在的文件。 这意味着你可以使用它作为一个非常慢的磁盘浏览器! 通过尝试一大堆不同的东西,你可以逐渐build立一个什么文件和目录在别人的硬盘上的图片。 应该devise例外,以便如果由不可信的代码处理它们,则该代码不会从用户所做的任何事情中获知用户的私人信息,从而导致exception。 (LoadPicture现在提供的帮助较less的错误消息。)

  • 安全和资源pipe理错误:不清理资源的处理程序是等待发生的资源泄漏 。 资源泄漏可以被恶意的部分信任代码用作拒绝服务攻击,故意产生exception情况。

  • 健壮性错误:处理程序必须假定程序状态是混乱的,除非处理特定的外生exception。 最后的块尤其如此。 当你处理一个意想不到的exception时,这是完全可能的,甚至有可能是你的程序中的东西被深深地搞砸了。 你不知道你的任何子系统是否正在工作,如果是,调用它们会使情况变好或变坏。 集中logging错误并尽可能保存用户数据,并尽可能干净地closures。 假设没有什么正确的。

  • 安全性错误: 任何可能具有敌意的代码可以运行之前 ,需要撤销具有安全影响的临时全局状态突变 。 恶意代码可以最后运行之前运行! 有关详细信息,请参阅我的文章:

http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx

重新抛出这样的exception:

 try { // some code here } catch(Exception ex) { // logging, etc throw ex; } 

这杀死了堆栈跟踪,使得可用性更低。 重新抛出的正确方法是这样的:

 try { // some code here } catch(Exception ex) { // logging, etc throw; } 

在很多情况下捕捉所有的exception情况,您应该尝试捕捉特定的exception情况:

 try { // Do something. } catch (Exception exc) { // Do something. } 

而不是:

 try { // Do something. } catch (IOException exc) { // Do something. } 

例外情况应该从最具体到最less的顺序排列。

用一个毫无意义的信息重新审视一个例外。

 try { ... } catch (Exception ex) { throw new Exception("An error ocurred when saving database changes"). } 

你不会相信我经常看到这样的代码在生产中运行。

没有人在谈论看到像这些空的catch块…

  try{ //do something } catch(SQLException sqex){ // do nothing } 

也从来没有使用exception处理来创build备用方法stream程…

  try{ //do something }catch(SQLException sqex){ //do something else } 

不使用IDisposable对象

 File myFile = File.Open("some file"); callSomeMethodWhichThrowsException(myFile); myFile.Close(); 

myFile.Close()之前抛出myFile的终结器(这可能永远不会),因为myFile没有closures。

正确的做法是

 using(File myFile = File.Open("some file")) { callSomeMethodWhichThrowsException(myFile); } 

这被编译器翻译成如下所示:

 File myFile = File.Open("some file"); try { callSomeMethodWhichThrowsException(myFile); } finally { if(myFile != null) myFile.Dispose(); //Dispose() calls Close() } 

所以即使在例外的情况下文件也会被closures。

当重新抛出一个捕获的exception时,忘记设置内部exception

 try { ... } catch (IOException ioException) { throw new AppSpecificException("It was not possible to save exportation file.") // instead of throw new AppSpecificException("It was not possible to save exportation file.", ioException); } 

当我发布这个答案时,我忘了提及,由于安全原因,我们应该总是考虑何时包含内部exception。 正如Eric Lippert 针对这个主题的另一个答案所指出的那样,一些例外可以提供关于服务器实现细节的敏感信息。 因此,如果将要处理exception的调用者不被信任,那么包含内部exception信息并不是一个好主意。

清空:

 //What's the point? catch() {} 

重新抛出:

 //Exceptions are for *adding* detail up the stack catch (Exception ex) {throw ex;} 

假设一个涵盖许多场景的例外是特定的。 一个真实的场景是一个Web应用程序,其中exception处理始终假定所有错误都是会话超时并logging,并在会话超时时报告所有错误。

另一个例子:

 try { Insert(data); } catch (SqlException e) { //oh this is a duplicate row, lets change to update Update(data); } 

loggingException.Message而不是Exception.ToString()

很多时候,我看到代码只loggingexception消息,而应该loggingToString方法的返回值。 ToString提供了比Message更多的关于exception的信息。 它包含除了消息外的内部exception和堆栈跟踪等信息。

试图捕获OutOfMemoryException或StackOverflowException – 那些导致运行时closures,从而在同一个进程(甚至从整个CLR)中捕获它们的方法?

OutOfMemoryException:当没有足够的内存继续执行程序时引发的exception。

“从.NET Framework 2.0版开始,StackOverflowException对象不能被try-catch块捕获,相应的进程默认被终止,因此build议用户编写代码来检测和防止堆栈溢出。

无法捕获捕获处理程序内可能的exception。 这会导致错误的exception向上传播。

例如:

 try { DoImportantWork(); } catch { Cleanup(); throw; } 

如果Cleanup()抛出一个exception会发生什么? 您不希望在此捕获处理程序中看到指向Cleanup()方法的exception。 你想要原来的错误。 您可以尝试logging清除错误,但即使您的日志logging代码需要exception处理,以避免引发exception。

 try { DoImportantWork(); } catch { try { Cleanup(); } catch { // We did our best to clean up, and even that failed. // If you try to log this error, the logging may throw yet another Exception. } throw; } 

错误

 try { // Do something stupid } catch { // ignore the resulting error because I'm lazy, and later spend // a week trying to figure out why my app is crashing all over // the place. } 

更好

 try { /// do something silly. } catch (InvalidOperationException ex) { /// respond, or log it. } catch (Exception e) { /// log it. } 

正常stream量控制使用例外。 例外情况应例外。 如果这是一个好的/预期的操作,则使用返回值等。