Generic types
A generic type is a class, record, interface, array, procedural type or method (and, in Free Pascal, stand-alone routine) with one or more type parameters in angle brackets. Delphi 2009 and Free Pascal 2.2 and later have them.
type
TStack<T> = class
private
FItems: array of T;
public
procedure Push(const Item: T);
function Pop: T;
end;
procedure TStack<T>.Push(const Item: T);
begin
SetLength(FItems, Length(FItems) + 1);
FItems[High(FItems)] := Item;
end;
var
Numbers: TStack<Integer>;
begin
Numbers := TStack<Integer>.Create;
Numbers.Push(42);
end;
The two syntaxes
| Dialect | Declare | Use |
|---|---|---|
Delphi, and Free Pascal in delphi mode | TList<T> = class … end; | TList<Integer> written directly |
Free Pascal in objfpc mode | generic TList<T> = class … end; | specialize TList<Integer>. The words generic and specialize are required in this mode. |
{$mode objfpc}
type
generic TBox<T> = class
Value: T;
end;
TIntBox = specialize TBox<Integer>; { a name for one specialisation }
generic function Max<T>(A, B: T): T;
A generic routine is a procedure or function with type parameters
(function Max<T>(A, B: T): T;). In Delphi it must be a method of a class
or record; Free Pascal also allows stand-alone generic routines. Two angle brackets
closing together (TList<TList<Integer>>) are read as two
brackets, not as a shift.
Constraints
type
TFactory<T: class, constructor> = class
function Make: T; { T.Create is allowed }
end;
TCompare<T: IComparable> = class end; { T must implement an interface }
TNumberBox<T: record> = class end; { T must be a value type }
TDescendant<T: TControl> = class end; { T must be TControl or a descendant }
A constraint after a colon limits the types that may be used. The forms are a class
type or interface type (the argument must be it or implement it), class (any
class), record (a value type), constructor (a class with a
parameterless constructor), and a list of these, separated by commas. Without a
constraint the code may use only what every type has.
What the compiler does
- A generic is a template, and the compiler produces code for each distinct set of type arguments (a specialisation), as C++ templates do. There is no code for the generic itself.
Default(T)is the zero value of T.SizeOf(T),TypeInfo(T)andGetTypeKind(T)work inside a generic, andTypeInfoand RTTI give the name of the specialisation.- Inside a generic, operators cannot be applied to T unless its constraint provides them, so arithmetic on a type parameter is done through helpers such as
TComparer<T>orIComparer<T>. - A generic may inherit from another generic:
TBase<T> = class … end; TDerived<T> = class(TBase<T>), and from a specialisation. - Methods of a generic class are defined outside with the type and its parameters written out:
procedure TStack<T>.Push(…). The names of the parameters must match.
Generic collections
The generic library units are Generics.Collections and
Generics.Defaults in Delphi. Free Pascal ships units of the same names in its
rtl-generics package, and the older gvector, gmap and
gutil units in fcl-stl. The main types:
| Type | What it is |
|---|---|
TList<T> | a growable array of T, with Add, Insert, Delete, IndexOf, Sort and for … in |
TObjectList<T> | the same, and frees the objects it holds |
TDictionary<K,V> | a hash table from keys to values; TObjectDictionary frees them |
TQueue<T>, TStack<T> | first-in first-out and last-in first-out containers |
TPair<K,V> | a key and a value |
IComparer<T>, IEqualityComparer<T>, TArray<T> | comparison interfaces, and the generic array type with TArray.Sort and TArray.BinarySearch |
Anonymous methods and closures
type
TIntFunc = reference to function(X: Integer): Integer;
function Adder(N: Integer): TIntFunc;
begin
Result := function(X: Integer): Integer
begin
Result := X + N; { N is captured }
end;
end;
TThread.Queue(nil, procedure begin Label1.Caption := 'done'; end);
List.Sort(TComparer<Integer>.Construct(
function(const A, B: Integer): Integer begin Result := A - B; end));
An anonymous method is a routine without a name written as an expression, which may use
the variables of the routine around it (it captures them, and they live as long
as the method). It is assigned to a variable of a reference to procedure or
function type, which also accepts ordinary routines and methods. Delphi 2009 introduced
them. Free Pascal’s development version has anonymous functions and function references
behind the anonymousfunctions and functionreferences mode switches
(the Delphi mode turns both on).
Type inference and other late additions
- Delphi 10.3 and later infer the type of an inline variable from its initial value:
var X := TList<Integer>.Create;. - Free Pascal’s development version has the mode switches
typeinquiryandarrayoperators. Delphi XE7 and later add+,InsertandConcatfor dynamic arrays.