Difference between revisions of "Custom Attributes/ru"
Line 115: | Line 115: | ||
* используйте <code>GetAttributes</code> для <code>TRttiProperty</code> рассматриваемого свойства | * используйте <code>GetAttributes</code> для <code>TRttiProperty</code> рассматриваемого свойства | ||
− | == | + | ==Совместимость функций атрибутов с Delphi== |
− | + | Сама функция совместима с Delphi при условии, что FPC гораздо более неумолим в отношении несвязанных свойств. Если класс атрибута неизвестен или предложения атрибута не связаны с допустимым типом или свойством, компилятор выдаст ошибку. | |
− | |||
− | |||
− | |||
− | FPC | + | RTTI от FPC не считается совместимым с Delphi. Однако он охватывает ту же функциональность. В отличие от Delphi (который использует Invoke для создания экземпляра атрибута), FPC использует функцию конструктора. Преимущество реализации FPC заключается в том, что она работает на системах, которые не имеют полной поддержки Invoke. |
− | |||
− | |||
− | |||
− | + | Кроме того, использование переключателя режима ''PREFIXEDATTRIBUTES'' отключает разделы директив для функций, методов и типов процедур/методов. | |
− | + | ||
− | + | Следующее больше не разрешено при включенном переключателе режимов ''PREFIXEDATTRIBUTES'': | |
<source lang="delphi"> | <source lang="delphi"> | ||
Line 136: | Line 130: | ||
end; | end; | ||
</source> | </source> | ||
+ | |||
== Complete example == | == Complete example == | ||
<source lang="Delphi">program testattributes; | <source lang="Delphi">program testattributes; |
Revision as of 23:02, 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
рассматриваемого свойства
Совместимость функций атрибутов с Delphi
Сама функция совместима с Delphi при условии, что FPC гораздо более неумолим в отношении несвязанных свойств. Если класс атрибута неизвестен или предложения атрибута не связаны с допустимым типом или свойством, компилятор выдаст ошибку.
RTTI от FPC не считается совместимым с Delphi. Однако он охватывает ту же функциональность. В отличие от Delphi (который использует Invoke для создания экземпляра атрибута), FPC использует функцию конструктора. Преимущество реализации FPC заключается в том, что она работает на системах, которые не имеют полной поддержки Invoke.
Кроме того, использование переключателя режима PREFIXEDATTRIBUTES отключает разделы директив для функций, методов и типов процедур/методов.
Следующее больше не разрешено при включенном переключателе режимов PREFIXEDATTRIBUTES:
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