Richard A
Richard A

Reputation: 2833

How do you override delegated method implementation?

In Delphi 2007, I am using one class to implement one of the supported interfaces of second class. This is working. The Delphi help states:

By default, using the implements keyword delegates all interface methods. However, you can use methods resolution clauses or declare methods in your class that implement some of the interface methods to override this default behavior.

However, when I declare a method in my second class that has the matching signature of one of the interface methods, it isn't getting called.

I wonder if this is because I'm accessing the class through another interface when I create it.

Below is a test program that demonstrates my problem:

program Project1;

{$APPTYPE CONSOLE}

type
  IInterface1 = interface
  ['{15400E71-A39B-4503-BE58-B6D19409CF90}']
    procedure AProc;
  end;

  IInterface2 = interface
  ['{1E41CDBF-3C80-4E3E-8F27-CB18718E8FA3}']
  end;

  TDelegate = class(TObject)
  protected
    procedure AProc;
  end;

  TMyClass = class(TInterfacedObject, IInterface1, IInterface2)
  strict private
    FDelegate: TDelegate;
    property Delegate: TDelegate read FDelegate implements IInterface1;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AProc;
  end;

procedure TDelegate.AProc;

begin
  writeln('TClassDelegate.AProc');
end;

constructor TMyClass.Create;
begin
  inherited;
  FDelegate := TDelegate.Create;
end;

destructor TMyClass.Destroy;
begin
  FDelegate.Free;
  inherited;
end;

procedure TMyClass.AProc;

begin
  writeln('TMyClass.AProc');
end;

var
  MyObj : IInterface2;

begin
    MyObj := TMyClass.Create;
   (MyObj as IInterface1).AProc;
end.

When I run this I get as output:

TClassDelegate.AProc

What I want is:

TMyClass.AProc

Any help appreciated.

Upvotes: 4

Views: 1474

Answers (4)

David Heffernan
David Heffernan

Reputation: 612954

The documentation explicitly states that the behaviour you see is as designed:

If the delegate property is of a class type, that class and its ancestors are searched for methods implementing the specified interface before the enclosing class and its ancestors are searched.

I guess in the full example you have an interface with multiple methods and are wanting the majority specified by the delegate, and specific ones overridden by the implementing class. I can't see how to achieve that with just one class, but it can be done if you introduce a second class:

program Project1;

{$APPTYPE CONSOLE}

type
  IInterface1 = interface
  ['{15400E71-A39B-4503-BE58-B6D19409CF90}']
    procedure AProc;
    procedure AnotherProc;
  end;

  TDelegate = class
  protected
    procedure AProc;
    procedure AnotherProc;
  end;

  TMyClass = class(TInterfacedObject, IInterface1)
  strict private
    FDelegate: TDelegate;
    property Delegate: TDelegate read FDelegate implements IInterface1;
  public
    constructor Create;
    destructor Destroy; override;
    procedure AProc;
  end;

  TMyOtherClass = class(TMyClass, IInterface1)
    procedure IInterface1.AProc = AProc;
  end;

procedure TDelegate.AProc;
begin
  writeln('TDelegate.AProc');
end;

procedure TDelegate.AnotherProc;
begin
  writeln('TDelegate.AnotherProc');
end;

constructor TMyClass.Create;
begin
  inherited;
  FDelegate := TDelegate.Create;
end;

destructor TMyClass.Destroy;
begin
  FDelegate.Free;
  inherited;
end;

procedure TMyClass.AProc;
begin
  writeln('TMyClass.AProc');
end;

var
  MyObj: IInterface1;

begin
  MyObj := TMyOtherClass.Create;
  MyObj.AProc;
  MyObj.AnotherProc;
  Readln;
end.

As @teran points out, if you are prepared to rename your method then there is an easier solution.

Upvotes: 2

Ondrej Kelle
Ondrej Kelle

Reputation: 37211

The part of documentation you mentioned seems to be outdated. If you try to use method resolution for an interface which is used in an implements clause you will get compiler error E2264: Cannot have method resolutions for interface '%s'.

The solution shown in the link above - to simply give the procedure the same name as declared in the interface - doesn't seem to work, either, in Delphi XE (it compiles but the procedure is not called).

Upvotes: 1

teran
teran

Reputation: 3234

seems you have to redeclare your method in this way:

TMyClass = class(TInterfacedObject, IInterface1, IInterface2)
strict private
  ....
  procedure test();
public
  ....
  procedure  IInterface1.AProc = test;
end;

procedure TMyClass.test;
begin
  writeln('TMyClass.AProc');
end;

so IInterface1.AProc for TMyClass is mapped to Test() (not to FDelegate.AProc) and result is TMyClass.AProc

Upvotes: 5

Nat
Nat

Reputation: 5453

It might be due to the visibility of the property. Every time I use implements the properties are protected or public, same for all the examples I could find in the VCL (eg TAutoObjectEvent.

Attempt #2:

What happens if you remove the AProc() method from TMyClass? Does it then use the one on TDelegate?

Upvotes: 1

Related Questions