Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Geerbter Getter für lokale Objekt-Konstante? (https://www.delphipraxis.net/205007-geerbter-getter-fuer-lokale-objekt-konstante.html)

Andreas13 22. Jul 2020 22:53

Delphi-Version: XE5

Geerbter Getter für lokale Objekt-Konstante?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Delphi-Profis,
ich bräuchte mal wieder Eure Hilfe & Tipps. Für numerische Berechnungen mit verschiedenen mathematisch verwandten Gleichungen habe ich eine Basisklasse und abgeleitete Klassen gebildet. Jede Klasse besitzt u.a. eine lokale String-Konstante FGLTxt, die den Namen der jeweiligen Gleichung enthält und eine Integer-Konstante Fn_Var, die die Anzahl der Variablen angibt. Beide Konstanten tragen in allen Klassen den identischen Namen, haben jedoch unterschiedliche Inhalte.
Auf den GleichungsText FGLTxt und VariablenZahl Fn_Var greife ich von außen über die Properties GL bzw. n_Var zu. Mir geht es ausschließlich um diesen „äußeren“ Zugriff.
Ich habe mehrere funktionierende Lösungen erstellt, allerdings gefällt mir nicht, daß jede abgeleitete Klasse einen eigenen Getter für beide genannte Properties haben muß, die "innen" alle identisch aussehen (= Kopie der Eltern-Klasse).
Deswegen suche ich nach einer Vereinfachung, die wie folgt aussehen sollte (wenn es geht):
Nur die Basisklasse besitzt Getter für die beiden Properties, alle abgeleiteten Klassen sollen die geerbten Getter für ihre eigenen Konstanten benutzen.
Mein Problem dabei ist, daß bei allen meinen bisherigen Lösungsversuchen immer die Properties der Basisklasse zurückgegeben werden, nicht von der aktuellen Kind-Klasse.
Hat jemand eine Idee wie man das Problem einfach umsetzen kann? Mit einfach meine ich: einfacher als das Hineinkopieren der Getter der Vorfahren. Eine funktionierende, aber umständliche Lösung mit Zwischenvariablen sieht so aus:
Delphi-Quellcode:
Interface

Type
  TFunk_2 = Class(TObject)
    Strict Private
      CONST
        FGLTxt: String = 'Funktion mit ZWEI Variablen';
        Fn_Var: Integer = 2;

    Private
      VAR F_FGLTxt: String; // FGLTxt als VARIABLE

    Strict Private
      Function Get_n_var: Integer;

    Public
      Constructor Create;
      Procedure  Free;        
     
      Property GL  : String Read F_FGLTxt;
      Property n_Var: Integer Read Get_n_var;    
End;
//-------------------------------------------------------------
Type
  TFunk_3 = Class(TFunk_2)
    Strict Private
      CONST
        FGLTxt: String = 'Funktion mit DREI Variablen';
        Fn_Var: Integer = 3;
       
   Strict Private
     Function Get_n_var: Integer;

    Public
      Constructor Create;
      Property n_Var: Integer Read Get_n_var;
  End;
//-------------------------------------------------------------

Implementation

//-------------------------------------------------------------
{ TFunk_2 }
Constructor TFunk_2.Create;
Begin
  inherited;
  F_FGLTxt:= FGLTxt;
End;

Procedure TFunk_2.Free;
Begin
  inherited;
End;

Function TFunk_2.Get_n_var: Integer;
Begin
  Result:= Fn_Var;
End;
//-------------------------------------------------------------
{ TFunk_3 }

Function TFunk_3.Get_n_var: Integer;
Begin
  Result:= Fn_Var;
End;

Constructor TFunk_3.Create;
Begin
  inherited;
  F_FGLTxt:= FGLTxt;
End;
Im Anhang befindet sich ein Demo-Programm dazu.
Herzlichen Dank für Eure Tipps & Ideen im Voraus!
Viele Grüße
Andreas

himitsu 22. Jul 2020 23:05

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Der Vorfahre kann NIEMALS etwas vom Nachfahren wissen. (Ausnahme er liest es via RTTI aus der/seiner TypeInfo/ClassType der erzeugten Instanz, oder greifst in dessen Methoden hart auf die anderen Typen zu)
Das ist wie bei Class-Procedure und Class-Procedure-Static, wo Letztere nur den ClassType seiner Deklaration kennt und Ersteres den aktuellen Typ der Ableitung (der Variable).

Also gibt es nur die Möglichkeit mit OVERRIDE, denn dann kennt es auch der Vorfahre. (abgesehn von der RTTI)



PS: Auch bei Property gibt es sowas wie Override Overload und Reintroduce.

Delphi-Quellcode:
//Procedure Test; Reintroduce;
Property n_Var Read Get_n_var; // overload/override = das gleiche Property, aber beim Nachfahren-[B]Typ[/B] mit anderem Getter

//Procedure Test; {Override} Overload;
Property n_Var: Integer Read Get_n_var; // reintroduce = ein neues Property mit nit selbem Namen
Und natürlich bezieht sich sowas immer auf den Typ der Variable und nicht auf den internen Typ vom Create.

Erstes wird meistens genommen, wenn man nur Default bzw. Stored ändern will, aber es kann auch für Read, Write und Index benutzt werden und der Rest wird geerbt.
Also von unserem Verständnis eher wie ein Override, aber aus Sicht des Compilers eigentlich mehr ein Overload.



Wenn es nur eine Reihe der Vererbung gibt, dann könnte man eine Function/GetterFunction in den letzen Erben tun,
oder in der selben Unit eine Function/GetterFunction in die Basisklasse, wo anhand vom ClassType bzw. IS mit ein paar IFs jeweils in den gewünschten Typ castet und dann das "richtige" zurückgibt.
Aber das ist alles eigentlich echt unschöner und hart verknubbelter Code, somit bleibt nur noch Override oder RTTI.

Uwe Raabe 23. Jul 2020 00:02

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Brauchst du die Konstanten noch für was anderes oder könnte man bei einer Lösung auf die verzichten?

Andreas13 23. Jul 2020 10:03

AW: Geerbter Getter für lokale Objekt-Konstante?
 
@himitsu: Danke Himitsu für Deine ausführlichen Erläuterungen. Alles scheint wesentlich komplizierter zu sein als die triviale Lösung durch einfaches manuelles Kopieren der „zu vererbenden“ Methoden im Quellcode wie z.B.:
Delphi-Quellcode:
Type
  TFunk_2 = Class(TObject)
    Strict Private
      CONST
        FGLTxt: String = 'Funktion mit ZWEI Variablen';
        Fn_Var: Integer = 2;

  Public
      Property GL  : String  Read Get_GLTxt;
      Property n_Var: Integer Read Get_n_var;
  End;

Function TFunk_2.Get_GLTxt: String;
Begin
  Result:= FGLTxt;
End;

Function TFunk_2. Get_n_var: Integer;
Begin
  Result:= Fn_Var;
End;
...
//------------------------------ 
Type
  TFunk_3 = Class(TFunk_2) // [edit]: und natürlich nicht von TObject
    Strict Private
      CONST
        FGLTxt: String = 'Funktion mit DREI Variablen';
        Fn_Var: Integer = 3;

  Public
      Property GL  : String  Read Get_GLTxt;
      Property n_Var: Integer Read Get_n_var;
  End;

Function TFunk_3.Get_GLTxt: String;
Begin
  Result:= FGLTxt; // das sollte automatisch per Vererbung funktionieren
End;

Function TFunk_3. Get_n_var: Integer;
Begin
  Result:= Fn_Var; // das sollte automatisch per Vererbung funktionieren
End;
Ich wünschte mir an dieser Stelle eine echte („biologische“) Vererbung, so daß auch die privaten Konstanten und ihre Methoden weitervererbt werden können, wobei der Inhalt der vererbten Konstanten in den Nachfahr-Klassen zur Entwicklungszeit im Quellcode aktuell belegt werden könnte und kein Kopieren, kein overload, kein override der Methoden notwendig wären, sondern nur die eine einzige Deklaration in der Basisklasse, die über alle Unterklassen hinweg mit den aktuellen Konstanten der Unterklassen funktioniert.

Zitat:

Zitat von Uwe Raabe (Beitrag 1470162)
Brauchst du die Konstanten noch für was anderes oder könnte man bei einer Lösung auf die verzichten?

Am liebsten wären mir typisierte Variablen anstelle der Konstanten, welche ich bei der Klassendefinition mit ihren aktuellen Werten sofort belegen könnte. Aber das läßt der (mein XE5-) Compiler nicht zu. Den Wert der Konstanten FGLTxt und Fn_Var wollte ich weder als bloßen Kommentar handhaben, noch in den Constructor verschieben, sondern gleich am Anfang gut sichtbar und abrufbar darstellen, weil das für mich zur Entwicklungszeit bei der Vielzahl von abgeleiteten Klassen mit ähnlichen Gleichungen ein wichtiges Instrument der Fehlervermeidung & der Kontrolle darstellt.
Danke & Gruß, Andreas

Uwe Raabe 23. Jul 2020 10:43

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Ich kann zwar immer noch nicht ganz überblicken, was du da nun brauchst bzw. willst, aber ich hätte hier einen etwas anderen Ansatz (hab nur leider gerade kein XE5 zur Hand):
Delphi-Quellcode:
UNIT Unit_1;

Interface

uses
  System.Rtti;

type
  FuncInfoAttribute = class(TCustomAttribute)
  private
    FGL: string;
    Fn_Var: Integer;
  public
    constructor Create(AGL: string; An_Var: Integer);
    property GL: string read FGL;
    property n_Var: Integer read Fn_Var;
  end;

type
  TRttiHelper = record
  public
    class function FindAttribute<T: TCustomAttribute>(Source: TClass): T; overload; static;
    class function FindAttribute<T: TCustomAttribute>(Source: TRttiObject): T; overload; static;
  end;

//------------------------------------------------------------------------------------
Type
  [FuncInfo('Funktion mit ZWEI Variablen', 2)]
  TFunk_2 = Class(TObject)
  Strict Private
    function GetGL: String;
    Function Get_n_var: Integer;
  Public
    property GL: String read GetGL;
    Property n_Var: Integer Read Get_n_var;
  End;
  //------------------------------------------------------------------------------------

Type
  [FuncInfo('Funktion mit DREI Variablen', 3)]
  TFunk_3 = Class(TFunk_2)
  End;

  //------------------------------------------------------------------------------------

Implementation

function TFunk_2.GetGL: String;
var
  attr: FuncInfoAttribute;
begin
  attr := TRttiHelper.FindAttribute<FuncInfoAttribute>(ClassType);
  Result := attr.GL;
end;

Function TFunk_2.Get_n_var: Integer;
var
  attr: FuncInfoAttribute;
begin
  attr := TRttiHelper.FindAttribute<FuncInfoAttribute>(ClassType);
  Result := attr.n_Var;
End;


constructor FuncInfoAttribute.Create(AGL: string; An_Var: Integer);
begin
  FGL := AGL;
  Fn_Var := An_Var;
end;

class function TRttiHelper.FindAttribute<T>(Source: TClass): T;
var
  context: TRttiContext;
  myType: TRttiType;
begin
  Result := nil;
  context := TRttiContext.Create;
  myType := context.GetType(Source);
  if myType <> nil then begin
    Result := FindAttribute<T>(myType);
  end;
end;

class function TRttiHelper.FindAttribute<T>(Source: TRttiObject): T;
var
  attr: TCustomAttribute;
  attributes: TArray<TCustomAttribute>;
begin
  Result := nil;
  attributes := Source.GetAttributes;
  for attr in attributes do begin
    if attr is T then begin
      Result := T(attr);
      Break;
    end;
  end;
end;

Initialization
  TRttiContext.KeepContext;

Finalization
  TRttiContext.DropContext;

End.

Andreas13 23. Jul 2020 16:07

AW: Geerbter Getter für lokale Objekt-Konstante?
 
@Uwe Raabe: Danke Uwe!!! Das ist die high sophisticated Lösung eines wirklichen Master Developers!
Da ich bisher weder mit Rtti noch mit der Klasse TCustomAttribute zu tun hatte, muss ich mich zuerst in die Materie einlesen. Im Buch von Marco Cantu: Object Pascal Handbook (2015) habe ich eine recht verständliche Einführung gefunden.
Leider läßt sich Dein Delphi 10.4 Sydney – Code mit meinem XE5 nicht kompilieren. An folgenden drei Stellen rebelliert mein Compiler:

Delphi-Quellcode:
1): Implementation: class function TRttiHelper.FindAttribute<T>(Source: TRttiObject): T;
--> TRttiHelper.FindAttribute<T> ist rot unterstrichen

