11

インターフェイスのメソッドをパラメータとして渡すことは可能ですか?

私はこのようなことを試みています:

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;

しかし、次の理由で機能していません。

E2010 互換性のない型: 'TMoveProc' および 'procedure, untyped pointer or untyped parameter'

もちろん、呼び出しごとにプライベートメソッドを実行できますが、それは醜いです。もっと良い方法はありますか?

デルファイ 2006


編集:インターフェイス全体を渡すことができることはわかっていますが、使用する関数を指定する必要があります。1 つの異なる呼び出しで 2 つのまったく同じ手順は必要ありません。

2 番目のパラメーターを使用できますが、それも醜いです。

type
  SomeInterfaceMethod = (siPred, siNext)

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

助けとアイデアをありがとう。クリーンなソリューション (私の Delphi 2006 の場合) は、Diego's Visitor です。今、私は単純な (「醜い」) ラッパー (TOndrej と Aikislave による私自身の同じソリューション) を使用しています。

しかし、本当の答えは、「ある種のプロバイダーなしでインターフェースのメソッドをパラメーターとして渡す (直接的な) 方法はありません。

4

7 に答える 7

4

なぜそれを行う必要があるのか​​ 正確な理由はわかりませんが、個人的には、メソッドの1つではなく、「Mover」オブジェクト全体を渡す方が良いと思います. 私は過去にこのアプローチを使用しました。これは「ビジター」パターンと呼ばれます。オブジェクト永続化フレームワークである tiOPF は、これを広範に使用し、それがどのように機能するかの良い例を提供します:訪問者パターンと tiOPF

比較的長いですが、tiOPF を使用していない場合でも、非常に役立つことがわかりました。ドキュメントのステップ 3 に注意してください。「ステップ #3. メソッド ポインターを渡す代わりに、オブジェクトを渡します」。

DiGi、あなたのコメントに答えるために: 訪問者パターンを使用する場合、複数のメソッドを実装するインターフェイスはありませんが、1 つだけ (実行) があります。次に、TPred、TNext、TSomething などの各アクションのクラスを作成し、そのようなクラスのインスタンスを処理対象のオブジェクトに渡します。このように、何を呼び出すかを知る必要はありません。「Visitor.Execute」を呼び出すだけで、ジョブが実行されます。

ここでは、基本的な例を見つけることができます:

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;
于 2009-04-01T11:13:10.310 に答える
3

ラッパー クラスのソリューションは機能しますが、それはやり過ぎだと思います。コードが多すぎるため、新しいオブジェクトの有効期間を手動で管理する必要があります。

おそらく、より簡単な解決策は、返すインターフェイスにメソッドを作成することですTMoveProc

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

インターフェイスを実装するクラスは を提供でき、procedure of objectインターフェイスを介してアクセスできます。

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;
于 2012-10-23T02:02:25.063 に答える
2

これはどう:

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;
于 2009-04-01T11:32:58.327 に答える
1

これは、Delphi 20006 で動作している別のソリューションです。@Rafael のアイデアに似ていますが、インターフェイスを使用しています。

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;
于 2014-06-24T17:34:24.967 に答える
1

できません。インターフェイスのスコープにより、.Next 関数を呼び出す前にインターフェイスが解放される可能性があります (おそらく?)。これを行う場合は、メソッドだけでなく、インターフェイス全体をメソッドに渡す必要があります。

編集済み...申し訳ありませんが、この次のビット、特に「インターフェースの」ビットは冗談を意味していました。

また、私はここで間違っている可能性があります。i.Next はオブジェクトのメソッドではありません。タイプの定義によると、それはインターフェイスのメソッドになります。

関数を再定義する

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

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

  O.Move(I);

お役に立てれば。

于 2009-04-01T11:03:17.853 に答える
-1

現在、TMoveProc は次のように定義されています。

TMoveProc = procedure of object;

非表示の「this」ポインターを最初のパラメーターとして意味する「of object」を取り出してみてください。

TMoveProc = procedure;

これにより、通常のプロシージャを呼び出すことができます。

于 2009-04-01T11:13:54.083 に答える