Delphi的退出声明是否危险?

时间:2012-03-23 16:25:37

标签: delphi

阅读Delphi的Exit声明(例如参见here),我不能忽视写作,每个作者都有责任给出一条建议,例如:

  

警告:谨慎使用 - 跳跃是一种与结构化编码不一致的概念 - 它使代码维护变得困难。

现在,我来自Unix中的C和C ++,我熟悉重入问题,但说实话,我无法弄清楚为什么Delphi在它到达自然结束之前从函数返回应该是邪恶的。 / p>

除非Delphi中的每个函数和过程都被认为是可重入的。

我错过了什么?

4 个答案:

答案 0 :(得分:12)

有两种思想流派。

有一种观点认为功能应该有一个退出点。许多编码标准通常都会强制执行。这样做的动机来自于使用gotos,多个出口等维护意大利面条代码的大量功能的艰难体验。

另一种思想流派认为意大利面条代码很糟糕,但应该务实并根据其优点判断编码风格,而不是遵循教条规则。例如,许多程序员认为,当您不使用exit时,保护子句比使用深度缩进函数要好得多。作为一个例子,请考虑Martin Fowler出色的重构目录中的以下示例:Replace Nested Conditional with Guard Clauses

从根本上说,这一切都取决于个人喜好。我个人鼓励使用保护条款,但不要在漫长的程序中大量使用退出。

答案 1 :(得分:9)

你错过了它没有说“不要使用这个”或“退出是邪恶的”;它说“仔细使用它。”它说它可以使维护变得困难。例如,如果你有一个很大的方法,并且中间有一条这样的线,你可能会完全错过它:

if aLocalObject.CheckSomeValue(aParameter) <> RIGHT_VALUE then Exit;

是的,不幸的是,我之前见过这样的东西。 :(

通过一些经验法则可以减轻许多问题:

  • 始终将Exit(以及BreakContinue)语句放在他们自己的行上。这让他们更难错过。
  • 支持较小的方法而不是较大的方法,并在合理的情况下将大方法分解为较小的方法。 (当合理的时候!这不是一个绝对的规则,过于热心地应用它会使事情变得更糟而不是更好。保持爱因斯坦的名言:“尽可能简化所有事情,但不要简单。”)
  • 学会使用try/finally块,以便Exit可以安全地执行{{1}}过程,而不会导致泄漏或损坏。

答案 2 :(得分:2)

除了David和Mason的精湛答案之外,我还想分享我个人最喜欢的Exit用法。让我们称它为“安全卫士”,与“最后的提供者”相反。 ;)

基本理念:

function Search(List: TList; Item: TObject): Integer;
begin
  for Result := 0 to List.Count - 1 do
    if List[Result] = Item then
      Exit;
  Result := -1;
end;

其他更现实的例子(来自this answerthis answer):

const 
  Order: array[0..6] of String = ('B', 'C', 'A', 'D', 'G', 'F', 'E'); 

function GetStringOrder(const S: String; CaseSensitive: Boolean): Integer; 
begin 
  for Result := 0 to Length(Order) - 1 do 
    if (CaseSensitive and (CompareStr(Order[Result], S) = 0)) or 
        (not CaseSensitive and (CompareText(Order[Result], S) = 0)) then 
      Exit; 
  Result := Length(Order); 
end; 

function FindControlAtPos(Window: TWinControl; const ScreenPos: TPoint): TControl; 
var 
  I: Integer; 
  C: TControl; 
begin 
  for I := Window.ControlCount - 1 downto 0 do 
  begin 
    C := Window.Controls[I]; 
    if C.Visible and PtInRect(C.ClientRect, C.ScreenToClient(ScreenPos)) then 
    begin 
      if C is TWinControl then 
        Result := FindControlAtPos(TWinControl(C), ScreenPos) 
      else 
        Result := C; 
      Exit; 
    end; 
  end; 
  Result := Window; 
end; 

最后引用Delphi对编译器错误消息 FOR-Loop variable'&lt; element&gt;'的帮助循环后可能未定义

  

如果循环留有goto或exit语句,则只能依赖for循环控制变量的最终值。

答案 3 :(得分:0)

与许多事情一样,退出在某些情况下很有用,而在其他情况下则不然。我发现它在搜索某个容器的小函数中非常有价值,将一个对象从容器加载到循环内的'result'中并寻找匹配的属性。如果找到,该函数可以退出,如果没有,结果可以在循环之后设置为nil,就在“正常”返回之前。

此外,肯定很少有开发人员在调试过程中没有在程序顶部的“退出”处推迟停止执行...

我只能同意其他海报提出的观点。保护条款,埋在大型复杂程序中的出口等。