尝试/捕获最佳实践以获得最佳性能

本文关键字:最佳 性能 尝试 | 更新日期: 2023-09-27 18:30:26

我听说try/catches被认为对性能不利,但是如果你很少期望抛出异常,那么它们被认为是返回失败信息的更好方法,而不是使用具有布尔值的方法/函数。

以这种方式使用 try/catch 对性能是更好还是更差? 它当然使编码更容易。

例:

void DoSomething(){
  try{
    DoSomethingIffy();
  } catch {
    // Yikes! Do failure stuff
  }
}
void DoSomethingIffy(){
  if (rareCondition) {
    throw new Exception("oops");
  }
}

void DoSomething(){
  if (!DoSomethingIffy()) {
    // Yikes! Do failure stuff
  }
}
bool DoSomethingIffy(){
  if (rareCondition) {
    return false;
  }
  else {
    return true;
  }
}

尝试/捕获最佳实践以获得最佳性能

如果您看到或期望看到大于每秒 100 次的抛出率,则可能会对性能产生影响。(请参见 .NET 框架设计指南:异常引发)在这种情况下,使用避免在代码的"热"部分引发异常的设计将是有益的。

考虑一下,如果这是代码的性能关键部分,则每秒输入 1,000,000 次,并且 1,000 分之一的抛出概率可能是"罕见的",但它可能会导致不可接受的性能下降。

您确实必须针对真实的用户场景来衡量这些东西,并收集数据,以便从性能角度了解一种或另一种设计是否会更好(或值得实施更复杂的设计)。

在数据方面,.NET 提供了专门与异常相关的性能计数器,这些计数器在遇到性能问题时非常有用。

这实际上取决于情况。 Try/Catch 通常比 if/else 对性能的影响更大(同样,实际上取决于情况),但 try/catch 块也是良好错误处理的一部分。 作为最佳实践(为了更好的性能和代码可维护性),最好通过将 try/catch 块放在更高级别来限制它们的数量。 例如,如果您有一个名为"Car"的类,其中包含"Drive"方法,并且该方法调用Car.StartEngine()和Person.FastenSeatBelt()(并且假设两种方法都可能存在异常),则不是在StartEngine方法中有一个try/catch,而在FastenSeatBelt方法中有一个,您可以在Drive方法中有一个,并且在StartEngine或FastenSeatBelt方法中引发的异常将冒泡到Drive方法中的捕获块。

在决定是使用 if/else 还是 try/catch 时,需要考虑更多因素。 假设你有一个方法ConvertToCamelCase(string)。 如果为字符串参数传递 null,您想做什么? 就个人而言,我更愿意让此方法抛出一个 ArgumentNullException。 然后,如果我需要记录或处理异常,我会在调用方法中使用 try/catch 块。 但是,如果您决定返回 true/false,而不管参数是否为 null,我建议将此方法重命名为 TryConvertToCamelCase(string)。 此命名约定向调用方建议他们不必担心此方法引发的异常,因为如果由于任何原因失败,它将尝试返回 false。 然后,调用方法将使用 if/else 来处理字符串无法转换为 camelCase 的情况。

要考虑的另一件事是,您是否可以优雅地处理"异常"并从当前所在的代码部分继续。 例如,假设我有一个 Windows 窗体,它只是打开用户在单击"打开"后在文本框中指定的文件。 如果我在按钮单击的事件处理程序中进行文件处理,我会将处理打开文件的代码放在 if 语句中,条件为 File.Exists。 如果该文件不存在,它将转到 else 块,并弹出一个消息框,让用户知道该文件不存在。 但是,如果事件处理程序使用我创建的帮助程序类,我们称之为"FileHelper",那么FileHelper的"ReadFile"方法甚至不会费心检查文件是否存在,它只会尝试打开它,如果不能,则会抛出FileNotFoundException。 然后,我的按钮单击事件处理程序将捕获异常并提醒用户。 原因是"FileHelper"类不知道表单或任何用户交互。 如果"ReadFile"方法无法打开文件,则无法继续,因此这是一个例外。

我想提到的最后一件事是确保在处理错误时,它是您期望的错误。 通常,我看到开发人员执行"捕获(异常示例)",然后通过提示用户来处理错误。 但是,使用以前的 ReadFile 方法,假设您将其作为 32 位应用程序运行,并且它已经消耗了 1.9999 GB 的 2GB 限制,也许不是"ex"是 FileNotFoundException,它实际上是一个 OutOfMemoryException。 当它确实存在时,通过说"嘿伙计,该文件不存在"来提示用户只会增加负面的用户体验(不是说你会这样做,只是我经常看到的东西)。 相反,您可以使用"catch (FileNotFoundException ex) {...}" 或 "catch (Exception ex) { if (ex is FileNotFoundException) {...}否则{...}}"

总结:当你需要记录或处理异常时,不要害怕使用 try/catch,但也不要对 try/catch 块发疯。