2009-04-01 10 views
10

È possibile passare il metodo dell'interfaccia come parametri?Metodo dell'interfaccia passata come parametro

Sto cercando qualcosa di simile:

interface 

type 
    TMoveProc = procedure of object; 
    // also tested with TMoveProc = procedure; 
    // procedure of interface is not working ;) 

    ISomeInterface = interface 
    procedure Pred; 
    procedure Next; 
    end; 

    TSomeObject = class(TObject) 
    public 
    procedure Move(MoveProc: TMoveProc); 
    end; 

implementation 

procedure TSomeObject.Move(MoveProc: TMoveProc); 
begin 
    while True do 
    begin 
    // Some common code that works for both procedures 
    MoveProc; 
    // More code... 
    end; 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := GetSomeInterface; 
    o.Move(i.Next); 
    // somewhere else: o.Move(i.Prev); 
    // tested with o.Move(@i.Next), @@... with no luck 
    o.Free; 
end; 

Ma non funziona perché:

E2010 tipi incompatibili: 'TMoveProc' e 'di procedura, puntatore non tipizzata o di parametri non tipizzato'

Naturalmente posso eseguire il metodo privato per ogni chiamata, ma questo è brutto. C'è un modo migliore?

Delphi 2006


Edit: So che posso passare tutta l'interfaccia, ma poi devo specificare quali l'uso della funzione. Non voglio due esattamente le stesse procedure con una chiamata diversa.

Posso usare il secondo parametro, ma anche quello è brutto.

type 
    SomeInterfaceMethod = (siPred, siNext) 

procedure Move(SomeInt: ISomeInterface; Direction: SomeInterfaceMethod) 
begin 
    case Direction of: 
    siPred: SomeInt.Pred; 
    siNext: SomeInt.Next 
    end; 
end; 

Grazie a tutti per l'aiuto e le idee. La soluzione pulita (per il mio Delphi 2006) è il visitatore di Diego. Ora sto usando un semplice ("brutto") wrapper (la mia stessa, la stessa soluzione di TOndrej e Aikislave).

Ma la vera risposta è "non esiste un modo (diretto) per passare i metodi di interfaccia come parametri senza un qualche tipo di fornitore.

+0

Il codice in TSomeObject.Move appare come un caso d'uso per un modello di "strategia". MoveProc potrebbe essere un metodo di una classe TAbstractMoveProcStrategy, in cui le sottoclassi implementano il comportamento richiesto nel metodo Move. TMovePredStrategy/TMoveNextStrategy avrebbe diversi proc di spostamento. – mjn

risposta

1

Ecco un'altra soluzione che sta lavorando in Delphi 20006. E 'simile al concetto di @Rafael, ma utilizzando interfacce:

interface 

type 
    ISomeInterface = interface 
    //... 
    end; 

    IMoveProc = interface 
    procedure Move; 
    end; 

    IMoveProcPred = interface(IMoveProc) 
    ['{4A9A14DD-ED01-4903-B625-67C36692E158}'] 
    end; 

    IMoveProcNext = interface(IMoveProc) 
    ['{D9FDDFF9-E74E-4F33-9CB7-401C51E7FF1F}'] 
    end; 


    TSomeObject = class(TObject) 
    public 
    procedure Move(MoveProc: IMoveProc); 
    end; 

    TImplementation = class(TInterfacedObject, 
     ISomeInterface, IMoveProcNext, IMoveProcPred) 
    procedure IMoveProcNext.Move = Next; 
    procedure IMoveProcPred.Move = Pred; 
    procedure Pred; 
    procedure Next; 
    end; 

implementation 

procedure TSomeObject.Move(MoveProc: IMoveProc); 
begin 
    while True do 
    begin 
    // Some common code that works for both procedures 
    MoveProc.Move; 
    // More code... 
    end; 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := TImplementation.Create; 
    o.Move(i as IMoveProcPred); 
    // somewhere else: o.Move(i as IMoveProcNext); 
    o.Free; 
end; 
+0

Questo è molto promettente – DiGi

1

non si può. A causa della scoping delle interfacce sarebbe possibile (forse?) per l'interfaccia da rilasciare prima di chiamare la funzione .Next. Se si desidera eseguire questa operazione è necessario passare l'intera interfaccia al metodo anziché solo un metodo

Modificato ... Siamo spiacenti, questo prossimo bit in particolare il bit "Of Interface" era inteso per scherzo

Inoltre, e potrei sbagliarmi qui, i.Next non è un metodo Of Object, come per il tuo tipo def, sarebbe un metodo di interfaccia!

ridefinire la funzione

TSomeObject = class(TObject) 
    public 
     procedure Move(Const AMoveIntf: ISomeInterface); 
    end; 

    Procedure TSomeObject.Move(Const AMoveIntf : ISomeInterface); 
    Begin 
     ....; 
     AMoveIntf.Next; 
    end; 

    O.Move(I); 

Spero che questo aiuti.

+0

No, non funziona - interfaccia nasconde oggetto :( – DiGi

+0

procedura di spostamento (Cost AInterface: ISomeInterface); Iniziare .... AInterface.Next; Fine; – Aikislave

4

Non conosco il motivo esatto per cui è necessario farlo, ma, personalmente, penso che sarebbe meglio passare l'intero oggetto "Mover" anziché uno dei suoi metodi. Ho usato questo approccio in passato, si chiama modello "Visitatore". tiOPF, un framework di persistenza degli oggetti, lo utilizza estensivamente e offre un buon esempio di come funziona: The Visitor Pattern and the tiOPF.

È relativamente lungo, ma mi è risultato molto utile, anche quando non usavo tiOPF. Nota il passaggio 3 nel documento, intitolato "Passaggio 3. Invece di passare un puntatore del metodo, passeremo un oggetto".

DiGi, per rispondere al tuo commento: Se si utilizza il pattern Visitor, non si dispone di un'interfaccia che implementa più metodi, ma solo uno (Esegui).Quindi avresti una classe per ogni azione, come TPred, TNext, TSomething e passerai un'istanza di tali classi all'oggetto da elaborare. In questo modo, non devi sapere cosa chiamare, basta chiamare "Visitor.Execute", e farà il lavoro.

Qui potete trovare un esempio di base:

interface 

type 
TVisited = class; 

TVisitor = class 
    procedure Execute(Visited: TVisited); virtual; abstract; 
end; 

TNext = class(TVisitor) 
    procedure Execute (Visited: TVisited); override; 
end; 

TPred = class(TVisitor) 
    procedure Execute (Visited: TVisited); override; 
end; 

TVisited = class(TPersistent) 
public 
    procedure Iterate(pVisitor: TVisitor); virtual; 
end; 

implementation 

procedure TVisited.Iterate(pVisitor: TVisitor); 
begin 
    pVisitor.Execute(self); 
end; 

procedure TNext.Execute(Visited: TVisited); 
begin 
    // Implement action "NEXT" 
end; 

procedure TPred.Execute(Visited: TVisited); 
begin 
    // Implement action "PRED" 
end; 

procedure Usage; 
var 
    Visited: TVisited; 
    Visitor: TVisitor; 
begin 
    Visited := TVisited.Create; 
    Visitor := TNext.Create; 

    Visited.Iterate(Visitor); 
    Visited.Free; 
end; 
+0

Perché funzione Move non sa quale metodo dovrebbe chiamare – DiGi

+0

La soluzione più pulita – DiGi

-1

Al momento è TMoveProc definito come

TMoveProc = procedure of object; 

Prova a prendere il "di oggetto" che implica un nascosto "questo" puntatore come prima parametro.

TMoveProc = procedure; 

Ciò dovrebbe consentire di chiamare una normale procedura.

+0

Questo non funziona troppo .. Aggiornerò la mia domanda – DiGi

2

ne dite di questo:

type 
    TMoveProc = procedure(const SomeIntf: ISomeInterface); 

    TSomeObject = class 
    public 
    procedure Move(const SomeIntf: ISomeInterface; MoveProc: TMoveProc); 
    end; 

procedure TSomeObject.Move(const SomeIntf: ISomeInterface; MoveProc: TMoveProc); 
begin 
    MoveProc(SomeIntf); 
end; 

procedure MoveProcNext(const SomeIntf: ISomeInterface); 
begin 
    SomeIntf.Next; 
end; 

procedure MoveProcPred(const SomeIntf: ISomeInterface); 
begin 
    SomeIntf.Pred; 
end; 

procedure Usage; 
var 
    SomeObj: TSomeObject; 
    SomeIntf: ISomeInterface; 
begin 
    SomeIntf := GetSomeInterface; 
    SomeObj := TSomeObject.Create; 
    try 
    SomeObj.Move(SomeIntf, MoveProcNext); 
    SomeObj.Move(SomeIntf, MoveProcPred); 
    finally 
    SomeObj.Free; 
    end; 
end; 
+0

Questo è il mio "Certo che posso fare il metodo privato per ogni chiamata, ma questo è brutto" ... – DiGi

6

Se si sta utilizzando Delphi 2009, si potrebbe fare questo con un metodo anonimo:

TSomeObject = class(TObject) 
public 
    procedure Move(MoveProc: TProc); 
end; 

procedure Usage; 
var 
    o: TSomeObject; 
    i: ISomeInterface; 
begin 
    o := TSomeObject.Create; 
    i := GetSomeInterface; 
    o.Move(procedure() begin i.Next end); 

Il problema con il tentativo di passare un riferimento al solo l'interfaccia il metodo è che non si sta passando un riferimento all'interfaccia stessa, quindi l'interfaccia non può essere conteggiata. Ma i metodi anonimi sono a loro volta conteggiati, quindi anche il riferimento all'interfaccia all'interno del metodo anonimo può essere conteggiato come riferimento. Questo è il motivo per cui questo metodo funziona.

+1

OK, hai intenzione di vivere con "brutto", poi (o aggiornare!), perché un metodo "non brutto" che rompe il conteggio dei riferimenti sarebbe peggio –

3

Sebbene la soluzione di classe wrapper funzioni, penso che sia eccessivo. È troppo codice e devi gestire manualmente la durata del nuovo oggetto.

Forse una soluzione più semplice sarebbe quella di creare metodi nell'interfaccia che restituisce TMoveProc

ISomeInterface = interface 
    ... 
    function GetPredMeth: TMoveProc; 
    function GetNextMeth: TMoveProc; 
    ... 
end; 

La classe che implementa l'interfaccia può fornire la procedure of object e sarà accessibile attraverso l'interfaccia.

TImplementation = class(TInterfaceObject, ISomeInterface) 
    procedure Pred; 
    procedure Next; 

    function GetPredMeth: TMoveProc; 
    function GetNextMeth: TMoveProc; 
end; 

... 

function TImplementation.GetPredMeth: TMoveProc; 
begin 
    Result := Self.Pred; 
end; 

function TImplementation.GetNextMeth: TMoveProc; 
begin 
    Result := Self.Next; 
end; 
+0

+1 Funziona, ma bisogna fare attenzione, che il riferimento all'attuazione l'oggetto è ancora esistente al momento, la funzione viene chiamata . Per una soluzione che funziona in modo simile ma si prende cura dei riferimenti, vedere [risposta] (http: // stackoverflow.com/a/24392902/2306907) – yonojoy