Tuesday, September 15, 2009

Exploring TRttiMember Descendants in depth (Part I) Properties and Fields

Some types such as classes and records contain "members" such as field, properties and methods.

RTTI information starts with the TRttiMember which provides 4 bits of information about each Member.


  1. The name

  2. Visibility (private,protected,public,published)

  3. The type that the member is associated with

  4. Attributes associated with that member



This example code shows how to access each of these.


program Project4;

{$APPTYPE CONSOLE}

uses
SysUtils, Rtti, TypInfo;

type
TTest = class(TCustomAttribute)
end;

TBook = class(TObject)
private
FTitle: String;
public
[TTest]
property Title : String read FTitle write FTitle;
end;

var
c : TRttiContext;
m : TRttiMember;

{ TExample }


begin
c := TRttiContext.Create;
m := c.GetType(TBook).GetProperty('Title');
Writeln('Name:',m.Name);
Writeln('Visibility:',GetEnumName(TypeInfo(TMemberVisibility),ord(m.Visibility)));
WriteLn('Parent:',m.Parent.ToString);
Writeln('First Attribute:',m.GetAttributes[0].ToString);
writeln;
m := c.GetType(TBook).GetField('FTitle');
WriteLn('Name:',m.Name);
WriteLn('Visibility:',GetEnumName(TypeInfo(TMemberVisibility),ord(m.Visibility)));
WriteLn('Parent:',m.Parent.ToString);
readln;
c.Free;
end.

Output:

Name:Title
Visibility:mvPublic
Parent:TBook
First Attribute:TTest

Name:FTitle
Visibility:mvPrivate
Parent:TBook



The two basic TRttiMember descendants I want to cover in this blog post is TRttiField and TRttiPropery. Both descendants allow you to get and set the values on an Instance of the given type. They both provide SetValue() and GetValue() methods.

The following code demonstrates how to use both. If you missed my Introduction to TValue
I suggest you take some time to read it as it explains some of the "Magic" behind what is going on here.


program Project10;
{$APPTYPE CONSOLE}

uses
SysUtils, Rtti, TypInfo;

type

TBook = class(TObject)
private
FTitle: String;
public
property Title : String read FTitle write FTitle;
end;

var
c : TRttiContext;
p : TRttiProperty;
f : TRttiField;
book : TBook;
v : TValue;
begin
book := TBook.Create;
try
c := TRttiContext.Create;

p := c.GetType(TBook).GetProperty('Title');
p.SetValue(Book,'Go, Dog, Go!');
v := p.GetValue(Book);
Writeln('Title:',v.ToString);

f := c.GetType(TBook).GetField('FTitle');
f.SetValue(Book,'Green Eggs and Ham');
v := f.GetValue(Book);
Writeln('FTitle:',v.ToString);

readln;

c.Free;

finally
Book.Free;
end;
end.

Output:

Title:Go, Dog, Go!
FTitle:Green Eggs and Ham


With Properties you have to worry about if they are readable or writable, as attempting to call SetValue on a property that is not writable will produce an EPropReadOnly Exception. TRttiProperty has two properties "isReadable" and 'isWritable" that allow you to discover how the property was declared. The following code demonstrates how it works.


program Project10;
{$APPTYPE CONSOLE}

uses
SysUtils, Rtti, TypInfo;

type

TBook = class(TObject)
private
FTitle: String;
FUgly: String;
FAuthor: String;
public
property Title : String read FTitle write FTitle;
property Author : String read FAuthor;
property Ugly : String write FUgly;
end;

var
c : TRttiContext;
p : TRttiProperty;

begin
c := TRttiContext.Create;
try
p := c.GetType(TBook).GetProperty('Title');
WriteLn('Name:',p.Name);
Writeln('IsReadable:',p.IsReadable);
Writeln('IsWritable:',p.IsWritable);
writeln;
p := c.GetType(TBook).GetProperty('Author');
WriteLn('Name:',p.Name);
Writeln('IsReadable:',p.IsReadable);
Writeln('IsWritable:',p.IsWritable);
writeln;
p := c.GetType(TBook).GetProperty('Ugly');
WriteLn('Name:',p.Name);
Writeln('IsReadable:',p.IsReadable);
Writeln('IsWritable:',p.IsWritable);
readln;
finally
c.Free;
end;
end.

Output:

Name:Title
IsReadable:TRUE
IsWritable:TRUE

Name:Author
IsReadable:TRUE
IsWritable:FALSE

Name:Ugly
IsReadable:FALSE
IsWritable:TRUE



That pretty much wraps it up for TRttiField and TRttiProperty, in the next
post I will cover TRttiMethod.

RTTI Article List

2 comments:

  1. Your code is not correct with regard to the documentation. The context does not have to be freed. Actually, you do not even have to call Create in order to use it. TRttiContext is a record and thus can be used without instantiating it.

    ReplyDelete
  2. I took some time to explain why I do what I do.

    http://robstechcorner.blogspot.com/2009/09/trtticontextcreate-trtticontextfree.html

    ReplyDelete