Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing Interface's method as parameter

Is it possible to pass interface's method as parameters?

I'm trying something like this:

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;

But it is not working because:

E2010 Incompatible types: 'TMoveProc' and 'procedure, untyped pointer or untyped parameter'

Of course I can do private method for each call, but that is ugly. Is there any better way?

Delphi 2006


Edit: I know that I can pass whole interface, but then I have to specify which function use. I don't want two exactly same procedures with one different call.

I can use second parameter, but that is ugly too.

type
  SomeInterfaceMethod = (siPred, siNext)

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

Thanks all for help and ideas. Clean solution (for my Delphi 2006) is Diego's Visitor. Now I'm using simple ("ugly") wrapper (my own, same solution by TOndrej and Aikislave).

But true answer is "there is no (direct) way to pass interface's methods as parameters without some kind of provider.

like image 559
DiGi Avatar asked Apr 01 '09 10:04

DiGi


People also ask

Can we pass interface as a parameter to a method?

Yes, you can pass Interface as a parameter in the function.

Can you pass method as parameter in Java?

Information can be passed to methods as parameter. Parameters act as variables inside the method. Parameters are specified after the method name, inside the parentheses. You can add as many parameters as you want, just separate them with a comma.

Can we pass interface as a parameter to a method in C#?

Interfaces can be only compile-time types because there one cannot create an object of interface type. Nevertheless, interface reference can represent an real object, only the run-time type of this object can be some structure or class implementing the interface. There is no such thing as "interface to a method".


2 Answers

If you were using Delphi 2009, you could do this with an anonymous method:

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

The problem with trying to pass a reference to just the interface method is that you are not passing a reference to the interface itself, so the interface cannot be reference counted. But anonymous methods are themselves reference counted, so the interface reference inside the anonymous method here can be reference counted as well. That is why this method works.

like image 132
Craig Stuntz Avatar answered Sep 26 '22 07:09

Craig Stuntz


I don't know the exact reason why you need to do that, but, personally, I think it would be better to pass the whole "Mover" object instead of one of its methods. I used this approach in the past, it's called "Visitor" pattern. tiOPF, an object persistence framework, uses it extensively and gives you a good example of how it works: The Visitor Pattern and the tiOPF.

It's relatively long, but it proved very useful to me, even when I didn't use tiOPF. Note step 3 in the document, titled "Step #3. Instead of passing a method pointer, we will pass an object".

DiGi, to answer your comment: If you use Visitor pattern, then you don't have an interface implementing multiple methods, but just one (Execute). Then you'd have a class for each action, like TPred, TNext, TSomething, and you pass an instance of such classes to the object to be processed. In such way, you don't have to know what to call, you just call "Visitor.Execute", and it will do the job.

Here you can find a basic example:

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;
like image 39
Diego Avatar answered Sep 26 '22 07:09

Diego