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