提取嵌套的 try/finally 块
如何将嵌套的 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
中对 ResourceN 变量进行零初始化并在 TDoSomething.Done
中执行一些 if Assigned(ResourceN) then
检查是否足够?
How would you "extract" nested try/finally blocks from a routine into a reusable entity? Say I have
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;
and want something like
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;
I want this to have the same exception-safety as the nested original. Is it enough to zero-initialize the ResourceN variables in TDoSomething.Init
and do some if Assigned(ResourceN) then
checks in TDoSomething.Done
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
关于类的三件事使得这个习惯用法安全且简单:
Free
始终是安全的,因此您无需先检查Assigned
。由于析构函数可以依赖所有字段都具有已知值,因此它可以安全地对所有内容调用
Free
,无论构造函数在崩溃之前走了多远。每个字段要么保存一个有效的对象引用,要么为零,无论哪种方式,释放它都是安全的。使用它的方式与使用任何其他类的方式相同:
There are three things about classes that make this idiom safe and easy:
Free
on a null reference, so you never need to checkAssigned
first.Since the destructor can rely on all fields to have known values, it can safely call
Free
on everything, regardless of how far the constructor got before crashing. Each field will either hold a valid object reference or it will be nil, and either way, it's safe to free it.Use it the same way you use any other class:
是的,您可以对零初始化的多个资源使用单个 try/finally/end 块。
另一种可能的解决方案可以在 Barry Kelly 博客
Yes, you can use a single try/finally/end block for multiple resources with zero-initialization.
Another possible solution can be found in Barry Kelly blog
Delphi 源代码中使用了在finally 中对Assigned 进行测试的模式。您做了同样的事情,但我认为您应该移动 Context.Init 以捕获 Context.Init 中的异常。
编辑 1 这是在没有 Context.Init 和 Context.Done 的情况下应该如何进行的操作。如果将所有 AquireResource 代码放在
try
之前,则在 AcquireResource2 中出现异常时,您将不会释放 Resource1The pattern with testing on Assigned in finally is used in the Delphi source. You do kind of the same thing but I think you should move Context.Init to capture exception from Context.Init.
Edit 1 This is how you should do it without Context.Init and Context.Done. If you place all AquireResource code before
try
you will not free Resource1 if you get an exception in AcquireResource2