Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Дженерики: передача процедуры в качестве параметра / 25 сообщений из 108, страница 1 из 5
28.10.2018, 12:33
    #39724027
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Я пытаюсь написать бота. Больше нужна идея, нежели код и пример, но и примеры тоже будут полезны.
Важно, чтобы в итоге код получился легко масштабируемым, чтобы можно было легко добавлять обработчики (процедуры) новых команд.

Я так понимаю, что нужно создать какой-то список, где будет ключ: команда боту /start, /find, /найти, /Ещё что-то) и значение: название, соответствующей этому ключевому слову процедуре. Не писать же в процедуре входящих команд 100500 IF`ов?

Дженерики почти никогда не использовал. Использовал что-то простое для строк и объектов или чисел.
Буду благодарен за примеры и идеи.
Хоть как это правильно объявить, с чего начать?
...
Рейтинг: 0 / 0
28.10.2018, 12:45
    #39724034
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
что-то типа
Код: pascal
1.
2.
Var
  DicCmds: TDictionary<String, а здесь что>;


чтобы можно было потом подставить название процедуры в качестве параметра
и как это потом использовать?
...
Рейтинг: 0 / 0
28.10.2018, 12:47
    #39724035
asviridenkov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11что-то типа
Код: pascal
1.
2.
Var
  DicCmds: TDictionary<String, а здесь что>;


чтобы можно было потом подставить название процедуры в качестве параметра
и как это потом использовать?

type
TMyProc = function(const Command: string): string;
...
Рейтинг: 0 / 0
28.10.2018, 13:07
    #39724038
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Внутри секции ptivate пишу
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

...
...
Var
  DicCmds: TDictionary<String, TProcedure>;



так?
даже если разное количество параметров у процедур будет?
...
Рейтинг: 0 / 0
28.10.2018, 13:18
    #39724041
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11
Код: pascal
1.
Type botStart = procedure;


в итоге ругается компилятор
Incompatible types: 'regular procedure and method pointer'


Так правильно?
Код: pascal
1.
2.
Type
  TcmdStart = procedure of object;
...
Рейтинг: 0 / 0
28.10.2018, 13:24
    #39724045
ziv-2014
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11,
TMethod и Invoke на вскидку.
...
Рейтинг: 0 / 0
28.10.2018, 13:48
    #39724061
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
ziv-2014,
???
...
Рейтинг: 0 / 0
28.10.2018, 13:54
    #39724065
JayDi
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Лучше уж добавлять обработку команд через о дельный метод:

Код: pascal
1.
procedure registerCommand(const ACommand: string; const AWorker: procedure of object);
...
Рейтинг: 0 / 0
28.10.2018, 13:58
    #39724067
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
JaDi,

а дальше?
пока не понял идеи
...
Рейтинг: 0 / 0
28.10.2018, 14:01
    #39724069
JayDi
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
и вообще, в обработчик ведь надо параметры разные передавать. Поэтому сначала надо определиться с форматом этих параметров. Например, в виде json-строки.

Type TBotWorker = procedure(const AParams: string);

После чего получится
Код: pascal
1.
2.
3.
4.
5.
6.
7.
procedure registerCommand(const ACommand: string; const AWorker: TBotWorker);

...

registerCommand('start', this.ProcessStart);
registerCommand('find_by_id', this.ProcessFindByID);
registerCommand('find_by_name', this.ProcessFindByName);
...
Рейтинг: 0 / 0
28.10.2018, 14:03
    #39724070
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
JaDi, я пока не знаю, какие будут параметры и какого типа.
Мало того, заранее неизвестно, что напишет пользователь боту. Это может быть не только команда, но и простой текст, которые придётся распарсить и понять - какую процедуру обработки вызвать.
...
Рейтинг: 0 / 0
28.10.2018, 14:28
    #39724075
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11Внутри секции ptivate пишу
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
...
...
Type
    Type botStart = procedure;
    Type botFindByID = procedure (id: integer);
    Type botFindByPhone= procedure(const sPhone, sType: string);

...
...
Var
  DicCmds: TDictionary<String, TProcedure>;



так?
даже если разное количество параметров у процедур будет?

Уже давно пора писать
Код: pascal
1.
botStart = reference to procedure;


И там неважно что будет тогда, метод, процедура или анонимная процедура
...
Рейтинг: 0 / 0
28.10.2018, 15:27
    #39724081
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X-Cite
Код: pascal
1.
reference to procedure;



Тогда как объявить словарь?

Вот сделал так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

    Var      
      DicCmdRows: TDictionary<String, TProcedure>;
      cmdStart:  TBotProcRef;
      cmdDateTime: TBotProcRef;
      cmdFindByID: TBotProcRef1;




Получаю ошибку при компиляции:
Incompatible types: 'TProcedure' and 'TfmXXX.TBotProcRef'

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
procedure TfmMainTelegramBot.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TProcedure>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', cmdStart);
//  DicCmdRows.Add('/время', cmdDateTime);

end;
...
Рейтинг: 0 / 0
28.10.2018, 15:35
    #39724083
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
А если словарь объявлять так:
Код: pascal
1.
2.
3.
4.
5.
6.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

   Var
    DicCmdRows: TDictionary<String, TBotProcRef>;


То тогда придётся объявлять несколько разных словарей
...
Рейтинг: 0 / 0
28.10.2018, 15:47
    #39724087
defecator
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Навскидку

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
type
    { один параметр }
    TParam = record
      ParamName  : string[64] ;
      ParamValue : Variant ;
    end ;

    TParams = array of TParam ;

    TBotProcRef = procedure(const aParams : TParams) ;
...
Рейтинг: 0 / 0
28.10.2018, 15:51
    #39724088
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Я пока думаю, что всегда будет один параметр, т.е. в качестве этого одного параметра будет входящее написанное пользователем сообщение....
...
Рейтинг: 0 / 0
28.10.2018, 15:57
    #39724089
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11А если словарь объявлять так:
Код: pascal
1.
2.
3.
4.
5.
6.
    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);

   Var
    DicCmdRows: TDictionary<String, TBotProcRef>;



То тогда придётся объявлять несколько разных словарей

Ладно, продолжаю тормозить

rcvMessage - метод ресивера, который срабатывает при входящем сообщении
Код: pascal
1.
2.
3.
4.
procedure TfmMainXXX.rcvMessage(ASender: TObject; AMessage: ITgMessage);
begin
  if DicCmdRows.ContainsKey(AMessage.Text) then
    cmdStart := DicCmdRows[AMessage.Text];




И как запустить на выполнение cmdStart?


Сейчас объявлено так:

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
    procedure PcmdStart;
    procedure FillDicProcs;

    Type TBotProcRef  = reference to procedure;
    Type TBotProcRef1 = reference to procedure (id: integer);
    Type TBotProcRef2 = reference to procedure(const sPhone, sType: string);



    Var
      sUserDocsKvxTelegramBotPath: string;
      DicCmdRows: TDictionary<String, TBotProcRef>;
      cmdStart:  TBotProcRef;
      cmdDateTime: TBotProcRef;
      cmdFindByID: TBotProcRef1;


...
...
...

// наполняем словарь ключами и ссылками на процедуры-обработчики команд
procedure TfmMainXXX.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TBotProcRef>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', cmdStart);
  DicCmdRows.Add('/время', cmdDateTime);

end;

// в итоге должен сработать метод PcmdStart, когда пользователь прислал /start
procedure TfmMainXXX.PcmdStart;
begin
// обработка команды /Start
  ShowMessage('/Start');
end;
...
Рейтинг: 0 / 0
28.10.2018, 16:04
    #39724090
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11
Код: pascal
1.
2.
 DicCmdRows.Add('/Start', cmdStart);
  DicCmdRows.Add('/время', cmdDateTime);



здесь я ошибся, нужно подставлять имена реальных процедур

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
procedure TfmMainXXX.PcmdStart;
begin
// обработка команды /Start
  ShowMessage('/Start');
end;


procedure TfmMainXXX.PcmdShowTime;
begin
// обработка команды /время
  ShowMessage('/время');
end;

// заполняем словарь
procedure TfmMainTelegramBot.FillDicProcs;
begin
  if Not Assigned(DicCmdRows) then
    DicCmdRows := TDictionary<string, TBotProcRef>.Create
  else
    DicCmdRows.Clear;

  DicCmdRows.Add('/Start', PcmdStart);
  DicCmdRows.Add('/время', PcmdShowTime);

end;


procedure TfmMainXXX.rcvMessage(ASender: TObject; AMessage: ITgMessage);
begin
  if DicCmdRows.ContainsKey(AMessage.Text) then
    BotProcRef := DicCmdRows[AMessage.Text];

// просто написать, так правильно?
 BotProcRef;



и как теперь выполнить cmdStart, в котором живёт
...
Рейтинг: 0 / 0
28.10.2018, 16:06
    #39724091
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Да, работает, бот получил эти 2 команды и запустил нужные процедуры.
...
Рейтинг: 0 / 0
28.10.2018, 16:20
    #39724095
JayDi
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Пример реализации: менеджер ботов, два боты со своими командами, обработка и вывод результата. Можно дальше дорабатывать, например, добавить пользовательские сессии в менеджере и хранить там состояние о об активном боте и введенных данных.

Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
type
  // обработчик одной команды
  TBotWorker = procedure(const AParams: string; var AResults: string) of object;

  TBotsManager = class;
  TBot = class
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); virtual; abstract;
  end;

  // менеджер для управления ботами, командами и обработки
  TBotsManager = class
  private
    BotsList: TObjectList<TBot>;
    CommandsList: TDictionary<string, TBotWorker>;
    procedure ProcessCommand(const ACommandLine: string);
  public
    constructor Create; overload;
    destructor Destroy; override;

    procedure RegisterBot(ABot: TBot);
    procedure RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
  end;

  // бот 1
  TBotDialogHello = class(TBot)
  private
    procedure ProcessHello(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

  // бот 2
  TBotDialogSearch = class(TBot)
  private
    procedure ProcessStart(const AParams: string; var AResults: string);
    procedure ProcessFindByName(const AParams: string; var AResults: string);
    procedure ProcessFindByID(const AParams: string; var AResults: string);

    procedure ProcessFind(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;




Полностью реализация:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
unit Unit14;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  System.Generics.Collections;

type
  TForm14 = class(TForm)
    Button_Send: TButton;
    edit_Command: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button_SendClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
  public
  end;

type
  // обработчик одной команды
  TBotWorker = procedure(const AParams: string; var AResults: string) of object;

  TBotsManager = class;
  TBot = class
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); virtual; abstract;
  end;

  // менеджер для управления ботами, коммандами и обработки
  TBotsManager = class
  private
    BotsList: TObjectList<TBot>;
    CommandsList: TDictionary<string, TBotWorker>;
    procedure ProcessCommand(const ACommandLine: string);
  public
    constructor Create; overload;
    destructor Destroy; override;

    procedure RegisterBot(ABot: TBot);
    procedure RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
  end;

  // бот 1
  TBotDialogHello = class(TBot)
  private
    procedure ProcessHello(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

  // бот 2
  TBotDialogSearch = class(TBot)
  private
    procedure ProcessStart(const AParams: string; var AResults: string);
    procedure ProcessFindByName(const AParams: string; var AResults: string);
    procedure ProcessFindByID(const AParams: string; var AResults: string);

    procedure ProcessFind(const AParams: string; var AResults: string);
  public
    procedure RegisterCommands(const ABotsManager: TBotsManager); override;
  end;

var
  Form14: TForm14;

implementation

{$R *.dfm}

var
  BotsManager: TBotsManager;

{ TBotManager }

constructor TBotsManager.Create;
begin
  inherited;

  Self.BotsList := TObjectList<TBot>.Create;
  Self.CommandsList := TDictionary<string, TBotWorker>.Create();
end;

destructor TBotsManager.Destroy;
begin
  FreeAndNil(Self.CommandsList);
  FreeAndNil(Self.BotsList);

  inherited;
end;

procedure TBotsManager.ProcessCommand(const ACommandLine: string);
var
  commandName: string;
  commandParams: string;
  foundedWorker: TBotWorker;
  s: string;
  res: string;
begin
  s := Trim(ACommandLine);
  if s.IndexOf(' ') >= 0 then
  begin
    commandName := AnsiLowerCase(trim(s.Substring(0, s.IndexOf(' '))));
    commandParams := trim(s.Substring(commandName.Length + 1));
  end
  else
  begin
    commandName := AnsiLowerCase(s);
    commandParams := '';
  end;

  if Self.CommandsList.TryGetValue(commandName, foundedWorker) then
  begin
    res := '';
    foundedWorker(commandParams, res);
    ShowMessage(res);
  end
  else
  begin
    ShowMessage('unknown command: ' + commandName);
  end;
end;

procedure TBotsManager.RegisterBot(ABot: TBot);
begin
  Self.BotsList.Add(ABot);

  ABot.RegisterCommands(Self);
end;

procedure TBotsManager.RegisterCommand(const ACommand: string; const AWorker: TBotWorker);
begin
  Self.CommandsList.Add(ACommand, AWorker);
end;

procedure TForm14.Button_SendClick(Sender: TObject);
begin
  BotsManager.ProcessCommand(edit_Command.Text);
end;

procedure TForm14.FormCreate(Sender: TObject);
begin
  BotsManager := TBotsManager.Create;
  BotsManager.RegisterBot(TBotDialogHello.Create);
  BotsManager.RegisterBot(TBotDialogSearch.Create);
end;

procedure TForm14.FormDestroy(Sender: TObject);
begin
  FreeAndNil(BotsManager);
end;

procedure TBotDialogSearch.ProcessFind(const AParams: string; var AResults: string);
var
  id: Integer;
begin
  id := StrToIntDef(AParams, -1);
  if id <> -1 then
  begin
    Self.ProcessFindByID(AParams, AResults);
  end
  else
  begin
    Self.ProcessFindByName(AParams, AResults);
  end;
end;

procedure TBotDialogSearch.ProcessFindByID(const AParams: string; var AResults: string);
var
  id: Integer;
begin
  id := StrToIntDef(AParams, -1);
  if id <> -1 then
  begin
    AResults := 'searching by id = ' + id.ToString;
  end
  else
  begin
    AResults := 'unknown search params';
  end;
end;

procedure TBotDialogSearch.ProcessFindByName(const AParams: string; var AResults: string);
begin
  if AParams <> '' then
  begin
    AResults := 'searching by name = ' + AParams;
  end
  else
  begin
    AResults := 'unknown search params';
  end;
end;

procedure TBotDialogSearch.ProcessStart(const AParams: string; var AResults: string);
begin
  AResults := 'started';
end;

procedure TBotDialogSearch.RegisterCommands(const ABotsManager: TBotsManager);
begin
  ABotsManager.RegisterCommand('start', Self.ProcessStart);
  ABotsManager.RegisterCommand('find by name', Self.ProcessFindByName);
  ABotsManager.RegisterCommand('find by id', Self.ProcessFindByID);
  ABotsManager.RegisterCommand('find', Self.ProcessFind);
end;

procedure TBotDialogHello.ProcessHello(const AParams: string; var AResults: string);
begin
  AResults := 'Hello, username!';
end;

procedure TBotDialogHello.RegisterCommands(const ABotsManager: TBotsManager);
begin
  ABotsManager.RegisterCommand('hello', Self.ProcessHello);
end;

end.

...
Рейтинг: 0 / 0
28.10.2018, 16:23
    #39724097
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
JaDi, это тя прямо сейчас написал?
...
Рейтинг: 0 / 0
28.10.2018, 16:25
    #39724099
JayDi
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11,

ога... дфм форму не прикладываю, она там простая - кнопка и поле для ввода.
...
Рейтинг: 0 / 0
28.10.2018, 16:31
    #39724103
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Ага, спасибо.
Значит, я шёл в нужном направлении.
...
Рейтинг: 0 / 0
30.10.2018, 16:40
    #39725074
X11
X11
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
Не смог найти.
Поэтому вопрос.
А есть ли у Delphi встроенный способ создать словарь с регистронезависимым (case-insensitive) поиском по ключам?

Код: pascal
1.
DicCmdRows := TDictionary<string, TBotProcRef>.Create(что здесь указать);



Или нужно самому пилить?
...
Рейтинг: 0 / 0
30.10.2018, 16:47
    #39725083
zinpub
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Дженерики: передача процедуры в качестве параметра
X11,

Надо TCustomEqualityComparer.GetHashCode и Equals - переопределить и вуаля
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Дженерики: передача процедуры в качестве параметра / 25 сообщений из 108, страница 1 из 5
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]