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>; |