我想对属性使用属性,但这些属性有时会在继承的类中发生更改。这是一个示例代码(非常简化):
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)可以解决问题,但在我的情况下这是 Not Acceptable ,因为我必须一一增加此属性的可见性以在所有子类中发布。
我的问题是如何防止 Foo 属性重复出现,或者至少有什么方法可以在这些重复之间决定哪一个来自基类,哪一个来自子类?
更新,更多解释:
我们有一个算法,它将对象的所有已发布属性保存到导出文件中,并且 BaseAttrib 属性保存一些必要的信息。 假设我有一个 TBaseClass 实例和一个 TChildClass 实例,对于 TBaseClass,我希望在输出文件中看到“hello”,对于 TChildClass,我希望在输出文件中看到“good bye”。
还值得一提的是,如果我不创建 TBaseClass 的实例并降低 Foo 对公众的可见性,然后引入一个新的可实例化类,在其中发布 Foo 属性,我将丢失 Foo 属性的属性(在TBaseClass)。因此,如果我有 100 个后代类,但我只想更改一个类中的属性值,我仍然需要将相同的属性(带有原始参数)复制到其余 99 个类中。
我想指出的是,这是一个非常简化的示例,我必须在现有的复杂类结构中引入属性,并且我想以最少的方式来做到这一点,而不需要更改/重新编写所有类继承.
如果我可以避免/管理属性重复,那将是最好的方法,这就是我寻找这种解决方案的原因。
最佳答案
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?
决定哪个属性来自哪个类实际上是微不足道的。以下是 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.
因此,第一个来自子类。
举个例子,考虑一下
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
返回空字符串。
关于Delphi RTTI,已发布的属性出现两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65980714/