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/)
-   -   "forward" von Units mit Record/Class Helpern (https://www.delphipraxis.net/197686-forward-von-units-mit-record-class-helpern.html)

Rollo62 28. Aug 2018 07:50

Delphi-Version: 5

"forward" von Units mit Record/Class Helpern
 
Hallo zusammen,

mangels besserer Bezeichnung nenne ich das was ich möchte mal "Unit forwarding",
und zwar aus einem Base-Unit die Klassen in ein Interface-Unit übernehmen und re-deklarieren.
So das nur noch das Interface-Unit in der Anwendung benutzt werden muss.


Base-Unit
Delphi-Quellcode:
interface Pages.Types;

type
    TPage_Elem       = (Eins, Zwei);            // Hier werden diese Klassen etc. definiert
    TPage_Elem_Helper = record helper for TPage_Elem; // Hier kann es Helper geben
       procedure DoDomething;
    end;

...
...
Interface-Unit
Delphi-Quellcode:
interface Pages;

Uses
    Pages.Types // Hier sind Klassen, Records, Enums, etc. definiert, s.o.
  ;

type
    TPage_Elem       = Pages.Types.TPage_Elem;        // Hier werden diese Klassen etc. ge-forwared
    TPage_Elem_Helper = Pages.Types.TPage_Elem_Helper; // das möchte ich auch mit Helpern so machen
und dann nur per Interface nutzen, während die Implementation in den Base-Units bleibt.

Delphi-Quellcode:
interface Main_Unit;

Uses
  Pages; // die Typen aus den Base-Units werden hier ge-forwarded und sind direkt nutzbar

...
...

procedure Test;
begin

  LElem := TPage_Elem.Eins; // Alles OK, kann ich benutzen mit Klassen, Records, Enums ...
  LElem.DoSomething  // <-- ! Hier kann es nicht kompiliert werden, die Base-Units werden benötigt

end;

Gibt es da einen Trick wie man auch die Helper aus den Base-Units "forwarden" kann ?
Oder ist sowas etwa ein schlechter Stil ?

Rollo

Der schöne Günther 28. Aug 2018 08:01

AW: "forward" von Units mit Record/Class Helpern
 
Spaßig dass es die Code Completion sogar vorschlägt, der Compiler aber dann aber nicht kann :stupid:


Ich sehe den Gefallen nicht denn man sich mit diesem "Forwarding" tut. Um sich eine Zeile im
Delphi-Quellcode:
uses
zu sparen?

Rollo62 28. Aug 2018 08:03

AW: "forward" von Units mit Record/Class Helpern
 
Bei viel Modularisierung ist es eben nicht nur eine Unit, sondern vielleicht auch 5.
Das macht sich schon bemerkbar :stupid:

Aviator 28. Aug 2018 08:42

AW: "forward" von Units mit Record/Class Helpern
 
Wenn du so etwas schon machen willst und es dann auch noch Interface nennst, dann solltest du am besten auch mit Interfaces arbeiten :wink:

Dann sprichst du das Thema Modularisierung an. Da bietet es sich auch gleich im Zusammenhang mit Interfaces an, mit Factories zu arbeiten.

Schau dir das Thema mal an. Damit erreichst du eigentlich das was du willst und es wird noch modularer.

Edit:
Oh Moment ... du sprichst hier von Records :?
Naja vielleicht bringt es dir trotzdem was oder du kannst auf Klassen umstellen. (Die benutze ich zumindest lieber als Records)

Der schöne Günther 28. Aug 2018 09:53

AW: "forward" von Units mit Record/Class Helpern
 
Zitat:

Zitat von Rollo62 (Beitrag 1411795)
Bei viel Modularisierung ist es eben nicht nur eine Unit, sondern vielleicht auch 5.
Das macht sich schon bemerkbar :stupid:

Ich sehe trotzdem nicht dass das ein Vorteil ist. Damit versteckt man nur vor sich selber wovor die aktuelle Unit grade abhängig ist.


PS: Ich kann es schon etwas verstehen wenn man den Helper für ein Enum vermisst, darum geht es ja eigentlich. Entweder inkludierst du die Unit wo der Helper drinsteckt, oder du verabschiedest dich ganz vom Enum und nimmst stattdessen gleich einen Record der ein Enum kapselt. Deinem Record kannst du nun Methoden verpassen und brauchst keinen Helper mehr. Durch Operator-Überladung kannst du dein altes Enum diesem Record zuweisen und umgekehrt...


Delphi-Quellcode:
type
   TMyEnum = (uno, dos, tres);

   TMyEnumStruct = record
      Value: TMyEnum;
      function Next(): TMyEnumStruct;
      function Prev(): TMyEnumStruct;
      function ToString(): String;
      class operator Implicit(a: TMyEnumStruct): TMyEnum;
      class operator Implicit(a: TMyEnum): TMyEnumStruct;
   end;
und

Delphi-Quellcode:
procedure p();
var
   myEnum: TMyEnumStruct;
begin
   myEnum := TMyEnum.dos;
   myEnum := myEnum.Next();

   WriteLn( myEnum.ToString() );
end;

Rollo62 28. Aug 2018 10:09

AW: "forward" von Units mit Record/Class Helpern
 
@Aviator
Ich nenne es Interface weil auch eins drin ist.
Die Helper sind Hilfs-Typen für das Interface.

@Günther
Mir ging es eigentlich im Wesentlichen um die Helper dabei.
Die funktionieren nämlich weder bei Enum noch den anderen Sprachelementen.

Ich möchte das Konzept so ja auch erstmal für mich einführen.
Ich nenne das für mich "API", in der Hoffnung das ich komplexere Dinge simpel in der Anwenung zusammenfassen kann.

Also z.B. Pages API die mir Alles für die Seitenverwaltung mitliefert, auch die Unterklassen, etc.
Es geht mir um eine einfache Lernkurve dabei, wo man eben nicht Alle 5 Unter-Units genau kennen muss.

Ich habe es bisher auch anders gelöst, und spiele gerade mit diesem Konzept etwas herum,
und bin nur verwundert das ausgerechnet die Helper sich nicht "forwarden" lassen,
der Rest der Elemente aber schon.
Liegts an mir, oder an ... ?

Rollo

Uwe Raabe 28. Aug 2018 10:34

AW: "forward" von Units mit Record/Class Helpern
 
Zitat:

Zitat von Rollo62 (Beitrag 1411804)
und bin nur verwundert das ausgerechnet die Helper sich nicht "forwarden" lassen,

Das liegt an der Art wie Helper implementiert sind. Man kann das in etwa so betrachten wie globale Prozeduren und Funktionen.

Aus
Delphi-Quellcode:
type
    TPage_Elem_Helper = record helper for TPage_Elem
       procedure DoSomething;
    end;
wird dann intern so etwas in der Art
Delphi-Quellcode:

procedure TPage_Elem_Helper_DoSomething(var Self: TPage_Elem);
Damit der Compiler das auflösen kann, muss die Unit auch im uses erscheinen.

Es ist halt so, daß Helper nicht überall dort einsetzbar sind, wo andere Konstrukte funktionieren. Dies hier ist eben ein solcher Fall.

Der schöne Günther 28. Aug 2018 10:52

AW: "forward" von Units mit Record/Class Helpern
 
Ich finde aber auch Helper sind ein Hilfsmittel, ein Notnagel oder ein bisschen Syntax-Zucker um Dinge lesbarer zu machen. Ein Helper ist, fast immer lokal auf die aktuelle Unit zugeschnitten.

Ich geb's ja zu, ich habe, genau wie du, auch oft Enums und direkt in der gleichen Unit gibt's noch den passenden Helper dazu, ob man will oder nicht.

Aber das ganze mit einem Record kapseln - Ist zwar ein bisschen mehr tippen, aber sonst in jeder Hinsicht besser, oder? Dann nimmst du deine Methoden (und sogar Variablen) auch gleich "mitgeforwardet" mit ;-)

