C# 什么是;“仅捕获您可以处理的异常”;真的吗?

C# 什么是;“仅捕获您可以处理的异常”;真的吗?,c#,exception-handling,C#,Exception Handling,我的任务是为我正在从事的.NET/C项目编写一份异常处理策略和指导方针文档。我很难做到这一点。关于如何/何时抛出、捕获、包装异常,有大量可用的信息,但我希望描述除了包装和抛出异常之外,在catch块中应该进行哪些操作 try { DoSomethingNotNice(); } catch (ExceptionICanHandle ex) { //Looking for examples of what people are doing in catch blocks //oth

我的任务是为我正在从事的.NET/C项目编写一份异常处理策略和指导方针文档。我很难做到这一点。关于如何/何时抛出、捕获、包装异常,有大量可用的信息,但我希望描述除了包装和抛出异常之外,在catch块中应该进行哪些操作

try
{
   DoSomethingNotNice();
}
catch (ExceptionICanHandle ex)
{
   //Looking for examples of what people are doing in catch blocks
   //other than throw or wrapping the exception, and throwing.
}

提前谢谢

这正是我的意思。如果您希望正在运行的代码抛出异常,并且当抛出异常时,您的代码知道发生了什么错误以及如何继续,那么捕获异常并处理它

基本上,规则的存在是为了防止反模式,例如:

try
{
   ...
}
catch(Exception ex)
{
   throw;
}
这里的catch只不过是在释放调用堆栈时增加了一个减速。如果你真的不想做任何事情,除了你捕获的例外,你甚至不应该为捕获而烦恼

一个相关但更有效的情况是,您不关心抛出的异常,但在所有情况下都需要清理。在这种情况下,跳过捕捉;你不需要它,只要试一下就行了

编辑:要回答帖子中的问题,而不仅仅是主题,您可以编写如下规则:“不要编写不起任何作用的try-catch语句,或者只重新调用捕获的异常。所有catch语句都应该执行与抛出的异常相关的增值操作。”

例如,假设您正试图使用用户登录应用程序时提供的凭据连接到SQL Server实例。很多事情都可能出错,有些是你无法预料的,有些是你应该做的

  • 服务器没有响应-您可以重试;可能在catch中递归调用连接方法,使用“重试计数器”来中断本来无限的循环
  • 用户身份验证失败-在对话框上以红色显示友好(或不太友好,但简洁易懂)消息
  • 用户无权连接到指定的数据库-取决于您的安全设置;在大多数办公室中,您应该向DBA发送电子邮件,因为这意味着他创建了登录名,但忘记了分配适当的权限
  • 网络不可用:您可以通过登录对话框或新对话框上的错误警告用户,重试几次,等等
  • 被零除-WTF?在登录过程中,什么可能导致Div为零?您不希望出现这种异常,您不知道在这种情况下发生了什么错误,因此无法继续运行代码,所以不要捕获它
  • 如果出现任何问题,出于审计/安全目的,您可能希望将消息记录到文件或共享资源中。如果您想继续执行,这应该发生在较低的级别上,如果您想在之后优雅地关闭,则应该发生在较高的级别上
所有这些示例都涉及到首先捕获已知类型的异常并询问它以查看到底出了什么问题,然后执行一些已知的操作以允许程序继续执行。这样做的目的是防止应用程序在出现错误时崩溃和烧坏,您知道可能会出现错误,但知道如何在这种情况下保持程序运行

捕获异常的基本规则:

  • 如果您不希望出现异常,请不要捕获异常
  • 如果您不能或不想在收到异常后继续执行代码,无论您是否知道它可能发生,都不要捕获它
  • 如果您预期异常会发生,并且知道如何在异常发生时继续执行代码(至少在一段时间内),那么捕获并执行为此所需的任何特殊操作
  • 从不捕获异常(一个空的catch块);这会导致应用程序以更不可预测的方式无声地失败
  • 不要在生产代码中留下catch和rethrow(只有rethrow的catch块)。它们有时在调试时很有用,因为它们允许您识别失败的特定代码段,但在生产代码中,它们只是抛出或实际处理异常的一个减速点
    如果您的代码能够优雅地处理特定类型的异常,请捕获并处理它,然后让代码继续运行。如果没有,让异常向上传播,因为它可能是在更高级别捕获的,或者可能是您不应该捕获的真正错误,因为它可能会掩盖错误。

    您不应该捕获无法处理的异常,但您可以捕获您可能能够处理的异常:

    try
    {
       DoSomethingNotNice();
    }
    catch (ExceptionIMightBeAbleToHandle ex)
    {
       if(iCanHandle(ex))
           thenHandle(ex);
       else
           throw;
    }
    
    请注意,单独使用
    throw
    应该保留堆栈跟踪信息


    如果您在捕获异常时可以执行某种操作,则可以优雅地处理
    FileNotFoundException

    (例如,执行一段代码,该代码将执行try语句中尝试的功能,但执行方式不同,但效率可能较低,或者只是通知用户无法执行其操作),然后您应该捕获它并执行此操作。如果您只是记录异常以便稍后跟踪问题,那么您应该重新显示异常
    throw;
    (而不是
    throw ex;
    ),以防有其他代码块可以处理该类型的异常

    try
    {
       DoSomethingNotNice();
    }
    catch (ExceptionICanHandle ex)
    {
       //Looking for examples of what people are doing in catch blocks
       //other than throw or wrapping the exception, and throwing.
    }
    
    捕获异常以将捕获的异常包装到您自己的异常中也是可以接受的,这可能对调用函数更有意义。

    一些示例:

  • 记录异常并继续
  • 重试出错的事情
  • 试着用另一种方法做你想做的事情

  • 这一切都取决于出了什么问题。关键是,仅仅抓住并重新投掷对任何人都没有用处。

    我认为这条常见建议的基本思想是避免出现以下情况:

    try
    {
        SomeImportantResource = GetSomeImportantResource();
        SomeOtherImportantResource = GetSomeOtherImportantResource();
    }
    catch (Exception ex)
    {
        SomeGlobalErrorHandlingMechanism(ex);
    }
    
    我一直在工作