问题描述
注意:请耐心等待,由于此处和此处,以及我报告的一些问题此处和此处.
NOTE: Bear with me, I feel a little "flame grilled" due to some discussions over here and here and some issues I reported here and here.
某些背景
过去(10.4之前) FreeAndNil
看起来像这样:
Ye olde (pre 10.4) FreeAndNil
looked like this:
FreeAndNil(var SomeObject)
最新的 FreeAndNil
看起来像这样:
FreeAndNil(const [ref] SomeObject: TObject);
IMO都有缺点:
- 旧版本不执行任何类型检查,因此在指针,记录和接口上调用
FreeAndNil
可以很好地进行编译,但是会在运行时产生有趣但通常不想要的效果.(会完全发疯,或者如果您很幸运,它会因EAccessViolation,EInvalidOperation等而停止运行.) - 新对象接受const参数,因此可以接受任何对象.但是随后实际上使用某些 hacky-wacky代码更改了提供的对象指针.
- 您现在可以像这样调用新的
FreeAndNil
:FreeAndNil(TObject.Create)
,它将编译甚至可以正常运行.我喜欢旧的FreeAndNil
,当我出错时会警告我并提供例如属性而不是字段.如果您向此FreeAndNil
实现提供对象类型属性,则不确定会发生什么.没有尝试.
- The old one doesn't do any type checking, so calling
FreeAndNil
on pointers, records and interfaces compiles just fine, but produces interesting but usually unwanted effects during runtime. (Goes completely berserk or if you are lucky it halts with EAccessViolation, EInvalidOperation etc.) - The new one accepts a const parameter, and therefore any object. But then the provided object pointer is actually changed using some hacky-wacky code.
- You can now call the new
FreeAndNil
like this:FreeAndNil(TObject.Create)
and it will compile and even run just fine. I liked the oldFreeAndNil
that warned me when I went wrong and provided e.g. a property instead of a field. Unsure what happens if you provide a object type property to thisFreeAndNil
implementation. Didn't try.
如果我们将签名更改为 FreeAndNil(var SomeObject:TObject)
,那么它将不允许我们传递除 TObject
类型之外的任何其他变量类型.这也很有道理,就好像它不是 FreeAndNil
一样,可以很容易地在例程中更改类型为 TComponent
的变量,将var变量转换为一个完全相同的对象不同类型,例如 TCollection
.当然, FreeAndNil
不会做任何事情,因为它总是将var参数更改为nil.
If we would change the signature into FreeAndNil(var SomeObject:TObject)
then it will not allow us to pass any other variable type then exactly the TObject
type. Which also makes sense, as if it weren't FreeAndNil
, one could easily change a variable provided as type TComponent
in the routine change the var variable into an object of a completely different type, e.g. TCollection
. Of course FreeAndNil
will do no such thing, as it always changes the var parameter to nil.
所以这使 FreeAndNil
成为特例.也许甚至足以说服delphi添加编译器魔术 FreeAndNil
实现?投票给任何人吗?
So this makes FreeAndNil
a special case.Maybe even special enough to convince delphi to add a compiler magic FreeAndNil
implementation? Votes anyone?
可能的解决方法
我想出了下面的代码作为替代(这里是一个辅助方法,但也可能是 TObject
实现的一部分),它结合了两个世界. Assert
将有助于在运行时查找无效的呼叫.
I came up with the code below as an alternative (here as a helper method, but could as well be part of TObject
implementation) which kind-a combines both worlds. The Assert
will help finding invalid calls during runtime.
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
.我不是很喜欢的是:
I have actually tested this routine, and it even is slightly faster than the 10.4 FreeAndNil
implementation. I guess because I do the assignment check first and call Destroy
directly.What I do not like so much is that:
- 类型检查在运行时进行,然后仅在断言为ON时进行.
- 感觉就像必须两次传递相同的变量.不一定是正确的/必需的.它必须是同一对象,参数必须是变量.
另一项调查
但是如果没有参数就可以打电话
But wouldn't it be great if one could call without the parameter
var MyObj:=TSomeObject.Create;
...
MyObj.FreeAndNil;
因此,我弄乱了 self
指针,并设法使用10.4在其 FreeAndNil 中使用的相同Hacky-Wacky代码将其设置为
nil
.代码>.嗯,在内部方法中起作用的 self
指向了 nil
.但是像这样调用 FreeAndNil
之后,MyObj变量不是nil,而是一个陈旧的指针.(这是我的预期.)此外, MyObj
可以是属性,也可以是例程,构造函数等的结果.
So I messed around with the self
pointer and managed to set it to nil
using the same Hacky-Wacky code that 10.4 utilizes in their FreeAndNil
. Well... that worked inside the method, self
pointed to nil
. But after calling FreeAndNil
like this, the MyObj variable wasn't nil, but a stale pointer. (This was what I expected.) Moreover, MyObj
could be a property or (the result of) a routine, constructor etc.
也不在这里...
最后是一个问题:
您能想到一种更干净/更好的解决方案或技巧吗?
Can you think of a cleaner/better solution or trick that would:
- FreeAndNil(var aObject:TObject)具有不太严格的类型检查编译时间(也许是Compiler指令?),因此它允许编译和调用 any 对象类型的变量.
- 在传递不是 某些对象类型的变量/字段的内容时,说明编译时间
- 帮助描述什么是 RSP-29716 中的最佳解决方案/要求.li>
- FreeAndNil(var aObject:TObject) with not-so-strict type checking compile time (maybe a Compiler directive?) so it allows compiling and calling for variables of any object type.
- Complains compile time when something is passed that is not a variable/field of some object type
- Help describing what is the best solution/requirement in RSP-29716
推荐答案
因为我们无法创建全局 procedure FreeAndNil< T:class>(var aObject:T)
,所以我建议以下代码TObject类的方法.(rtl更改由embarcadero进行,但不需要更改编译器)
Because we cannot create a global procedure FreeAndNil<T:class>(var aObject:T)
I would suggest the code below as a method to the TObject class. (rtl change to be made by embarcadero, but does not need a compiler change)
class procedure TObject.InternalFreeAndNil(var Object:TObject); static; // strict private class method
begin
if Assigned(Object) then
begin
var tmp:=Object;
Object:=nil;
tmp.Destroy;
end;
end;
class procedure TObject.FreeAndNil<T:class>(var Object:T); inline; // public generic class method
begin
InternalFreeAndNil(TObject(Object));
end;
,并从 sysutils
单元中删除当前的(10.4和更早版本的) FreeAndNil
,以避免产生歧义.
and to have the current (10.4 and earlier) FreeAndNil
removed from the sysutils
unit to avoid ambiguity.
当从任何其他方法中调用新的通用 FreeAndNil
方法时,只需调用以下方法即可:
When the new generic FreeAndNil
method is called from within any other method, one can simply call:
FreeAndNil(SomeObjectVariable)
和10.3+类型推断可避免编写:
and 10.3+ type inference avoids having to write:
FreeAndNil<TMyClassSpec>(SomeObjectVariable)
这很不错,因为您的大多数代码都可以很好地编译而无需更改.
which is nice because most of your code will compile nicely without a change.
在其他一些地方,例如全局例程和 initialization/finalization
部分,则必须调用:
In some other spots, eg global routines and initialization / finalization
sections one would have to call:
TObject.FreeAndNil(SomeObjectVariable)
我可以接受,并且比使用 FreeAndNil(const [ref] aObject:TObject)
或无类型的 FreeAndNil(var aObject)
Which to me would be acceptable, and a lot better than the current and historical half-way solutions with a FreeAndNil(const [ref] aObject:TObject)
or an untyped FreeAndNil(var aObject)
由于例程非常简单,而且性能似乎是一个问题,因此可以争辩说有一个汇编程序实现.虽然我不确定通用方法(最好是 inline )是否允许/可能这样做.
And since the routine is so utterly simple and performance appears to be an issue, one could argue to have an assembler implementation for it. Though I am not sure if this is allowed/possible for generic, (and preferably inline) methods.
FTM:还可以保留 FreeAndNil(var aObject:TObject)
并告诉人们进行如下所示的类型转换,这也避免了编译器抱怨var类型.但是在这种情况下,可能必须调整许多源代码.另一方面,它节省了代码膨胀,仍然避免了无效使用函数结果,属性或无效类型(例如记录和指针)作为 FreeAndNil
的参数,并且完全易于更改/实现.
FTM: One could also just keep FreeAndNil(var aObject:TObject)
and tell people to do a typecast like below, which also avoids the compiler complaining about the var type. But in this case, probably a lot of source code has to be adjusted. On the other hand it saves on code bloat, still avoids Invalid use of function results, properties or invalid types like records and pointers as parameter to FreeAndNil
, and is utterly simple to change/implement.
...
var Obj:=TSomeObject.Create;
try
DoSOmethingUseFulWithObj(Obj);
finally
FreeAndNil(TObject(Obj)); // typecast avoids compiler complaining. Compiler wont allow invalid typecasts
end;
...
这篇关于Delphi FreeAndNil:寻找替代实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!