views:

107

answers:

2

Hi! There is 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;

Thanks!

+8  A: 

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.

Michał Niklas
Thanks! Yours 1st method is exact what i want =)
SomeOne
Please mind what Michal Niklas said about access violation. You'll still get one, even with "inherited". That's because you cannot have a pointer to a method of a CLASS (unless it's declared as "class procedure"), only to a method of an OBJECT. Your object is destroyed when you call Free(), and any references to it, including SomeDelegate which contains a reference to it's function, are no longer valid.
himself
When tried code with my Turbo Delphi I got access violation exception.
Michał Niklas
im using it just for example, but thx for remark anyway =)
SomeOne
+3  A: 

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