Rollo62 28. Aug 2018 13:01

AW: "forward" von Units mit Record/Class Helpern
 
Hallo Uwe,

ja dankesehr für die Erleuchtung, dann muss ich das Helper.forwarding wohl vergessen.


@Der schöne Günther,

Ok, das muss ich dann wohl so ähnlich machen.

Ich arbeite bevorzugt mit Enums, weil die sich im Gegensatz zu Konstanten "automatisch" richtig verhalten in den meisten Fällen, und super leicht erweiterbar sind.
Solche Enums ersetzen bei mir fast alle "Magic-Numbers".

Mit dem einbau in Records: Meinst du das Beste aus beiden Welten (enum/record-class) zu kombinieren ?
So in etwa:

Delphi-Quellcode:
type
  TTest_Record_Level2 = record
  public
  type
      TEnum = (COneHundret = 1, CTwoHundret, CThreeHundret);

  public
      class function OneHundret : TTest_Record_Level2.TEnum; {inline;} static; // inline needs Base unit
      class function TwoHundret : TTest_Record_Level2.TEnum; {inline;} static;
      class function ThreHundret : TTest_Record_Level2.TEnum; {inline;} static;

      class function ToInteger(const AEnum : TTest_Record_Level2.TEnum) : Integer; static;
  end;

...
...
...

class function TTest_Record_Level2.OneHundret: TTest_Record_Level2.TEnum;
begin
    Result := TTest_Record_Level2.TEnum.COneHundret;
end;

class function TTest_Record_Level2.ThreHundret: TTest_Record_Level2.TEnum;
begin
    Result := TTest_Record_Level2.TEnum.CTwoHundret;
end;


class function TTest_Record_Level2.TwoHundret: TTest_Record_Level2.TEnum;
begin
    Result := TTest_Record_Level2.TEnum.CThreeHundret;
end;

class function TTest_Record_Level2.ToInteger(const AEnum: TTest_Record_Level2.TEnum): Integer;
begin
    Result := Integer( AEnum ) * 100;
end;


...
...
...

procedure TForm1.Button5Click(Sender: TObject);
var
  LEnum : TTest_Record_Level2.TEnum;
  LInt: Integer;
begin

   LEnum := TTest_Record_Level2.OneHundret;
   LInt := TTest_Record_Level2.ToInteger( LEnum );
   if LEnum = TTest_Record_Level2.OneHundret then
   begin
       LEnum := TTest_Record_Level2.TwoHundret;
       LInt := TTest_Record_Level2.ToInteger( LEnum );
   end;

end;
Das funktioniert, kapselt die Enums, hat aber den Charme der simplen Helper etwas verloren :-(
Kann man auch gleich class helper statt record helper nehmen, ist aber womöglich kein Unterschied.

Delphi-Quellcode:
      class function OneHundret : TTest_Record_Level2.TEnum; {inline;} static;
      class function TwoHundret : TTest_Record_Level2.TEnum; {inline;} static;
      class function ThreHundret : TTest_Record_Level2.TEnum; {inline;} static;
Wobei man die Funktionen auch weglassen, und direkt mit dem TEnum arbeiten kann.
Das mache ich z.T. bei einigen Klassen schon so.
Der Vorteil wäre wohl beim besseren AutoComplete, und das die Funktionen auch etwas mehr machen könnten als nur Werterückgabe.


Delphi-Quellcode:
      class function OneHundret : TTest_Record_Level2.TEnum; {inline;} static; // inline

Inline geht beim "forwarden" leider auch nicht, weil dann auch die Base-Units gebraucht werden (nicht zum Kompilieren, aber um 0 warnings zu Erreichen).


Stilfrage:
Da wäre dann noch die Frage zum Stil des "Unit-Forwarding".
Ich habe ja schon gehört das dies Einige nicht gut finden, ich sehe da nur ein Problem das man
"versteckte" Abhängigkeiten bekommt, im Gegensatz zu "offen sichtbaren" Abhängigkeiten in den uses.
Sonst sehe ich da keine anderen grundsätzlichen Probleme, oder gibt es die etwa doch ?

Rollo

Uwe Raabe 28. Aug 2018 13:48

AW: "forward" von Units mit Record/Class Helpern
 
Zitat:

Zitat von Rollo62 (Beitrag 1411812)
Ich habe ja schon gehört das dies Einige nicht gut finden, ich sehe da nur ein Problem das man
"versteckte" Abhängigkeiten bekommt, im Gegensatz zu "offen sichtbaren" Abhängigkeiten in den uses.

Die Basis-Unit wird doch in jedem Fall gebraucht, selbst wenn sie nicht explizit in der jeweiligen Uses-Anweisung drin steht.

Was verstehst du denn unter einer "versteckten" Abhängigkeit?
Ich verstehe darunter eigentlich genau das, was eben durch dieses "Typ-Forwarding" ausgelöst wird: Man nutzt ja schon die Basis-Unit, gibt sie aber nicht in der Uses-Anweisung an. Man ist also abhängig von der Basis-Unit, sieht es aber nicht gleich.

Die besagte inline-Warnung zeigt das ganz deutlich: Sie ist ja nicht nur eine Warnung, sondern der Compiler erzeugt dann auch anderen Code (nämlich non-inlined). Du bekommst also dasselbe wie ohne das inline. Der Compiler tut einfach so als wäre es nicht da.


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