8.2 Определение дженерика классов

Вверх  Предыдущий  Следующий

Объявление дженерика очень похоже на определение типа, за исключением того, что он должен содержать список типов заполнителей (шаблонов), как показано на следующей синтаксической диаграмме:


Дженерик классов

801


Для классов, объектов, процедурных типов и расширенных записей, объявление дженерика должно сопровождаться его реализацией. Это то же самое, что и обычная реализация класса, кроме того что, имя  идентификатора шаблона, должен быть именем типа (класса, записи оди простого типа).

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

Ниже приведено допустимое определение дженерика:

Type

   generic TList<_T>=class(TObject)

     type public

       TCompareFunc = function(const Item1, Item2: _T): Integer;

     var public

       data : _T;

     procedure Add(item: _T);

     procedure Sort(compare: TCompareFunc);

   end;

Класс может быть реализован  следующим образом:

procedure TList.Add(item: _T);

begin

  data:=item;

end;

 

procedure TList.Sort(compare: TCompareFunc);

begin

  if compare(data, 20) <= 0 then halt(1);

end;

В этом объявлении и реализации есть некоторые особенности:

1.Заполнитель _T будет заменен именем уточнённого  типа, когда дженерик будет специализирован. Идентификатор _T не может использоваться для чего - нибудь другого, кроме заполнителя типа. Что значит, что следующий код неверен:

procedure TList.Sort(compare: TCompareFunc);

Var

   _t : integer;

 

begin

   //  какие-то действия

end;

2.Блок локальных типов содержит единственный тип TCompareFunc. Обратите внимание, что фактический тип  не известен при определения дженерика: определение содержит ссылку на заполнитель _T. Все остальные ссылки на идентификатор должны быть известны, когда определяется класс дженерика, а не когда дженерик специализируется.

3.Блок локальных переменных определяется следующим образом:

   generic TList<_T>=class(TObject)

     type public

       TCompareFunc = function(const Item1, Item2: _T): Integer;

   Public

     data : _T;

     procedure Add(item: _T);

     procedure Sort(compare: TCompareFunc);

   end;

Дженериками могут быть определены не только классы, но и другие типы:

{$mode objfpc}

{$INTERFACES CORBA}

type

   generic PlanarCoordinate<t> = record

     x,y : t;

   end;

 

   TScreenCoordinate = specialize PLanarCoordinate<word>;

   TDiscreteCoordinate = specialize PlanarCoordinate<integer>;

   TRealCoordinate = specialize PlanarCoordinate<extended>;

 

   generic TDistanceFunction<t> = function (x,y : t) : Extended of object;

 

   TScreenDistance = specialize TDistanceFunction<word>;

   TDiscreteDistance = specialize TDistanceFunction<integer>;

   TRealDistance = specialize TDistanceFunction<Extended>;

 

   generic TArray<t> = array of t;

 

   TMyIntegerArray = specialize TArray<integer>;

 

   generic IList<_T> = Interface

     Function GetItem(AIndex : Integer) : _T;

     Procedure SetItem(AIndex : Integer; AValue : _T);

     Function GetCount : Integer;

     Property Items [AIndex : Integer] : _T Read GetItem Write SetItem;

     Property Count : Integer Read GetCount;

   end;

 

   generic TList<_T>=class(TObject, specialize IList<_T>)

   public type

     TCompareFunc = function(const Item1, Item2: _T): Integer;

     Function GetItem(AIndex : Integer) : _T;

     Procedure SetItem(AIndex : Integer; AValue : _T);

     Function GetCount : Integer;

   Public

     data : _T;

     procedure Add(item: _T);

     procedure Sort(compare: TCompareFunc);

   end;

 

   generic TPointSet<t> = array of specialize PlanarCoordinate<t>;

 

   TScreenPointSet = specialize TPointSet<word>;

   TDiscretePointSet = specialize TPointSet<integer>;

   TRealPointSet = specialize TPointSet<extended>;

Примечание:

Несколько слов о зоне видимости. Типы шаблонов T или _T доступны в качестве strict private (строгих частных) типов. Это означает, что эти типы будут недоступны в классах-потомках, пока они не будут сделаны доступными с помощью  некоторых специальных механизмов (переопределения поля как protected (защищенного) или private (частного)), что и показано в следующем примере:

generic TList<_T>=class(TObject)

public type

   TItemType = _T;

end;