AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Delphi Vererbung: Wie rufe ich Klasse.Parent.Methode auf?
Thema durchsuchen
Ansicht
Themen-Optionen

Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

Ein Thema von MaBuSE · begonnen am 23. Okt 2014 · letzter Beitrag vom 27. Okt 2014
Antwort Antwort
Benutzerbild von MaBuSE
MaBuSE

Registriert seit: 23. Sep 2002
Ort: Frankfurt am Main (in der Nähe)
1.837 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 23. Okt 2014, 14:09
Hallo,
ich habe ein Problem mit einem Methodenaufruf.

Folgendes Szenario:
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

  TMutter = class(TObject)
  public
    procedure a(x: Boolean); virtual;
    procedure b; virtual;
  end;

  TKind = class(TMutter)
  public
    procedure a(x: Boolean); override;
    procedure b; override;
  end;


procedure Log(s:string);

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure Log(s:string);
begin
  Form1.Memo1.Lines.Add(s);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Mutter: TMutter;
  Kind: TKind;
begin
  Memo1.Lines.Clear;

  Log('');
  Log('Mutter beinhaltet Mutter');
  Mutter := TMutter.Create;
  Mutter.a(True); // TMutter.a wird aufgerufen
  Mutter.Free;

  Log('');
  Log('Kind beinhaltet Kind');
  Kind := TKind.Create; // TKind.a wird aufgerufen
  Kind.a(True);
  Kind.Free;

  Log('');
  Log('Mutter beinhaltet Kind');
  Mutter := TKind.Create;
  Mutter.a(False); // TKind.a wird aufgerufen
  Mutter.Free;

end;

{ TKind }

procedure TKind.a(x: Boolean);
begin
  Log('TKind.a1');
  inherited;
  Log('TKind.a2');
end;

procedure TKind.b;
begin
  Log('TKind.b1');
  inherited;
  Log('TKind.b2');
end;

{ TMutter }

procedure TMutter.a(x: Boolean);
begin
  Log('TMutter.a1');
  if x then
  begin
    // b aufrufen. (Wenn Self = TKind ist, dann wird TKind.b aufgerufen.)
    b;
  end
  else
  begin
    // Hier sollte immer TMutter.b aufgerufen werden!!!
    b;
  end;
  Log('TMutter.a2');
end;

procedure TMutter.b;
begin
  Log('TMutter.b');
end;

end.

Bei Log('Mutter beinhaltet Kind'); wird z.B. folgendes Aufgerufen:
  • TKind.a (da in Mutta ja ein TKind steckt)
  • TMutter.a (inherited)
  • TKind.b (in TMutter.a wird das b von TKind aufgerufen, da Methode a virtual oder dynamic)
  • TMutter.a (inherited)
Wie ist es möglich in TMutter.a TMutter.b aufzurufen, obwohl in TMutter ein TKind steckt?

So eine einfache Lösung wie TMutter(Self).b funktionieren natürlich nicht.

Hat jemand eine Idee?

Ich denke imMoment darüber nach mit den Methoden Pointer zu ermitteln und die Methode über die Adresse direkt aufzurufen. Aber das muß auch einfacher gehen.

Danke im Voraus.
MaBuSE
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
10.993 Beiträge
 
Delphi 12 Athens
 
#2

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 23. Okt 2014, 14:19
Wie ist es möglich in TMutter.a TMutter.b aufzurufen, obwohl in TMutter ein TKind steckt?
Eigentlich stimmt hier dein Design nicht mehr. Es mag andere Ansätze geben, aber ich löse das meistens so:

Delphi-Quellcode:
  TMutter = class(TObject)
  protected
    procedure InternalB;
  public
    procedure a(x: Boolean); virtual;
    procedure b; virtual;
  end;

procedure TMutter.a(x: Boolean);
begin
  Log('TMutter.a1');
  if x then
  begin
    // b aufrufen. (Wenn Self = TKind ist, dann wird TKind.b aufgerufen.)
    b;
  end
  else
  begin
    // Hier sollte immer TMutter.b aufgerufen werden!!!
    InternalB;
  end;
  Log('TMutter.a2');
end;

