Passing a function instead of calling it (Delphi)

So I have a form in Delphi

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

Interface

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

And another class

 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; 

I have an instance of the MainForm form and the view model class MainFormViewModel

and i want to try

 MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display 

The problem is that it gives me an error message

Not enough actual parameters

I believe this is because delphi is trying to call the display function, not assign it to the OnTimer event. I'm not sure how to fix this, I tried using the @ operator without success.

EDIT

I have to add that MainForm is declared in this function as

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

 procedure TController.Initialise<T>(MainForm : T); begin MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display ; end; 
+5
source share
3 answers
 MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display; 

The problem is that you cannot use the interface method in this context. The OnTimer event is a type of of object method. It must be an object or record method.

+5
source

I think MainFormViewModel.OnTimer := MainForm.Display should work. Why in any case pour an instance into the interface?

+1
source

The problem is that references to interface methods are incompatible with references to object methods.

But instead of directly passing the method reference in any interface implementation, just pass the interface link. On the other side of the fence, instead of referencing the specific method that is being called, you hold a link to an interface that implements this method.

Then you just call the target method at the appropriate time.

In fact, it’s important that you do this in a reference-counting environment, since a link to a specific method on an interface will NOT contribute to link counting on the interface itself .... if you try to save only a link to a method, by the time when your code tries to call this method, the implementation object could be destroyed (since you did not support any reference to it).

If you need to address any aspect of any object that implements an interface, then in the context of reference counting, you must maintain a reference to that interface.

In addition, however, I would suggest sharing the problems. those. separate the fact that your form responds to a timer event from its ability to display:

 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; 

Then in your implementation of 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; 

What do you "attach" to the timer of the view model using the property of the interface type, directly assigning the main form (which will lead to the transfer of a link to the corresponding type):

 ViewModel.OnTimer := frmMain; 

You may have noticed that in the above example, the view model passes the β€œI” as the Sender of the OnTimer call to the listener interface, and not through the initiating timer object. This is to demonstrate how the listener can use the type of the Sender class to (potentially) distinguish between several timer sources that he can listen to.

There are several ways to approach this problem, if it arises, of which this is only one.

Another would be to take advantage of the fact that you now have a special method for listening to the interface for this purpose, separately from the concrete implementation of the main type of event method ( TNotifyEvent ). As a result, you can enter any additional parameters necessary for your timer listener interface method that suit your needs. for example, if your view models have multiple timers, your ITimerListener interface can compress so that in addition to (or instead of) the sender, a timer identifier is passed, for example:

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

Source: https://habr.com/ru/post/1203196/


All Articles