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;