将Interface的方法作为参数传递

时间:2009-04-01 10:47:03

标签: delphi oop interface callback

是否可以将接口的方法作为参数传递?

我正在尝试这样的事情:

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'和'过程,无类型指针或无类型参数'

当然我可以为每个电话做私人方法,但这很难看。还有更好的办法吗?

Delphi 2006


编辑: 我知道我可以传递整个界面,但是我必须指定使用哪个函数。我不希望两个完全相同的程序与一个不同的调用。

我可以使用第二个参数,但这也很难看。

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的访客。现在我使用简单的(“丑陋”)包装器(我自己的,TOndrej和Aikislave的相同解决方案)。

但真正的答案是“在没有某种提供者的情况下,没有(直接)方法将接口的方法作为参数传递。

7 个答案:

答案 0 :(得分: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 :(得分:4)

我不知道你需要这样做的确切原因,但是,就个人而言,我认为传递整个“Mover”对象而不是其中一种方法会更好。我过去使用过这种方法,它被称为“访客”模式。 tiOPF是一个对象持久性框架,它广泛使用它并为您提供了一个很好的示例:The Visitor Pattern and the tiOPF

它相对较长,但事实证明它对我很有用,即使我没有使用tiOPF。请注意文档中的步骤3,标题为“步骤#3。我们将传递一个对象而不是传递方法指针。”

DiGi,回答您的评论:如果您使用访客模式,那么您没有实现多个方法的接口,只有一个(执行)。然后你就会为每个动作创建一个类,比如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;

答案 2 :(得分: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;

答案 3 :(得分: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;

答案 4 :(得分:1)

你做不到。由于接口的范围,在调用.Next函数之前,可能(可能是?)释放接口。如果你想这样做,你应该将整个界面传递给你的方法,而不仅仅是一个方法。

...编辑 对不起,这个下一位,特别是“Of Interface”位用于开玩笑。

另外,我可能在这里错了,i.Next不是Object的方法,根据你的类型def,它将是一个接口的方法!

重新定义您的功能

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

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

  O.Move(I);

希望这有帮助。

答案 5 :(得分: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;

答案 6 :(得分:-1)

您目前将TMoveProc定义为

TMoveProc = procedure of object;

尝试取出“of object”,暗示隐藏的“this”指针作为第一个参数。

TMoveProc = procedure;

这应该允许调用正常的过程。