Sign in Join
Wiki copytemporary

Revision 2 of 2, by WikiArchive on 11-11-2026. The page as it is now.

Generics/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, который является модулем-прототипом для классов дженериков базовой системы. Пока он содержит несколько основных классов:

Как начать

В следующем простом примере показано, как хранить несколько экземпляров определенного пользователем класса в списке:

pascal
{$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 перед именем класса и используется в объявлении класса:

pascal
type
      generic TList<T> = class
        Items: array of T;
        procedure Add(Value: T);
      end;

Пример реализации класса-дженерика:

pascal
implementation
    
    procedure TList.Add(Value: T);
    begin
      SetLength(Items, Length(Items) + 1);
      Items[Length(Items) - 1] := Value;
    end;

Класс-дженерик может быть просто специализирован для определенного типа с помощью ключевого слова specialize.

pascal
Type  
      TIntegerList = specialize TList<Integer>;
      TPointerList = specialize TList<Pointer>;
      TStringList = specialize TList<string>;

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

  1. Компилятор анализирует общий тип, но вместо генерации кода он сохраняет все токены в буфере токенов внутри файла PPU.
  2. Компилятор разбирает специализацию; для этого он загружает буфер токенов из файла PPU и анализирует его снова. Он заменяет общие параметры (в большинстве примеров "T") конкретным заданным типом (например, LongInt , TObject).

Код в основном выглядит так, как будто тот же класс был написан как общий, но с заменой T на данный тип.

Поэтому в теории не должно быть различий в скорости между «нормальным» классом и классом-дженериком.

Примеры

Пример использования дженериков для написания функции gmax(), которая принимает максимум две еще-не-типизированные переменные. Обратите внимание, что функции имеют пространство имен по имени класса. Недостатком может быть то, что дженерики не могут быть перегружены.

pascal
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.

См. также

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


From the Free Pascal wiki, page "Generics/ru", as the Internet Archive kept it on 2025-04-18, last changed on 2024-09-11. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.