2): Initialization: TRttiContext.KeepContext;
--> KeepContext ist rot unterstrichen

3): Finalization: TRttiContext.DropContext;
--> DropContext ist rot unterstrichen
Vielleicht kennt jemand eine Abhilfe für Delphi XE5?
Danke & Gruß, Andreas

himitsu 23. Jul 2020 16:29

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Hmmm, technisch sollte es auch schon mindestens im XE(1) gehen.
Es kann aber sein, dass
Delphi-Quellcode:
: TCustomAttribute
auch in der Implementation angegeben sein muss/musste.
Und KeepContext/DropContext gab es früher nicht ... dein Teil kannst einfach weglassen. :stupid: (dann wird erst im TRttiContext.Create jedes mal der Context neu erstellt)

Und ob Man nun via RTTI ein [Attribut] oder die Konstante ausliest, macht eigentlich keinen Unterschied, ABER
* man muß aufpassen dass die Konstante nicht wegoptimiert wird (wenn sie nicht "direkt" benutzt wird)
* und dass man die privaten Teile der Klasse nicht aus der RTTI entfernt hat
http://docwiki.embarcadero.com/RADSt...ctive_(Delphi)

Uwe Raabe 23. Jul 2020 17:26

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Dann lass den TRTTIHelper ganz weg und mach das direkt in dem FuncInfoAttribute:
Delphi-Quellcode:
type
  FuncInfoAttribute = class(TCustomAttribute)
  private
    FGL: string;
    Fn_Var: Integer;
  public
    constructor Create(AGL: string; An_Var: Integer);
    class function FindAttribute(Source: TClass): FuncInfoAttribute;
    property GL: string read FGL;
    property n_Var: Integer read Fn_Var;
  end;

