Generics/ru

From Free Pascal wiki

English (en) français (fr) 한국어 (ko) polski (pl) русский (ru)

Вступление

FPC имеет официальную поддержку дженериков(обобщений) в синтаксисе Objfpc начиная с версии 2.2. и 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>;

Другие пункты

  1. Компилятор анализирует общий тип, но вместо генерации кода он сохраняет все токены в буфере токенов внутри файла PPU.
  2. Компилятор разбирает специализацию; для этого он загружает буфер токенов из файла 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.

См. также

Внешние ссылки