1. Technology

Understanding and Using Attributes in Delphi

Attributes Extend the Normal Object-Oriented Model With Aspect-Oriented Elements


Along with the extended RTTI in Delphi 2010, a new language feature was introduced: Attributes

Attributes as a language feature exist in .Net and Java (known as "annotation") and are a new feature in Delphi (Win32) introduced in Delphi 2010.

Attributes are a language feature in Delphi that allow annotating types and type members with special objects that carry additional information. This information can be queried at run time.

Along with existing OOP mechanisms (inheritance and ownership) you can now use annotations for a class (and class members) to further define what your classes are capable of.

Attributes are useful when you want to introduce new behavior to types (objects or record) based on additional information supplied by the annotated attributes.

Where It All Starts: TCustomAttribute

An attribute is just a simple class. To declare your own custom attribute, you derive it from a special predefined TCustomAttribute class.

Next, you annotate or "apply" the attribute to a class, a method, property or a field of a class.

Finally, using RTTI, you query a class (method, property, ...) for an attribute and do something with it ;)

Since attributes are classes, they also can have properties, methods and fields that further define how attributes operate.

To get started with attributes consider reading a few articles:

RTTI and Attributes in Delphi 2010
A quick run through of creating and then querying custom attributes.

More Attributes in Delphi 2010
An example of attribute usage, not an example of a robust, complete validation framework.

Using Attributes and TCustomAttribute descendants
Attributes are a way of associating additional metadata information with a given type or member of a type. They can be applied in many places, the following code shows several of the places you can place attributes.

An Example: Annotating Enumeration Values

After reading the above articles and examining what can be found on Attributes in the Delphi Help system, you should be ready to answer the following question:

What is the result of a call to "ValueFromEnumOverClass(TWhatElement.All, TWhatElementInteger)"?

Of course, to be able to answer, you need the following attribute usage example:

  TWhatElement = (None, All, Random);

  WhatElementAttribute = class(TCustomAttribute)
    fElement: TWhatElement;
    property Element : TWhatElement read fElement write fElement;
    constructor Create(const element : TWhatElement);

  TWhatElementDescription = class(TObject);

  TWhatElementDescriptionClass = class of TWhatElementDescription;

  TWhatElementString = class(TWhatElementDescription)
    function None : string;
    function All : string;
    function Random : string;

  TWhatElementInteger = class(TWhatElementDescription)
    function Zero : integer;
    function MaxInt : integer;
    function Random : integer;
That was the interface section. Here's the implementation:
{ WhatElementAttribute }
constructor WhatElementAttribute.Create(const element: TWhatElement);
  fElement := element;

{ TWhatElementString }
function TWhatElementString.All: string;
  result := 'all';

function TWhatElementString.None: string;
  result := 'none';

function TWhatElementString.Random: string;
  result := 'random';

{ TWhatElementInteger }
function TWhatElementInteger.MaxInt: integer;
  result := System.MaxInt;

function TWhatElementInteger.Zero: integer;
  result := 0;

function TWhatElementInteger.Random: integer;
  result := System.Random(MaxInt);
Finally, here's the "ValueFromEnumOverClass" function:
function ValueFromEnumOverClass(const whatElement : TWhatElement; const descriptionClass : TWhatElementDescriptionClass) : string;
  ctx : TRttiContext;
  t : TRttiType;
  m : TRttiMethod;
  a : TCustomAttribute;
  noParams: array of TValue;

  o : TObject;
  o := descriptionClass.Create;
    t := ctx.GetType(descriptionClass);
    for m in t.GetMethods do
      for a in m.GetAttributes do
        if a is WhatElementAttribute then
          if WhatElementAttribute(a).Element = whatElement then
            result := m.Invoke(o, noParams).ToString;
Answer: "2147483647" :)

In short (if the above code can be explained "in short"):

  • There's an enum type "TWhatElement".
  • There's a custom attribute class "WhatElementAttribute". It has one property of TWhatElement type value. You can "apply" this attribute to a class, method, property ...
  • There are two descendants of the TWhatElementDescription class: TWhatElementString and TWhatElementInteger.
  • Each of the 3 methods of TWhatElementString and TWhatElementInteger have been marked with the WhatElementAttribute specifying a unique TWhatElement value.
  • There's the ValueFromEnumOverClass procedure. it takes a TWhatElement and a class derived from TWhatElementDescriptionClass.
  • Depending on the value of the "whatElement" parameter a different method of the descriptionClass is executed (invoked) - the one marked with the whatElement value.
  • Use ValueFromEnumOverClass to execute a method of a TWhatElementDescriptionClass depending on the value of whatElement.
Piece of cake ;)

Of course, you must have Delphi 2010 or newer to compile the above code, plus you need "reading" knowledge or the new RTTI introduced in Delphi 2010.

No, do not go looking for simple attribute usage examples. Once you understand the above one - you have attributes in your pocket!

Here's a test question: what is the result of a call to "ValueFromEnumOverClass(TWhatElement.Random, TWhatElementString)"?

©2014 About.com. All rights reserved.