Difference between revisions of "Generics/ru"

From Lazarus wiki
Jump to navigationJump to search
 
(10 intermediate revisions by 2 users not shown)
Line 2: Line 2:
  
 
==Вступление==
 
==Вступление==
[[FPC]] имеет официальную поддержку дженериков в синтаксисе Objfpc начиная с версии 2.2. и Delphi-совместимый синтаксис начиная с версии 2.6.0. <br>
+
[[FPC]] имеет официальную поддержку дженериков(обобщений) в синтаксисе [[Mode_ObjFPC|<syntaxhighlight lang="pascal" inline>{$mode ObjFPC}</syntaxhighlight>]], начиная с версии 2.2, и [[Mode_Delphi|<syntaxhighlight lang="pascal" inline>{$mode Delphi}</syntaxhighlight>]] синтаксис начиная с версии 2.6.0. <br>
 
Дженерики иногда называют параметризованными типами. <br>
 
Дженерики иногда называют параметризованными типами. <br>
 
Причина, по которой FPC поддерживает два разных диалекта, Objfpc и Delphi, заключается в том, что у FPC были дженерики до Delphi.
 
Причина, по которой FPC поддерживает два разных диалекта, Objfpc и Delphi, заключается в том, что у FPC были дженерики до Delphi.
Line 21: Line 21:
 
В следующем простом примере показано, как хранить несколько экземпляров определенного пользователем класса в списке:
 
В следующем простом примере показано, как хранить несколько экземпляров определенного пользователем класса в списке:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
{$mode objfpc}
 
{$mode objfpc}
 
uses fgl;
 
uses fgl;
Line 46: Line 46:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Custom Generic Classes==
+
==Пользовательские классы дженериков==
If the generics defined in the fgl unit do not suit your needs, you may need to define your own generic classes from scratch using the underlying language primitives.
+
Если дженерики, определенные в модуле fgl, не соответствуют вашим потребностям, вам может потребоваться определить свои собственные классы дженериков с нуля, используя базовые языковые примитивы.
  
A generic class is defined using the [[keyword]] '''generic''' before the class name and use in class declaration:
+
Класс-дженерик определяется с помощью [[keyword]] (ключевого слова) '''generic''' перед именем класса и используется в объявлении класса:
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
type
 
type
 
   generic TList<T> = class
 
   generic TList<T> = class
Line 58: Line 59:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Example of generic class implementation:
+
Пример реализации класса-дженерика:
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
implementation
 
implementation
  
Line 70: Line 71:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
A generic class can be simply specialized for a particular type by using the '''specialize''' keyword.
+
Класс-дженерик может быть просто специализирован для определенного типа с помощью ключевого слова '''specialize'''.
<syntaxhighlight>
+
 
 +
<syntaxhighlight lang=pascal>
 
Type   
 
Type   
 
   TIntegerList = specialize TList<Integer>;
 
   TIntegerList = specialize TList<Integer>;
Line 78: Line 80:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Other Points ==
+
== Другие пункты ==
# The compiler parses a generic, but instead of generating code it stores all tokens in a token buffer inside the PPU file.
+
# Компилятор анализирует общий тип, но вместо генерации кода он сохраняет все токены в буфере токенов внутри файла PPU.
# The compiler parses a specialization; for this it loads the token buffer from the PPU file and parses that again. It replaces the generic parameters (in most examples "T") by the particular given type (e.g. '''LongInt''', '''TObject''').
+
# Компилятор разбирает специализацию; для этого он загружает буфер токенов из файла PPU и анализирует его снова. Он заменяет общие параметры (в большинстве примеров "T") конкретным заданным типом (например, '''LongInt''', '''TObject''').
The code basically appears as if the same class had been written as the generic but with T replaced by the given type.
+
Код в основном выглядит так, как будто тот же класс был написан как общий, но с заменой T на данный тип.
 +
 
 +
Поэтому в теории не должно быть различий в скорости между «нормальным» классом и классом-дженериком.
  
Therefore in theory there should be no speed differences between a "normal" class and a generic one.
+
== Примеры ==
 +
Пример использования дженериков для написания функции gmax(), которая принимает максимум две еще-не-типизированные переменные.
 +
Обратите внимание, что функции имеют пространство имен по имени класса. Недостатком может быть то, что дженерики не могут быть перегружены.
  
== Example ==
+
<syntaxhighlight lang=pascal>
{{:How to use generics}}
+
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.
 +
</syntaxhighlight>
  
==See also==
+
==См. также==
* [[Templates]]
+
* [[Templates/ru|Templates]]
 
* [[Generics proposals]]
 
* [[Generics proposals]]
 
* [[Data Structures, Containers, Collections]]
 
* [[Data Structures, Containers, Collections]]
  
==External links==
+
==Внешние ссылки==
 
* [http://www.freepascal.org/docs-html/ref/refch8.html Free Pascal: Reference guide - Generics]
 
* [http://www.freepascal.org/docs-html/ref/refch8.html Free Pascal: Reference guide - Generics]
 +
* [http://keeper89.blogspot.com/2011/07/delphi-1.html Используем дженерики в Delphi!]
 +
* [http://proghouse.ru/programming/49-delphi-generics Использование стандартных дженериков Delphi для работы с наборами данных]

Latest revision as of 17:13, 6 August 2022

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

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

  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.

См. также

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