代码之家  ›  专栏  ›  技术社区  ›  graham.reeds

处理异常时.NET中常见的编程错误?[关闭]

  •  38
  • graham.reeds  · 技术社区  · 6 年前

    在处理异常时,您看到的最常见的错误是什么?

    似乎异常处理是学习如何在.NET中“正确”操作的最困难的事情之一。特别是考虑到目前排名第一的答案 Common programming mistakes for .NET developers to avoid? 与异常处理相关。

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

    14 回复  |  直到 11 年前
        1
  •  44
  •   Eric Lippert    11 年前

    在处理异常时,您看到的最常见的错误是什么?

    我能想很多。

    首先阅读我的文章,将异常分类为 烦恼的 , 笨头笨脑的 , 致命的 外源性 :

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

    一些常见错误:

    • 未能处理外部异常。
    • 未能处理恼人的异常。
    • 构造引发烦人异常的方法。
    • 处理实际上无法处理的异常,如致命异常。
    • 处理在代码中隐藏bug的异常;不要处理无头异常,修复bug,使其不会首先被抛出。

    • 安全错误: 不安全模式故障

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

      看到了吗?我们未能进入不安全模式。如果checkpassword引发networkdriverisallmessupException,那么我们捕获它,记录它,然后 访问用户数据,无论密码是否正确 . 无法进入安全模式;当您遇到任何异常时,请假设最坏的情况。

    • 安全错误:产生异常 泄漏敏感信息 直接或间接。

      这并不完全是关于处理代码中的异常,而是关于生成由敌对代码处理的异常。

      有趣的故事。在.NET 1.0交付给客户之前,我们发现了一个bug,在该bug中,可以调用引发异常的方法“调用此方法的程序集没有权限确定文件C:\foo.txt的名称”。伟大的。谢谢你告诉我。如何阻止所述程序集捕获异常并询问其消息以获取文件名?没有什么。我们在装船前把它修好了。

      这是一个直接的问题。一个间接的问题就是我在 LoadPicture ,在VBScript中。根据错误参数是目录、不是图片的文件还是不存在的文件,它给出了不同的错误消息。这意味着您可以将其用作非常慢的磁盘浏览器!通过尝试一大堆不同的东西,你可以逐渐建立一张图片,显示某人硬盘上的文件和目录。异常的设计应确保,如果它们是由不可信的代码处理的,则该代码不会从用户所做的导致异常的任何操作中学习到任何用户的私有信息。(LoadPicture现在提供的错误信息不太有用。)

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

    • 稳健性错误:处理程序必须 假设程序状态混乱 除非处理特定的外部异常。在finally块中尤其如此。当您处理一个意外的异常时,完全有可能,甚至有可能是程序中的某个严重混乱。您不知道您的任何子系统是否工作,如果工作正常,调用它们是否会使情况好转或恶化。如果可能,集中精力记录错误并保存用户数据,然后尽可能干净地关闭。假设没有什么是正确的。

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

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

        2
  •  25
  •   Adam Lear    14 年前

    重新引发这样的异常:

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

    这会杀死堆栈跟踪,使其更不可用。正确的重排方式如下:

    try 
    { 
       // some code here
    }
    catch(Exception ex)
    {
       // logging, etc
       throw;
    }
    
        3
  •  12
  •   Jay Riggs    14 年前

    捕获所有异常在许多情况下,您应尝试捕获特定的异常:

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

    而不是:

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

    例外情况应从最具体到最少排序。

        4
  •  10
  •   CARLOS LOTH    14 年前

    用无意义的消息重新引发异常。

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

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

        5
  •  9
  •   Srikar Doddi    14 年前

    没有人在谈论看到像这样的空捕获块……

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

    也不要使用异常处理来创建替代方法流…

     try{  
         //do something  
    
     }catch(SQLException sqex){  
    
         //do something else  
     }
    
        6
  •  7
  •   BlueRaja - Danny Pflughoeft    14 年前

    不使用 using IDisposable 物体 :

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

    在调用MyFile的终结器(可能永远不会调用)之前,不会关闭MyFile,因为之前引发了异常 myFile.Close() 被叫来。

    正确的方法是

    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()
    }
    

    所以即使面对异常,文件也会关闭。

        7
  •  6
  •   Community CDub    7 年前

    当重新引发捕获的异常时,忘记设置内部异常

    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);
    }
    

    当我发布这个答案时,我忘记了提到我们应该总是考虑什么时候包括内部异常,或者不是出于安全原因。AS Eric Lippert 指出 another answer for this topic ,一些异常可以提供有关服务器实现详细信息的敏感信息。因此,如果将要处理异常的调用者不受信任,那么最好包括内部异常信息。

        8
  •  4
  •   Dave Swersky    14 年前

    空渔获量:

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

    再投掷:

    //Exceptions are for *adding* detail up the stack
    catch (Exception ex)
    {throw ex;}
    
        9
  •  3
  •   MatthewMartin muthu    14 年前

    假设一个包含许多场景的异常是特定的。真实场景是一个Web应用程序,在该应用程序中,异常处理始终假定所有错误都是会话超时,并在会话超时时记录和报告所有错误。

    另一个例子:

    try
    {
         Insert(data);
    }
    catch (SqlException e)
    {
       //oh this is a duplicate row, lets change to update
       Update(data);
    }
    
        10
  •  3
  •   CARLOS LOTH    14 年前

    记录exception.message而不是exception.toString()。

    很多时候,我看到代码只记录异常消息,而它应该记录toString方法的返回。ToString提供了更多关于异常的信息,而不是消息。除了消息之外,它还包含诸如内部异常和堆栈跟踪之类的信息。

        11
  •  1
  •   Michael Stum    14 年前

    试图抓住 OutOfMemoryException StackOverflowException -这些会导致运行时关闭,因此可以从同一进程(甚至从整个CLR)中捕获它们。

    OutofMemoryException:当内存不足,无法继续执行程序时引发的异常。

    “从.NET Framework 2.0版开始,Try-Catch块无法捕获StackOverflowException对象,默认情况下终止相应的进程。因此,建议用户编写代码以检测和防止堆栈溢出。”

        12
  •  1
  •   Paul Williams    14 年前

    未能在catch处理程序中捕获可能的异常。这可能导致错误的异常向上传播。

    例如:

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

    如果发生了什么 Cleanup() 引发异常?您不希望看到指向此catch处理程序中cleanup()方法的异常。您需要原始错误。您可以尝试记录清除错误,但即使您的日志代码也需要异常处理,以避免从中抛出异常。

    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;
    }
    
        13
  •  0
  •   3Dave    14 年前

    错误的

    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.
    }
    
        14
  •  0
  •   Donnie    14 年前

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