多个单元中的泛型实例化是否会破坏可执行文件?

Dav*_*nan 13 delphi

这篇Embarcadero文章讨论了XE7 IDE的内存问题,其中包含以下内容:

注意"泛型增长"

另一种可能取决于您的应用程序代码并导致编译器和调试器使用的内存增加的方案与使用通用数据类型的方式有关.Object Pascal编译器的工作方式可以导致基于相同的通用定义生成许多不同类型,有时甚至是在不同模块中编译的完全相同的类型.虽然我们不一定建议删除泛型,但恰恰相反,有几个选项需要考虑:

  • 尽量避免定义核心泛型类型的单元的循环单元引用
  • 尽可能定义和使用相同的具体类型定义
  • 如果可能,重构泛型以在基类中共享代码,泛型类从中继承

我理解的最后一项.前两个我不太清楚.

这些问题是否只影响IDE性能,还是会影响编译代码的大小?

例如,考虑到第二项,如果我TList<Integer>在两个单独的单元中声明,我将在可执行文件的每个单元中获得两个单独的代码块吗?我当然希望不是!

Dal*_*kar 8

第2点.这是指在可能的情况下实例化相同的通用类型.例如,TList<Integer>在所有地方使用,而不是有两个泛型类型TList<Integer>TList<SmallInt>.

声明和使用TList<Integer>的几个单位将只包括单拷贝TList<Integer>EXE文件中.此外,声明TIntegerList = TList<Integer>将产生相同的结果.

普通臃肿的人指的是TList<T>即使底层生成的代码是相同的,也为您使用的每种特定类型提供完整的副本.

例如:TList<TObject>并且TList<TPersistent>将包括两个单独的副本,TList<T>即使生成的代码可以折叠成单个副本.

这将我们转移到Point 3.其中使用基类用于公共类代码,然后在其上使用泛型类来获得类型安全性,可以在编译期间和exe文件中节省内存.

例如,在非泛型基础之上构建泛型类TObjectList将仅包括每种特定类型的瘦通用层而不是完整TObjectList功能.报告为QC 108966

  TXObjectList<T: class, constructor> = class(TObjectList)
  protected
    function GetItem(index: Integer): T;
    procedure SetItem(index: Integer; const Value: T);
  public
    function Add: T;
    property Items[index: Integer]: T read GetItem write SetItem; default;
  end;

function TXObjectList<T>.GetItem(index: Integer): T;
begin
  Result := T( inherited GetItem(index));
end;

procedure TXObjectList<T>.SetItem(index: Integer; const Value: T);
begin
  inherited SetItem(index, Value);
end;

function TXObjectList<T>.Add: T;
begin
  Result := T.Create;
  inherited Add(Result);
end;
Run Code Online (Sandbox Code Playgroud)

  • 从TObjectList派生会阻止你编写接受`TList <T>`并提供`TObjectList <T>`的代码.Emba的XE8改变了System.Generics以不同的方式攻击了这个,但他们搞砸了,因为他们没有像样的单元测试.你知道,我确信.最终结果是无法读取的可怕RTL代码.他们应该咬紧牙关并正确地完成工作.在我看来,一半的解决方案比以前更糟糕.感谢您对第2点的评论. (4认同)

Ste*_*nke 7

他们在文章中讨论的代码膨胀(因为它是关于IDE中的内存不足问题)与生成的DCU和IDE中保存的所有元信息有关.每个DCU都包含所有使用的泛型.只有在编译二进制文件时,链接器才会删除重复项.

这意味着如果你有Unit1.pasUnit2.pas都使用TList<Integer>两者Unit1.dcu并且Unit2.dcu具有用于TList<Integer>编译的二进制代码.

如果声明TIntegerList = TList<Integer>Unit3,并使用在Unit1Unit2你可能会认为这将只包括编译TList<Integer>Unit3.dcu而不是在其他两个.但不幸的是情况并非如此.

  • 令我震惊的是,他们让RTL人员在XE8中销毁Sytem.Generics.Collections中的类而不是让编译人员解决真正的问题,这是一个可怕的耻辱.同样,Spring系列也以类似的方式进行了重新设计.所需要的只是链接器检查每个实例化方法,寻找要合并的重复项. (4认同)
  • @DalijaPrasnikar不幸的是,因为那只是一个别名.它仍然会将类型编译到每个单元中(刚刚测试过).只写`TIntegerList = class(TList <Integer>)`才能解决这个问题 - 而且这不是真正的解决方案. (2认同)