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 for Interface (https://www.delphipraxis.net/183972-class-helper-interface.html)

himitsu 16. Feb 2015 22:20

Delphi-Version: XE7

Class Helper for Interface
 
Man kann doch Class/Record Helper nun an "alles" dran hängen?

Warum nicht auch an Interfaces?

Stevie 16. Feb 2015 22:37

AW: Class Helper for Interface
 
Weil Embarcadero einfach zu be... ach, lassen wir das, sonst reg ich mich nur wieder auf...

Dejan Vu 17. Feb 2015 07:16

AW: Class Helper for Interface
 
Class Helper sind doch dazu gedacht, eine Klasse nachträglich etwas zu pimpen.
Interfaces sind Verträge.

Wieso sollte ich Verträge nachträglich noch aufbohren wollen. Dann passt das doch nicht mehr.

Wo ist mein Denkfehler?

Stevie 17. Feb 2015 07:22

AW: Class Helper for Interface
 
Berühmtestes Beispiel: IEnumerable<T>

Eigentlich müsste dieses Interface nur so aussehen:

Delphi-Quellcode:
type
  IEnumerable<T> = interface
    function GetEnumerator: IEnumerator<T>
  end;
Und über Extension Methodseinen interface helper kann man nun alle möglichen Operationen, die auf GetEnumerator operieren, implementieren.
Das ermöglicht einem eine Verkettung dieser Operationen -> syntax Sugar.

Anderes Beispiel:

Delphi-Quellcode:
type
  ILogger = interface
    procedure WriteLogEntry(const entry: TLogEntry);
  end;
Simples Interface, wenn man neue Logger bauen will, muss man nur eine Methode implementieren -> gut!

Aber den Logger zu benutzen ist nun eher umständlich, denn es mag nun verschiedene Eigenschaften in TLogEntry geben, die ich immer befüllen muss.
Also würde sich so ein Interface helper anbieten:

Delphi-Quellcode:
type
  ILoggerHelper = interface helper for ILogger
    procedure LogValue<T>(const value: T)
    procedure EnterMethod(const methodName: string);
    procedure LeaveMethod(const methodName: string);
  end;
In diesen Methoden wird nun nix anderes gemacht als die Information in ein TLogEntry gepackt und dann an die WriteLogEntry Methode übergeben.
D.h. ich kann die Funktionalität des Interfaces erweitern, ohne den Vertrag zu verletzen und ohne weitere Kopplung für den Implementierenden zu erzeugen.

Im Grunde sind so helper Methoden nix anderes als das:

Delphi-Quellcode:
procedure Methode(Self: <gehelpter type>; args)
Aber ein:
Delphi-Quellcode:
logger.EnterMethod('foo');

// liest sich einfach besser als:

EnterMethod(logger, 'foo');
Und umso mehr, wenn ich solche Methoden verketten kann (ja, das geht mit Spring4D aber auch nur, weil die ganzen Methoden auf IEnumerable<T> sitzen und somit dem Implementierer aufgezwungen werden, obwohl sie nur auf GetEnumerator aufsetzen -> fette Klassen):
Delphi-Quellcode:
for c in customers.Where(...).Ordered.Take(10) do
  // mach was

TiGü 17. Feb 2015 07:51

AW: Class Helper for Interface
 
Aber warum nicht einfach
Delphi-Quellcode:
ILoggerHelper = interface(ILogger)
?

Stevie 17. Feb 2015 07:55

AW: Class Helper for Interface
 
Zitat:

Zitat von TiGü (Beitrag 1290133)
Aber warum nicht einfach
Delphi-Quellcode:
ILoggerHelper = interface(ILogger)
?

Und, wie bekomm ich dann die Methoden von ILoggerHelper aufgerufen, wenn ich nur ILogger implementiere? :roll:
Ja, Dekorator bauen, etc... aber darum gehts nicht.

Außerdem könnte ich, wären die helper vernünftig implementiert auch mehrere gleichzeitig aktiv haben, mach das mal mit Vererbung

Eine mögliche Syntax die ich mir auch vorstellen könnte wäre sowas (woher ich das wohl habe...):

Code:
procedure EnterMethod(extends logger: ILogger; const methodName: string);
Und die kann man dann so aufrufen:

Code:
logger.EnterMethod('foo')
Simpler Syntaxzucker also. Und wenn ich das gerade richtig überblicke dürften dort auch alle Regeln von normalen Routinen (sprich, welche Methode ist im Scope, Overload resolution etc) greifen.

Der schöne Günther 17. Feb 2015 08:04

AW: Class Helper for Interface
 
Bei der Gelegenheit könnte man gleich noch eine Diskussion über, seit Java 8 vorhandene, Standard-Implementierungen für Interfaces anstoßen: Ein Interface kann eine Standard-Implementierung einer Methode sowie statische Methoden vorgeben. Im Endeffekt geht das ja in eine stark ähnliche Richtung.

Würdet ihr, so etwas gerne in Delphi sehen wollen? Ich dachte erst "Was für ein Quatsch" aber in der Zwischenzeit bin ich auch über ein paar Fälle gestolpert wo man das hätte gebrauchen können...

Sir Rufo 17. Feb 2015 08:06

AW: Class Helper for Interface
 
Unter Delphi ist das mit den Interface Helpern doch uninteressant, denn die Interfaces unterstützen keine Generics
Delphi-Quellcode:
IFoo<T> = interface
  function AsType<TResult> : TResult; // <- Zonk
end;
Mit einem
Delphi-Quellcode:
record
drumherum ist das dann wieder kein Problem (ist ja in Spring4D auch so vorhanden ;))
Delphi-Quellcode:
Foo<T> = record
  class operator implicit ( a : IFoo<T> ) : Foo<T>;
  function AsType<TResult> : TResult;
