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

时间:2013-09-06 02:34:51

标签: c# exception exception-handling

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

以这种方式使用try / catch更好还是更差?它确实使编码更容易。

示例:

void DoSomething(){
  try{
    DoSomethingIffy();
  } catch {
    // Yikes! Do failure stuff
  }
}

void DoSomethingIffy(){
  if (rareCondition) {
    throw new Exception("oops");
  }
}

vs

void DoSomething(){
  if (!DoSomethingIffy()) {
    // Yikes! Do failure stuff
  }
}

bool DoSomethingIffy(){
  if (rareCondition) {
    return false;
  }
  else {
    return true;
  }
}

2 个答案:

答案 0 :(得分:1)

如果您看到或预计抛出率大于每秒100次,则可能会对性能产生影响。 (请参阅.NET Framework设计指南:Exception Throwing)在这种情况下,使用避免在代码的“热”部分中抛出异常的设计将是有益的。

如果这是您的代码的性能关键部分,您可以每秒输入1,000,000次,并且抛出概率为1 / 1,000可能是“罕见的”,但可能会导致性能无法接受的降低。

您必须针对真实的用户场景测量这些内容,并从性能角度收集数据以了解一个设计或另一个设计是否会更好(或者值得实现更复杂的设计)。

在数据方面,.NET提供了与exceptions特别相关的性能计数器,这在面临性能问题时可以作为一种有用的诊断。

答案 1 :(得分:1)

这实际上取决于具体情况。 Try / Catch通常比if / else具有更多的性能损失(再次,实际上取决于具体情况),但try / catch块也是良好错误处理的一部分。作为最佳实践(为了获得更好的性能和代码可维护性),最好通过将try / catch块置于更高级别来限制它们的数量。例如,如果你有一个名为“Car”的类,带有“Drive”方法,并且该方法调用Car.StartEngine()和Person.FastenSeatBelt()(并假设两种方法都有可能存在异常),那么请不要尝试/ catch在StartEngine方法内部,另一个在FastenSeatBelt方法内,你可以在Drive方法中有一个,并且在StartEngine或FastenSeatBelt方法中抛出的异常将冒泡到Drive方法内的catch块。

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

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

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

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