在运行时创建并运行TTimer

在运行时创建并运行TTimer

本文介绍了在运行时创建并运行TTimer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在Delphi中实现与Javascript setTimeout()过程类似的行为:延迟几秒钟后再运行。为此,我在运行时创建一个 TTimer ,运行它,然后释放它。

I'm trying to achieve in Delphi a behavior similar to Javascript's setTimeout() procedure : run things after a delay of some seconds. To do so, I'm creating a TTimer at runtime, running it, and then free it.

这里是我的代码:

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;

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

But my code won't compile, the compiler returns the error below:

有提示吗?

推荐答案

TNotifyEvent 声明为:

TNotifyEvent = procedure(Sender: TObject) of object;

对象的 使其成为闭合,这是一种特殊的方法指针,它带有2个指针-指向对象的指针和指向在对象上调用的非静态类方法的指针,因此,您无法分配独立的函数,当然不是嵌套函数,直接指向 TNotifyEvent 。这就是编译器所抱怨的。

The of object makes it a closure, which is a special type of method pointer that carries 2 pointers - an pointer to an object, and a pointer to a non-static class method which gets called on the object. As such, you cannot assign a standalone function, and certainly not a nested function, directly to a TNotifyEvent. That is what the compiler is complaining about.

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

So, you need to declare a class to wrap your OnTimer event handler, eg:

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;

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

Alternatively, you can use a class method so you don't need an actual instance of the wrapper 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;

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

Or, in Delphi 2006 and later, you can use a class helper:

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;

话虽这么说, IS 是一种使用独立功能的方法完全使用任何类包装器:

That being said, there IS a way to use a standalone function without using any class wrapper at all:

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;

这篇关于在运行时创建并运行TTimer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 02:22