13 Stimmen

Übergabe der Methode der Schnittstelle als Parameter

Ist es möglich, die Methode der Schnittstelle als Parameter zu übergeben?

Ich versuche etwas in dieser Richtung:

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;

Aber es funktioniert nicht, weil:

E2010 Inkompatible Typen: 'TMoveProc' und 'Prozedur, nicht typisierter Zeiger oder nicht typisierter Parameter'

Natürlich kann ich für jeden Aufruf eine private Methode verwenden, aber das ist unschön. Gibt es einen besseren Weg?

Delphi 2006


Bearbeiten: Ich weiß, dass ich ganze Schnittstelle übergeben kann, aber dann muss ich angeben, welche Funktion verwenden. Ich möchte nicht zwei exakt gleiche Prozeduren mit einem unterschiedlichen Aufruf.

Ich kann den zweiten Parameter verwenden, aber auch das ist hässlich.

type
  SomeInterfaceMethod = (siPred, siNext)

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

Vielen Dank für alle Hilfe und Ideen. Die saubere Lösung (für mein Delphi 2006) ist Diegos Visitor. Jetzt bin ich mit einfachen ("hässlich") Wrapper (meine eigene, gleiche Lösung von TOndrej und Aikislave).

Aber die wahre Antwort lautet: "Es gibt keine (direkte) Möglichkeit, die Methoden der Schnittstelle als Parameter zu übergeben, ohne eine Art von Anbieter.

6voto

Craig Stuntz Punkte 124703

Wenn Sie Delphi 2009 verwenden würden, könnten Sie dies mit einer anonymen Methode tun:

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);

Das Problem bei dem Versuch, eine Referenz nur an die Schnittstellenmethode zu übergeben, ist, dass Sie keine Referenz auf die Schnittstelle selbst übergeben, so dass die Schnittstelle nicht referenziert werden kann. Aber anonyme Methoden werden selbst referenziert, so dass die Schnittstellenreferenz innerhalb der anonymen Methode hier ebenfalls referenziert werden kann. Aus diesem Grund funktioniert diese Methode.

4voto

Diego Punkte 6704

Ich kenne den genauen Grund nicht, warum Sie das tun müssen, aber ich persönlich denke, dass es besser wäre, das gesamte "Mover"-Objekt statt einer seiner Methoden zu übergeben. Ich habe diesen Ansatz in der Vergangenheit verwendet, er wird "Visitor"-Muster genannt. tiOPF, ein Objektpersistenz-Framework, verwendet es ausgiebig und gibt Ihnen ein gutes Beispiel dafür, wie es funktioniert: Das Besuchermuster und der tiOPF .

Sie ist relativ lang, aber sie hat sich für mich als sehr nützlich erwiesen, auch wenn ich tiOPF nicht verwendet habe. Beachten Sie Schritt 3 in dem Dokument mit dem Titel " Schritt #3. Anstelle eines Methodenzeigers übergeben wir ein Objekt ".

DiGi, um Ihren Kommentar zu beantworten: Wenn Sie das Visitor-Muster verwenden, dann haben Sie keine Schnittstelle, die mehrere Methoden implementiert, sondern nur eine (Execute). Dann hätten Sie eine Klasse für jede Aktion, wie TPred, TNext, TSomething, und Sie übergeben eine Instanz dieser Klassen an das zu verarbeitende Objekt. Auf diese Weise müssen Sie nicht wissen, was Sie aufrufen müssen, Sie rufen einfach "Visitor.Execute" auf, und es wird die Aufgabe erledigen.

Hier finden Sie ein einfaches Beispiel:

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;

3voto

Rafael Piccolo Punkte 2279

Obwohl die Lösung mit der Wrapper-Klasse funktioniert, denke ich, dass das ein Overkill ist. Es ist zu viel Code, und Sie müssen die Lebensdauer des neuen Objekts manuell verwalten.

Eine einfachere Lösung wäre vielleicht, Methoden in der Schnittstelle zu erstellen, die Folgendes zurückgeben TMoveProc

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

Die Klasse, die die Schnittstelle implementiert, kann die procedure of object und es wird über die Schnittstelle zugänglich sein.

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;

2voto

Ondrej Kelle Punkte 36511

Wie wäre es damit:

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;

1voto

Aikislave Punkte 636

Das können Sie nicht. Aufgrund des Scopings von Schnittstellen wäre es (vielleicht?) möglich, dass die Schnittstelle freigegeben wird, bevor Sie die Funktion .Next aufrufen. Wenn Sie dies tun wollen, sollten Sie die gesamte Schnittstelle an Ihre Methode übergeben und nicht nur eine Methode.

Bearbeitet... Entschuldigung, der nächste Teil, insbesondere der Teil "Of Interface", war im Scherz gemeint.

Auch, und ich könnte hier falsch sein, i.Next ist nicht eine Methode des Objekts, wie pro Ihre Art def, es wäre eine Methode der Schnittstelle!

Definieren Sie Ihre Funktion neu

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

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

  O.Move(I);

Ich hoffe, das hilft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X