delphifreeandnil:寻找替代实现

delphifreeandnil:寻找替代实现,delphi,memory-management,object-lifetime,rad-studio,delphi-run-time-library,Delphi,Memory Management,Object Lifetime,Rad Studio,Delphi Run Time Library,注:恕我直言,我觉得有点“火焰烤”由于一些讨论和一些问题,我报告和 一些背景 Ye olde(10.4之前)FreeAndNil看起来像这样: FreeAndNil(var SomeObject) FreeAndNil(const [ref] SomeObject: TObject); var MyObj:=TSOmeObject.Create; ... MyObj.FreeAndNilObj(MyObj); 全新的FreeAndNil如下所示: FreeAndNil(var SomeOb

注:恕我直言,我觉得有点“火焰烤”由于一些讨论和一些问题,我报告和

一些背景

Ye olde(10.4之前)
FreeAndNil
看起来像这样:

FreeAndNil(var SomeObject)
FreeAndNil(const [ref] SomeObject: TObject);
var MyObj:=TSOmeObject.Create;
...
MyObj.FreeAndNilObj(MyObj);
全新的
FreeAndNil
如下所示:

FreeAndNil(var SomeObject)
FreeAndNil(const [ref] SomeObject: TObject);
var MyObj:=TSOmeObject.Create;
...
MyObj.FreeAndNilObj(MyObj);
IMO两者都有其缺点:

  • 旧版本不进行任何类型检查,因此在指针、记录和接口上调用
    FreeAndNil
    可以很好地编译,但在运行时会产生有趣但通常不需要的效果。(完全狂暴,或者如果幸运的话,它会因每次访问违规、EInvalidOperation等而停止)
  • 新的接受常量参数,因此接受任何对象。但随后,所提供的对象指针实际上是使用一些古怪的代码更改的
  • 您现在可以像这样调用新的
    FreeAndNil
    FreeAndNil(TObject.Create)
    ,它将编译,甚至可以正常运行。我喜欢旧的
    FreeAndNil
    ,它在我出错时警告我,并提供例如属性而不是字段。不确定如果为此
    FreeAndNil
    实现提供对象类型属性会发生什么情况。我没试过
如果我们将签名更改为
FreeAndNil(var SomeObject:TObject)
,那么它将不允许我们传递任何其他变量类型,而恰恰是
TObject
类型。这也是有意义的,就像它不是
free和nil
,可以很容易地更改例程中作为type
TComponent
提供的变量,将var变量更改为完全不同类型的对象,例如
TCollection
。当然,
FreeAndNil
不会做这样的事情,因为它总是将var参数更改为nil

所以这使得
FreeAndNil
成为一个特例。 也许特别到足以说服delphi添加一个编译器magic
FreeAndNil
实现?有人投票吗

潜在的解决办法

我提出了下面的代码作为一种替代方法(这里是一种辅助方法,但也可以是
TObject
实现的一部分),它将两个世界结合起来。断言将有助于在运行时查找无效调用

procedure TSGObjectHelper.FreeAndNilObj(var aObject);
begin
  if Assigned(self) then
  begin
    Assert(TObject(aObject)=self,ClassName+'.FreeAndNil Wrong parameter provided!');
    pointer(aObject):=nil;
    Destroy;
  end;
end;
用法如下:

FreeAndNil(var SomeObject)
FreeAndNil(const [ref] SomeObject: TObject);
var MyObj:=TSOmeObject.Create;
...
MyObj.FreeAndNilObj(MyObj);
我实际上已经测试过这个例程,它甚至比10.4
FreeAndNil
实现稍微快一点。我猜是因为我先做作业检查,然后直接调用
Destroy
。 我不太喜欢的是:

  • 类型检查在运行时进行,并且仅当断言处于启用状态时才进行
  • 这感觉就像必须将同一个变量传递两次。这不一定是真的/必需的。它必须是同一个对象,并且参数必须是变量
另一项调查

但是如果一个人可以在没有参数的情况下调用,那不是很好吗

var MyObj:=TSomeObject.Create;
...
MyObj.FreeAndNil;
因此,我把
self
指针弄乱了,并设法使用10.4在
FreeAndNil
中使用的相同的骇客古怪代码将其设置为
nil
。好。。。在方法内部起作用的,
self
指向
nil
。但是像这样调用
FreeAndNil
之后,MyObj变量不是nil,而是一个过时的指针。(这正是我所期望的。)此外,
MyObj
可以是一个属性或(例程、构造函数等的结果)

所以这里也没有

最后是一个问题:

你能想出一个更干净/更好的解决方案或技巧:

  • FreeAndNil(var aoobject:TObject)在编译时没有那么严格的类型检查(可能是编译器指令?),因此它允许编译和调用任何对象类型的变量
  • 当传递的不是某个对象类型的变量/字段时,会抱怨编译时
  • 帮助描述以下方面的最佳解决方案/需求:

对于类型安全且不允许释放函数结果和属性的
FreeAndNil
唯一正确的解决方案是通用var参数:

 procedure FreeAndNil<T: class>(var Obj: T); inline;
Rio中引入的类型推断将允许您在不指定泛型签名的情况下调用它:

TObj.FreeAndNil(Obj);
在旧的Delphi版本中调用(并使用)generic
FreeAndNil
也是可能的,但更详细

TObj.FreeAndNil<TFoo>(Obj);
TObj.FreeAndNil(Obj);

因为我们不能创建一个全局的
过程FreeAndNil(var aObject:T)
我建议将下面的代码作为TObject类的方法。(由embarcadero进行rtl更改,但不需要更改编译器)

10.3+类型推断避免了编写:

FreeAndNil<TMyClassSpec>(SomeObjectVariable)
这对我来说是可以接受的,并且比当前和历史上使用
FreeAndNil(const[ref]aObject:TObject)
或非类型
FreeAndNil(var aObject)

由于例程非常简单,而且性能似乎是一个问题,因此有人可能会争论为它提供一个汇编程序实现。尽管我不确定泛型(最好是内联)方法是否允许/可能这样做

FTM:你也可以保持
FreeAndNil(var-aoobject:TObject)
并告诉人们进行如下类型转换,这也避免了编译器抱怨var类型。但是在这种情况下,可能需要调整很多源代码。另一方面,它节省了代码膨胀,仍然避免了无效地使用函数结果、属性或无效类型(如记录和指针)作为
FreeAndNil
的参数,并且非常易于更改/实现

...
var Obj:=TSomeObject.Create;
try
   DoSOmethingUseFulWithObj(Obj);
finally
  FreeAndNil(TObject(Obj)); // typecast avoids compiler complaining. Compiler wont allow invalid typecasts
end;
...

首先不要使用FreeAndNil!如果你真的必须把变量设为零,那你自己去做吧,嗯?FreeAndNill接受常量[ref],但它仍然将传入的引用设置为nil?功能正在运行。虽然它以前相当不安全(新的不再安全),但是