我需要一种方法来编写一个通用过程来处理对象类型或其任何后代.
我的第一次尝试是宣布
procedure TotalDestroy(var obj:TMyObject);
Run Code Online (Sandbox Code Playgroud)
但是当它与后代对象一起使用时
type TMyNewerObject = class(TMyObject);
var someNewerObject: TMyNewerObject;
TotalDestroy(someNewerObject);
Run Code Online (Sandbox Code Playgroud)
我得到了臭名昭着的错误"正式和实际参数的类型必须相同"
因此,在寻找解决方案时,我查看了Delphi系统FreeAndNil过程的源代码.我发现这个令人敬畏的声明,以及这个令人惊讶的评论
{ FreeAndNil frees the given TObject instance and
sets the variable reference to nil.
Be careful to only pass TObjects to this routine. }
procedure FreeAndNil(var Obj);
Run Code Online (Sandbox Code Playgroud)
它避免了类型检查错误,但它没有使用安全网.
我的问题是......是否有任何安全的方法来检查无类型var参数的类型?
或者换句话说,你能改进这个Delphi源代码,以便不需要警告吗?
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
Run Code Online (Sandbox Code Playgroud)
让我们来看看你想做什么.
你想调用一个接受X的方法,传入一个Y类型的对象,其中Y是X的后代.该障碍,参数是一个"var"参数.
让我们分析如果可能的话你可以做些什么.
type
TBase = class
end;
TDescendant = class(TBase)
end;
procedure Fiddle(var x: TBase);
begin
x := TDescendant.Create;
end;
type
TOtherDescendant = class(TBase)
end;
var a: TOtherDescendant;
a := TOtherDescendant.Create;
Fiddle(a);
Run Code Online (Sandbox Code Playgroud)
呃哦,现在a不再包含一个实例TOtherDescendant,它包含一个实例TDescendant.这可能会让电话跟随的代码感到惊讶.
您不仅要考虑您打算使用您提出的语法做什么,还要考虑您可以使用语法做些什么.
你应该阅读Eric Lipperts关于.NET中类似问题的优秀博客文章,在这里找到:为什么ref和out参数不允许类型变化?.
我以前写过这个,用一个与Lasse非常类似的例子:
除非您正在编写赋值语句来更改输入参数本身的值,而不仅仅是其中一个属性,否则您不应该首先通过引用传递参数.
如果您正在编写赋值语句来更改参数的值,那么编译器消息确实是真的,您应该注意它.
需要绕过错误的一个原因是当你编写类似的函数时TApplication.CreateForm.它的工作是更改输入参数的值,并且新值的类型会有所不同,并且无法在编译时确定.如果您正在编写这样的函数,那么Delphi的唯一选择是使用无类型的var参数,然后调用者和接收者都要承担额外的负担以确保一切正常.调用者需要确保它传递一个变量,该变量能够保存函数将放入其中的任何类型的值,并且该函数需要确保它存储与调用者请求的类型兼容的类型的值.
在这种情况下CreateForm,调用者传入类引用文字和该类类型的变量.该函数实例化类并将引用存储在变量中.
我不认为很高的任意CreateForm或FreeAndNil,主要是因为它们的非类型化的参数牺牲类型安全,以换取相对较少的额外便利的方式.您还没有显示TotalDestroy函数的实现,但我怀疑它的var参数最终会提供与其他两个函数相同的低效用.请参阅我的文章:
| 归档时间: |
|
| 查看次数: |
1797 次 |
| 最近记录: |