注意:请耐心听我说,我感觉有点「火焰烤」由于一些讨论here and here以及我报告的一些问题here and here.
一些背景
旧的(10.4 之前)FreeAndNil
看起来像这样:
FreeAndNil(var SomeObject)
新的和新鲜的FreeAndNil
看起来像这样:
FreeAndNil(const [ref] SomeObject: TObject);
IMO 都有各自的缺点:
- 旧的不做任何类型检查,所以调用
FreeAndNil
在指针、记录和接口上编译得很好,但在运行时会产生有趣但通常不需要的效果。 (完全疯狂,或者如果你幸运的话,它会因 EAccessViolation、EInvalidOperation 等而停止。)
- 新的接受一个 const 参数,因此也接受任何对象。但随后提供的对象指针实际上是使用一些更改的古怪的代码.
- 您现在可以调用新的
FreeAndNil
像这样:FreeAndNil(TObject.Create)
它会编译甚至运行得很好。我喜欢旧的FreeAndNil
当我出错时它会警告我并提供例如属性而不是字段。不确定如果您为此提供对象类型属性会发生什么FreeAndNil
执行。没有尝试。
如果我们将签名更改为FreeAndNil(var SomeObject:TObject)
那么它将不允许我们传递任何其他变量类型TObject
类型。这也有道理,好像没有一样FreeAndNil
,可以轻松更改作为类型提供的变量TComponent
在例程中将 var 变量更改为完全不同类型的对象,例如TCollection
。当然FreeAndNil
不会做这样的事情,因为它总是将 var 参数更改为 nil。
所以这使得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
直接地。
我做什么not非常喜欢的是:
- 类型检查在运行时进行,并且仅在断言为 ON 时进行。
- 感觉就像必须传递同一个变量两次。这不一定是真的/必需的。它必须是同一个对象,并且参数必须是变量。
另一项调查
但如果可以不带参数调用该多好
var MyObj:=TSomeObject.Create;
...
MyObj.FreeAndNil;
所以我乱搞了self
指针并设法将其设置为nil
使用 10.4 中使用的相同 Hacky-Wacky 代码FreeAndNil
。嗯...这有效inside方法,self
指向nil
。但打电话之后FreeAndNil
像这样,MyObj 变量不是 nil,而是一个过时的指针。 (这正是我所期望的。)此外,MyObj
可以是属性或例程(的结果)、构造函数等。
so nope这里也...
最后的问题是:
您能想到一个更干净/更好的解决方案或技巧吗:
- FreeAndNil(var aObject:TObject) 具有不太严格的类型检查编译时(可能是编译器指令?),因此它允许编译和调用变量any对象类型。
- 当某些东西通过时抱怨编译时间not某种对象类型的变量/字段
- 帮助描述什么是最好的解决方案/要求RSP-29716