end;
Wenn also diese Schranke fällt, dann ist auch der Interface Helper interessant. Bis dahin müsste man sich je nach Fall immer zwischen Interface Helper oder eben doch Record entscheiden. Dann doch lieber immer auf die gleiche Weise

himitsu 17. Feb 2015 08:12

AW: Class Helper for Interface
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1290137)
aber in der Zwischenzeit bin ich auch über ein paar Fälle gestolpert wo man das hätte gebrauchen können...

Wenn man es so nimmt, dann wollte ich diesesmal genau das.

Im Grunde geht es genau um das, wozu diese Helper erfunden wurden. Das "erweitern" der Funktionen, von irgedwas (Klasse/Record/Typ), ohne diesen Typen direkt zu verändern.


Bei Klassen kann man einfach vererben, aber bei Interfaces geht das nicht, denn dort muß man das "Objekt" hinter dem Interface verändern/kapseln und Funktionsaufrufe an das originale Interface weiterreichen.

Man kann ein eigenes Objekt schreiben, daß beim QueryInterface die Zugriff auf das/die originalen Interfaces weitergibt, aber rückwärts geht das nicht, daß das "fremde" Objekt mein Interface nicht kennt.
Dazumal sind das zwei getrennten Objekte und die Referenzzählung/Freigabe ist nicht lösbar, ohne das andere Interface komplett zu kapseln.

Stevie 17. Feb 2015 08:13

AW: Class Helper for Interface
 
Zitat:

Zitat von Sir Rufo (Beitrag 1290138)
Unter Delphi ist das mit den Interface Helpern doch uninteressant, denn die Interfaces unterstützen keine Generics

Genau, weil b nicht geht, lassen wir a auch gleich lieber sein.
Dass es keine generischen Methoden auf Interface selber gibt, hat einen technischen Grund, nämlich den gleichen, warum es keine virtuellen Methoden mit generischen Parametern gibt: ich hab nur einen Slot in der VMT dafür, für welches T soll ich den nehmen?

Methoden in einem Interface Helper wären aber nicht virtuell.
Viel schlimmer: man kann aktuell keine helper für generische Typen bauen!


Zitat:

Zitat von Sir Rufo (Beitrag 1290138)
Mit einem
Delphi-Quellcode:
record
drumherum ist das dann wieder kein Problem (ist ja in Spring4D auch so vorhanden ;))

Wenn der Compiler da nicht bei so nem record mit ner Menge Methoden ohne das IDEFixPack, wo Andreas nen Fix für den Compiler eingebaut hat, total abröcheln würde, dann würd ich das mehr benutzen.

himitsu 17. Feb 2015 08:19

AW: Class Helper for Interface
 
Einen Implicit-Cast mit einem Record hatte ich zwar schon überlegt, aber die Lösung fand ich nicht gut, denn erstmal ist das für statische Methoden, auf den Interface-Typ nicht möglich
und dann kann man die Codevervollständigung auch gleich komplett vergessen.

Dejan Vu 17. Feb 2015 08:29

AW: Class Helper for Interface
 
Wenn also Foo IFoo implementiert und ich dann IFoo nachträglich über einen Helper erweitere, z.B. mit der Methode Bar()... Was ist dann mit Foo? Implementiert Foo dann noch IFoo?
In meiner DLL:
Delphi-Quellcode:
type
  IFoo = Interface
     procedure FooMethod;
  end;

  Foo = Class (IFoo)
    public procedure FooMethod;
  end
Foo implementiert also IFoo. Gut.

Nun erweitere ich IFoo mit einem Helper
Delphi-Quellcode:
  IFooHelper = interface helper for IFoo
    procedure Bar;
  end;
...
var Foo : IFoo;
...
Foo.Bar; // das geht also.

Var oldFoo : Foo;

begin
  Foo.Bar // und das?
Preisfrage: Wo ist 'Bar' denn implementiert? Im Interface sicherlich nicht...

Stevie 17. Feb 2015 08:38

AW: Class Helper for Interface
 
Zitat:

Zitat von Dejan Vu (Beitrag 1290143)
Preisfrage: Wo ist 'Bar' denn implementiert? Im Interface sicherlich nicht...

Delphi-Quellcode:
IFooHelper = interface helper for IFoo
  procedure Bar;
end;
ist equivalent zu:

Delphi-Quellcode:
procedure Bar(const Self: IFoo);
Damit kannste dir die Frage selbst beantworten.

himitsu 17. Feb 2015 08:52

AW: Class Helper for Interface
 
Die Funktion ist eben nicht im Interface, genauso wenig, wie bei den anderen Record/Class-Helpern oder so wie eine class procedure nicht im Objekt steckt, zu Welchem sie deklariert wurde.

Helper sollen nicht das Objekt/Interface selbst verändern, sondern nur darauf aufbauend eine Funktionen hinzufügen. (drum darf/kann man dort auch keine Felder deklarieren oder dynamic/virtual/abstract verwenden, weil das eine Veränderung erfordern würde)

sh17 17. Feb 2015 09:01

AW: Class Helper for Interface
 
Wo wir gerade dabei sind, weiß jemand, ob es irgendwann Unterstützung für mehrere Helper für einen Typ geben wird? Würde mir gern meinen eigenen String-Helper basteln, der haut mir ja momentan aber den vom Delphi raus. Ich hätte aber gern beide, OHNE jetzt eine Kopie der Delphi-Variante pflegen zu müssen.

Der schöne Günther 17. Feb 2015 09:05

AW: Class Helper for Interface
 
Bestimmt. Die ersten Requests im QC sind erst seit knapp sieben Jahren offen. Sind also schätzungsweise noch etwa 4-6 Jahre. 8-)

himitsu 17. Feb 2015 09:26

AW: Class Helper for Interface
 
Delphi ist ja bald richtig erwachsen ... mit 35 sind dann wohl auch die letzten Jugendsünden behoben. :angel:

Der schöne Günther 17. Feb 2015 09:38

AW: Class Helper for Interface
 
Ich will gar nicht wissen wie dann die Midlife Crisis aussieht.

Sir Rufo 17. Feb 2015 09:58

AW: Class Helper for Interface
 
Das mit den mehrfachen Helpern ist wohl angemacht, oder kann mir ansonsten jemand das hier erklären
Delphi-Quellcode:
type
   Bezeichnername = class|record helper [(Vorfahrenliste)] for TypBezeichnername
       MemberListe
     end;
Oder was ist da mit dieser Vorfahrenliste gemeint? Die Dokumentation schweigt sich darüber pauschal erst mal aus.

Stevie 17. Feb 2015 10:05

AW: Class Helper for Interface
 
Zitat:

Zitat von Sir Rufo (Beitrag 1290152)
Das mit den mehrfachen Helpern ist wohl angemacht, oder kann mir ansonsten jemand das hier erklären
Delphi-Quellcode:
type
   Bezeichnername = class|record helper [(Vorfahrenliste)] for TypBezeichnername
       MemberListe
     end;
Oder was ist da mit dieser Vorfahrenliste gemeint? Die Dokumentation schweigt sich darüber pauschal erst mal aus.

