Difference between revisions of "Custom Attributes/ru"

From Lazarus wiki
Jump to navigationJump to search
 
(4 intermediate revisions by the same user not shown)
Line 11: Line 11:
 
Атрибут используется для связывания определенных метаданных с классом. Например, вы можете использовать атрибут, чтобы пометить класс именем соответствующей таблицы базы данных или аннотировать класс веб-службы строкой, определяющей его базовый путь.
 
Атрибут используется для связывания определенных метаданных с классом. Например, вы можете использовать атрибут, чтобы пометить класс именем соответствующей таблицы базы данных или аннотировать класс веб-службы строкой, определяющей его базовый путь.
  
==How are attributes declared?==
+
==Как объявляются атрибуты?==
Attributes are simply classes that descend from the new System type
 
TCustomAttribute. The constructors of such TCustomAttribute descendants are the most important feature to implement since they are used to pass additional parameters to the attribute (such as the associated database table name, or the base path in the above examples).
 
  
'''Important''': If you want to use your attribute type without any arguments then you ''must'' declare a parameterless constructor yourself as the one of ''TCustomAttribute'' is private.
+
Атрибуты — это просто классы, происходящие от нового системного типа <code>TCustomAttribute</code>. Конструкторы таких потомков <code>TCustomAttribute</code> являются наиболее важной функцией для реализации, поскольку они используются для передачи дополнительных параметров атрибуту (таких, как имя связанной таблицы базы данных или базовый путь в приведенных выше примерах).
  
==How are attributes used?==
+
'''Важно''': если вы хотите использовать свой тип атрибута без каких-либо аргументов, вы ''должны'' самостоятельно объявить конструктор без параметров, поскольку один из <code>TCustomAttribute</code> является закрытым (private).
  
Attributes are bound to a type or property by specifying at least one
+
==Как используются атрибуты?==
attribute clause ahead of the type or property. For a type the attribute is specified in the
 
type definition (such as a class, record, or enum declaration) or in a unique
 
type redeclaration (e.g. "TLongInt = type LongInt"). Mere type renames
 
(e.g. "TLongInt = LongInt") are not allowed.
 
  
Attribute clauses are only available if the new modeswitch
+
Атрибуты привязываются к типу или свойству путем указания хотя бы одного предложения атрибута перед типом или свойством. Для типа атрибут указывается в определении типа (например, в объявлении класса, записи или перечисления) или в повторном объявлении уникального типа (например, <code>TLongInt = type LongInt</code>). Простое переименование типов (например, <code>TLongInt = LongInt</code>) не допускается.
PREFIXEDATTRIBUTES is set, which is the default in mode Delphi and
 
DelphiUnicode.
 
  
The syntax of an attribute clause is as follows:
+
Предложения атрибутов доступны только в том случае, если установлен новый переключатель режимов <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>
  
The IDENTIFIER is the name of the attribute class.  
+
''IDENTIFIER'' — это имя класса атрибута. Если вы называете класс атрибута заканчивающимся на "Attribute" (случай суффикса "Attribute" не имеет значения), то имя может использоваться впоследствии без суффикса "Attribute". Таким образом, <code>TMyAttribute</code> и <code>TMy</code> являются эквивалентными альтернативами в следующем примере:
If you name the attribute class to end with "Attribute" (the case of the "attribute" suffix is immaterial) then the name may be used subsequently without the "Attribute" suffix. So TMyAttribute and TMy are equivalent alternatives in the following example:
 
  
 
<source lang="delphi">
 
<source lang="delphi">
Line 97: Line 91:
 
</source>
 
</source>
  
===Querying attributes===
+
===Запрос атрибутов===
  
Attributes can be accessed by both the TypInfo and Rtti units.
+
Атрибуты могут быть доступны как модулю <code>TypInfo</code>, так и модулю <code>Rtti</code>.
  
For the TypInfo unit the ways to access attributes are as follows:
+
Для модуля <code>TypInfo</code> доступны следующие способы доступа к атрибутам:
  
