Wednesday, March 25, 2009

Delphi2009 Generics and Type Constraints

The .NET type system is rooted at System.Object. The Delphi/Win32 type system isn’t rooted (built-in simple types, records, and classes don’t have a common ancestor), and primitive types can’t/don’t implement interfaces, so someone in the newsgroups asked how to deal with generics which must perform operations on primitive types, without the use of IComparable, et. al. Here’s one way. I’m not sure if it’s the best way, but it works.



unit Unit5;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TAdder<T> = class
public
function AddEm(A, B: T): T; virtual; abstract;
end;

TIntAdder = class(TAdder<integer>)
public
function AddEm(A, B: integer): integer; override;
end;

TStringAdder = class(TAdder<string>)
public
function AddEm(A, B: string): string; override;
end;

TMath<T; A: TAdder<T>, constructor> = class
public
function AddTwoDigits(Left, Right: T): T;
end;

TForm5 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;

var
Form5: TForm5;

implementation

{$R *.dfm}

procedure TForm5.Button1Click(Sender: TObject);
var
AddInt: TMath<integer, TIntAdder>;
AddString: TMath<string, TStringAdder>;
begin
AddInt := TMath<integer, TIntAdder>.Create;
try
ShowMessage(IntToStr(AddInt.AddTwoDigits(2, 2)));
finally
AddInt.Free;
end;
AddString := TMath<string, TStringAdder>.Create;
try
ShowMessage(AddString.AddTwoDigits('2', '2'));
finally
AddString.Free;
end;
end;

{ TIntAdder }

function TIntAdder.AddEm(A, B: integer): integer;
begin
Result := A + B;
end;

{ TStringAdder }

function TStringAdder.AddEm(A, B: string): string;
begin
Result := IntToStr(StrToInt(A) + StrToInt(B));
end;

{ TMath<T, A> }

function TMath<T, A>.AddTwoDigits(Left, Right: T): T;
var
Add: A;
begin
Add := A.Create;
try
Result := Add.AddEm(Left, Right);
finally
Add.Free;
end;
end;

end.

No comments: