Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Class helper wird übersprungen wenn... (https://www.delphipraxis.net/182325-class-helper-wird-uebersprungen-wenn.html)

Der schöne Günther 16. Okt 2014 19:33

Delphi-Version: 5

Class helper wird übersprungen wenn...
 
Properties im Spiel sind.

Beispiel: Ich schreibe einen Helfer für eine
Delphi-Quellcode:
getValue(): Integer
-Funktion. Fügt eine Unterklasse oder ein passendes Interface nun eine Property
Delphi-Quellcode:
Value: Integer
ein, bekommt man über diese Property immer den reinen Getter, ohne die Hilfsfunktion!

Folgende Typen
Delphi-Quellcode:
TSomeClass = class
   public function getValue(): Integer; // Ergibt 42
end;

TSubClass = class(TSomeClass)
   property Value: Integer read getValue;
end;

TSomeClassHelper = class helper for TSomeClass
   public function getValue(): Integer; // Verdoppelt den Wert von getValue()
   public property Value: Integer read getValue;
end;
erzeigen unsinnigerweise folgende Ausgabe:
Code:
   WriteLn( TSomeClass.Create().Value ); // 84
   WriteLn( TSubClass.Create().Value ); // 42

Kann ich etwas dagegen tun oder nur weinen? :cry:

Meine Intention war die Methode
Delphi-Quellcode:
SetNodeValue(OleVariant)
von
Delphi-Quellcode:
TXMLNode
zu patchen. Und das ruft man üblicherweise über die Property
Delphi-Quellcode:
NodeValue
von
Delphi-Quellcode:
IXMLNode
auf.

Daniel 16. Okt 2014 19:42

AW: Class helper wird übersprungen wenn...
 
Du kannst mit einem Class-Helper nichts überschreiben. Wenn Du den Getter tauschen möchtest, müsstest Du die Methoden-Tabelle der Klasse patchen. Hallvard Vassbotn hat in seinem Blog einiges dazu geschrieben.

Uwe Raabe 16. Okt 2014 20:03

AW: Class helper wird übersprungen wenn...
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1276299)
Meine Intention war die Methode
Delphi-Quellcode:
SetNodeValue(OleVariant)
von
Delphi-Quellcode:
TXMLNode
zu patchen. Und das ruft man üblicherweise über die Property
Delphi-Quellcode:
NodeValue
von
Delphi-Quellcode:
IXMLNode
auf.

Class Helper wirken nicht auf Interfaces. Dein Beispiel trifft leider nicht dein Problem.

himitsu 16. Okt 2014 20:09

AW: Class helper wird übersprungen wenn...
 
Oder anders ausgedrückt: Ein Class-Helper kann nur nachträglich erweitern, aber nichts überschreiben/ändern.


In deinem Fall wäre doch ein virtueller Getter mit späteren überschreiben besser?

Und wenn der Getter (die Methode) virtuell ist, dann könnte man dein Vorhaben auch über die RTTI erledigen (TVirtualMethodInterceptor, wenn man die VMT nicht manuell manipulieren will)

Dejan Vu 17. Okt 2014 04:19

AW: Class helper wird übersprungen wenn...
 
Class Helper sind ziemlich gefährlich. Denn es ist durch den Blick in den Code einer Klasse (hier) nicht ersichtlich, was sie genau macht, denn es könnte ja irgendwo noch ein Class Helper verborgen sein. Schlimm genug von Borland, das es das Überschreiben/Ersetzen einer Methode durch Class Helper erlaubt.

Class Helper haben aber natürlich ihre Daseinsberechtigung, denn das *erweitern* von Klassen ist nicht nur praktisch, sondern lagert redundante Hilfsfunktionen eben in ... 'class helper' aus.

Blödes Beispiel:
Delphi-Quellcode:
Type
  TMemoHelper = Class Helper for TMemo
  public
    Procedure Writeln (const text : String);
  end;
Die Writeln-Methode ist sehr praktisch, hat aber eigentlich in der reinen TMemo-Klasse nichts zu suchen, weil sie die Funktionalität der Klasse nicht erweitert, sondern nur eine handliche Abkürzung für 'Lines.Add(Text)' anbietet. Die VErwendung von 'WriteLn' spart Tipparbeit und Code und wenn später die Semantik von 'WriteLn' irgendwie noch erweitert werden soll (loggen z.B.) hat man gleich den richtigen Punkt (nämlich einen einzigen), wo man ansetzen kann.

Mit Class Helpern kann man seine Klasse alltagstauglich machen, ohne sie zu überladen.

PS: Bitte zerpflückt das Beispiel nicht, denn es ist nur ein Beispiel.

Der schöne Günther 17. Okt 2014 09:24

AW: Class helper wird übersprungen wenn...
 
Ich verstehe ausnahmsweise bei keinem von euch, was er mir sagen will :love:

Ich stelle noch einmal dar was ich meinte:
  • Man setze einen Class Helper ein der einen Getter ersetzt/verändert. Alles funktioniert wie gewohnt. Ist der Helper im Scope, werden seine Dinge genommen.
  • Man führt in der Klasse eine Property ein welche den normalen Getter benutzt
  • Fragt man nun die Property ab bekommt man den "unbeholfenen" Getter obwohl der Class Helper im Gültigkeitsbereich ist
.

Der Grund: Die Property der Basisklasse weiß nichts vom Helper. Aufrufe auf die Property gehen direkt auf den Getter zu und überspringen die Hilfsmethode komplett.

Hier noch einmal Futter für Quellcode-Freunde:

Delphi-Quellcode:
program Project6;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  ClassUnit in 'ClassUnit.pas',
  ClassHelperUnit in 'ClassHelperUnit.pas';


begin
  try
   WriteLn( TMyClass.Create().getValue() ); // Ergibt 99
   WriteLn( TMyClass.Create().Value ); // Ergibt 42
  except
   on E: Exception do
     Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end.
Delphi-Quellcode:
unit ClassUnit;
interface

type
   TMyClass = class
      function getValue(): Integer;
      property Value: Integer read getValue;
   end;

implementation

function TMyClass.getValue(): Integer;
begin
   Result := 42;
end;
end.
Delphi-Quellcode:
unit ClassHelperUnit;

interface uses ClassUnit;

type
   TMyClassHelper = class helper for ClassUnit.TMyClass
      function getValue(): Integer;
   end;

implementation

function TMyClassHelper.getValue():Integer;
begin
   Result := 99;
end;
end.

Die Abhilfe wäre, im Klassenhelper die Property auch zu überschreiben. Also eigentlich die Lösung für Leute die alleine denken können und nicht gleich ein Forum überfluten.
Das blöde daran ist nur, dass man gekniffen wird wenn eine Unterklasse (oder eben ein Interface) die Property erst einführt. Dann bekommt man das "unbeholfene" Verhalten ohne es wahrscheinlich zu ahnen. Gefährliche Sachen...

Uwe hatte also von Anfang an recht, ich hätte gleich anfangen können zu weinen. Denn in meinem Fall führt das Interface die Property ein, die Klasse hat die nicht. Und in Delphi gibt es keine Interface-Helper.

himitsu 17. Okt 2014 09:39

AW: Class helper wird übersprungen wenn...
 
Bei Class-Helpern, sollte man eben nie etwas überschreiben verdecken und wenn man es dennoch macht, dann ist man selber Schuld und muß genauso aufpassen, wie bei den bösen WITHs. :stupid:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  R: TRect;
behin
  R := irgendwas;
  with R do
    {Self.}Width := Right - Left;
end;
Jetzt ratet mal, was die "neueren" Records für nette Hilfsfunktionen besitzen und warum der Code so jetzt nicht mehr geht. :roll:

Der schöne Günther 17. Okt 2014 09:57

AW: Class helper wird übersprungen wenn...
 
Kein Augenrollen, das ist auch gut so. Allen die
Delphi-Quellcode:
with
benutzen kann man das Leben nicht schwer genug machen! :smile2:
Das ist der "expert"-Modus von Delphi.

Stevie 17. Okt 2014 12:27

AW: Class helper wird übersprungen wenn...
 
Die Regel scheint hier zu sein, dass Member in einer Subklasse der "geholfenen" Klasse vorrang vor den Membern im Helper haben.
Das ergibt sich aber aus dem Typ der Variable auf den die Methode aufgerufen wird.

Wenn man drüber nachdenkt, dann wird schon klar, warum das so ist:
Wenn ich in einer abgeleiteten Klasse einen Member einführe, den es über den Class Helper der Basisklasse auch schon gibt, dann wird der implizit verdeckt.
Hier hat man dafür entschieden, die Vererbung gegen den Helper gewinnen zu lassen, was durchaus nachvollziehbar ist.

Die Extension Methods in C# können übrigens auf keinen Fall schon vorhandene Methoden verdecken. Dass die Helper in Delphi das können, ist eher nen Designfehler (ein manchmal nützlicher muss ich zugeben).

Willst du nun auch, dass der Helper auch explizit für TSubClass funktioniert, dann musst du auch einen expliziten Helper schreiben.

Delphi-Quellcode:
  TSubClassHelper = class helper(TSomeClassHelper) for TSubClass
  end;
Eine saubere Lösung sähe allerdings eher so aus, den Member im Helper so zu nennen, dass er nicht mit in Subklassen eingeführten Membern kollidiert.

Uwe Raabe 17. Okt 2014 12:37

AW: Class helper wird übersprungen wenn...
 
Knackpunkt ist einfach, daß über ein Interface IXMLNode auf den Getter zugegriffen wird. Dessen Implementation liegt aber nun mal in TXMLNode und das kennt den Code des Class Helpers eben nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:50 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