procedure TMutter.InternalB;
begin
  Log('TMutter.b');
end;

procedure TMutter.b;
begin
  InternalB;
end;
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von ChrisE
ChrisE

Registriert seit: 15. Feb 2006
Ort: Hechingen
504 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#3

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 23. Okt 2014, 14:28
Hallo Mabuse,

wenn du alle Quellen in der Hand hast (Mutter & Kind) und wir hier nicht über Sinn & Unsinn deiner Forderung reden wollen dann mach TMutter.b; OHNE virtual und dementsprechend TKind.b; ohne override . Dein Problem ist ja eben die VMT die eb en genau dafür sorgen soll, dass nur die Methoden aufgerufen werden die auch wirklich zu dem drinne Steckenden gehören. Also ohne VMT arbeiten und die Methoden überdecken und in den Sourcen selber entscheiden was aufgerufen werden soll aufgrund dessen was man ist.

Sollte eigentlich gehen.

Gruß, Chris
Christian E.
Es gibt 10 Arten von Menschen, die die Binär lesen können und die die es nicht können

Delphi programming rules
  Mit Zitat antworten Zitat
zagota

Registriert seit: 3. Sep 2014
38 Beiträge
 
#4

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 23. Okt 2014, 15:04
Habe den Source mal angepasst. Nicht schön aber sollte gehen...


Hallo,
ich habe ein Problem mit einem Methodenaufruf.

Folgendes Szenario:
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

  TMutter = class(TObject)
  public
    procedure a(x: Boolean); virtual;
    procedure b; virtual;
  end;

  TKind = class(TMutter)
  public
    procedure a(x: Boolean); override;
    procedure b; override;
  end;

//++++++++++++++++ inserted +++++++
  TB = procedure of object;
//++++++++++++++++


procedure Log(s:string);

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure Log(s:string);
begin
  Form1.Memo1.Lines.Add(s);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Mutter: TMutter;
  Kind: TKind;
begin
  Memo1.Lines.Clear;

  Log('');
  Log('Mutter beinhaltet Mutter');
  Mutter := TMutter.Create;
  Mutter.a(True); // TMutter.a wird aufgerufen
  Mutter.Free;

  Log('');
  Log('Kind beinhaltet Kind');
  Kind := TKind.Create; // TKind.a wird aufgerufen
  Kind.a(True);
  Kind.Free;

  Log('');
  Log('Mutter beinhaltet Kind');
  Mutter := TKind.Create;
  Mutter.a(False); // TKind.a wird aufgerufen
  Mutter.Free;

end;

{ TKind }

procedure TKind.a(x: Boolean);
begin
  Log('TKind.a1');
  inherited;
  Log('TKind.a2');
end;

procedure TKind.b;
begin
  Log('TKind.b1');
  inherited;
  Log('TKind.b2');
end;

{ TMutter }

procedure TMutter.a(x: Boolean);

// +++++++++++
var
  Proc: TB;
// +++++++++++

begin
  Log('TMutter.a1');
  if x then
  begin
    // b aufrufen. (Wenn Self = TKind ist, dann wird TKind.b aufgerufen.)
    b;
  end
  else
  begin
    // Hier sollte immer TMutter.b aufgerufen werden!!!
// +++++++++++
    TMethod(Proc).Code := @TMutter.b;
    TMethod(Proc).Data := Self;
    Proc();
// +++++++++++
// b;
  end;
  Log('TMutter.a2');
end;

procedure TMutter.b;
begin
  Log('TMutter.b');
end;

end.

MaBuSE
  Mit Zitat antworten Zitat
Benutzerbild von MaBuSE
MaBuSE

Registriert seit: 23. Sep 2002
Ort: Frankfurt am Main (in der Nähe)
1.837 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 24. Okt 2014, 10:41
Habe den Source mal angepasst. Nicht schön aber sollte gehen...
Delphi-Quellcode:
...
begin
    // Hier sollte immer TMutter.b aufgerufen werden!!!
// +++++++++++
    TMethod(Proc).Code := @TMutter.b;
    TMethod(Proc).Data := Self;
    Proc();
