I'm trying to implement a configuration file class wrapper and would be easier using a single function to get and a single function to set values to properties.
The code bellow is a minimum version of what I'm trying to achieve.
Any help will be welcome.
unit Config;
interface
uses Rtti;
type
Group = class(TCustomAttribute)
strict private
FName: string;
public
constructor Create(const Name: string);
property Name: string read FName;
end;
IConfig = class
protected
function GetString: string;
procedure SetString(const Value: string);
end;
TConfig = class(IConfig)
public
[Group('Person')]
property Name: string read GetString write SetString;
[Group('Person')]
property City: string read GetString write SetString;
end;
implementation
{ Group }
constructor Group.Create(const Name: string);
begin
FName := Name;
end;
{ IConfig }
function IConfig.GetString: string;
begin
// Here I would need the following from the property that call this function:
// * Property name
// * Property attribute name
// This kind of code will not work, because it loop through all available properties
(*
var
ctx: TRttiContext;
objType: TRttiType;
Prop: TRttiProperty;
begin
ctx := TRttiContext.Create;
objType := ctx.GetType(Obj.ClassInfo);
for Prop in objType.GetProperties do begin
if Prop.GetClassType is TClassBase then
// do something special with base class properties
else
// standard functionality on all other properties
end;
end;
*)
end;
procedure IConfig.SetString(const Value: string);
begin
// Need the same as above
end;
end.
Property getters and setters do not know which property is calling them. The only way for a shared getter/setter to know that is to use the index
specifier, eg:
unit Config;
interface
uses Rtti;
type
Group = class(TCustomAttribute)
strict private
FName: string;
public
constructor Create(const Name: string);
property Name: string read FName;
end;
IConfig = class
protected
function GetString(Index: Integer): string;
procedure SetString(Index: Integer; const Value: string);
end;
TConfig = class(IConfig)
public
[Group('Person')]
property Name: string index 0 read GetString write SetString;
[Group('Person')]
property City: string index 1 read GetString write SetString;
end;
implementation
{ Group }
constructor Group.Create(const Name: string);
begin
FName := Name;
end;
{ IConfig }
function IConfig.GetString(Index: Integer): string;
begin
case Index of
0: begin // Name
...
end;
1: begin // City
...
end;
...
end;
end;
procedure IConfig.SetString(Index: Integer; const Value: string);
begin
// same as above
end;
end.
If the getter/setter needs to know the property name, you can use RTTI to find the property that has the corresponding index
value, and if found then you would also have access to its attributes, eg:
function GetPropNameAndGroup(Cls: TClass; PropIndex: Integer; var PropName, GroupName: String): Boolean;
var
Ctx: TRttiContext;
Prop: TRttiProperty;
Attr: TCustomAttribute;
begin
PropName := '';
GroupName := '';
Ctx := TRttiContext.Create;
for Prop in Ctx.GetType(Cls).GetProperties do
begin
if (Prop as TRttiInstanceProperty).Index = PropIndex then
begin
PropName := Prop.Name;
for Attr in Prop.GetAttributes do
begin
if Attr is Group then
begin
GroupName := Group(Attr).Name;
Break;
end;
end;
Break;
end;
end;
Result := (PropName <> '') and (GroupName <> '');
end;
function IConfig.GetString(Index: Integer): string;
var
PropName, GroupName: string;
begin
if GetPropNameAndGroup(ClassType, Index, PropName, GroupName) then
begin
//...
end;
end;
procedure IConfig.SetString(Index: Integer; const Value: string);
var
PropName, GroupName: string;
begin
if GetPropNameAndGroup(ClassType, Index, PropName, GroupName) then
begin
//...
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With