Delphi properties with one receiver and setter
I am trying to implement a wrapper around a config file class and it will be easier to use one function to get and one function to set values ββin properties.
The code below is the minimal version of what I am trying to achieve.
Any help would be greatly appreciated.
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.
source to share
Property getters and setters don't know which property is calling them. The only way for a generic getter / setter to know is to use a specifier index
, e.g .:
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 name of a property, you can use RTTI to find the property that has a matching value index
, and if found, you can also access its attributes, for example:
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;
source to share
The getter or setter attribute has no way of determining which property was accessed, resulting in the getter or setter being called.
The consequence of this statement is that what you are asking to do is impossible.
As for how to solve your main problem, I wouldn't think too much about it. It's possible that property index access specifiers might be useful to you. But without knowing the real problem you are facing, I donβt feel ready to go into more detail.
source to share