13

我通过TMS Aurelius知道,例如,我们可以使用“新”2010 属性功能在运行时将数据库表字段序列化为对象属性,而且我不是这种深度面向对象模式的专家,所以我研究了TMS源代码,无法理解如何自己实现,不是DB,不是XML。

因此,我查找了 Google 的所有结果Delphi Attributes,人们发布的所有结果都是声明示例,然后在展示他们的示例之前就停止了。

那么我们如何在表单/执行代码中投影、声明、编码和使用这些榨汁类的真实示例在哪里?

有没有人可以在这里分享一个例子或者知道一篇完整的好文章?

编辑1:

答案应该有一个TFormwith a TButtonwhere,当点击时,执行一些创建的属性类的使用,不要回答只显示属性和类接口,因为有很多我之前说过的声明示例

4

3 回答 3

12

我必须说我不太清楚你需要什么样的例子。http://docwiki.embarcadero.com/RADStudio/Rio/en/Overview_of_Attributes中的恕我直言是您应该需要的一切,也许前提是您对注释和/或方面编程有一些基本知识。

一个示例取决于特定 SW 的作者使用属性的方式/目的。您提到了 ORM 系统:这里的典型用法是在此类框架的后端使用 DB 操作所需的附加信息来注释表示 DB 实体的类的成员。假设您有一个具有字段 COMPANY CHAR(32) NOT NULL 的数据库实体,并且您想在 Delphi 类中表示它:

TSomeDBEntity = class(...)
  FCDS: TClientDataset;
  ...
  constructor Create;
  ... 
  [TCharColumn('COMPANY', 32, false)]
  property CompanyName: string read GetCompanyName write SetCompanyName;
end;

然后您将使用构造函数定义属性 TCharColumn

constructor TCharColumn.Create(const AFieldName:string; ALength:integer; ANullable:boolean);
begin
  inherited;
  FName := AFieldName;
  FLength := ALength;
  FNullable := ANullable;
end;

并且此类注释的用法可能如下所示:

FCDS := TClientDataset.Create(nil);
RttiContext := TRttiContext.Create;
try
  RttiType := RttiContext.GetType(self.ClassType);
  Props := RttiType.GetProperties;
  for Prop in Props do
    begin
      Attrs := Prop.GetAttributes;
      case Prop.PropertyType.TypeKind of
        tkUString:
          begin
            for Attr in Attrs do
              if Attr is TCharColumn then
              begin
                ColAttr := TCharColumn(Attr);
                FCDS.FieldDefs.Add(ColAttr.FName, ftString, ColAttr.FLength, not ColAttr.FNullable);
              end;
          end;
        else
          //... ;
      end;
    end;
finally
  RttiContext.Free;
end;

这段程序演示了如何在 Delphi 中基于注释在运行时定义数据集中的字段。由于缺少命名参数,我们受到了一点限制,因此使用参数列表并不像在 Java 中那样灵活(比较 TMS Aurelius 注释集http://www.tmssoftware.com/site/manuals/aurelius_manual.pdfhttp://www.techferry.com/articles/hibernate-jpa-annotations.html

于 2013-07-27T21:28:09.380 回答
10

如果你想声明你自己的属性,你可以这样做:

type
  TDisplayLabelAttribute = class(TCustomAttribute)
  private
    FText: string;
  public
    constructor Create(const aText: string);
    property Text: string read FText write FText;
  end;

属性是一个常规类,它以TCustomAttribute作为其祖先。你像往常一样实现它:

implementation

constructor TDisplayLabelAttribute.Create(const aText: string);
begin
  FText := aText;
end;

现在该属性已声明并实现,您可以使用它:

[DisplayLabel('My Class')]
TMyClass = class
end;

因此,现在您已经声明并实现了一个属性,并且您已经使用它为某个类添加了显示标签。最后阶段是使用该属性,因为您有一个用它装饰的类。使用属性的代码不存在于属性或装饰类中,它在将使用装饰的服务层中实现。

假设我们有一个类,它返回一个类的可能显示标签:

type
  TArtifactInspector = class
  public
    class function DisplayLabelFor(aClass: TClass): string;
  end;

该方法将检查一个类并返回它的显示标签,如果它存在的话。否则返回一个空字符串:

implementation

uses
  Rtti;

class function TArtifactInspector.DisplayLabelFor(aClass: TClass): string;
var
  rttiContext: TRttiContext;
  rttiType: TRttiType;
  attribute: TCustomAttribute;
begin
  rttiContext := TRttiContext.Create;
  try
    rttiType := rttiContext.GetType(aClass);
    for attribute in rttiType.GetAttributes do
      if attribute is TDisplayLabelAttribute then
        Exit(TDisplayLabelAttribute(attribute).Text);
    Result := '';
  finally
    rttiContext.Free;
  end; // try to recover and return the DisplayLabel
end;
于 2013-07-28T14:19:48.633 回答
9

不确定问题是否要求提供属性使用的真实示例,或者如何使用属性将数据库表序列化为对象。下面的示例是一个人为的简单示例(但仍然是一个示例),展示了如何使用属性来记录对对象属性的更改。

定义您的自定义属性

//By convention attributes are *not* prefixed with a `T` 
//and have the word `Attribute` in their name
LoggableAttribute = class(TCustomAttribute)
  private
    FDescription : String;
  public
    constructor Create(Description: String);
    property Description: String read FDescription;
  end;

使用属性的 TProduct 类的“hello world”

TProduct = Class(TObject)
   private
    FPrice: Double;
    FDescription: String;
    ..
   public  
    [LoggableAttribute('Product Price')]
    property Price : Double read FPrice write SetPrice;
    [Loggable('Product Description')]   {the `Attribute` part is optional}
    property Description : String read FDescription write SetDescription;
    property IsDirty : Boolean read FIsDirty;
  End;

任何具有“可记录属性”的类都可以传递给此方法以遍历属性并记录它们。

procedure LogChanges(LoggableClass: TObject);
var
 c : TRttiContext;
 t : TRttiType;
 p : TRttiProperty;
 a : TCustomAttribute;
 Value : TValue;
begin
 c := TRttiContext.Create;    
 try
   t := c.GetType(LoggableClass.ClassType);
   for p in t.getProperties do
     for a in p.GetAttributes do
       if a is TLoggableProperty then begin
         Value := p.GetValue(LoggableClass);   
         // log to db.. 
         AddLogEntry(p.Name, TLoggableProperty(a).Description, Value.ToString);
       end;
 finally
   c.Free;
 end;

结尾;

使用示例:

var
 P : TProduct;
begin    
 P := TProduct.Create; 
 P.LoadPropertiesFromDB;
 ...
 ... User edits price ...    
 ... 
 P.Price := 499.99;
 ...
 ... Save product to DB 
 if P.IsDirty then  // save and log
   LogChanges(P);
于 2013-07-27T23:36:33.973 回答