【问题标题】:Delphi RTTI, published property appears twiceDelphi RTTI,发布的属性出现两次
【发布时间】:2021-01-31 15:15:48
【问题描述】:

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

  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

在基类中将属性可见性更改为 public 可以解决问题,但在我的情况下这是不可接受的,因为我必须一一增加此属性可见性以在所有子类中发布。

我的问题是如何防止 Foo 属性重复出现,或者至少有什么方法可以在这些重复项之间决定哪个来自基类,哪个来自子类?

更新,更多解释:

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

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

我想指出,这是一个非常简化的示例,我必须在现有的复杂类结构中引入属性,并且我想以最少的油漆方式做到这一点,而无需更改/重写所有类继承.

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

【问题讨论】:

  • 通常的做法是不要发布两次
  • @Andreas Rejbrand:不,我还没有,快看看,谢谢!
  • @David Heffernan:谢谢!这让我变成了一只悲伤的熊猫。我很好奇 IDE 对象检查器是如何解决这个问题的?
  • @Andreas Rejbrand:该主题描述了相同的问题,但仅解释了为什么会发生这种情况,但不会提供解决方案。无论如何,这很有帮助,再次感谢。

标签: delphi


【解决方案1】:

我的问题 [原文如此] 是如何防止 Foo 属性重复出现,或者至少有什么方法可以在这些重复之间决定哪一个来自基类,哪一个来自子类?

确定哪个属性来自哪个类实际上是微不足道的。这是TRttiType.GetProperties文档的摘录:

GetProperties 返回的列表按类/接口层次结构排序。这意味着最近包含的属性位于列表顶部。

因此,第一个来自子类。

举个例子,考虑

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;

将返回类层次结构中最接近 Test 属性的值。所以,

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

显示y,但如果您从子类的Foo 属性中删除Test 属性,您将获得x,因为那已成为最接近的值。 GetTestValueOfFoo 如果没有祖先设置值,则返回空字符串。

【讨论】:

  • 你是对的,我没有注意到输出之前的排序。
  • 啊,GetProperties 是按类排序的!我完全错过了那个信息,非常感谢!我查看了前面讨论的主题,其中输出按字母顺序排序,但我错过了,所以我什至没有考虑检查 GetProperties 数组中的顺序。
猜你喜欢
  • 2012-07-06
  • 2011-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
相关资源
最近更新 更多