阅读Delphi的Exit
声明(例如参见here),我不能忽视写作,每个作者都有责任给出一条建议,例如:
警告:谨慎使用 - 跳跃是一种与结构化编码不一致的概念 - 它使代码维护变得困难。
现在,我来自Unix中的C和C ++,我熟悉重入问题,但说实话,我无法弄清楚为什么Delphi在它到达自然结束之前从函数返回应该是邪恶的。 / p>
除非Delphi中的每个函数和过程都被认为是可重入的。
我错过了什么?
答案 0 :(得分:12)
有两种思想流派。
有一种观点认为功能应该有一个退出点。许多编码标准通常都会强制执行。这样做的动机来自于使用gotos,多个出口等维护意大利面条代码的大量功能的艰难体验。
另一种思想流派认为意大利面条代码很糟糕,但应该务实并根据其优点判断编码风格,而不是遵循教条规则。例如,许多程序员认为,当您不使用exit
时,保护子句比使用深度缩进函数要好得多。作为一个例子,请考虑Martin Fowler出色的重构目录中的以下示例:Replace Nested Conditional with Guard Clauses。
从根本上说,这一切都取决于个人喜好。我个人鼓励使用保护条款,但不要在漫长的程序中大量使用退出。
答案 1 :(得分:9)
你错过了它没有说“不要使用这个”或“退出是邪恶的”;它说“仔细使用它。”它说它可以使维护变得困难。例如,如果你有一个很大的方法,并且中间有一条这样的线,你可能会完全错过它:
if aLocalObject.CheckSomeValue(aParameter) <> RIGHT_VALUE then Exit;
是的,不幸的是,我之前见过这样的东西。 :(
通过一些经验法则可以减轻许多问题:
Exit
(以及Break
和Continue
)语句放在他们自己的行上。这让他们更难错过。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 answer和this 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,就在“正常”返回之前。
此外,肯定很少有开发人员在调试过程中没有在程序顶部的“退出”处推迟停止执行...
我只能同意其他海报提出的观点。保护条款,埋在大型复杂程序中的出口等。