![]() |
Delphi-Version: XE4
Polymorphie und Interfaces in Delphi
Hallo,
Mir ist gerade etwas aufgefallen. Angenommen wir haben 2 Interfaces die voneinander erben
Delphi-Quellcode:
und eine Klasse die IExtendedInterface implementiert:
IBaseInterface = interface;
IExtendedInterface = interface(IBaseInterface);
Delphi-Quellcode:
TKlasse = class(TInterfacedObject, IExtendedInterface);
Nun gibt es folgende Methode:
Delphi-Quellcode:
procedure Irgendwas(Base: IBaseInterface);
und der Aufruf:
Delphi-Quellcode:
Hab ich was verpasst oder sollte das nicht prinzipiell funktionieren?
var obj: TKlasse;
begin // ... Irgendwas(obj); // Inkompatible Typen: 'IBaseInterface' und 'TKlasse' end; Wenn ich TKlasse explizit zusätzlich IBaseInterface implementieren lasse (was ja eigentlich unnötig ist) dann funktionierts. Hat das einen Grund (bin ich blöd?) oder ist Delphi blöd? |
AW: Polymorphie und Interfaces in Delphi
Hatten wir kürzlich schon mal (suche gleich den Link): Ja, es ist eigentlich unnötig. Aber du musst das Parent-Interface trotzdem in der Typdefinition angeben. Sonst merkst du noch nichtmal zur Laufzeit mit Supports(..) dass es eigentlich IBaseInterface kann! :shock:
Delphi-Style. Gefällt mir auch nicht. Update: ![]() |
AW: Polymorphie und Interfaces in Delphi
TKlasse implementiert nur IExtendedInterface.
Auf die Methoden von IBaseInterface kann man also nur über das IExtendedInterface zugreifen. Soll die Klasse beide Interfaces implementieren, dann mußt du auch Beide angeben.
Delphi-Quellcode:
TKlasse = class(TInterfacedObject, IBaseInterface, IExtendedInterface);
Nur die angegebenen Interfaces (deren GUID) werden in die Interface-Liste der Klasse eingetragen und nur diese lassen sich "supporten", außer man erweitert die Support-Routine und macht es manuell. [edit] :schnarsch: |
AW: Polymorphie und Interfaces in Delphi
Ja da ist Delphi etwas eigen, aber der Grund für dieses Verhalten würde mich auch interessieren.
Alle Interfaces die eine Klasse unterstützen soll (für das Casten zur Laufzeit), müssen explizit von dieser Klasse implementiert werden, bei voneinander abgeleiteten Interfaces auch die Super-Interfaces wie Du gemerkt hast. Ausnahme wäre, dass eine Superklasse diese bereits implementiert, dann musst Du die Interfaces in deiner abgeleiteten Klasse nicht noch einmal explizit mit in die Klassendeklaration nehmen. @himitsu: Aber eine Klasse die IExtendedInterface implementiert, implementiert doch auch implizit IBaseInterface. In welcher Hinsicht macht das "Sinn", dass man nicht nach IBaseInterface casten kann, ohne dieses Interface zusätzlich noch mit in die Deklaration aufzunehmen. |
AW: Polymorphie und Interfaces in Delphi
Den Grund hab ich in meinem letzten Post nacheditiert.
|
AW: Polymorphie und Interfaces in Delphi
Ok, also ist dieses Verhalten der TGUID Verwendung "geschuldet". Dennoch hätte ich erwartet, dass Embacadero es schafft einfach die GUIDs aller Super-Interfaces mit einzutragen ... andererseits ... :>
|
AW: Polymorphie und Interfaces in Delphi
Das wäre aber auch nicht so gut, wenn Delphi das einfach so machen würde, denn es kann ja gewollt sein, daß IBaseInterface nicht supported wird und was macht man dann?
Das mit den GUIDs liegt an Windows und nicht an Delphi. :zwinker: Nur die GUID ist wichtig und den Namen interessiert keinen. Du kannst das Interface problemlos umbenennen, bzw. jedes Programm/DLL kann einen anderen Namen vor dem Compilieren verwenden, solange die GUID und die ordinalen Positionen der beliebig benennbaren Methoden gleich bleibt. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Wenn ich eine Klasse definiere, die IExtendedInterface implementiert, bekomme ich schließlich auch einen Compilerfehler, wenn sie nicht die Methoden von IBaseInterface implementiert. Wenn ich den obigen Code leicht modifiziere, wird er vom Compiler anstandslos geschluckt:
Delphi-Quellcode:
Es fehlt also schlicht und einfach der implizite Cast von Objektreferenz auf das implementierte Interface.
var obj: TKlasse;
begin // ... Irgendwas(IExtendedInterface(obj)); end; |
AW: Polymorphie und Interfaces in Delphi
Zitat:
Dennoch bin ich da auch auf Mikkey's Seite. Zitat:
Man verwendet die Ableitung (also die Abhängigkeit) von Interfaces ja aus einem trifftigen Grund .. und der ist für mich: jemand der das Subinterface implementiert, muss auch das Superinterface bereitstellen (implementieren). Beispiel:
Delphi-Quellcode:
Also eine Klasse die den Schreibzugriff über IObjectWriter anbietet, muss auch den Lesezugriff über IObjectReader anbieten (sonst würde der Compiler meckern aufgrund der Property).
IObjectReader = interface
GetName: String; property Name: String read GetName; end; IObjectWriter = interface(IObjectReader) SetName(const AValue: String); property Name: String read GetName write SetName; end; |
AW: Polymorphie und Interfaces in Delphi
Zitat:
Z.B. TKlasse = class(TObject, IExtendedInterface_1, IExtendedInterface_2). Welche Implementierung soll man nehmen? (Die Implementierungen können unterschiedlich delegiert sein...) Der Compiler könnte sich natürlich etwas mehr Mühe machen in eindeutigen Fällen ein passendes Interface zu suchen, aber die mit impliziten Cast verbundene Raterei verursacht eigentlich immer an unerwarteter Stelle Probleme. |
AW: Polymorphie und Interfaces in Delphi
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Von außen kann man nicht auf die Methoden des Vorfahren zugreifen. Man kommt da nur daran, wenn man die Interfaceinstanz auf den Vorfahren castet (als Typcast und nicht über Supports), wenn man weiß, daß dort etwas drin ist. Ich hatte das mal verwendet, um in der Codevervollständigung die Liste kürzer zu halten, da man in Interfaces keine private-Abschnitt deklarieren kann, worin sich Getter und Setter verstecken lassen. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Delphi-Quellcode:
Möglicherweise könnte das aber irgendwann mal klappen, insoweit könnte Uwe Raabes Einwand gerechtfertigt sein.
unit IFTest;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type IBase = interface(IInterface) function f1(): Integer; end; IExtend1 = interface(IBase) function f2(): Integer; end; IExtend2 = interface(IBase) function f3(): Integer; end; TK1 = class(TInterfacedObject, IExtend1) function f1(): Integer; virtual; function f2(): Integer; end; TK2 = class(TK1, IExtend2) function f1(): Integer; override; function f3(): Integer; end; TForm1 = class(TForm) procedure FormCreate(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} procedure testF(i: IBase); begin end; function TK1.f1(): Integer; begin result := 11; end; function TK1.f2(): Integer; begin result := 12; end; function TK2.f1(): Integer; begin result := 21; end; function TK2.f3(): Integer; begin result := 23; end; procedure TForm1.FormCreate(Sender: TObject); var o1: TK1; o2: TK2; i1, i2, i3: Integer; begin o1 := TK1.Create(); o2 := TK2.Create(); testF(IExtend1(o1)); testF(IExtend2(o2)); i1 := o1.f1(); i2 := o2.f1(); i3 := (TK1(o2)).f1(); ShowMessage(Format('o1: %d, o2: %d, Base(o2): %d', [i1, i2, i3])); // Ausgabe: 11 / 21 / 21 end; end. Edit: Ziehe meinen Einwand zurück, mit "reintroduce" in der TK2-Definition (anstelle "virtual"/"override") wird tatsächlich 11/21/11 ausgegeben. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Wollte ich auch grade sagen. Stichwort "Method Resolution clause". Hier ein Beispiel:
Delphi-Quellcode:
Ein realitätsnahes Beispiel hierfür will mir aber auch nicht einfallen.
program Project4;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; type IBaseInterface = interface procedure baseMethod(); end; IChildInterface1 = interface(IBaseInterface) // Stub end; IChildInterface2 = interface(IBaseInterface) // Stub end; TMyObject1 = class(TInterfacedObject, IChildInterface1) public procedure baseMethod(); virtual; abstract; end; TMyObject2 = class(TMyObject1, IChildInterface1, IChildInterface2) procedure baseForChildInterface1(); procedure baseForChildInterface2(); procedure IChildInterface2.baseMethod = baseForChildInterface1; procedure IChildInterface1.baseMethod = baseForChildInterface2; end; { TMyObject2 } procedure TMyObject2.baseForChildInterface1(); begin WriteLn('Das ist "baseForChildInterface1()"'); end; procedure TMyObject2.baseForChildInterface2(); begin WriteLn('Das ist "baseForChildInterface2()"'); end; var iChild1Reference: IChildInterface1; iChild2Reference: IChildInterface2; objRef: TMyObject2; begin try objRef := TMyObject2.Create(); iChild1Reference := objRef; iChild2Reference := objRef; iChild1Reference.baseMethod(); iChild2Reference.baseMethod(); except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; readln; end. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Hmmm, das ist jetzt komisch.
Delphi-Quellcode:
Ich weiß jetzt nicht mehr welche Delphi-Version das war, aber damals wurde der Getter und Setter nicht ausgeblendet, was XE jetzt aber macht.
type
IMyIntf = interface function Getter: Integer; procedure Setter(i: Integer); property Prop: Integer read Getter write Setter; end;
Delphi-Quellcode:
Dafür wird im IMyIntf der Getter/Setter jetzt doch angezeigt. :gruebel:
type
IInnerMyIntf = interface function Getter: Integer; procedure Setter(i: Integer); end; IMyIntf = interface(IInnerMyIntf) function Abc: Integer; end; Hmmmmmmmmm. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Delphi-Quellcode:
ist, die diese Methoden benutzt
property
|
AW: Polymorphie und Interfaces in Delphi
Damals wurde das halt auch angezeigt, obwohl es die Property dazu gab. :stupid:
Das war auch der einzige Grund, warum ich überhaupt mit diesen vererbten Interfaces gearbeitet hatte. Also kann ich mir nun den Spaß ersparen, was mir aber nicht aufgefallen ist, da ich das aus "Erfahreung" halt auch in aktuelleren Projekten gleich so gemacht hatte. Obwohl es jetzt zwar immernoch funktionierte, auch wenn aus einem anderem Grund. :wall: |
AW: Polymorphie und Interfaces in Delphi
Zitat:
Delphi-Quellcode:
dafür hast ;)
property
|
AW: Polymorphie und Interfaces in Delphi
Jupp, denn damals wurde alles aus den Vorfahren nicht angezeigt, womit ich jetzt nicht damit gerechnet hätte, daß man die nun sieht, wenn der Property weg ist. :wink:
Getter/Setter von Property wurden im selben Interface angezeigt, aber nichts aus dem Vorfahren. |
AW: Polymorphie und Interfaces in Delphi
Zitat:
Zitat:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Siehe: ![]() Meinen damaligen Post gelesen, aber nicht verstanden??? :gruebel: |
AW: Polymorphie und Interfaces in Delphi
Alzheimer? :oops:
|
AW: Polymorphie und Interfaces in Delphi
Zitat:
Zitat:
Warum genau wird in ![]() |
AW: Polymorphie und Interfaces in Delphi
Zitat:
Delphi-Quellcode:
Die Interfaces zum Schreiben der Log-Infos sind in IRead und IWrite dieselben, aber Reader und Writer
IBase = interface(IInterface)
procedure SetLogfile(const FileName: TFileName); procedure WriteLog(const LogInfo: String); end; IRead = interface(IBase); IWrite = interface(IBase); könnten unterschiedliche Logfiles verwenden. Dass man Interface-Methoden beliebig auf eigene Implementierungen mit anderem Namen umbiegen kann ist ein recht zentrales Feature von Interfaces. Es löst die ganzen Probleme, die man in anderen Programmiersprachen bei rein Klassen-basierter Mehrfachvererbung hat (C++). Mehr oder weniger ist das mal ein Featur, bei dem man mal von einem echten Vorteil von Delphi sprechen könnte. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:09 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 by Thomas Breitkreuz