Delphi 2009 přináší několik novinek kompilátoru a RTL z nichž nejzásadnější je podpora Unicode, Generika (generics), anonymní metody a pak zbytek.
Minule byla první část změn v Delphi 2009, nyní přicházejí na řadu generika - generics (dobré číst postupně od Delphi 2007).
Generika (generics) v Delphi 2009
Nejdříve raději příklad, kde se bude demonstrovat syntaxe a snad i použití:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, TypInfo;
type
// deklarace generické třídy
TSampleClass <T> = class
private
fdata: T;
public
function ReadT: T;
procedure SetT (value: T);
end;
// implementace dvou metod generické třídy
function TSampleClass<T>.ReadT: T;
begin
exit (fData); // novinka - rozsireni Exit o parametr
// nebo treba Result := fData;
end;
procedure TSampleClass<T>.SetT(value: T);
begin
fData := value;
end;
var
t1: TSampleClass<Integer>; // data budou integer
t2: TSampleClass<string>; // data budou string
s: string;
i: integer;
begin
t1 := TSampleClass<Integer>.Create;
t1.SetT (10);
t2 := TSampleClass<string>.Create;
t2.SetT ('test');
// demonstrace typoveho přiřazení
s := t2.ReadT;
i := t1.ReadT;
writeln (s, i); // vypise test10
t2.Free;
t1.Free;
end.
Snad je tedy z demo příkladu patrné o co jde, ale raději: TSampleClass je námi definovaná generická třída a podle
deklarace pak obsahuje typ dat a umožňuje nám s nimi pracovat. Navíc nás kompilátor chrání aby nebylo provedeno
neplatné přiřazení atd.
Je evidentní, že tomu něco schází, jelikož nelze dělat některé oprace nad obecnými daty. Naštěstí autoři na to myslí a přidávají tři nové (resp. dvě z nich
jen rozšiřují stávající kód) možnosti:
- Default (T) - novinka: vrací nulovou, nebo prázdnou hodnotu pro aktuální typ (tj. třeba 0 pro integer, '' pro string nebo třeba nil pro objekt)
- SizeOf (T) - velikost v paměti, ale pro objekty a řetězce vrací velikost pointeru tedy 4
- TypeInfo (T) - vrací informace RTTI o generickém typu (třeba GetTypeName (TypeInfo (T)) by nám vratilo 'Integer' a v druhém případě 'string'
function TSampleClass<T>.Info: string;
begin
Result := GetTypeName (TypeInfo (T));
end;
writeln(t1.Info);
Takže to je deklarace obecného generického typu. Je patrné, že to stále není úplně ono. Proto Object Pascal umožňuje deklaraci rozšířit o limit (Class Constraints),
tj. jakého typu musí být nejméně parametr generického typu. Špatně se to vysvětluje tak raději příklad:
Změníme deklaraci z předchozího příkladu na
type
TSampleClass <T: class> = class
Nyní při kompilaci kompilátor zaprotestuje na t1: TSampleClass<Integer> jelikož Integer není třída (samozřejmě i na druhé deklaraci).
Výhodou je, že nyní máme zaručeno, že data budou určitě třída, tj. můžeme volat metody z TObject, např. novou metodu ToString (fData.ToString - viz minule).
Samozřejmě můžeme specifikovat omezení na libovolnou třídu a pak můžeme volat metody té třídy.
Default constructor
Teď by kompilátor vždy pro třídu volal TObject.Create, ale existuje možnost jak tomu zamezit.
type
TConstrClass <T: class, constructor> = class
private
val: T;
public
constructor Create;
function Get: T;
end;
Nyní můžeme napsat
constructor TConstrClass<T>.Create;
begin
val := T.Create;
end;
a kompilátor bude volat vždy správný constructor.
Předdefinované generické typy
Pro usnadnění práce existuje několik předdefinovaných často používaných typů deklarovaných v unitu Generics.Collections.
type
TList<T> = class
TQueue<T> = class
TStack<T> = class
TDictionary<TKey,TValue> = class
a pak
TObjectList<T: class> = class(TList<T>)
TObjectQueue<T: class> = class(TQueue<T>)
TObjectStack<T: class> = class(TStack<T>)
např. tedy
var
t1: TList<Integer>;
begin
t1 := TList<Integer>.Create;
t1.Add(10);
t1.Free;
Výhodou takového kódu je větší robustnost napsaného kódu, kdy kompilátor kontroluje co může.
Předdefinované rozhraní
Jen pro úplnost: v unitu Generics.Default jsou definovány některé rozhraní, z nichž důležité jsou
- IComparer<T>
- IEqualityComparer<T>
a z nich vycházející TComparer<T> a další. Používají se např. při řazení seznamů.
Téma je rozsáhlejší, ale jako úvod by to snad mohlo stačit.