Where are the Delphi Attributes Real World Examples?

后端 未结 3 1809
刺人心
刺人心 2021-02-04 08:45

I know by TMS Aurelius that we can use the \"new\" 2010 attributes feature to serialize database table fields into object properties at run-time, for example, and I am not an ex

相关标签:
3条回答
  • 2021-02-04 08:57

    Not sure if the question is asking for real world examples of attribute use or how to serialize db tables into objects using attributes. The example below is a contrived simple one (but an example none the less) showing how to use attributes to log changes to object properties.

    Define your custom attribute

    //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;
    

    The "hello world" of classes TProduct using the attribute

    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;
    

    Any class that has a "loggable attribute" can be passed to this method to iterate through the properties and log them.

    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;
    

    end;

    Example of use:

    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);
    
    0 讨论(0)
  • 2021-02-04 09:14

    I must say it's not much clear to me what kind of example do you need. IMHO in http://docwiki.embarcadero.com/RADStudio/Rio/en/Overview_of_Attributes is everything you should need, perhaps providing that you have some basic knowledge of annotation and/or aspect programming resp.

    An example depends on the way/purpose an author of particular SW used attributes for. You mentioned the ORM system: the typical usage here is to annotate member of the class representing DB entity with additional information necessary for DB operation in the backend of such framework. Let assume you have a DB entity having field COMPANY CHAR(32) NOT NULL and you want to represent it in Delphi class:

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

    then you will define attribute TCharColumn with constructor

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

    And usage of such annotation could look something like this:

    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;
    

    This piece of the program demonstrates, how to define fields in a dataset in run-time based on annotation in Delphi. We are limited little bit due lack of named parameters, hence working with parameter list is not flexible as should be e.g. like in Java (compare TMS Aurelius annotation set http://www.tmssoftware.com/site/manuals/aurelius_manual.pdf and http://www.techferry.com/articles/hibernate-jpa-annotations.html

    0 讨论(0)
  • 2021-02-04 09:20

    If you want to declare you own attribute, you can do it like this:

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

    An attribute is a regular class, that has the TCustomAttribute as its ancestor. You implement it as usual:

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

    Now the attribute is declared and implemented, you can just use it:

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

    So now you have an attribute declared and implemented and you have used it to add a display label to some class. The final phase is to use that attribute, since you have a class decorated with it. The code that uses the attribute does not resides in the attribute nor the decorated class, it is implemented in the service layer that will use the decoration.

    Let's say we have a class that returns a possible display label for a class:

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

    That method will inspect a class and return its display label, given it exists. Otherwise it returns an empty string:

    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;
    
    0 讨论(0)
提交回复
热议问题