class helper kannste voneinander erben lassen, record helper nicht.
Allen Bauer redet aber schon seit Jahren davon, dass sie zu einem rooted Type System wollen.
Man kann übrigens auch heute schon den Compiler entsprechend hacken, dass er record helper Vererbung zulässt ;) (fragt mich jetzt aber nicht, wie - keine Ahnung)

himitsu 17. Feb 2015 10:13

AW: Class Helper for Interface
 
Bei der Vererbung müssen sich aber die Helper kennen, bzw. es sind dann "immer" beide Helper eingebunden, wenn man den Nachfahren haben will.


Man kann aber nicht mehrere Helper unabhängig "parallel" deklarieren.
z.B. der Helper vom Delphi, dann Meiner (OK, da könnte man vererben), aber jetzt kommen noch die Helper von sh17 und Anderen dazu, die ich mir über Fremdkomponenten reinhole.

Sollen wir uns jetzt alle einigen, wer von wem erbt?
Vererbung ist keine Lösung, denn gerade diese Abhängigkeiten wollte man doch loswerden. Da könnte man die Class-Helper auch gleich wegwerfen und direkt die Klassen-Vererbung benutzen.

Der schöne Günther 17. Feb 2015 10:15

AW: Class Helper for Interface
 
Es würde immerhin reichen, dem RTL-eigenen
Delphi-Quellcode:
TStringHelper
(und Konsorten) noch etwas eigenes hinzuzufügen. Wenigstens einmal. Ich bin ein genügsamer Mensch.

TiGü 17. Feb 2015 10:18

AW: Class Helper for Interface
 
Zitat:

Zitat von Stevie (Beitrag 1290144)
Zitat:

Zitat von Dejan Vu (Beitrag 1290143)
Preisfrage: Wo ist 'Bar' denn implementiert? Im Interface sicherlich nicht...

Delphi-Quellcode:
IFooHelper = interface helper for IFoo
  procedure Bar;
end;
ist equivalent zu:

Delphi-Quellcode:
procedure Bar(const Self: IFoo);

Aber muss das Objekt dann nicht trotzdem IFooHelper kennen?
Wie soll das sonst funktionieren?

Stevie 17. Feb 2015 10:18

AW: Class Helper for Interface
 
Zitat:

Zitat von himitsu (Beitrag 1290155)
Bei der Vererbung müssen sich aber die Helper kennen, bzw. es sind dann "immer" beide Helper eingebunden, wenn man den Nachfahren haben will.


Man kann aber nicht mehrere Helper unabhängig "parallel" deklarieren.
z.B. der Helper vom Delphi, dann Meiner (OK, da könnte man vererben), aber jetzt kommen noch die Helper von sh17 und Anderen dazu, die ich mir über Fremdkomponenten reinhole.

Sollen wir uns jetzt alle einigen, wer von wem erbt?
Vererbung ist keine Lösung. Da könnte man die Class-Helper auch gleich wegwerfen und direkt die Klassen-Vererbung benutzen.

Ich hab nur gesagt, wie es ist, nicht dass es gut ist. Vererbung von helpern ist Unfug. Sie sollten es vernünftig (oh je) implementieren und mehrere erlauben und korrekt erkennen, wenn es Konflikte gibt - oder den klassischen Weg von "wenn es 2 gleiche gibt, dann überdeckt der letzte den vorherigen" - wobei ich dieses Verhalten von Delphi schon seit jeher als sehr gefährlich betrachte.

Zitat:

Zitat von TiGü (Beitrag 1290157)
Aber muss das Objekt dann nicht trotzdem IFooHelper kennen?
Wie soll das sonst funktionieren?

Was hat das Objekt damit zu tun? Ich operiere auf dem Interface, wo is da nen Objekt?

TiGü 17. Feb 2015 10:20

AW: Class Helper for Interface
 
Zitat:

Zitat von himitsu (Beitrag 1290155)
Man kann aber nicht mehrere Helper unabhängig "parallel" deklarieren.
z.B. der Helper vom Delphi, dann Meiner (OK, da könnte man vererben), aber jetzt kommen noch die Helper von sh17 und Anderen dazu, die ich mir über Fremdkomponenten reinhole.

