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í:
1program Project1;
2
3
4
5uses
6 SysUtils, TypInfo;
7
8type
9
10 TSampleClass <T> = class
11 private
12 fdata: T;
13 public
14 function ReadT: T;
15 procedure SetT (value: T);
16 end;
17
18
19
20function TSampleClass<T>.ReadT: T;
21begin
22 exit (fData);
23
24end;
25
26procedure TSampleClass<T>.SetT(value: T);
27begin
28 fData := value;
29end;
30
31var
32 t1: TSampleClass<Integer>;
33 t2: TSampleClass<string>;
34 s: string;
35 i: integer;
36begin
37
38 t1 := TSampleClass<Integer>.Create;
39 t1.SetT (10);
40
41 t2 := TSampleClass<string>.Create;
42 t2.SetT ('test');
43
44
45 s := t2.ReadT;
46 i := t1.ReadT;
47
48 writeln (s, i);
49 t2.Free;
50 t1.Free;
51end.
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'
1function TSampleClass<T>.Info: string;
2begin
3 Result := GetTypeName (TypeInfo (T));
4end;
5
6writeln(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
1type
2 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.
1type
2 TConstrClass <T: class, constructor> = class
3 private
4 val: T;
5 public
6 constructor Create;
7 function Get: T;
8 end;
Nyní můžeme napsat
1constructor TConstrClass<T>.Create;
2begin
3 val := T.Create;
4end;
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.
1type
2 TList<T> = class
3 TQueue<T> = class
4 TStack<T> = class
5 TDictionary<TKey,TValue> = class
6
7 a pak
8
9 TObjectList<T: class> = class(TList<T>)
10 TObjectQueue<T: class> = class(TQueue<T>)
11 TObjectStack<T: class> = class(TStack<T>)
např. tedy
1var
2 t1: TList<Integer>;
3begin
4
5 t1 := TList<Integer>.Create;
6 t1.Add(10);
7
8 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.