将以下代码作为样本:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor:= crHourGlass;
Obj:= TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor:= crDefault;
end;
如果// do something
部分中发生错误,我创建的TSomeObject将不会被释放,而Screen.Cursor仍然会被作为小时玻璃卡住,因为代码在获取之前已经破了那些线?
现在除非我误会,否则应该有一个Exception语句来处理任何错误的发生,例如:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
try
Screen.Cursor:= crHourGlass;
Obj:= TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor:= crDefault;
except on E: Exception do
begin
Obj.Free;
Screen.Cursor:= crDefault;
ShowMessage('There was an error: ' + E.Message);
end;
end;
现在除非我做的事情非常愚蠢,否则没有理由在Finally块和之后以及在Exception块中两次使用相同的代码。
基本上我有时会有一些程序可能与我发布的第一个样本类似,如果我收到错误,光标会被卡在小时玻璃上。添加Exception处理程序会有所帮助,但这似乎是一种肮脏的方式 - 它基本上忽略了Finally块,更不用说带有从Last到Exception部分的copy-paste的丑陋代码。
如果这似乎是一个直截了当的问题/答案,我仍然非常了解Delphi,所以道歉。
如何正确编写代码来处理语句并正确释放对象和捕获错误等?
答案 0 :(得分:31)
您只需要两个try/finally
块:
Screen.Cursor:= crHourGlass;
try
Obj:= TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
finally
Screen.Cursor:= crDefault;
end;
要遵循的准则是,您应使用finally
而不是except
来保护资源。正如您所观察到的,如果您尝试使用except
,那么您将被迫两次编写最终代码。
一旦您输入try/finally
块,无论finally
和try
之间发生什么,finally
部分中的代码都会保证运行。
因此,在上面的代码中,外部try/finally
可确保在遇到任何异常时恢复Screen.Cursor
。同样,内部try/finally
确保Obj
在其生命周期中出现任何异常时被销毁。
如果要处理异常,则需要一个不同的try/except
块。但是,在大多数情况下,您应该不尝试处理异常。只需让它传播到主应用程序异常处理程序,它将向用户显示一条消息。
如果你处理异常以降低调用链,那么调用代码就不会知道它调用的代码失败了。
答案 1 :(得分:17)
您的原始代码没有您想象的那么糟糕:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
Screen.Cursor := crDefault;
end;
无论Obj.Free
发生什么, // do something
都将执行。即使发生异常(try
之后),也会执行finally
块 !这是try..finally
构造的全部要点!
但您还想恢复光标。最迂腐的方式是使用两个try..finally
构造:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Screen.Cursor := crHourGlass;
try
Obj := TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
finally
Screen.Cursor := crDefault;
end;
end;
[但是,我也不介意
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Obj := TSomeObject.Create;
Screen.Cursor := crHourGlass;
try
// do something
finally
Screen.Cursor := crDefault;
Obj.Free;
end;
end;
太多了。 Screen.Cursor := crHourGlass
失败的风险非常低,但在这种情况下,对象将不会被释放(finally
将无法运行,因为您不在try
内,所以双try..finally
更安全。]
答案 2 :(得分:14)
正如其他人所解释的那样,您需要使用try finally
块来保护光标更改。为了避免写这些,我使用这样的代码:
unit autoCursor;
interface
uses Controls;
type
ICursor = interface(IInterface)
['{F5B4EB9C-6B74-42A3-B3DC-5068CCCBDA7A}']
end;
function __SetCursor(const aCursor: TCursor): ICursor;
implementation
uses Forms;
type
TAutoCursor = class(TInterfacedObject, ICursor)
private
FCursor: TCursor;
public
constructor Create(const aCursor: TCursor);
destructor Destroy; override;
end;
{ TAutoCursor }
constructor TAutoCursor.Create(const aCursor: TCursor);
begin
inherited Create;
FCursor := Screen.Cursor;
Screen.Cursor := aCursor;
end;
destructor TAutoCursor.Destroy;
begin
Screen.Cursor := FCursor;
inherited;
end;
function __SetCursor(const aCursor: TCursor): ICursor;
begin
Result := TAutoCursor.Create(aCursor);
end;
end.
现在你就像使用它一样
uses
autoCursor;
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
__SetCursor(crHourGlass);
Obj:= TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
end;
和Delphi的引用计数接口机制负责恢复游标。
答案 3 :(得分:3)
我会这样做:
var
savedCursor: TCursor;
Obj: TSomeObject;
begin
savedCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
Obj:= TSomeObject.Create;
try
try
// do something
except
// record the exception
end;
finally
if Assigned(Obj) then
Obj.Free;
Screen.Cursor := savedCursor;
end;
end;
答案 4 :(得分:1)
在需要处理异常而不是杀死应用程序的服务/服务器中完成了很多代码我通常会这样做:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
try
Obj := NIL;
try
Screen.Cursor := crHourGlass;
Obj := TSomeObject.Create;
// do something
finally
Screen.Cursor := crDefault;
if assigned(Obj) then FreeAndNil(Obj);
end;
except
On E: Exception do ; // Log the exception
end;
end;
请注意最后的尝试;里面的尝试除外;以及Obj创作的位置。
如果Obj在其构造函数中创建了其他内容,它可能会在中途工作并在.create()内部出现异常而失败;但仍然是一个创建的Obj。所以我确保Obj总是在被分配后被销毁......
答案 5 :(得分:0)
我认为最“正确”的版本是:
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: TSomeObject;
begin
Obj := NIL;
Screen.Cursor := crHourGlass;
try
Obj := TSomeObject.Create;
// do something
finally
Screen.Cursor := crDefault;
Obj.Free;
end;
end;
答案 6 :(得分:0)
如果您在这里找到了自己的方式并正在寻找如何在 Delphi 中从 C# 构建 try-except-finally
构造:
// C#
try
{
// Do something
}
catch
{
// Exception!
}
finally
{
// Always do this...
}
答案是您不能直接执行此操作。相反,正如@sacconago 所暗示的,嵌套 try
块如下:
// Delphi
try
try
// Do something
except
// Exception!
end;
finally
// Always do this...
end;
Delphi 的一个很好的功能是您可以将块嵌套为 try...except...finally
或 try...finally...except
,尽管前者更为常见。