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

时间:2015-07-28 18:24:52

标签: delphi

Embarcadero article讨论XE7 IDE的内存问题包含以下内容:

  

注意“泛型增长”

     

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

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

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

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

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

2 个答案:

答案 0 :(得分:8)

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

在多个单元中声明和使用TList<Integer>只会在exe文件中包含{strong>单一副本 TList<Integer>。此外,声明TIntegerList = TList<Integer>将导致相同的结果。

人们所指的泛型膨胀涉及对您使用的每种特定类型都有完整的TList<T>副本,即使基础生成的代码是相同的。

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

这将我们转移到第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;

答案 1 :(得分:7)

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

这意味着,如果您有Unit1.pasUnit2.pas并且两者都使用TList<Integer> Unit1.dcuUnit2.dcu都拥有TList<Integer>的二进制代码汇编成。

如果您在TIntegerList = TList<Integer>中声明Unit3并在Unit1Unit2中使用该TList<Integer>,您可能会认为这只会在{{1}中包含已编译的Unit3.dcu而不是其他两个。但不幸的是情况并非如此。