[C#] 如何使用try catch进行异常处理是最佳实践


Answers

最佳做法是异常处理不应该隐藏问题 。 这意味着try-catch块应该非常少见。

有三种情况使用try-catch是有道理的。

  1. 尽可能处理已知的例外情况。 但是,如果您希望发生异常,通常先进行测试是更好的做法。 例如,解析,格式化和算术异常几乎总是由逻辑检查首先处理,而不是特定的try-catch

  2. 如果您需要对异常执行某些操作(例如记录或回滚事务),则重新抛出异常。

  3. 始终处理未知异常,尽可能高 - 唯一应该消费异常并且不会重新抛出异常的代码应该是UI或公共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;
}

请注意,我们重新抛出异常 - 上面的代码仍然需要知道某些失败。

最后我们有了用户界面 - 这里我们不想完全没有处理异常,但我们也不想隐藏它们。 这里我们有一个案例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也可以被更好的模式取代,例如事务处理范围using回滚块中未提交的任何事务的块)会使开发人员难以获得最佳实践模式。

例如,假设你有一个大规模的ASP.Net应用程序。 错误记录可以通过ELMAH ,错误显示可以是本地信息量大的ELMAH ,也可以是生产中的一个很好的本地化信息。 数据库连接都可以通过事务范围和using块。 你不需要一个try-catch块。

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

Question

在保持我的同事代码的同时,即使是自称是高级开发人员的人,我也经常看到以下代码:

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

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

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

我只是想知道他们所做的是否是最佳做法? 这让我感到困惑,因为我认为用户应该知道系统会发生什么。

请给我一些建议。







有时你需要处理对用户说什么都没有的例外。

我的方法是:

  • 在应用程序级别(即在global.asax中)捕捉关键异常(应用程序无法使用)的uncaughted异常。 这些豁免我没有捕捉到这个地方。 只需将它们登录到应用程序级别并让系统完成其工作即可。
  • 抓住“就地”并向用户显示一些有用的信息(输入错误的号码,无法解析)。
  • 抓住地方,对诸如“我将检查后台更新信息,但服务未运行”等边际问题不采取任何措施。

它绝对不一定是最佳实践。 ;-)




唯一一次应该让用户担心代码中发生的事情是,如果他们可以或需要做某些事情来避免这个问题。 如果他们可以更改表单上的数据,请按下按钮或更改应用程序设置以避免此问题,然后让他们知道。 但是用户无法避免的警告或错误只会让他们失去对产品的信心。

例外和日志适用于您,开发人员,而不是您的最终用户。 理解捕捉每个异常时要做的正确事情要比仅仅应用一些黄金法则或依靠整个应用程序范围的安全网要好得多。

无意识编码是唯一一种错误的编码。 事实上,你觉得在这些情况下可以做得更好的事情表明,你已经投入了良好的编码,但是避免在这些情况下试图加入一些通用规则,并理解某些事情的原因以及什么原因你可以做它来恢复。




更好的方法是第二个(您指定异常类型的方法)。 这样做的好处是,您知道这种类型的异常可能发生在您的代码中。 您正在处理这种类型的异常,您可以继续。 如果出现其他异常,那就意味着有些问题会帮助您找到代码中的错误。 应用程序最终会崩溃,但是你会知道有些东西你错过了(错误),需要修复。




对我来说,处理异常可以被看作是商业规则。 显然,第一种方法是不可接受的。 第二个更好,如果上下文如此说明,它可能是100%正确的方式。 现在,例如,您正在开发一个Outlook Addin。 如果你的插件引发了未处理的异常,outlook用户现在可能会知道它,因为由于一个插件失败,outlook不会自行销毁。 而且你很难弄清楚哪里出了问题。 因此,在这种情况下的第二种方法,对我来说,这是正确的。 除了记录异常之外,您可能会决定向用户显示错误消息 - 我认为它是一项业务规则。




第二种方法是一个好方法。

如果您不想显示错误并通过显示与它们无关的运行时异常(即错误)来混淆应用程序的用户,那么只需记录错误,技术团队就可以查找问题并解决问题。

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

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