You had access to pointers for properties, fields and Methods on objects. If you spent some time learning, the old RTTI system was powerful. However, the power of the RTTI in prior versions seem very small when compared to what is now available in Delphi 2010.
In Delphi 2010 you have the option to have RTTI information for almost everything. The choice of what to include is yours, it is controlled by the new $RTTI directive. The default behavior is defined in System.pas shows that, properties and methods are now available via RTTI in both public and published sections, and Fields are available in all of the sections.
01.
Section
of
System
.
pas:
02.
{ RTTI Visibility }
03.
type
04.
TVisibilityClasses =
set
of
(vcPrivate, vcProtected, vcPublic, vcPublished);
05.
06.
const
07.
{ These constants represent the default settings built into the compiler.
08.
For classes, these settings are normally inherited from TObject. }
09.
DefaultMethodRttiVisibility = [vcPublic, vcPublished];
10.
DefaultFieldRttiVisibility = [vcPrivate
..
vcPublished];
11.
DefaultPropertyRttiVisibility = [vcPublic, vcPublished];
12.
13.
type
14.
{ Default RTTI settings }
15.
{
$RTTI
INHERIT
16.
METHODS(DefaultMethodRttiVisibility)
17.
FIELDS(DefaultFieldRttiVisibility)
18.
PROPERTIES(DefaultPropertyRttiVisibility)}
Raw RTTI Information would be worthless unless you have good way to access the information. The new Unit RTTI.Pas provides a simple and elegant way to access this data. Flexibility of classes, with out the headaches of Memory Management was a key concern of the new design. As such RTTI access is done through a context, once that context is freed all the RTTI objects created are freed.
01.
var
02.
c : TRttiContext;
03.
begin
04.
c := TRttiContext
.
Create;
05.
try
06.
// RTTI Access code here
07.
finally
08.
c
.
free;
09.
end
;
10.
end
;
If you open up RTTI.pas you will need notice that TRttiContext is not an object it is a Record, so don't get confused you should still call .Create and .Free as you would with an object. The reason for this is to free the pool of RTTI objects that may have been created. Atlhough the help file tells you not to free it, I personally like to clean up. Update:
I have had enough questions on this alone, I thought I would explain in more detail.
TRttiContext offers several key methods, which allow you to get access, to the types in the system.
1.
function
GetType(ATypeInfo: Pointer): TRttiType; overload;
2.
function
GetType(AClass: TClass): TRttiType; overload;
3.
function
GetTypes: TArray<TRttiType>;
4.
function
FindType(
const
AQualifiedName:
string
): TRttiType;
For example all of the following will return the TRttiType class representing, TButton.
01.
var
02.
c : TRttiContext;
03.
t : TRttiType;
04.
begin
05.
c := TRttiContext
.
Create;
06.
try
07.
// Via a String
08.
t := c
.
FindType(
'StdCtrls.TButton'
);
09.
10.
// Via the pTypeInfo Pointer
11.
t := c
.
GetType(TButton
.
ClassInfo);
12.
13.
// Via the class type
14.
t := c
.
GetType(TButton);
15.
finally
16.
c
.
Free;
17.
end
;
18.
end
;
The TRttiType has many functions that allow you to query the members of that type.
1.
function
GetMethods: TArray<TRttiMethod>; overload; virtual;
2.
function
GetFields: TArray<TRttiField>; virtual;
3.
function
GetProperties: TArray<TRttiProperty>; virtual;
4.
5.
function
GetMethod(
const
AName:
string
): TRttiMethod; virtual;
6.
function
GetMethods(
const
AName:
string
): TArray<TRttiMethod>; overload; virtual;
7.
function
GetField(
const
AName:
string
): TRttiField; virtual;
8.
function
GetProperty(
const
AName:
string
): TRttiProperty; virtual;
So for example the following console application would show all of the methods of TButton.
01.
program
Project10;
02.
{$APPTYPE CONSOLE}
03.
uses
04.
StdCtrls, TypInfo, Rtti;
05.
06.
var
07.
c : TRttiContext;
08.
m : TRttiMethod;
09.
begin
10.
c := TRttiContext
.
Create;
11.
for
m
in
c
.
GetType(TButton).GetMethods
do
12.
begin
13.
Writeln(m
.
ToString);
14.
end
;
15.
c
.
Free;
16.
readln;
17.
end
.
Output:
constructor Create(AOwner: TComponent)
class destructor Destroy
procedure Click
... (Many Lines Removed) ...
procedure AfterConstruction
procedure BeforeDestruction
procedure Dispatch(var Message)
procedure DefaultHandler(var Message)
class function NewInstance: TObject
procedure FreeInstance
class destructor Destroy
Taking this to the next level the following code creates TStringList using the RTTI System, invokes the Add Method and accesses the Text Property. Granted this is not a practical example, it is just designed to show you some of the functionality available. You will notice that values are stored using the type TValue. TValue can store and retrieve any type.
01.
program
Project11;
02.
{$APPTYPE CONSOLE}
03.
uses
04.
StdCtrls, TypInfo, Classes, Rtti;
05.
06.
var
07.
c : TRttiContext;
08.
m : TRttiMethod;
09.
t : TRttiInstanceType;
10.
SL : TValue;
11.
Lines : TValue;
12.
begin
13.
c := TRttiContext
.
Create;
14.
t := (c
.
FindType(
'Classes.TStringList'
)
as
TRttiInstanceType);
15.
SL := t
.
GetMethod(
'Create'
).Invoke(t
.
MetaclassType,[]);
16.
t
.
GetMethod(
'Add'
).Invoke(SL,[
'Hello Do you like my hat?'
]);
17.
t
.
GetMethod(
'Add'
).Invoke(SL,[
'I like that hat, what a party hat!'
]);
18.
Lines := t
.
GetProperty(
'Text'
).GetValue(SL
.
AsObject);
19.
Writeln(Lines
.
ToString);
20.
c
.
Free;
21.
readln;
22.
end
.
Output:
Hello Do you like my hat?
I like that hat, what a party hat!
Although it appears that TValue may act like a variant, TValue is not a replacement for Variant. Specifically, If you assign a specific type to a TValue you must retrieve it as that specific type. For example you can't assign an Integer to a TValue and retrieve it as a String, doing so results in an Invalid Type Cast.
This is just taste, future articles will cover this in more detail.
RTTI Article List
Hi Robert!
ReplyNice article, thank you for share with us.
Keep posting.
Cesar Romero