如何防止通用类重复生成的代码?

时间:2016-05-21 15:10:15

标签: delphi generics

Delphi有一种讨厌泛型类代码的恶习。即使该代码实际上是相同的,因为泛型类型是相似的 我想防止重复存储不同的类 在我的通用容器中,我只使用Free来清理(如果需要)。

假设我有一个像这样的通用容器:

unit Unit1;

interface

uses Generics.Collections;

type
  TMyContainer<T> = class(TObject)
  strict private
    FData: TList<T>;
  public
    constructor Create; virtual;
  end;

我知道T通常会成为一个对象。因为所有对象都是TObject,所以我不希望我的容器为不同类型的对象创建重复的通用代码。

以下技巧是否可以防止重复?

A-用类函数替换构造函数:

unit Unit2;

uses Unit1;

type
  TMyContainer<T> = class(Unit1.TMyContainer<T>)
  public
    class function Create: TMyContainer<T>; static;
  end;

B:像这样实现类函数Create

class function TMyContainer<T>.Create: TMyContainer<T>;
var
  X: TObject;
begin
  if GetTypeKind(T) = tkClass then begin
    X:= Unit1.TMyContainer<TObject>.Create;
  end else begin
    X:= Unit1.TMyContainer<T>.Create;
  end;
  TObject(Result):= X;
end;

这个技巧是否会阻止编译器为不同类型的对象生成重复代码,或者这会因为我使用不正确的假设而失败? 请注意,我不想使用非通用商店来处理我的数据。

完整的示例代码

unit Unit49;

interface

uses Generics.Collections;

type
  TMyContainer<T> = class(TObject)
  strict private
    FData: TList<T>;
  public
    constructor Create; virtual;
  end;

implementation

constructor TMyContainer<T>.Create;
begin
  inherited Create;
  FData:= TList<T>.Create;
end;

end.

示例程序

program Project85;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Unit49 in 'Unit49.pas';

type
  TMyContainer<T> = class(Unit49.TMyContainer<T>)
  public
    class function Create: TMyContainer<T>; static;
  end;

{ TMyContainer<T> }

class function TMyContainer<T>.Create: TMyContainer<T>;
var
  Y: T;
  X: TObject;
begin
  if GetTypeKind(T) = tkClass then begin
    X:= Unit49.TMyContainer<TObject>.Create;
  end else begin
    X:= Unit49.TMyContainer<T>.Create;
  end;
  TObject(Result):= X;
end;

var
  A: TMyContainer<TObject>;
  B: TMyContainer<TLanguages>;

begin
  A:= TMyContainer<TObject>.Create;
  B:= TMyContainer<TLanguages>.Create;
  readln;
end.

1 个答案:

答案 0 :(得分:4)

  

这个技巧是否会阻止编译器生成重复   不同类型对象的代码,或者因为我而失败   使用不正确的假设?

不,它不会起作用。

基本上,编译器遵循您的T到整个类层次结构,并用特定类型替换它。

首先,您将为TList<T>TObject生成单独的TLanguages代码,因为您的容器已声明为FData: TList<T>,然后 你的技巧集合也继承自通用T TMyContainer<T> = class(Unit49.TMyContainer<T>),你的类函数中的整个代码基本没用。

编译器会为Unit49.TMyContainer<TLanguages>类以及Unit49.TMyContainer<TObject>类生成重复的代码。

从您的示例中,很难说您想要避免哪些代码重复。如果容器类与您在示例中编写的一样简单,那么所有代码​​重复都将来自TList<T>类。如果你想避免那个,那就没有简单的方法了。

您的部分问题来自于T可以是任何事实。很难优化它。您可以获得的最优化是使用array of T来存储数据,然后委托操作函数,您可以使用TObject作为所有类的基础,并使用普通T作为其他类。

上面你能获得多少也取决于你使用哪个Delphi版本,因为在最近的版本中TList<T>已经使用类似的技术进行了一些优化。

但是,如果您可以为类和其他类型设置单独的容器,则可以使用TObject(甚至Windows上的非泛型TObjectList<TObject>实现TObjectList和后代容器的代码折叠用于存储所有特定类并使用类型转换实现瘦包装函数,以满足您需要的任何类型安全功能。当然,每个这样的函数都会为每种特定的类型生成一些代码,但由于它们只是类型转换包装器,因此如果你对每个类类型都使用完整的TList<T>那么代码就不会那么多。 / p>

  TMyObjectContainer<T> = class(TObject)
  strict private
    FData: TObjectList<TObject>; 
  public
    constructor Create; virtual;
    destructor Destroy; override;  
    function Data(index: integer): T;
  end;

constructor TMyObjectContainer<T>.Create; 
begin
  inherited;
  FData := TObjectList<TObject>.Create;
end; 

constructor TMyObjectContainer<T>.Create; 
begin
  FData.Free;
  inherited;
end; 

function TMyObjectContainer<T>.Data(index: integer): T;
begin
  Result := T(FData.Items[index]);
end;