class function FuncInfoAttribute.FindAttribute(Source: TClass): FuncInfoAttribute;
var
  context: TRttiContext;
  myType: TRttiType;
  attr: TCustomAttribute;
  attributes: TArray<TCustomAttribute>;
begin
  Result := nil;
  context := TRttiContext.Create;
  myType := context.GetType(Source);
  if myType <> nil then begin
    attributes := myType.GetAttributes;
    for attr in attributes do begin
      if attr is FuncInfoAttribute then begin
        Result := FuncInfoAttribute(attr);
        Break;
      end;
    end;
  end;
end;
In den Gettern musst du dann die FindAttribute-Zeile so schreiben:
Delphi-Quellcode:

  attr := FuncInfoAttribute.FindAttribute(ClassType);

Andreas13 23. Jul 2020 18:01

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Vielen-vielen Dank Himitsu und Uwe!!!
Ich habe die beiden Zeilen
Delphi-Quellcode:
TRttiContext.KeepContext;
und
Delphi-Quellcode:
TRttiContext.DropContext;
auskommentiert. Auch Uwe’s neue Class Function habe ich eingebaut. So läßt sich alles kompilieren und es funktioniert korrekt!! Danke!

Noch zwei kurze Fragen bitte:
In der Klassen-Methode
Delphi-Quellcode:
class function FuncInfoAttribute.FindAttribute(Source: TClass): FuncInfoAttribute;
wird das Object mit
Delphi-Quellcode:
context := TRttiContext.Create;
erzeugt. Sollte ich evtl. noch
Delphi-Quellcode:
context.Free
einfügen, weil ich
Delphi-Quellcode:
TRttiContext.DropContext;
auskommentieren mußte? Wäre hier evtl. ein Speicherschutzblock try .. finally notwendig? Oder ist es bei Klassenmethoden nicht erforderlich?
Danke Euch allen & viele Grüße, Andreas

himitsu 23. Jul 2020 18:13

AW: Geerbter Getter für lokale Objekt-Konstante?
 
Das ist ein Record, kein Objekt.

Intern liegen Interfaces, welche automatisch freigegeben werden.
Und bei gemangten Variablen hat Delphi heimlich ein Try-Finally in der Funktion versteckt. (quasi im BEGIN und END davon, für Interfaces, Strings, DynArrays usw.)


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:17 Uhr.
Seite 1 von 2  1 2      

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