Использование атрибутов и наследников TCustomAttribute в Delphi 2010

Источник: delphi2010
Александр Божко

В Delphi 2010 атрибуты были добавлены как функциональность языка. Они были доступны в Delphi Prism (для .Net) и вот теперь схожая функциональность появилась и для Win32.
Атрибуты являются способом закрепления метаданных за отдельно взятым типом или членом этого типа.
Они могут применяться во многих областях, и следующий код демонстрирует несколько вариантов использования атрибутов.

  // Объявление атрибута
  TAttrTest = class(TCustomAttribute)
  end;

  // Место, где вы можете использовать атрибуты
  [TAttrTest]
  TRec = Record
    [TAttrTest]
    value : String;
    [TAttrTest]
    procedure DoThis([TAttrTest]arg1: String);
  End;

  [TAttrTest]
  TMyEnum = (enOne,enTwo,enThree);

  [TAttrTest]
  TMySet = set of TMyEnum;

  [TAttrTest]
  TObj = class(TObject)
  private
    [TAttrTest]
    FID: Integer;
  public
    [TAttrTest]
    FName : String;
    [TAttrTest]
    property Id : Integer read FID write FID;
    [TAttrTest]
    constructor Create;
    [TAttrTest]
    destructor Destroy; override;
    [TAttrTest]
    procedure DoThis;
  end;

var
  [TAttrTest]
  I : Integer;

Так как же работают атрибуты в Delphi 2010?
Атрибуты должны наследоваться от TCustomAttribute, и если вы посмотрите на описание TCustomAttribute, то вы не найдете там ничего особенного.

{Базовый класс для всех пользовательских атрибутов. Экземпляры атрибутов создаются RTTI модулем,
и являются собственностью тех членов, к которым они применяются}
TCustomAttribute = class(TObject)
end;

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

Type
  TAttrTest2 = class(TObject)
  private
     FId : Integer;
  public
     constructor Create(aID : Integer);
     property ID : Integer read FID write FID;
  end;

  [TAttrTest2(123)]
  TMyObject = Class(TObject)
  end;

Это сделано исключительно для того, что бы объявить Атрибут и "украсить" им ваш тип. Доступ к значению атрибутов осуществляется с помощью rtti.pas, общие основы того как это работает я описал в предыдущем посте.
Для всех элементов, которые могут иметь атрибуты, имеется методом .GetAttributes(), который возвращает массив атрибутов, связанных с соответствующим элементом.

Следующий код показывает, как осуществлять доступ к атрибутам.

program Project10;

{$APPTYPE CONSOLE}

uses
  SysUtils, RTTI;
type
  TAttrTest2 = class(TCustomAttribute)
  private
     FId : Integer;
  public
     constructor Create(aID : Integer);
     property ID : Integer read FID write FID;
  end;

  [TAttrTest2(1)]
  [TAttrTest2(2)]
  [TAttrTest2(3)]
  TMyObject = Class(TObject)
  end;

{ TAttrTest2 }

constructor TAttrTest2.Create(aID: Integer);
begin
 FID := aId;
end;

var
 c : TRttiContext;
 t : TRttiType;
 a : TCustomAttribute;
begin
   c := TRttiContext.Create;
   try
     t := c.GetType(TMyObject);
     for a in  t.GetAttributes do
     begin
       Writeln((a as TAttrTest2).ID);
     end;
   finally
     c.Free
   end;
   readln;
end.

Результат:

1
2
3
Кроме того, атрибуты имеют несколько других специфических особенностей, реализованных в компиляторе.
Допустим, у вас есть атрибут, поименованный следующим образом…

type
TestAttribute = class(TCustomAttribute)
end;

// На него можно сослаться двумя различными способами:

[TestAttribute]
TExample = class(Tobject)
end;

[Test]
TExample2 = class(TObject)
end;

Компилятор ищет соответствующий тип, и если не находит, то автоматически добавляет "Attribute" к имени, и снова начинает искать. Это сделано для того, что бы сымитировать поведение .Net.
Компилятор так же реализует несколько специфическую поддержку типов, позволяющую вам легко получать TRttiType через указатель pTypeInfo.

uses
  SysUtils, Rtti;

type
  TestAttribute = class(TCustomAttribute)
  public
    constructor Create(aType : TRttiType);
  end;

  [Test(typeinfo(Integer))]
  TEmployee = class(TObject)
  end;

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=22095