Declaring Custom Attributes (RTTI)

From RAD Studio
Jump to: navigation, search

Go Up to Attributes (RTTI)

This topic describes the basic methods used to create custom attributes, the proper design decisions for attribute classes, and the general use cases.

Declaring an Attribute

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

    MyCustomAttribute = class(TCustomAttribute)

MyCustomAttribute can then be used to annotate any type or any member of a type (such as class, record, or interface):

   TSpecialInteger = type integer;

   TSomeClass = class
        procedure Work;

Note that the declared attribute class must not be declared as class abstract and should not contain any abstract methods. Even though the compiler allows you to use these attributes for annotation, the built binary will not include them in the emitted RTTI information.

Attribute Names that End with 'Attribute' are Implicitly Shortened

Suppose you define two TCustomAttribute subclasses with the same name prefix, but one has 'Attribute' as a suffix, such as:

  • MyCustom
  • MyCustomAttribute

The class with the 'Attribute' suffix (MyCustomAttribute) is always used, and the class with the shorter name (MyCustom) becomes inaccessible.

The following code snippet demonstrates this issue. One might expect the TCustomAttribute subclass, Test, to be applied but because of implicit name shortening, TestAttribute will actually be applied where either [Test] or [TestAttribute] are used.

  // To check ambigious names
  TestAttribute = class(TCustomAttribute)

  // Becomes unaccessible
  Test = class(TCustomAttribute)

  [Test] // Resolves to TestAttribute at run time
  TAmbigiousClass = class

Constructors in Attributes

Normally, an attribute is designed to carry some additional information that can be queried at run time. To allow specifying custom information for the attribute class, you must declare constructors for it:

    AttributeWithConstructor = class(TCustomAttribute)
        constructor Create(const ASomeText: String);

which can then be used as follows:

   [AttributeWithConstructor('Added text value!')]
   TRecord = record
        FField: Integer;

The method resolution works for attributes as well, which means that you can define overloaded constructors in the custom-defined attribute. Declare only constructors that accept constant values and not out or var ones. This comes out of a basic restriction in how attributes work and is discussed in more detail in Annotating Types and Type Members.

See Also