在c#中捕获特定与通用exception

这个问题来自针对我创建的对象的代码分析。 分析说我应该捕获一个比基本exception更具体的exception类型。

您是否发现自己只使用捕获通用exception或尝试捕获特定exception并使用多个catch块默认为通用exception?

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

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’更具体的exception或重新抛出exception

你应该几乎永远不会捕获顶级exception。

在大多数情况下,您应该捕获并处理可能的最具体的exception,并且只有在您可以使用它时才有用。

对此的exception(哈哈)是,如果您正在捕获日志记录并重新抛出exception,那么有时可以捕获顶级exception,记录并重新抛出它。

你应该几乎从不捕获顶级exception并吞下它。 这是因为如果你正在捕捉一个顶级exception,你真的不知道你在处理什么; 绝对地,任何东西都可能导致它,所以你几乎肯定无法做任何能够正确处理每一个失败案例的事情。 可能有一些失败,你可能只想静静地处理和吞下,但是通过吞下顶级exception,你也会吞下一大堆真的应该向上抛出你的代码来处理更高的错误。 在你的代码示例中,你可能想要做的是处理一个SQLException并记录+吞下它; 然后对于exception,记录并重新抛出它。 这涵盖了你自己。 您仍在记录所有exception类型,但您只能吞下可预测的SQLException,这表明您的SQL /数据库存在问题。

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

看看Krzysztof Cwalina的这篇文章,我发现它非常有助于理解何时捕获或忽略exception:

如何设计exception层次结构

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

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

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

对于许多未知/意外的exception,您不应该允许应用程序继续。 一般来说,你“抓住”并只处理玩具已确定的exception,作为对你编写catch子句的方法的分析的重新分析,该方法实际上可以创建,并且你可以做些什么。 你应该捕获所有expcetoins(捕获exceptionx)的唯一一次是执行类似日志记录的操作,在这种情况下你应该立即重新抛出相同的exception(无论它是什么),这样它就可以将堆栈冒泡到一些通用的“未处理的exception”处理程序“可以向用户显示适当的消息,然后使应用程序终止。

是,

您应该从最具体的exception中捕获到最少,这样您就可以以适当的方式处理事情。

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

然后你可以捕捉到一些不那么普遍的东西,万一有些古怪的东西出错了,然后直接回到只是在一个荒谬的事情发生的情况下捕获exception。

作为最佳实践,您应该避免捕获Exception并使用标志作为返回值。

相反,您应该为预期的exception设计自定义exception并直接捕获它们。 其他任何东西都应该成为一个意想不到的例外。

在上面的示例中,您可能希望重新抛出更具体业务的exception。

我同意,一般来说,您应该只捕获您期望的exception并理解如何处理。 在一些我经常不这样做的情况:

  1. 如上所述,如果我捕获某些有用的信息来记录然后重新抛出。

  2. 如果我正在执行异步操作,例如处理工作线程中的排队消息或作业,我想捕获exception以便在不同的上下文中重新抛出。 我也常常在这里使用一个丑陋的黑客攻击CLR来附加堆栈跟踪信息,以便在新环境中重新抛出时不会丢失。

  3. 如果我正在处理孤立的任务或操作,我可以通过关闭任务来处理exception,而无需关闭整个应用程序。 我经常希望这里有一个真正致命exception(如OutOfMemoryException)的顶级exception,因为我一直忽略这些exception。 处理这个问题的正确方法是在自己的AppDomain中运行隔离的任务,但我还没有可用的调度时间来在项目上实现它。

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

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

您可以通过将连接字符串和ID值添加到exception的Data属性来使您的示例更好,并确保它也被记录。 这样你就可以给自己一个再现错误的机会。