For types:
+
Для типов:
* use the AttributesTable field in TTypeData
+
* используйте поле <code>AttributesTable</code> в <code>TTypeData</code>
* use GetAttributeTable on a PTypeInfo
+
* использовать <code>GetAttributeTable</code> для <code>PTypeInfo</code>
* use GetAttribute on the attribute table together with an index to get a TCustomAttribute instance
+
* используйте <code>GetAttribute</code> в таблице атрибутов вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code>
  
For properties:
+
Для свойств:
* use the AttributesTable of TPropInfo
+
* используйте <code>AttributesTable</code> из <code>TPropInfo</code>
* use GetAttribute on the attribute table together with an index to get a TCustomAttribute instance
+
* используйте <code>GetAttribute</code> в таблице атрибутов вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code>
* use GetPropAttribute on the PPropInfo together with an index to get a TCustomAttribute instance
+
* используйте <code>GetPropAttribute</code> в <code>PPropInfo</code> вместе с индексом, чтобы получить экземпляр <code>TCustomAttribute</code>
  
For the Rtti unit the ways to access attributes are as follows:
+
Для модуля <code>Rtti</code> доступны следующие способы доступа к атрибутам:
  
For types:
+
Для типов:
* use GetAttributes on the TRttiType of the type in question
+
* используйте <code>GetAttributes</code> для <code>TRttiType</code> рассматриваемого типа
  
For properties:
+
Для свойств:
* use GetAttributes on the TRttiProperty of the property in question
+
* используйте <code>GetAttributes</code> для <code>TRttiProperty</code> рассматриваемого свойства
  
==The attributes feature's compatibility with Delphi==
+
==Совместимость функций атрибутов с Delphi==
  
The feature itself is Delphi compatible with the proviso that FPC is much more
+
Сама функция совместима с Delphi при условии, что FPC гораздо более неумолим в отношении несвязанных свойств. Если класс атрибута неизвестен или предложения атрибута не связаны с допустимым типом или свойством, компилятор выдаст ошибку.
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
+
RTTI от FPC не считается совместимым с Delphi. Однако он охватывает ту же функциональность. В отличие от Delphi (который использует Invoke для создания экземпляра атрибута), FPC использует функцию конструктора. Преимущество реализации FPC заключается в том, что она работает на системах, которые не имеют полной поддержки Invoke.
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
+
Кроме того, использование переключателя режима ''PREFIXEDATTRIBUTES'' отключает разделы директив для функций, методов и типов процедур/методов.
directive clauses for functions, methods and procedure/method types.
+
 
The following is not allowed any more with the PREFIXEDATTRIBUTES modeswitch enabled:
+
Следующее больше не разрешено при включенном переключателе режимов ''PREFIXEDATTRIBUTES'':
  
 
<source lang="delphi">
 
<source lang="delphi">
Line 142: Line 130:
 
end;
 
end;
 
</source>
 
</source>
== Complete example ==
+
 
 +
== Полный пример ==
 
<source lang="Delphi">program testattributes;
 
<source lang="Delphi">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+}
 
{$mode delphi}{$H+}{$M+}
{$warn 5079 off} { turn warning experimental off }
+
{$warn 5079 off} { отключаем экспериментальное предупреждение }
 
uses
 
uses
 
   sysutils, typinfo, rtti, classes;
 
   sysutils, typinfo, rtti, classes;
 
   
 
   
 
type
 
type
   {A custom attribute to decorate a class with a certain date }
+
   {Пользовательский атрибут для украшения класса с определенной датой }
 
   ADateTimeAttribute = class(TCustomAttribute)
 
   ADateTimeAttribute = class(TCustomAttribute)
 
   private
 
   private
 
     FArg:TDateTime;
 
     FArg:TDateTime;
 
   public
 
   public
   { Just to show a Custom Attribute can have mutiple constructors }