// +++++++++++
// b;
  end;
  Log('TMutter.a2');
end;
...
Danke für Deine Antwort.
Ich denke imMoment darüber nach mit den Methoden Pointer zu ermitteln und die Methode über die Adresse direkt aufzurufen. Aber das muß auch einfacher gehen.
Das war auch meine Idee, aber wie Du schon sagtest: "nicht schön"

@Uwe: Stimmt, auf die Idee bin ich gar nicht gekommen. Ich probier das mal aus. Danke.

@Chris: Das geht leider nicht, da ich das alte Verhalten ja durchaus wünsche, wenn (True) übergeben wurde. Außerdem sollte man wenn man es so macht das Schlüsselwort reintreduce verwenden um zu dokumentieren, dass man genau das machen möchte und nicht aus versehen virtual/override vergessen hat.

Danke für die Antworten
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#6

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 24. Okt 2014, 23:50
Ich denke du solltest dir mal das Liskovsches Substitutionsprinzip anschauen.
Verkürzt gesagt muss es jederzeit möglich sein an eine Klasse durch eine davon abgeleitete Klasse zu ersetzen.
Delphi-Quellcode:
// der Procedure muss es egal sein ob "myobject" vom Typ TBasisKlasse oder von einer davon abgeleiteten Klasse ist
procedure Machwas(TBasisKlasse: myobject);
begin
Dies ist auch der Grund dafür weshalb man in abgleiteten Klassen keine Properties oder Methoden entfernen kann.
Auch darf man die Sichtbarkeit (z.B. von public auf private) nicht einschränken.
Das Überdecken einer virtuellen Methode mit reintroduce ist ein ganz klares Pfui.

Häufig lassen sich Designprobleme lösen indem man virtuelle Methoden nicht public sondern nur protected macht:
Delphi-Quellcode:
TBaseclass = class(TObject)
protected
   procedure Internal_A(x: Boolean); virtual;
public
   procedure A(x: Boolean);
    
...
procedure TBaseclass.A(x: Boolean);
begin
    // code vorher
    // die Basisklasse kann hier sogar entscheiden ob sie Internal_A() überhaupt aufrufen möchte
    Internal_A(x); // virtuelle Methode aufrufen
    // code danach (Exceptionhandling, Logging, was auch immer)
end;
Diese Vorgehensweise ist auch als template method pattern bekannt.
fork me on Github
  Mit Zitat antworten Zitat
Benutzerbild von ChrisE
ChrisE

Registriert seit: 15. Feb 2006
Ort: Hechingen
504 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#7

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 27. Okt 2014, 06:42
Hallo MaBuSE,

@Chris: Das geht leider nicht, da ich das alte Verhalten ja durchaus wünsche, wenn (True) übergeben wurde. Außerdem sollte man wenn man es so macht das Schlüsselwort reintreduce verwenden um zu dokumentieren, dass man genau das machen möchte und nicht aus versehen virtual/override vergessen hat.
reintroduce bin ich zu 100% bei dir. Der Quelltext funktioniert aber tatsächlich, wenn du es so machst - zumindest bei mir XE5.

Delphi-Quellcode:
  TMutter = class(TObject)
  public
    procedure a(x: Boolean); virtual;
    procedure b;
  end;

  TKind = class(TMutter)
  public
    procedure a(x: Boolean); override;
    procedure b;reintroduce;
  end;

//....


procedure TMutter.a(x: Boolean);
begin
  Log('TMutter.a1');
  if x then
  begin
    // b aufrufen. (Wenn Self = TKind ist, dann wird TKind.b aufgerufen.)
    TKind(Self).b;
  end
  else
  begin
    // Hier sollte immer TMutter.b aufgerufen werden!!!
    TMutter(Self).b;
  end;
  Log('TMutter.a2');
end;
Oder is dir das zu explizit?

Gruß, Chris
Christian E.
Es gibt 10 Arten von Menschen, die die Binär lesen können und die die es nicht können

Delphi programming rules
  Mit Zitat antworten Zitat
Benutzerbild von MaBuSE
MaBuSE

Registriert seit: 23. Sep 2002
Ort: Frankfurt am Main (in der Nähe)
1.837 Beiträge
 
