代码之家  ›  专栏  ›  技术社区  ›  Scott Vercuski

捕获C中的特定异常与一般异常#

  •  6
  • Scott Vercuski  · 技术社区  · 15 年前

    这个问题来自对我创建的对象运行的代码分析。分析表明,我应该捕获比基本异常更具体的异常类型。

    您是否发现自己使用的只是捕获一般异常,或者尝试捕获特定异常,并使用多个catch块默认为一般异常?

    有问题的代码块之一如下:

    internal static bool ClearFlags(string connectionString, Guid ID)
    {
        bool returnValue = false;
        SqlConnection dbEngine = new SqlConnection(connectionString);
        SqlCommand dbCmd = new SqlCommand("ClearFlags", dbEngine);
        SqlDataAdapter dataAdapter = new SqlDataAdapter(dbCmd);
    
        dbCmd.CommandType = CommandType.StoredProcedure;
        try
        {
            dbCmd.Parameters.AddWithValue("@ID", ID.ToString());
    
            dbEngine.Open();
            dbCmd.ExecuteNonQuery();
            dbEngine.Close();
    
            returnValue = true;
        }
        catch (Exception ex)
        { ErrorHandler(ex); }
    
        return returnValue;
    }
    

    谢谢你的建议

    编辑: 这是代码分析的警告

    警告351 CA1031:Microsoft.Design:修改“ClearFlags(string,guid)”以捕获比“exception”更具体的异常或重新引发异常

    7 回复  |  直到 14 年前
        1
  •  23
  •   Simon P Stevens    15 年前

    您几乎不应该捕获顶级异常。

    在大多数情况下,您应该捕获并处理可能的最具体的异常,并且只有在您可以用它做一些有用的事情时才这样做。

    对于这个异常(haha),如果您正在捕获并重新引发异常,那么有时可以捕获一个顶级异常,将其记录并重新引发。

    你几乎不应该抓住一个顶级的异常并吞下它。这是因为,如果您捕获到一个顶级异常,您实际上不知道自己在处理什么;绝对地,任何事情都可能导致它,所以您几乎肯定无法正确处理每个故障案例。可能有一些失败,您可能只是想默默地处理和吞咽,但通过吞咽顶级的异常,您也将吞咽一整批真正应该向上扔的代码处理更高。在您的代码示例中,您可能想要做的是处理一个sqlException,然后日志+吞咽它;然后对于一个异常,记录并重新执行它。这包括你自己。您仍在记录所有异常类型,但您只会忽略相当可预测的sqlException,这表示您的SQL/数据库有问题。

    一个常见的实践是,只处理在那一点上可以实际解决的每一个异常,如果在代码中不能在那一点上解决它,那么就允许它向上冒泡。如果您无法在下一个级别上解决它,请允许它继续向上。如果它到达未处理的顶部,那么向用户显示礼貌的应用程序(可能尝试快速自动保存),然后关闭应用程序。在未处理的异常之后允许应用程序继续运行通常被认为是更糟的,因为您无法预测应用程序的状态,因为发生了异常情况。最好只是关闭并重新启动应用程序以恢复到预期状态。

        2
  •  5
  •   Ryan Lundy    15 年前

    请看一下Krzysztof cwalina撰写的这篇文章,我发现它对于理解何时捕获或忽略异常非常有帮助:

    How to Design Exception Hierarchies

    它描述的有关设计异常层次结构的所有原则也适用于决定何时捕获、抛出或忽略异常。他将例外情况分为三组:

    • 使用错误 ,例如 DivideByZeroException 指示代码中的错误;您不应该处理这些错误,因为可以通过更改代码来避免这些错误。
    • 逻辑错误 ,如 FileNotFoundException 因为你不能保证他们不会发生,所以你需要处理。(即使您检查文件是否存在,在您读取文件之前,它仍然可以在该分秒内被删除。)
    • 系统故障 ,如 OutOfMemoryException 这是你无法避免或处理的。
        3
  •  2
  •   Charles Bretana    15 年前

    你应该阅读一篇普通的文章或谷歌的“结构化异常处理”,并更好地了解这个主题的全部内容,但一般来说,捕捉每个异常都被认为是不好的做法,因为你不知道异常是什么(内存错误、内存不足错误、磁盘故障等)。

    对于许多未知/意外的异常,您不应该允许应用程序继续。一般来说,您“捕获”并只处理toy确定的那些异常,作为对您正在为其编写catch子句的方法进行分析的结果,该方法实际上可以创建,并且您可以做一些事情。您应该捕获所有表达式(捕获异常X)的唯一时间是执行类似于日志记录的操作,在这种情况下,您应该立即重新引发相同的异常(无论它是什么),以便它可以冒泡到堆栈中某个通用的“未处理异常处理程序”,该处理程序可以向用户显示适当的消息,然后导致应用程序终止。

        4
  •  1
  •   Greg B    15 年前

    对,

    你应该从最具体的例外情况到最少的例外情况,这样你就可以以适当的方式处理事情。

    例如,如果您正在发出一个Web请求,您应该首先捕获超时和404之类的内容,然后您可以通知最终用户他们应该重试(超时)和/或检查他们输入的URL。

    然后你可以抓住一些不太一般的东西,以防一些更古怪的事情出错,然后马上回到仅仅抓住一个例外,以防一些荒谬的事情发生。

        5
  •  1
  •   bryanbcook    15 年前

    作为最佳实践,你应该避免 例外 并使用标志作为返回值。

    相反,您应该为预期的异常设计自定义异常,并直接捕获这些异常。任何其他的事情都会冒泡成为一个意外的例外。

    在上面的示例中,您可能希望重新引发更特定于业务的异常。

        6
  •  1
  •   Dan Bryant    14 年前

    我同意,一般来说,您应该只捕获期望的异常,并理解如何处理。我经常不这样做的几个案例:

    1. 如前所述,如果我正在捕获一些有用的信息来进行日志记录,然后再进行回放。

    2. 如果我正在执行一个异步操作,例如在工作线程中处理排队的消息或作业,并且我想捕获在不同上下文中重新引发的异常。我也经常在这里使用一个丑陋的黑客,它欺骗clr添加堆栈跟踪信息,以便在新上下文中重新执行时不会丢失。

    3. 如果我正在处理一个独立的任务或操作,并且我可以通过关闭任务来处理异常,而不关闭整个应用程序。我经常希望对于真正致命的异常(比如outofmemoryException)有一个顶级的异常,因为我忽略了这些异常。处理这个问题的正确方法是在它自己的AppDomain中运行独立的任务,但是我还没有在项目上实现这个任务的可用计划时间。

        7
  •  0
  •   Austin Salonen gmlacrosse    15 年前

    我同意代码分析工具。我对规则的例外是,我在事件处理程序中捕获常规异常,并且用户可以选择报告错误或忽略错误。

    在您提供的示例中,我认为代码分析是正确的。如果你不能在那里处理一个特定的异常,你就不应该抓住任何东西,让它冒泡到最高层。这样,当您试图修复问题时,您可以更轻松地重新创建问题。

    您可以通过将连接字符串和ID值添加到异常的数据属性中,并确保它也被记录,从而使示例变得更好。这样你就有机会复制错误。