Class constructors and Generics

The new great addition to the Delphi language in Delphi 2010 is the possibility to specify a class constructor and a class destructor to your class/record. I will not describe this feature in this post since you can see the online documentation for it on the Embarcadero Doc Wiki. The part I am interested in is the combination of class constructors and generics. As you might already know generic types aren’t really “run-time entities” but rather “compile-time” ones. This makes the initialization of “static” members of the type a bit more complicated. See for example this record:

type
  TMyType<T> = record
  private class var
    FSomeList: TList<T>;

  public
    ...
    ...
  end;

In this case the static FSomeList variable needs to be initialized to make sense. Normally, in non-generic classes, you would use the “initialization” and “finalization” sections to create and then destroy that variable. In generic classes this is impossible though. You have to get some workarounds (like initializing the list lazily), but you still cannot destroy the list on finalization.

Well, not anymore! Using class constructors and destructors allows you to easily initialize any static member of a type:

type
  TMyType<T> = record
  private class var
    FSomeList: TList<T>;
    
    class constructor Create;
    class destructor Destroy;
  public
    ...
    ...
  end;

class constructor TMyType<T>.Create;
begin
  // Executed on application initialization.
  FSomeList := TList<T>.Create();
end;

class destructor TMyType<T>.Destroy;
begin
  // Freed on application termination.
  FSomeList.Free;
end;

There is a catch tough. Since generic types are compile-time entities, any unit that uses a specialized generic type basically defines that type in itself. For example if you use TList<String> in four of your units, all four units will internally declare the TList<String> class. This results in the TList<T>‘s class constructor to be executed four times – once per unit. This is expected behavior since each type specialization has different static members. For example:

type
  TDistinctType<T> = record
  private class var
    FMarker: Integer;

    class constructor Create;
  end;

If TDistinctType<String> is used in multiple units, each unit’s version has it own FMarker, which means it needs to be initialized for each unit.

The conclusion — be aware that class constructors and destructors for generic types may execute multiple times.