RTTI a nové RTTI v Delphi 2010

vložil Radek Červinka 29. prosince 2009 02:07

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 …).


uses Typinfo;
{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var
  Count, Loop: Integer;
  List: TPropList;
begin
  Count := GetPropList(TypeInfo(TForm1), tkAny, @List);
  Listbox1.Items.BeginUpdate;
  for Loop := 0 to Pred(Count) do
    Listbox1.Items.Add(List[Loop]^.Name);
  Listbox1.Items.EndUpdate;

end;

nebo

  ShowMessage(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.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Rtti;

type
  // definice atrubutu
  MyAttribute = class(TCustomAttribute)
  private
    FFieldName: string;
    FSize: Integer;
  public
    constructor Create(const sFieldName : string; iSize : Integer);
    property FieldName : string read FFieldName write FFieldName;
    property Size : Integer read FSize write FSize;
  end;

  // nase trida
  TMyClass = class
  private
    FFirstName:string;
    FSurname: string;
  public
    [MyAttribute('dcFirstName', 50)]        // v podstate volaní constructoru MyAttribute.Create
    property FirstName:string read FFirstName write FFirstName;
    [MyAttribute('dcSurnName', 30)]
    property SurName: string read FSurname write FSurname;
  end;


constructor MyAttribute.Create(const sFieldName: string; iSize: Integer);
begin
  FSize := iSize;
  FFieldName := sFieldName;
end;

// zde se pracuje s RTTI
procedure Test;
var
  ctx : TRttiContext;
  t : TRttiType;
  m : TRttiProperty;
  a : TCustomAttribute;
begin

  ctx := TRttiContext.Create;
  try
    t := ctx.GetType(TMyClass);
    for m in t.GetProperties do 
      for a in m.GetAttributes do
        if a is MyAttribute then // ať se nám tam neplete nic co není naše
          writeln(Format('Property = %s; Attribute = %s, FieldName = %s, Size = %d',
                                    [m.Name, a.ClassName, MyAttribute(a).FieldName,
                                     MyAttribute(a).Size]));
  finally
    ctx.Free;
  end;
end;

begin
  Test;
end.

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:

TRttiMethod = class(TRttiMember)
 //…
  function Invoke(Instance: TObject; const Args: array of TValue): TValue; overload;
  function Invoke(Instance: TClass; const Args: array of TValue): TValue; overload;
  function Invoke(Instance: TValue; const Args: array of TValue): TValue; overload;
  function GetParameters: TArray<TRttiParameter>; virtual; abstract;
  function ToString: string; override;
  property ReturnType: TRttiType read GetReturnType;
end;

Tagy: , , , ,

Jazyk | Recenze

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců