我习惯在每个方法中使用try/catch块。这样做的原因是为了在违规点捕获每个异常并记录它。从我的阅读和与他人的交谈中,我理解到这不是一个受欢迎的观点。一个人应该只捕获自己准备处理的异常。然而,如果我不在违规点捕获异常,那么就有可能从未记录该违规点并了解它。注意:当我捕获但不处理异常时,我仍然抛出异常。这使我能够让异常传播到将其处理的地方,同时让我在违规点记录它。
那么...如何避免在每个方法中使用try/catch块,但仍然可以在发生错误的位置记录错误?
我习惯在每个方法中使用try/catch块。这样做的原因是为了在违规点捕获每个异常并记录它。从我的阅读和与他人的交谈中,我理解到这不是一个受欢迎的观点。一个人应该只捕获自己准备处理的异常。然而,如果我不在违规点捕获异常,那么就有可能从未记录该违规点并了解它。注意:当我捕获但不处理异常时,我仍然抛出异常。这使我能够让异常传播到将其处理的地方,同时让我在违规点记录它。
那么...如何避免在每个方法中使用try/catch块,但仍然可以在发生错误的位置记录错误?
不要捕获所有异常。异常会在堆栈中向上传递。您需要确保在异常到达堆栈顶部之前将其捕获。
这意味着,例如,您应该使用try/catch块来包围事件处理程序的代码。事件处理程序可能是“堆栈顶部”。对于ThreadStart处理程序或来自异步方法的回调也是如此。
您还想在层边界捕获异常,不过在这种情况下,您可能只需将异常包装在特定于该层的异常中。
在ASP.NET的情况下,您可以决定允许ASP.NET健康监视为您记录异常。
但是,您绝对不需要在每个方法中捕获异常。那是一个重大的反模式。如果您使用此类异常处理提交代码,我会强烈反对。
您可以在堆栈跟踪中查看所有内容 - 无需尝试/捕获每个方法。
遵循以下几个规则:
好的,我已经阅读了所有建议在顶层放置单个try/catch的答案,现在我想提出另一种观点。
我不会在每个方法中放置try/catch,远非如此。但是我会在我预计会失败的代码段(例如打开文件)周围使用try/catch,并且我希望添加附加信息到异常中(以便在更高层次记录日志)。
堆栈跟踪和消息“权限被拒绝”可能足以让您作为程序员找出问题所在,但我的目标是向用户提供有意义的信息,例如“无法打开文件'C:\ lockedfile.txt'。 权限被拒绝。”。
如下:
private void DoSomethingWithFile(string filename)
{
// Note: try/catch doesn't need to surround the whole method...
if (File.Exists(filename))
{
try
{
// do something involving the file
}
catch (Exception ex)
{
throw new ApplicationException(string.Format("Cannot do something with file '{0}'.", filename), ex);
}
}
}
我绝对不会在每个方法周围使用try-catch包装器(有趣的是,我在刚开始时确实这样做了,但那是在我学到更好的方法之前)。
1)为了防止程序崩溃和用户丢失信息,我这样做:
runProgram:
try
{
container.ShowDialog();
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
if (MessageBox.Show("A fatal error has occurred. Please save work and restart program. Would you like to try to continue?", "Fatal Error", MessageBoxButtons.YesNo) == DialogResult.Yes)
goto runProgram;
container.Close();
}
容器是我的应用程序的起点,因此它基本上在我的整个应用程序周围放了一个包装器,以便没有任何东西会导致无法恢复的崩溃。这是我不介意使用goto的少数情况之一(它只有少量代码,仍然非常可读)。
2) 我只在我期望出现问题的方法(如超时)中捕获异常。
3) 为了提高可读性,如果您有一个try catch块,并且在try部分和catch部分都有一堆代码,最好将该代码提取到一个命名良好的方法中。
public void delete(Page page)
{
try
{
deletePageAndAllReferences(page)
}
catch (Exception e)
{
logError(e);
}
}
要在发生的地方处理异常,你仍然需要使用 try/catch。但是你不一定需要在每个地方都捕获异常。它们会沿着调用栈向上传播,当它们被捕获时,你会得到一个堆栈跟踪。因此,如果出现问题,你可以随时根据需要添加更多的 try/catch。
考虑查看其中一个可用的日志记录框架。
我认为你不需要在违规点立即捕获所有内容。你可以冒泡异常,然后使用 StackTrace 找出实际发生违规的地方。
此外,如果你需要有一个 try catch 块,最好的方法是将其隔离在一个方法中,以免用大量的 try catch 块来混淆代码。同时,尽可能少使用 try 语句。
当然,重申一下,将异常冒泡到顶部并记录 stacktrace 是比在代码中多次嵌套 try-catch-log-throw 块更好的方法。
我建议使用ELMAH来处理异常,这基本上是一个“让异常发生”的概念。 ELMAH会负责记录它们,并且您甚至可以将其设置为在某个项目的异常达到或超过特定阈值时向您发送电子邮件。 在我们的部门中,我们尽可能远离try/catch块。 如果应用程序出现问题,我们希望立即知道问题所在,以便我们可以修复它,而不是压制异常并在代码中处理它。
如果发生异常,那就意味着有些事情不对劲。 思想是使您的应用程序只执行应该执行的操作。 如果它正在执行不同的操作并导致异常,则您的响应应该是修复它发生的原因,而不是让它发生并在代码中处理它。 这只是我的/我们的哲学,不适用于所有人。 但是,我们已经被应用程序“吃掉”异常太多次了,因为某种原因或另一个原因,没有人知道出了什么问题。
永远不要捕获通用异常。始终捕获最具体的异常,这样如果抛出异常,但它不是您期望的类型,您将知道,因为应用程序将崩溃。如果您只是捕获(Exception e),那么无论抛出什么类型的异常,您的catch块现在都将负责响应可能抛出的每种类型的异常。如果它没有,那么您就会遇到整个“吞噬”异常的问题,其中某些东西出了问题,但直到很可能为时已晚才知道。
现实情况是,避免细粒度的try/catch。让异常向上遍历堆栈,并在尽可能高的级别上被捕获。如果您有特定的关注点,则在立即catch中放置日志记录,如果您担心异常会级联 - 尽管您仍然可以通过深入内部异常来解决这些问题。
异常处理不应该是一个事后想法。确保您始终如一地执行它。我见过很多人从每个方法的开始到结束都放置了一个广泛的try/catch并捕获了一般异常。人们认为这可以帮助他们获得更多信息,但实际上并非如此。在某些情况下,更少才是更多。我永远不会厌倦“异常应用于标记异常行为”的格言。如果可以恢复,请尝试减少总体异常数量。当您尝试解决问题并在出现问题时看到数百个相同的NullReferenceException或类似异常时,没有什么比这更令人沮丧的了。