Delphi RTTI,已发布 属性 出现两次

Delphi RTTI, published property appears twice

我想为属性使用属性,但这些属性偶尔会在继承的 class 中发生变化。这是一个示例代码(非常简化):

  TBaseClass = class(TObject)
  private
    FFoo: string;
  published
    [BaseAttirib('hello')]
    property Foo: string read FFoo;
  end;

  TChildClass = class(TBaseClass)
  published
    [BaseAttirib('good bye')]
    property Foo;
  end;

当我使用 RTTI 查看属性时,Foo 属性 在 GetProperties 数组中出现了两次:

var
  xObj: TObject;
  xType: TRttiType;
  xProp: TRttiProperty;
begin
  // FContext is a TRttiContext, already created, not included in this sample
  xObj := TChildClass.Create;
  try
    xType := FContext.GetType(xObj.ClassType);

    for xProp in xType.GetProperties do
    begin
      if not (xProp.Visibility in [mvPublished]) then
        Continue;

      Memo1.lines.add(xProp.Name + ' = ' + xProp.GetValue(xObj).AsString);
    end;
  finally
    FreeAndNil(xObj);
  end;
end;

这是输出:

  Foo = TChildClass
  Foo = TChildClass

将基础 class 中的 属性 可见性更改为 public 可以解决问题,但这对我来说是不可接受的,因为我必须增加这个 属性 在所有 child classes 中一一发布的可见性。

我的问题是如何防止 Foo 属性 重复出现,或者至少有什么方法可以决定这些重复中哪一个来自基础 class 哪个来自基础 class child?

更新,多一点解释:

我们有一个算法可以将 object 的所有已发布属性保存到一个导出文件中,并且 BaseAttrib 属性包含一些必要的信息。 假设我有一个 TBaseClass 实例和一个 TChildClass 实例,对于 TBaseClass,我希望在输出文件中看到 'hello',对于 TChildClass,我希望在输出文件中看到 'good bye'。

另外值得一提的是,如果我不创建 TBaseClass 的实例并将 Foo 的可见性降低到 public,然后引入一个新的可实例化 class,我将在其中发布 Foo 属性,我会丢失 Foo 属性 的属性(在 TBaseClass 中引入)。因此,如果我有 100 个后代 classes 但我只想更改一个 class 中的属性值,我仍然必须将相同的属性(使用原始参数)复制到剩余的 99 classes.

我想指出的是,这是一个非常简化的例子,我必须在现有的复杂 class 结构中引入属性,我想用最不完整的方式来做到这一点,而不需要 changing/rewrinting 所有 class 继承。

如果我可以 avoid/manage 属性 重复那将是最好的方法,这就是我寻找这种解决方案的原因。

My quesion [sic] is how can I prevent the Foo property duplicated appearance, or at least is there any way to decide between these duplications which one came from the base class and which one from the child?

决定哪个属性来自哪个class实际上是微不足道的。这是 TRttiType.GetProperties:

文档的摘录

The list returned by GetProperties is ordered by the class/interface hierarchy. This means that the most recently included properties are located at the top of the list.

因此,第一个来自 child class。

例如,考虑

type
  TestAttribute = class(TCustomAttribute)
    Value: string;
    constructor Create(S: string);
  end;

  TBaseClass = class(TObject)
  protected
    function GetFoo: string; virtual;
  published
    [Test('x')]
    property Foo: string read GetFoo;
  end;

  TChildClass = class(TBaseClass)
  published
    [Test('y')]
    property Foo;
  end;

然后

function GetTestValueOfFoo(AObject: TBaseClass): string;
begin
  var LRttiType := TRttiContext.Create.GetType(AObject.ClassType);
  if Assigned(LRttiType) then
    for var LRttiProp in LRttiType.GetProperties do
      if LRttiProp.Name = 'Foo' then
        for var LAttrib in LRttiProp.GetAttributes do
          if LAttrib is TestAttribute then
            Exit(TestAttribute(LAttrib).Value);
  Result := '';
end;

将 return 最接近 Test 属性的值 class 层次结构。所以,

var obj := TChildClass.Create;
try
  ShowMessage(GetTestValueOfFoo(obj))
finally
  obj.Free;
end;

显示 y,但是如果您从 child class 的 Foo 属性 中删除 Test 属性,您将取而代之的是 x,因为那已经成为最接近的值。 GetTestValueOfFoo return 如果没有祖先设置值,则为空字符串。