Difference between revisions of "Custom Attributes/ru"
(6 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{LanguageBar|Custom Attributes}} | {{LanguageBar|Custom Attributes}} | ||
− | |||
− | |||
− | |||
− | + | Пользовательские атрибуты в настоящее время позволяют украшать определения типов и published свойства классов дополнительными метаданными, которые можно запрашивать с помощью RTTI (информация о типе времени выполнения). | |
− | |||
− | |||
− | |||
− | + | {{Note|В настоящее время (по состоянию на октябрь 2020 г.) эта функция доступна только в транке FPC (также известной как 3.3.1).}} | |
− | + | <br> | |
− | |||
− | + | ==Для чего можно использовать атрибуты?== | |
− | + | Атрибут используется для связывания определенных метаданных с классом. Например, вы можете использовать атрибут, чтобы пометить класс именем соответствующей таблицы базы данных или аннотировать класс веб-службы строкой, определяющей его базовый путь. | |
− | + | ==Как объявляются атрибуты?== | |
− | |||
− | |||
− | |||
− | |||
− | + | Атрибуты — это просто классы, происходящие от нового системного типа <code>TCustomAttribute</code>. Конструкторы таких потомков <code>TCustomAttribute</code> являются наиболее важной функцией для реализации, поскольку они используются для передачи дополнительных параметров атрибуту (таких, как имя связанной таблицы базы данных или базовый путь в приведенных выше примерах). | |
− | |||
− | |||
− | + | '''Важно''': если вы хотите использовать свой тип атрибута без каких-либо аргументов, вы ''должны'' самостоятельно объявить конструктор без параметров, поскольку один из <code>TCustomAttribute</code> является закрытым (private). | |
+ | ==Как используются атрибуты?== | ||
+ | |||
+ | Атрибуты привязываются к типу или свойству путем указания хотя бы одного предложения атрибута перед типом или свойством. Для типа атрибут указывается в определении типа (например, в объявлении класса, записи или перечисления) или в повторном объявлении уникального типа (например, <code>TLongInt = type LongInt</code>). Простое переименование типов (например, <code>TLongInt = LongInt</code>) не допускается. | ||
+ | |||
+ | Предложения атрибутов доступны только в том случае, если установлен новый переключатель режимов <code>PREFIXEDATTRIBUTES</code>, который используется по умолчанию в режимах Delphi и 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 96: | 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> рассматриваемого свойства |
− | == | + | ==Совместимость функций атрибутов с Delphi== |
− | + | Сама функция совместима с Delphi при условии, что FPC гораздо более неумолим в отношении несвязанных свойств. Если класс атрибута неизвестен или предложения атрибута не связаны с допустимым типом или свойством, компилятор выдаст ошибку. | |
− | |||
− | |||
− | |||
− | FPC | + | RTTI от FPC не считается совместимым с Delphi. Однако он охватывает ту же функциональность. В отличие от Delphi (который использует Invoke для создания экземпляра атрибута), FPC использует функцию конструктора. Преимущество реализации FPC заключается в том, что она работает на системах, которые не имеют полной поддержки Invoke. |
− | |||
− | |||
− | |||
− | + | Кроме того, использование переключателя режима ''PREFIXEDATTRIBUTES'' отключает разделы директив для функций, методов и типов процедур/методов. | |
− | + | ||
− | + | Следующее больше не разрешено при включенном переключателе режимов ''PREFIXEDATTRIBUTES'': | |
<source lang="delphi"> | <source lang="delphi"> | ||
Line 141: | Line 130: | ||
end; | end; | ||
</source> | </source> | ||
− | == | + | |
+ | == Полный пример == | ||
<source lang="Delphi">program testattributes; | <source lang="Delphi">program testattributes; | ||
{ | { | ||
− | + | Это простой пример использования настраиваемых атрибутов. | |
− | + | Класс использует настраиваемый атрибут для получения статической даты. | |
− | + | при запуске программы. Это только для демонстрации. | |
− | + | Это демо: | |
− | - | + | - создает |
− | - | + | - украшает |
− | - | + | - возвращает |
} | } | ||
{$mode delphi}{$H+}{$M+} | {$mode delphi}{$H+}{$M+} | ||
− | {$warn 5079 off} { | + | {$warn 5079 off} { отключаем экспериментальное предупреждение } |
uses | uses | ||
sysutils, typinfo, rtti, classes; | sysutils, typinfo, rtti, classes; | ||
type | type | ||
− | { | + | {Пользовательский атрибут для украшения класса с определенной датой } |
ADateTimeAttribute = class(TCustomAttribute) | ADateTimeAttribute = class(TCustomAttribute) | ||
private | private | ||
FArg:TDateTime; | FArg:TDateTime; | ||
public | public | ||
− | { | + | { Просто чтобы показать, что пользовательский атрибут может иметь несколько конструкторов } |
constructor Create(const aArg: TDateTime);overload; | constructor Create(const aArg: TDateTime);overload; | ||
− | { | + | { Мы можем использовать предопределенное включение компилятора %DATE% |
− | + | В контексте пользовательского атрибута нам нужно | |
− | + | постоянное выражение для параметра по умолчанию} | |
constructor Create(const aArg: String = {$I %DATE%});overload; | constructor Create(const aArg: String = {$I %DATE%});overload; | ||
published | published | ||
Line 174: | Line 164: | ||
end; | end; | ||
− | { | + | { Класс datetime, украшенный нашим пользовательским атрибутом } |
− | { | + | { Обратите внимание, что вы можете не указывать 'Attribute', компилятор разрешает это. } |
− | { [ADateTime(21237.0)] | + | { [ADateTime(21237.0)] использует первый конструктор, отображая дату в 1958 году } |
− | { | + | {Это вызывает второй конструктор } |
[ADateTime] | [ADateTime] | ||
TMyDateTimeClass = class | TMyDateTimeClass = class | ||
Line 199: | Line 189: | ||
MySettings:Tformatsettings; | MySettings:Tformatsettings; | ||
begin | begin | ||
− | { | + | { настраиваем формат даты в соответствии с тем, как |
− | + | задан включенный формат компилятора %DATE% } | |
MySettings :=DefaultFormatSettings; | MySettings :=DefaultFormatSettings; | ||
MySettings.ShortDateFormat:='yyyymmdd'; | MySettings.ShortDateFormat:='yyyymmdd'; | ||
MySettings.DateSeparator :='/'; | MySettings.DateSeparator :='/'; | ||
− | { | + | { Теперь конвертируем } |
FArg := StrToDateTime(aArg, MySettings); | FArg := StrToDateTime(aArg, MySettings); | ||
end; | end; | ||
− | { | + | { Мы запрашиваем rtti, чтобы задать значение } |
constructor TMyDateTimeClass.Create; | constructor TMyDateTimeClass.Create; | ||
var | var | ||
Line 234: | Line 224: | ||
Test := TMyDateTimeClass.Create; | Test := TMyDateTimeClass.Create; | ||
try | try | ||
− | writeln(' | + | writeln('Дата компиляции :',DateTimeToStr(Test.Day)); |
finally | finally | ||
test.free; | test.free; | ||
Line 240: | Line 230: | ||
end.</source> | end.</source> | ||
− | == | + | ==См.также== |
* [[FPC New Features Trunk]] | * [[FPC New Features Trunk]] | ||
− | * http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes - Delphi | + | * http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes - Справочник по атрибутам Delphi |
− | * http://delphi.about.com/od/oopindelphi/a/delphi-attributes-understanding-using-attributes-in-delphi.htm Delphi | + | * http://delphi.about.com/od/oopindelphi/a/delphi-attributes-understanding-using-attributes-in-delphi.htm Учебник Delphi по атрибутам свойств |
[[Category:FPC]] | [[Category:FPC]] |
Latest revision as of 23:16, 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;
Полный пример
program testattributes;
{
Это простой пример использования настраиваемых атрибутов.
Класс использует настраиваемый атрибут для получения статической даты.
при запуске программы. Это только для демонстрации.
Это демо:
- создает
- украшает
- возвращает
}
{$mode delphi}{$H+}{$M+}
{$warn 5079 off} { отключаем экспериментальное предупреждение }
uses
sysutils, typinfo, rtti, classes;
type
{Пользовательский атрибут для украшения класса с определенной датой }
ADateTimeAttribute = class(TCustomAttribute)
private
FArg:TDateTime;
public
{ Просто чтобы показать, что пользовательский атрибут может иметь несколько конструкторов }
constructor Create(const aArg: TDateTime);overload;
{ Мы можем использовать предопределенное включение компилятора %DATE%
В контексте пользовательского атрибута нам нужно
постоянное выражение для параметра по умолчанию}
constructor Create(const aArg: String = {$I %DATE%});overload;
published
property Date:TDateTime read Farg;
end;
{ Класс datetime, украшенный нашим пользовательским атрибутом }
{ Обратите внимание, что вы можете не указывать 'Attribute', компилятор разрешает это. }
{ [ADateTime(21237.0)] использует первый конструктор, отображая дату в 1958 году }
{Это вызывает второй конструктор }
[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
{ настраиваем формат даты в соответствии с тем, как
задан включенный формат компилятора %DATE% }
MySettings :=DefaultFormatSettings;
MySettings.ShortDateFormat:='yyyymmdd';
MySettings.DateSeparator :='/';
{ Теперь конвертируем }
FArg := StrToDateTime(aArg, MySettings);
end;
{ Мы запрашиваем rtti, чтобы задать значение }
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('Дата компиляции :',DateTimeToStr(Test.Day));
finally
test.free;
end;
end.
См.также
- FPC New Features Trunk
- http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes - Справочник по атрибутам Delphi
- http://delphi.about.com/od/oopindelphi/a/delphi-attributes-understanding-using-attributes-in-delphi.htm Учебник Delphi по атрибутам свойств