是否可以将接口的方法作为参数传递?
我正在尝试这样的事情:
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的相同解决方案)。
但真正的答案是“在没有某种提供者的情况下,没有(直接)方法将接口的方法作为参数传递。
答案 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;
这应该允许调用正常的过程。