注意:请耐心等待,由于此处和此处的一些讨论以及我在此处和此处报告的一些问题,我感到有点“火上浇油”。
一些背景
Ye olde(10.4 之前)FreeAndNil
看起来像这样:
FreeAndNil(var SomeObject)
新的和新鲜的FreeAndNil
样子是这样的:
FreeAndNil(const [ref] SomeObject: TObject);
IMO 都有其缺点:
- 旧的不做任何类型检查,所以调用
FreeAndNil
指针、记录和接口编译得很好,但在运行时会产生有趣但通常不需要的效果。(完全发疯,或者如果幸运的话,它会因 EAccessViolation、EInvalidOperation 等而停止。) - 新的接受一个 const 参数,因此接受任何对象。但是随后提供的对象指针实际上是使用一些hacky-wacky 代码更改的。
- 你现在可以
FreeAndNil
像这样调用 new:FreeAndNil(TObject.Create)
它会编译甚至运行得很好。我喜欢在FreeAndNil
我出错时警告我并提供例如属性而不是字段的旧版本。FreeAndNil
如果您为此实现提供对象类型属性,不确定会发生什么。没试过。
如果我们将签名更改为FreeAndNil(var SomeObject:TObject)
then 它将不允许我们传递任何其他变量类型,然后正是该TObject
类型。这也是有道理的,就好像它不是一样,人们可以轻松地更改例程中FreeAndNil
作为类型提供的变量,将 var 变量更改为完全不同类型的对象,例如. 当然不会做这样的事情,因为它总是将 var 参数更改为 nil。TComponent
TCollection
FreeAndNil
所以这是FreeAndNil
一个特殊情况。
也许甚至足以说服delphi添加编译器魔术 FreeAndNil
实现?投票给任何人?
潜在的解决方法
我想出了下面的代码作为替代方案(这里作为辅助方法,但也可以作为实现的一部分TObject
),它结合了两个世界。这Assert
将有助于在运行时查找无效调用。
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;
用法将是这样的:
var MyObj:=TSOmeObject.Create;
...
MyObj.FreeAndNilObj(MyObj);
我已经实际测试过这个例程,它甚至比 10.4 的FreeAndNil
实现要快一些。我猜是因为我先做作业检查,然后Destroy
直接打电话。我不太喜欢的是:
- 类型检查在运行时进行,并且仅在断言为 ON 时进行。
- 感觉就像必须两次传递相同的变量。这不一定是正确的/必需的。它必须是同一个对象,并且参数必须是变量。
另一项调查
但是如果一个人可以在没有参数的情况下调用,那不是很好吗
var MyObj:=TSomeObject.Create;
...
MyObj.FreeAndNil;
所以我弄乱了self
指针,并设法将其设置为nil
使用 10.4 在其FreeAndNil
. 嗯......这在方法内部有效,self
指向nil
. 但是在FreeAndNil
这样调用之后,MyObj 变量不是 nil,而是一个过时的指针。(这是我所期望的。)此外,MyObj
可以是属性或(的结果)例程、构造函数等。
所以这里也不行……
最后是问题:
您能想出一个更清洁/更好的解决方案或技巧吗:
- FreeAndNil(var aObject:TObject) 具有不那么严格的类型检查编译时间(可能是编译器指令?),因此它允许编译和调用任何对象类型的变量。
- 当传递的东西不是某个对象类型的变量/字段时,抱怨编译时间
- 帮助描述RSP-29716中的最佳解决方案/要求是什么