如何在Delphi中运行时创建和附加自定义属性到字段

6

是否有可能并且如何在运行时创建和附加自定义属性到字段上?

uses
  System.SysUtils,
  System.Classes,
  System.Rtti;

type
  MyAttribute = class(TCustomAttribute)
  private
    fCaption: string;
  public
    constructor Create(const aCaption: string);
    property Caption: string read fCaption write fCaption;
  end;

  TFoo = class(TPersistent)
  public
    [MyAttribute('Title')]
    Bar: string;
    Other: string;
  end;

constructor MyAttribute.Create(const aCaption: string);
begin
  fCaption := aCaption;
end;

procedure CreateAttributes(Typ: TRttiType);
var
  Field: TRttiField;
  MyAttr: MyAttribute;
begin
  for Field in Typ.GetFields do
    begin
      if Length(Field.GetAttributes) = 0 then
        begin
          MyAttr := MyAttribute.Create('Empty');
          // how to attach created attribute to Field ???
        end;
    end;
end;

var
  Context: TRttiContext;
  Typ: TRttiType;
  Field: TRttiField;
  Attr: TCustomAttribute;

begin
  Context := TRttiContext.Create;
  Typ := Context.GetType(TFoo);

  CreateAttributes(Typ);

  for Field in Typ.GetFields do
    for Attr in Field.GetAttributes do
      if Attr is MyAttribute then 
        writeln(Field.Name + ' ' + MyAttribute(Attr).Caption);
  readln;
  Context.Free;
end.

运行上面的代码会产生输出:
Bar Title

我希望在运行时向没有赋值的字段注入带有值EmptyMyAttribute,并产生以下输出结果:
Bar Title
Other Empty
2个回答

2
该框架没有提供在运行时附加属性的机制。任何尝试这样做的行为都会涉及篡改该框架。

我认为可能是这样的。这只证明了属性并不比发布指令更适合序列化。 - Dalija Prasnikar
@Dalija 为什么序列化选项会在运行时发生变化? - David Heffernan
其中一个原因是更改类的序列化超出您的控制,例如在无垃圾JSON序列化中。 - Dalija Prasnikar
更改序列化选项的另一个原因是,使用属性会在类和序列化框架之间创建非常紧密的耦合。从一个JSON库切换到另一个可能会是一场噩梦,想象一下如果您不得不同时使用几个框架并且必须将其序列化为JSON和XML。RTTI(不包括属性)(包括发布指令)提供了更多的灵活性和松散的耦合。在设计良好的序列化框架中,您可以创建字段映射,添加转换器、过滤器和其他自定义,而不会强制对类进行更改。 - Dalija Prasnikar

0

属性是编译时的魔法。在运行时,您不需要这样的东西。只需创建自己的字典,其中类型和成员(成员可以设置为字符串)是键,属性列表是值。当您需要检查属性时,请通过两种方式进行 - 通用方式和使用您的字典。


谢谢,但这并不能解决当你无法控制类(即编译时无法更改)且你必须对其执行基于属性的操作(同样超出你的控制)时所遇到的问题。请查看JSON序列化无垃圾以获取更多了解。 - Dalija Prasnikar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接