提取嵌套的try/finally块 [英] Extract nested try/finally blocks
问题描述
您如何将嵌套的try/finally块从例程提取"到可重用的实体中?说我有
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;
并想要类似的东西
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
检查是否足够?
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
?
推荐答案
有关类的三件事使此成语变得安全又简单:
There are three things about classes that make this idiom safe and easy:
- 在构造函数的内存分配阶段(在实际构造函数主体运行之前),类引用字段被初始化为nil.
- 当构造函数中发生异常时,析构函数将自动被调用.
- 在空引用上调用
Free
始终是安全的,因此您无需先检查Assigned
.
- During the memory-allocation phase of the constructor (before the real constructor body runs), class-reference fields get initialized to nil.
- When an exception occurs in a constructor, the destructor is called automatically.
- It's always safe to call
Free
on a null reference, so you never need to checkAssigned
first.
由于析构函数可以依赖所有字段具有已知值,因此它可以安全地对所有内容调用Free
,而不管构造函数在崩溃前走了多远.每个字段要么保存一个有效的对象引用,要么为nil,无论哪种方式,释放它都是安全的.
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.
constructor TDoSomething.Create;
begin
Resource1 := AcquireResource1;
Resource2 := AcquireResource2;
Resource3 := AcquireResource3;
end;
destructor TDoSomething.Destroy;
begin
Resource1.Free;
Resource2.Free;
Resource3.Free;
end;
以与其他任何类相同的方式使用它:
Use it the same way you use any other class:
Context := TDoSomething.Create;
try
Context.UseResources;
finally
Context.Free;
end;
这篇关于提取嵌套的try/finally块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!