在运行时创建并运行TTimer

del*_*les 2 javascript delphi delphi-7

我试图在Delphi中实现类似于Javascript setTimeout过程的行为:在延迟几秒后运行.为此,我在运行时创建一个TTimer,运行它然后释放它.

这是我的代码:

procedure createAndRunTimer();
  procedure goTimer(Sender: TObject);
  begin
  (sender as ttimer).enabled := false;
  // do stuff here
  sender.free;
  end;
var t : ttimer;
begin
t := ttimer.create(frmprinc);
t.interval := 5000;
t.ontimer := goTimer(t);
end;
Run Code Online (Sandbox Code Playgroud)

但是我的代码无法编译,IDE会返回以下错误:

[DCC错误] unit1.pas(2153):E2010不兼容的类型:'TNotifyEvent'和'过程,无类型指针或无类型参数'"

任何提示?

谢谢

Rem*_*eau 5

TNotifyEvent 声明为:

TNotifyEvent = procedure(Sender: TObject) of object;
Run Code Online (Sandbox Code Playgroud)

of object使它成为一个闭包,它是一种特殊类型的方法指针,它携带2个指针 - 指向对象的指针,以及指向在对象上调用的非静态类方法的指针.因此,您无法直接将一个独立函数(当然也不是嵌套函数)分配给TNotifyEvent.这就是编译器所抱怨的.

因此,您需要声明一个类来包装您的OnTimer事件处理程序,例如:

type
  TTimerEvents = class
  public
    procedure goTimer(Sender: TObject);
  end;

procedure TTimerEvents.goTimer(Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // NOTE: you cannot destroy the Sender object from here, you must delay
  // the destruction until after this handler exits!  You can post a
  // custom window message via PostMessage() and have the message handler
  // call Sender.Free().  Or, you can use a worker thread to call
  // Sender.Free() via TThread.Synchronize() (or TThread.Queue() in Delphi
  // 8 and later).  Or, in Delphi 10.2 Tokyo and later, you can call
  // Sender.Free() via TThread.ForceQueue().  Or, use whatever other
  // mechanism you want to use to call Sender.Free(), as long as it works
  // asynchronously and calls Sender.Free() in the same thread that
  // constructed the TTimer object ...
end;

var
  events: TTimerEvents;

procedure createAndRunTimer();
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := events.goTimer;
  t.Enabled := True;
end;

initialization
  events := TTimerEvents.Create;
finalization
  events.Free;
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用class方法,因此您不需要包装类的实际实例:

type
  TTimerEvents = class
  public
    class procedure goTimer(Sender: TObject);
  end;

class procedure TTimerEvents.goTimer(Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // delay-destroy the Sender as needed ...
end;

procedure createAndRunTimer();
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := TTimerEvents.goTimer;
  t.Enabled := True;
end;
Run Code Online (Sandbox Code Playgroud)

或者,在Delphi 2006及更高版本中,您可以使用类助手:

type
  TTimerHelper = class helper for TTimer
  public
    procedure goTimer(Sender: TObject);
  end;

procedure TTimerHelper.goTimer(Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // delay-destroy the Sender as needed ...
end;

procedure createAndRunTimer();
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := t.goTimer;
  t.Enabled := True;
end;
Run Code Online (Sandbox Code Playgroud)

话虽这么说,有IS使用一个独立的功能,而无需使用任何类包装的所有方式:

procedure goTimer(Self: Pointer; Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // delay-destroy the Sender as needed ...
end;

procedure createAndRunTimer();
var
  t : TTimer;
  event : TNotifyEvent;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;

  TMethod(event).Data := nil; // or whatever you want to pass to the Self parameter...
  TMethod(event).Code := @goTimer;
  t.OnTimer := event;

  t.Enabled := True;
end;
Run Code Online (Sandbox Code Playgroud)

  • @MartynA我只是提供多个选项,因为其他读者可能正在使用更现代版本的Delphi。我已经为你重新措辞了。 (2认同)