2009-04-01 25 views
10

インターフェイスのメソッドをパラメータとして渡すことはできますか?このようなインターフェイスのメソッドをパラメータとして渡す

私がしようとしている何か:

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' と '手順、型なしポインタまたは型なしパラメーター'

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

デルファイ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の同じ解決策)を使用しています。そのためのインターフェイスのスコーピングの

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

+0

TSomeObject.Moveのコードはのためのユースケースのように見えます「戦略」パターン。 MoveProcはTAbstractMoveProcStrategyクラスのメソッドです。ここでは、サブクラスがMoveメソッドで必要な動作を実装しています。 TMovePredStrategy/TMoveNextStrategyは、異なるMove procsを持ちます。 – mjn

答えて

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; 
+0

これは非常に有望です – DiGi

1

あなたがすることはできません。それはおそらく(可能でしょうか? )を呼び出す前にインターフェイスが解放されるようにしたい場合は、メソッド全体にメソッドを渡す必要があります。

編集済み... 申し訳ありません、この次のビット具体的には、「Of Interface」ビットは冗談で意味されていました。

また、私は間違っている可能性がありますここでは、i.Nextはオブジェクトのメソッドではありません、あなたの型defによると、それはInterfaceのメソッドになります!

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

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

    O.Move(I); 

は、この情報がお役に立てば幸いあなたの関数を再定義します。

+0

いいえ、働いていません - インタフェースは、オブジェクト:( – DiGi

+0

手順移動(定数AInterface:ISomeInterface)を隠し、 は を開始します.... AInterface.Next; END; – Aikislave

4

あなたはそれを行う必要がある正確な理由はわかりませんが、個人的には、そのメソッドの1つではなく「Mover」オブジェクト全体を渡す方がよいと思います。私は過去にこのアプローチを使用しました。それは「ビジター」パターンと呼ばれています。 オブジェクト・パーシスタンス・フレームワークであるtiOPFは、これを広範囲に使用し、どのように動作するかの良い例を提供します:The Visitor Pattern and the tiOPF

これは比較的時間がかかりますが、tiOPFを使用していないときでさえ、私にとっては非常に便利です。 "ステップ#3。メソッドポインタを渡す代わりに、オブジェクト"を渡します。

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

ここでは、基本的な例を見つけることができます:あなたは現在TMoveProcは「これは」などの最初のポインタ隠されたことを意味し、「オブジェクトの」を取り出してみ

TMoveProc = procedure of object; 

として定義されている

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

移動機能を呼び出す必要がありますどの方法を知らないので。あまり機能していない – DiGi

+0

ほとんどクリーンなソリューション – DiGi

-1

をパラメータ。

TMoveProc = procedure; 

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

+0

が..私は私の「もちろん、私は、各コールのためのプライベートメソッドを行うことができますが、それは醜いです」である私の質問 – DiGi

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; 
+0

を更新します... – DiGi

6

あなたは、Delphi 2009を使用していた場合は、匿名メソッドでこれを行うことができます:

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

だけインターフェイスへの参照を渡そうとして問題メソッドは、インターフェイス自体に参照を渡していないので、インターフェイスを参照カウントすることはできません。しかし、匿名メソッド自体が参照カウントされるため、ここでの匿名メソッド内のインタフェース参照も参照カウントされる可能性があります。それがこの方法が機能する理由です。

+1

OK、あなたはするつもりです参照カウントを中断する "非醜い"方法が悪化するので、 "醜い"(またはアップグレード!)のままで生きなければなりません。 –

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; 
+0

+1これは機能しますが、実装することへの言及オブジェクトはその時点ではまだ存在していますが、関数は呼び出されます。同じように動作するが、参照を扱うソリューションについては、[私の答え](http:// stackoverflow。com/a/24392902/2306907) – yonojoy

関連する問題