Generics
22.1 Generic Types
type
TStack<T> = class
private
FItems: TList<T>;
public
constructor Create;
destructor Destroy; override;
procedure Push(const Item: T);
function Pop: T;
function Peek: T;
function IsEmpty: Boolean;
end;
constructor TStack<T>.Create;
begin
inherited;
FItems := TList<T>.Create;
end;
destructor TStack<T>.Destroy;
begin
FItems.Free;
inherited;
end;
procedure TStack<T>.Push(const Item: T);
begin
FItems.Add(Item);
end;
function TStack<T>.Pop: T;
begin
if FItems.Count = 0 then raise EInvalidOpException.Create('Stack empty');
Result := FItems.Last;
FItems.Delete(FItems.Count - 1);
end;
function TStack<T>.Peek: T; begin Result := FItems.Last; end;
function TStack<T>.IsEmpty: Boolean; begin Result := FItems.Count = 0; end;
var S := TStack<Integer>.Create;
try
S.Push(1); S.Push(2);
Writeln(S.Pop);
finally
S.Free;
end;
type
TPair<TKey, TValue> = record
Key: TKey;
Value: TValue;
constructor Create(const AKey: TKey; const AValue: TValue);
end;22.2 Generic Methods
Delphi has no free-standing generic procedures. Generic methods live on classes/records (often class/static methods).
type
TUtils = class
class function Max<T>(const A, B: T): T; static;
class procedure Swap<T>(var A, B: T); static;
end;
class function TUtils.Max<T>(const A, B: T): T;
begin
if TComparer<T>.Default.Compare(A, B) > 0 then Result := A else Result := B;
end;
class procedure TUtils.Swap<T>(var A, B: T);
begin
var Tmp := A; A := B; B := Tmp;
end;
Writeln(TUtils.Max<Integer>(3, 7), ' ', TUtils.Max<string>('a', 'b'));22.3 Constraints
Must be a class type Must be a value type (non-nullable) T: constructor
Has a parameterless Create (so T.Create is allowed) T: TBaseClass Descends from the class T: IInterface Implements the interface T: class, constructor
type
TRepository<T: TEntity, constructor> = class
function CreateNew: T;
end;
function TRepository<T>.CreateNew: T;
begin
Result := T.Create;
end;22.4 Generics Notes
Compile-time type checking. Code is generated per instantiation (which can grow executable size).
Use Default(T) for default values, TypeInfo(T) / GetTypeKind(T) (compile-time intrinsic) for type-specific branches, and IsManagedType(T).
There's no variance, and operators on T aren't available. Use TComparer<T>, TEqualityComparer<T>, or supplied function parameters.