![]() |
Delphi-Version: XE7
Class Helper for Interface
Man kann doch Class/Record Helper nun an "alles" dran hängen?
Warum nicht auch an Interfaces? |
AW: Class Helper for Interface
Weil Embarcadero einfach zu be... ach, lassen wir das, sonst reg ich mich nur wieder auf...
|
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? |
AW: Class Helper for Interface
Berühmtestes Beispiel: IEnumerable<T>
Eigentlich müsste dieses Interface nur so aussehen:
Delphi-Quellcode:
Und über Extension Methodseinen interface helper kann man nun alle möglichen Operationen, die auf GetEnumerator operieren, implementieren.
type
IEnumerable<T> = interface function GetEnumerator: IEnumerator<T> end; Das ermöglicht einem eine Verkettung dieser Operationen -> syntax Sugar. Anderes Beispiel:
Delphi-Quellcode:
Simples Interface, wenn man neue Logger bauen will, muss man nur eine Methode implementieren -> gut!
type
ILogger = interface procedure WriteLogEntry(const entry: TLogEntry); end; 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:
In diesen Methoden wird nun nix anderes gemacht als die Information in ein TLogEntry gepackt und dann an die WriteLogEntry Methode übergeben.
type
ILoggerHelper = interface helper for ILogger procedure LogValue<T>(const value: T) procedure EnterMethod(const methodName: string); procedure LeaveMethod(const methodName: string); end; 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:
Aber ein:
procedure Methode(Self: <gehelpter type>; args)
Delphi-Quellcode:
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):
logger.EnterMethod('foo');
// liest sich einfach besser als: EnterMethod(logger, 'foo');
Delphi-Quellcode:
for c in customers.Where(...).Ordered.Take(10) do
// mach was |
AW: Class Helper for Interface
Aber warum nicht einfach
Delphi-Quellcode:
?
ILoggerHelper = interface(ILogger)
|
AW: Class Helper for Interface
Zitat:
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:
Und die kann man dann so aufrufen:
procedure EnterMethod(extends logger: ILogger; const methodName: string);
Code:
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.
logger.EnterMethod('foo')
|
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... |
AW: Class Helper for Interface
Unter Delphi ist das mit den Interface Helpern doch uninteressant, denn die Interfaces unterstützen keine Generics
Delphi-Quellcode:
Mit einem
IFoo<T> = interface
function AsType<TResult> : TResult; // <- Zonk end;
Delphi-Quellcode:
drumherum ist das dann wieder kein Problem (ist ja in Spring4D auch so vorhanden ;))
record
Delphi-Quellcode:
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
Foo<T> = record
class operator implicit ( a : IFoo<T> ) : Foo<T>; function AsType<TResult> : TResult; end; |
AW: Class Helper for Interface
Zitat:
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. |
AW: Class Helper for Interface
Zitat:
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:
|
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. |
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:
Foo implementiert also IFoo. Gut.
type
IFoo = Interface procedure FooMethod; end; Foo = Class (IFoo) public procedure FooMethod; end Nun erweitere ich IFoo mit einem Helper
Delphi-Quellcode:
Preisfrage: Wo ist 'Bar' denn implementiert? Im Interface sicherlich nicht...
IFooHelper = interface helper for IFoo
procedure Bar; end; ... var Foo : IFoo; ... Foo.Bar; // das geht also. Var oldFoo : Foo; begin Foo.Bar // und das? |
AW: Class Helper for Interface
Zitat:
Delphi-Quellcode:
ist equivalent zu:
IFooHelper = interface helper for IFoo
procedure Bar; end;
Delphi-Quellcode:
Damit kannste dir die Frage selbst beantworten.
procedure Bar(const Self: IFoo);
|
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) |
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.
|
AW: Class Helper for Interface
Bestimmt. Die ersten
![]() |
AW: Class Helper for Interface
Delphi ist ja bald richtig erwachsen ... mit 35 sind dann wohl auch die letzten Jugendsünden behoben. :angel:
|
AW: Class Helper for Interface
Ich will gar nicht wissen wie dann die Midlife Crisis aussieht.
|
AW: Class Helper for Interface
Das mit den mehrfachen Helpern ist wohl angemacht, oder kann mir ansonsten jemand das hier erklären
Delphi-Quellcode:
Oder was ist da mit dieser Vorfahrenliste gemeint? Die
type
Bezeichnername = class|record helper [(Vorfahrenliste)] for TypBezeichnername MemberListe end; ![]() |
AW: Class Helper for Interface
Zitat:
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) |
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. |
AW: Class Helper for Interface
Es würde immerhin reichen, dem RTL-eigenen
Delphi-Quellcode:
(und Konsorten) noch etwas eigenes hinzuzufügen. Wenigstens einmal. Ich bin ein genügsamer Mensch.
TStringHelper
|
AW: Class Helper for Interface
Zitat:
Wie soll das sonst funktionieren? |
AW: Class Helper for Interface
Zitat:
Zitat:
|
AW: Class Helper for Interface
Zitat:
Wie soll das Problem gelöst werden, welche Methode genommen werden soll? Mit As-Operator? |
AW: Class Helper for Interface
Zitat:
|
AW: Class Helper for Interface
Zitat:
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: |
AW: Class Helper for Interface
Zitat:
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:
Das haben die einfach so von mir abgeguckt, denn sowas hatte ich früher schon gemacht, als es noch keine Helper gab.
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; |
AW: Class Helper for Interface
Zitat:
:thumb: |
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? |
AW: Class Helper for Interface
Zitat:
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 07:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz