![]() |
Fehlende Mehrfachvererbung bei Schnittstellen
Liste der Anhänge anzeigen (Anzahl: 1)
Die Frage ist so dumm, ich habe mir ernsthaft überlegt, sie unter anderem Namen zu stellen. Hoffentlich ist es nur die Hitze.
Nehmen wir an, wir haben eine Schnitstelle "Messgerät" und zwei weitere Schnittstellen, die jeweils davon erben. Und wir haben eine Implementation welche diese beiden implementieren wird. Siehe Bild im Anhang. Nun habe ich Code der nur Sinn macht wenn ich ein Objekt habe, das diese beiden Schnittstellen implementiert. Da der
Delphi-Quellcode:
(im Beispiel) nicht die einzige Implementation bleiben wird, möchte ich die Klasse hier nicht ins Spiel bringen- Nur die beiden Interfaces.
TFluxkompensator
Meine Frage: Ich komme also wohl nicht drum herum, mit mindestens zwei verschiedenen Referenzen (Typ
Delphi-Quellcode:
und
IMessgerätKontrollierbar
Delphi-Quellcode:
) zu arbeiten. Das wäre noch vollkommen ok, aber ich bin stark verunsichert, wann ich welche Referenz verwenden sollte, wollte ich eine Methode von
IMessgerätLivedatenfähig
Delphi-Quellcode:
verwenden? Klar, die Auswirkungen sind dieselben, aber ich sehe mich jedes mal in Erklärungsnot Warum diese Referenz, warum nicht die andere?
IMessgerät
Im Endeffekt fehlt mir eine Typdefinition die sagt: "Implementiert beide Interfaces". Mehrfachvererbung bei Interfaces geht ja in Delphi nicht. In Java hätte ich die Frage nicht stellen brauchen. C++ sowieso nicht ;-) In C# weiß ich nicht. Was habe ich nun für Alternativen?
Das ähnelt auch etwas der Frage ![]() wo versucht wurde, Generics mit "Typ A oder Typ B, beides ok" einzuschränken. Ergänzung: ![]() Ich bin für jeglichen Gedankensturm höchst dankbar. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zusammenführen (Mehrfachvererbung) ist nunmal grundsätzlich nirgendwo möglich.
Aber es verbietet keiner einem TMessgerät die beiden Interfaces zu implementieren, selbst wenn sie von einem Vorfahren abgeleitet sind. Vererbung bei Interfaces würde ich persönlich aber eh nur zur Versionierung verwenden. Die neue Version implementiert die alten Schnittstellen (also erbt sie Diese einfach) und bietet zusätzlich neue/weitere Schnittstellen an. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Richtig. Es realisiert beide. Das ist ja auch nicht das Problem.
Der TFluxkompensator (oder andere Klassen) selbst sind nicht das Problem. Es geht um den Code der sich mit Messgeräten befassen soll, die sowohl Livedaten bereitstellen als sich auch steuern lassen. Wie ich den am besten aufziehe: Habe ich hier (für ein Objekt) zwei verschiedene Variablen oder gibt es bessere Wege in Delphi? |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Interfaces lassen sich inzwischen (fast) wie Objekte casten.
- harte Casts sind zwar verboten, da unterschiedliche Interfaces auch unterschiedliche Referenzzeiger (Pointer-Adressen) besitzen, im gegensatz zu Klassen-Typcasts, welche alle die selbe Referenz (Adresse) besitzen. - aber soft-casts (AS) sind erlaubt |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Also du würdest mit einer IMessgerät-Referenz arbeiten und jedes mal (da es klar ist dass die Instanz beide Unter-Interfaces implementiert) runtercasten?
Delphi-Quellcode:
type TMessgeräteBehandler = class
protected var myMessgerät: IMessgerät; public constructor Create(myMessgerät: IMessgerät); procedure behandleGerät(); end; procedure TMessgeräteBehandler.behandleGerät(); begin if not myMessgerät.störungsfrei() then [...] WriteLn( 'Aktuelle Temperatur: ' + (myMessgerät as IMessgerätLivedatenfähig).getTemperatur().toString() ); end; |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Also grundsätzlich arbeite ich dann immer mit dem Typen, welcher am Meisten benötigt wird und der Rest wird gecastet.
Wenn kurz hintereinander mehrfach auf das andere Interface zugegriffen wird, dann wird in einer Temp-Variable kurz zwischengespeichert. (oder im extremen Notfall auch mal ein WITH) Aber du kannst den Cast auch intern machen.
Delphi-Quellcode:
s := myMessgerät.Livedaten.getTemperatur.toString;
Livedaten castet intern und gibt als Result ein IMessgerätLivedatenfähig zurück. iMessgerät erbt dann vom Wichtigeren und referenziert das Andere. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Als Ergänzung vielleicht noch: So würde ich es in Java machen:
Code:
In Delphi ist die Deklaration von IBoth nicht möglich.
class App
{ public static void main (String[] args) throws java.lang.Exception { IBoth myVar = new BothImplementer(); myVar.subMethod1(); myVar.subMethod2(); } } interface IBase { // Stub } interface ISub1 extends IBase { void subMethod1(); } interface ISub2 extends IBase { void subMethod2(); } // Reines Tagging-Interface interface IBoth extends ISub1, ISub2 {} // Muss das Tagging-Interface implementieren class BothImplementer implements ISub1, ISub2, IBoth { public void subMethod1() { System.out.println("submethod1"); } public void subMethod2() { System.out.println("submethod2"); } } |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Schlimm ist ja, daß selbst ein Singlepasscompiler, wie Delphi, theoretisch Mehrfachvererbung anbieten könnte,
also rein von den "sichtbaren" Schnittstellen, welche der Programmierer sieht. (vor dem Compilieren) Man würde einfach nur beim neuen "kombinierten" Objekt/Interface die direkten Schnittstellen der Vorfahren nochmals durch den Compiler jagen und dabei die bekannten Fehlermeldungen werfen, wenn sich etwas überschneidet. Zitat:
Und auch die IS/AS-Prüfungen, und Ähnliches, würden etwas aufwändiger, da man dann ja keine lineare Vererbung mehr hat, sondern einen Baum. Aber nein, es ist dennoch nicht möglich, in Delphi eine Mehrfachvererbung hinzubekommen. (jedenfalls nicht so, daß man die Typen, so wie es jetzt möglich ist, einfach so blind in die neuen Vererbungslinien casten kann) In dynamischen Sprachen, welche paktisch alles zur Laufzeit parsen und nicht mit festen Indize/Adressen arbeiten, wie z.B. Java/PHP/JavaScript, ist das halt was Anderes. Klasse/Interface 1 * Addresse 1 = Funktion A * Addresse 2 = Funktion B Klasse/Interface 2 * Addresse 1 = Funktion C * Addresse 2 = Funktion D Klasse/Interface 1+2 * Addresse 1 = Funktion A oder Funktion C * Addresse 2 = Funktion B oder Funktion D (das Selbe nochmal für alle Felder/Variablen in der Klasse usw.) Wenn man jetzt auf Adresse 1 zugreift, bei wem soll man denn nun rauskommen? |
Du könntest die Hierarchie auch auf den Kopf stellen:
Dein Messgerät hat allgemeine Schnittstelle (IMessgerät), welche zusätzliche Sichten auf Messgerät (also die anderen Interfaces) erzeugen kann. Vererbung gibt es unter den Interfaces keine. Die Klassen, welche IMessgerätLivedatenfähig (und so weiter) bereitstellen, müssen dabei noch nicht einmal mit TFluxKompensator verwandt sein, sondern werden vielleicht nur von der TFluxKompensator-Instanz zur Laufzeit erstellt und konfiguriert. Zitat:
Eigentlich sollte es mit "Mehrfachvererbung" von Interfaces überhaupt kein Problem geben, eben weil nur eine Schnittstellenbeschreibung ohne Implementierung ist. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Auch für Delphi ist das "möglich", aber man muß da etwas aufpassen und in den zweiten, dritten und x-ten Vererbungslinien, neben der Hauptvererbungslinie, sind bestimmte Dinge nicht mehr möglich.
In dem neuen kombinierten Typen ist dann (intern) nur die erste Vererbungslinie wirklich vererbt und alle anderen Felder/Funktion sind praktisch in den neuen Typen reinkopiert. (so wie bei den Generics) |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Jetzt mal etwas out-of-the-box gedacht (weiß nicht, ob das hier taugt):
Delphi-Quellcode:
type
IBase = interface end; ISub1 = interface (IBase) end; ISub2 = interface (IBase) end; IBoth = interface function GetSub1: ISub1; function GetSub2: ISub2; property Sub1: ISub1 read GetSub1; property Sub2: ISub2 read GetSub2; end; type TFluxKompensator = class(TInterfacedObject, ISub1, ISub2, IBoth) function GetSub1: ISub1; function GetSub2: ISub2; end; function TFluxKompensator.GetSub1: ISub1; begin result := Self; end; function TFluxKompensator.GetSub2: ISub2; begin result := Self; end; |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Ich hab grad son Deja Vu, das Thema hatten wir doch neulich erst :gruebel:
Generell stell ich mir immer die Frage, warum man vom einen Interface auf ein anderes kommen sollte. Das ![]() Wenn also ein Programmteil
Delphi-Quellcode:
konsumiert, warum sollte es dann darauf schauen, ob das Objekt dahinter nich auch zufällig noch
IDiesesDings
Delphi-Quellcode:
implementiert und damit was machen. Klar, mag es Fälle geben, aber generell ist das eher ein Architektursmell, wenn man sowas braucht. Das geht ein wenig in die Richtung Leaky Abstraction.
IJenesDings
|
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
als Parameter an eine Prozeduren übergeben können, die als Parameter ein ParentInterface erwartet). Das ISP will, dass man nicht unnötig große Schnittstellen übergiebt, und genau das ist es, was der Cast auf den Parent macht. Leider geht das in Delphi eben nicht ganz ohne Bastelei, daher ist sowas leider immer wieder ein Thema... |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Vielen Dank für die ganzen schlauen Beiträge. :cheers:
Ich glaube die Beiträge von Uwe und BUG sind in Sachen Lösung wohl das, worauf es in Delphi am besten hinausläuft. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
![]() |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Ich wollte, mit einem mehr oder weniger konkreten Beispiel, schauen, wie andere es schreiben würden.
Das andere Thema geht ja eher (technisch) darum zu schauen, was und was nicht in Delphi geht; mit dem Fokus auf das "Herausfinden, das etwas implementiert wird". Mir ging es jetzt nicht um das Herausfinden, sondern um das "Wie am besten ansprechen?". Und wieso Januar? |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Zitat:
da es ein Implementierungsdetail wird und nicht über die Interfacedefinition ersichtlich ist. Zudem hat man leider keine Compile time safety.
Delphi-Quellcode:
P.S: Ich hab mich gerade mal mit meinem Kollegen zusammen gesetzt, der C# beherrscht und uns angeschaut, was diese "Multiple Interface Inheritance" in C# so macht.
program Project1;
{$APPTYPE CONSOLE} uses SysUtils; type IBar = interface ['{F2BDC6D3-6E79-48EF-99A9-94DE53C035DC}'] procedure DoIt; end; IBaz = interface ['{3E99A294-48EF-4E2A-B077-562462997EA7}'] procedure DoIt; end; IFoo = interface{(IBar, IBaz)} ['{F2173B9B-43A5-4631-A08B-7D64102F1867}'] end; TFoo = class(TInterfacedObject, IBar, IBaz, IFoo) // in C#: Foo : IFoo protected procedure DoIt; end; procedure TFoo.DoIt; begin Writeln('DoIt') end; procedure DoSomething(const bar: IBar); begin bar.DoIt; end; var foo: IFoo; begin foo := TFoo.Create; DoSomething(foo as IBar); Readln; end. Wir sind zu dem Schluss gekommen, dass es letztlich nur Syntax Sugar ist, was dafür sorgt, dass eine Klasse die ein Interface implementiert, welches von verschiedenen anderen "abgeleitet" ist, diese auch implementiert. Auch beim Übergeben eines solchen Interfaces an eine Methode, welche eins der Elterninterfaces erwartet, benötigt man dann keinen Cast, sondern kann es so übergeben. Letztlich ist es aber dasselbe wie ich oben im Delphi Code geschrieben habe, nur ein wenig typensicherer. Im Übrigen lässt C# ein foo.DoIt() nicht zu und quittiert es mit einem "ambiguous call" Compilerfehler. Und auch die Reflection zeigt einem, dass die Klasse alle 3 Interfaces implementiert, obwohl man eigentlich nur IFoo hingeschrieben hat. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Danke, du hast den Problemkreis sehr anschaulich beschrieben.
Der Delphicompiler müsste nur zwei Dinge zusätzliche beherschen:
Delphi-Quellcode:
Das vermisse ich eigentlich schon fast so lange, wie ich mit Interfaces in Delphi arbeite.
TFoo = class(TInterfacedObject, IFoo) // IBar, IBaz, alle Eltern durch den Compiler versteckt mit angelegt}
DoSomething(foo); // (foo as IBar) duch den Compiler automatische Abfrage des passenden Elter |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Delphi-Quellcode:
Das Hauptproblem ist aber hierbei, dass nach dem Aufruf von
procedure DoSomethingWithBar( ABar : IBar );
procedure DoSomethingWithBaz( ABaz : IBaz ); TFoo = class( TInterfacedObject, IBar, IBaz, IFoo ) ... end; var Foo : TFoo; begin Foo := TFoo.Create; DoSomethingWithBar( Foo ); DoSomethingWithBaz( Foo ); end;
Delphi-Quellcode:
die Foo-Instanz sich in Rauch auflöst. Somit müsste man entweder
DoSomethingWithBar( Foo );
Delphi-Quellcode:
von
TFoo
Delphi-Quellcode:
ableiten und explizit freigeben
TPersistentInterface
Delphi-Quellcode:
oder sich eben zusätzlich eine
TFoo = class( TInterfacedPersistent, IBar, IBaz, IFoo )
end; var Foo : TFoo; begin Foo := TFoo.Create; try DoSomethingWithBar( Foo ); DoSomethingWithBaz( Foo ); finally Foo.Free; end; end;
Delphi-Quellcode:
Referenz merken.
IFoo
Delphi-Quellcode:
oder man hat ARC und dann klappt es einfach so ohne dieses Gedöns.
var
Foo : TFoo; FooIntf : IFoo; begin Foo := TFoo.Create; FooIntf := Foo; DoSomethingWithBar( Foo ); DoSomethingWithBaz( Foo ); end; |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Delphi-Quellcode:
ist und nicht wie im Beispiel oben von
TFoo
Delphi-Quellcode:
, worum es auch hier eigentlich geht.
IFoo
Ich kann dir nicht sagen, was C# da genau macht, wenn man ein IFoo an ein IBar oder IBaz übergibt (hab mir den IL Code nicht angeschaut). Aber dadurch, dass dort fest steht, "wenn IFoo implementiert wird, dann ist auch IBar und IBaz implementiert" ist sichergestellt, dass ein Cast von IFoo auf IBar oder IBaz immer erfolgreich ist. Und genau das geht in Delphi ebend nicht. Zitat:
|
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Denn was ist, wenn jemand nicht will, daß das Objekt auch alle Vorfahren der supporteten Interfaces supported? Delphi hat nunmal eine strenge Typsicherheit und sowas würde das total aufweichen, was dann schnell mal Probleme mit überladenen Methoden ergibt, da am Ende dann jede Interface-Variable wie IInterface aussieht. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Die ganzen Devs, die mit C# programmieren, müssen ja echt arme Schweine sein, da sie mit solchen grausamen Problem zu kämpfen haben ... /ironie off |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Hier beißen sich einfach zwei Anwenderwünsche. Die aktuelle Implementation bietet aber einen Möglichkeit, das gewünschte trotzdem zu erreichen (durch explizites Aufführen der Interfaces), während ein implizites Einbinden der Parent-Interfaces nicht so einfach verhindert werden kann. Ergo: der aktuelle Ansatz ist flexibler. |
AW: Fehlende Mehrfachvererbung bei Schnittstellen
Zitat:
Die aktuelle Implementierung ist designtechnisch komplett Tinnef und nur so ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07: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-2025 by Thomas Breitkreuz