8.4 Ограничения дженериков

На верх  Назад  Вперёд

Диаграмма в разделе 8.1 Введение, показывает, что список шаблонов для типа может иметь дополнительные спецификаторы для типов. Это особенно полезно для типов объектов: если тип шаблона должен быть унаследован от определенного класса, то это может быть указано в списке шаблонов:

{$mode objfpc}

{$h+}

uses sysutils, classes;

 

Type

  generic TList<_T : TComponent> = class(TObject)

  public

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

  Public

    data : _T;

    procedure Add(item: _T);

    procedure Sort(compare: TCompareFunc);

  end;

Учитывая приведенное выше описание, следующий код будет скомпилирован:

TPersistentList = specialize TList<TComponent>;

Но не будет компилироваться это

TPersistentList = specialize TList<TPersistent>;

Компилятор вернёт ошибку:

Error: Incompatible types: got "TPersistent" expected "TComponent"

Ошибка: Несовместимые типы: получен "TPersistent" вместо ожидаемого "TComponent"

Можно сгруппировать вместе несколько типов:

Type

  generic TList<Key1,Key2 : TComponent; Value1 : TObject> = class(TObject)

Кроме того, можно указать более одного идентификатора типа для ограничений типа класса и интерфейса. Если указан класс, то компилятор определяет подойдёт или нет такой дженерик для использования как шаблона для типа.

Type

  generic TList<T: TComponent, IEnumerable> = class(TObject)

Класс используемый для специализации T должен наследоваться от TComponent и реализовывать интерфейс IEnumerable.

Если указан интерфейс, то шаблон для типа должен реализовать этот интерфейс, но он может быть и потомком этого интерфейса:

Type

  generic TGenList<T: IEnumerable> = class(TObject)

 

  IMyEnum = Interface (IEnumerable)

    Procedure DoMy;

  end;

 

  TList = specialize TGenList<IMyEnum>;

  TSomeList = Specialize TGenList<TList>;

Можно указать несколько интерфейсов, в этом случае класс должен реализовать все перечисленные интерфейсы: можно смешать одно имя класса с несколькими именами интерфейсов.

Если не действуют ограничения для типа, то компилятор будет считать, что типы шаблонов не совместимы по присваиванию.

Это особенно важно, когда дженерик содержит перегруженные методы. Учитывая следующее обобщение для типов:

type

  generic TTest<T1, T2> = class

    procedure Test(aArg: LongInt);

    procedure Test(aArg: T1);

    procedure Test(aArg: T2);

  end;

При специализации написанное выше будет компилироваться, если T1 и T2 это два различных типа, и ни один не является LongInt. Такое выражение должно  компилироваться:

T1 = specialize TTest<String, TObject>;

Но следующих два выражения не компилируются:

T2 = specialize TTest<String, String>;

или

T2 = specialize TTest<String, Longint>;