前几天我进行了一次讨论:https://stackoverflow.com/a/42156860/937125
我不太明白为什么一个Abort比Exit在这种情况下打电话更好.我倾向于不在我的代码流中使用它.我认为这是一个不好的做法,对代码流程也不好.但@David在评论中的陈述让我想知道我是否遗漏了一些东西:
如果没有静默异常,在深层调用堆栈时如何中止操作.例如,如何使用10深度调用堆栈中止文件复制操作?这不是为什么例外设计的?当然,您可以无异常地对其进行编码,但它更加冗长且容易出错.
我无法想象这种情况.有人能给我一个这样的代码/场景的例子,并说服我Abort在上面的例子中确实是一件好事并且"更加冗长和容易出错".(3-4深度调用堆栈足以说明)
说明我的观点的最简单方案是这样的:
procedure MethodA;
begin
MethodB;
MethodC;
end;
procedure MethodB;
begin
// ... do stuff
end;
procedure MethodC;
begin
// ... do stuff
end;
Run Code Online (Sandbox Code Playgroud)
这很好,因为它是.现在假设MethodB要求用户输入一些输入,如果用户按下取消按钮,则不应该执行进一步的工作.你可以像这样实现:
procedure MethodA;
begin
if MethodB then
MethodC;
end;
function MethodB: Boolean;
begin
Result := MessageDlg(...)=mrOK;
if not Result then
exit;
// ... do stuff
end;
procedure MethodC;
begin
// ... do stuff
end;
Run Code Online (Sandbox Code Playgroud)
这很好,但想象一下,在现实世界的代码中,有更深的嵌套.返回的布尔值MethodB可能需要在很多级别上传递.这会变得很麻烦.
或者考虑如果MethodB需要将值返回给调用者会发生什么.在那种情况下,原始代码可能是这样的:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
Result := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
Run Code Online (Sandbox Code Playgroud)
现在再一次考虑如果用户有机会取消会发生什么.我们如何从中返回布尔值和字符串MethodB?使用out参数作为其中一个返回值?使用像记录这样的复合结构来包装两个值.后者显然涉及很多样板,所以让我们探索前者.
procedure MethodA;
var
Value: string;
begin
if MethodB(Value) then
MethodC(Value);
end;
function MethodB(out Value: string): Boolean;
begin
Result := MessageDlg(...)=mrOK;
if not Result then
exit;
Value := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
Run Code Online (Sandbox Code Playgroud)
您肯定可以这样做,但这开始看起来像是为了简化而设计的异常代码.在这一点上,让我们考虑一下EAbort通过调用引发的静默异常的存在Abort,这不会导致顶级异常处理程序显示消息.最后一点是沉默的含义.
现在代码变成:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
if MessageDlg(...)<>mrOK then
Abort;
Result := ...;
end;
procedure MethodC(Value: string);
begin
// ... do stuff with Value
end;
Run Code Online (Sandbox Code Playgroud)
优点是MethodA不需要担心取消.如果调用堆栈更深,则MethodA在顶部和MethodB用户输入点之间的任何方法都不需要知道有关取消的任何信息.
另一个好处是MethodB可以保留其自然特征.它返回一个string.如果发生故障,无论是来自更传统的异常,还是来自用户取消,都会引发异常.
这个非常简单的例子并不比前一个不使用的例子更引人注目Abort.但想象一下,如果MethodB调用堆栈中的深度为4或5 ,代码会是什么样子?
我绝对不是说Abort应该总是用来代替exit.我的信念是两者都有自己的位置.凡Abort眼前一亮,当用户选择取消操作,你不希望任何更多的处理发生在当前的事件处理程序.此外,由于用户明确选择取消,因此不需要向他们呈现进一步的UI.您不需要一个消息框告诉用户他们已取消,他们已经知道了.