如何安全地绕过Delphi错误:"正式和实际参数的类型必须相同"

PA.*_*PA. 6 delphi

我需要一种方法来编写一个通用过程来处理对象类型或其任何后代.

我的第一次尝试是宣布

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)

ang*_*son 9

让我们来看看你想做什么.

你想调用一个接受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参数不允许类型变化?.


Rob*_*edy 7

我以前写过这个,用一个与Lasse非常类似的例子:

除非您正在编写赋值语句来更改输入参数本身的值,而不仅仅是其中一个属性,否则您不应该首先通过引用传递参数.

如果您正在编写赋值语句来更改参数的值,那么编译器消息确实是真的,您应该注意它.

需要绕过错误的一个原因是当你编写类似的函数时TApplication.CreateForm.它的工作是更改输入参数的值,并且新值的类型会有所不同,并且无法在编译时确定.如果您正在编写这样的函数,那么Delphi的唯一选择是使用无类型的var参数,然后调用者和接收者都要承担额外的负担以确保一切正常.调用者需要确保它传递一个变量,该变量能够保存函数将放入其中的任何类型的值,并且该函数需要确保它存储与调用者请求的类型兼容的类型的值.

在这种情况下CreateForm,调用者传入类引用文字和该类类型的变量.该函数实例化类并将引用存储在变量中.

我不认为很高的任意CreateFormFreeAndNil,主要是因为它们的非类型化的参数牺牲类型安全,以换取相对较少的额外便利的方式.您还没有显示TotalDestroy函数的实现,但我怀疑它的var参数最终会提供与其他两个函数相同的低效用.请参阅我的文章:

  • 如果`TMyObject`需要特殊清理,请考虑将其放入析构函数中.当普通的"免费"适用于其他一切时,不要强迫你的班级消费者学习一种新的方法来摧毁它. (3认同)