尝试除<?>退出是否安全? 或者我应该拨打加注?
我尝试了下面的两个示例,并在举例中,跟踪经历了Delphi的内部库代码。退出时只退出程序,仅此而已。
我认为最好保留应用程序堆栈或队列或类似的东西。调用exit
会打破那个堆栈吗?
示例1(加注)
SDDatabase1.StartTransaction;
Try
SDQuery1.ApplyUpdates;
SDDatabase1.Commit;
SDQuery1.CommitUpdates;
Except
SDDatabase1.Rollback;
SDQuery1.RollbackUpdates;
raise;
End;
..............//other codes I don't want to execute
示例2(退出)
SDDatabase1.StartTransaction;
Try
SDQuery1.ApplyUpdates;
SDDatabase1.Commit;
SDQuery1.CommitUpdates;
Except
SDDatabase1.Rollback;
SDQuery1.RollbackUpdates;
MessageDlg('Save Failed because: '+E.Message, mtError, [mbOK], 0);
exit;
end;
..............//other codes I don't want to execute
答案 0 :(得分:11)
很少有替代选项(A与B)可以客观地评估,因为一个人总是更好&#34;比另一个。这就是为什么正确理解每种方法的差异和含义非常重要。
当单独检查单个方法时,您的示例都会在除块结束后跳过代码。但是,一个离开异常状态而另一个不离开。这不会影响您编写的方法,而是影响方法的调用者(直接和间接)。
procedur Caller1;
begin
//...[A]
Caller2;
//...[B]
end;
procedure Caller2;
begin
//...[C]
CallDatabaseMethod; {Will raise; or Exit; based on example chosen}
//...[D]
end;
两个例子之间的关键区别是:
示例1 也会跳过 [B]和[D]代码。但是,例2将执行[B]和[D]代码。当您了解这种差异时,您就有权决定[B]和[D] 是否 执行。
但是,我怀疑CallDatabaseMethod
无法正确执行所有操作的事实表明[B]和[D]应该不被称呼。例如。假设数据库方法更新客户帐户数据,[B]和[D]执行与发送最新语句相关的操作。 您可能不希望在更新失败时发送声明!
那就是说,如果你的方法可以被视为&#34;成功完成&#34; 尽管例外,但通过吞噬异常的方式完全可以接受。例如。假设你有一个方法来&#34;添加一行&#34;它的post条件只是该行必须存在于数据库中。然后,如果您的数据库返回PK违规,显然行确实存在。 在这种情况下,吞下异常非常有意义。
您当然可以调整示例2的实现,以便隐藏错误。
如果您的方法被编写为一个返回成功或失败状态的函数,那么调用者可以使用它来解决上述问题。 E.g。
function Caller1: Boolean;
begin
Result := Caller2;
{Caller can decide to skip/ignore/do something different}
if Result then ...
end;
function Caller2: Boolean;
begin
Result := CallDatabaseMethod;
{Caller can decide to skip/ignore/do something different}
if Result then ...
end;
function CallDatabaseMethod: Boolean;
begin
Result := True;
//...
try
//...
except
on E: ExceptionType do
begin
//...
Result := False;
end;
end;
//...
end;
这与Windows API的工作方式相同。它确实有其优点和缺点:
我建议最好的方法是确定可以考虑哪种错误&#34;正常&#34;并确保使用显式错误结果而不是异常处理此问题。当然,以上 1 的实例是主要候选人。
最后David已经在示例2中标记了对您的消息对话框的关注。因此,此注释假设此代码始终在用户上下文中运行。
我理解立即显示消息的冲动。您有异常传播到应用程序级别处理程序时丢失的上下文。要考虑的一个选项是使用Abort
,它只会引发EAbort
例外。
try
//...
except
on E: ExceptionType do
begin
MessageDlg(...);
Abort;
end;
end;
默认应用程序异常应忽略此异常并且不显示消息。如果你有自己的处理程序,你应该在显示任何消息之前检查异常类。
作为附注,我想考虑问题中的特定句子。
我读到保留应用程序堆栈或队列或其他类似的东西更好。
显然,如果您不确定自己所阅读的内容,很难向您解释。根据我的答案的前面部分,您可能已经有了更清晰的图片。
但是,它可能指的是另一种异常处理方法的另一个问题。提出新的例外。 (您可以使用raise;
来避免此问题,因为它会在原始上下文中重新引发原始异常。)这样做是为了提供&# 34;更有意义的错误消息 - 类似于示例2 。
try
except
raise EOtherError.Create('My Message');
end;
上面的问题是,当这个异常最终传播到应用程序处理程序时,你已经丢失了原来的类;原始异常地址;和原始的消息。这种方法通常会给用户带来更明确的错误:例如&#34;无法打开文件filename
&#34;但隐藏可能在故障排除中有用的信息。例如。它是磁盘错误,文件不是文件,是访问权限错误。
因此:无论何时处理错误(无论使用何种方法),都要考虑的重要事项是:是否会有足够的信息来解决错误?
答案 1 :(得分:10)
原则上两者都是安全的,但不可能推荐一种或其他方法。这取决于你的设计意图。鉴于代码的预期用途,您必须决定哪个是合适的。
如果您在此代码中处理异常,并将函数保留为exit
,则执行将返回到调用函数,并且它不知道函数是成功还是失败。这可能有问题。
如果你重新引发异常,执行将移动到调用堆栈的下一个合适的异常处理程序,并沿途传递任何finally块。
所以,行为会有所不同,由你来决定你想要的是什么。
尝试在调用堆栈中进一步处理异常是一个常见的初学者错误。例如,假设您希望在GUI应用程序和非可视应用程序中使用您的代码。您对MessageDlg
的使用不适合非可视应用。
在GUI应用程序中,大多数操作通常是响应用户输入,例如按下按钮。异常通常会导致整个操作中止。在这种情况下,您根本不应尝试处理异常。让它们传递给应用程序级异常处理程序。
最后,您的代码以相同的方式处理所有异常。这通常是不明智的。例如,访问冲突肯定会与数据库错误区别对待。