如何将例程中的嵌套try / finally块“提取”到可重用的实体中?说我有
procedure DoSomething;
var
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
begin
AcquireResource1;
try
AcquireResource2;
try
AcquireResource3;
try
// Use the resources
finally
ReleaseResource3;
end;
finally
ReleaseResource2;
end;
finally
ReleaseResource1;
end;
end;
并想要像
这样的东西TDoSomething = record // or class
strict private
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
public
procedure Init; // or constructor
procedure Done; // or destructor
procedure UseResources;
end;
procedure DoSomething;
var
Context: TDoSomething;
begin
Context.Init;
try
Context.UseResources;
finally
Context.Done;
end;
end;
我希望它具有与嵌套原件相同的异常安全性。是否足以在TDoSomething.Init
中对资源N变量进行零初始化,并在if Assigned(ResourceN) then
中进行一些TDoSomething.Done
次检查?
答案 0 :(得分:5)
关于课程有三件事使这个成语变得安全和容易:
Free
总是安全的,因此您永远不需要先检查Assigned
。由于析构函数可以依赖所有字段来获取已知值,因此无论构造函数在崩溃前获得多远,它都可以安全地调用Free
。每个字段都将保留一个有效的对象引用,或者它将为nil,无论哪种方式,都可以安全地释放它。
constructor TDoSomething.Create;
begin
Resource1 := AcquireResource1;
Resource2 := AcquireResource2;
Resource3 := AcquireResource3;
end;
destructor TDoSomething.Destroy;
begin
Resource1.Free;
Resource2.Free;
Resource3.Free;
end;
以与使用任何其他类相同的方式使用它:
Context := TDoSomething.Create;
try
Context.UseResources;
finally
Context.Free;
end;
答案 1 :(得分:1)
是的,您可以使用单个try / finally / end块来实现零初始化的多个资源。
另一种可能的解决方案可以在Barry Kelly blog
中找到答案 2 :(得分:1)
最终在Assigned中测试的模式用于Delphi源代码。你做了同样的事情,但我认为你应该移动Context.Init从Context.Init中捕获异常。
procedure DoSomething;
var
Context: TDoSomething;
begin
try
Context.Init;
Context.UseResources;
finally
Context.Done;
end;
end;
编辑1 如果没有Context.Init和Context.Done,就应该这样做。如果您将所有AquireResource代码放在try
之前,如果在AcquireResource2中出现异常,则不会释放Resource1
procedure DoSomething;
var
Resource1: TSomeKindOfHandleOrReference1;
Resource2: TSomeKindOfHandleOrReference2;
Resource3: TSomeKindOfHandleOrReference3;
begin
Resource1 := nil;
Resource2 := nil;
Resource3 := nil;
try
AcquireResource1;
AcquireResource2;
AcquireResource3;
//Use the resources
finally
if assigned(Resource1) then ReleaseResource1;
if assigned(Resource2) then ReleaseResource2;
if assigned(Resource3) then ReleaseResource3;
end;
end;