重新启动例外的成本是多少?

时间:2011-06-22 10:37:23

标签: delphi exception-handling delphi-xe

这是

try
  DoSomethingThatMightThrowAnException;
except
  on E : ESyntaxError do
    begin
    if (E.ErrorCode = errMissingBracket) then
      HandleError
    else
      raise;
    end;
end;

比这慢?

try
  DoSomethingThatMightThrowAnException;
except
  on E : EMissingBracketSyntaxError do
    begin
    HandleError;
    end;
end;

预期有什么不同?有关系吗?请注意,这可能会通过调用堆栈多次发生。

4 个答案:

答案 0 :(得分:2)

  

预期有什么不同?

您描述的情景之间的差异很小 但是 在提出异常和根本不提高异常之间存在显着差异(使用错误结果)。

  

重要吗?请注意,这可能会通过调用堆栈多次发生。

您应该只对“特殊情况”使用例外。如果错误频繁发生,特别是在循环中,那么它应该被提升到一个完全成熟的用例场景。

如果你不这样做,那么看似简单的事情很快就会恶化到你的区块变得比其他例程更大的情况。

通常,检查条件并将其作为主线代码中的显式分支处理应该是非常简单的。

即。而不是:

begin
  try
    //Do1
    //Do2 (possibly raising an exception that you can handle)
    //Do3
    //Do4
  except
    //Dealing with main-line cases in exception handlers is
    //very bad, leading to difficult to read code in the future.
  end;
end;

而是写:

begin
  //Do1
  //LDo2Result := Do2
  //NOTE: Do2 can still raise exceptions in EXCEPTIONAL situations.
  //  But for "main-line" use-case scenarios should rather return explicit 
  //  results that can be handled.
  if LDo2Result = d2rNoErrors then
  begin
    //Do3
    //Do4
  end;
  if LDo2Result = d2rBracketMissing then
  begin
    //DoX
  end;
end;

与您描述的任何一种情况相比,上述性能和可维护性通常都更好。然而,正如软件开发相关的所有事情:你得到了指导和技术,但你需要运用你的经验来选择“当前特定工作的最佳工具”。

答案 1 :(得分:1)

除非你的程序逻辑严重依赖于异常(这可能是设计糟糕的迹象),否则我认为异常处理只占你应用程序所需CPU时间的0.5%。

但是我猜不出会有太大的性能差异,因为内部异常将以任何一种方式传递。

另一方面,我更喜欢第二种方法,因为你在语言中表达你想要的语法,这很好。但我理解方法一在某些情况下可能是首选,特别是当上下文更大更复杂时。

免责声明:我从未在Delphi中编程,我对该语言的内部一无所知。我不知道可能会有很大的性能差异。

答案 2 :(得分:1)

在实际应用程序的上下文中,性能差异可以忽略不计。在我的机器上引发和处理异常(使用空处理程序)大约需要0.3毫秒,如果我添加大量日志记录,则需要1.3毫秒。因此,如果异常真的非常特殊,那么它不会对应用程序的性能造成任何影响。

答案 3 :(得分:0)

我已经快速浏览了编译器为上面的代码片段喷出的汇编程序。事实证明jmp @HandleOnExeption之后的字节包含数据,例如您在on子句中使用的异常类指针(如果有的话)。

我不是那么精通汇编程序以确切知道发生了什么,但足以理解大致正在发生的事情并得出这个结论:

我怀疑System.pas'HandleOnException已经执行了call @IsClass,并且如果找不到合适的处理程序则传递异常,所以如果你使用on e:Exception并重新加注,这将添加一点代码并进行两次额外的调用:

  • 一个回到你的异常处理部分(在所有情况下)
  • 一个call @RaiseAgain(如果异常被重新提升)

所以,这是有区别的。一个小的,但它仍然存在。