SomeOne
SomeOne

Reputation: 365

How to get a pointer to a method in a base class from a child class in Delphi?

Here is my code example:

type
  TMyBaseClass = class
  public
    procedure SomeProc; virtual;
  end;

  TMyChildClass = class(TMyBaseClass)
  public
    procedure SomeProc; override;
  end;

var
  SomeDelegate: procedure of object;

procedure TMyBaseClass.SomeProc;
begin
  ShowMessage('Base proc');
end;

procedure TMyChildClass.SomeProc;
begin
  ShowMessage('Child proc');
  // here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
  SomeDelegate := SomeProc;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TMyChildClass.Create do
  try
    // there will be "Child proc" message:
    SomeProc;
  finally
    Free;
  end;
  // there i want to get "Base proc" message, but i get "Child proc" again
  // (but it is destroyed anyway, how coud it be?):
  SomeDelegate;
end;

Upvotes: 3

Views: 1232

Answers (2)

Lieven Keersmaekers
Lieven Keersmaekers

Reputation: 58431

Adding a type declaration and some typecasting works but comes with some notes of warning.

As you've mentioned it yourself, the call to somedelegate after the instance has been freed doesn't AV. This is because your SomeProc method doesn't use any instance variables, all it does is calling ShowMessage.

Should you add any instance variables to the call, you even might still get away with it if the memory has not been reassigned. It would be an AV waiting to happen.

Bottom line:

  • don't call methods off destroyed objects.
  • setting a global variable from within an instance of a class that survives the lifetime of the class is not considered good design.
  • in a ideal design, there should be no need for a child class to revert a call anyhow to the ancestor's method, other than by calling inherited.

Code changes

...
type
  TSomeDelegate = procedure of object;

var
  SomeDelegate: TSomeDelegate;

...

procedure TMyChildClass.SomeProc;
var
  method: TMethod;
begin
  ShowMessage('Child proc');
  // here i want to get a pointer to TMyBaseClass.SomeProc (NOT IN THIS CLASS!):
  method.Code :=  @TMyBaseClass.SomeProc;
  method.Data := Self;
  SomeDelegate := TSomeDelegate(method);
end;

Upvotes: 4

Michał Niklas
Michał Niklas

Reputation: 54292

The one way I know is:

procedure TMyChildClass.BaseSomeProc;
begin
  inherited SomeProc;
end;


procedure TMyChildClass.SomeProc;
begin
  ShowMessage('Child proc');
  SomeDelegate := BaseSomeProc;
end;

The 2nd is to change SomeProc declaration from override to reintroduce:

 TMyChildClass = class(TMyBaseClass)
 public
    procedure SomeProc; reintroduce;
 end;

and then cast self as TMyBaseClass (do not use as cast):

 SomeDelegate := TMyBaseClass(self).SomeProc;

Also note that your code will give Access Violation because you call SomeDelegate on already freed object.

Upvotes: 8

Related Questions