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

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

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


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


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

type
TMyProc = function(const Command: string): string;
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724038
Фотография 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
Дженерики: передача процедуры в качестве параметра
    #39724041
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11
Код: pascal
1.
Type botStart = procedure;


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


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

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

а дальше?
пока не понял идеи
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #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
Дженерики: передача процедуры в качестве параметра
    #39724070
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi, я пока не знаю, какие будут параметры и какого типа.
Мало того, заранее неизвестно, что напишет пользователь боту. Это может быть не только команда, но и простой текст, которые придётся распарсить и понять - какую процедуру обработки вызвать.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #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
Дженерики: передача процедуры в качестве параметра
    #39724081
Фотография 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
Дженерики: передача процедуры в качестве параметра
    #39724083
Фотография 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
Дженерики: передача процедуры в качестве параметра
    #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
Дженерики: передача процедуры в качестве параметра
    #39724088
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я пока думаю, что всегда будет один параметр, т.е. в качестве этого одного параметра будет входящее написанное пользователем сообщение....
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724089
Фотография 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
Дженерики: передача процедуры в качестве параметра
    #39724090
Фотография 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
Дженерики: передача процедуры в качестве параметра
    #39724091
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да, работает, бот получил эти 2 команды и запустил нужные процедуры.
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #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
Дженерики: передача процедуры в качестве параметра
    #39724097
Фотография X11
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
JaDi, это тя прямо сейчас написал?
...
Рейтинг: 0 / 0
Дженерики: передача процедуры в качестве параметра
    #39724099
Фотография JayDi
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
X11,

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

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



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

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


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