在维护我同事的代码时,甚至有人声称自己是高级开发人员,我经常看到以下代码:

try
{
  //do something
}
catch
{
  //Do nothing
}

或者有时他们将日志信息写入日志文件,例如下面的try catch块

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

我只是想知道他们所做的是不是最好的做法?这让我很困惑,因为在我看来,用户应该知道系统发生了什么。


当前回答

对于异常,我尝试如下:

首先,我捕获特殊类型的异常,如除零、IO操作等,并根据这些编写代码。例如,除以0,这取决于我可以提醒用户的值的出处(例如,一个简单的计算器在中间计算(而不是参数)中到达一个除以0),或者静默地处理这个异常,记录它并继续处理。

然后我尝试捕获剩余的异常并记录它们。如果可能,允许执行代码,否则提醒用户发生错误,并要求他们发送错误报告。

在代码中,是这样的:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}

其他回答

最佳实践是异常处理永远不应该隐藏问题。这意味着try-catch块应该非常少。

在三种情况下使用try-catch是有意义的。

Always deal with known exceptions as low-down as you can. However, if you're expecting an exception it's usually better practice to test for it first. For instance parse, formatting and arithmetic exceptions are nearly always better handled by logic checks first, rather than a specific try-catch. If you need to do something on an exception (for instance logging or roll back a transaction) then re-throw the exception. Always deal with unknown exceptions as high-up as you can - the only code that should consume an exception and not re-throw it should be the UI or public API.

假设你连接到一个远程API,在这里你知道预期某些错误(在这些情况下有一些事情),所以这是情况1:

try 
{
    remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex) 
{
    // User's security details have expired
    return false;
}

return true;

注意,没有捕获其他异常,因为它们不是预期的。

现在假设您试图将一些东西保存到数据库中。如果失败,我们必须回滚它,所以我们有情况2:

try
{
    DBConnection.Save();
}
catch
{
    // Roll back the DB changes so they aren't corrupted on ANY exception
    DBConnection.Rollback();

    // Re-throw the exception, it's critical that the user knows that it failed to save
    throw;
}

注意,我们重新抛出了异常——更高级别的代码仍然需要知道某些事情失败了。

最后我们有了UI——这里我们不想有完全未处理的异常,但我们也不想隐藏它们。这里是情况3的例子:

try
{
    // Do something
}
catch(Exception ex) 
{
    // Log exception for developers
    WriteException2LogFile(ex);

    // Display message to users
    DisplayWarningBox("An error has occurred, please contact support!");
}

然而,大多数API或UI框架都有处理情形3的通用方法。例如ASP。Net有一个黄色的错误屏幕,用来转储异常详细信息,但是在生产环境中可以用更通用的消息代替。遵循这些是最佳实践,因为它节省了大量代码,但也因为错误记录和显示应该是配置决策,而不是硬编码。

这意味着情况1(已知异常)和情况3(一次性UI处理)都有更好的模式(避免预期错误或将错误处理交给UI)。

即使情况2也可以被更好的模式所取代,例如事务作用域(使用块回滚在块期间未提交的任何事务)使开发人员更难获得错误的最佳实践模式。

例如,假设你有一个大规模的ASP。网络应用程序。错误日志可以通过ELMAH进行记录,错误显示可以是本地的信息YSoD和生产中的漂亮的本地化消息。数据库连接都可以通过事务作用域并使用块。您不需要单个的try-catch块。

TL;DR:最佳实践实际上是根本不使用try-catch块。

我可以告诉你:

代码片段#1是不可接受的,因为它忽略了异常。(它像什么都没发生一样吞下去)。

所以不要添加不做任何事情或只是重新抛出的catch块。

Catch块应该增加一些价值。例如,向最终用户输出消息或记录错误。

不要对正常的流程序逻辑使用异常。例如:

例如输入验证。<-这不是有效的例外情况,而是你应该写方法IsValid(myInput);检查输入项是否有效。

设计代码以避免异常。例如:

int Parse(string input);

如果我们传递一个不能解析为int的值,这个方法会抛出一个异常,我们可以这样写:

bool TryParse(字符串输入,输出int结果);<-该方法将返回布尔值,表示解析是否成功。

也许这有点超出了这个问题的范围,但我希望这将帮助您在涉及try {} catch(){}和异常时做出正确的决定。

留下空白的catch块是最糟糕的做法。如果出现错误,最好的处理方法是:

登录到文件数据库等。 尝试在飞行中修复它(可能尝试做该操作的替代方式) 如果我们不能修复这个问题,那么就通知用户有错误,当然还要中止操作

异常是阻塞错误。

首先,最好的实践应该是不要为任何类型的错误抛出异常,除非它是阻塞错误。

如果错误阻塞,则抛出异常。一旦异常已经被抛出,就没有必要隐藏它,因为它是异常的;让用户知道它(你应该在UI中将整个异常重新格式化为对用户有用的内容)。

作为软件开发人员,您的工作是努力防止异常情况,即某些参数或运行时情况可能以异常结束。也就是说,例外情况不能被压制,但必须避免。

