![]() |
Re: Zirkuläre Unit Reference
Nachdem ich zunächst dachte alle wird gut, musste ich feststellen das nur die geerbten Methoden und Eigenschaften übernommen werden :( (bei den Anderen Fehlermeldung: "Undefinierter Bezeichner")
Also habe ich zum test nicht mehr geerbt, nun kriege ich aber die Fehlermeldung: "Typ ... nicht vollständig definiert" Die Fehlermeldung lässt sich so umgehen:
Delphi-Quellcode:
Aber so sieht Delphi die Klasse leider als leere Klasse und ich kriege die Fehlermeldung: "Undefinierter Bezeichner"
type
TClass1 = class end; TClass2 = class FClass1: TClass1; end; TClass1 = class FClass2: TClass2; end; |
Re: Zirkuläre Unit Reference
ohne das end bei der Forward-Deklaration. Sonst denkt Delphi, die Klasse ist komplett, also so:
Delphi-Quellcode:
Type
TFoo=Class; TBar=Class FFoo: TFoo; End; TFoo=Class FBar: TBar; End; |
Re: Zirkuläre Unit Reference
Liste der Anhänge anzeigen (Anzahl: 1)
Das habe ich ja schon probiert. Passend zu dem Quelltext käme jetzt: Typ TFoo nicht vollständig definiert.
Ich habe ein kleines Beispiel gemacht. |
Re: Zirkuläre Unit Reference
Hallo,
ich habe Dir schon in meinem letzten Beitrag geschrieben, wie es geht. In Deinem Beispielcode deklarierst Du die beiden Klassen in verschiedenen Units, bzw. schreibst in der einen Unit eine Forward-Deklaration auf eine Klasse in einer anderen Unit. Das funktioniert natürlich nicht. Wenn Du Forward-Deklaration nutzt ("type TFoo = class;"), dann musst Du diese Klasse auch komplett in dieser Unit deklarieren. Gruß xaromz |
Re: Zirkuläre Unit Reference
Präziser:
Forward Deklarationen eines Types müssen im gleichen "Type" Block erfolgen samt ihrer vollständigen Deklaration. Ein regülärer Bezeichner auf Unitebene beendet eines Type Block automatisch. Ein regulärer Unitbezeichner sind "Unit", "uses", "implementation" und "interface". Ergo: mit Delphi gehen keine Forward Deklarationen über Unitgrenzen hinweg, noch in unterschiedlichen Type-Blöcken. Erweitere mal virtuell in Gedanken alle Sachen wie "const", "type", "implementation", "interface" nit einem sofortigem "begin end;" Block. Denn defakto existieren diese quasi im Compiler auch wenn wir sie nicht im Source schreiben müssen/dürfen. Dh. nach einem "type" stände sofort "begin end;" und dazwischen kannst du Forward Deklaration machen wie du möchtest Hauptsache du deklarierst diese Typen dann in diesem Block auch vollständig. Eine Deklaration innerhalb dieses "virtuellen" Bereiches ist nicht in anderen "Bereichen" sichtbar. Einzigste Ausnahme speziell auf den Bezeichner "type" bezogen ist die Deklaration eines neuen eigenständigen Standarddatentyps abgeleitet von einem anderen Datentyp, also sowas:
Delphi-Quellcode:
Gruß Hagentype //begin TTime = type TDateTime; TDate = type TDateTime; MyInteger = type Integer; //end; type //begin //end; |
Re: Zirkuläre Unit Reference
Liste der Anhänge anzeigen (Anzahl: 1)
Das Problem ließ sich mit noch ein bisschen externer hilfe lösen.
Es müssen zu den einzelnen Klassen Interfaces erstellt, und in einzelne Units gepackt werden. Ich habe für ein Beispiel geschrieben und für alle Interessierten in den Anhang gepackt. @xamroz Mein Problem war das es mir sehr wichtig ist die Unitunterteilung aufrecht zu erhalten. Ich dachte es wäre so möglich... |
Re: Zirkuläre Unit Reference
Hallo,
Zitat:
Zu Deinem Demoprogramm: - Du castest ein Interface auf das zugrundeliegende Objekt. Das ist nicht zulässig. - Du benutzt Objekte, ohne Instanzen zu erzeugen. Das fliegt Dir bald um die Ohren. Gruß xaromz |
Re: Zirkuläre Unit Reference
Neben den korrekten Bemerkungen von Xaromz noch als Bemerkung zusätzlich:
Das was du in deinem Testprojekt machts geht auch einfacher
Delphi-Quellcode:
Gruß hagenunit class1; interface type TClass1 = class private FClass2: TObject; public procedure BlaBla; end; implementation uses class2; procedure TClass1.BlaBla; begin (FClass2 as TClass2).BlaBla; end; unit class2; interface type TClass2 = class private FClass1: TObject; public procedure BlaBla; end; implementation uses Class1; procedure TClass2.BlaBla; begin (FClass1 as TClass1).BlaBla; end; |
Re: Zirkuläre Unit Reference
Dieser Weg soll der sauberere sein, die andere Möglichkeit wäre alles von einem Basisobjektabzuleiten, dann statt dem Interface über das Basisobjekt die Typen zu deklarieren, und am ende wie bei dem Interface zu casten.
@xaromz [qoute]Ich hoffe, Du weißt, was Du da tust. Interfaces sind eine sehr fehlerträchtige Sache. Insbesondere durch die gegenseitige Abhängigkeit der beiden Interfaces wirst Du vermutlich ein Speicherleck bekommen. Für diesen Zweck sind Interfaces [/quote] Ich werde wie gesagt den Quellcode erzeugen, also muss ich es nur einmal richtig hin bekommen, und alle interfaces sind richtig. Zitat:
In meinem echten Projekt werden selbst verständlich alle Objekte instanziiert. @NegaH Du leitest statt von dem Interface von TObject ab, habe heute keine Zeit mehr das zu testen, werde ich aber auch ausprobieren. Hat es Nachteile? |
Re: Zirkuläre Unit Reference
Nein, hat keine Nachteile (mein Vorschlag oben), mal davon abgesehen das deine ganze Objektorientierte Konstruktion insich falsch sein muß.
Wenn du über solche Sachen wie oben mein Typcast oder den Interfaces herangehen musst dann stimmt irgendwas nicht an deinem Grundkonzept. Xaromz hat aber insofern Recht, das 1.) der Weg über die Interfaces ein unnötiger Überbau darstellt da wie in meinem Beispiel das Ganze auch ohne Interfaces geht 2.) du die Verwendung von Interfaces und Klassen nicht vermischen solltest da ansonsten das automatische Referencecounting und somit die Speicherfreigabe durcheinander kommt 3.) der Typcast eines Interfaces direkt in das implementierende Objekt in deinem Source vollkommen falsch ist 4.) der Typcast eines Interfaces in sein implementierendes Objekt grundsätzlich gegen die Regeln, bzw. dem Sinn der Interfaces ist Wenn du schon eine Referenz von einem Interface auf sein implementierendes Objekt erhalten möchtest dann so:
Delphi-Quellcode:
Aber!! zum Widersinnigen der Sache:type IMyInterface = interface function Implementor: TInterfacedObject; end; TMyImplementor = class(TInterfacedObject, IMyInterface) function Implementor: TInterfacedObject; end; function TMyImplementor.Implementor: TInterfacedObject; begin Result := Self; end; // oder gleich so, da Result -> EAX und Self -> EAX function TMyImplementor.Implementor: TInterfacedObject; begin end; Interfaces sollen die Implementierung einer Funktionalität im Zusammenhang mit dessen Schnittstellen Deklaration vollständig voneinander trennen. Es soll also keinerlei Zusammenhang zwischen einer deklarierten Schnittstelle zu der eigentlichen Implementierung dieser Schnittstelle bestehen, vollkommene Abstraktion der verschiedenen Schichten eines Denkmodelles das zur "idealen" Black Box führt. Führt man aber wie oben in die Schnittstelle = Interface wieder eine Schnittstelle auf den Implementor-> TMyInterfcae ein so durchbricht man diese strikte Logik und macht die Anwendung der Interfaces sinnnlos. Denn die Deklaration von IMyInterface oben bedeutet das diese Schnittstelle nur durch ein Objekt kompatibel zur Klasse TInterfacedObject durchgeführt werden kann. Diese Restriktion soll aber gerade mit Interfaces strikt vermieden werden. Ergo: ein normaler OOP Ansatz wie in meinem letzten Posting erreicht exakt das Gleiche wie der Vorschlag über die Interfaces. Nur mit dem Unterschied 1.) das es Typsicher ist und somit sauber, wir eben keinen harten und typ-unsicheren Cast verwenden wie in deinen Sourcen 2.) ohne Umwege funktioniert und somit zusätzliche Fehlerquellen beseitigt. Bedenke die schlimmsten Fehler liegen immer im Konzept eines Modelles ! 3.) kein Hilfsmittel misbraucht das für ganz andere Aufgaben gedacht ist, hier eben die Interfaces, und somit die spätere Verwenung deines Modelles für andere "korrekt denkende" Programmierer unmöglich macht. Denn für diese Programmierer ist es ein Unding wie du Interfaces mißbrauchen möchtest. Grundsätzlich wird man aber deine Problemstellung durch pure OOP und konzeptionelle Änderungen lösen können. Statt mit 2 Units zu arbeiten musst du OOP konform dein Klassenkonzept umstellen und mit 3 Unit arbeiten. 1. Unit -> Basisunit die eine abstrakte Vorfahrklasse enthält in der alle Gemeinsamkeiten der Nachfahrklassen deklariert wurden, meistens abstrakte Klassen. Sprich Klassen aus reiner Deklaration von abstrakten virtuellen Methoden ohne reale Implementierung. 2. Unit -> Nachfahre Class1 von der Basisklasse und Verwendung von UnitClass2 in der Implementation Sektion dieser Unit. 3. Unit -> Nachfahre Class2 von der Basisklasse und Verwendung von UnitClass1 in der Implementation Sektion dieser Unit. Zitat:
Gruß hagen |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:48 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