Delphi最终尝试了多个对象

Delphi最终尝试了多个对象,delphi,Delphi,我已经编写了这段代码: FD := TList<double>.Create; FN := TList<double>.Create; RemList := TList<double>.Create; dati := TStringList.Create; try try //code except on E : Exception do ShowMessage('Incorrect values. Error:

我已经编写了这段代码:

 FD := TList<double>.Create;
 FN := TList<double>.Create;
 RemList := TList<double>.Create;
 dati := TStringList.Create;

 try

  try

   //code

  except
   on E : Exception do 
   ShowMessage('Incorrect values. Error: ' + E.Message);
  end;

 finally

  if Assigned(dati) then
   dati.Free;

  if Assigned(FD) then
   FD.Free;

  if Assigned(FN) then
   FN.Free;

  if Assigned(RemList) then
   RemList.Free;

 end;
FD:=TList.Create;
FN:=TList.Create;
RemList:=TList.Create;
dati:=TStringList.Create;
尝试
尝试
//代码
除了
关于E:Exception-do
ShowMessage('值不正确。错误:'+E.Message);
结束;
最后
如果分配(dati),则
免费;
如果分配(FD),则
FD.免费;
如果分配(FN),则
FN.免费;
如果已分配(RemList),则
RemList.免费;
结束;
我正在使用firemonkey,所以在移动设备上运行时不会出现问题,因为ARC将管理生命周期。但当我在windows上时,这段代码安全吗

我已经读到,我不能把finally和catch都放在一起,所以我需要一个嵌套的try块。我想最好的办法是:

FD := TList<double>.Create;
try

 FN := TList<double>.Create;
 try

  RemList := TList<double>.Create;
  try

   //and so on...

  finally
   Rem.Free;
  end;

 finally
  FN.Free;
 end;

finally
 FD.Free;
end;
FD:=TList.Create;
尝试
FN:=TList.Create;
尝试
RemList:=TList.Create;
尝试
//等等。。。
最后
自由物;
结束;
最后
FN.免费;
结束;
最后
FD.免费;
结束;
在这里,我确信在任何情况下,我都会以安全的方式释放该对象,但代码很难阅读。我已经看到Marco Cantu在object pascal手册中提出了第二种方法,我理解这一点,但就我而言,我想避免它


我写的第一段代码会出错吗?

如果我是你,我会使用第二段代码,因为你已经知道它更好;正如您所说,
TList.Create
(与其他构造函数一样)是“安全”的,但如果您总是假设角落后面有风险,那就更好了

请注意,当我说safe时,我的意思是如果您以这种方式调用它们,它们不会引发异常。异常可能发生在您可能无法预测的任何情况下,因此您确实应该通过try finally来保护代码

您还使用了
Assigned()
,这是无用的。如果您查看
Free
的实现,您会发现此代码(也有注释):

procedure-TObject.Free;
开始
//在ARC下,由于编译器进行翻译,因此实际上不会调用此方法
//该调用仅是实例变量的nil赋值,然后实例变量调用_InstClear
{$IFNDEF AUTOREFCOUNT}
如果Self为零,则
破坏;
{$ENDIF}
结束;

Assigned()
如果对象等于或不等于零,则返回true或false,因此基本上是在进行无用的双重检查。

而嵌套的第二个代码
try…最终
是通过书本方式处理对象构造/解构块,您还可以安全地将这些分组到单个
try…finally
块中

一些事实将帮助我们压缩上述代码

  • 构造函数总是可以抛出异常
  • 析构函数决不能抛出异常。如果您处理的类破坏了可以抛出的析构函数,那么您将面临更大的问题
  • 非托管局部变量未初始化-在包含对象引用的非ARC编译器中
因此,您的代码如下所示:

// initialize object references so we can safely use them
FN := nil;
FD := nil;
RemList := nil; 
dati := nil;

try
  FD := TList<double>.Create;
  FN := TList<double>.Create;
  RemList := TList<double>.Create;
  dati := TStringList.Create;

  // do something

finally   
   dati.Free;
   FD.Free;
   FN.Free;
   RemList.Free;
end;
//初始化对象引用以便安全使用它们
FN:=零;
FD:=零;
RemList:=零;
dati:=零;
尝试
FD:=TList.Create;
FN:=TList.Create;
RemList:=TList.Create;
dati:=TStringList.Create;
//做点什么
最后
免费;
FD.免费;
FN.免费;
RemList.免费;
结束;
如果任何构造函数失败,最后将执行块,并清除分配的所有引用。因为
Free
在调用实例析构函数之前测试nil,所以在nil引用上调用它是安全的

上面介绍了如何编写安全构造/解构链。如果您的do something代码引发异常,您可以在内部使用
try..except
块处理它,就像您现在正在做的一样


请记住,上面的代码不会处理构造函数抛出的异常,它只是执行适当的清理。如果您想处理可能的构造异常,您必须将上述代码包装到另一个
try…中,但
块除外,或者让它们传播到更高的级别

是的,其中一个析构函数可能会引发跳过以下析构函数。那么?这安全吗?经验法则是不要假设它是安全的。我现在正在研究这两个特定的类,到目前为止,我没有看到在它们的析构函数中存在潜在异常的证据。但我可能错了,你永远不应该认为它是安全的。将它们嵌套在第二个示例中。编程的#1规则:假设一切都是错误,并确保正确处理它们。联系析构函数以避免引发错误。如果他们这样做了,你已经完蛋了,所以别担心。真正的问题是,如果其中一个构造函数引发,您将泄漏已经创建的对象。为什么你在免费打电话之前要测试分配的任务?自由已经做到了这一点。第二个是正确的做事方式。您可能不喜欢它,但除了
try..finally块之外,它是编写多个
try..的正确方法。在我看来,它实际上比第一个更容易阅读,因为你可以清楚地遵循代码的流程。好吧,我不知道免费的东西。那么赋值是没有用的?@RaffaeleRossi好的,谢谢,我会设法格式化block 2,如果说这些构造函数不会引发,那就错了。每个人都可以提高。例如,内存不足。
// initialize object references so we can safely use them
FN := nil;
FD := nil;
RemList := nil; 
dati := nil;

try
  FD := TList<double>.Create;
  FN := TList<double>.Create;
  RemList := TList<double>.Create;
  dati := TStringList.Create;

  // do something

finally   
   dati.Free;
   FD.Free;
   FN.Free;
   RemList.Free;
end;