Difference between revisions of "Custom Attributes/ru"
Line 17: | Line 17: | ||
'''Важно''': если вы хотите использовать свой тип атрибута без каких-либо аргументов, вы ''должны'' самостоятельно объявить конструктор без параметров, поскольку один из <code>TCustomAttribute</code> является закрытым (private). | '''Важно''': если вы хотите использовать свой тип атрибута без каких-либо аргументов, вы ''должны'' самостоятельно объявить конструктор без параметров, поскольку один из <code>TCustomAttribute</code> является закрытым (private). | ||
− | == | + | ==Как используются атрибуты?== |
− | + | Атрибуты привязываются к типу или свойству путем указания хотя бы одного предложения атрибута перед типом или свойством. Для типа атрибут указывается в определении типа (например, в объявлении класса, записи или перечисления) или в повторном объявлении уникального типа (например, <code>TLongInt = type LongInt</code>). Простое переименование типов (например, <code>TLongInt = LongInt</code>) не допускается. | |
− | |||
− | |||
− | |||
− | ( | ||
− | + | Предложения атрибутов доступны только в том случае, если установлен новый переключатель режимов <code>PREFIXEDATTRIBUTES</code>, который используется по умолчанию в режимах Delphi и DelphiUnicode. | |
− | PREFIXEDATTRIBUTES | ||
− | DelphiUnicode. | ||
− | |||
− | |||
+ | Синтаксис предложения атрибута следующий: | ||
+ | <source lang="pascal"> | ||
ATTRIBUTECLAUSE::='[' ATTRIBUTELIST ']' | ATTRIBUTECLAUSE::='[' ATTRIBUTELIST ']' | ||
ATTRIBUTELIST::=ATTRIBUTE [, ATTRIBUTELIST ] | ATTRIBUTELIST::=ATTRIBUTE [, ATTRIBUTELIST ] | ||
ATTRIBUTE::=IDENTIFIER [ ( PARAMLIST ) ] | ATTRIBUTE::=IDENTIFIER [ ( PARAMLIST ) ] | ||
PARAMLIST::=CONSTEXPR [, PARAMLIST ] | PARAMLIST::=CONSTEXPR [, PARAMLIST ] | ||
+ | </source> | ||
− | + | ''IDENTIFIER'' — это имя класса атрибута. Если вы называете класс атрибута заканчивающимся на "Attribute" (случай суффикса "Attribute" не имеет значения), то имя может использоваться впоследствии без суффикса "Attribute". Таким образом, <code>TMyAttribute</code> и <code>TMy</code> являются эквивалентными альтернативами в следующем примере: | |
− | |||
<source lang="delphi"> | <source lang="delphi"> | ||
Line 97: | Line 91: | ||
</source> | </source> | ||
− | === | + | ===Запрос атрибутов=== |
− | + | Атрибуты могут быть доступны как модулю <code>TypInfo</code>, так и модулю <code>Rtti</code>. | |
− | + | Для модуля <code>TypInfo</code> доступны следующие способы доступа к атрибутам: | |
− | + | Для типов: | |
− | * | + | * используйте поле <code>AttributesTable</code> в <code>TTypeData</code> |
− | * | + | * использовать <code>GetAttributeTable</code> для <code>PTypeInfo</code> |
− | * | + | * используйте <code>GetAttribute</code> в таблице атрибутов вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code> |
− | + | Для свойств: | |
− | * | + | * используйте <code>AttributesTable</code> из <code>TPropInfo</code> |
− | * | + | * используйте <code>GetAttribute</code> в таблице атрибутов вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code> |
− | * | + | * используйте <code>GetPropAttribute</code> в <code>PPropInfo</code> вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code> |
− | + | Для модуля <code>Rtti</code> доступны следующие способы доступа к атрибутам: | |
− | + | Для типов: | |
− | * | + | * используйте <code>GetAttributes</code> для <code>TRttiType</code> рассматриваемого типа |
− | + | Для свойств: | |
− | * | + | * используйте <code>GetAttributes</code> для <code>TRttiProperty</code> рассматриваемого свойства |
==The attributes feature's compatibility with Delphi== | ==The attributes feature's compatibility with Delphi== |
Revision as of 22:59, 30 December 2022
│ English (en) │ русский (ru) │
Пользовательские атрибуты в настоящее время позволяют украшать определения типов и published свойства классов дополнительными метаданными, которые можно запрашивать с помощью RTTI (информация о типе времени выполнения).
Для чего можно использовать атрибуты?
Атрибут используется для связывания определенных метаданных с классом. Например, вы можете использовать атрибут, чтобы пометить класс именем соответствующей таблицы базы данных или аннотировать класс веб-службы строкой, определяющей его базовый путь.
Как объявляются атрибуты?
Атрибуты — это просто классы, происходящие от нового системного типа TCustomAttribute
. Конструкторы таких потомков TCustomAttribute
являются наиболее важной функцией для реализации, поскольку они используются для передачи дополнительных параметров атрибуту (таких, как имя связанной таблицы базы данных или базовый путь в приведенных выше примерах).
Важно: если вы хотите использовать свой тип атрибута без каких-либо аргументов, вы должны самостоятельно объявить конструктор без параметров, поскольку один из TCustomAttribute
является закрытым (private).
Как используются атрибуты?
Атрибуты привязываются к типу или свойству путем указания хотя бы одного предложения атрибута перед типом или свойством. Для типа атрибут указывается в определении типа (например, в объявлении класса, записи или перечисления) или в повторном объявлении уникального типа (например, TLongInt = type LongInt
). Простое переименование типов (например, TLongInt = LongInt
) не допускается.
Предложения атрибутов доступны только в том случае, если установлен новый переключатель режимов PREFIXEDATTRIBUTES
, который используется по умолчанию в режимах Delphi и DelphiUnicode.
Синтаксис предложения атрибута следующий:
ATTRIBUTECLAUSE::='[' ATTRIBUTELIST ']'
ATTRIBUTELIST::=ATTRIBUTE [, ATTRIBUTELIST ]
ATTRIBUTE::=IDENTIFIER [ ( PARAMLIST ) ]
PARAMLIST::=CONSTEXPR [, PARAMLIST ]
IDENTIFIER — это имя класса атрибута. Если вы называете класс атрибута заканчивающимся на "Attribute" (случай суффикса "Attribute" не имеет значения), то имя может использоваться впоследствии без суффикса "Attribute". Таким образом, TMyAttribute
и TMy
являются эквивалентными альтернативами в следующем примере:
program tcustomattr;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
type
TMyAttribute = class(TCustomAttribute)
constructor Create;
constructor Create(aArg: String);
constructor Create(aArg: TGUID);
constructor Create(aArg: LongInt);
end;
{$M+}
[TMyAttribute]
TTestClass = class
private
fTest: LongInt;
published
[TMyAttribute('Test')]
property Test: LongInt read fTest;
end;
{$M-}
[TMyAttribute(1234)]
[TMy('Hello World')]
TTestEnum = (
teOne,
teTwo
);
[TMyAttribute(IInterface), TMy(42)]
TLongInt = type LongInt;
constructor TMyAttribute.Create;
begin
end;
constructor TMyAttribute.Create(aArg: String);
begin
end;
constructor TMyAttribute.Create(aArg: LongInt);
begin
end;
constructor TMyAttribute.Create(aArg: TGUID);
begin
end;
begin
end.
Запрос атрибутов
Атрибуты могут быть доступны как модулю TypInfo
, так и модулю Rtti
.
Для модуля TypInfo
доступны следующие способы доступа к атрибутам:
Для типов:
- используйте поле
AttributesTable
вTTypeData
- использовать
GetAttributeTable
дляPTypeInfo
- используйте
GetAttribute
в таблице атрибутов вместе с индексом, чтобы получить экземплярTCustomAttribute
Для свойств:
- используйте
AttributesTable
изTPropInfo
- используйте
GetAttribute
в таблице атрибутов вместе с индексом, чтобы получить экземплярTCustomAttribute
- используйте
GetPropAttribute
вPPropInfo
вместе с индексом, чтобы получить экземплярTCustomAttribute
Для модуля Rtti
доступны следующие способы доступа к атрибутам:
Для типов:
- используйте
GetAttributes
дляTRttiType
рассматриваемого типа
Для свойств:
- используйте
GetAttributes
дляTRttiProperty
рассматриваемого свойства
The attributes feature's compatibility with Delphi
The feature itself is Delphi compatible with the proviso that FPC is much more unforgiving regarding unbound properties. If the attribute class is not known or the attribute clauses are not bound to a valid type or property the compiler will generate an error.
FPC's RTTI is not considered Delphi compatible. However it covers the same functionality. In contrast to Delphi (which uses Invoke to create the attribute instance) FPC uses a constructor function. FPC's implementation has the advantage of working on systems that don't have full Invoke support.
Additionally using the PREFIXEDATTRIBUTES modeswitch disables the directive clauses for functions, methods and procedure/method types. The following is not allowed any more with the PREFIXEDATTRIBUTES modeswitch enabled:
procedure Test; [cdecl];
begin
end;
Complete example
program testattributes;
{
This is a simple example on how to use custom attributes.
The class uses a custom attribute to retrieve a static date
at program start-up. This is just for the purpose of demo.
This demo's:
- Creation
- Decoration
- Retrieval
}
{$mode delphi}{$H+}{$M+}
{$warn 5079 off} { turn warning experimental off }
uses
sysutils, typinfo, rtti, classes;
type
{A custom attribute to decorate a class with a certain date }
ADateTimeAttribute = class(TCustomAttribute)
private
FArg:TDateTime;
public
{ Just to show a Custom Attribute can have mutiple constructors }
constructor Create(const aArg: TDateTime);overload;
{ We can use the predefined %DATE% compiler include
In the context of a custom attribute we need a
constant expression for the default parameter }
constructor Create(const aArg: String = {$I %DATE%});overload;
published
property Date:TDateTime read Farg;
end;
{ A datetime class that is decorated with our custom attribute }
{ Note you can leave out 'Attribute', the compiler resolves it }
{ [ADateTime(21237.0)] uses first constructor,displays some date in 1958 }
{This calls the second constructor }
[ADateTime]
TMyDateTimeClass = class
private
FDateTime:TDateTime;
public
constructor create;
published
[ADateTime]
property Day:TDateTime read FDatetime write FdateTime;
end;
constructor ADateTimeAttribute.Create(const aArg: TDateTime);
begin
FArg := aArg;
end;
constructor ADateTimeAttribute.Create(const aArg: string );
var
MySettings:Tformatsettings;
begin
{ set up the date format according to how
the compiler formats %DATE% include }
MySettings :=DefaultFormatSettings;
MySettings.ShortDateFormat:='yyyymmdd';
MySettings.DateSeparator :='/';
{ Now convert }
FArg := StrToDateTime(aArg, MySettings);
end;
{ We query the rtti to set the value }
constructor TMyDateTimeClass.Create;
var
Context : TRttiContext;
AType : TRttiType;
Attribute : TCustomAttribute;
begin
inherited;
Context := TRttiContext.Create;
try
AType := Context.GetType(typeinfo(TMyDateTimeClass));
for Attribute in AType.GetAttributes do begin
if Attribute is ADateTimeAttribute then
FDateTime := ADateTimeAttribute(Attribute).Date;
end;
finally
Context.Free
end;
end;
var
Test:TMyDateTimeClass;
begin
Test := TMyDateTimeClass.Create;
try
writeln('Compile date is :',DateTimeToStr(Test.Day));
finally
test.free;
end;
end.
See Also
- FPC New Features Trunk
- http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes - Delphi Attributes reference
- http://delphi.about.com/od/oopindelphi/a/delphi-attributes-understanding-using-attributes-in-delphi.htm Delphi Tutorial on property attributes