例如,如果您知道某些整数输入可能带有无效的格式,则使用int。TryParse而不是int.Parse。在很多情况下,你可以这样做,而不是仅仅说“如果它失败了,就抛出一个异常”。

抛出异常的代价很高。

毕竟,如果抛出了异常,最好的实践之一是在第一时间异常处理程序中捕获异常,而不是在抛出异常后将异常写入日志。例如:

ASP。净:全球。asax Application_Error 其他:应用程序域中。FirstChanceException事件。

我的立场是,局部try/catch更适合处理特殊情况,例如您可能会将一个异常转换为另一个异常,或者当您想要“静音”一个非常非常非常非常非常特殊的情况(库错误抛出一个不相关的异常,您需要静音以解决整个错误)。

其余情况:

尽量避免异常。 如果不可能:第一次机会异常处理程序。 或者使用PostSharp方面(AOP)。

回复@thewhiteambit的一些评论…

@thewhiteambit说:

异常不是致命错误,而是异常!有时他们 甚至不是错误,但认为它们是致命错误是完全的 对异常的错误理解。

首先,异常怎么就不能是错误呢?

No database connection =>异常。 解析为某些type =>异常的无效字符串格式 试图解析JSON,而输入实际上不是JSON =>异常 当object was expected =>异常时,参数为空 某些库有bug =>抛出意外异常 有一个套接字连接,它被断开了。然后尝试发送message =>异常 ...

我们可以列出1k种抛出异常的情况,毕竟,任何可能的情况都将是错误。

异常是一种错误,因为在一天结束时,它是一个收集诊断信息的对象——它有一个消息,当出现错误时就会发生。

没有异常情况时,没有人会抛出异常。异常应该阻止错误,因为一旦它们被抛出,如果你不尝试使用try/catch和异常来实现控制流,它们意味着你的应用程序/服务将停止进入异常情况的操作。

此外,我建议每个人都去看看Martin Fowler出版的快速失败范例(Jim Shore写的)。这就是我总是理解如何处理异常的方式,甚至在我开始研究这个文档之前。

[…致命错误是完全错误的理解什么是异常。

异常通常会切断一些操作流程,并将其处理为人类可以理解的错误。因此,似乎异常实际上是处理错误案例和处理它们以避免应用程序/服务完全崩溃并通知用户/消费者出错的更好范例。

更多关于@thewhiteambit关注点的答案

For example in case of a missing Database-Connection the program could exceptionally continue with writing to a local file and send the changes to the Database once it is available again. Your invalid String-To-Number casting could be tried to parse again with language-local interpretation on Exception, like as you try default English language to Parse("1,5") fails and you try it with German interpretation again which is completely fine because we use comma instead of point as separator. You see these Exceptions must not even be blocking, they only need some Exception-handling.

If your app might work offline without persisting data to database, you shouldn't use exceptions, as implementing control flow using try/catch is considered as an anti-pattern. Offline work is a possible use case, so you implement control flow to check if database is accessible or not, you don't wait until it's unreachable. The parsing thing is also an expected case (not EXCEPTIONAL CASE). If you expect this, you don't use exceptions to do control flow!. You get some metadata from the user to know what his/her culture is and you use formatters for this! .NET supports this and other environments too, and an exception because number formatting must be avoided if you expect a culture-specific usage of your application/service.

一个未处理的异常通常会变成一个错误,但异常本身 不是codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors

这篇文章只是作者的一个观点或观点。

因为维基百科也可能只是文章作者的观点,我不会说这是教条,但看看例外编码文章在某些段落中说了什么:

[…使用这些异常来处理出现的特定错误 继续这个程序被称为异常编码。这种反模式会迅速降低软件的性能和可维护性。

它还在某处说:

异常使用不正确

通常,异常编码会导致软件中出现进一步的问题 使用不正确的异常。除了使用异常 处理一个独特的问题,不正确的异常使用 甚至在异常引发后执行代码。这 在许多软件中,糟糕的编程方法类似于goto方法 语言,但只有在检测到软件中的问题后才会出现。

老实说,我相信如果不认真对待用例,软件就无法开发。如果你知道…

你的数据库可以离线… 某些文件可以被锁定… 某些格式可能不支持… 某些域验证可能失败… 你的应用应该在离线模式下工作… 无论用例如何……

...你不会为此使用异常。您可以使用常规控制流来支持这些用例。

如果没有涉及到一些意想不到的用例,您的代码将很快失败,因为它将抛出异常。对,因为例外就是例外情况。

另一方面,最后,有时会涉及异常情况,抛出预期的异常,但不抛出它们来实现控制流。这样做是因为您希望通知上层您不支持某些用例,或者您的代码无法处理某些给定的参数或环境数据/属性。

第二种方法很好。

如果你不想显示错误,并通过显示运行时异常(即。错误),这与他们无关,然后只记录错误,技术团队可以寻找问题并解决它。

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);//it will write the or log the error in a text file
}

我建议您对整个应用程序采用第二种方法。