嗨什么是嵌套try& amp;的最佳方法最后在delphi中的陈述?
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
cds1 := TClientDataSet.Create(application );
try
cds2 := TClientDataSet.Create(application );
try
cds3 := TClientDataSet.Create(application );
try
cds4 := TClientDataSet.Create(application );
try
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
cds4.free;
end;
finally
cds3.free;
end;
finally
cds2.free;
end;
finally
cds1.free;
end;
end;
你能建议一个更好的方法吗?
答案 0 :(得分:30)
以下内容如何:
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
cds1 := Nil;
cds2 := Nil;
cds3 := Nil;
cds4 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
cds3 := TClientDataSet.Create(nil);
cds4 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
freeandnil(cds4);
freeandnil(cds3);
freeandnil(cds2);
freeandnil(Cds1);
end;
end;
这使它保持紧凑,并且只尝试释放创建的实例。确实没有必要执行嵌套,因为任何失败都会导致掉到最后并执行您提供的示例中的所有清理。
我个人尝试不在同一个方法中嵌套...例外是try / try / except / finally场景。如果我发现自己需要嵌套,那么对我来说这是一个思考重构到另一个方法调用的好时机。
编辑将对象创建更改为不引用应用程序,因为在此示例中不需要。
答案 1 :(得分:18)
我会用这样的东西:
var
Safe: IObjectSafe;
cds1 : TClientDataSet;
cds2 : TClientDataSet;
cds3 : TClientDataSet;
cds4 : TClientDataSet;
begin
Safe := ObjectSafe;
cds1 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds2 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds3 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
cds4 := Safe.Guard(TClientDataSet.Create(nil)) as TClientDataSet;
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
// if Safe goes out of scope it will be freed and in turn free all guarded objects
end;
有关界面的实现,请参阅this文章,但您可以轻松地自己创建类似的内容。
修改强>
我只是注意到在链接文章中Guard()是一个过程。在我自己的代码中,我重载了返回TObject的Guard()函数,上面的示例代码假定类似的东西。当然,对于泛型,现在可以使用更好的代码......
编辑2:
如果你想知道为什么尝试...终于在我的代码中被完全删除:在不引入内存泄漏(当析构函数引发异常)或访问冲突的可能性时,删除嵌套块是不可能的。因此,最好使用辅助类,并让接口的引用计数完全接管。即使某些析构函数引发了异常,辅助类也可以释放它所保护的所有对象。
答案 2 :(得分:4)
还有另一种代码的变体,没有嵌套的尝试...最后我刚刚发生了。如果你没有将构造函数的AOwner参数设置为nil来创建组件,那么你可以简单地利用VCL免费提供的生命周期管理:
var
cds1: TClientDataSet;
cds2: TClientDataSet;
cds3: TClientDataSet;
cds4: TClientDataSet;
begin
cds1 := TClientDataSet.Create(nil);
try
// let cds1 own the other components so they need not be freed manually
cds2 := TClientDataSet.Create(cds1);
cds3 := TClientDataSet.Create(cds1);
cds4 := TClientDataSet.Create(cds1);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
cds1.Free;
end;
end;
我非常相信小代码(如果它没有太多混淆)。
答案 3 :(得分:3)
如果你想要这个(IMO)丑陋的路线(初始化为nil的组处理以了解是否需要释放),你至少必须保证你不会让一个析构函数中的异常阻止释放其余的物体。
类似的东西:
function SafeFreeAndNil(AnObject: TObject): Boolean;
begin
try
FreeAndNil(AnObject);
Result := True;
except
Result := False;
end;
end;
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
IsOK1 : Boolean;
IsOK2 : Boolean;
begin
cds1 := Nil;
cds2 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
IsOk2 := SafeFreeAndNil(cds2); // an error in freeing cds2 won't stop execution
IsOK1 := SafeFreeAndNil(Cds1);
if not(IsOk1 and IsOk2) then
raise EWhatever....
end;
end;
答案 4 :(得分:2)
exceptions in constructors & destructors
上有一个很棒的视频它显示了一些很好的例子,例如:
var cds1 : TClientDataSet;
cds2 : TClientDataSet;
begin
cds1 := Nil;
cds2 := Nil;
try
cds1 := TClientDataSet.Create(nil);
cds2 := TClientDataSet.Create(nil);
///////////////////////////////////////////////////////////////////////
/// DO WHAT NEEDS TO BE DONE
///////////////////////////////////////////////////////////////////////
finally
freeandnil(cds2); //// what has if there in an error in the destructor of cds2
freeandnil(Cds1);
end;
end;
如果在cds2
的析构函数中出现错误,该怎么办?Cds1不会被销毁
编辑
另一个好资源是:
Jim McKeeth关于代码范围III中Delayed Exception Handling的精彩视频,他谈到了处理finally块中异常的问题。
答案 5 :(得分:1)
@mghie:Delphi有堆栈分配的对象:
type
TMyObject = object
private
FSomeField: PInteger;
public
constructor Init;
destructor Done; override;
end;
constructor TMyObject.Init;
begin
inherited Init;
New(FSomeField);
end;
destructor TMyObject.Done;
begin
Dispose(FSomeField);
inherited Done;
end;
var
MyObject: TMyObject;
begin
MyObject.Init;
/// ...
end;
不幸的是,正如上面的例子所示:堆栈分配的对象不会阻止内存泄漏。
所以这仍然需要像这样调用析构函数:
var
MyObject: TMyObject;
begin
MyObject.Init;
try
/// ...
finally
MyObject.Done;
end;
end;
好吧,我承认,这几乎是关于主题的,但我认为在这种情况下它可能很有意思,因为堆栈分配的对象被提到作为解决方案(如果没有自动析构函数调用它们就不是这样)。 / p>