Generics/ru
│
English (en) │
français (fr) │
한국어 (ko) │
polski (pl) │
русский (ru) │
Вступление
FPC имеет официальную поддержку дженериков(обобщений) в синтаксисе {$mode ObjFPC}
, начиная с версии 2.2, и {$mode Delphi}
синтаксис начиная с версии 2.6.0.
Дженерики иногда называют параметризованными типами.
Причина, по которой FPC поддерживает два разных диалекта, Objfpc и Delphi, заключается в том, что у FPC были дженерики до Delphi.
Можно использовать модули, написанные в синтаксисе Objfpc, в других модулях, использующих синтаксис Delphi, и наоборот.
Free Generics Library или FGL является нативной реализацией class templates, написанной в обобщенном синтаксисе Objfpc.
Rtl-generics package является реализацией class templates, написанной в синтаксисе дженериков Delphi, и пытается быть совместимым с библиотекой дженериков Delphi. Этот пакет является стандартным в FPC 3.1.1.+, но есть версия, доступная для FPC 3.0.4.
Обе [библиотеки], FGL и rtl-generics могут использоваться в обоих режимах.
Модуль fgl
Самый простой способ начать работу с дженериками - это использовать fgl unit, который является модулем-прототипом для классов дженериков базовой системы. Пока он содержит несколько основных классов:
- TFPGList
- TFPGObjectList
- TFPGInterfacedObjectList
- TFPGMap
Как начать
В следующем простом примере показано, как хранить несколько экземпляров определенного пользователем класса в списке:
{$mode objfpc}
uses fgl;
type
TMyClass = class(TObject)
fld1 : string;
end;
TMyList = specialize TFPGObjectList<TMyClass>;
var
list : TMyList;
c : TMyClass;
begin
// создаем список и добавляем элемент
list := TMyList.Create;
c := TMyClass.Create;
c.fld1 := 'c1';
list.Add(c);
// получаем элемент из списка
c := list[0];
Пользовательские классы дженериков
Если дженерики, определенные в модуле fgl, не соответствуют вашим потребностям, вам может потребоваться определить свои собственные классы дженериков с нуля, используя базовые языковые примитивы.
Класс-дженерик определяется с помощью keyword (ключевого слова) generic перед именем класса и используется в объявлении класса:
type
generic TList<T> = class
Items: array of T;
procedure Add(Value: T);
end;
Пример реализации класса-дженерика:
implementation
procedure TList.Add(Value: T);
begin
SetLength(Items, Length(Items) + 1);
Items[Length(Items) - 1] := Value;
end;
Класс-дженерик может быть просто специализирован для определенного типа с помощью ключевого слова specialize.
Type
TIntegerList = specialize TList<Integer>;
TPointerList = specialize TList<Pointer>;
TStringList = specialize TList<string>;
Другие пункты
- Компилятор анализирует общий тип, но вместо генерации кода он сохраняет все токены в буфере токенов внутри файла PPU.
- Компилятор разбирает специализацию; для этого он загружает буфер токенов из файла PPU и анализирует его снова. Он заменяет общие параметры (в большинстве примеров "T") конкретным заданным типом (например, LongInt, TObject).
Код в основном выглядит так, как будто тот же класс был написан как общий, но с заменой T на данный тип.
Поэтому в теории не должно быть различий в скорости между «нормальным» классом и классом-дженериком.
Примеры
Пример использования дженериков для написания функции gmax(), которая принимает максимум две еще-не-типизированные переменные. Обратите внимание, что функции имеют пространство имен по имени класса. Недостатком может быть то, что дженерики не могут быть перегружены.
program UseGenerics;
{$mode objfpc}{$H+}
type
generic TFakeClass<_GT> = class
class function gmax(a,b: _GT):_GT;
end;
TFakeClassInt = specialize TFakeClass<integer>;
TFakeClassDouble = specialize TFakeClass<double>;
class function TFakeClass.gmax(a,b: _GT):_GT;
begin
if a > b then
result := a
else
result := b;
end;
begin
// показать большее из двух целых [чисел]
writeln( 'Integer GMax:', TFakeClassInt.gmax( 23, 56 ) );
// показать большее из двух [чисел] с плавающей точкой
writeln( 'Double GMax:', TFakeClassDouble.gmax( 23.89, 56.5) );
readln();
end.