Declaring Custom Attributes (RTTI)
Go Up to Attributes Index
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:
type MyCustomAttribute = class(TCustomAttribute) end;
MyCustomAttribute can then be used to annotate any type or any member of a type (such as class, record, or interface):
type [MyCustomAttribute] TSpecialInteger = type integer; TSomeClass = class [MyCustomAttribute] procedure Work; end;
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:
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
Test, to be applied but because of implicit name shortening,
TestAttribute will actually be applied where either
[TestAttribute] are used.
type // To check ambigious names TestAttribute = class(TCustomAttribute) end; // Becomes unaccessible Test = class(TCustomAttribute) end; [Test] // Resolves to TestAttribute at run time TAmbigiousClass = class end;
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:
type AttributeWithConstructor = class(TCustomAttribute) public constructor Create(const ASomeText: String); end;
which can then be used as follows:
type [AttributeWithConstructor('Added text value!')] TRecord = record FField: Integer; end;
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.