有没有办法在Delphi中通过类信息参数动态类型转换?

kdt*_*top 0 delphi polymorphism typecast-operator typecasting-operator

当使用作为传递参数的类时,我在理解类型转换方面遇到了一些困难。我尝试寻找这个但找不到其他答案。

我正在使用一些旧的 Delphi 代码,使用 Delphi 2006,它不支持泛型(在 Delphi 2009 中引入)。

该代码使用 TList 来存储指向特定类型的实例化类的指针。当清除列表时,他们使用这个:

  procedure ClearList(AList: TList);  
  var i: Integer;
  begin
    for i := 0 to AList.Count - 1 do
      TObject(AList[i]).Free;
    AList.Clear;
  end; 
Run Code Online (Sandbox Code Playgroud)

它的名字是这样的:

  ClearList(FExtraVisitTypes);
  ClearList(FDiagnoses);
  ClearList(FProcedures);
  ClearList(FImmunizations);
  ClearList(FSkinTests);
  ClearList(FPatientEds);
  ClearList(FHealthFactors);
  ClearList(FExams);
Run Code Online (Sandbox Code Playgroud)

我对此的理解可能有所偏差,但我担心如果将指向的对象作为 TObject 释放,则不会调用后代对象的析构函数,从而可能导致内存泄漏。(我的多态功夫有点生疏,可能会造成我的困惑。)

所以我尝试将清除功能更改如下:

 procedure ClearList(AList: TList; ItemClass: TPCEItemClass);  //mod to add ItemClass
  var i: Integer;
  begin
    for i := 0 to AList.Count - 1 do begin
      (AList[i] as ItemClass).Free;
    end;
    AList.Clear;
  end;
Run Code Online (Sandbox Code Playgroud)

TPCEItemClass 定义如下:

  TPCEItemClass = class of TPCEItem;
Run Code Online (Sandbox Code Playgroud)

然后我像这样更改了清晰的调用:

  ClearList(FExtraVisitTypes, TPCEProc);
  ClearList(FDiagnoses, TPCEDiag);
  ClearList(FProcedures, TPCEProc);
  ClearList(FImmunizations, TPCEImm);
  ClearList(FSkinTests, TPCESkin);
  ClearList(FPatientEds, TPCEPat);
  ClearList(FHealthFactors, TPCEHealth);
  ClearList(FExams, TPCEExams);
Run Code Online (Sandbox Code Playgroud)

但编译器不允许这样做并给出以下错误:

[Pascal Error] uPCE.pas(1730): E2015 Operator not applicable to this operand type
Run Code Online (Sandbox Code Playgroud)

对于这个错误行:

  (AList[i] as ItemClass).Free;
Run Code Online (Sandbox Code Playgroud)

问题:

  1. 原始的编码方式(通过简单地调用伟大-伟大-伟大(等)祖先 Free 方法来释放项目)是否最终会影响后代的析构函数方法?当我写这篇文章时,我现在认为它确实如此。但我不知道为什么。所以任何能帮助我记住这一点的答案都会很棒。

  2. 为什么我尝试通过类类型的参数进行类型转换的方法不起作用?难道这只是不允许的吗?或者我的语法错误?还有其他方法可以做到这一点吗?

  3. 我这一切都错了吗?有没有更好的办法?

谢谢

Rem*_*eau 7

我担心如果将指向的对象作为 TObject 释放,则不会调用后代对象的析构函数,这可能会导致内存泄漏。

对于正确实现的类来说,情况并非如此。

所有类都派生于TObject. TObject.Free()调用TObject.Destroy()析构函数,即virtual. 任何需要破坏逻辑的后代都必须使用override该析构函数(如果没有,则它有一个需要修复的缺陷)。

因此,在正确编写的代码中,原始代码将完美地工作,如图所示。调用Free()任何有效正确实现的对象都将调用其最派生的析构函数。

话虽这么说,多年来人们override在类需要析构函数时忘记了很多情况,从而导致了您所担心的内存泄漏。因此,请确保您关注班级正在做的事情,这样您就会没事的。

所以我尝试如下更改清除函数...但是编译器不允许这样做并给出此错误

正确,因为您无法使用变量对对象执行元类类型的类型转换,就像您尝试做的那样。类型转换要求在编译时指定目标类型,但元类变量直到运行时才会分配。

原始的编码方式(通过简单地调用伟大-伟大-伟大(等)祖先 Free 方法来释放项目)是否最终会影响后代的析构函数方法?

是的,原始代码在 99% 的情况下都可以正常工作。大多数 Delphi 程序员都擅长override在适当的时候使用析构函数。但是,另外 1% 仅在您处理未正确实现的类时出现,在这种情况下,修复它们是它们的作者的责任,而不是您修复调用它们的代码的责任Free()

当我写这篇文章时,我现在认为它确实如此。但我不知道为什么。

虚拟析构函数的多态分派,就像调用任何其他virtual方法时一样。

为什么我尝试通过类类型的参数进行类型转换的方法不起作用?难道这只是不允许的吗?

正确的。这是非法的。

还有其他方法可以做到这一点吗?

不(嗯,是的,但它涉及在运行时手动遍历对象的类结构,但这需要深入了解编译器如何在内存中布置对象,所以我不打算在这里深入讨论)。