Delphi 如何正确编写Try..Finally..Except语句?
以以下代码为例:Delphi 如何正确编写Try..Finally..Except语句?,delphi,try-finally,try-except,Delphi,Try Finally,Try Except,以以下代码为例: 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 so
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将不会被释放,屏幕上的光标仍将被卡住,因为代码在到达这些行之前就被破坏了
现在,除非我弄错了,否则应该有一个异常语句来处理任何此类错误的发生,比如:
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块、after块和Exception块中使用相同的代码两次
基本上,我有时会有一些程序,可能类似于我发布的第一个示例,如果我出现错误,光标会被卡住。添加异常处理程序会有所帮助,但这似乎是一种肮脏的做法——它基本上忽略了Finally块,更不用说从Finally部分复制粘贴到异常部分的丑陋代码了
我仍在学习德尔福,如果这是一个直截了当的问题/答案,我深表歉意
如何正确编写代码来处理语句、正确释放对象和捕获错误等?您只需要两个
try/finally
块:
Screen.Cursor:= crHourGlass;
try
Obj:= TSomeObject.Create;
try
// do something
finally
Obj.Free;
end;
finally
Screen.Cursor:= crDefault;
end;
遵循的指导原则是,您应该使用finally
而不是except
来保护资源。正如您所观察到的,如果您尝试使用除之外的执行此操作,那么您将被迫编写两次最终代码
进入try/finally
块后,无论try
和finally
之间发生什么情况,finally
部分中的代码都保证运行
因此,在上面的代码中,外部的try/finally
确保Screen.Cursor
在遇到任何异常时都会恢复。同样,内部try/finally
确保在其生存期内出现异常时销毁Obj
如果要处理异常,则需要一个不同的try/except
块。但是,在大多数情况下,您不应该尝试处理异常。只需让它传播到主应用程序异常处理程序,它将向用户显示一条消息
如果您处理异常以降低调用链,则调用代码将不知道它调用的代码已失败。您的原始代码并不像您想象的那么糟糕(尽管很糟糕):
Obj.Free
将被执行,无论当您//做某事时发生什么。即使发生异常(在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;
正如其他人所解释的,您需要使用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.
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;
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;
现在你就这样使用它
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的引用计数接口机制负责恢复光标。我认为最“正确”的版本应该是:
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;
在服务/服务器中编写了大量代码,需要处理异常,而不是杀死应用程序后,我通常会选择以下方式:
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.
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;
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;
注意最后的尝试;内试除外;以及Obj创建的位置
如果Obj在其构造函数中创建了其他内容,它可能会半途而废,并在.create()中出现异常而失败;但仍然是一个创造的对象。因此,如果Obj被分配,我会确保它总是被销毁…我会这样做:
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.
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;
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;
@David这就是我的问题,当发生错误时,光标会被卡住一个小时glass@Andreas我要引用你的话。“你永远不知道。这就像系安全带一样。大多数时候,这显然是不必要的,但这是值得的,因为总有发生事故的风险。”我一直在用这个来“恢复”光标到正常状态+1没有必要再尝试了。最后,安德烈亚斯。只需在finally中反转顺序-Obj将始终被释放,可能发生的所有糟糕情况是光标无法更改。@Fabricio:我想我们也应该在尝试之前反转顺序。但是如果光标停留在沙漏上,这是一个恼人的错误,不是吗?写一个嵌套的try..finally
不是很多工作…请解释一下为什么在这段代码中添加try/finally块?安德烈亚斯推断问题背后是对try/finally含义的理解,这可能是正确的。我不确定你的意思,但我补充了try.最后是为了确保someobject一旦完成就从记忆中释放出来,需要做的每一件事都很聪明,也许太聪明了。我不会提倡这一点,尤其是对那些相对较新的人来说。如果你一定要这么做,为什么还要费心于伊库索呢。让SetCursor返回界面就行了。为什么不呢,那里没有危险。编译器只是为您创建一个不可见的try finally
块。。。ICursor提供了额外的功能(不包括在示例中),即读取要还原的原始游标的属性。哦,它工作得非常好。这只是复杂性的另一个层次。非常聪明,但Delphi只是在幕后添加了一个不可见的try finally
,以确保引用计数为零,因此您只需增加复杂性,以确保几次击键的安全,同时强制编译器生成与您自己额外尝试finally基本相同的代码。我没有看到这里的好处,它看起来像是一场代码混淆比赛。在我看来,显式try finally要好得多,因为它更好地解释了代码的意图,并且代码是为人类而编写的,而不是为机器而编写的