Annotating Types and Type Members
Go Up to Attributes (RTTI)
This topic describes the syntax and rules appropriate when annotating a type or a member with an attribute.
To annotate a Delphi type or a member, such as a class or a class member, you must precede the declaration of that type by the name of the attribute class between brackets:
[CustomAttribute] TMyClass = class;
If the name of the attribute class ends in "Attribute", you can also omit the "Attribute" suffix:
[Custom] procedure DoSomething;
Having a set of parenthesis after the attribute class name is also a valid syntax:
[Custom()] TMyRecord = record;
Some attributes accept parameters. To pass arguments to your attribute, use the same syntax as you use for method calls:
[Custom(Argument1, Argument2, …)] TSimpleType = set of (stOne, stTwo, stThree);
To annotate a single type with several attributes, you can either use several sets of brackets:
[Custom1] [Custom2(MyArgument)] FString: String;
Or use comma-separated attributes between a single set of brackets:
[Custom1, Custom2(MyArgument)] function IsReady: Boolean;
You Can Only Use Constant Expressions as Attribute Parameters
An attribute that is annotated to a type or a member is inserted into the RTTI information block in the generated binary. The emitted information includes:
- The class type of the attribute.
- The pointer to the selected constructor.
- A list of constants that are later passed to the attribute constructor.
The values passed to the constructor of the attribute must be constant expressions. Because those values must be embedded directly into the resulting binary, it is impossible to pass an expression that requires run-time evaluation. This raises a few limitations to the information that can be passed to the attribute at compile time:
- You can only use constant expressions, including sets, strings, and ordinal expressions.
- You can use TypeInfo() to pass type information because the RTTI block addresses are known at compile time.
- You can use class references because the metaclass addresses are known at compile time.
- You cannot use out or var parameters because they require run-time evaluation of addresses of passed parameters.
- You cannot use Addr() or @.
The following code exemplifies the case in which the compiler does not compile the annotation:
var a, b: Integer; type [SomeAttribute(a + b)] TSomeType = record // … end;
In the previous example, the constructor of
SomeAttribute requires an integer value. The passed expression requires a run-time evaluation of
a + b. The compiler emits a compile-time error because it expects a constant expression.
The code below shows an accepted expression:
const a = 10; b = 20; type [SomeAttribute(a + b)] TSomeType = record // … end;
The values of
b are known at compile time; thus, the constant expression is evaluated directly.