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”和“过程、无类型指针或无类型参数”

当然我可以为每个调用做私有方法,但这很难看。有没有更好的办法?

德尔福 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's Visitor。现在我正在使用简单的(“丑陋”)包装器(我自己的,由 TOndrej 和 Aikislave 提供的相同解决方案)。

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

4

7 回答 7

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

尝试仅传递对接口方法的引用的问题在于,您没有传递对接口本身的引用,因此无法对接口进行引用计数。但是匿名方法本身是引用计数的,所以这里匿名方法内部的接口引用也可以引用计数。这就是这种方法有效的原因。

于 2009-04-01T12:26:53.003 回答
4

我不知道您需要这样做的确切原因,但就个人而言,我认为传递整个“Mover”对象而不是它的一种方法会更好。我过去使用过这种方法,它被称为“访客”模式。tiOPF 是一个对象持久性框架,广泛使用它并为您提供了一个很好的例子来说明它是如何工作的:访问者模式和 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;
于 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

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

已编辑...对不起,下一位,特别是“接口”位是开玩笑的。

另外,我在这里可能是错的,i.Next 不是 Object 的方法,根据您的类型 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);

希望这可以帮助。

于 2009-04-01T11:03:17.853 回答
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

您当前将 TMoveProc 定义为

TMoveProc = procedure of object;

尝试取出“对象”,这意味着隐藏的“this”指针作为第一个参数。

TMoveProc = procedure;

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

于 2009-04-01T11:13:54.083 回答