V nových Delphi 2010 bylo konečně vylepšeno RTTI (Run Time Type Information - tj. informace o typech získané za běhu).
Jen ve zkratce dřívější stav: pokud při deklaraci třídy uvedete published, budou členy v této skupině mít sice viditelnost jako public, ale kompilátor k nim navíc přigeneruje speciální RTTI informace.
Tyto informace používá Delphi např. pro ukládání a načítání DFM souborů, zobrazování dat v Object Inspectoru a další. Kromě toho se pomocí několika metod dají používat i ve Vašem kódu (např. ClassInfo, GetTypeData, GetPropInfo, GetPropList, GetEnumName, GetEnumValue …).
1uses Typinfo;
2
3
4procedure TForm1.FormCreate(Sender: TObject);
5var
6 Count, Loop: Integer;
7 List: TPropList;
8begin
9 Count := GetPropList(TypeInfo(TForm1), tkAny, @List);
10 Listbox1.Items.BeginUpdate;
11 for Loop := 0 to Pred(Count) do
12 Listbox1.Items.Add(List[Loop]^.Name);
13 Listbox1.Items.EndUpdate;
14
15end;
nebo
1ShowMessage(GetEnumName(TypeInfo(TFormBorderStyle), Ord(BorderStyle)));
Podrobněji o originálním RTTI zde spolu s pěknými příklady.
RTTI a Delphi 2010
Kromě původních RTTI je v Delphi 2010 přidána možnost Atributů jako má třeba C#. Všechny atributy jsou následníkem TCustomAttribute a mohou tudíž mít metody, property atd. (viz. třída MyAttribute v příkladu).
Příklad ukazuje jednoduché dotazování RTTI (nezapomenout že veškerá podpora je v rtti.pas). V reálném případě by TMyClass pak byl objekt a v proceduře Test by se pracovalo i s tímto objektem (resp. jeho daty). Cílem by bylo uložení dat objektu TMyClass za pomoci RTTI do datového úložiště.
Pro zajímavost dle komentáře autora implementace v Delphi (Barry Kelly), je TRttiContext record zapouzdřující interface a je vytvořen při přístupu a uvolněn mimo viditelnost, tj. Create a Free nejsou třeba.
Použití může být již uvedené ukládání a načítání objektů do DB nebo třeba kdykoliv, kdy se vyplatí napsat obecný přístup pro práci s daty, které mají něco společného.
1program Project1;
2
3
4
5uses
6 SysUtils, Rtti;
7
8type
9
10 MyAttribute = class(TCustomAttribute)
11 private
12 FFieldName: string;
13 FSize: Integer;
14 public
15 constructor Create(const sFieldName : string; iSize : Integer);
16 property FieldName : string read FFieldName write FFieldName;
17 property Size : Integer read FSize write FSize;
18 end;
19
20
21 TMyClass = class
22 private
23 FFirstName:string;
24 FSurname: string;
25 public
26 [MyAttribute('dcFirstName', 50)]
27 property FirstName:string read FFirstName write FFirstName;
28 [MyAttribute('dcSurnName', 30)]
29 property SurName: string read FSurname write FSurname;
30 end;
31
32
33constructor MyAttribute.Create(const sFieldName: string; iSize: Integer);
34begin
35 FSize := iSize;
36 FFieldName := sFieldName;
37end;
38
39
40procedure Test;
41var
42 ctx : TRttiContext;
43 t : TRttiType;
44 m : TRttiProperty;
45 a : TCustomAttribute;
46begin
47
48 ctx := TRttiContext.Create;
49 try
50 t := ctx.GetType(TMyClass);
51 for m in t.GetProperties do
52 for a in m.GetAttributes do
53 if a is MyAttribute then
54 writeln(Format('Property = %s; Attribute = %s, FieldName = %s, Size = %d',
55 [m.Name, a.ClassName, MyAttribute(a).FieldName,
56 MyAttribute(a).Size]));
57 finally
58 ctx.Free;
59 end;
60end;
61
62begin
63 Test;
64end.
Výsledek je pak:
Property = FirstName; Attribute = MyAttribute, FieldName = dcFirstName, Size = 50
Property = SurName; Attribute = MyAttribute, FieldName = dcSurnName, Size = 30
Samozřejmě každá sranda něco stojí a proto jsou EXE díky RTTI trošku větší (ale ne zas tak moc). Pokud použijete {$WEAKLINKRTTI ON} nebo dcc32 –weaklinkrtti popř. na začátku každého unitu (lépe přes include)
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
tak výsledná velikost EXE bude menší (ale zase nebudete moci použít nové RTTI).
A poslední poznámka: TRttiMethod umožňuje volání metod objektu za běhu:
1TRttiMethod = class(TRttiMember)
2
3 function Invoke(Instance: TObject; const Args: array of TValue): TValue; overload;
4 function Invoke(Instance: TClass; const Args: array of TValue): TValue; overload;
5 function Invoke(Instance: TValue; const Args: array of TValue): TValue; overload;
6 function GetParameters: TArray<TRttiParameter>; virtual; abstract;
7 function ToString: string; override;
8 property ReturnType: TRttiType read GetReturnType;
9end;