+
   { Просто чтобы показать, что пользовательский атрибут может иметь несколько конструкторов }
 
     constructor Create(const aArg: TDateTime);overload;
 
     constructor Create(const aArg: TDateTime);overload;
     { We can use the predefined %DATE% compiler include
+
     { Мы можем использовать предопределенное включение компилятора %DATE%
       In the context of a custom attribute we need a
+
       В контексте пользовательского атрибута нам нужно
       constant expression for the default parameter }
+
       постоянное выражение для параметра по умолчанию}
 
     constructor Create(const aArg: String = {$I %DATE%});overload;
 
     constructor Create(const aArg: String = {$I %DATE%});overload;
 
     published
 
     published
Line 175: Line 164:
 
   end;
 
   end;
 
   
 
   
   { A datetime class that is decorated with our custom attribute    }
+
   { Класс datetime, украшенный нашим пользовательским атрибутом                          }
   { Note you can leave out 'Attribute', the compiler resolves it    }
+
   { Обратите внимание, что вы можете не указывать 'Attribute', компилятор разрешает это. }
   { [ADateTime(21237.0)] uses first constructor,displays some date in 1958 }
+
   { [ADateTime(21237.0)] использует первый конструктор, отображая дату в 1958 году      }
 
   
 
   
   {This calls the second constructor }
+
   {Это вызывает второй конструктор }
 
   [ADateTime]
 
   [ADateTime]
 
   TMyDateTimeClass = class
 
   TMyDateTimeClass = class
Line 200: Line 189:
 
     MySettings:Tformatsettings;
 
     MySettings:Tformatsettings;
 
   begin
 
   begin
     { set up the date format according to how
+
     { настраиваем формат даты в соответствии с тем, как
       the compiler formats %DATE% include }
+
       задан включенный формат компилятора %DATE% }
 
     MySettings :=DefaultFormatSettings;
 
     MySettings :=DefaultFormatSettings;
 
     MySettings.ShortDateFormat:='yyyymmdd';
 
     MySettings.ShortDateFormat:='yyyymmdd';
 
     MySettings.DateSeparator :='/';
 
     MySettings.DateSeparator :='/';
     { Now convert }
+
     { Теперь конвертируем }
 
     FArg := StrToDateTime(aArg, MySettings);
 
     FArg := StrToDateTime(aArg, MySettings);
 
   end;
 
   end;
 
    
 
    
   { We query the rtti to set the value }
+
   { Мы запрашиваем rtti, чтобы задать значение }
 
   constructor TMyDateTimeClass.Create;
 
   constructor TMyDateTimeClass.Create;
 
   var
 
   var
Line 235: Line 224:
 
   Test := TMyDateTimeClass.Create;
 
   Test := TMyDateTimeClass.Create;
 
   try
 
   try
     writeln('Compile date is :',DateTimeToStr(Test.Day));
+
     writeln('Дата компиляции :',DateTimeToStr(Test.Day));
 
   finally
 
   finally
 
     test.free;
 
     test.free;
Line 241: Line 230:
 
end.</source>
 
end.</source>
  
==See Also==
+
==См.также==
 
* [[FPC New Features Trunk]]
 
* [[FPC New Features Trunk]]
* http://docwiki.embarcadero.com/RADStudio/XE6/en/Overview_of_Attributes - Delphi Attributes reference
+
* 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 Tutorial on property attributes
+
* 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 (информация о типе времени выполнения).

Light bulb  Примечание: В настоящее время (по состоянию на октябрь 2020 г.) эта функция доступна только в транке FPC (также известной как 3.3.1).


Для чего можно использовать атрибуты?

Атрибут используется для связывания определенных метаданных с классом. Например, вы можете использовать атрибут, чтобы пометить класс именем соответствующей таблицы базы данных или аннотировать класс веб-службы строкой, определяющей его базовый путь.

Как объявляются атрибуты?

Атрибуты — это просто классы, происходящие от нового системного типа 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.

См.также