【问题标题】:Delphi properties with single getter and setter具有单个 getter 和 setter 的 Delphi 属性
【发布时间】:2026-02-22 16:40:01
【问题描述】:

我正在尝试实现一个配置文件类包装器,使用单个函数获取和设置属性值会更容易。

下面的代码是我想要实现的最低版本。

欢迎任何帮助。

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.

【问题讨论】:

标签: delphi properties attributes rtti


【解决方案1】:

属性 getter 和 setter 不知道是哪个属性调用它们。共享 getter/setter 知道这一点的唯一方法是使用 index 说明符,例如:

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.

如果 getter/setter 需要知道属性名,你可以使用 RTTI 查找具有对应 index 值的属性,如果找到了,你也可以访问它的属性,例如:

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;

【讨论】:

    【解决方案2】:

    属性 getter 或 setter 无法确定访问导致 getter 或 setter 被调用的属性。

    该陈述的推论是您要求做的事情是不可能的。

    至于如何解决您的根本问题,我不想过多地推测这一点。 property index access specifiers 可能对您有用是合理的。但是如果不了解您所面临的实际问题,我觉得没有资格进行更详细的说明。

    【讨论】:

    • 这是不正确的。在 Delphi 中,您可以为属性指定 Index 说明符,该说明符成为 getter 和 setter 的额外参数。
    • @Lasse 是的。但问题并没有询问索引属性。
    • 我理解它只是因为他问如何添加单个 getter 和一个 setter 来处理属性。
    • @Lasse 不,他想通过某种魔法找到属性,然后读取附加到属性的属性,然后访问基于该属性的值。
    • 实际上他并没有问如何使用 RTTI 和属性来做到这一点,它只是在源代码内部并显示了他到目前为止所尝试的内容。