![]() |
Delphi-Version: 2010
Interfaces - Multiple Inheritance
Hallo liebe Delphi-Gemeinde,
ich habe jetzt schon etliche Artikel und Forendiskussionen dazu abgesucht, aber leider keinen Hinweis gefunden. Ich versuche, ein Interface von 2 weiteren Interfaces erben zu lassen, etwa wie folgt:
Delphi-Quellcode:
Wie bereits im Codeblock geschrieben tritt ein Fehler auf.IReadObject = Interface(IInterface) ['{26828CFB-864E-488E-914D-B4BEBD49F641}'] function getMass: Double; End; IWriteObject = Interface(IInterface) ['{B9F10F92-1145-4108-8C93-386ABA334A88}'] procedure setMass(AValue: Double); End; // Das funktioniert so leider nicht // Fehler: [DCC Fehler] E2029 ')' erwartet, aber ',' gefunden (In der Zeile "IFullObject..", Cursor steht vor ", IWriteObject)" IFullObject = Interface(IReadObject, IWriteObject) ['{19FCFAF2-1BF0-45BE-AABF-0C5138F7D7ED}'] property Mass: Double read getMass write setMass; end; Funktioniert das mehrfache Erben bei Interfaces (so wie bei Klassen) nicht? Oder mache ich etwas falsch? Mit freundlichen Grüßen, BliZZarD |
AW: Interfaces - Multiple Inheritance
Interfaces können genau wie Klassen immer nur einen Vorfahren haben. Klassen können aber mehrere Interfaces implementieren.
|
AW: Interfaces - Multiple Inheritance
Um es mal auf dein Beispiel zu beziehen:
Delphi-Quellcode:
Du kannst jetzt auch noch dies machen:
IReadObject = Interface(IInterface)
['{26828CFB-864E-488E-914D-B4BEBD49F641}'] function getMass: Double; property Mass: Double read getMass; End; IWriteObject = Interface(IInterface) ['{B9F10F92-1145-4108-8C93-386ABA334A88}'] procedure setMass(AValue: Double); property Mass: Double write setMass; End; TFullObject = class(TInterfacedObject, IReadObject, IWriteObject) private function getMass: Double; procedure setMass(AValue: Double); end;
Delphi-Quellcode:
Die implementierende Klasse muss dann das Interface nur noch aufführen.
IFullObject = Interface(IReadObject)
['{19FCFAF2-1BF0-45BE-AABF-0C5138F7D7ED}'] procedure setMass(AValue: Double); property Mass: Double read getMass write setMass; end; TFullObject = class(TInterfacedObject, IReadObject, IWriteObject, IFullObject) In diesem Fall wäre IWriteObject dann aber eigentlich überflüssig. |
AW: Interfaces - Multiple Inheritance
Nein, IReadObject ist "überlüssig"/unnötig, da es schon in IFullObject enthalten ist (der Vorfahre).
Die Funktion von IWriteObject ist zwar auch in IFullObject enthalten, aber das Interface selber natürlich nicht. |
AW: Interfaces - Multiple Inheritance
Zitat:
Delphi-Quellcode:
type
IFather = interface ['{41780D81-7DD0-491B-831D-E3C247FB425F}'] end; IChild = interface (IFather) ['{BAB88147-561D-4786-BC59-FDA90AFF3669}'] end; TClass1 = class(TInterfacedObject, IFather, IChild) end; TClass2 = class(TInterfacedObject, IChild) end; procedure Test; var intf1: IInterface; intf2: IInterface; begin intf1 := TClass1.Create; intf2 := TClass2.Create; if Supports(intf1, IFather) then ShowMessage('TClass1 supports IFather') else ShowMessage('TClass1 doesn''t support IFather'); if Supports(intf2, IFather) then ShowMessage('TClass2 supports IFather') else ShowMessage('TClass2 doesn''t support IFather'); end; |
AW: Interfaces - Multiple Inheritance
Zitat:
|
AW: Interfaces - Multiple Inheritance
Uwe hat recht. Interfaces, die nicht explizit bei einer Klasse angegeben sind bekommen keinen VMT Slot und sind demnach auch nicht über Supports verfügbar. Auch wenn sie eigentlich über eine Interface Vererbung drin sein müssten.
|
AW: Interfaces - Multiple Inheritance
Zitat:
Meinst du das Interface oder die Angabe dessen im Objekt? Aber gut zu wissen ... dachte daß auch die Interfaces supportet werden, welche als Parent indirekt mit eingebunden sind. :shock: |
AW: Interfaces - Multiple Inheritance
Ich hoffe das ist ok, dass ich das alte Thema wieder ausbuddele.
Aber genau die gleiche Frage stelle ich mir mittlerweile auch. Nochmal:
Wie kann ich überhaupt erfahren, dass
Delphi-Quellcode:
auch
TChild
Delphi-Quellcode:
supported? Müsste ich in meiner Klasse das
IParent
Delphi-Quellcode:
selbst überschreiben? Oder sollte man sich Interface-Vererbung generell abgewöhnen?
QueryInterface
|
AW: Interfaces - Multiple Inheritance
Zitat:
Übrigens: Interface-Vererbung hast du immer dabei - jedes Interface ist von IInterface abgeleitet. |
AW: Interfaces - Multiple Inheritance
Zitat:
|
AW: Interfaces - Multiple Inheritance
Zitat:
Zitat:
|
AW: Interfaces - Multiple Inheritance
Sollte 'GetInterface' das Problem nicht lösen?
Dessenungeachtet frage ich mich, was 'Supports' für einen Sinn macht, wenn man damit nicht herausfinden kann, ob eine Instanz ein bestimmtes Interface implementiert. Oder einfach ausgedrückt: Wozu dient 'Supports'? Was es macht, weiß ich mittlerweile, aber wann benötigt man genau diese Funktionalität ("Ist IMyInterface in der Deklaration der Klasse TMyClass enthalten"). |
AW: Interfaces - Multiple Inheritance
Zitat:
a) Supports mehrfach überladen ist, so dass man nicht nur prüfen kann, ob eine Klasse ein Interface implementiert, sondern auch ein Interface auf ein anderes "gecastet" werden kann. b) Supports nix anderes macht, als letztlich GetInterface bzw GetInterfaceEntry aufzurufen, welches in diesem Fall keinen Eintrag für ein durch Interface Vererbung implizit unterstütztes Interface zurückliefert (wie ich schon in meinem Post von 2010 erwähnte). ;) P.S. Hab sogar gerade gefunden, warum das so ist: ![]() |
AW: Interfaces - Multiple Inheritance
Zitat:
IReadWrite = interface(IRead, IWrite) geht nicht. Daher kann leider nur IReadWrite = interface(IRead) oder IReadWrite = interface(IWrite) machen. Hat man jetzt ein IReadWrite und braucht sowohl IRead als auch IWrite, muss man zur Laufzeit einen der Casts über Supports() machen. Für COM-Interfaces braucht man ausserdem so ein QueryInterface() um aus einem fremden Pointer ein Interface zu machen. Als Sprachfeature intern ist die Verwendung von Supports ansonsten aber eher schlechter Stil (so in etwa wie wenn man Klassen und Methoden nicht normal benutzt, sondern alles zur Laufzeit über RTTI und irgendwelche Strings zusammenwurstet). |
AW: Interfaces - Multiple Inheritance
Zitat:
Zitat:
@panino: Ich bekomme niemals heraus, ob meine Klasse/Interface ein Interface implementiert, das nicht direkt in der Klassendeklaration angegeben ist. Das ist blöd. Es bleibt (für mich) dabei: 'Supports' ist nicht wirklich zu gebrauchen und wer diesbezüglich richtig programmieren will, muss zu .NET wechseln. Ich arbeite gerade damit und schüttle über die Restriktionen, die einem hier zwangsweise begegnen, nur den Kopf. |
AW: Interfaces - Multiple Inheritance
Zitat:
Zitat:
|
AW: Interfaces - Multiple Inheritance
Zitat:
Delphi-Quellcode:
true.
Supports
Bezieht sich halt auf meinen Beispielcode im Beitrag #5 weiter oben. TClass2 deklariert nur IChild bei den unterstützen Interfaces und deshalb kann man IFather nicht über Supports abrufen. Will man das, muss man es so deklarieren wie bei TClass1. |
AW: Interfaces - Multiple Inheritance
Zitat:
Ist das in XE6 immer noch so? Eine Mehrfachvererbung von Interfaces wäre wirklich sehr übersichtlich. Dass Supports die Vererbungen nicht nachvollziehen kann und in den Klassen daher die einzelnen Interfaces angegeben werden müssen finde ich weniger schlimm. Aber ein Interface von mehreren anderen abzuleiten wäre schon sehr hilfreich. |
AW: Interfaces - Multiple Inheritance
Zitat:
|
AW: Interfaces - Multiple Inheritance
Wozu willst du mehrere Interfaces vererben?
IRead und IWrite und dann wird eines von Beiden oder Beides bei den entsprechenden Klassen angegeben. Sowas geht zwar auch
Delphi-Quellcode:
oder sowas
type
IRead = interface function ReadInt: Integer; end; IWrite = interface procedure WriteInt(Value: Integer); end; IReadWrite = interface function ReadInt: Integer; procedure WriteInt(Value: Integer); end;
Delphi-Quellcode:
Aber da muß man dann bei den Funktionen, welche auf diese Interfaces prüfen, die Interfaces doppelt abfragen, da es je "Funktion" zwei Interfaces gibt.
type
IRead = interface function ReadInt: Integer; end; IWrite = interface procedure WriteInt(Value: Integer); end; IReadWrite = interface(IRead) procedure WriteInt(Value: Integer); end; Read = IRead oder IReadWrite Write = IWrite oder IReadWrite |
AW: Interfaces - Multiple Inheritance
Zudem man bei der Klasse (wie schon mehrfach erwähnt) jedes Interface angeben muss.
Somit hilft einem dieses Multi-Erben auch nicht wirklich. BTW Wenn man die implementierende Klasse nicht von
Delphi-Quellcode:
ableiten möchte.
TInterfacedObject
Delphi-Quellcode:
dann kann man das auch nicht gegen das Basis-Interface
type
IMyInterface = interface end; TMyInterfacedObject = class( TObject, IMyInterface ) ... end;
Delphi-Quellcode:
casten.
IInterface
|
AW: Interfaces - Multiple Inheritance
Da muss ich offenbar noch etwas drauf rum denken...
Mit der Klassendefinition habe ich kein Problem. Wenn ich die Objekte in einer Factory erzeugen lasse und nur mit den Interfaces weiter arbeite, dann erscheint mir eine Mehrfachvererbung doch sinnvoll.
Delphi-Quellcode:
In RW sind beide Funktionalitäten deklariert.
var RW: IReadWrite;
X: Integer; ... RW := TFactory.GetNewRW; // erzeugt ein TReadWrite und gibt es als IReadWrite zurück ... // folgendes ist möglich RW.WriteInt(1); X := RW.ReadInt; // bzw. auch über Property RW.Int := 1; X := RW.Int; IReadWrite selbst könnte einfach so definiert sein:
Delphi-Quellcode:
IReadWrite = Interface(IRead, IWrite);
end; Ohne diese Mehrfachvererbung muss man die Funktionen (mindestens zum Teil) in den Interfacedefinitionen doppelt schreiben. Wenn man RW nicht als IReadWrite definiert sondern als "BasisInterface" muss man immer Supports verwenden und casten. PS: Gibt es denn irgendwo ein Delphi-Tutorial, wie man korrekt mit einer Factory und komplexen Schnittstellen arbeitet? So ganz konkret habe ich dazu noch nichts gefunden. Vielleicht bin ich ja noch etwas auf dem Holzweg... (Das ![]() |
AW: Interfaces - Multiple Inheritance
Warum denn nicht einfach kreativ so
Delphi-Quellcode:
IRead = interface
function Read : Integer; end; IWrite = interface procedure Write( Value : Integer ); end; IReadWrite = Interface; function Reader : IRead; function Writer : IWrite; end; TReadWrite = class( TInterfacedObject, IRead, IWrite, IReadWrite ) private FValue : Integer; public // IRead function Read : Integer; // IWrite procedure Write( Value : Integer ); // IReadWrite function Reader : IRead; function Writer : IWrite; end; function TReadWrite.Read : Integer; begin Result := FValue; end; procedure Write( Value : Integer ); begin FValue := Value; end; function Reader : IRead; begin Result := Self; end; function Writer : IWrite; begin Result := Self; end; |
AW: Interfaces - Multiple Inheritance
Nur als Einwurf, etwas OT aber auch nicht ganz. Interface-Vererbung, zumal mehrfache/tiefe Vererbung, halte ich so ganz aus dem Bauch heraus für mitunter fragwürdig. Oft endet es dann, dass man eine komplexe Hierarchie Interfaces definiert, welche dann fast 1:1 als Hierarchie von Klassen implementiert wird. Der Gedanke, ein Interface als eine Art "Service" zu sehen, den ein Objekt anbietet/unterstützt, fällt dabei vollkommen heraus. Die meisten Interfaces sollten wohl eher "schlank" sein.
Und jetzt lese ich mir mal durch, warum man in Delphi die implementierten Interfaces explizit angeben muss. Mich hat das schon mehr als einmal genervt... Schöne Feiertage btw! |
AW: Interfaces - Multiple Inheritance
Das ist ein guter Einwand.
Wenn man aber die Instanziierung der Klassen (Erzeugen der Objekte) auslagert und fortan nur noch mit Interfaces arbeiten will, dann braucht man wiederum mächtige Interfaces, die die gesamte Funktionalität veröffentlichen. Andernfalls müsste man immer prüfen, ob das vorliegende (Objekt-)Interface nun gerade zufällig IRead oder IWrite unterstützt, darauf casten und dann auf die Propertys zugreifen. Andererseits bringt das natürlich auch wieder Vorteile, da man so sehr flexibel mit den Objekt-Interfaces umgehen kann. Eigentlich bräuchte man dann IReadWrite gar nicht sondern würde einfach mit IInterface arbeiten:
Delphi-Quellcode:
var RW: IInterface;
X: Integer; ... RW := TFactory.GetNewRW; // erzeugt ein TReadWrite und gibt es als IInterface zurück ... if Supports(RW, IWrite) then (RW as IWrite).WriteInt(1); if Supports(RW, IRead) then X := (RW as IRead).ReadInt; // bzw. auch über Property if Supports(RW, IWrite) then RW.Int := 1; if Supports(RW, IRead) then X := (RW as IRead).Int; Dann braucht man sich gar nicht mehr kümmern, was genau da für ein Objekt vorliegt (eigentlich ja der Sinn von Interfaces). Wenn man sicher ist, was man übergibt kann man Supports ja notfalls weg lassen. Alternativ könnte man natürlich noch Variablen wie R: IRead und W: IWrite einführen. Auf jeden Fall könnte man auf mächtige Schnittstellen als Klassenkopien so verzichten. Aktuell scheint mir der Ansatz eigentlich sinnvoll zu sein (kann sich aber wieder ändern ;-)). Ginge das so? |
AW: Interfaces - Multiple Inheritance
Wenn man sich an das
![]() ![]() Genau genommen ist schon allein das Supports abfragen des einen Interfaces auf das andere ein Code Smell, denn dadurch greift man von hinten rum auf etwas zu, was einem gar nicht übergeben wurde. Wenn ich möchte, dass nur lesender Zugriff erfolgt, übergeb ich das IRead Interface. Wenn sich nun jemand mit Supports mal ebend aus diesem Interface Schreibzugriff besorgt, ist das ziemlich bedenklich. |
AW: Interfaces - Multiple Inheritance
Ok, die ersten 2 Sätze kann ich nachvollziehen.
Aber das Supports stinkt nicht so ganz. Wenn ich einer Funktion bestimmte Objekt-Interfaces (also quasi irgendwelche Objekte) übergebe und dann diverse Dinge damit tun will, dann muss ich doch wissen, was ich vor mir habe. Z.B. kann ich prüfen, ob ISerialization unterstützt wird und das Objekt dann serialisieren. Wenn IOrganic unterstützt wird kann ich DoDestroying(1) aufrufen usw. Dass Supports schlecht sein soll, erschließt sich mir irgendwie nicht. NACHTRAG: Wenn ich absichtlich ein allgemeines Interface übergebe und damit ausdrücklich unterschiedliche Aktionen ermöglichen will, müsste mein Ansatz doch korrekt sein? Ich sehe ein, dass der Ansatz fragwürdig wäre, wenn tatsächlich nur IRead übergeben wird und das Objekt aber dennoch auseinander genommen würde (ähnlich wie mit der RTTI). |
AW: Interfaces - Multiple Inheritance
Zitat:
Ich kann aber auch Interfaces als Services übergeben (im Gegensatz zu Datenobjekten sind das Klassen bzw Interfaces, die irgendwelche Arbeit erledigen) und da sollte man aufpassen, irgendwelche Annahmen darüber zu machen, welche Interfaces möglicherweise noch implementiert sind (Stichwort ![]() Zu dem oben angesprochenen Punkt. Wenn Datenobjekte allerlei Interfaces implementieren, die dann irgendwas mit dem Objekt machen, handelt man sich sehr schnell Probleme mit dem SRP ein (das Objekt hält also nicht nur Daten, sondern Serialisiert sich, schreibt sich in die Datenbank, kann sich ausdrucken und vieles mehr). Zudem kommt es dann auch sehr schnell zu einer vermischung von verschiedenen Layern, die nix miteinander zu tun haben. Ich will damit nicht sagen, dass man das keineswegs machen sollte, aber man sollte aufpassen, welche Funktionalität man damit zur Verfügung stellt und inwieweit man seine Datenobjekte dann von anderen Schnittstellen abhängig macht. |
AW: Interfaces - Multiple Inheritance
Zitat:
Dann braucht man nur noch jeweils Eines der Drei anzugeben. |
AW: Interfaces - Multiple Inheritance
Zitat:
Mehr noch - bei passender Implementierung (Stichwort: TAggregatedObject und implements) muss die effektiv IFoo-implementierende Klasse ja gar nichts über IBar wissen. Es kommt doch am Ende nur darauf an, welches QueryInterface bei Supports aufgerufen wird. Natürlich könnte man um von IFoo auf IBar zu kommen in IFoo auch eine Funktion einbauen, die das IBar zurückgibt, aber damit hat man wiederum eine Abhängigkeit von IFoo zu IBar geschaffen. Auch die Verlagerung in ein Über-Interface, das sowohl IFoo als auch IBar als Funktionen bereitstellt, ist m.E. nicht gerade erstrebenswert. Der Weg über Interface-Aggregation liefert im Endeffekt das gleiche, die Verwendung von Supports finde ich aber deutlich übersichtlicher. Ob ich dann nicht doch lieber beide Interfaces in einer Klasse implementiere kann dem aufrufenden Code dann aber egal sein. |
AW: Interfaces - Multiple Inheritance
Es geht darum, dass es sich um eine implizite Annahme handelt, dass man wie auch immer von IFoo an ein IBar kommen kann, was ein Implementierungsdetail ist.
Wenn man sich mal durchliest, ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:24 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