Delphi supports generic interfaces; for example we can declare a generic interface

type IChecker<T> = interface function Check(const Instance: T): Boolean; end;

and use this generic interface as follows:

unit UseDemo; interface uses GenChecks; type TDemo<T> = class private FChecker: IChecker<T>; public constructor Create(AChecker: IChecker<T>); procedure Check(AValue: T); end; implementation { TDemo<T> } procedure TDemo<T>.Check(AValue: T); begin if FChecker.Check(AValue) then Writeln('Passed') else Writeln('Stopped') end; constructor TDemo<T>.Create(AChecker: IChecker<T>); begin FChecker:= AChecker; end; end.

To implement the above generic interface IChecker we need a generic class; the straightforward solution is

type TChecker<T> = class(TInterfacedObject, IChecker<T>) function Check(const Instance: T): Boolean; end;

If the IChecker interface can be implemented like that, we need nothing else. The problem with the above implementation is that we are limited to the generic constraints on the type T and can’t use properties of specific types like Integer or string that will finally be substituted for the type T.

A more elastic solution is to introduce an abstract base type and derive the specific implementations from it. Here is a full code example:

program GenericEx1; {$APPTYPE CONSOLE} uses SysUtils, GenChecks in 'GenChecks.pas', UseDemo in 'UseDemo.pas'; procedure TestInt; var Demo: TDemo<Integer>; begin Demo:= TDemo<Integer>.Create(TIntChecker.Create(42)); Demo.Check(0); Demo.Check(42); end; procedure TestStr; var Demo: TDemo<string>; begin Demo:= TDemo<string>.Create(TStrChecker.Create('trololo')); Demo.Check('ololo'); Demo.Check('olololo'); end; begin TestInt; TestStr; ReadLn; end.

unit GenChecks; interface type IChecker<T> = interface function Check(const Instance: T): Boolean; end; type TCustomChecker<T> = class(TInterfacedObject, IChecker<T>) protected FCheckValue: T; function Check(const Instance: T): Boolean; virtual; abstract; public constructor Create(ACheckValue: T); end; TIntChecker = class(TCustomChecker<Integer>) protected function Check(const Instance: Integer): Boolean; override; end; TStrChecker = class(TCustomChecker<string>) protected function Check(const Instance: string): Boolean; override; end; implementation { TCustomChecker<T> } constructor TCustomChecker<T>.Create(ACheckValue: T); begin FCheckValue:= ACheckValue; end; { TIntChecker } function TIntChecker.Check(const Instance: Integer): Boolean; begin Result:= Instance = FCheckValue; end; { TStrChecker } function TStrChecker.Check(const Instance: string): Boolean; begin Result:= Length(Instance) = Length(FCheckValue); end; end.

In the above example each interface reference ICheck references its own class instance; this is necessary because every instance contains a parameter (FCheckValue) set in the constructor. If an implementation does not require such a parameter creating new instances for every interface reference will be an overhead. A better solution is to use a singleton instance.

Here is a full code example for the integer type:

program GenericEx2; {$APPTYPE CONSOLE} uses SysUtils, GenChecks in 'GenChecks.pas', UseDemo in 'UseDemo.pas'; procedure TestInt; var Demo: TDemo<Integer>; begin Demo:= TDemo<Integer>.Create(TIntChecker.Ordinal); Demo.Check(0); Demo.Check(42); end; begin TestInt; ReadLn; end.

unit GenChecks; interface uses Generics.Defaults; type IChecker<T> = interface function Check(const Instance: T): Boolean; end; TCustomChecker<T> = class(TSingletonImplementation, IChecker<T>) protected function Check(const Instance: T): Boolean; virtual; abstract; end; TIntChecker = class(TCustomChecker<Integer>) private class var FOrdinal: TCustomChecker<Integer>; public class function Ordinal: TIntChecker; end; implementation type TOrdinalIntChecker = class(TIntChecker) public function Check(const Instance: Integer): Boolean; override; end; { TOrdinalIntChecker } function TOrdinalIntChecker.Check(const Instance: Integer): Boolean; begin Result:= Instance = 42; end; { TIntChecker } class function TIntChecker.Ordinal: TIntChecker; begin if FOrdinal = nil then FOrdinal := TOrdinalIntChecker.Create; Result := TIntChecker(FOrdinal); end; end.