2014-09-23 25 views
5

所以我有一個形式,德爾福傳遞函數而不是調用它(德爾福)

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; 

我有一個表單MainForm和視圖模型類的實例MainFormViewModel

,我想嘗試

MainFormViewModel.OnTimer := IFrmMainFormInterface(MainForm).Display 

問題是這樣給我一個錯誤消息

沒有足夠的實際參數

我相信這是因爲德爾福試圖調用顯示函數而不是將其分配給OnTimer事件。我不知道如何解決這個問題,我試過使用@運營商沒有成功。

編輯

我要補充的是,MainForm的聲明在此功能

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

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

我認爲DSharp有一些額外的功能,使處理匿名函數,但我希望我可以避免使用外部庫只是爲了這個 – sav 2014-09-23 08:01:59

+0

爲什麼你會聲明'Initialise'那樣?似乎沒有泛型聲明它會更有表現力:'Initialise(MainForm:IFrmMainFormInterface)'。當然,這仍然不允許你將接口成員分配給一個標準的方法指針,但我很困惑你爲什麼有複雜的起點。 – 2014-09-24 23:27:55

+0

我正在嘗試使用MVVM設計模式。在初始化過程中,我將表單綁定到ViewModel。綁定(MainFormViewModel,'ScheduledWorkRecords'); MainForm.WorkRecords:= binding;出於測試目的,我也有一個模擬用戶界面對象,可以用來代替實際的表單。 – sav 2014-09-26 01:29:23

回答

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

的問題是,你不能使用的方法界面在這方面。 OnTimer事件是一個of object方法類型。它必須是一個對象或記錄的方法。

1

我認爲MainFormViewModel.OnTimer := MainForm.Display應該工作。無論如何,爲什麼要將實例轉換爲接口?

+0

我已經試過這兩種方式,它沒有區別。看來德爾福不支持這樣做。 – sav 2014-09-24 01:59:44

+0

我也許應該補充一點,MainForm變量在函數中被聲明爲: procedure TController。初始化(MainForm:T); – sav 2014-09-24 04:23:55

+0

@sav,好吧,這會改變這個問題。 – iamjoosy 2014-09-24 08:14:25

1

問題是接口方法引用與對象方法引用不兼容。

但不是直接將某個引用傳遞給某個接口實現上的某個方法,而只是傳遞接口引用本身。在圍欄的另一邊,不是保持對要調用的特定方法的引用,而是保持對實現該方法的接口的引用。

然後您只需在適當的時間調用目標方法。

事實上,這是您這樣做,在引用計數的環境,因爲參考了具體的方法在接口上無助於接口本身的引用計數....如果你來着至關重要只保留對某種方法的引用,然後在代碼嘗試調用該方法時,實現對象可能已被銷燬(因爲您沒有保留對它的任何引用)。

如果需要參考一些對象實現接口,那麼任何方面 - 在參考計數上下文 - 你必須保持到該接口的引用。

另外,但我會建議關注的分離。即分離,您的形式迴應了其能力的計時器事件所顯示的事實:

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; 

你「重視」的觀點通過直接分配主窗體本身(這將導致傳遞正確類型的接口引用)來使用接口類型屬性的模型計時器:

ViewModel.OnTimer := frmMain; 

您可能已經注意到,在上面的例子中,視圖模型通過「自我」爲發件人的的OnTimer調用偵聽器接口的,而不是通過原始計時器對象。這是爲了演示如何聽衆可能使用類型的發件人(可能)區分它可能正在偵聽的多個定時器源。

有很多方法可以解決這個問題,如果它出現了,其中只有一個。

另一種方法是利用現在爲此具有特定接口偵聽器方法的事實,這與以下事件方法類型的具體實現(TNotifyEvent)分開。因此,您可以根據需要向您的計時器偵聽器接口方法引入所需的其他任何參數。例如如果您的視圖模型有多個計時器,然後您ITimerListener接口可以承包一個定時器ID除了傳遞(或代替)發送,例如:

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