Delphi’s Run-Time Type Information (RTTI) system provides powerful capabilities for dynamic type management. By leveraging RTTI, developers can inspect, modify, and interact with types and their members at runtime, enabling more flexible and adaptable applications. This article explores how to manage types dynamically in Delphi using RTTI, focusing on three key aspects:
To work with RTTI in Delphi, you first need to obtain a TRttiType instance that represents the type you want to work with. This can be done using the TRttiContext class.
Ensure that your Delphi project includes the necessary units:
uses
System.SysUtils,
RTTI;
Here’s how you can assign a specific class type, such as TClientDataSet, to a TRttiType variable:
procedure AssignRttiType;
var
Context: TRttiContext;
RttiType: TRttiType;
begin
// Initialize the RTTI context
Context := TRttiContext.Create;
try
// Assign the TRttiType to TClientDataSet
RttiType := Context.GetType(TClientDataSet);
if Assigned(RttiType) then
Writeln('Successfully assigned TRttiType to TClientDataSet.')
else
Writeln('Failed to assign TRttiType.');
finally
// TRttiContext is automatically managed
end;
end;
This procedure creates an RTTI context and retrieves the RTTI type information for the TClientDataSet class.
Once you have a TRttiType variable, you might need to determine what specific type it represents. This can be particularly useful when working with polymorphism or dynamic type scenarios.
To verify if the TRttiType represents an exact type, use the following approach:
procedure CheckExactType(RttiType: TRttiType);
begin
if (RttiType is TRttiInstanceType) and
(TRttiInstanceType(RttiType).MetaclassType = TClientDataSet) then
begin
Writeln('RttiType represents exactly TClientDataSet.');
end
else
begin
Writeln('RttiType does not represent TClientDataSet.');
end;
end;
To determine if the TRttiType is TClientDataSet or a descendant of it, use the InheritsFrom method:
procedure CheckInheritsFrom(RttiType: TRttiType);
var
Metaclass: TClass;
begin
if RttiType is TRttiInstanceType then
begin
Metaclass := TRttiInstanceType(RttiType).MetaclassType;
if Metaclass.InheritsFrom(TClientDataSet) then
Writeln('RttiType is TClientDataSet or a descendant.')
else
Writeln('RttiType is neither TClientDataSet nor a descendant.');
end
else
begin
Writeln('RttiType is not an instance type.');
end;
end;
With the RTTI type information, you can dynamically access and manipulate properties or invoke methods of the class. Below are examples demonstrating how to achieve this.
Suppose you want to set the Active property of a TClientDataSet instance dynamically:
procedure SetActiveProperty(RttiType: TRttiType; Instance: TObject; Value: Boolean);
var
Prop: TRttiProperty;
begin
if Assigned(RttiType) then
begin
Prop := RttiType.GetProperty('Active');
if Assigned(Prop) and Prop.IsWritable then
begin
Prop.SetValue(Instance, Value);
Writeln('Property "Active" set to ', BoolToStr(Value, True));
end
else
begin
Writeln('Property "Active" not found or not writable.');
end;
end;
end;
To invoke a method, such as Open, on a TClientDataSet instance:
procedure InvokeOpenMethod(RttiType: TRttiType; Instance: TObject);
var
Method: TRttiMethod;
begin
if Assigned(RttiType) then
begin
Method := RttiType.GetMethod('Open');
if Assigned(Method) then
begin
Method.Invoke(Instance, []);
Writeln('Method "Open" invoked successfully.');
end
else
begin
Writeln('Method "Open" not found.');
end;
end;
end;
If the class has a static method, you can invoke it as well. For example, to invoke a static method StaticMethod of TMyClientDataSet:
procedure InvokeStaticMethod(RttiType: TRttiType);
var
Method: TRttiMethod;
begin
if Assigned(RttiType) then
begin
Method := RttiType.GetMethod('StaticMethod');
if Assigned(Method) and Method.IsStatic then
begin
Method.Invoke(nil, []);
Writeln('Static method "StaticMethod" invoked.');
end
else
begin
Writeln('Static method "StaticMethod" not found.');
end;
end;
end;
In addition to assigning RTTI types based on class references, you can also assign the RTTI type based on an instance of an object. This is particularly useful when working with object instances whose types are determined at runtime.
Here’s how you can assign the RTTI type based on an existing object instance:
procedure AssignRttiTypeFromInstance(Instance: TObject; out RttiType: TRttiType);
var
Context: TRttiContext;
begin
if Assigned(Instance) then
begin
Context := TRttiContext.Create;
try
// Assign RTTI type based on the instance's class type
RttiType := Context.GetType(Instance.ClassType);
Writeln('TRttiType assigned based on the instance of ', Instance.ClassName, '.');
finally
// TRttiContext is automatically managed
end;
end
else
begin
RttiType := nil;
Writeln('Instance is nil. RTTI type not assigned.');
end;
end;
This procedure takes an object instance and assigns its RTTI type to the RttiType variable. This allows you to work with the RTTI information of the specific instance dynamically.
The following example ties together assigning a type, checking its type, accessing its members, and assigning RTTI based on an object instance:
program RTTIDynamicTypeManagement;
{$APPTYPE CONSOLE}
uses
System.SysUtils,
RTTI,
Datasnap.DBClient;
type
TMyClientDataSet = class(TClientDataSet)
public
class procedure StaticMethod;
end;
{ TMyClientDataSet }
class procedure TMyClientDataSet.StaticMethod;
begin
Writeln('StaticMethod of TMyClientDataSet called.');
end;
procedure AssignRttiType(var RttiType: TRttiType);
var
Context: TRttiContext;
begin
Context := TRttiContext.Create;
try
// Assigning TClientDataSet type
RttiType := Context.GetType(TClientDataSet);
Writeln('TRttiType assigned to TClientDataSet.');
finally
// TRttiContext is automatically managed
end;
end;
procedure AssignRttiTypeFromInstance(Instance: TObject; out RttiType: TRttiType);
var
Context: TRttiContext;
begin
if Assigned(Instance) then
begin
Context := TRttiContext.Create;
try
// Assign RTTI type based on the instance's class type
RttiType := Context.GetType(Instance.ClassType);
Writeln('TRttiType assigned based on the instance of ', Instance.ClassName, '.');
finally
// TRttiContext is automatically managed
end;
end
else
begin
RttiType := nil;
Writeln('Instance is nil. RTTI type not assigned.');
end;
end;
procedure CheckType(RttiType: TRttiType);
begin
// Check exact type
if (RttiType is TRttiInstanceType) and
(TRttiInstanceType(RttiType).MetaclassType = TClientDataSet) then
Writeln('Exact type: TClientDataSet.')
else
Writeln('Not TClientDataSet.');
// Check inheritance
if (RttiType is TRttiInstanceType) and
(TRttiInstanceType(RttiType).MetaclassType.InheritsFrom(TClientDataSet)) then
Writeln('Type is TClientDataSet or a descendant.')
else
Writeln('Type is neither TClientDataSet nor a descendant.');
end;
procedure AccessMembers(RttiType: TRttiType; Instance: TObject);
var
Prop: TRttiProperty;
Method: TRttiMethod;
begin
// Access and set property 'Active'
Prop := RttiType.GetProperty('Active');
if Assigned(Prop) and Prop.IsWritable then
begin
Prop.SetValue(Instance, True);
Writeln('Property "Active" set to True.');
end
else
Writeln('Property "Active" not found or not writable.');
// Invoke method 'Open'
Method := RttiType.GetMethod('Open');
if Assigned(Method) then
begin
Method.Invoke(Instance, []);
Writeln('Method "Open" invoked.');
end
else
Writeln('Method "Open" not found.');
// Invoke a static method if available
Method := RttiType.GetMethod('StaticMethod');
if Assigned(Method) and Method.IsStatic then
begin
Method.Invoke(nil, []);
Writeln('Static method "StaticMethod" invoked.');
end
else
Writeln('Static method "StaticMethod" not found.');
end;
begin
try
var
RttiType: TRttiType;
ClientDataSet: TClientDataSet;
MyClientDataSet: TMyClientDataSet;
begin
// Assign RTTI type to TClientDataSet
AssignRttiType(RttiType);
// Check the type
CheckType(RttiType);
// Create an instance of TClientDataSet
ClientDataSet := TClientDataSet.Create(nil);
try
// Access members
AccessMembers(RttiType, ClientDataSet);
finally
ClientDataSet.Free;
end;
Writeln('---');
// Assign RTTI type to TMyClientDataSet
var Context: TRttiContext := TRttiContext.Create;
try
RttiType := Context.GetType(TMyClientDataSet);
Writeln('TRttiType assigned to TMyClientDataSet.');
// Check the type
CheckType(RttiType);
// Create an instance of TMyClientDataSet
MyClientDataSet := TMyClientDataSet.Create(nil);
try
// Access members
AccessMembers(RttiType, MyClientDataSet);
finally
MyClientDataSet.Free;
end;
finally
Context.Free;
end;
Writeln('---');
// Assign RTTI type based on an object instance
MyClientDataSet := TMyClientDataSet.Create(nil);
try
AssignRttiTypeFromInstance(MyClientDataSet, RttiType);
// Check the type
CheckType(RttiType);
// Access members
AccessMembers(RttiType, MyClientDataSet);
finally
MyClientDataSet.Free;
end;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
AssignRttiType procedure initializes a TRttiContext and assigns the RTTI type for TClientDataSet.AssignRttiTypeFromInstance procedure takes an object instance and assigns its RTTI type to the RttiType variable.CheckType procedure verifies if the RTTI type is exactly TClientDataSet and if it inherits from TClientDataSet.AccessMembers procedure demonstrates how to set the Active property, invoke the Open method, and call a static method StaticMethod if it exists.TMyClientDataSet and repeats the RTTI operations to showcase inheritance handling.TMyClientDataSet and perform the same operations dynamically.TRttiType assigned to TClientDataSet.
Exact type: TClientDataSet.
Type is TClientDataSet or a descendant.
Property "Active" set to True.
Method "Open" invoked.
Static method "StaticMethod" not found.
---
TRttiType assigned to TMyClientDataSet.
Not TClientDataSet.
Type is TClientDataSet or a descendant.
Property "Active" set to True.
Method "Open" invoked.
Static method "StaticMethod" invoked.
---
TRttiType assigned based on the instance of TMyClientDataSet.
Exact type: TMyClientDataSet.
Type is TClientDataSet or a descendant.
Property "Active" set to True.
Method "Open" invoked.
Static method "StaticMethod" invoked.
TRttiContext is lightweight and managed automatically, but always ensure that dynamically created instances are properly freed.Delphi’s RTTI system provides a versatile framework for dynamic type management, enabling developers to write more flexible and adaptable code. By understanding how to assign types to RTTI variables, determine their types at runtime, and interact with their properties and methods dynamically, you can harness the full potential of RTTI in your Delphi applications.
Additionally, assigning RTTI types based on object instances allows for even greater flexibility, especially in scenarios where types are not known until runtime. Embracing RTTI not only enhances your ability to handle dynamic scenarios but also contributes to building more maintainable and scalable software solutions.