Delphi 10 Seattle Enterprise
 
#8

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 27. Okt 2014, 10:01
Oder is dir das zu explizit?
Ja
Ich bin Fan von KISS ("keep it stupid simple" oder "So einfach wie möglich, so kompliziert wie nötig")


Die Lösung mit dem internal_procname finde ich gut.
Sie ist vor allem einfacher zu verstehen, wenn man in ein paar Jahren noch mal über die Zeilen stolpert.

Internal_procname hatte ich schon in den protected Abschnitt gesetzt, bevor ich die Nachricht von sx2008 las.

Ich habe das mit dem internal_procname auch schon früher gemacht, aber aus irgendeinem Grund habe ich mich diesmal in dem Problem festgefahren. Man sieht den Wald vor lauter Bäumen nicht. Normalerweise finde ich die Lösung dann beim Erstellen des Beispielprogramms, diesmal nicht
Da ist es gut, wenn man von der DP einen Schups in die richtige Richtung bekommt.


Danke für alle Antworten.
MaBuSE
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)

Geändert von MaBuSE (27. Okt 2014 um 10:04 Uhr)
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#9

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 27. Okt 2014, 12:21
Delphi-Quellcode:
...
procedure TMutter.a(x: Boolean);
begin
...
  TKind(Self).b;
...
Schwerer Fehler.
1. Woher soll die Mutter wissen, das es ein Kind gibt?
2. Welche Methode soll aufgerufen werden, wenn es noch eine Ableitung der TMutter (oder von TKind) gibt?
3. Wenn die Mutter wissen muss, das es genau das eine Kind gibt, wozu dann zwei Klassen?

Mach es so, wie Uwe vorgeschlagen hat. Die Mutter ruft entweder explizit die eigene Methode oder aber die virtuelle Methode auf. So ist es OOP-konform und auch sauber.
  Mit Zitat antworten Zitat
Benutzerbild von MaBuSE
MaBuSE

Registriert seit: 23. Sep 2002
Ort: Frankfurt am Main (in der Nähe)
1.837 Beiträge
 
Delphi 10 Seattle Enterprise
 
#10

AW: Vererbung: Wie rufe ich Klasse.Parent.Methode auf?

  Alt 27. Okt 2014, 13:45
Delphi-Quellcode:
...
procedure TMutter.a(x: Boolean);
begin
...
  TKind(Self).b;
...
Schwerer Fehler.
1. Woher soll die Mutter wissen, das es ein Kind gibt?
Delphi-Quellcode:
...
  if Self is TKind then (Self as TKind).b;
...
2. Welche Methode soll aufgerufen werden, wenn es noch eine Ableitung der TMutter (oder von TKind) gibt?
In diesem Fall würde automatisch die von TMutter aufgerufen.
Da sie ja nicht mit reintreduce überschrieben wurde.

3. Wenn die Mutter wissen muss, das es genau das eine Kind gibt, wozu dann zwei Klassen?
Das ist die Schwachstelle. Dann wird es umständlich.
Delphi-Quellcode:
...
  if Self is TLieblingsKind then
  begin
   (Self as TLieblingsKind).b
  end
  else
  begin
    if Self is TStiefKind then
    begin
      (Self as TStiefKind).b
    end
    else
    begin
      // Mutterschaftstest durchführen.

      // Den Vorteil, den Mütter haben ist,
      // das sie in der Regel wissen, dass es ihre Kinder sind.
    end;
  end;
...
Aber wahrscheinlich gibt es dafür auch einen einfacheren Weg.

Fakt ist, das die Mutter alle Kinder (und Kindes-Kinder), die eine Sonderbehandlung benötigen, kennen muss.
Das icht schlecht.
Das ist bei der internal_proc Lösung nicht der Fall.

Mach es so, wie Uwe vorgeschlagen hat. Die Mutter ruft entweder explizit die eigene Methode oder aber die virtuelle Methode auf. So ist es OOP-konform und auch sauber.
Ich habe es wie Uwe gemacht.
Wenn du meine Texte gelesen hättest, wüsstest Du wie ich darüber denke.
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:20 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz