我展示这个观点最简单的场景是这样的:
procedure MethodA;
begin
MethodB;
MethodC;
end;
procedure MethodB;
begin
end;
procedure MethodC;
begin
end;
现在这样就可以了。现在假设MethodB
要求用户输入一些内容,如果用户按下取消按钮,则不应继续进行任何工作。你可以像这样实现:
procedure MethodA;
begin
if MethodB then
MethodC;
end;
function MethodB: Boolean;
begin
Result := MessageDlg(...)=mrOK;
if not Result then
exit;
end;
procedure MethodC;
begin
end;
这个很好,但是想象一下,在真实的编码中,可能会有更深层次的嵌套。由
MethodB
返回的布尔值可能需要向上传递很多级别,这将变得繁琐。
或者考虑一下如果
MethodB
需要向其调用者返回一个值会发生什么情况。在这种情况下,原始代码可能如下所示:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
Result := ...;
end;
procedure MethodC(Value: string);
begin
end;
现在再考虑一下如果用户有机会取消会发生什么。我们如何从MethodB
返回布尔值和字符串?使用一个输出参数来返回一个返回值?使用一个复合结构,比如记录来包装这两个值。后者显然涉及大量的样板文件,因此让我们探索前者。
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
end;
当然,您可以这样做,但这似乎是为了简化异常而设计的代码。此时,让我们考虑存在一个称为EAbort
的静默异常,通过调用Abort
引发,不会导致顶层异常处理程序显示消息。最后一点就是所谓的silent。
现在代码变成了:
procedure MethodA;
begin
MethodC(MethodB);
end;
function MethodB: string;
begin
if MessageDlg(...)<>mrOK then
Abort;
Result := ...;
end;
procedure MethodC(Value: string);
begin
end;
优点是
MethodA
不需要担心取消操作。如果调用栈更深,
MethodA
在顶部和用户输入点之间的任何方法都不需要知道有关取消操作的内容。
另一个好处是
MethodB
可以保留其自然签名。它返回一个字符串。如果出现传统异常或用户取消等错误,则会抛出异常。
这个非常简单的例子并没有比不使用
Abort
的前一个例子更令人信服。但是,想象一下如果
MethodB
在调用栈中深达4或5层会是什么样子?
我绝对不是说
Abort
应该总是代替
exit
。我的观点是两者都有适用的场景。当用户选择取消操作,并且你不希望在当前事件处理程序中进行进一步处理时,
Abort
很棒。此外,由于用户明确选择取消,因此无需向他们呈现其他UI。您不需要消息框告诉用户他们已取消,因为他们已经知道了。
Abort
有什么关系,特别是在复制文件并在堆栈深度为10时取消的示例中。我理解BeforePost
的示例,尽管我不会使用这样的代码。在OnBeforePost
上调用Abort
后数据集状态如何?如果在发布之前启动了事务,事务的状态是什么?这取决于幕后实现的方式,您需要全面了解它才能安全/确保在这种情况下使用Abort
。 - kobikAbort
是更好的解决方案。 - kobik