在c#中捕获特定与通用异常

时间:2009-09-11 14:38:45

标签: c# exception-handling coding-style

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

您是否发现自己只使用捕获通用异常或尝试捕获特定异常并使用多个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 个答案:

答案 0 :(得分:23)

你几乎永远不会抓住顶级的例外。

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

异常(哈哈)就是如果你正在捕获日志记录并重新抛出异常,那么有时可以捕获顶级异常,记录它并重新抛出它。

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

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

答案 1 :(得分:6)

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

How to Design Exception Hierarchies

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

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

是,

你应该从最具体的例外中捕捉到最少,所以你可以以适当的方式处理事情。

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

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

答案 4 :(得分:1)

作为最佳做法,您应该避免捕获异常并使用标记作为返回值。

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

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

答案 5 :(得分:1)

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

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

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

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

答案 6 :(得分:0)

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

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

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