Und bei gleichen Methoden (Bezeichner und Rückgabewert) in den Helpern?
Wie soll das Problem gelöst werden, welche Methode genommen werden soll? Mit As-Operator?

TiGü 17. Feb 2015 10:22

AW: Class Helper for Interface
 
Zitat:

Zitat von Stevie (Beitrag 1290158)
Zitat:

Zitat von TiGü (Beitrag 1290157)
Aber muss das Objekt dann nicht trotzdem IFooHelper kennen?
Wie soll das sonst funktionieren?

Was hat das Objekt damit zu tun? Ich operiere auf dem Interface, wo is da nen Objekt?

Muss nicht irgendwer irgendwann mal irgendwas implementieren? :wiejetzt:

himitsu 17. Feb 2015 10:23

AW: Class Helper for Interface
 
Zitat:

Zitat von TiGü (Beitrag 1290159)
Und bei gleichen Methoden (Bezeichner und Rückgabewert) in den Helpern?

Siehe Stevies Antwort, kurz vor dir.


Auch Interfaces kann man vererben, aber dann muß ich die Methoden nicht nur ins Interface einbauen, sondern auch in das dahinterliegende Objekt und genau das kann/will ich nicht, vorallem nicht, wenn man das Dahinter garnicht kennt und keinen Zugriff darauf hat.

Mir würde es auch reichen, wenn man statische Methoden direkt im Interface deklarieren könnte, aber weil das nicht geht, dachte ich, ich probiere es einfach mal mit einem Helper, der dann auch garnicht ging. :stupid:

himitsu 17. Feb 2015 10:29

AW: Class Helper for Interface
 
Zitat:

Zitat von TiGü (Beitrag 1290160)
Muss nicht irgendwer irgendwann mal irgendwas implementieren? :wiejetzt:

Der Code steckt im Helper, so wie jetzt auch, bei all den anderen Helpern.

Das Objekt/Interface muß und soll garnichts davon wissen, genauso wie der Vorfahr von einer Vererbung auch nichts wissen soll.
Der Compiler leitet den Aufuf auf die Helper-Methode um, wenn er sie findet und geht nicht direkt auf das Objekt/Interface los.

[add]
Das ist es, was Delphi bei den Class-Helpern macht ... so im Prinzip.

Delphi-Quellcode:
type
  TMyClass = class
    procedure Foo(S: string);
  end;
  TMyDummyClassHelper = class(TMyClass)
    procedure Bar(S: string);
  end;

procedure TMyClass.Foo(S: string);
begin
  ShowMessage(S);
end;

procedure TMyDummyClassHelper.Bar(S: string);
begin
  S := StringReplace(S, ' ', ' du schöne ', []);
  Foo(S);
end;

var
  C: TMyClass;
begin
  C := TMyClass.Create;
  C.Foo('Hallo Welt!');
  //C.Bar('Hallo Welt!'); << Compiler findet Bar im Helper und baut quasi einen Cast ein.
  TMyDummyClassHelper(C).Bar('Hallo Welt!');
end;
Das haben die einfach so von mir abgeguckt, denn sowas hatte ich früher schon gemacht, als es noch keine Helper gab.

Dejan Vu 17. Feb 2015 12:34

AW: Class Helper for Interface
 
Zitat:

Zitat von himitsu (Beitrag 1290146)
Die Funktion ist eben nicht im Interface, genauso wenig, wie bei den anderen Record/Class-Helpern oder so wie eine class procedure nicht im Objekt steckt, zu Welchem sie deklariert wurde.

:wall: :wall: :wall:

:thumb:

TiGü 18. Feb 2015 08:41

AW: Class Helper for Interface
 
So einfach kann es manchmal sein, wenn man kein Brett vorm Kopf hat! :thumb:

Spricht denn irgendwas rein technisch gegen Interface-Helper?
COM-Kompatibilität oder so?

himitsu 18. Feb 2015 08:53

AW: Class Helper for Interface
 
Zitat:

Zitat von TiGü (Beitrag 1290313)
Spricht denn irgendwas rein technisch gegen Interface-Helper?
COM-Kompatibilität oder so?

Nein,

da das Interface eja nicht beeinflusst wird.


Drum hatte ich ja dacht, es würde gehen, wobei IMHO eine Class Procedure im Interface nicht stören würde, aber es würde reichen, wenn man einen normalen Class Helper dort anhängen könnte.


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