传递函数而不是调用它(Delphi)

时间:2014-09-23 08:01:05

标签: function delphi pascal

所以我在Delphi中有一个表单

TFrmMainForm = class(TForm, IFrmMainFormInterface)
  public
    procedure Display(Sender:TObject); 
end;

界面

IFrmMainFormInterface = interface
  procedure Display(Sender:TObject);
end;

另一个班级

TMainFormViewModel = class
    strict private
      fTimer : TTimer;
      function GetOnTimer : TNotifyEvent;
      procedure SetOnTimer(timerEvent : TNotifyEvent);
    public
      property OnTimer : TNotifyEvent read GetOnTimer write SetOnTimer;
end;

implementation

function TMainFormViewModel.GetOnTimer : TNotifyEvent;
begin
    Result := fTimer.OnTimer;
end;

procedure TMainFormViewModel.SetOnTimer(timerEvent : TNotifyEvent);
begin
    fTimer.OnTimer := timerEvent;
end;

我有一个Form MainForm 的实例和视图模型类 MainFormViewModel

我想尝试

MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display

问题是这给我一个错误信息

  

实际参数不够

我相信这是因为delphi试图调用显示功能而不是将其分配给OnTimer事件。我不确定如何解决这个问题,我尝试使用 @ 运算符但没有成功。

修改

我应该补充一点,MainForm在此函数中声明为

procedure Initialise<T:Class, IFrmMainFormInterface>(MainForm : T);

procedure TController.Initialise<T>(MainForm : T);
begin
    MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display ;
end;

3 个答案:

答案 0 :(得分:5)

MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display;

问题是您无法在此上下文中使用接口的方法。 OnTimer事件是of object方法类型。它必须是对象或记录的方法。

答案 1 :(得分:1)

我认为MainFormViewModel.OnTimer := MainForm.Display应该有效。为什么还要将实例转换为接口?

答案 2 :(得分:1)

问题是接口方法引用与对象方法引用不兼容。

但是,不要直接在某个接口实现上传递对方法的引用,而只需传递接口引用本身。在fence的另一侧,不是保持对要调用的特定方法的引用,而是保持对实现该方法的接口的引用。

然后,您只需在适当的时间调用目标方法。

实际上至关重要你是在引用计数环境中执行此操作,因为对接口上的特定方法的引用将不会对接口本身的引用计数做出贡献....如果你设法只保留对某个方法的引用,那么当你的代码试图调用该方法时,实现对象可能已被销毁(因为你没有维护对它的任何引用)。

如果您需要引用某个实现接口的对象的任何方面,那么 - 在引用计算的上下文中 - 您必须维护对该接口的引用

另外,我建议将问题分开。即,将表单响应计时器事件的事实与其显示能力分开:

IfrmMainFormInterface = interface
[..guid..]
  procedure Display;
end;


ITimerListener = interface
[..guid..]
  procedure OnTimer(Sender: TObject);
end;


TMainFormViewModel = class
    strict private
      fTimer : TTimer;
      fOnTimer: ITimerListener;
      procedure DoOnTimer(Sender: TObject);  // internal event handler for fTimer.OnTimer
    public
      property OnTimer: ITimerListener read fOnTimer write fOnTimer;  // no need for getter/setter anymore
end;


procedure TMainFormViewModel.DoOnTimer(Sender: TObject);
begin
  // Enabling/disabling the timer might not be needed/or appropriate, but if it is
  //  then you can take care of that here, rather than relying on the listener to
  //  do it

  fTimer.Enabled := FALSE;
  try
    if Assigned(fOnTimer) then
       fOnTimer.OnTimer(self);  // call the method on the assigned listener interface

  finally
    fTimer.Enabled := TRUE;
  end;
end;



// Meanwhile, Somewhere in your view model initialisation....

fTimer.OnTimer := DoOnTimer;

然后,在 TMainForm 实施中:

TMainForm = class(TForm, IfrmMainFormInterface,
                         ITimerListener)
..
  procedure Display;
  procedure OnTimer(Sender: TObject);
..
end;


procedure TMainForm.OnTimer(Sender: TObject);
begin
  if Sender is TMainFormViewModel then
    Display;
end;

你&#34;附加&#34;通过直接分配主窗体本身(这将导致传递正确类型的接口引用),使用接口类型属性到视图模型计时器:

ViewModel.OnTimer := frmMain;

您可能已经注意到,在上面的示例中,视图模型通过&#34; self&#34;作为 OnTimer 发件人调用侦听器接口,而不是通过原始计时器对象。这是为了演示侦听器可能如何使用发件人的类类型来(可能)区分它可能正在侦听的多个计时器源。

有很多方法可以解决这个问题,如果出现问题,只有一个。

另一种方法是利用这样一个事实,即您现在有一个特定的接口侦听器方法用于此目的,与底层事件方法类型( TNotifyEvent )的特定实现分开。因此,您可以根据需要引入计时器侦听器接口方法所需的任何其他参数。例如如果您的视图模型有多个计时器,那么您的 ITimerListener 接口可能会与发件人一起(或代替)发送计时器ID,例如:

ITimerListener = interface
[..guid..]
  procedure OnTimer(Sender: TObject; aTimerID: Integer);
end;