我的任务是编写一个我正在研究的.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.
}
提前致谢
答案 0 :(得分:11)
这意味着。如果您期望正在运行的代码抛出异常,并且当抛出该异常时,您的代码知道出了什么问题以及如何继续,那么就抓住异常并处理它。
基本上,规则存在以防止反模式,如:
try
{
...
}
catch(Exception ex)
{
throw;
}
这里的捕获只会添加一个减速带来展开调用堆栈。如果你实际上并不想要捕捉到任何异常,你甚至不应该为捕获而烦恼。
一个相关但更有效的案例是你不关心被抛出的异常,但你需要在所有情况下进行清理。在这种情况下,跳过捕获;你不需要它,只需将它作为try-finally块。
编辑:要回答帖子中的问题,不仅仅是主题,您可以编写如下规则:“不要编写不执行任何操作的try-catch语句,或者只重新捕获捕获的异常。语句应该执行一些与抛出的异常相关的增值操作。“
例如,假设您尝试使用用户在登录应用时提供的凭据连接到SQL Server实例。许多事情可能会出错,其中一些你不能指望,其中一些你应该。
所有这些示例都涉及首先捕获已知类型的异常并询问它以查看确切的错误,然后执行一些可以允许程序继续执行的已知操作。目的是防止应用程序在出现错误而导致崩溃和烧毁时知道可能出错,但知道如何在这种情况下保持程序运行。
捕获例外的基本规则:
答案 1 :(得分:2)
我认为这个共同建议的基本思想是避免这样的情况:
try
{
SomeImportantResource = GetSomeImportantResource();
SomeOtherImportantResource = GetSomeOtherImportantResource();
}
catch (Exception ex)
{
SomeGlobalErrorHandlingMechanism(ex);
}
我曾与开发人员合作过,当遇到错误时,他们只会将有问题的代码包装在try
/ catch
块中并说“我修复了错误”。上述示例中的问题是,通过简单地捕获异常而不是解决导致它的问题,您可能会破坏程序的可靠性。上面,catch
做了什么让我们不确定SomeImportantResource
和SomeOtherImportantResource
是否已经正确初始化。似乎可能在程序的其他地方有代码需要来初始化这些代码,在这种情况下,我们刚刚通过“修复”错误引入了一个错误。
所以我认为标准的智慧是只有尝试才能处理异常,如果你能以一种不会破坏程序中其他任何代码的方式从中恢复的话。
或者,更好的是:不要捕获异常并做一些微弱的尝试(或非尝试)来“处理”它;找出导致它的原因并修复那个问题。显然这不是永远可能,但它可能比它应该更频繁。
答案 2 :(得分:2)
考虑一下像OneNote这样的应用程序是否允许您将文件存储在共享网络驱动器上,但是如果网络不可用,则它会暂时使用本地存储,直到主存储可用为止。
如果您的程序在与文件交互时遇到异常,那么您可以使用本地存储重试该操作。
这是一个您拥有所需特定程序行为的示例,并通过您处理异常的方式来完成它。通常,您应该尝试找到一种方法来实现您的目标而不使用异常处理,例如在上面的例子中,您可以在尝试操作之前始终检查文件是否可用。这样你就可以把它编码为“if / else”而不是“try / catch”。但是,如果您这样做,在上述情况下仍有始终的可能性,即某人可能会在操作过程中失去对文件的访问权限,这样无论您是否提前进行了检查,仍然可能会得到一个可以优雅处理的异常。所以你可能会将你的else块重构为一个从else和catch调用的函数,这样你就可以优雅地回退到本地存储。
如果我正在记录的内容没有安全问题,并且正如您所提到的那样重新抛出,我也常常包括日志记录,并且我的日志记录包含更多描述性信息和上下文信息,可能还有一些本地值,这使得调试更容易。我总是努力让日志文件如此详细,以至于我可以确定问题的原因而无需在我的机器上重现。我讨厌听到程序员做出“我无法重现它”的借口。您不必重现它。如果您的日志记录足够,则无需重现它。
当异常通过重新抛出一直到你的GUI层时,然后在那一点你抓住它并且不重新抛出它,而是向用户显示一条消息,表明发生了意外错误,通常退出应用程序。您可能会给他们一个保存工作的机会,但可能会自动备份被覆盖的文件,因为未处理的异常是您从未编写过的,这意味着某些内容可能已损坏,您可能正在保存错误的文件,但却领先用户相信他们正在挽救他们的工作。这最终是许多程序在发生意外情况时选择自杀的原因,从那时起谁知道程序可能处于什么状态,以及在数据库中保存一些行这些简单的事情可能会产生严重的后果和软件很多数据
答案 3 :(得分:1)
如果你能捕获一个在某种程度上有用的异常(例如执行将执行try语句中尝试的函数的代码块,但执行不同但可能效率较低的代码),则执行操作方式,或只是通知用户他们的行为无法执行),那么你应该抓住它并这样做。如果您只是记录异常以便稍后跟踪问题,那么您应该重新抛出异常throw;
(非throw ex;
),以防有另一个代码块可以处理该类型的异常。
捕获异常以将捕获的异常包装在您自己的异常中也可以接受,这可能对调用函数更有意义。
答案 4 :(得分:1)
一些例子:
一切都取决于出了什么问题。关键是,只是捕捉和重新投掷对任何人都没有用。
答案 5 :(得分:0)
如果您的代码可以优雅地处理特定类型的异常,请捕获并处理它,然后让代码继续运行。如果没有,请让异常传播,因为它可能会被更高级别捕获,或者它可能是一个非常错误的东西,你不应该捕获它,因为它可能会掩盖错误。
答案 6 :(得分:0)
您不应该捕获无法处理的异常,但是您可以捕获可能能够处理的异常:
try
{
DoSomethingNotNice();
}
catch (ExceptionIMightBeAbleToHandle ex)
{
if(iCanHandle(ex))
thenHandle(ex);
else
throw;
}
请注意,单独使用throw
应该可以保留堆栈跟踪信息。
您可以优雅地处理的典型事情是FileNotFoundException
。
答案 7 :(得分:0)
catch块应该拆除可能已经打开以供在try中使用的任何东西,并且由于异常被抛出没有正确关闭。数据库连接和文件访问通常需要关闭(尽管正确使用using块可以处理这个)
完成后,您可以使用throw;
将异常转移到下一级
或者,您可能希望将当前异常包装在与当前方法
更相关的新异常中catch(LowLevelException ex){
throw new HighLevelException("argh bad things happened!",ex);
}
答案 8 :(得分:0)
游戏来晚了,但是MS建议在.net核心中全局处理错误的方法是middleware。
此外,您可以使用this之类的switch语句来确保重新抛出无法处理的错误。
尝试使我的回答与问题一样笼统;),但如有需要,我可以提供一些代码。